1
0
mirror of https://github.com/dkmstr/openuds.git synced 2025-10-16 23:33:45 +03:00

Merge remote-tracking branch 'origin/dev/andres/v4.0' into dev/janier/v4.0

This commit is contained in:
Janier Rodríguez
2025-09-15 12:07:06 +02:00
10 changed files with 163 additions and 54 deletions

View File

@@ -99,6 +99,7 @@ class RDPTransport(BaseRDPTransport):
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,
@@ -184,7 +185,7 @@ class RDPTransport(BaseRDPTransport):
{
'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

@@ -341,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='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 or linux, add customParameters
if self.target == types.os.KnownOS.WINDOWS or self.target == types.os.KnownOS.LINUX:
# 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

@@ -116,6 +116,7 @@ class TRDPTransport(BaseRDPTransport):
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:
@@ -214,11 +215,14 @@ class TRDPTransport(BaseRDPTransport):
}
)
elif os.os == types.os.KnownOS.MAC_OS:
r.custom_parameters = self.mac_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.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

@@ -3,7 +3,6 @@ import shutil
import os
import logging
import subprocess
import re
import os.path
logger = logging.getLogger(__name__)

View File

@@ -1 +1 @@
Lvoxe6lblShKwupfG/wNpHADmNdtyvBU3Jk5h9Wvvk2H6Y9wOgE032YAEJMlGZuRB0gJJDYPtX1enisrYKILEjyoYoOoTcvr8eUssUZVvxyag1ePUAP2egW7nBibksvqFUeb+/IcI7DP34N1DJoOQDHxPnuEgYonPeVVB4qPTqTXV3622k+6LuwxExXmPEUC4hmUzC2rmSa8+t45KykRAJx3fOZcmKBJgs2dWfnTQ0qyWy1k96rOvu5ZZX2zsxhZB38kPqatKGFS6q3fR7F/h7/oV7AJS2vpf9uiTu7O6XToRpjcYkVZMGJVz7r7OEcdTuIq2Bqm2KQb6NX/ot1wAXCUDa5lH7xqQ50BU3bqZHry9U3kPpID/A6lRuq2k3dIgKo5TCeNrK3t2vQOwOAjIfpCQPV3J+jh6HxPxj7ZHnW9frZ5VJ7VYXo+BoSCnwsoKXF0DGh8NojPd/ELedmrAI14K8UiYGr08l0JECchW5sngk5ToIqoriI0RJNzrC21212xrUJTOVu6RyOxfRUix5SXiCksR/Bf/gGnF3YNdxYnFbQfeCr9xTxLtJEtVmhO6ffPgi7quUHKX/NXWbmMk5zubdx1g6k3RNkbh6yabBL2vhG4/Sh72rzxyVqAGO5MUYmmV/BMyh0ed6PT5idNgiXrKrzvYw5MkWdVpjZq35k=
rHy0885L4XPOrWLCKgn/A+7LYfPpuymRo5Pq3zcDKpW9lUmBdBryTJAGYuaEDiJWq3+Lp0y3W5GIhE11xHtR/XpF70GW/GW7YGRgbDvYlvDz06jNoQWi6tzFnAsA+cvcVPd18PLaC6rDWJpx55PEa8Aq+kqZeGIjiLBRFeTMiv4BXGz60ba9vNNfn+dCHbqOXjXk+zOKHkaGE3hHz3CIrb3IkLYI0KfEODgySZs39yNqfcnRmFgdnH3sM+V09ZUm5hJNgP41Nc8UJgwidBwQXnZXMk0VQMv5BBX9NWdnwuO/nI0JKanpnWd0QLQ991i4TDjWDkchXVWAIwune7djgKy9b0yVkfWYxykJXT2EEzBtSGqm4NETj4q3BIzHJNSaL2HZkPSY9/5olTCaPZY5JfmUPVNJK8wSMzSTPeinn1NkGfSQDgcc28yu6M+/xSrQV6XB9kt9k5Q0TacXfONr5q5F4ReDqasD/EPLJdcVc0f5DI+t7+/no+xNk+f0NrfmjvTNKTqCmTBaJXC+gX72rzTQslxyFokIPJN+luLYasZureRFHCnXlrQn579B4eLQ1+QcPVFKU0SZChkbNbBgpTABkAI3j9Kg41NLp5f1/Sx28ACTW3cSRJbNQKe3s5XXCCn1Fg5XC49MinrSwNk34HfafgQ+X/G1HNqeydtHn1M=

View File

@@ -46,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 = [
@@ -63,7 +63,7 @@ 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'
@@ -73,11 +73,11 @@ if not executable:
logger.debug('Searching for xfreerdp in: %s', xfreerdp_list)
found_xfreerdp = False
for xfreerdp_executable in xfreerdp_list:
xfreerdp = tools.findApp(xfreerdp_executable)
logger.debug('tools.findApp(%s) result: %s', xfreerdp_executable, xfreerdp)
if xfreerdp and os.path.isfile(xfreerdp):
logger.debug('xfreerdp found: %s', 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
@@ -121,35 +121,90 @@ if not executable:
'''
)
logger.debug('Using %s client of kind %s', executable, kind)
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(
tools.addTaskToWait( # type: ignore
subprocess.Popen(
[
'open',
'-a',
executable,
filename + '.rdp',
]
] # type: ignore
)
)
tools.addFileToUnlink(filename + '.rdp')
tools.addFileToUnlink(filename + '.rdp') # type: ignore
# if kind == 'thincast':
# theFile = sp['as_file'] # type: ignore
# filename = tools.saveTempFile(theFile)
# shutil.move(filename, filename + '.rdp')
# subprocess.Popen([executable, filename + '.rdp'])
# tools.addFileToUnlink(filename + '.rdp')
else: # for now, both xfreerdp and thincast or udsrdp
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()
@@ -157,4 +212,5 @@ else: # for now, both xfreerdp and thincast or udsrdp
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 @@
X456AmHbWx+XU0ekt8toTGHb3hWXmNGKZVH3UEjwFbu0U11IRLrWxsNNafq0iTku/Hejlu6qPEOaNUcA665RANlEkM0UrXYkXHUlOZ/ogArhSRvgHXiuEZuPNInI9MUOVNizLbh6sFT8FkrZoiOSYbudXILkId80r+6YrNXey2jsKLwCcmUk6RuFoJQDEH573fgTDSVwJ1ccAIgTQgcVQxwEpoCmRMvuZWZKYYd9f/tTywryVv0ASElLrjTRdSy6unMh5vdJoz8Mtsw+zfUzljcYlU7S/g5zzubRozPZUL/l3k5VmhyTNS+o6gcv95Q2QPEqhUINOJ5jLYp2yXFZ0i4ey8SFElI/AXBlt2eqmCWhucLrbfg4geuxNoxCQ26GTy+G6rdJa0iHIUNckr1zRH+Ej9Pm2Pw6orxtUryKhcjNyEolj7Rp9cg9rRUlkpwr6UsRNB+xuria/3Da/U821yObHu9eTQkN9b4zfhYQHHShWTZ6UtK4MW/ng9bw+2ogQJcS2yy2dHPPVrskQi/fYUsYmN+ssZFciB4KifO3AmM7dBFQFNx5UvssfoTq07258l+VF2H9Dhto7nvaj81FrJkj3XZiad0rx+8QPIWN8wTtEi+FsgROwL6nQ/Ou2D1vrK4iTvSPE1BMoitzu7hwMyYq7Zj/EA9agLeg6I+e6bA=
Drn6UWH67We3++pXcLo6HZCH2Zmh75WH7DipAgZ7O5vcvadlaiFQJEsd4fV+EJWfk2ztemR0qiCTWftJBiO++jm5UzAQlpmKUIt3IZ2/HNHxFojF69szOahgZhNN+kAiDjm/sEryLmuUn3cH5lc0mqMja0nV82DYX0jhelV+1YLJqb33ZspwgqCMGoThSwASc8JgmAIAyVLIJl58VVPsS3dnMldq7ZRw3Zy5EvoLpswc/2cCeZ2VKe2Q8TmLqmQhbl8lKrppnBF+7iekcfiduyHJRLCSzpv9MuHftWQuUmHCSj5JCDYaF+JUVxbHQJg8LsN+A+Q015iUbuEvo/86rbi4VZ4izCUXzG77P3GcbkN58i6JT7bJ+cOvCXvB/WggkHzz1NydOnu6E7sGJYx6bSwIMnvHhMDx45XWqdaVZ9FcwF7CEZUt9F6jVxKVcxCtsYOcnLzfhaYR7PDNnWzgSzKPp1zKcumB2GsDqcQ/nq4y72bJb/qksIeYIq4Zdp39p0M1NcsEOJyg0085twkfZ48eh1pKXFVaPIo/saYmKCbeFjfAi8v8Rw6gdzTCEweZe9kBgPfTFgqrR+uyC/hYX70+KO+yRFeJHp7+zPuzZIM0Yg+Y+xWOM0IHsn7n8DQcJ1LZ96KZnhhCWoYtf4xblabF6DCMwPDCKGUg6GbK7jI=

View File

@@ -53,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 = [
@@ -70,7 +70,7 @@ 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)
@@ -79,12 +79,12 @@ for thincast in thincast_list:
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)
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)
@@ -129,44 +129,84 @@ 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)
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')
tools.addFileToUnlink(filename + '.rdp') # type: ignore
# if kind == 'thincast':
# theFile = sp['as_file'] # type: ignore
# filename = tools.saveTempFile(theFile)
# shutil.move(filename, filename + '.rdp')
# subprocess.Popen([executable, filename + '.rdp'])
# tools.addFileToUnlink(filename + '.rdp')
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:
@@ -174,5 +214,5 @@ else: # freerdp or udsrdp
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 @@
bOvHuyDmq7zRZ4mQ9E6z/MmZioJBL1fgqPHQJXL7krDB8r5qvyItc0uxi18ZBdKUIlrVeezj0UE5F+f7PlY33GNfwW1cOwRsfGcmQ+xQbQWDA5Lg9gnu3Xqka21WU1FTr1K1j87k9wexGmmKZubqCMqSZzr2Z6gb3MWmV7dRmKN1bocyxG0Rwro5Onsrm1GW6hKTn7mU47oIOf3AnkS8pIUHbSP7L4uMMS7LG7LTG7nQRDhpY5QGfge5yf5MSiypDossZQlaUbtAcGDzn+tQ44PDapL8mr8cdmoJvavdOLbfWMOZWuvktr4//t49OV+FhdbocgYLSacvldiYrjHKcyuLyYjRVueFDYDlaaxBKQN8YPxLyBjakKVKaiVMefUKs90w/wloz2zEra7OhIX+pN1FMlh70tk6SCDJ/fAh9Z1mTQXrAxlyyjvXUOZrGjikc5heof59PklbMxjd57JphH+f1KNvsTywZG2wjOIPexxnnXv8PcXzaQKT6/Q9Y43AmNW1tKrbb1J3FGa09XOti6jLqdGeTeYNPeyCIa1DLh8Yi395etsrY7hIilk/uzRJx6D+FU8o9ToHjUaf3d+CrPaHjHvqdjJc3D71NmTM4A+yi7zFlOE0WsxK4D+ylfcO52VBEkI5kpj50WcQughr5c1JlRZWbR+uyhLdMgHe6W0=
cIpmOlAR05XU+3jH6VRkb4MzSBJ4PS6llBWrBtnCudx2KRx3ScBMMdU147G5g2aMwTx6KfNLFKUohhfXpFaqKbk+a6ulmiA1nQ9kfjsXBUqQ+7jj2RHeakrTbSkMB3LiyQGmVJUczt70LEsBxcQPwmK7vKhGamWOke3v7dA8+NxTxL7jTtyIeVVywsM7ciY+VhAFrcYvCUzXcdgmxVWAUlXi5nmzvk3HiLP9F+Jz2fEFDVgWw+LaiaRqyykDXCmKsA13ZNapksEco5SJ9oBEn0vL/8kyWGVmQSwam5S/p/5cxo4zdfX1RrISuGaMiIRCnqFROS+tQbAuEKL/Iss6LPHRdClF3zmGFoi6zLjnt7nsXXAjNBXyS9U1wmMjpxmelkh0+pfBzAHVdBCf56d2Jn78td+LovqbwZbWBNvbpfoCfR7A9Yjf8zm2ViYZm7fH684pAmGho4pjxsIGEqRQEYOpIWLo1mBBiSoyAKgtEtVFLz2blqWA0ktB+JYBq4tV2IToHbL88msfd5AnTGd5NgT+UipFw/64ef2QoUrgmNgMZrhqbiUmZKbzgVEdv4oNxf+pCiBPLlZW7gL0F16o1mM3HhdReA79KOD/w7dzUWKFiRRWK075+vhCEXGLkba4o+gB1SNObTGLLHScD6UzvvlA3BtG0wD/ROkLfCD2Sno=