1
0
mirror of https://github.com/dkmstr/openuds.git synced 2025-10-07 15:33:51 +03:00

25 Commits

Author SHA1 Message Date
Adolfo Gómez García
cd60b398a9 Fixed sample settings and added more info to log on case of SAML failure 2025-10-03 18:03:50 +02:00
Adolfo Gómez García
979f992b6d Update RDP and X2GO tunnel scripts and signatures with new configurations 2025-10-01 18:47:43 +02:00
Adolfo Gómez García
83689dddaa Merge remote-tracking branch 'origin/dev/andres/v4.0' into v4.0 2025-09-25 19:19:19 +02:00
aschumann-virtualcable
2fd157e463 Merge remote-tracking branch 'origin/v4.0' into dev/andres/v4.0 2025-09-22 17:36:09 +02:00
aschumann-virtualcable
afbd4c5355 Fixes incorrect parameter usage for macOS RDP connections
Updates logic to use the correct macOS-specific custom parameters
instead of Linux parameters when generating RDP connection settings.
Adds type ignore comments to improve compatibility with type checkers
and prevent related runtime issues.
2025-09-19 13:54:36 +02:00
aschumann-virtualcable
e4377b83e4 Corrects Mac RDP file usage and field mapping
Aligns Mac-specific RDP file logic to use the appropriate configuration and updates legacy field naming for better clarity and migration. Ensures Mac connections consistently respect intended custom parameter and file options, reducing potential confusion with Linux settings.
2025-09-18 13:59:48 +02:00
aschumann-virtualcable
6763de2bab Merge remote-tracking branch 'origin/v4.0' into dev/andres/v4.0 2025-09-17 12:35:22 +02:00
aschumann-virtualcable
f494c706fc Updates RDP signature files for macOS with new parameters 2025-09-15 12:34:00 +02:00
aschumann-virtualcable
76b488dc1d Extends RDP custom parameter support for macOS clients
Unifies logic for applying custom RDP parameters to macOS alongside Windows and Linux, improving compatibility and flexibility for connecting from Apple platforms.

Refactors script handling to better support Thincast and MSRDC clients on macOS, allowing password injection into RDP files and debugging RDP file content. Adds consistent type hints to suppress type checking warnings in subprocess and file operations.

Enhances tunnel scripts to properly apply RDP file logic for Thincast and improves debugging output.

No issue reference provided.
2025-09-15 11:23:47 +02:00
aschumann-virtualcable
826cc7aed8 Add macOS support for RDP file usage in Thincast connections
Adds macOS RDP file support for Thincast connections

Introduces a configurable option to use RDP files for Thincast and xfreerdp on macOS, enabling seamless file-based connections. Updates logic to open Thincast with the RDP file when the option is enabled, improving compatibility and user experience for macOS users.
2025-09-12 15:38:14 +02:00
aschumann-virtualcable
4da15d66fe Improves Thincast client detection and launch on macOS
Switches Thincast detection from file to directory check to match macOS app bundle structure.

Updates Thincast launch logic to use the 'open' command with appropriate arguments, improving compatibility and reliability.

Removes unused code for opening .rdp files with Thincast and applies consistent resolution handling.

Ensures signature files are updated accordingly.
2025-09-12 12:09:54 +02:00
aschumann-virtualcable
79495fc3b1 Enables Thincast support for RDP transport on macOS
Uncomments and activates logic for launching Thincast client,
allowing users to initiate RDP sessions via Thincast.

Updates the related signature file for integrity validation.
2025-09-12 11:24:31 +02:00
aschumann-virtualcable
e37b345aff Adds support for RDP file custom params on Linux
Enables the use of Windows custom parameters in RDP file generation when specified for Linux targets, aligning Linux behavior with Windows.

Improves flexibility for custom connection settings across platforms.
2025-09-11 13:15:07 +02:00
aschumann-virtualcable
ce1330066f Enhance XFREERDP and Thincast support to conditionally use RDP files, improving parameter handling and logging.
Improves RDP client handling with conditional file usage

Allows XFREERDP and Thincast to use RDP files when provided, enhancing parameter management and execution flexibility.
Refines logging for better traceability of client launch logic.
2025-09-10 19:22:37 +02:00
aschumann-virtualcable
20e86cd8c7 Refactor Thincast support: rename lnx_thincast_rdp_file to lnx_use_rdp_file, update related logic in RDPTransport and BaseRDPTransport, and enhance RDP file handling in direct.py and tunnel.py.
Refactors Thincast RDP file support for Linux clients

Renames and consolidates configuration for using RDP files with Thincast and xfreerdp, streamlines related logic, and enhances RDP file handling in Linux scripts. Improves clarity, maintainability, and user experience for Linux RDP connections.
2025-09-10 18:33:59 +02:00
aschumann-virtualcable
34676c817f Enhance Thincast support by updating RDPTransport to conditionally handle 'as_file' and improve logging in direct.py for better debugging. 2025-09-09 11:16:52 +02:00
aschumann-virtualcable
d17224c9cb Merge branch 'dev/andres/v4.0' of github.com:VirtualCable/openuds into dev/andres/v4.0 2025-09-09 10:43:02 +02:00
aschumann-virtualcable
b57b00f3fc Add lnx_thincast_rdp_file field to RDPTransport and BaseRDPTransport for Thincast support 2025-09-09 10:42:40 +02:00
aschumann-virtualcable
f82041da1e Add debug logging for Thincast RDP file processing and update signatures 2025-09-08 13:10:04 +02:00
aschumann-virtualcable
03a837f865 Add Thincast support and improve logging in RDP scripts 2025-09-08 13:08:12 +02:00
aschumann-virtualcable
95f0b0ab26 Update tunnel.py.signature with new signature data 2025-09-04 11:59:05 +02:00
aschumann-virtualcable
28433fc33e Add support for Thincast in RDP scripts and improve executable search logic 2025-09-04 11:55:41 +02:00
aschumann-virtualcable
fc4e7414df Update subproject commits for actor and client modules 2025-09-04 11:26:01 +02:00
aschumann-virtualcable
e61cb1f855 Add logging for client discovery in RDP scripts 2025-09-04 11:25:24 +02:00
aschumann-virtualcable
1fddc17b75 initial dev enviroment 2025-08-21 18:04:11 +02:00
17 changed files with 417 additions and 96 deletions

2
actor

Submodule actor updated: 3c40cb45f0...04ce3fc2d1

View File

@@ -14,6 +14,7 @@ BASE_DIR = '/'.join(
) # If used 'relpath' instead of abspath, returns path of "enterprise" instead of "openuds"
DEBUG = True
PROFILING = False
# USE_X_FORWARDED_HOST = True
SECURE_PROXY_SSL_HEADER = (

View File

@@ -657,7 +657,10 @@ class SAMLAuthenticator(auths.Authenticator):
raise exceptions.auth.AuthenticatorException(gettext('Error processing SAML response: ') + str(e))
errors = typing.cast(list[str], auth.get_errors())
if errors:
raise exceptions.auth.AuthenticatorException('SAML response error: ' + str(errors))
logger.debug('Errors processing SAML response: %s (%s)', errors, auth.get_last_error_reason()) # pyright: ignore reportUnknownVariableType
logger.debug('post_data: %s', req['post_data'])
logger.info('Response XML: %s', auth.get_last_response_xml()) # pyright: ignore reportUnknownVariableType
raise exceptions.auth.AuthenticatorException(f'SAML response error: {errors} ({auth.get_last_error_reason()})')
if not auth.is_authenticated():
raise exceptions.auth.AuthenticatorException(gettext('SAML response not authenticated'))

View File

@@ -98,6 +98,9 @@ class RDPTransport(BaseRDPTransport):
mac_custom_parameters = BaseRDPTransport.mac_custom_parameters
wnd_custom_parameters = BaseRDPTransport.wnd_custom_parameters
lnx_use_rdp_file = BaseRDPTransport.lnx_use_rdp_file
mac_use_rdp_file = BaseRDPTransport.mac_use_rdp_file
def get_transport_script( # pylint: disable=too-many-locals
self,
userservice: 'models.UserService',
@@ -165,20 +168,27 @@ class RDPTransport(BaseRDPTransport):
}
)
elif os.os == types.os.KnownOS.LINUX:
r.custom_parameters = self.lnx_custom_parameters.value
if self.lnx_use_rdp_file.as_bool():
r.custom_parameters = self.wnd_custom_parameters.value
else:
r.custom_parameters = self.lnx_custom_parameters.value
sp.update(
{
'as_new_xfreerdp_params': r.as_new_xfreerdp_params,
'address': r.address,
'as_file': r.as_file if self.lnx_use_rdp_file.as_bool() else '',
}
)
elif os.os == types.os.KnownOS.MAC_OS:
r.custom_parameters = self.mac_custom_parameters.value
if self.mac_use_rdp_file.as_bool():
r.custom_parameters = self.wnd_custom_parameters.value
else:
r.custom_parameters = self.mac_custom_parameters.value
sp.update(
{
'as_new_xfreerdp_params': r.as_new_xfreerdp_params,
'as_rdp_url': r.as_rdp_url if self.mac_allow_msrdc.as_bool() else '',
'as_file': r.as_file if self.mac_allow_msrdc.as_bool() else '',
'as_file': r.as_file if self.mac_use_rdp_file.as_bool() else '',
'address': r.address,
}
)

View File

@@ -297,6 +297,14 @@ class BaseRDPTransport(transports.Transport):
tab='Linux Client',
old_field_name='alsa',
)
lnx_use_rdp_file = gui.CheckBoxField(
label=_('Use RDP file for connections'),
order=42,
tooltip=_('If marked, an RDP file will be used for connections with Thincast or xfreerdp on Linux.'),
tab='Linux Client',
default=True,
old_field_name='lnx_thincastRdpFile',
)
lnx_printer_string = gui.TextField(
label=_('Printer string'),
order=43,
@@ -333,9 +341,18 @@ class BaseRDPTransport(transports.Transport):
old_field_name='allowMacMSRDC',
)
mac_use_rdp_file = gui.CheckBoxField(
label=_('Use RDP file for connections'),
order=51,
tooltip=_('If marked, an RDP file will be used for connections with Thincast or xfreerdp on Mac OS X.'),
tab='Mac OS X',
default=True,
old_field_name='mac_thincastRdpFile',
)
mac_custom_parameters = gui.TextField(
label=_('Custom parameters'),
order=51,
order=52,
tooltip=_(
'If not empty, extra parameter to include for Mac OS X Freerdp Client (for example /usb:id,dev:054c:0268, or aything compatible with your xfreerdp client)'
),

View File

@@ -295,8 +295,8 @@ class RDPFile:
# Camera?
# res += 'camerastoredirect:s:*\n'
# If target is windows, add customParameters
if self.target == types.os.KnownOS.WINDOWS:
# If target is windows or linux or macOS, add customParameters
if self.target == types.os.KnownOS.WINDOWS or self.target == types.os.KnownOS.LINUX or self.target == types.os.KnownOS.MAC_OS:
if self.custom_parameters and self.custom_parameters.strip() != '':
res += self.custom_parameters.strip() + '\n'

View File

@@ -114,6 +114,9 @@ class TRDPTransport(BaseRDPTransport):
lnx_custom_parameters = BaseRDPTransport.lnx_custom_parameters
mac_custom_parameters = BaseRDPTransport.mac_custom_parameters
wnd_custom_parameters = BaseRDPTransport.wnd_custom_parameters
lnx_use_rdp_file = BaseRDPTransport.lnx_use_rdp_file
mac_use_rdp_file = BaseRDPTransport.mac_use_rdp_file
# optimizeTeams = BaseRDPTransport.optimizeTeams
def initialize(self, values: 'types.core.ValuesType') -> None:
@@ -201,18 +204,25 @@ class TRDPTransport(BaseRDPTransport):
}
)
elif os.os == types.os.KnownOS.LINUX:
r.custom_parameters = self.lnx_custom_parameters.value
if self.lnx_use_rdp_file.as_bool():
r.custom_parameters = self.wnd_custom_parameters.value
else:
r.custom_parameters = self.lnx_custom_parameters.value
sp.update(
{
'as_new_xfreerdp_params': r.as_new_xfreerdp_params,
'as_file': r.as_file if self.lnx_use_rdp_file.as_bool() else '',
}
)
elif os.os == types.os.KnownOS.MAC_OS:
r.custom_parameters = self.mac_custom_parameters.value
if self.mac_use_rdp_file.as_bool():
r.custom_parameters = self.wnd_custom_parameters.value
else:
r.custom_parameters = self.mac_custom_parameters.value
sp.update(
{
'as_new_xfreerdp_params': r.as_new_xfreerdp_params,
'as_file': r.as_file if self.mac_allow_msrdc.as_bool() else '',
'as_file': r.as_file if self.mac_use_rdp_file.as_bool() else '',
'as_rdp_url': r.as_rdp_url if self.mac_allow_msrdc.as_bool() else '',
}
)

View File

@@ -1,4 +1,12 @@
import typing
import logging
import subprocess
import os.path
import shutil
import os
logger = logging.getLogger(__name__)
# On older client versions, need importing globally to allow inner functions to work
import subprocess # type: ignore
@@ -14,38 +22,105 @@ if 'sp' not in globals():
globals()['sp'] = sp # type: ignore # pylint: disable=undefined-variable
def exec_udsrdp(udsrdp: str) -> None:
import subprocess
import os.path
def _prepare_rdp_file(theFile: str, extension: str = '.rdp') -> str:
"""Save RDP file to user's home directory with the given extension and return its path."""
filename = tools.saveTempFile(theFile)
home_dir = os.path.expanduser("~")
base_name = os.path.basename(filename)
dest_filename = os.path.join(home_dir, base_name + extension)
temp_rdp_filename = filename + extension
logger.debug(f'Renaming temp file {filename} to {temp_rdp_filename}')
os.rename(filename, temp_rdp_filename)
logger.debug(f'Moving temp file {temp_rdp_filename} to {dest_filename}')
shutil.move(temp_rdp_filename, dest_filename)
logger.debug(f'RDP file content (forced): {theFile}')
return dest_filename
params: typing.List[str] = [os.path.expandvars(i) for i in [udsrdp] + sp['as_new_xfreerdp_params'] + ['/v:{}'.format(sp['address'])]] # type: ignore
def _exec_client_with_params(executable: str, params: typing.List[str], unlink_file: typing.Optional[str] = None) -> None:
logger.info(f'Executing {executable} with params: {params}')
tools.addTaskToWait(subprocess.Popen(params))
if unlink_file:
tools.addFileToUnlink(unlink_file)
def exec_udsrdp(udsrdp: str) -> None:
params = [os.path.expandvars(i) for i in [udsrdp] + sp['as_new_xfreerdp_params'] + [f'/v:{sp["address"]}']] # type: ignore
_exec_client_with_params(udsrdp, params)
def exec_new_xfreerdp(xfreerdp: str) -> None:
import subprocess # @Reimport
import os.path
if sp.get('as_file', ''): # type: ignore
dest_filename = _prepare_rdp_file(sp['as_file'], '.uds.rdp') # type: ignore
params = [xfreerdp, dest_filename, f'/p:{sp.get("password", "")}'] # type: ignore
_exec_client_with_params(xfreerdp, params, unlink_file=dest_filename)
else:
params = [os.path.expandvars(i) for i in [xfreerdp] + sp['as_new_xfreerdp_params'] + [f'/v:{sp["address"]}']] # type: ignore
_exec_client_with_params(xfreerdp, params)
params: typing.List[str] = [os.path.expandvars(i) for i in [xfreerdp] + sp['as_new_xfreerdp_params'] + ['/v:{}'.format(sp['address'])]] # type: ignore
tools.addTaskToWait(subprocess.Popen(params))
def exec_thincast(thincast: str) -> None:
if sp.get('as_file', ''): # type: ignore
dest_filename = _prepare_rdp_file(sp['as_file'], '.rdp') # type: ignore
params = [thincast, dest_filename, f'/p:{sp.get("password", "")}'] # type: ignore
_exec_client_with_params(thincast, params, unlink_file=dest_filename)
else:
params = [os.path.expandvars(i) for i in [thincast] + sp['as_new_xfreerdp_params'] + [f'/v:{sp["address"]}']] # type: ignore
_exec_client_with_params(thincast, params)
# Typical Thincast Routes on Linux
thincast_list = [
'/usr/bin/thincast-remote-desktop-client',
'/usr/bin/thincast',
'/opt/thincast/thincast-remote-desktop-client',
'/opt/thincast/thincast',
'/snap/bin/thincast-remote-desktop-client',
'/snap/bin/thincast',
'/snap/bin/thincast-client'
]
# Try to locate a xfreerdp and udsrdp. udsrdp will be used if found.
xfreerdp: typing.Optional[str] = tools.findApp('xfreerdp3') or tools.findApp('xfreerdp') or tools.findApp('xfreerdp2')
udsrdp: typing.Optional[str] = tools.findApp('udsrdp')
fnc, app = None, None
# Search Thincast first
executable = None
kind = ''
for thincast in thincast_list:
if os.path.isfile(thincast) and os.access(thincast, os.X_OK):
executable = thincast
kind = 'thincast'
break
if xfreerdp:
fnc, app = exec_new_xfreerdp, xfreerdp
# If you don't find Thincast, search UDSRDP and XFREERDP
if not executable:
udsrdp: typing.Optional[str] = tools.findApp('udsrdp')
xfreerdp: typing.Optional[str] = tools.findApp('xfreerdp3') or tools.findApp('xfreerdp') or tools.findApp('xfreerdp2')
if udsrdp:
executable = udsrdp
kind = 'udsrdp'
elif xfreerdp:
executable = xfreerdp
kind = 'xfreerdp'
if udsrdp is not None:
fnc, app = exec_udsrdp, udsrdp
if app is None or fnc is None:
if not executable:
raise Exception(
'''<p>You need to have xfreerdp (>= 2.0) installed on your systeam, and have it your PATH in order to connect to this UDS service.</p>
'''<p>You need to have Thincast Remote Desktop Client or xfreerdp (>= 2.0) installed on your system, and have it in your PATH in order to connect to this UDS service.</p>
<p>Please, install the proper package for your system.</p>
<ul>
<li>Thincast: <a href="https://thincast.com/en/products/client">Download</a></li>
<li>xfreerdp: <a href="https://github.com/FreeRDP/FreeRDP">Download</a></li>
</ul>
'''
)
else:
logging.debug(f'RDP client found: {executable} of kind {kind}')
fnc(app)
# Execute the client found
if kind == 'thincast':
if isinstance(executable, str):
exec_thincast(executable)
else:
raise TypeError("Executable must be a string for exec_thincast")
elif kind == 'udsrdp':
if isinstance(executable, str):
exec_udsrdp(executable)
else:
raise TypeError("Executable must be a string for exec_udsrdp")
elif kind == 'xfreerdp':
if isinstance(executable, str):
exec_new_xfreerdp(executable)
else:
raise TypeError("Executable must be a string for exec_new_xfreerdp")

View File

@@ -1 +1 @@
fGtGXYFIwNgr7B2h23tZSTRTZZzuUjrRqphmqgpRAS+hQ3FKqZJIoNIO7qxHh2ibA9BUyMHN21mjQvtVvAnv7ic0HfYPfJQPGro/yAJooMIuZPvqZS6e1hOBdd50Z3FKuqHMyHvQZhMu9tdoE06gyArwcSE++PZoT8dptOhwm5ogSCf2yfPA+bPxm9ACC3OmHTvKjZExFlnWLec/idASdGBxWnqHoWrXpBR3N8V/CMS4/QZZ3I+e+hJ8I2Sz2hINH0X2TIVVr2CTe3j4TzkxkCDAC5JrRmgj35vkiaOHKpW6drRopLiOxE2DC4mshL0wwUw0wHExeP2W03tobSZpK8bRmNBe8s7bjUlQ7df2V0dB8W/G10ez3rIJnUGzeXhOjUy/f3T0KFP0wAJzQo1LNjTFfc3XEZq+IyqyIHjxDiN9yG/rsZP8vAZHc/kNCwNtuVjRE1K1hmUckVa5RD1TtATY8c5h2JkIL2pcPgFJvJh9s2CbK2VCfF2U3VUJu0icDqRREqY542/y84aZW7Pz+kIicBI6blwpCA6DxgCJ3fs/pgQYqueF+cJ7UteBEUaDOyvxvttYr02xio/izs7vRJsL0Gpve5WUHIl1+9QTuoDRi8w6l92AtrnNPN9QooOuS6VbrTW9up0nkHkUz3zm7QuXulennCFgO8je81FhxOk=
Y7IpJNU/ne7JUqmOqNPu5gPlCWRDjK6nYKK69K6yxjEu7PT6g1ZkGlrNS2QeQY9bvOuxTlWF8nF+inbQTUQOte6rIX8kSZUJFcOKbxxcWDJiJvPArE5iKCJPpPPHFFH97MVVDYuYT4C5XvR7bDQNVlmxB6oUGvO/71mC4D3gxau1FA24fISRio3aCUVWFjEBlt55PyVhg9qn4r+jBP4bwBVW1NmmjONsIrf6+pvI3gIspYIwzBg9Nflv67aKY7tLdylkcQ7lHx2pY20onaDuT+WGRlQdqc4K0kAzZyTO9FEe/SGe0Nsi0BPMBK1LjFtNJ0KQABLWoTZWSXgloY/2JQvoOC1Z57K4g9R8Nq2BDNpCWADz2nP713zt30dK4Emg6Uh3lkew4zF98atY9Mb5bdNmzQp3dCv9xGAGPm66eIqQUEKDt7zone6j6r81c8jPbl7m/9f/iul8d3a4bPEv5DMHTL23YdFSc6AHDp32ngaB4qLxgtDCvQhnkrayqiYHOlYnS1A/TBS7c2K/Od0nZvuxZKUuDiC7RlSxEAo/EdmWSbeA01cYeh7v0lkdkQaOMGI45DPjLaCwuhgb85cbHcFEDo1CpZSYhyhqApx15/0Jt/CIuFM3FG0cf4B6/6T7Ll7IqGCeTl6nuF2N3ux1ND7UAMBpqjWAlg9/Y7T+aq4=

View File

@@ -1,4 +1,11 @@
import typing
import shutil
import os
import logging
import subprocess
import os.path
logger = logging.getLogger(__name__)
# On older client versions, need importing globally to allow inner functions to work
import subprocess # type: ignore
@@ -20,40 +27,75 @@ if 'sp' not in globals():
# Inject local passed sp into globals for inner functions if not already there
globals()['sp'] = sp # type: ignore # pylint: disable=undefined-variable
def _prepare_rdp_file(theFile: str, port: int, extension: str = '.rdp') -> str:
"""Save RDP file to user's home directory with the given extension and return its path."""
# Replace the address in the RDP file with 127.0.0.1:{port}
# Replace any line starting with "full address:s:" with the desired value
theFile = theFile.format(
address='127.0.0.1:{}'.format(port)
)
logger.info(f'Preparing RDP file with address 127.0.0.1:{port}')
logger.debug(f'RDP file content (forced): {theFile}')
filename = tools.saveTempFile(theFile)
home_dir = os.path.expanduser("~")
base_name = os.path.basename(filename)
dest_filename = os.path.join(home_dir, base_name + extension)
temp_rdp_filename = filename + extension
logger.debug(f'Renaming temp file {filename} to {temp_rdp_filename}')
os.rename(filename, temp_rdp_filename)
logger.debug(f'Moving temp file {temp_rdp_filename} to {dest_filename}')
shutil.move(temp_rdp_filename, dest_filename)
logger.debug(f'RDP file content (forced): {theFile}')
return dest_filename
def _exec_client_with_params(executable: str, params: typing.List[str], unlink_file: typing.Optional[str] = None) -> None:
logger.info(f'Executing {executable} with params: {params}')
tools.addTaskToWait(subprocess.Popen(params))
if unlink_file:
tools.addFileToUnlink(unlink_file)
def exec_udsrdp(udsrdp: str, port: int) -> None:
import subprocess # @Reimport
import os.path
params: typing.List[str] = [os.path.expandvars(i) for i in [udsrdp] + sp['as_new_xfreerdp_params'] + ['/v:127.0.0.1:{}'.format(port)]] # type: ignore
tools.addTaskToWait(subprocess.Popen(params))
logging.debug('UDSRDP client will use command line parameters')
params: typing.List[str] = [os.path.expandvars(i) for i in [app] + sp['as_new_xfreerdp_params'] + [f'/v:127.0.0.1:{port}']] # type: ignore
_exec_client_with_params(udsrdp, params)
def exec_new_xfreerdp(xfreerdp: str, port: int) -> None:
import subprocess # @Reimport
import os.path
if sp.get('as_file', ''): # type: ignore
logger.debug('XFREERDP client will use RDP file')
dest_filename = _prepare_rdp_file(sp['as_file'], port, '.rdp') # type: ignore
params = [xfreerdp, dest_filename, f'/p:{sp.get("password", "")}'] # type: ignore
_exec_client_with_params(xfreerdp, params, unlink_file=dest_filename)
else:
logging.debug('XFREERDP client will use command line parameters')
params: typing.List[str] = [os.path.expandvars(i) for i in [app] + sp['as_new_xfreerdp_params'] + [f'/v:127.0.0.1:{port}']] # type: ignore
_exec_client_with_params(xfreerdp, params)
params: typing.List[str] = [os.path.expandvars(i) for i in [xfreerdp] + sp['as_new_xfreerdp_params'] + ['/v:127.0.0.1:{}'.format(port)]] # type: ignore
tools.addTaskToWait(subprocess.Popen(params))
def exec_thincast(thincast: str, port: int) -> None:
if sp.get('as_file', ''): # type: ignore
logger.debug('Thincast client will use RDP file')
dest_filename = _prepare_rdp_file(sp['as_file'], port, '.rdp') # type: ignore
params = [thincast, dest_filename, f'/p:{sp.get("password", "")}'] # type: ignore
_exec_client_with_params(thincast, params, unlink_file=dest_filename)
else:
logging.debug('Thincast client will use command line parameters')
params: typing.List[str] = [os.path.expandvars(i) for i in [app] + sp['as_new_xfreerdp_params'] + [f'/v:127.0.0.1:{port}']] # type: ignore
_exec_client_with_params(thincast, params)
# Try to locate a xfreerdp and udsrdp. udsrdp will be used if found.
xfreerdp: typing.Optional[str] = tools.findApp('xfreerdp3') or tools.findApp('xfreerdp') or tools.findApp('xfreerdp2')
udsrdp = tools.findApp('udsrdp')
fnc, app = None, None
if xfreerdp:
fnc, app = exec_new_xfreerdp, xfreerdp
if udsrdp:
fnc, app = exec_udsrdp, udsrdp
if app is None or fnc is None:
raise Exception(
'''<p>You need to have xfreerdp (>= 2.0) installed on your systeam, and have it your PATH in order to connect to this UDS service.</p>
<p>Please, install the proper package for your system.</p>
'''
)
# Add thinclast support
thincast_list = [
'/usr/bin/thincast-remote-desktop-client',
'/usr/bin/thincast',
'/opt/thincast/thincast-remote-desktop-client',
'/opt/thincast/thincast',
'/snap/bin/thincast-remote-desktop-client',
'/snap/bin/thincast',
'/snap/bin/thincast-client'
]
thincast_executable = None
for thincast in thincast_list:
if os.path.isfile(thincast) and os.access(thincast, os.X_OK):
thincast_executable = thincast
break
# Open tunnel and connect
fs = forward(remote=(sp['tunHost'], int(sp['tunPort'])), ticket=sp['ticket'], timeout=sp['tunWait'], check_certificate=sp['tunChk']) # type: ignore
@@ -64,4 +106,29 @@ if fs.check() is False:
'<p>Could not connect to tunnel server.</p><p>Please, check your network settings.</p>'
)
fnc(app, fs.server_address[1])
# If thincast exists, use it. If not, continue with UDSRDP/XFREERDP as before
if thincast_executable:
logging.debug('Thincast client found, using it')
#logging.debug(f'RDP file params: {sp.get("as_file", "")}')
fnc, app = exec_thincast, thincast_executable
else:
xfreerdp: typing.Optional[str] = tools.findApp('xfreerdp3') or tools.findApp('xfreerdp') or tools.findApp('xfreerdp2')
udsrdp = tools.findApp('udsrdp')
fnc, app = None, None
if xfreerdp:
fnc, app = exec_new_xfreerdp, xfreerdp
if udsrdp:
fnc, app = exec_udsrdp, udsrdp
if app is None or fnc is None:
raise Exception(
'''<p>You need to have Thincast Remote Desktop Client o xfreerdp (>= 2.0) installed on your system, y tenerlo en tu PATH para conectar con este servicio UDS.</p>
<p>Please install the right package for your system.</p>
<ul>
<li>Thincast: <a href="https://thincast.com/en/products/client">Download</a></li>
<li>xfreerdp: <a href="https://github.com/FreeRDP/FreeRDP">Download</a></li>
</ul>
'''
)
if fnc is not None and app is not None:
fnc(app, fs.server_address[1])

View File

@@ -1 +1 @@
Tqjv2NoZjghIsF/sAHDgawag9vu/A+YEmr59t6HzER8TB13/UqZnCPJpyJrOL643h+ssN51HEoBjj3fAYBlleNjmr1Nkle9/VaFWNhuruRQDMUp41GoGzICkF4dNMWhdEgpZ80xGxcv+0AZzmo4eCZsIAZjMNDYJKq7N/M77I2kl++K79VU9OB/npjArVKUBiROvS5Y6E9dOXhUAFgpO5zKLPPLuFzdLyNnGpIOmu3ei5bG8arVq8S1nF4aBjVXgSP9bZ15VKHgT5vBkOIK0TIKKS+qfP7Hb1+QqxLeV5BKeVn4jLYBj+TZmqtUmIJQG226PdzsFTnjpPH33twdhTFFb8aCyoNma/U+xOOJZNSqouUPQV7KvHQuzYqrLB9Zg1rA9O2cz/sp4seePfrit8dw+H8Wq4dAlvgB05/zfoVMhttjuYuI46C69XrK5M8SFsnyD7faFRwNqZtdeJt7XwYtg1/TUMH6JM4UxTuL49aXjJQ3aOrNA9r6ukNe7lPMEbGBx4mtlGqlg+2ZCbqG1HZRITnUfuHy5GklU2scBGoEgoz6YeguGAgNbiwLohrWYAA1IGZEhSJc7Fx+nn5IRuYJdCof2dO/o0ntGi4pfVDL91sqmafEn/f90A5lBsoFotlbSG67tI7CcWs+QR0P0T6KspBv/TIFSNu/Igs1c0hY=
n3a+b8OD5i8IAbP/fK2c72A1UzRScr7kH2BeY4zSaVJoJf+Xn4+F6Osm0emtS+1muStwHvIE9Hqnxdg8lbYHIrjRAGvFazs5xcExIvYU1rVA/gmBjaViqUl6SQc+3zWa7xXwOVrwQdoXF9WGYOAH8DAPT/aiKgeaCoKlCFNyrxlMImxWPlWRvCKrZQlWxvB6rKFC0apO+xxi4FU3xXIomWBufnLuDppYL+xwVzMj4YNWec3rdV6RT8cpBQzfkNec7rgHBOB+Py2BnY54TtMqlECpXZlEYy4NVORI0rwZ+aK8dvpGvRS4nWQnq3edRkWTzDZAo+Kkp17/fmCTEGucQ/TC4Y2qBoB35kxQmv1aWhWWyJ/BdpHI6lNJR884lK/yup3I+vQbf7ZJzu6wT98YHk0x+rsPt97FPxJsVdsxWG4IcaVgheuIjlYPNShMDoy/slOyEz/k0j7jWAhRu0WWMHjI/ozighX+jHmNeIG9EPim0sKZ/04okSXxhwwC1UzwcevUdrI2eUyaUEbq9Ua3SMCuQNmDVKFlJJ6PUucbWvgKsdsSMbW1L4syA41yTPk9dcYHlu32k8oaZ1o3QIDw7Do48pygwnWSJM9XlynFcRcYmOikRIrINJlJmKH6oY+ez9nva7cAV6rFel+jk7bgSZUrKMfdsnsLh1PSabnLJ/s=

View File

@@ -2,6 +2,9 @@ import typing
import shutil
import os
import os.path
import logging
logger = logging.getLogger(__name__)
# On older client versions, need importing globally to allow inner functions to work
import subprocess # type: ignore
@@ -43,7 +46,7 @@ msrdc_list = [
]
thincast_list = [
'/Applications/Thincast Remote Desktop Client.app/Contents/MacOS/Thincast Remote Desktop Client',
'/Applications/Thincast Remote Desktop Client.app',
]
xfreerdp_list = [
@@ -58,21 +61,28 @@ executable = None
kind = ''
# Check first thincast (better option right now, prefer it)
logger.debug('Searching for Thincast in: %s', thincast_list)
for thincast in thincast_list:
if os.path.isfile(thincast):
if os.path.isdir(thincast):
logger.debug('Thincast found: %s', thincast)
executable = thincast
kind = 'thincast'
break
if not executable:
logger.debug('Searching for xfreerdp in: %s', xfreerdp_list)
found_xfreerdp = False
for xfreerdp_executable in xfreerdp_list:
xfreerdp: str = tools.findApp(xfreerdp_executable)
if xfreerdp and os.path.isfile(xfreerdp):
executable = xfreerdp
xfreerdp = tools.findApp(xfreerdp_executable) # type: ignore
logger.debug('tools.findApp(%s) result: %s', xfreerdp_executable, xfreerdp) # type: ignore
if xfreerdp and os.path.isfile(xfreerdp): # type: ignore
logger.debug('xfreerdp found: %s', xfreerdp) # type: ignore
executable = xfreerdp # type: ignore
# Ensure that the kind is 'xfreerdp' and not 'xfreerdp3' or 'xfreerdp2'
kind = xfreerdp_executable.rstrip('3').rstrip('2')
break
else:
if not found_xfreerdp:
logger.debug('Searching for MSRDC in: %s', msrdc_list)
for msrdc in msrdc_list:
if os.path.isdir(msrdc) and sp['as_file']: # type: ignore
executable = msrdc
@@ -80,6 +90,7 @@ if not executable:
break
if not executable:
logger.debug('No compatible executable found (Thincast, xfreerdp, MSRDC)')
msrd = msrd_li = ''
if sp['as_rdp_url']: # type: ignore
msrd = ', Microsoft Remote Desktop'
@@ -110,26 +121,90 @@ if not executable:
'''
)
logger.debug('Using %s client of kind %s', executable, kind) # type: ignore
if kind == 'msrdc':
theFile = sp['as_file'] # type: ignore
filename = tools.saveTempFile(theFile)
filename = tools.saveTempFile(theFile) # type: ignore
# Rename as .rdp, so open recognizes it
shutil.move(filename, filename + '.rdp')
shutil.move(filename, filename + '.rdp') # type: ignore
# tools.addTaskToWait(subprocess.Popen(['open', filename + '.rdp']))
# Force MSRDP to be used with -a (thanks to Dani Torregrosa @danitorregrosa (https://github.com/danitorregrosa) )
tools.addTaskToWait(
# Force MSRDP to be used with -a (thanks to Dani Torregrosa @danitorregrosa (https://github.com/danitorregrosa))
tools.addTaskToWait( # type: ignore
subprocess.Popen(
[
'open',
'-a',
executable,
filename + '.rdp',
]
] # type: ignore
)
)
tools.addFileToUnlink(filename + '.rdp')
else: # thincast, udsrdp, freerdp
tools.addFileToUnlink(filename + '.rdp') # type: ignore
if kind == 'thincast':
if sp['as_file']: # type: ignore
logger.debug('Opening Thincast with RDP file %s', sp['as_file']) # type: ignore
theFile = sp['as_file'] # type: ignore
filename = tools.saveTempFile(theFile) # type: ignore
# # add to file the encrypted password for RDP
# import win32crypt
# import binascii
# def encrypt_password_rdp(plain_text_password):
# # Convert password to UTF-16-LE (Unicode string used by RDP)
# data = plain_text_password.encode('utf-16-le')
# # Encrypt with DPAPI (CryptProtectData)
# encrypted_data = win32crypt.CryptProtectData(data, None, None, None, None, 0)
# # Convert bytes to hexadecimal for RDP
# encrypted_hex = binascii.hexlify(encrypted_data).decode('ascii')
# return encrypted_hex
# filename_handle = open(filename, 'a') # type: ignore
# if sp.get('password', ''): # type: ignore
# encrypted_password = encrypt_password_rdp(sp["password"])
# filename_handle.write(f'password 51:b:{encrypted_password}\n') # type: ignore
# filename_handle.close()
# add to file the password without encryption (Thincast will encrypt it)
filename_handle = open(filename, 'a') # type: ignore
if sp.get('password', ''): # type: ignore
filename_handle.write(f'password 51:b:{sp["password"]}\n') # type: ignore
filename_handle.close()
# Rename as .rdp, so open recognizes it
shutil.move(filename, filename + '.rdp') # type: ignore
params = [ # type: ignore
'open',
'-a',
executable,
filename + '.rdp', # type: ignore
]
logger.debug('Opening Thincast with RDP file with params: %s', ' '.join(params)) # type: ignore
tools.addTaskToWait( # type: ignore
subprocess.Popen(params) # type: ignore
)
tools.addFileToUnlink(filename + '.rdp') # type: ignore
else:
logger.debug('Opening Thincast with xfreerdp parameters')
# Fix resolution...
try:
xfparms = fix_resolution()
except Exception as e:
xfparms = list(map(lambda x: x.replace('#WIDTH#', '1400').replace('#HEIGHT#', '800'), sp['as_new_xfreerdp_params'])) # type: ignore
params = [ # type: ignore
'open',
'-a',
executable,
'--args',
] + [os.path.expandvars(i) for i in xfparms + ['/v:{}'.format(sp['address'])]] # type: ignore
#logger.debug('Executing: %s', ' '.join(params))
subprocess.Popen(params) # type: ignore
else: # for now, both xfreerdp or udsrdp
# Fix resolution...
try:
xfparms = fix_resolution()
@@ -137,4 +212,5 @@ else: # thincast, udsrdp, freerdp
xfparms = list(map(lambda x: x.replace('#WIDTH#', '1400').replace('#HEIGHT#', '800'), sp['as_new_xfreerdp_params'])) # type: ignore
params = [os.path.expandvars(i) for i in [executable] + xfparms + ['/v:{}'.format(sp['address'])]] # type: ignore
subprocess.Popen(params)
logger.debug('Executing: %s', ' '.join(params)) # type: ignore
subprocess.Popen(params) # type: ignore

View File

@@ -1 +1 @@
kIL9OXr/AnhhqE+5ln8YpmUhgCDxK1r7yuHjb5j+n1VJOLHtJwH0Rm5Dh5SrhNkCFo2suHTVb0f1nbkQ4mkzlszBNaCSvLkwxpTIZrO8P9aOpEJDmq5aGRHNHLPNzmhBzUfhK+ILGlsCElJzP/19kNxifoanH9xyZkybHAuxNy4QjRt8dkIZkxj0qxsRFRAt8R2yZARdjUkfufiztNAdTuHLVUG+JF5OnTQDJi6vIqOrbPiAIn/vweHuL4zFH4UJXHipvgXJiO9nq4ZvXCAy9+ASxEDG5ql/iwOFoQyGekXK5XtkSbT9F+fDejVoxKP/qp4lIrEHQ/Y9WMJnpGBt2ko7FeyDP8msx45svLQYBNeqxGqIUi0yzfzktglLfexInisHBtz0lb7Uuz/mdPscUoKU+j/5/ZgFS0yFD+NelFmG1q52X7ndkXUXaLb3tPtNDd1ZdgsyEkTiVuo3jW+kT1SWlAeK+YEyZinC8/Df7PLCX7fOl+WPm8MAnncfmbHO654HHgYiqDh+I93IqqReytepxWgK+bwxdGz/z0BPzLXSCy2im6UjEdNFSFRBEywXAdMTxgC2YD2XOl7SHAtcPT2kpgJtlxT1V7+wwv2tyzWf0bL0GA0EIDEAP6cga+l4wJGSIDnYnxiJHaF6aF3XYL8yh4ghHTD7IAJFpU6vWjM=
rP6Eur9PlTONUNLjIRAVL/CdtT7ATNYC8l0AzvU57tqyFDFa/C8nNyq3Aaepf+SSYaYzxg9TnWUge8jpcnM20ERV6H2IA2aN3Hrg0+q76OPNlH1UmygyT1+UxxccPemnGAcVVnBXOHwONHvpE8FqdOFZn6P2CWWojOLUMB2yj/kO0l+bZDmDRlihg5sIpSd4Wkt4ezyz9j7Cjsz6JuFDQjVdaIDEFeGcqfEJIDKlpIY6GJgJYbGMx0C0uayNtQlFO653EcS7mnXhlIQwGg4YJl3fjKksjDWL2H65MsddRvZubIIrBU6jQnIj2W+gl1/xT8mRom48SogBJWzjzjT/X7sN6QRfvKCMfLwhfqHw7p0MYVV1Tcpjzn1sFMyrR4zPXGaH80+2hn9yf2HGVb6QVmir0x0VKRy0eQAEqYtb3TeMU0lmXShkuSogiOfdpqd65NKpboUuv/cVttpa8qzZhroBQXyufSEi1gmVTc6tp2PeQIXFZLrL6SOP263HXOWPirmIuLri8k3qK2L4BiuD7ZTiwursqCytoFjBCVpWPhnI3c6Q81WpzESBs6E8Kyvanr/jMX4T95i9m/kZBdLELLA7uj2dTaxsdUHJrs1fO7/hMGdgxdmWzXwXJX9VzJ+ZyF69KP0w4oZd+bazxFK0aaHqttxS2ZjATJ5rlOARtzs=

View File

@@ -3,6 +3,9 @@ import typing
import shutil
import os
import os.path
import logging
logger = logging.getLogger(__name__)
# On older client versions, need importing globally to allow inner functions to work
import subprocess # type: ignore
@@ -50,7 +53,7 @@ msrdc_list = [
]
thincast_list = [
'/Applications/Thincast Remote Desktop Client.app/Contents/MacOS/Thincast Remote Desktop Client',
'/Applications/Thincast Remote Desktop Client.app',
]
xfreerdp_list = [
@@ -65,25 +68,31 @@ executable = None
kind = ''
# Check first thincast (better option right now, prefer it)
logger.debug('Searching for Thincast in: %s', thincast_list)
for thincast in thincast_list:
if os.path.isfile(thincast):
if os.path.isdir(thincast):
executable = thincast
kind = 'thincast'
logger.debug('Found Thincast client at %s', thincast)
break
if not executable:
logger.debug('Searching for xfreerdp in: %s', xfreerdp_list)
for xfreerdp_executable in xfreerdp_list:
xfreerdp: str = tools.findApp(xfreerdp_executable)
if xfreerdp and os.path.isfile(xfreerdp):
executable = xfreerdp
xfreerdp: str = tools.findApp(xfreerdp_executable) # type: ignore
if xfreerdp and os.path.isfile(xfreerdp): # type: ignore
executable = xfreerdp # type: ignore
# Ensure that the kind is 'xfreerdp' and not 'xfreerdp3' or 'xfreerdp2'
kind = xfreerdp_executable.rstrip('3').rstrip('2')
logger.debug('Found xfreerdp client: %s (kind: %s)', xfreerdp, kind) # type: ignore
break
else:
logger.debug('Searching for Microsoft Remote Desktop in: %s', msrdc_list)
for msrdc in msrdc_list:
if os.path.isdir(msrdc) and sp['as_file']: # type: ignore
executable = msrdc
kind = 'msrdc'
logger.debug('Found Microsoft Remote Desktop client at %s', msrdc)
break
if not executable:
@@ -91,6 +100,7 @@ if not executable:
if sp['as_rdp_url']: # type: ignore
msrd = ', Microsoft Remote Desktop'
msrd_li = '<li><p><b>{}</b> from Apple Store</p></li>'.format(msrd)
logger.debug('as_rdp_url is set, will suggest Microsoft Remote Desktop')
raise Exception(
f'''<p><b>xfreerdp{msrd} or thincast client not found</b></p>
@@ -119,38 +129,90 @@ if not executable:
# Open tunnel
fs = forward(remote=(sp['tunHost'], int(sp['tunPort'])), ticket=sp['ticket'], timeout=sp['tunWait'], check_certificate=sp['tunChk']) # type: ignore
address = '127.0.0.1:{}'.format(fs.server_address[1])
address = '127.0.0.1:{}'.format(fs.server_address[1]) # type: ignore
# Check that tunnel works..
if fs.check() is False:
if fs.check() is False: # type: ignore
logger.debug('Tunnel check failed, could not connect to tunnel server')
raise Exception('<p>Could not connect to tunnel server.</p><p>Please, check your network settings.</p>')
else:
logger.debug('Tunnel check succeeded, connection to tunnel server established')
logger.debug('Using %s client of kind %s', executable, kind) # type: ignore
if kind == 'msrdc':
theFile = theFile = sp['as_file'].format(address=address) # type: ignore
filename = tools.saveTempFile(theFile)
filename = tools.saveTempFile(theFile) # type: ignore
# Rename as .rdp, so open recognizes it
shutil.move(filename, filename + '.rdp')
shutil.move(filename, filename + '.rdp') # type: ignore
# tools.addTaskToWait(subprocess.Popen(['open', filename + '.rdp']))
# Force MSRDP to be used with -a (thanks to Dani Torregrosa @danitorregrosa (https://github.com/danitorregrosa) )
tools.addTaskToWait(
tools.addTaskToWait( # type: ignore
subprocess.Popen(
[
'open',
'-a',
executable,
filename + '.rdp',
]
)
] # type: ignore
)
)
tools.addFileToUnlink(filename + '.rdp')
else: # freerdp, thincast or udsrdp
tools.addFileToUnlink(filename + '.rdp') # type: ignore
if kind == 'thincast':
if sp['as_file']: # type: ignore
logger.debug('Opening Thincast with RDP file %s', sp['as_file']) # type: ignore
theFile = sp['as_file'] # type: ignore
theFile = theFile.format( # type: ignore
address='{}'.format(address)
)
filename = tools.saveTempFile(theFile) # type: ignore
# filename_handle = open(filename, 'a') # type: ignore
# if sp.get('password', ''): # type: ignore
# filename_handle.write(f'password 51:b:{sp["password"]}\n') # type: ignore
# filename_handle.close()
# Rename as .rdp, so open recognizes it
shutil.move(filename, filename + '.rdp') # type: ignore
# show filename content in log for debug
with open(filename + '.rdp', 'r') as f: # type: ignore
logger.debug('RDP file content:\n%s', f.read()) # type: ignore
params = [ # type: ignore
'open',
'-a',
executable,
filename + '.rdp', # type: ignore
]
logger.debug('Opening Thincast with RDP file with params: %s', ' '.join(params)) # type: ignore
tools.addTaskToWait( # type: ignore
subprocess.Popen(params) # type: ignore
)
tools.addFileToUnlink(filename + '.rdp') # type: ignore
else:
logger.debug('Opening Thincast with xfreerdp parameters')
# Fix resolution...
try:
xfparms = fix_resolution()
except Exception as e:
xfparms = list(map(lambda x: x.replace('#WIDTH#', '1400').replace('#HEIGHT#', '800'), sp['as_new_xfreerdp_params'])) # type: ignore
params = [ # type: ignore
'open',
'-a',
executable,
'--args',
] + [os.path.expandvars(i) for i in xfparms + ['/v:{}'.format(address)]] # type: ignore
#logger.debug('Executing: %s', ' '.join(params))
subprocess.Popen(params) # type: ignore
else: # freerdp or udsrdp
# Fix resolution...
try:
xfparms = fix_resolution()
except Exception as e:
xfparms = list(map(lambda x: x.replace('#WIDTH#', '1400').replace('#HEIGHT#', '800'), sp['as_new_xfreerdp_params'])) # type: ignore
params = [os.path.expandvars(i) for i in [executable] + xfparms + ['/v:{}'.format(address)]]
subprocess.Popen(params)
params = [os.path.expandvars(i) for i in [executable] + xfparms + ['/v:{}'.format(address)]] # type: ignore
subprocess.Popen(params) # type: ignore

View File

@@ -1 +1 @@
fLDRyAJrjER+znvjbhAuUa+XJ0itaLRspgQzk5AKMz/YpNZgdaHBtOw25XUsYrszsjSq6AIPB2VnT45Bhg7/GB8CRpTuCkpeoDpUA2rNR24DW7i5urmhjfPjtuMmSOBKPdXkiYPIhavFeD+kRWrrm6X1lolTnGz+pie2IXxsEhsot4gbg6eOv1ieloRHnTptO2qRr99I35BmSTxCKnyhrV6AWJBb6lHhuT04fhk5+X/ZA4kQewaI0ncqBdsqZoxcdSDgiOllF8kUsDgBL91wpYAB9s5eQpnI5VUPY/7Gzd47guRNCf24EHswd1lJrGAGta1p/e3hvtU0whdaaAiuhG/U6zHhpDpE5SPV66MuPfo1kKyIeNTMTC50Dkahu+j19cVbubdSJIUj42+nXKHDy5Bojotd/IYYSSR68K0eblpHDId7YxXpYmKY2QyA2cWDUrGD4glRWinKR/Hoi1oNuuBItMQUcDCPxxPWks4OsYqwpbAAkdFYRXzUR18TKndtwmHKox5AfXuQkuHpICRRnEMzD5pfNerUtEk3Zy0BHZYtoD7BQu2OEh9e3jC5bRHaQvf/sGBGMWfKchBwtPiBKlji6nPw6pqLPW5npD3GHYkcxI8tgzlTzIZS6YkOXDI8lHMYV5lvHxOPYFrAecfNpK8pj2uAeGoEFvBRmZZG79g=
NKfVtsTQWErfjpdrXd7qGLp6c0ScKMcbj4o5QTDAfQfDEuuj1/Qcg9gx+1NCyIF0hxy9ZIKbvwaYBYu9rARxz3XYsidxpgsnhZPyshdPN236M+zRo9SBFY3Ug0aNBZSewSZ6MSfrCZkMUW0NJOOpGu41KQNUVE5+DciC618rMoD0V//zJhz4SFy7dscjLg8cm69KNS6jC1trJkX7Ep19TF5DG6s0P9lQGaMLSj2UYTsF5gaZZY1jwZcCSw0QGrXon/mxR4i/t3BiARekyUB/ygM5DfzG5BMnQIMtLPNZu89vdJ+vAU5bUq1MrQjpAI3dzhdJwiUWTSVbQ3rHAoQHyBL4yE8bvuLIB0sUD5Jqd3EkTlVBJ0mmo1dHJZj0dzbXd5cHDlK+0Ms+vDSm/Qw8rqKatSBZlavcdQ+ScIEHlj0bEf6teKD3VsMUKFDxpdIBivkSz0KIZW+HXQiiFprdeN5tnAVj8ItlxFZlItNw3Zz6CrNqHpfSdyW0uzmuN3mD1iyQhkW6+JzWC+G9hmMY9X7eFv/xzwXQHUiB8rrt5QK7PJHlK1uRC2MFQRg7s83bYeZBEhAynuyjwLAT6mo+RsINgqF3hkNUHfh7E4IvK09ynl5Dnv3ypAfYKGvlWbpH8Moo+Pp45r+8sQPa83br9+y52I8JxDxqkdHLrzmXqc4=

View File

@@ -27,7 +27,7 @@ theFile = sp['xf'].format(export=home, keyFile=keyFile.replace('\\', '/'), ip='1
filename = tools.saveTempFile(theFile)
x2goPath = os.environ['PROGRAMFILES(X86)'] + '\\x2goclient'
executable = tools.findApp('x2goclient.exe', [x2goPath])
executable = tools.findApp('x2goclient.exe', x2goPath)
if executable is None:
raise Exception(
'''<p>You must have installed latest X2GO Client in default program file folder in order to connect to this UDS service.</p>

View File

@@ -1 +1 @@
NzheeXAbv2kVktgPeHMrFR+fkSjoz4NcPHrfrMXKP4BGS8oeBO8CBJw0OFYqXIRKCsBPi6yvd961Ofg2UFi6brr4dCut6PfXddKEJIEbanwTUr3yx6882TOw95h3ibLkIP9Fi/6VmId8X3QnPqdmN8XgU2eB7ONrvFzu9aCcMuKj2rKLXlesJ1t/ArxRn80xrCKp/27jDX1aitcTHzo9VsM9bjBNodHp6V+/hHiw+Ht7qI9MZ1a2jQxA63CR5uqsAJ5B4qcp/fn4TBXgPr2NxL1StT19v3e36YuCKhSUVS8b3ggDrGU982m5rfcbn9Gz3yaaYNcHmf2600AdMFGsjiBmiv65Tjc1utC81rsmbsomsSBj48IZ0ZoYXgZkSHDYzRU7xVA2QAYpLI1IM8tputACN1L5vcNQXl6SqrJc3+3PidOmUyxrQxjNDc6wlfsTOH0UhgT8FPxJu9NFPU/eYq3w3hPsvowg4MewjA4X8NBtqpus76Jyux2e8ym3WJHvlAff04VrwEeidaci591xbuu62ezTfACXtSOK5mOAgUWhCAHWE3/U32ibtsw5SER4u5ywk2nVGlvFY3+5x6RxoGN4FhsYwfumSHO54caonHyBVkkTK1FRbYJ6zNXMf37491YOrdqAFX7R205d4qcJkqLldo/DaYO10CSSiZ323x4=
TpOuQbtplQOuMXpTtxaAQJwZJtoVTMtWruV9d8+d6HJnSd7Y7iLMWkqerP3+rQBOqT67YAI8fq8ZZsYSDNKCoYQwJXqp8d2S8K2LyW7ClnYZXlFzVvjW0+wgTVV4E3f5Y7xT5TGlQ7/mO/KPgEloTxC8KD/DOTRc6MBusSgc/nd1ssE946qznCvihTsWuM4KGTfhQ4q9BVIVaxcFRszmhPqjVT0iY8sF3Aqghx4hvbgZKglqF9o/1JemzffLn/pxyclIePFJTPZlIzecfYcQ7Gsa+BpLbryRr6LUmuZLhBZYTnRP0dCrwMjfnitVSspwo0MJX4bDa1rQNPsQZLylrzNqbjA/aCf60qLit58KA2i7Vb/BfE3lPtoXKKILDEzOFqZObfRDDNQpiP8xCyJVXb5f3oVYYtRbuk/Ld1pNtsmTsEw69kQ30pW5Mqw+Qy5/6Nf75dAm1/9/FJga1uVBhlgPG0qgSf96M1s8FlSUrC5gn9neJB3ThOBpJkDRqvER192dwVlwcSbGLzgEGdZLFZq4cskfdsJHdWQ5aTcbnEBPrWRbvKbM1c2iuXXkLiKAAcY1rFmPWZscWbj8HqjFHwDd4bUjKzUA2GZt0LbhLG0ujQAK49Xdx7rHR15RL9hFFRwH4gUKQj/zuHCNkNXcW4bMmL1ZYWQXVW/X8Tv+LtQ=