From ef302205bcf888d1486e5c164e2b749de45e43ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20G=C3=B3mez=20Garc=C3=ADa?= Date: Thu, 23 Apr 2015 06:32:03 +0200 Subject: [PATCH] some cleanups --- actors/linux/buildrpm.sh | 5 +- actors/linux/debian/changelog | 6 + actors/linux/debian/files | 6 +- client/linux/build-packages.sh | 3 +- server/src/.gitignore | 1 + server/src/uds/REST/methods/connection.py | 7 +- server/src/uds/static/client/.gitignore | 2 - server/src/uds/static/client/.placeholder | 0 server/src/uds/transports/NX/web.py | 100 ----------- .../src/uds/transports/X2GO/X2GOTransport.py | 164 ------------------ server/src/uds/transports/X2GO/__init__.py | 45 ----- server/src/uds/transports/X2GO/x2go.png | Bin 7832 -> 0 bytes 12 files changed, 18 insertions(+), 321 deletions(-) delete mode 100644 server/src/uds/static/client/.gitignore delete mode 100644 server/src/uds/static/client/.placeholder delete mode 100644 server/src/uds/transports/NX/web.py delete mode 100644 server/src/uds/transports/X2GO/X2GOTransport.py delete mode 100644 server/src/uds/transports/X2GO/__init__.py delete mode 100644 server/src/uds/transports/X2GO/x2go.png diff --git a/actors/linux/buildrpm.sh b/actors/linux/buildrpm.sh index 60c98e4e..4447ea17 100755 --- a/actors/linux/buildrpm.sh +++ b/actors/linux/buildrpm.sh @@ -1,6 +1,6 @@ #!/bin/bash -VERSION=1.7.0 +VERSION=1.7.5 RELEASE=1 top=`pwd` @@ -11,6 +11,7 @@ cat udsactor-template.spec | # Now fix dependencies for opensuse cat udsactor-template.spec | + sed -e s/"version 1.7.0"/"version ${VERSION}"/g | sed -e s/"name udsactor"/"name udsactor-opensuse"/g | sed -e s/"PyQt4"/"python-qt4"/g | sed -e s/"libXScrnSaver"/"libXss1"/g > udsactor-opensuse-$VERSION.spec @@ -27,4 +28,4 @@ for pkg in udsactor-$VERSION.spec udsactor-opensuse-$VERSION.spec; do rpmbuild -v -bb --clean --buildroot=$top/rpm/BUILD/$pkg-root --target noarch $pkg 2>&1 done -#rm udsactor-$VERSION \ No newline at end of file +#rm udsactor-$VERSION diff --git a/actors/linux/debian/changelog b/actors/linux/debian/changelog index 41d6e92b..70144d7a 100644 --- a/actors/linux/debian/changelog +++ b/actors/linux/debian/changelog @@ -1,3 +1,9 @@ +udsactor (1.7.5) stable; urgency=medium + + * Upgrade for 1.7.5 + + -- Adolfo Gómez García Thu, 23 Apr 2015 06:08:53 +0200 + udsactor (1.7.0) stable; urgency=medium * Initial release. diff --git a/actors/linux/debian/files b/actors/linux/debian/files index e7f905ee..482983d3 100644 --- a/actors/linux/debian/files +++ b/actors/linux/debian/files @@ -1,3 +1,3 @@ -udsactor_1.7.0_all.deb admin optional -udsactor-xrdp_1.7.0_all.deb x11 optional -udsactor-nx_1.7.0_all.deb x11 optional +udsactor_1.7.5_all.deb admin optional +udsactor-xrdp_1.7.5_all.deb x11 optional +udsactor-nx_1.7.5_all.deb x11 optional diff --git a/client/linux/build-packages.sh b/client/linux/build-packages.sh index 8e40e35b..567a84ee 100755 --- a/client/linux/build-packages.sh +++ b/client/linux/build-packages.sh @@ -14,12 +14,13 @@ cat udsclient-template.spec | # Now fix dependencies for opensuse cat udsclient-template.spec | + sed -e s/"version 1.7.0"/"version ${VERSION}"/g | sed -e s/"name udsclient"/"name udsclient-opensuse"/g | sed -e s/"PyQt4"/"python-qt4"/g | sed -e s/"libXScrnSaver"/"libXss1"/g > udsclient-opensuse-$VERSION.spec -# Right now, udsactor-xrdp-1.7.0.spec is not needed +# Right now, udsactor-xrdp-.spec is not needed for pkg in udsclient-$VERSION.spec udsclient-opensuse-$VERSION.spec; do rm -rf rpm diff --git a/server/src/.gitignore b/server/src/.gitignore index 372169bd..a088ded4 100644 --- a/server/src/.gitignore +++ b/server/src/.gitignore @@ -1,3 +1,4 @@ /log/ /static/ .coverage +/uds/static/clients/ diff --git a/server/src/uds/REST/methods/connection.py b/server/src/uds/REST/methods/connection.py index dee1b50a..0d83723d 100644 --- a/server/src/uds/REST/methods/connection.py +++ b/server/src/uds/REST/methods/connection.py @@ -86,9 +86,9 @@ class Connection(Handler): # Skip maintenance services... trans = [] for t in svr.transports.all().order_by('priority'): - typeTrans = t.getType() if t.validForIp(self._request.ip) and t.getType().providesConnetionInfo(): - trans.append({'id': t.uuid, 'name': t.name, 'needsJava': t.getType().needsJava}) + trans.append({'id': t.uuid, 'name': t.name}) + services.append({'id': 'A' + svr.uuid, 'name': svr['name'], 'transports': trans, @@ -102,8 +102,7 @@ class Connection(Handler): trans = [] for t in svr.transports.all().order_by('priority'): if t.validForIp(self._request.ip) and t.getType().providesConnetionInfo(): - typeTrans = t.getType() - trans.append({'id': t.uuid, 'name': t.name, 'needsJava': typeTrans.needsJava}) + trans.append({'id': t.uuid, 'name': t.name}) # Locate if user service has any already assigned user service for this ads = UserServiceManager.manager().getExistingAssignationForUser(svr, self._user) diff --git a/server/src/uds/static/client/.gitignore b/server/src/uds/static/client/.gitignore deleted file mode 100644 index 61643f7e..00000000 --- a/server/src/uds/static/client/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -UDSClient.dmg -UDSClientSetup.exe diff --git a/server/src/uds/static/client/.placeholder b/server/src/uds/static/client/.placeholder deleted file mode 100644 index e69de29b..00000000 diff --git a/server/src/uds/transports/NX/web.py b/server/src/uds/transports/NX/web.py deleted file mode 100644 index f25ea368..00000000 --- a/server/src/uds/transports/NX/web.py +++ /dev/null @@ -1,100 +0,0 @@ -# -*- 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 as _ -from django.core.urlresolvers import reverse -from uds.core.util import OsDetector -from uds.core.util.Config import Config -import logging, os, sys - -logger = logging.getLogger(__name__) - - -def simpleScrambler(data): - ''' - Simple scrambler so password are not seen at source page - ''' - res = [] - n = ord('M') - pos = 0 - for c in data: - res.append(chr(ord(c) ^ n)) - n = n ^ pos - pos = pos + 1 - return "".join(res).encode('hex') - - -def generateHtmlForNX(transport, idUserService, idTransport, ip, os, user, password, extra): - isMac = os['OS'] == OsDetector.Macintosh - applet = reverse('uds.web.views.transcomp', kwargs={ 'idTransport' : idTransport, 'componentId' : '1' }) - # Gets the codebase, simply remove last char from applet - codebase = applet[:-1] - # We generate the "data" parameter - data = simpleScrambler('\t'.join([ - 'user:' + user, - 'pass:' + password, - 'ip:' + ip, - 'port:' + extra['port'], - 'session:' + extra['session'], - 'connection:' + extra['connection'], - 'cacheDisk:' + extra['cacheDisk'], - 'cacheMem:' + extra['cacheMem'], - 'width:' + str(extra['width']), - 'height:' + str(extra['height']), - 'is:' + idUserService - ] - )) - if isMac is True: - msg = '

' + _('In order to use this transport, you need to install first OpenNX Client for mac') + '

' - msg += '

' + _('You can oibtain it from ') + ''.format(Config.section('NX').value('downloadUrlMACOS').get()) + _('OpenNx Website') + '

' - else: - msg = '

' + _('In order to use this transport, you need to install first Nomachine Nx Client version 3.5.x') + '

' - msg += '

' + _('you can obtain it for your platform from') + ''.format(Config.section('NX').value('downloadUrl').get()) + _('nochamine web site') + '

' - res = '
' % (codebase, '1', data) - res += '
' + msg + '
' - return res - - -def getHtmlComponent(module, componentId): - comps = { - '1': ['nxtransport.jar', 'application/java-archive'] - } - - if componentId not in comps: - return ['text/plain', 'no component'] - fname = os.path.dirname(sys.modules[module].__file__) + '/applet/' + comps[componentId][0] - logger.debug('Loading component {0} from {1}'.format(componentId, fname)) - - with open(fname, 'rb') as f: - data = f.read() - return [comps[componentId][1], data] diff --git a/server/src/uds/transports/X2GO/X2GOTransport.py b/server/src/uds/transports/X2GO/X2GOTransport.py deleted file mode 100644 index 83854618..00000000 --- a/server/src/uds/transports/X2GO/X2GOTransport.py +++ /dev/null @@ -1,164 +0,0 @@ -# -*- coding: utf-8 -*- - -# -# Copyright (c) 2012 Virtual Cable S.L. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Virtual Cable S.L. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -''' -Created on Jul 29, 2011 - -@author: Adolfo Gómez, dkmaster at dkmon dot com -''' -from 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 - -import logging - -logger = logging.getLogger(__name__) - -READY_CACHE_TIMEOUT = 30 - - -class X2GOTransport(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 - ''' - typeName = _('X2GO Transport (direct)') - typeType = 'X2GOTransport' - typeDescription = _('X2GO Transport for direct connection') - iconFile = 'x2go.png' - protocol = protocols.NX - - 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')) - listenPort = gui.NumericField(label=_('Listening port'), length=5, order=4, tooltip=_('Listening port of NX (ssh) at client machine'), defvalue='22') - connection = gui.ChoiceField(label=_('Connection'), order=6, tooltip=_('Connection speed for this transport (quality)'), - values=[ - {'id': 'modem', 'text': 'modem'}, - {'id': 'isdn', 'text': 'isdn'}, - {'id': 'adsl', 'text': 'adsl'}, - {'id': 'wan', 'text': 'wan'}, - {'id': 'lan', 'text': 'lan'} - ]) - session = gui.ChoiceField(label=_('Session'), order=7, tooltip=_('Desktop session'), - values=[ - {'id': 'gnome', 'text': 'gnome'}, - {'id': 'kde', 'text': 'kde'}, - {'id': 'cde', 'text': 'cde'}, - ]) - cacheDisk = gui.ChoiceField(label=_('Disk Cache'), order=8, tooltip=_('Cache size en Mb stored at disk'), - values=[ - {'id': '0', 'text': '0 Mb'}, - {'id': '32', 'text': '32 Mb'}, - {'id': '64', 'text': '64 Mb'}, - {'id': '128', 'text': '128 Mb'}, - {'id': '256', 'text': '256 Mb'}, - {'id': '512', 'text': '512 Mb'}, - ]) - cacheMem = gui.ChoiceField(label=_('Memory Cache'), order=9, tooltip=_('Cache size en Mb kept at memory'), - values=[ - {'id': '4', 'text': '4 Mb'}, - {'id': '8', 'text': '8 Mb'}, - {'id': '16', 'text': '16 Mb'}, - {'id': '32', 'text': '32 Mb'}, - {'id': '64', 'text': '64 Mb'}, - {'id': '128', 'text': '128 Mb'}, - ]) - - def initialize(self, values): - if values is None: - return - # Just pass over in fact - - def isAvailableFor(self, 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)) - ready = self.cache().get(ip) - if ready is None: - # Check again for readyness - if connection.testServer(ip, self.listenPort.value) is True: - self.cache().put(ip, 'Y', READY_CACHE_TIMEOUT) - return True - else: - self.cache().put(ip, 'N', READY_CACHE_TIMEOUT) - return ready == 'Y' - - def getUDSTransportScript(self, userService, transport, ip, os, user, password, request): - logger.debug('Getting X2Go Transport info') - - prefs = user.prefs('nx') - - username = user.getUsernameForAuth() - proc = username.split('@') - username = proc[0] - if self.fixedName.value != '': - username = self.fixedName.value - if self.fixedPassword.value != '': - password = self.fixedPassword.value - if self.useEmptyCreds.isTrue(): - username, password = '', '' - - # We have the credentials right now, let os manager - - width, height = CommonPrefs.getWidthHeight(prefs) - - # Fix username/password acording to os manager - username, password = userService.processUserPassword(username, password) - - # data - data = { - 'username': username, - 'password': password, - 'width': width, - 'height': height, - 'port': self.listenPort.value, - 'connection': self.connection.value, - 'session': self.session.value, - 'cacheDisk': self.cacheDisk.value, - 'cacheMem': self.cacheMem.value - } - - return ''' -from PyQt4 import QtCore, QtGui -import six -from uds import osDetector - -data = {data} -osname = {os} - -QtGui.QMessageBox.critical(parent, 'Notice ' + osDetector.getOs(), six.text_type(data), QtGui.QMessageBox.Ok) -'''.format(data=data, os=os) - diff --git a/server/src/uds/transports/X2GO/__init__.py b/server/src/uds/transports/X2GO/__init__.py deleted file mode 100644 index 9884b7a5..00000000 --- a/server/src/uds/transports/X2GO/__init__.py +++ /dev/null @@ -1,45 +0,0 @@ -# -*- 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 uds.core.managers.UserPrefsManager import UserPrefsManager, CommonPrefs -from uds.core.managers.DownloadsManager import DownloadsManager -from uds.core.util.Config import Config -from .X2GOTransport import X2GOTransport -from django.utils.translation import ugettext_noop as _ -import os.path - - -Config.section('X2GO').value('downloadUrl', 'http://wiki.x2go.org/doku.php/doc:installation:x2goclient').get() - - -UserPrefsManager.manager().registerPrefs('nx', _('NX Protocol'), [CommonPrefs.screenSizePref]) diff --git a/server/src/uds/transports/X2GO/x2go.png b/server/src/uds/transports/X2GO/x2go.png deleted file mode 100644 index ebfa1175864a2e091c3ab638624fbfc66044491a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7832 zcmV;J9%tc+P)@s7b^^p;Fu>eV3dCD!E>7I+8?mpl7mjCzvz9RsR|IR<<;IID&v2Fj)ZJyaQzOkXwks!Q8 z2mnNV>^~O>>VNdE>;4q?I2Qu3c9T!OwtFss(7pcSo>#w9|2y`-v#8%S2;V95&UaS(|PIlj6?K$1&B1G>#LI|Xw zcPN8zxc?R|Mh1Z|Ai3ZyW#rxi~xA#sjofz#n0^ePOH^%PkjChve{@+yu>anHd%Rfv$6-Jr@F5VqmjRJp zyK+$^Jb6*^rb^t^OXvJA{;kjc_WAcdJf6&M zn%Jnz;@~sS{cL#T!F^(As8OreU+)p!1q=bKj}v!(O$a_f@bxwK5JGSA5Y0k>4DqPs z+EILhMt;)UiJ%xA9^%1+_bC^=FW-G@?~c85vq>t|NRlXqnvJ5_Yzzzd*va2(<%C#U zZRg{#KH!7seHQ|Fq5ynY)0Xc2`utT~{dx!iN<>XiGM+01i5N7IkJG5TwLl0M9%>+h zH$uwB=+M|m5=4+v3L4FZm$fa15C%Md|77na6j!ghb@AMJ5sxm!)vpB;>b2`>fLODi z?#gy2E~DR!0`Rp4C{O{Mge0tK#!sfLqITX2C8ZA`5JZqk_e}(d5CT+=u?0_VVZur{u`aRKiI#(9kORF$PHE7C^F?BBKjns^#7r?ma}Ky;rQ{B z+`fH>Rx2mZbBdy*s!GaIW33^CxU#k2kTK>Ur9df(REjKBs6>$_3CN5?kAE5KJV(F( z0wJVzc#WY;2!T=xgg~k8y(Ec}B#A^Q#ixGy3+y}a0I3ov4_*e^t1bTU_x_N{i7||E z_20#?DP2wOJ%1{&qT5HNu zQ)-Jb7GoVYfOjE=ni%TvYp#bN0$F32vGvp3`0y5U^II`h$;|p8#wJFAIC+&MC@JH) zQZ$5M7#p8t&z=ROgq=GV2mz8L-Xl#@y!X8G&SfSi z$9e0mch|!0pa7@^pqQALU}R)uP1OVS(PLepGLF2o0gBXjeO06@5)QG|Fc__j%qsdydxaJ1pH?<=}x`abihIp5Nq+H_owc-6%y-@#K?_ z(P%UV{M&#DXkGEf8*gyr$PsSezC*j+p)3oG(RlCiAqFua6Qoq6N}DgtLnEWi@7mAy2M+T6Z-0yR>pjS%2Wf?XEK{64cat5D zKF$3H_K`?|7lP72T2J09NTgzR^Bh~}=jgOn`Q88YyFBv9{Zyr<(Mb5yKRwFN|NK+T z&u>CX6}8h3y2b$$NVALs2llgT*Us7rdYtoA+R!N~I)$bvGBoNi! z$vEjm#xR=78dS+O2bUL?jT@)%&NDpR&6lo;<~g6DMmE$m!$-ouZ&BqfHy5dm*wbOo2ejfUF%)q9lzh zB~2AsBc+k0jARMTMn>95NRt$2H0MqRW)~me^6@ilop*q$1>o4ab%xje{224Q_A)jx z8EwAC06suaHJ(sla?Sf^&$9D#_feJ=m#;3dbN61-todV6=8=4Y6yzWf5eOkkk^~_Y2uTnL0YyOKMS>RzE=YWk_#iMr;)1{jfkWV>A|UWc zJPMB@pyKyJ5`@GENo5u7r6sOhc#kl?z^$Sgv2y($WvMxIa2scj{E$vz&3I44WLMT_*v2zbsu3U%a1~$x0 zqiwyy0+fe32z;^$q-o0Dy?fZYb)HUXD723;zcjRSO((DDR5r%<%A&PJTaPvd<9v+K zHkyd{0pkNYfOQd1_#zPU2>eA51aA==rsMU~2e^Lo4HmXE2q7jr-gy@1*Ynolqb%&) zK{Hi@wy!JRK6ad+dvXz_B!B+>Gd%bF^JpuO#5}^vC;%ny#WCC!1!A1^l*UDyb|I=a zf*=GzNkko!y$~1#g$)#Cpe$?flUL}fiq2A7g0~PH1dj+VW_2<`-b6x@2CkKwb@x5Z zk6!)(yS7hZY(UlFt&u4fH%cyExXSh&^J^I-aP7iXW`|qMY?|dquO4U1&IcG88^^ks zFQ^bFZ+%Rh(jR96x~h2N=rInz_BzYA?$By?D9aLKD!dQG6e!|-rIOm)XQWBOaH5#Z zQq*`I9+g6>c&?;ECD{Pfl~SS-MV2VeU0vb5>z82eaV}jt!u-M}s54Tf5@tp;$F9CZ zxn&EXp%4z7)tq?cC4S@Yf0{chx4F3F+5M^QxS(($YN7I=T%5df5l4KY2{fBc9)0XF z?mzG;DibJlpfZ8ddMX2*YRw$FrjeJ9yokRq7ZD1VMM+guR62TnXAI6e@D}eZ1d9k7 zf6@C#T)f6lFzK zYAS6hwZ>V8t?fUAnDIA*peX~P2{h~M4iHKrL<2%hYa`(i7-}YbaPv*tfo9-s#ZJbIPJbn;6%sTM>(H`yKwz~ygr=ij0+w3&;`x7aY_fr#bH zqT;7My@zl8_P06k`R#1nyvSRp-$TqCVsvb*xB9+LoLt|;!(BqSXBdoxWuUAytE+cd zx!s~&l(brTT*a!QtSU^Y(Wb%}LvW7ZeGG8~5)}(3nUZU&pDMC6f}+MyLYgIHX+oBz zWLZL%Hpm(&Ns=-?+T_xUuhH824>)$=1p5{(&8Dik3W0U-wPzREvu6QkD*p0Vn?pZy zKgPvENI(1Bzv8YYWqtSP?}t;NXDM7Cc~u&5uU0V2}($l2#nH1^?1B25$xWed82caE7?PQct#UJ!}V0Ez4}vIvqaW8Z;;Ebci#X#%Bz(gboHD7B|FkefhJ`3Qij znjfqj?ZQPqP&@L%k(VZ#Myb(dMOoxjRfQ=FtkGCq;H^gZ3WXyThNdtyl%bJHCim}R z!@^!(_~GmP#;?uM>Qp_fvbcRCM=#!>y|Tj4%ye{YVXf-at3C9QApO-S0V2yXq>?0^ zJm>m{AL6_V&N{AKzRWxCyvNFFht<`bPLY%6nxZVwr9oSZH8nNfgAm;+E;NK>Skzjg zBuP`DMr6$yO420qhgq7CrcKghghrN-WeJIlboj(*#tSc>=4P=4Gj@;*?_XkO(2%v7Ab5QMHE`UJvVVNVD_QC9x7*WTd9hs(&KC|J6=jQ2j!U%Bx< z5}-r^P9pmF^q7ju&@E(!zJf-Gldl0%x2%<@ZVn+x6G@^(4Va=}y5)8$1=F@^_ozrTySY2J^`i&*-+da$mb0^Wdh%*d7c@eX!bU1eW1WPxUNnov3pCpR8 zty`IzoukmT3D}qul=bgLog);bqoX}VY3Y>F6bkLh3mc}AWmnFI? zuvLXMt6&Wxlr+#}Ler3zOymr2oM-zJ6PVlAICkWRy!Y03@Lu%!0w~P}c6{n_#upcA zyueyxMX#BHdkRM?g%Bb}f#^m8Mr%%;IL`4CC%JQ{MNw81MTxPFN^6|6gb;8(lAR$$ z0@V9ZpU3(EAu!|}4Il8%61?jTl9;B6A>S)l4pt4JDhi4XpGKt_H%@<#xeX4X))5A3 zU6Ns&^Y$w*F+cw`WZLXimwSa))&$aD^zGI^`$}W+!X!zU+d79#8?@S*c3#rTOFDT; zURIG3FRK_Gw52L7MOk4gT|?apqcu)fkp*^UEfDuoB5KnSC;>uvln4mnVx$m`s=2_> z+@rKVyvkVT2y!xGb=A{shCWimqm*Lh!mHfA^(tpJB7ZA!TOHp<~k&Prs6#-CD*(en4(AHC#SRgef7EQH_n8*71JcKBlI!zYd zfY&6-v3~I(#-}%O^~J9Y9B0m)W93eZRy(KNiPU&eloVx2m6w!N(PMnAH8^AO-V&Uv zSKJ{8^@<9la17PwqOa;JW%n4rS4whQkJH%q1h+4~&6Io>=b#Kz?0oWhZXEkIBkC=z zi!K=g!8>kWdV@R5UtnbObacAi>T;hjL|tybyTKyq4-ZlZnvIO{@o|JoX%3AsGNGwT z8zHWBSnF#zE3h7%mslULCSaV$nh1xjH4#9WSZa4hM`p=Zc&iD{;GIEu2gZQ+s093$ zXIM8hOn&h%$YclaC4T%M+xCBohJ;&(-)3l7M$G|~6111kasI92Y~J+w*ihBiS|htn z7Ob6%du4-yK$a$K-n5C4@u`7yxHM}iacKjU4pa`blNc)^Xwtrh$AMBu02IlSin6G1 zRfVofbXj7m5?AH8D#z;_Z*!E$ncTRStq(p;>(X)3_9@h`qOHf-{Lr(E3=gw)?;ftc zzKv1wJ{Hvr@-no!a_&t!k3Gc5)VkP7BG!70#9ikf(SWXRwbt;?*)tq{^B7Av?{Isy zO{di+uPO1WjAW;^Ha1ImAN_Z5YbhlG5qxcTp;q(2un3IFP}^lR2Qm>ScA!p^S^CU**Qdr!B1u5 zC=GQwY{&~kr;ZLqVPkZt>a@5t6nQ~e8mh9Ssx+=L7_0HlV!g&&)AIqiilmt^_t4W! zOpJ5o&#`fSJIzGIydjBw0f~a`dv*EW?>6l_?`5 z3C;~;ZS)0^GOv9=7ef2kafUZ!jJi*})UPp*3!f(0^(-N zYquSs#F8{+VPS#E$w|t}M0i`pTA{X{QhT)Zn7RP!g2Xt1^8({y5j6G(8p=vzw5F;I z#uzGHVJlM;sv28q#>U3D?=w$xuD;8gZyaTQ?y1;T_X*9$lKz&qZa0{X8Jq|nu!vy6J7lm( z97;rLTLh29#UXsW&S`?v2(R(B0#^}i1*XK?l3+BU>>xs$Jr6!gGliQMUZg!%|stC8Xie` z?MJWh#V>xAjnfYjLh`Zgv`N1Pq)Ed5{rlLpYiF-X%39l_!t>nFDk_S)ZdmAws&r@* zizmi9tgWeU>+u1MbvVaB+pUx^yKOg_A7ktYhNPE8Xw(wakmL*tvPYxBu9=MVP)ku?RJ|yui0FqG1lRojjP#f zRPsIMwVR@L<4u=xcRqpPEsRZ0GQDY@?K^jK?fiS3`oSMD+?050AsJ_UVw9mwB9-W+ zw7odqCxEo(7!wK2sR_0&Y~$onNpFrddptCh@Xc?&z{3yU&%S*-@2LfYK&a8+kw+fp z!3Ph0q*WHc`q(Yj<@`$RY6$EoD??ejx_Q!2)TUAvIiusl?7Dw1X=8}xo40uX)M;M( z%NJ-SH*l^dN=1iLhyM!GX|a9xPNp}kCrwlj(a_5Z?Gzxovp9oo&*)YJeH9RNbkd+yx1!^+A^-7H;^=Q(AmDQXN*7Mi>=Xl*ds zqMeUm$N885b(5jmIa=o^CMMajYo43SAM)ZKeS_A@5(#Z&s(Q5gx^;m|r@zCsbKmFP zZTIn|=YN%jZ412wrr!}N5%|uxUgYr0f6nsqGS23Vj-?-IgYEJhOG~Hu#y768ZQBlh z^Edzb02~@H0q-0id~l5uCr)zfR*V9jPN(h?D=3Nzt*hu9>q&RBJ69il@Ps-YjvC|` zpWMKKhaSTl!}TkdKx70z^wDiI5{jy%dE*+=Yno})%tQ`G1Sz1sy26#q*K5buMC(sr z!iAu!N-kf%g0Xg>XfhB5k|br%p51KUzP;xJL-2LoE!3MPYAPzk)^%O?;@O(-b6#Lw zz}jxpq$WvIHq34Sq1gW0|GDlr`uHs~%|^<^#28t!Mr?E^9wUI@YrpbK{KDUUZh-js zai1hnOiiutZMRz!fQar($k-=zcT4$4$}0oAvAUD5x61g~Sr+qv(6?hOB7^bKMsMQb zF2CF(GwT{)m&~|3qe~PFuNxtm8sV?Axh>S@*4>N=fXNTukNszdK6K6%&JJvK}YkQ=o&bXPHipLqJcpLvlB?>rLe5~OS! zZCP2lJzQ25L(Pm3?!6J|{g^y-s1Jt!#V|BZwH7V}h?D&&z z?~8&D6lKZnJ9mbhvjj;;jjX#*%uc6Io;Y@N!_GYq_}LB9DpAS3FvCwIWFZGx7DrfLn~^blX)=w65T1Z(^;?-c@+mElLPA2%O-;`CTgjb|vtx{fb!FuQm;jvJvk`eejb1k3LdJ qkCg4A9j;$EdFtBZ!{@@wIs6|!?XY)#xp~+C0000