1
0
mirror of https://github.com/dkmstr/openuds.git synced 2025-10-08 19:34:11 +03:00

2 Commits
v2.1 ... v2.0

Author SHA1 Message Date
Adolfo Gómez García
15a76f3b9b Small 2.0 fix 2017-04-07 10:47:57 +02:00
Adolfo Gómez García
a3110d4623 fixed tunneld cord access 2017-03-18 11:48:52 +01:00
271 changed files with 12893 additions and 28314 deletions

View File

@@ -1 +1 @@
2.1.0
2.0.0

2
actors/.gitignore vendored
View File

@@ -6,5 +6,3 @@ udsactor*.changes
/udsactor_1.7.0.dsc
/udsactor_1.7.0.tar.xz
/udsactor*.rpm
/udsactor_2.1.0_amd64.buildinfo
linux/debian/files

View File

@@ -1,9 +1,3 @@
udsactor (2.1.0) stable; urgency=medium
* Fixes for 2.1.0 release
-- Adolfo Gómez García <agomez@virtualcable.es> Tue, 19 Jan 2017 08:00:22 +0200
udsactor (2.0.0) stable; urgency=medium
* Upgrade for 2.0.0

View File

@@ -0,0 +1,3 @@
udsactor-nx_2.0.0_all.deb x11 optional
udsactor-xrdp_2.0.0_all.deb x11 optional
udsactor_2.0.0_all.deb admin optional

View File

@@ -5,7 +5,6 @@
set -e
case "$1" in
configure)
/usr/bin/python2.7 -m compileall /usr/share/UDSActor > /dev/nul 2>&1
# If new "fresh" install or if configuration file has disappeared...
if [ "$2" = "" ] || [ ! -f /etc/udsactor/udsactor.cfg ]; then
db_get udsactor/host

View File

@@ -8,5 +8,5 @@ Type=Application
NoDisplay=true
X-KDE-autostart-after=panel
X-KDE-StartupNotify=false
X-DBUS-StartupType=None
X-KDE-UniqueApplet=false
X-DBUS-StartupType=Unique
X-KDE-UniqueApplet=true

View File

@@ -51,8 +51,6 @@ from udsactor import VERSION
trayIcon = None
doLogoff = False
def sigTerm(sigNo, stackFrame):
if trayIcon:
@@ -255,7 +253,7 @@ class UDSSystemTray(QtGui.QSystemTrayIcon):
if remainingTime <= 0:
logger.info('User has been idle for too long, notifying Broker that service can be reclaimed')
self.quit(logoff=True)
self.quit()
def displayMessage(self, message):
logger.debug('Displaying message')
@@ -292,8 +290,7 @@ class UDSSystemTray(QtGui.QSystemTrayIcon):
def about(self):
self.aboutDlg.exec_()
def quit(self, logoff=False):
global doLogoff
def quit(self):
logger.debug('Quit invoked')
if self.stopped is False:
self.stopped = True
@@ -306,7 +303,10 @@ class UDSSystemTray(QtGui.QSystemTrayIcon):
# May we have lost connection with server, simply exit in that case
pass
doLogoff = logoff
try:
operations.loggoff() # Invoke log off
except Exception:
pass
self.app.quit()
@@ -339,12 +339,4 @@ if __name__ == '__main__':
logger.debug('Exiting')
trayIcon.quit()
if doLogoff:
try:
time.sleep(1)
operations.loggoff() # Invoke log off
except Exception:
pass
sys.exit(res)

View File

@@ -34,7 +34,7 @@ from __future__ import unicode_literals
# On centos, old six release does not includes byte2int, nor six.PY2
import six
VERSION = '2.1.0'
VERSION = '2.0.0'
__title__ = 'udsactor'
__version__ = VERSION

View File

@@ -196,22 +196,13 @@ class HTTPServerThread(threading.Thread):
self.initiateServer(address)
def getPort(self):
return self.address[1]
def getIp(self):
return self.address[0]
def initiateServer(self, address):
self.address = (address[0], address[1]) # Copy address & keep it for future reference...
addr = ('0.0.0.0', address[1]) # Adapt to listen on 0.0.0.0
self.server = socketserver.TCPServer(addr, HTTPServerHandler)
self.server = socketserver.TCPServer(address, HTTPServerHandler)
self.server.socket = ssl.wrap_socket(self.server.socket, certfile=self.certFile, server_side=True)
def getServerUrl(self):
return 'https://{}:{}/{}'.format(self.getIp(), self.getPort(), HTTPServerHandler.uuid)
return 'https://{}:{}/{}'.format(self.server.server_address[0], self.server.server_address[1], HTTPServerHandler.uuid)
def stop(self):
logger.debug('Stopping REST Service')
@@ -220,14 +211,11 @@ class HTTPServerThread(threading.Thread):
def restart(self, address=None):
if address is None:
# address = self.server.server_address
address = self.address
address = self.server.server_address
self.address = (address[0], self.address[1]) # Copy address & keep it for future reference, port is never changed once assigned on init
self.stop()
# Listening on 0.0.0.0, does not need to restart listener..
# self.stop()
# self.initiateServer(address)
self.initiateServer(address)
def run(self):
self.server.serve_forever()

View File

@@ -36,8 +36,6 @@ import sys
import six
import traceback
import pickle
import errno
import time
from udsactor.utils import toUnicode
from udsactor.log import logger
@@ -409,11 +407,8 @@ class ClientIPC(threading.Thread):
self.messageReceived()
except socket.error as e:
if e.errno == errno.EINTR:
time.sleep(1) #
continue # Ignore interrupted system call
logger.error('Communication with server got an error: {}'.format(toUnicode(e.strerror)))
# self.running = False
self.running = False
return
except Exception as e:
tb = traceback.format_exc()

View File

@@ -36,7 +36,6 @@ from udsactor import operations
from udsactor.service import CommonService
from udsactor.service import initCfg
from udsactor.service import IPC_PORT
from udsactor import ipc
from udsactor.log import logger
@@ -45,23 +44,15 @@ from udsactor.linux.daemon import Daemon
from udsactor.linux import renamer
import sys
import os
import stat
import subprocess
POST_CMD = '/etc/udsactor/post'
PRECONNECT_CMD = '/etc/udsactor/pre'
import time
try:
from prctl import set_proctitle # @UnresolvedImport
from prctl import set_proctitle
except Exception: # Platform may not include prctl, so in case it's not available, we let the "name" as is
def set_proctitle(_):
pass
class UDSActorSvc(Daemon, CommonService):
rebootMachineAfterOp = False
def __init__(self, args=None):
Daemon.__init__(self, '/var/run/udsa.pid')
CommonService.__init__(self)
@@ -71,12 +62,6 @@ class UDSActorSvc(Daemon, CommonService):
Renames the computer, and optionally sets a password for an user
before this
'''
hostName = operations.getComputerName()
if hostName.lower() == name.lower():
logger.info('Computer name is already {}'.format(hostName))
self.setReady()
return
# Check for password change request for an user
if user is not None:
@@ -90,48 +75,13 @@ class UDSActorSvc(Daemon, CommonService):
'Could not change password for user {} (maybe invalid current password is configured at broker): {} '.format(user, unicode(e)))
renamer.rename(name)
if self.rebootMachineAfterOp is False:
self.setReady()
else:
logger.info('Rebooting computer to activate new name {}'.format(name))
self.reboot()
self.setReady()
def joinDomain(self, name, domain, ou, account, password):
logger.fatal('Join domain is not supported on linux platforms right now')
def preConnect(self, user, protocol):
'''
Invoked when received a PRE Connection request via REST
'''
# Execute script in /etc/udsactor/post after interacting with broker, if no reboot is requested ofc
# This will be executed only when machine gets "ready"
try:
if os.path.isfile(PRECONNECT_CMD):
if (os.stat(PRECONNECT_CMD).st_mode & stat.S_IXUSR) != 0:
subprocess.call([PRECONNECT_CMD, user, protocol])
else:
logger.info('PRECONNECT file exists but it it is not executable (needs execution permission by root)')
else:
logger.info('PRECONNECT file not found & not executed')
except Exception as e:
# Ignore output of execution command
logger.error('Executing preconnect command give')
def run(self):
cfg = initCfg() # Gets a local copy of config to get "reboot"
logger.debug('CFG: {}'.format(cfg))
if cfg is not None:
self.rebootMachineAfterOp = cfg.get('reboot', True)
else:
self.rebootMachineAfterOp = False
logger.info('Reboot after is {}'.format(self.rebootMachineAfterOp))
initCfg()
logger.debug('Running Daemon')
set_proctitle('UDSActorDaemon')
@@ -156,21 +106,6 @@ class UDSActorSvc(Daemon, CommonService):
logger.debug('Reboot has been requested, stopping service')
return
# Execute script in /etc/udsactor/post after interacting with broker, if no reboot is requested ofc
# This will be executed only when machine gets "ready"
try:
if os.path.isfile(POST_CMD):
if (os.stat(POST_CMD).st_mode & stat.S_IXUSR) != 0:
subprocess.call([POST_CMD, ])
else:
logger.info('POST file exists but it it is not executable (needs execution permission by root)')
else:
logger.info('POST file not found & not executed')
except Exception as e:
# Ignore output of execution command
logger.error('Executing post command give')
self.initIPC()
# *********************

View File

@@ -122,7 +122,7 @@ def getComputerName():
def getNetworkInfo():
for ifname in _getInterfaces():
ip, mac = _getIpAndMac(ifname)
if mac != '00:00:00:00:00:00' and ip.startswith('169.254') is False: # Skips local interfaces & interfaces with no dhcp IPs
if mac != '00:00:00:00:00:00': # Skips local interfaces
yield utils.Bunch(name=ifname, mac=mac, ip=ip)

View File

@@ -78,11 +78,3 @@ def writeConfig(data):
cfg.write(f)
os.chmod(CONFIGFILE, 0o0600)
def useOldJoinSystem():
return False
# Right now, we do not really need an application to be run on "startup" as could ocur with windows
def runApplication():
return None

View File

@@ -70,8 +70,6 @@ def initCfg():
cfg = None
break
return cfg
class CommonService(object):
def __init__(self):
@@ -86,23 +84,6 @@ class CommonService(object):
def reboot(self):
self.rebootRequested = True
def execute(self, cmd, section):
import os
import subprocess
import stat
if os.path.isfile(cmd):
if (os.stat(cmd).st_mode & stat.S_IXUSR) != 0:
subprocess.call([cmd, ])
return True
else:
logger.info('{} file exists but it it is not executable (needs execution permission by admin/root)'.format(section))
else:
logger.info('{} file not found & not executed'.format(section))
return False
def setReady(self):
self.api.setReady([(v.mac, v.ip) for v in operations.getNetworkInfo()])
@@ -155,13 +136,6 @@ class CommonService(object):
# Wait a bit before next check
self.doWait(5000)
# Now try to run the "runonce" element
runOnce = store.runApplication()
if runOnce is not None:
if self.execute(runOnce, 'RunOnce') is True:
# operations.reboot()
return False
# Broker connection is initialized, now get information about what to
# do
counter = 0
@@ -236,7 +210,7 @@ class CommonService(object):
self.knownIps = dict(((v.mac, v.ip) for v in netInfo))
# And notify new listening address to broker
address = (self.knownIps[self.api.mac], self.httpServer.getPort())
address = (self.knownIps[self.api.mac], random.randrange(43900, 44000))
# And new listening address
self.httpServer.restart(address)
# sends notification

View File

@@ -40,11 +40,9 @@ import win32event # @UnresolvedImport, pylint: disable=import-error
import win32com.client # @UnresolvedImport, @UnusedImport, pylint: disable=import-error
import pythoncom # @UnresolvedImport, pylint: disable=import-error
import servicemanager # @UnresolvedImport, pylint: disable=import-error
import subprocess
import os
from udsactor import operations
from udsactor import store
from udsactor.service import CommonService
from udsactor.service import initCfg
@@ -57,8 +55,6 @@ from .SENS import SENSGUID_PUBLISHER
from .SENS import PROGID_EventSubscription
from .SENS import PROGID_EventSystem
POST_CMD = 'c:\\windows\\post-uds.bat'
class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService):
'''
@@ -117,7 +113,7 @@ class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService):
operations.renameComputer(name)
# Reboot just after renaming
logger.info('Rebooting computer to activate new name {}'.format(name))
logger.info('Rebooting computer got activate new name {}'.format(name))
self.reboot()
def oneStepJoin(self, name, domain, ou, account, password):
@@ -162,15 +158,12 @@ class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService):
ver = ver[0] * 10 + ver[1]
logger.debug('Starting joining domain {} with name {} (detected operating version: {})'.format(
domain, name, ver))
# If file c:\compat.bin exists, joind domain in two steps instead one
# Accepts one step joinDomain, also remember XP is no more supported by
# microsoft, but this also must works with it because will do a "multi
# step" join
if ver >= 60 and store.useOldJoinSystem() is False:
if ver >= 60:
self.oneStepJoin(name, domain, ou, account, password)
else:
logger.info('Using multiple step join because configuration requests to do so')
self.multiStepJoin(name, domain, ou, account, password)
def preConnect(self, user, protocol):
@@ -299,17 +292,6 @@ class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService):
logger.debug('Registered SENS, running main loop')
# Execute script in c:\\windows\\post-uds.bat after interacting with broker, if no reboot is requested ofc
# This will be executed only when machine gets "ready"
try:
if os.path.isfile(POST_CMD):
subprocess.call([POST_CMD, ])
else:
logger.info('POST file not found & not executed')
except Exception as e:
# Ignore output of execution command
logger.error('Executing post command give')
# *********************
# * Main Service loop *
# *********************

View File

@@ -82,6 +82,7 @@ def readConfig():
except Exception:
return None
def writeConfig(data, fixPermissions=True):
try:
key = wreg.OpenKey(baseKey, path, 0, wreg.KEY_ALL_ACCESS) # @UndefinedVariable
@@ -92,32 +93,3 @@ def writeConfig(data, fixPermissions=True):
wreg.SetValueEx(key, "", 0, wreg.REG_BINARY, encoder(cPickle.dumps(data))) # @UndefinedVariable
wreg.CloseKey(key) # @UndefinedVariable
def useOldJoinSystem():
try:
key = wreg.OpenKey(baseKey, 'Software\\UDSEnterpriseActor', 0, wreg.KEY_QUERY_VALUE) # @UndefinedVariable
try:
data, _ = wreg.QueryValueEx(key, 'join') # @UndefinedVariable
except Exception:
data = ''
wreg.CloseKey(key) # @UndefinedVariable
except:
data = ''
return data == 'old'
# Gives the oportunity to run an application ONE TIME (because, the registry key "run" will be deleted after read)
def runApplication():
try:
key = wreg.OpenKey(baseKey, 'Software\\UDSEnterpriseActor', 0, wreg.KEY_ALL_ACCESS) # @UndefinedVariable
try:
data, _ = wreg.QueryValueEx(key, 'run') # @UndefinedVariable
wreg.DeleteValue(key, 'run') # @UndefinedVariable
except Exception:
data = None
wreg.CloseKey(key) # @UndefinedVariable
except:
data = None
return data

View File

@@ -1,2 +0,0 @@
udsclient_2.1.0_all.deb admin optional
udsclient_2.1.0_amd64.buildinfo admin optional

View File

@@ -1,6 +1,6 @@
#!/bin/bash
VERSION=`cat ../../../VERSION`
VERSION=`cat ../../VERSION`
RELEASE=1
# Debian based
dpkg-buildpackage -b

View File

@@ -1,9 +1,3 @@
udsclient (2.1.0) stable; urgency=medium
* Updated release
-- Adolfo Gómez García <agomez@virtualcable.es> Sun, 23 Oct 2016 21:12:23 +0200
udsclient (2.0.0) stable; urgency=medium
* Release upgrade

View File

@@ -0,0 +1 @@
udsclient_2.0.0_all.deb admin optional

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Virtual Cable S.L.
# Copyright (c) 2014 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@@ -281,7 +281,7 @@ if __name__ == "__main__":
# Setup REST api endpoint
RestRequest.restApiUrl = '{}://{}/rest/client'.format(['http', 'https'][ssl], host)
logger.debug('Setting request URL to {}'.format(RestRequest.restApiUrl))
logger.debug('Setting requert URL to {}'.format(RestRequest.restApiUrl))
# RestRequest.restApiUrl = 'https://172.27.0.1/rest/client'
try:

View File

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View File

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

Before

Width:  |  Height:  |  Size: 137 KiB

After

Width:  |  Height:  |  Size: 137 KiB

View File

@@ -34,7 +34,7 @@ from __future__ import unicode_literals
# On centos, old six release does not includes byte2int, nor six.PY2
import six
VERSION = '2.1.0'
VERSION = '2.0.0'
__title__ = 'udclient'
__version__ = VERSION

View File

@@ -33,17 +33,10 @@ from __future__ import unicode_literals
import logging
import os
import sys
import tempfile
if sys.platform.startswith('linux'):
from os.path import expanduser
logFile = expanduser('~/udsclient.log')
else:
logFile = os.path.join(tempfile.gettempdir(), b'udsclient.log')
logging.basicConfig(
filename=logFile,
filename=os.path.join(tempfile.gettempdir(), b'udsclient.log'),
filemode='a',
format='%(levelname)s %(asctime)s %(message)s',
level=logging.DEBUG

View File

@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Virtual Cable S.L.
# Copyright (c) 2015 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,

View File

@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Virtual Cable S.L.
# Copyright (c) 2015 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,

View File

@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>UDSClient-Thin</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.python.pydev.PyDevBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.python.pydev.pythonNature</nature>
</natures>
</projectDescription>

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?eclipse-pydev version="1.0"?><pydev_project>
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
<path>/${PROJECT_DIR_NAME}/src</path>
</pydev_pathproperty>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
</pydev_project>

View File

@@ -1,158 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'''
@author: Adolfo Gómez, dkmaster at dkmon dot com
'''
from __future__ import unicode_literals
from uds import ui
from uds.rest import RestRequest, RetryException
from uds.forward import forward
from uds import VERSION
from uds.log import logger # @UnresolvedImport
from uds import tools
import six
import sys
import pickle
def approveHost(host):
from os.path import expanduser
hostsFile = expanduser('~/.udsclient.hosts')
try:
with open(hostsFile, 'r') as f:
approvedHosts = f.read().splitlines()
except Exception:
approvedHosts = []
host = host.lower()
if host in approvedHosts:
return True
errorString = 'The server {} must be approved:\n'.format(host)
errorString += 'Only approve UDS servers that you trust to avoid security issues.'
approved = ui.question("ACCESS Warning", errorString)
if approved:
approvedHosts.append(host)
logger.debug('Host was approved, saving to approvedHosts file')
try:
with open(hostsFile, 'w') as f:
f.write('\n'.join(approvedHosts))
except Exception:
logger.warn('Got exception writing to {}'.format(hostsFile))
return approved
def getWithRetry(rest, url, params=None):
while True:
try:
res = rest.get(url, params)
return res
except RetryException as e:
if ui.question('Service not available', 'Error {}.\nPlease, wait a minute and press "OK" to retry, or "CANCEL" to abort'.format(e)) is True:
continue
raise Exception('Cancelled by user')
if __name__ == "__main__":
logger.debug('Initializing connector')
if six.PY3 is False:
logger.debug('Fixing threaded execution of commands')
import threading
threading._DummyThread._Thread__stop = lambda x: 42
# First parameter must be url
try:
uri = sys.argv[1]
if uri == '--test':
sys.exit(0)
logger.debug('URI: {}'.format(uri))
if uri[:6] != 'uds://' and uri[:7] != 'udss://':
raise Exception()
ssl = uri[3] == 's'
host, ticket, scrambler = uri.split('//')[1].split('/')
logger.debug('ssl: {}, host:{}, ticket:{}, scrambler:{}'.format(ssl, host, ticket, scrambler))
except Exception:
logger.debug('Detected execution without valid URI, exiting')
ui.message('UDS Client', 'UDS Client Version {}'.format(VERSION))
sys.exit(1)
rest = RestRequest(host, ssl)
logger.debug('Setting request URL to {}'.format(rest.restApiUrl))
# Main requests part
# First, get version
try:
res = getWithRetry(rest, '')
logger.debug('Got information {}'.format(res))
if res['requiredVersion'] > VERSION:
ui.message("New UDS Client available", "A new uds version is needed in order to access this version of UDS.\nPlease, download and install it")
sys.exit(1)
res = getWithRetry(rest, '/{}/{}'.format(ticket, scrambler), params={'hostname': tools.getHostName(), 'version': VERSION})
script = res.decode('base64').decode('bz2')
logger.debug('Script: {}'.format(script))
six.exec_(script, globals(), {'parent': None})
except Exception as e:
error = 'ERROR: {}'.format(e)
logger.error(error)
ui.message('Error', error)
sys.exit(2)
# Finalize
try:
tools.waitForTasks()
except Exception:
pass
try:
tools.unlinkFiles()
except Exception:
pass
try:
tools.execBeforeExit()
except Exception:
pass

View File

@@ -1 +0,0 @@
../../../full/src/uds/__init__.py

View File

@@ -1 +0,0 @@
../../../full/src/uds/forward.py

View File

@@ -1 +0,0 @@
../../../full/src/uds/log.py

View File

@@ -1 +0,0 @@
../../../full/src/uds/osDetector.py

View File

@@ -1,86 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2015 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'''
@author: Adolfo Gómez, dkmaster at dkmon dot com
'''
from __future__ import unicode_literals
import requests
from . import VERSION
import json
import six
import osDetector
from .log import logger
class RetryException(Exception):
pass
class RestRequest(object):
restApiUrl = ''
def __init__(self, host, ssl=True): # parent not used
super(RestRequest, self).__init__()
self.host = host
self.ssl = ssl
self.restApiUrl = '{}://{}/rest/client'.format(['http', 'https'][ssl], host)
def get(self, url, params=None):
url = self.restApiUrl + url
if params is not None:
url += '?' + '&'.join('{}={}'.format(k, six.moves.urllib.parse.quote(six.text_type(v).encode('utf8'))) for k, v in params.iteritems()) # @UndefinedVariable
logger.debug('Requesting {}'.format(url))
try:
r = requests.get(url, headers={'Content-type': 'application/json', 'User-Agent': osDetector.getOs() + " - UDS Connector " + VERSION })
except requests.exceptions.ConnectionError as e:
raise Exception('Error connecting to UDS Server at {}'.format(self.restApiUrl[0:-11]))
if r.ok:
logger.debug('Request was OK. {}'.format(r.text))
data = json.loads(r.text)
if not 'error' in data:
return data['result']
# Has error
if data.get('retryable', '0') == '1':
raise RetryException(data['error'])
raise Exception(data['error'])
else:
logger.error('Error requesting {}: {}, {}'.format(url, r.code. r.text))
raise Exception('Error {}: {}'.format(r.code, r.text))
return data

View File

@@ -1 +0,0 @@
../../../full/src/uds/tools.py

View File

@@ -1,44 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'''
@author: Adolfo Gómez, dkmaster at dkmon dot com
'''
from __future__ import unicode_literals
try:
import gtkui as theUI
except Exception:
import consoleui as theUI # @Reimport
def message(title, message):
theUI.message(title, message)
def question(title, message):
return theUI.question(title, message)

View File

@@ -1,58 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'''
@author: Adolfo Gómez, dkmaster at dkmon dot com
'''
from __future__ import unicode_literals
import random
import os
import tempfile
import string
import webbrowser
TEMPLATE = '''<html>
<head>
<title>{title}</title>
</head>
<body>
<h1>{title}</h1>
<p>{message}<P>
</body>
</html>
'''
def _htmlFilename():
return os.path.join(tempfile.gettempdir(), ''.join([random.choice(string.ascii_lowercase) for i in range(22)]) + '.html')
def message(title, message):
filename = _htmlFilename()
with open(filename, 'w') as f:
f.write(TEMPLATE.format(title=title, message=message))
webbrowser.open('file://' + filename, new=0, autoraise=False)

View File

@@ -1,49 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'''
@author: Adolfo Gómez, dkmaster at dkmon dot com
'''
from __future__ import unicode_literals
import sys
import time
from uds.log import logger
counter = 0
def message(title, message):
sys.stderr.write("** {} **\n {}\n".format(title, message))
def question(title, message):
global counter
if counter > 100: # 5 minutes
return False
counter += 1
sys.stderr.write("** {} **\n{}\nReturning YES in 3 seconds. (counter is {})\n".format(title, message, counter))
time.sleep(3) # Wait 3 seconds before returning
return True

View File

@@ -1,143 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'''
@author: Adolfo Gómez, dkmaster at dkmon dot com
'''
from __future__ import unicode_literals
import re
import pygtk
pygtk.require('2.0')
import gtk
import gobject
LINE_LEN = 65
class Dialog():
def __init__(self, title, message, timeout=-1, withCancel=True):
self.title = title
self.message = message
self.timeout = timeout
self.withCancel = withCancel
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.set_position(gtk.WIN_POS_CENTER)
# self.window.set_size_request(320, 200)
self.window.set_title(self.title)
self.create_widgets()
self.connect_signals()
self.window.show_all()
self.window.connect("destroy", self.destroy)
# Setup "auto OK" timer
if timeout != -1:
self.timerId = gobject.timeout_add(self.timeout * 1000, self.callback_timer)
else:
self.timerId = -1
self.result = False
gtk.main()
@property
def adapted_message(self):
msg = ''
for l in re.sub(r'<p[^>]*>', '', self.message).replace('</p>', '\n').split('\n'):
words = []
length = 0
for word in l.split(' '):
if length + len(word) >= LINE_LEN:
msg += ' '.join(words) + '\n'
words = []
length = 0
length += len(word) + 1
words.append(word)
msg += ' '.join(words) + '\n'
return msg
def create_widgets(self):
self.vbox = gtk.VBox(spacing=10)
self.vbox.set_size_request(490, -1)
self.messageLabel = gtk.Label()
# Fix message markup
# self.message = re.sub(r'<p[^>]*>', '<span font_weight="bold">', self.message).replace('</p>', '</span>\n' )
# Set as simple markup
self.messageLabel.set_markup('\n' + self.adapted_message + '\n')
self.messageLabel.set_alignment(xalign=0.5, yalign=1)
self.hbox = gtk.HBox(spacing=10)
self.button_ok = gtk.Button("OK")
self.hbox.pack_start(self.button_ok)
if self.withCancel:
self.button_cancel = gtk.Button("Cancel")
self.hbox.pack_start(self.button_cancel)
self.vbox.pack_start(self.messageLabel)
self.vbox.pack_start(self.hbox)
self.window.add(self.vbox)
def connect_signals(self):
self.button_ok.connect("clicked", self.callback_ok)
if self.withCancel:
self.button_cancel.connect("clicked", self.callback_cancel)
def destroy(self, widget, data=None):
self.setResult(False)
def setResult(self, val):
if self.timerId != -1:
gobject.source_remove(self.timerId)
self.timerId = -1
self.result = val
self.window.hide()
gtk.main_quit()
def callback_ok(self, widget, callback_data=None):
self.setResult(True)
def callback_cancel(self, widget, callback_data=None):
self.setResult(False)
def callback_timer(self):
self.setResult(True)
def message(title, message):
Dialog(title, message, withCancel=False)
def question(title, message):
dlg = Dialog(title, message, timeout=30, withCancel=True)
return dlg.result

View File

@@ -1,4 +0,0 @@
#!/bin/sh
cd /lib/UDSClient
exec python UDSClient.pyc $@

View File

@@ -1,11 +0,0 @@
Steps:
1.- If building from repository, full copy (recursive) the "src" folder of "udsclient/thin" inside the "udsclient" folder. If building from the .tar.gz, simply ignor4e this step
2.- Copy the folder "udsclient" to /build/packages inside the thinstation build environment
3.- enter the chroot of thinstation
4.- go to the udsclient folder (/build/packages/udsclient)
5.- Execute "build.sh"
6.- Edit the file /build/build.conf, and add this line:
package udsclient
7.- Execute the build process
Ready!!!

View File

@@ -1,2 +0,0 @@
lib
src

View File

@@ -1,11 +0,0 @@
[Desktop Entry]
Name=UDSClient
Comment=UDS Helper
Keywords=uds;client;vdi;
Exec=/bin/udsclient %u
Icon=help-browser
StartupNotify=true
Terminal=false
Type=Application
Categories=Utility;
MimeType=x-scheme-handler/uds;x-scheme-handler/udss;

View File

@@ -1,4 +0,0 @@
#!/bin/sh
cd /lib/UDSClient
exec python UDSClient.pyc $@

View File

@@ -1,13 +0,0 @@
#!/bin/sh
pip install paramiko requests six
rm -rf lib
mkdir -p lib/python2.7/site-packages
for a in requests paramiko pyasn1 cryptography packaging idna asn1crypto six enum ipaddress cffi ; do cp -r /usr/lib/python2.7/site-packages/$a* lib/python2.7/site-packages/; done
cp src/udsclient bin
chmod 755 bin/udsclient
mkdir lib/UDSClient
cp src/UDSClient.py lib/UDSClient
chmod 755 lib/UDSClient/UDSClient.py
cp -r src/uds lib/UDSClient
mkdir lib/applications
cp UDSClient.desktop lib/applications

View File

@@ -1,5 +0,0 @@
base
#add your own dependancies to this file, base should always be included.
python
pygtk
freerdp

View File

@@ -1,15 +0,0 @@
In here you place the commands to start your application if using the scripts
/etc/thinstation.packages
or /etc/thinstation.console
see examples for for information
possible types are
example.global (this is always needed)
example.menu
example.console
example.window
example.fullscreen

View File

@@ -1 +0,0 @@
CMD_FULLSCREEN="example -FULLSCREEN"

View File

@@ -1 +0,0 @@
CMD_GLOBAL="example -startapp"

View File

@@ -1 +0,0 @@
Place a 0 length file in here as the same name as the package if your application is a Console App

View File

@@ -1,34 +0,0 @@
#! /bin/sh
. /etc/thinstation.global
# note you can replace this package with a symlink to /etc/thinstation.packages
# for GUI apps, or /etc/thinstation.console for console apps
# if you do then you will need to create a seperate initilization script for
# any other parameters which need to be started at bootup
case "$1" in
init)
if ! pkg_initialized $PACKAGE; then
# Your startup instructions go here
pkg_set_init_flag $PACKAGE
fi
;;
console)
;;
window)
;;
fullscreen)
;;
help)
echo "Usage: $0 init"
;;
*)
exit 1
;;
esac
exit 0

View File

@@ -1 +0,0 @@
/etc/init.d/your_start_up_script

View File

@@ -1,13 +0,0 @@
# Backup files
*~
# Generated files
src/main/webapp/META-INF/
src/main/webapp/generated/
target/
# IDE-specific configuration
nb-configuration.xml
.classpath
.project

View File

@@ -7,7 +7,7 @@
<groupId>org.openuds.server</groupId>
<artifactId>transport</artifactId>
<packaging>war</packaging>
<version>2.5.0</version>
<version>2.0.0</version>
<name>Guacamole Transport</name>
<url>https://github.com/dkmstr/openuds</url>
@@ -81,14 +81,14 @@
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-common</artifactId>
<version>0.9.10-incubating</version>
<version>0.9.9-incubating</version>
</dependency>
<!-- Guacamole JavaScript library -->
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-common-js</artifactId>
<version>0.9.12-incubating</version>
<version>0.9.9-incubating</version>
<type>zip</type>
<scope>runtime</scope>
</dependency>

View File

@@ -2,7 +2,7 @@
<!DOCTYPE html>
<!--
Copyright (C) 2017 Glyptodon, Inc.
Copyright (C) 2013 Glyptodon LLC
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -147,12 +147,30 @@
<!-- guacamole-default-webapp scripts -->
<script type="text/javascript" src="scripts/session.js"></script>
<script type="text/javascript" src="scripts/history.js"></script>
<script type="text/javascript" src="scripts/service.js"></script>
<script type="text/javascript" src="scripts/guac-ui.js"></script>
<script type="text/javascript" src="scripts/client-ui.js"></script>
<!-- Init -->
<script type="text/javascript"> /* <![CDATA[ */
/*function getQueryParams(qs) {
qs = qs.split("+").join(" ");
var params = {}, tokens,
re = /[?&]?([^=]+)=([^&]*)/g;
while (tokens = re.exec(qs)) {
params[decodeURIComponent(tokens[1])]
= decodeURIComponent(tokens[2]);
}
return params;
}
window.query = getQueryParams(document.location.search);*/
// Adapted to 1.5
function getQueryParams(qs) {
qs = qs.split("+").join(" ");
@@ -169,11 +187,16 @@
window.query = getQueryParams(document.location.href);
// Sanity check Guacamole JavaScript API version
// if (Guacamole.API_VERSION !== "0.9.1")
// GuacUI.Client.showStatus("Clear Your Cache", "An older version of Guacamole has been cached by your browser. Please clear your browser's cache and try again.");
// Start connect after control returns from onload (allow browser
// to consider the page loaded).
window.onload = function() {
window.setTimeout(GuacUI.Client.connect, 10);
};
//else
window.onload = function() {
window.setTimeout(GuacUI.Client.connect, 10);
};
/* ]]> */ </script>

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2017 Glyptodon, Inc.
* Copyright (C) 2013 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -746,6 +746,48 @@ GuacUI.Client.Pinch = function(element) {
};
/**
* Flattens the attached Guacamole.Client, storing the result within the
* connection history.
*/
GuacUI.Client.updateThumbnail = function() {
var guac = GuacUI.Client.attachedClient;
if (!guac)
return;
// Do not create empty thumbnails
if (guac.getDisplay().getWidth() <= 0 || guac.getDisplay().getHeight() <= 0)
return;
// Get screenshot
var canvas = guac.getDisplay().flatten();
// Calculate scale of thumbnail (max 320x240, max zoom 100%)
var scale = Math.min(
320 / canvas.width,
240 / canvas.height,
1
);
// Create thumbnail canvas
var thumbnail = document.createElement("canvas");
thumbnail.width = canvas.width*scale;
thumbnail.height = canvas.height*scale;
// Scale screenshot to thumbnail
var context = thumbnail.getContext("2d");
context.drawImage(canvas,
0, 0, canvas.width, canvas.height,
0, 0, thumbnail.width, thumbnail.height
);
// Save thumbnail to history
var id = decodeURIComponent(window.location.search.substring(4));
GuacamoleHistory.update(id, thumbnail.toDataURL());
};
/**
* Sets the current display scale to the given value, where 1 is 100% (1:1
* pixel ratio). Out-of-range values will be clamped in-range.
@@ -1546,11 +1588,15 @@ GuacUI.Client.attach = function(guac) {
}, false);
/*
* Disconnect on close
* Disconnect and update thumbnail on close
*/
window.onunload = function() {
GuacUI.Client.updateThumbnail();
if (GuacUI.Client.attachedClient)
GuacUI.Client.attachedClient.disconnect();
};
/*

Some files were not shown because too many files have changed in this diff Show More