Compare commits
437 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
1e6f806563 | ||
|
08ed93121b | ||
|
840cc57fdc | ||
|
e46b0d22e7 | ||
|
085b0c1393 | ||
|
e257ca0571 | ||
|
29e062ba5d | ||
|
2b9cd5508d | ||
|
79a591180d | ||
|
1e711eecbb | ||
|
7da01597e6 | ||
|
2e4bd4b1db | ||
|
f3f936e401 | ||
|
90ed08c779 | ||
|
c2874e74c8 | ||
|
3a11c9832f | ||
|
0f6ffb559b | ||
|
4769d7dc51 | ||
|
4fc2e98313 | ||
|
09a48c9c5a | ||
|
ac245e71e9 | ||
|
e5d2c711ce | ||
|
de5e7380c4 | ||
|
3e87c90c67 | ||
|
4a97872cdd | ||
|
19e67f5a18 | ||
|
3e7c01f7f9 | ||
|
0bda00e43d | ||
|
fe18faa863 | ||
|
808c715710 | ||
|
16d458bb69 | ||
|
22a7e2336e | ||
|
f773cb5ecc | ||
|
d1b6891ca7 | ||
|
a9e65975c5 | ||
|
c1377255b5 | ||
|
cce8fa4869 | ||
|
dda1018901 | ||
|
b44d02dbd9 | ||
|
cfded58563 | ||
|
8642f4291b | ||
|
e83ff8d69d | ||
|
16edf04f1d | ||
|
cbaa5cef46 | ||
|
da75dc79a7 | ||
|
9965be5a76 | ||
|
9d6f3111da | ||
|
9265ab3ac3 | ||
|
34e70394e9 | ||
|
3a750018e2 | ||
|
390eea5c3d | ||
|
dbf94b4c17 | ||
|
bc8b2de151 | ||
|
b73408043e | ||
|
2ba6e4fa7c | ||
|
9bbd7fd57d | ||
|
0ec3cb104a | ||
|
b1f1d6701e | ||
|
d54b447bf7 | ||
|
6ca7c5fb34 | ||
|
ca9ad5efbd | ||
|
b5154cbbe9 | ||
|
a3ad88b9e6 | ||
|
a4b9bf1fb9 | ||
|
a95684d864 | ||
|
6057e8a3f8 | ||
|
4fbbfb8d10 | ||
|
2daafa2c4a | ||
|
846489244e | ||
|
ec33b46848 | ||
|
e067f09958 | ||
|
8cbfcbb104 | ||
|
2b5cf494db | ||
|
39158bd7ae | ||
|
95a695ee88 | ||
|
a860732ed2 | ||
|
5d391a9fc4 | ||
|
148cd249cd | ||
|
57e643333e | ||
|
5794fab7da | ||
|
9955e7bcff | ||
|
86b252916c | ||
|
e81a8fdc5f | ||
|
92cf8c36bd | ||
|
de07a4f3cd | ||
|
320a530bbc | ||
|
e945ad9fe8 | ||
|
be3c242a7d | ||
|
f86712a05a | ||
|
777ca4c016 | ||
|
f715f48a7b | ||
|
315dd77a5a | ||
|
82f7f56590 | ||
|
8e594c7c7f | ||
|
e99c77a97f | ||
|
a80fc629a2 | ||
|
8b644d4fec | ||
|
17196ac31d | ||
|
632bb3e6d4 | ||
|
def1c59e4c | ||
|
e90e56c426 | ||
|
5f1676e56b | ||
|
72230c5a60 | ||
|
fad5eca5b9 | ||
|
8054f088b9 | ||
|
544b8b7b25 | ||
|
4d6f54f54c | ||
|
2004f4f882 | ||
|
08e33ee20e | ||
|
330973ac30 | ||
|
f78c755f12 | ||
|
bbc2cdfa30 | ||
|
d566285098 | ||
|
4c7b48588f | ||
|
22313a517f | ||
|
0c7a4911d2 | ||
|
f11bccc868 | ||
|
c62e90f0a1 | ||
|
170319f871 | ||
|
c6dd782150 | ||
|
cc7740bafb | ||
|
1626fe96b1 | ||
|
efbca46af9 | ||
|
548f44b1a7 | ||
|
2664be7ec4 | ||
|
bd3d0dd006 | ||
|
a83e218679 | ||
|
8ee34cd42b | ||
|
1ad57ee70c | ||
|
c9ab76c19c | ||
|
217655c276 | ||
|
57d1041f87 | ||
|
b56bf7b13f | ||
|
3a137213a7 | ||
|
90cb224c9a | ||
|
69ad1baa7d | ||
|
6d2dee2664 | ||
|
ace11fd77a | ||
|
afa6f62daa | ||
|
6c8f9affc2 | ||
|
fa0acb6a1a | ||
|
4fa7d26191 | ||
|
e22f489038 | ||
|
11100e3fda | ||
|
047eac18a7 | ||
|
32b5354a15 | ||
|
0d085ba708 | ||
|
d184f0eec3 | ||
|
675d502f73 | ||
|
4268004fcf | ||
|
aabc5540d0 | ||
|
68806c6366 | ||
|
343fed3245 | ||
|
c740457860 | ||
|
1d417c6075 | ||
|
ec7d3d39bd | ||
|
2e98197376 | ||
|
df17a86820 | ||
|
54c751e4c5 | ||
|
151d35459e | ||
|
1ae22ff249 | ||
|
983114af88 | ||
|
8a828bd087 | ||
|
76fc938604 | ||
|
c08cb38dbc | ||
|
f5dcb73e33 | ||
|
4108cf4df7 | ||
|
f33c536adf | ||
|
a66b36e142 | ||
|
c9bff899ba | ||
|
82097b67f4 | ||
|
3a58e0e446 | ||
|
5ce61e1f98 | ||
|
5af198db75 | ||
|
1f6279cd46 | ||
|
a07f1d5b24 | ||
|
26c0532fd5 | ||
|
9901bc2c8f | ||
|
715957feb2 | ||
|
c7dc1e8b81 | ||
|
00798e5927 | ||
|
92c3fbd827 | ||
|
a68837bf4b | ||
|
387b7f51d4 | ||
|
649a3515fc | ||
|
fe8f8be1d8 | ||
|
ea49e18f80 | ||
|
cd9f9ad523 | ||
|
bba494cdc0 | ||
|
393819bc94 | ||
|
49e3f72d89 | ||
|
a266cdaeff | ||
|
43bf2de385 | ||
|
447dc23914 | ||
|
49aaaedbe9 | ||
|
7892a196ac | ||
|
7ab956fa4a | ||
|
17044d38bd | ||
|
0dc461c30b | ||
|
ced5e06ff0 | ||
|
5e848cbc8c | ||
|
f924604217 | ||
|
efc59c0124 | ||
|
2eaa447155 | ||
|
3528424892 | ||
|
2eef7222d5 | ||
|
0d28a5b5f7 | ||
|
e0e7e26234 | ||
|
2674563535 | ||
|
a864fbac96 | ||
|
51c32a4350 | ||
|
ec95f27ee6 | ||
|
15ce5aa938 | ||
|
39b353fb24 | ||
|
7a0c8aa977 | ||
|
664418d4dc | ||
|
381cf47abf | ||
|
1578c92a88 | ||
|
6827380e99 | ||
|
62f637790c | ||
|
bf5311b5af | ||
|
d0e0418ede | ||
|
243cbbfd4b | ||
|
e7984d5a8e | ||
|
eacce7adda | ||
|
622e8629ff | ||
|
50e368ee7a | ||
|
6766fe41fe | ||
|
ba35968e8d | ||
|
4adfac1c02 | ||
|
437865b278 | ||
|
793b6c9004 | ||
|
96bd117622 | ||
|
8b2df76582 | ||
|
4196883bfb | ||
|
8585d10623 | ||
|
c715b92ed0 | ||
|
a3726399f1 | ||
|
8ce3efc7ee | ||
|
0bd3e1ac72 | ||
|
c2d4e995e7 | ||
|
1b71fef8b4 | ||
|
074a4d525d | ||
|
36e19574e0 | ||
|
ef9165b3a2 | ||
|
14cddbb210 | ||
|
2d14884454 | ||
|
ba4eeffc77 | ||
|
59179b818e | ||
|
dbcdc84b3b | ||
|
c73dae361f | ||
|
ec29371b41 | ||
|
9365f5937b | ||
|
971015d33a | ||
|
7a742c043b | ||
|
c08a0cb0ed | ||
|
b5926dec6f | ||
|
2fdcdb014f | ||
|
bd70a6290e | ||
|
3afa96f1c5 | ||
|
5da12a8091 | ||
|
08eeff5604 | ||
|
169a946a03 | ||
|
94842ce0ef | ||
|
0fb7d5ed1b | ||
|
2021fd69ec | ||
|
813764a100 | ||
|
cbd9330907 | ||
|
a250cf4aef | ||
|
50bc3cd3ef | ||
|
4fb863cfa7 | ||
|
3a1bd1eed3 | ||
|
bce3e429cf | ||
|
b5baea184f | ||
|
42cbad4117 | ||
|
09f329db62 | ||
|
6b5f9d266d | ||
|
242d9b5e6e | ||
|
99b17e573c | ||
|
701edb91f1 | ||
|
f0627db09f | ||
|
56a579e11b | ||
|
4427448eca | ||
|
203e2fcdd0 | ||
|
f7fa92e6c1 | ||
|
425257a464 | ||
|
0acc07ebb3 | ||
|
030078a619 | ||
|
0e6ca4c188 | ||
|
c7d5a1c928 | ||
|
e636a4afcd | ||
|
8b76324ffc | ||
|
8fe1e55770 | ||
|
caae694628 | ||
|
268e9d551a | ||
|
07137c2416 | ||
|
273b2a59c4 | ||
|
63364f4e72 | ||
|
abc9622d53 | ||
|
9cd7e2f67b | ||
|
9277d3b5fb | ||
|
6f46e16be8 | ||
|
0b390e406a | ||
|
f301e4654a | ||
|
62481899a1 | ||
|
869dfc8c06 | ||
|
939d456b9d | ||
|
9c70fb3caf | ||
|
baf4a677dd | ||
|
4c4820f166 | ||
|
2749bfc40c | ||
|
704e0607eb | ||
|
401fbac63e | ||
|
7f1252a70a | ||
|
2df103a348 | ||
|
489bb44c92 | ||
|
c606b6f00e | ||
|
0b4b38abe7 | ||
|
40b71fa983 | ||
|
6116db5147 | ||
|
e979c6e1e2 | ||
|
8f9d042cdd | ||
|
fcf030e693 | ||
|
31e6e01cad | ||
|
1aad0c85a4 | ||
|
e6e16334b3 | ||
|
8a285fddfa | ||
|
c9a690fe8c | ||
|
b5387d4922 | ||
|
b0a6807ea4 | ||
|
2e96074961 | ||
|
135392d245 | ||
|
ff622bb9cd | ||
|
608c1317d7 | ||
|
9f4ef20dc1 | ||
|
c45833c252 | ||
|
e260fc9790 | ||
|
cc6ba2ff41 | ||
|
bec74ddc99 | ||
|
7af7d11c8a | ||
|
8934d978fe | ||
|
8376e81532 | ||
|
6587d9ba2c | ||
|
2654036fd2 | ||
|
f9226e7deb | ||
|
56ac8aece9 | ||
|
12737df530 | ||
|
7b9c835562 | ||
|
cbb809db77 | ||
|
4f9085f0a2 | ||
|
389cf62150 | ||
|
44c367bf8f | ||
|
efb0083161 | ||
|
16c1aba3e7 | ||
|
07ed8b9762 | ||
|
fbd0a59a70 | ||
|
a08fe53383 | ||
|
e1b8c43cca | ||
|
60f20e64f3 | ||
|
6bb1109fe1 | ||
|
134f2059dd | ||
|
ec93e5c9cc | ||
|
fd6b0c9458 | ||
|
de4aef3a5c | ||
|
d024d74529 | ||
|
335fd338bd | ||
|
d7ac59f257 | ||
|
ca54c2c099 | ||
|
491a33421d | ||
|
f1cbcf86e1 | ||
|
e2e0f96d3e | ||
|
4be5d8d6e5 | ||
|
b9929566f6 | ||
|
f6492256a8 | ||
|
66d2c63a20 | ||
|
b6f582d84e | ||
|
d94cc70eff | ||
|
163245401b | ||
|
0add4b4321 | ||
|
19fdcadbcd | ||
|
7b85adaddf | ||
|
d8a0a2f80a | ||
|
ae9a9534fc | ||
|
1c1003eb41 | ||
|
4bc6d88006 | ||
|
f08593881f | ||
|
30cf167e5b | ||
|
064d881c1e | ||
|
c3f9d673bd | ||
|
4695dcaa0c | ||
|
e23f22d92d | ||
|
962f1fcc1c | ||
|
6bc70ff4de | ||
|
aaf4b539d5 | ||
|
03d0fc2ba4 | ||
|
407b0ebf55 | ||
|
033ac0d1f6 | ||
|
c9e9f60ed1 | ||
|
162c84e21c | ||
|
9f6d126484 | ||
|
0877cc01f4 | ||
|
00e85357cf | ||
|
cc72b3742f | ||
|
dff20bd6e1 | ||
|
69b6f7d7d8 | ||
|
9881df5117 | ||
|
bc2f83c937 | ||
|
fa78b4b295 | ||
|
95230292c3 | ||
|
fdaf974009 | ||
|
26764c4cab | ||
|
52d0e8977c | ||
|
1d7df7814c | ||
|
7e9a37e768 | ||
|
80bf6d77f1 | ||
|
2d7a2d6049 | ||
|
8363b41a68 | ||
|
9804678a30 | ||
|
1187c0aa5e | ||
|
36adbe387c | ||
|
df2dcf7acd | ||
|
3fbb492921 | ||
|
1b97e2015d | ||
|
3814986e41 | ||
|
037a4b523b | ||
|
522d493557 | ||
|
2ac06f7345 | ||
|
0745a7aa9a | ||
|
7e419aed8d | ||
|
baa9565140 | ||
|
069dfb154a | ||
|
485ed4d60b | ||
|
4de3e83ade | ||
|
73713eac69 | ||
|
6ed924655d | ||
|
7706262702 | ||
|
e0843179cd |
2
actors/.gitignore
vendored
@@ -6,3 +6,5 @@ udsactor*.changes
|
||||
/udsactor_1.7.0.dsc
|
||||
/udsactor_1.7.0.tar.xz
|
||||
/udsactor*.rpm
|
||||
/udsactor_2.1.0_amd64.buildinfo
|
||||
linux/debian/files
|
||||
|
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
VERSION=1.9.1
|
||||
VERSION=`cat ../../VERSION`
|
||||
RELEASE=1
|
||||
|
||||
top=`pwd`
|
||||
|
@@ -1,3 +1,15 @@
|
||||
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
|
||||
|
||||
-- Adolfo Gómez García <agomez@virtualcable.es> Tue, 01 Mar 2016 03:39:21 +0100
|
||||
|
||||
udsactor (1.9.1) stable; urgency=medium
|
||||
|
||||
* Upgrade for 1.9.1
|
||||
|
@@ -1,3 +0,0 @@
|
||||
udsactor-nx_1.9.1_all.deb x11 optional
|
||||
udsactor-xrdp_1.9.1_all.deb x11 optional
|
||||
udsactor_1.9.1_all.deb admin optional
|
@@ -5,6 +5,7 @@
|
||||
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
|
||||
|
@@ -8,5 +8,5 @@ Type=Application
|
||||
NoDisplay=true
|
||||
X-KDE-autostart-after=panel
|
||||
X-KDE-StartupNotify=false
|
||||
X-DBUS-StartupType=Unique
|
||||
X-KDE-UniqueApplet=true
|
||||
X-DBUS-StartupType=None
|
||||
X-KDE-UniqueApplet=false
|
||||
|
@@ -33,10 +33,11 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import sys
|
||||
from PyQt4 import QtGui
|
||||
from PyQt4 import QtCore
|
||||
from PyQt4 import QtGui # @UnresolvedImport
|
||||
from PyQt4 import QtCore # @UnresolvedImport
|
||||
import pickle
|
||||
import time
|
||||
import datetime
|
||||
import signal
|
||||
from udsactor import ipc
|
||||
from udsactor import utils
|
||||
@@ -50,6 +51,8 @@ from udsactor import VERSION
|
||||
|
||||
trayIcon = None
|
||||
|
||||
doLogoff = False
|
||||
|
||||
|
||||
def sigTerm(sigNo, stackFrame):
|
||||
if trayIcon:
|
||||
@@ -175,10 +178,13 @@ class UDSSystemTray(QtGui.QSystemTrayIcon):
|
||||
exitAction.triggered.connect(self.about)
|
||||
self.setContextMenu(self.menu)
|
||||
self.ipc = MessagesProcessor()
|
||||
self.sessionStart = datetime.datetime.now()
|
||||
self.maxIdleTime = None
|
||||
self.timer = QtCore.QTimer()
|
||||
self.timer.timeout.connect(self.checkIdle)
|
||||
self.showIdleWarn = True
|
||||
self.maxSessionTime = None
|
||||
self.showMaxSessionWarn = True
|
||||
self.timer = QtCore.QTimer()
|
||||
self.timer.timeout.connect(self.checkTimers)
|
||||
|
||||
if self.ipc.isAlive() is False:
|
||||
raise Exception('No connection to service, exiting.')
|
||||
@@ -206,6 +212,27 @@ class UDSSystemTray(QtGui.QSystemTrayIcon):
|
||||
# If this is running, it's because he have logged in
|
||||
self.ipc.sendLogin(operations.getCurrentUser())
|
||||
|
||||
def checkTimers(self):
|
||||
self.checkIdle()
|
||||
self.checkMaxSession()
|
||||
|
||||
def checkMaxSession(self):
|
||||
if self.maxSessionTime is None or self.maxSessionTime == 0:
|
||||
logger.debug('Returning because maxSessionTime is cero')
|
||||
return
|
||||
|
||||
remainingTime = self.maxSessionTime - (datetime.datetime.now() - self.sessionStart).total_seconds()
|
||||
logger.debug('Remaining time: {}'.format(remainingTime))
|
||||
|
||||
if self.showMaxSessionWarn is True and remainingTime < 300: # With five minutes, show a warning message
|
||||
self.showMaxSessionWarn = False
|
||||
self.msgDlg.displayMessage('Your session will expire in less that 5 minutes. Please, save your work and disconnect.')
|
||||
return
|
||||
|
||||
if remainingTime <= 0:
|
||||
logger.debug('Remaining time is less than cero, exiting')
|
||||
self.quit()
|
||||
|
||||
def checkIdle(self):
|
||||
if self.maxIdleTime is None: # No idle check
|
||||
return
|
||||
@@ -217,19 +244,19 @@ class UDSSystemTray(QtGui.QSystemTrayIcon):
|
||||
idleTime = operations.getIdleDuration()
|
||||
remainingTime = self.maxIdleTime - idleTime
|
||||
|
||||
if remainingTime > 300: # Reset show Warning dialog if we have more than 5 minutes left
|
||||
if remainingTime > 120: # Reset show Warning dialog if we have more than 5 minutes left
|
||||
self.showIdleWarn = True
|
||||
|
||||
logger.debug('User has been idle for: {}'.format(idleTime))
|
||||
|
||||
if remainingTime <= 0:
|
||||
logger.info('User has been idle for too long, notifying Broker that service can be reclaimed')
|
||||
self.quit()
|
||||
|
||||
if self.showIdleWarn is True and remainingTime < 120: # With two minutes, show a warning message
|
||||
self.showIdleWarn = False
|
||||
self.msgDlg.displayMessage("You have been idle for too long. The session will end if you don't resume operations")
|
||||
|
||||
if remainingTime <= 0:
|
||||
logger.info('User has been idle for too long, notifying Broker that service can be reclaimed')
|
||||
self.quit(logoff=True)
|
||||
|
||||
def displayMessage(self, message):
|
||||
logger.debug('Displaying message')
|
||||
self.msgDlg.displayMessage(message)
|
||||
@@ -247,7 +274,7 @@ class UDSSystemTray(QtGui.QSystemTrayIcon):
|
||||
'''
|
||||
Invoked when received information from service
|
||||
'''
|
||||
logger.debug('Got information message: {}'.format(info))
|
||||
logger.info('Got information message: {}'.format(info))
|
||||
if 'idle' in info:
|
||||
idle = int(info['idle'])
|
||||
operations.initIdleDuration(idle)
|
||||
@@ -256,10 +283,17 @@ class UDSSystemTray(QtGui.QSystemTrayIcon):
|
||||
else:
|
||||
self.maxIdleTime = None
|
||||
|
||||
if 'maxSession' in info:
|
||||
maxSession = int(info['maxSession'])
|
||||
# operations.initMaxSession(maxSession)
|
||||
self.maxSessionTime = maxSession
|
||||
logger.debug('Set maxsession to {}'.format(maxSession))
|
||||
|
||||
def about(self):
|
||||
self.aboutDlg.exec_()
|
||||
|
||||
def quit(self):
|
||||
def quit(self, logoff=False):
|
||||
global doLogoff
|
||||
logger.debug('Quit invoked')
|
||||
if self.stopped is False:
|
||||
self.stopped = True
|
||||
@@ -272,10 +306,7 @@ class UDSSystemTray(QtGui.QSystemTrayIcon):
|
||||
# May we have lost connection with server, simply exit in that case
|
||||
pass
|
||||
|
||||
try:
|
||||
operations.loggoff() # Invoke log off
|
||||
except Exception:
|
||||
pass
|
||||
doLogoff = logoff
|
||||
|
||||
self.app.quit()
|
||||
|
||||
@@ -308,4 +339,12 @@ 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)
|
||||
|
@@ -88,7 +88,7 @@
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
<number>2</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="aboutTab">
|
||||
<attribute name="title">
|
||||
@@ -109,7 +109,7 @@
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'Verdana'; font-size:9pt; font-weight:400; font-style:normal;">
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif';"><br /></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-weight:600;">(c) 2014, Virtual Cable S.L.U.</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-weight:600;">(c) 2012-2016, Virtual Cable S.L.U.</span></p>
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-style:italic;"><br /></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a href="http://www.udsenterprise.com"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt; text-decoration: underline; color:#0000ff;">http://www.udsenterprise.com</span></a></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a href="http://www.openuds.org"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt; text-decoration: underline; color:#0000ff;">http://www.openuds.org</span></a></p>
|
||||
@@ -167,7 +167,7 @@ p, li { white-space: pre-wrap; }
|
||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'Verdana'; font-size:9pt; font-weight:400; font-style:normal;">
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">Copyright (c) 2014 Virtual Cable S.L.</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">Copyright (c) 2012-2016 Virtual Cable S.L.</span></p>
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;"><br /></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">All rights reserved.</span></p>
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;"><br /></p>
|
||||
|
@@ -109,6 +109,7 @@ class Api(object):
|
||||
self.mac = None
|
||||
self.url = "{}://{}/rest/actor/".format(('http', 'https')[self.useSSL], self.host)
|
||||
self.idle = None
|
||||
self.maxSession = None
|
||||
self.secretKey = six.text_type(uuid.uuid4())
|
||||
try:
|
||||
self.newerRequestLib = requests.__version__.split('.')[0] >= '1'
|
||||
|
@@ -34,14 +34,14 @@ from __future__ import unicode_literals
|
||||
# On centos, old six release does not includes byte2int, nor six.PY2
|
||||
import six
|
||||
|
||||
VERSION = '1.9.0'
|
||||
VERSION = '2.1.0'
|
||||
|
||||
__title__ = 'udsactor'
|
||||
__version__ = VERSION
|
||||
__build__ = 0x010750
|
||||
__author__ = 'Adolfo Gómez'
|
||||
__author__ = 'Adolfo Gómez <dkmaster@dkmon.com>'
|
||||
__license__ = "BSD 3-clause"
|
||||
__copyright__ = "Copyright 2014-2015 VirtualCable S.L.U."
|
||||
__copyright__ = "Copyright 2014-2016 VirtualCable S.L.U."
|
||||
|
||||
|
||||
if not hasattr(six, 'byte2int'):
|
||||
|
@@ -172,7 +172,10 @@ class HTTPServerHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||
|
||||
def get_information(self, params):
|
||||
# TODO: Return something useful? :)
|
||||
return 'Information'
|
||||
return 'Up and running'
|
||||
|
||||
def get_uuid(self, params):
|
||||
return self.service.api.uuid
|
||||
|
||||
def log_error(self, fmt, *args):
|
||||
logger.error('HTTP ' + fmt % args)
|
||||
@@ -188,18 +191,43 @@ class HTTPServerThread(threading.Thread):
|
||||
if HTTPServerHandler.uuid is None:
|
||||
HTTPServerHandler.uuid = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(48))
|
||||
|
||||
self.certFile = createSelfSignedCert()
|
||||
HTTPServerHandler.service = service
|
||||
|
||||
self.certFile = createSelfSignedCert()
|
||||
self.server = socketserver.TCPServer(address, HTTPServerHandler)
|
||||
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.socket = ssl.wrap_socket(self.server.socket, certfile=self.certFile, server_side=True)
|
||||
|
||||
def getServerUrl(self):
|
||||
return 'https://{}:{}/{}'.format(self.server.server_address[0], self.server.server_address[1], HTTPServerHandler.uuid)
|
||||
return 'https://{}:{}/{}'.format(self.getIp(), self.getPort(), HTTPServerHandler.uuid)
|
||||
|
||||
def stop(self):
|
||||
logger.debug('Stopping REST Service')
|
||||
self.server.shutdown()
|
||||
|
||||
def restart(self, address=None):
|
||||
|
||||
if address is None:
|
||||
# address = self.server.server_address
|
||||
address = self.address
|
||||
|
||||
self.address = (address[0], self.address[1]) # Copy address & keep it for future reference, port is never changed once assigned on init
|
||||
|
||||
# Listening on 0.0.0.0, does not need to restart listener..
|
||||
# self.stop()
|
||||
# self.initiateServer(address)
|
||||
|
||||
def run(self):
|
||||
self.server.serve_forever()
|
||||
|
@@ -36,6 +36,8 @@ import sys
|
||||
import six
|
||||
import traceback
|
||||
import pickle
|
||||
import errno
|
||||
import time
|
||||
|
||||
from udsactor.utils import toUnicode
|
||||
from udsactor.log import logger
|
||||
@@ -407,8 +409,11 @@ 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()
|
||||
|
@@ -36,6 +36,7 @@ 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
|
||||
@@ -44,15 +45,23 @@ from udsactor.linux.daemon import Daemon
|
||||
from udsactor.linux import renamer
|
||||
|
||||
import sys
|
||||
import time
|
||||
import os
|
||||
import stat
|
||||
import subprocess
|
||||
|
||||
POST_CMD = '/etc/udsactor/post'
|
||||
PRECONNECT_CMD = '/etc/udsactor/pre'
|
||||
|
||||
try:
|
||||
from prctl import set_proctitle
|
||||
from prctl import set_proctitle # @UnresolvedImport
|
||||
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)
|
||||
@@ -62,6 +71,12 @@ 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:
|
||||
@@ -75,13 +90,48 @@ 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)
|
||||
self.setReady()
|
||||
|
||||
if self.rebootMachineAfterOp is False:
|
||||
self.setReady()
|
||||
else:
|
||||
logger.info('Rebooting computer to activate new name {}'.format(name))
|
||||
self.reboot()
|
||||
|
||||
|
||||
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):
|
||||
initCfg()
|
||||
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))
|
||||
|
||||
logger.debug('Running Daemon')
|
||||
set_proctitle('UDSActorDaemon')
|
||||
@@ -106,6 +156,21 @@ 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()
|
||||
|
||||
# *********************
|
||||
|
@@ -57,7 +57,7 @@ def _getMacAddr(ifname):
|
||||
try:
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
info = bytearray(fcntl.ioctl(s.fileno(), 0x8927, struct.pack(str('256s'), ifname[:15])))
|
||||
return six.text_type(''.join(['%02x:' % char for char in info[18:24]])[:-1])
|
||||
return six.text_type(''.join(['%02x:' % char for char in info[18:24]])[:-1]).upper()
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
@@ -122,7 +122,7 @@ def getComputerName():
|
||||
def getNetworkInfo():
|
||||
for ifname in _getInterfaces():
|
||||
ip, mac = _getIpAndMac(ifname)
|
||||
if mac != '00:00:00:00:00:00': # Skips local interfaces
|
||||
if mac != '00:00:00:00:00:00' and ip.startswith('169.254') is False: # Skips local interfaces & interfaces with no dhcp IPs
|
||||
yield utils.Bunch(name=ifname, mac=mac, ip=ip)
|
||||
|
||||
|
||||
@@ -194,8 +194,10 @@ try:
|
||||
# Fix result type to XScreenSaverInfo Structure
|
||||
xss.XScreenSaverQueryExtension.restype = ctypes.c_int
|
||||
xss.XScreenSaverAllocInfo.restype = ctypes.POINTER(XScreenSaverInfo) # Result in a XScreenSaverInfo structure
|
||||
display = xlib.XOpenDisplay(None)
|
||||
info = xss.XScreenSaverAllocInfo()
|
||||
except Exception: # Libraries not accesible, not found or whatever..
|
||||
xlib = xss = None
|
||||
xlib = xss = display = info = None
|
||||
|
||||
|
||||
def initIdleDuration(atLeastSeconds):
|
||||
@@ -219,20 +221,18 @@ def getIdleDuration():
|
||||
if xlib is None or xss is None:
|
||||
return 0 # Libraries not available
|
||||
|
||||
# production code might want to not hardcode the offset 16...
|
||||
display = xlib.XOpenDisplay(None)
|
||||
|
||||
event_base = ctypes.c_int()
|
||||
error_base = ctypes.c_int()
|
||||
|
||||
available = xss.XScreenSaverQueryExtension(display, ctypes.byref(event_base), ctypes.byref(error_base))
|
||||
|
||||
if available != 1:
|
||||
return 0 # No screen saver is available, no way of getting idle
|
||||
|
||||
info = xss.XScreenSaverAllocInfo()
|
||||
xss.XScreenSaverQueryInfo(display, xlib.XDefaultRootWindow(display), info)
|
||||
|
||||
if info.contents.state != 0:
|
||||
# Centos seems to set state to 1?? (weird, but it's happening don't know why... will try this way)
|
||||
if info.contents.state != 0 and 'centos' not in platform.linux_distribution()[0].lower().strip():
|
||||
return 3600 * 100 * 1000 # If screen saver is active, return a high enough value
|
||||
|
||||
return info.contents.idle / 1000.0
|
||||
|
@@ -78,3 +78,11 @@ 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
|
||||
|
||||
|
@@ -70,6 +70,8 @@ def initCfg():
|
||||
cfg = None
|
||||
break
|
||||
|
||||
return cfg
|
||||
|
||||
|
||||
class CommonService(object):
|
||||
def __init__(self):
|
||||
@@ -84,6 +86,23 @@ 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()])
|
||||
|
||||
@@ -136,6 +155,13 @@ 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
|
||||
@@ -205,8 +231,17 @@ class CommonService(object):
|
||||
try:
|
||||
# Notifies all interfaces IPs
|
||||
self.api.notifyIpChanges(((v.mac, v.ip) for v in netInfo))
|
||||
|
||||
# Regenerates Known ips
|
||||
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())
|
||||
# And new listening address
|
||||
self.httpServer.restart(address)
|
||||
# sends notification
|
||||
self.api.notifyComm(self.httpServer.getServerUrl())
|
||||
|
||||
except Exception as e:
|
||||
logger.warn('Got an error notifiying IPs to broker: {} (will retry in a bit)'.format(e.message.decode('windows-1250', 'ignore')))
|
||||
|
||||
@@ -217,14 +252,21 @@ class CommonService(object):
|
||||
return
|
||||
|
||||
if msg == ipc.REQ_LOGIN:
|
||||
self.api.login(data)
|
||||
elif msg == ipc.REQ_LOGOUT:
|
||||
res = self.api.login(data).split('\t')
|
||||
# third parameter, if exists, sets maxSession duration to this.
|
||||
# First & second parameters are ip & hostname of connection source
|
||||
if len(res) >= 3:
|
||||
self.api.maxSession = int(res[2]) # Third parameter is max session duration
|
||||
msg = ipc.REQ_INFORMATION # Senf information, requested or not, to client on login notification
|
||||
if msg == ipc.REQ_LOGOUT:
|
||||
self.api.logout(data)
|
||||
self.onLogout(data)
|
||||
elif msg == ipc.REQ_INFORMATION:
|
||||
if msg == ipc.REQ_INFORMATION:
|
||||
info = {}
|
||||
if self.api.idle is not None:
|
||||
info['idle'] = self.api.idle
|
||||
if self.api.maxSession is not None:
|
||||
info['maxSession'] = self.api.maxSession
|
||||
self.ipc.sendInformationMessage(info)
|
||||
|
||||
def initIPC(self):
|
||||
@@ -236,7 +278,7 @@ class CommonService(object):
|
||||
self.ipc.start()
|
||||
|
||||
if self.api.mac in self.knownIps:
|
||||
address = (self.knownIps[self.api.mac], random.randrange(40000, 44000))
|
||||
address = (self.knownIps[self.api.mac], random.randrange(43900, 44000))
|
||||
logger.info('Starting REST listener at {}'.format(address))
|
||||
self.httpServer = httpserver.HTTPServerThread(address, self)
|
||||
self.httpServer.start()
|
||||
|
@@ -76,7 +76,7 @@ class SensLogon(win32com.server.policy.DesignatedWrapPolicy):
|
||||
data = self.service.api.login(args[0])
|
||||
logger.debug('Data received for login: {}'.format(data))
|
||||
data = data.split('\t')
|
||||
if len(data) == 2:
|
||||
if len(data) >= 2:
|
||||
logger.debug('Data is valid: {}'.format(data))
|
||||
windir = os.environ['windir']
|
||||
with open(os.path.join(windir, 'remoteip.txt'), 'w') as f:
|
||||
|
@@ -40,9 +40,11 @@ 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
|
||||
|
||||
@@ -55,6 +57,8 @@ 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):
|
||||
'''
|
||||
@@ -113,7 +117,7 @@ class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService):
|
||||
|
||||
operations.renameComputer(name)
|
||||
# Reboot just after renaming
|
||||
logger.info('Rebooting computer got activate new name {}'.format(name))
|
||||
logger.info('Rebooting computer to activate new name {}'.format(name))
|
||||
self.reboot()
|
||||
|
||||
def oneStepJoin(self, name, domain, ou, account, password):
|
||||
@@ -158,12 +162,15 @@ 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:
|
||||
if ver >= 60 and store.useOldJoinSystem() is False:
|
||||
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):
|
||||
@@ -292,6 +299,17 @@ 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 *
|
||||
# *********************
|
||||
|
@@ -48,7 +48,7 @@ class LocalLogger(object):
|
||||
filename=os.path.join(tempfile.gettempdir(), 'udsactor.log'),
|
||||
filemode='a',
|
||||
format='%(levelname)s %(asctime)s %(message)s',
|
||||
level=logging.DEBUG
|
||||
level=logging.INFO
|
||||
)
|
||||
self.logger = logging.getLogger('udsactor')
|
||||
self.serviceLogger = False
|
||||
|
@@ -45,6 +45,7 @@ from udsactor.log import logger
|
||||
|
||||
|
||||
def getErrorMessage(res=0):
|
||||
# sys_fs_enc = sys.getfilesystemencoding() or 'mbcs'
|
||||
msg = win32api.FormatMessage(res)
|
||||
return msg.decode('windows-1250', 'ignore')
|
||||
|
||||
|
@@ -82,7 +82,6 @@ def readConfig():
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def writeConfig(data, fixPermissions=True):
|
||||
try:
|
||||
key = wreg.OpenKey(baseKey, path, 0, wreg.KEY_ALL_ACCESS) # @UndefinedVariable
|
||||
@@ -93,3 +92,32 @@ 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
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
VERSION=`cat ../../VERSION`
|
||||
VERSION=`cat ../../../VERSION`
|
||||
RELEASE=1
|
||||
# Debian based
|
||||
dpkg-buildpackage -b
|
@@ -1,3 +1,15 @@
|
||||
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
|
||||
|
||||
-- Adolfo Gómez García <agomez@virtualcable.es> Tue, 01 Mar 2016 09:33:18 +0100
|
||||
|
||||
udsclient (1.9.1) stable; urgency=medium
|
||||
|
||||
* Minor fixes & making version match UDS version
|
@@ -10,6 +10,6 @@ Package: udsclient
|
||||
Section: admin
|
||||
Priority: optional
|
||||
Architecture: all
|
||||
Depends: python-paramiko (>=0.8.2), python-qt4 (>=4.9), python-six(>=1.1), python (>=2.7), rdesktop | freerdp-x11, ${misc:Depends}
|
||||
Depends: python-paramiko (>=0.8.2), python-qt4 (>=4.9), python-six(>=1.1), python (>=2.7), rdesktop | freerdp-x11, desktop-file-utils, ${misc:Depends}
|
||||
Description: Client connector for Universal Desktop Services (UDS) Broker
|
||||
This package provides the required components to allow this machine to connect to services provided by UDS Broker.
|
2
client/full/linux/debian/files
Normal file
@@ -0,0 +1,2 @@
|
||||
udsclient_2.1.0_all.deb admin optional
|
||||
udsclient_2.1.0_amd64.buildinfo admin optional
|
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014 Virtual Cable S.L.
|
||||
# Copyright (c) 2017 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@@ -54,6 +54,9 @@ class UDSClient(QtGui.QMainWindow):
|
||||
ticket = None
|
||||
scrambler = None
|
||||
withError = False
|
||||
animTimer = None
|
||||
anim = 0
|
||||
animInverted = False
|
||||
|
||||
def __init__(self):
|
||||
QtGui.QMainWindow.__init__(self)
|
||||
@@ -73,8 +76,14 @@ class UDSClient(QtGui.QMainWindow):
|
||||
vpos = (screen.height() - mysize.height() - mysize.height()) / 2
|
||||
self.move(hpos, vpos)
|
||||
|
||||
self.animTimer = QtCore.QTimer()
|
||||
QtCore.QObject.connect(self.animTimer, QtCore.SIGNAL('timeout()'), self.updateAnim)
|
||||
|
||||
self.activateWindow()
|
||||
|
||||
self.startAnim()
|
||||
|
||||
|
||||
def closeWindow(self):
|
||||
self.close()
|
||||
|
||||
@@ -90,21 +99,36 @@ class UDSClient(QtGui.QMainWindow):
|
||||
# return
|
||||
|
||||
def showError(self, e):
|
||||
self.ui.progressBar.setValue(100)
|
||||
self.ui.info.setText('Error')
|
||||
QtGui.QMessageBox.critical(self, 'Error', six.text_type(e), QtGui.QMessageBox.Ok)
|
||||
logger.error('got error: {}'.format(e))
|
||||
self.stopAnim()
|
||||
self.ui.info.setText('UDS Plugin Error') # In fact, main window is hidden, so this is not visible... :)
|
||||
self.closeWindow()
|
||||
QtGui.QMessageBox.critical(None, 'UDS Plugin Error', '{}'.format(e), QtGui.QMessageBox.Ok)
|
||||
self.withError = True
|
||||
|
||||
def cancelPushed(self):
|
||||
self.close()
|
||||
|
||||
def _updateProgressBar(self, increment, maximum=100):
|
||||
val = self.ui.progressBar.value()
|
||||
val += increment
|
||||
if val > maximum:
|
||||
val = maximum
|
||||
self.ui.progressBar.setValue(val)
|
||||
@QtCore.pyqtSlot()
|
||||
def updateAnim(self):
|
||||
self.anim += 2
|
||||
if self.anim > 99:
|
||||
self.animInverted = not self.animInverted
|
||||
self.ui.progressBar.setInvertedAppearance(self.animInverted)
|
||||
self.anim = 0
|
||||
|
||||
self.ui.progressBar.setValue(self.anim)
|
||||
|
||||
def startAnim(self):
|
||||
self.ui.progressBar.invertedAppearance = False
|
||||
self.anim = 0
|
||||
self.animInverted = False
|
||||
self.ui.progressBar.setInvertedAppearance(self.animInverted)
|
||||
self.animTimer.start(40)
|
||||
|
||||
def stopAnim(self):
|
||||
self.ui.progressBar.invertedAppearance = False
|
||||
self.animTimer.stop()
|
||||
|
||||
@QtCore.pyqtSlot()
|
||||
def getVersion(self):
|
||||
@@ -114,10 +138,7 @@ class UDSClient(QtGui.QMainWindow):
|
||||
@QtCore.pyqtSlot(dict)
|
||||
def version(self, data):
|
||||
try:
|
||||
self.ui.progressBar.setValue(20)
|
||||
|
||||
self.processError(data)
|
||||
|
||||
self.ui.info.setText('Processing...')
|
||||
|
||||
if data['result']['requiredVersion'] > VERSION:
|
||||
@@ -128,17 +149,21 @@ class UDSClient(QtGui.QMainWindow):
|
||||
self.getTransportData()
|
||||
|
||||
except RetryException as e:
|
||||
self._updateProgressBar(5, 80)
|
||||
self.ui.info.setText(six.text_type(e))
|
||||
QtCore.QTimer.singleShot(1000, self.getVersion)
|
||||
|
||||
except Exception as e:
|
||||
self.showError(e)
|
||||
|
||||
|
||||
@QtCore.pyqtSlot()
|
||||
def getTransportData(self):
|
||||
self.req = RestRequest('/{}/{}'.format(self.ticket, self.scrambler), self, self.transportDataReceived, params={'hostname': tools.getHostName(), 'version': VERSION})
|
||||
self.req.get()
|
||||
try:
|
||||
self.req = RestRequest('/{}/{}'.format(self.ticket, self.scrambler), self, self.transportDataReceived, params={'hostname': tools.getHostName(), 'version': VERSION})
|
||||
self.req.get()
|
||||
except Exception as e:
|
||||
logger.exception('Got exception: {}'.format(e))
|
||||
raise e
|
||||
|
||||
|
||||
@QtCore.pyqtSlot(dict)
|
||||
@@ -147,11 +172,10 @@ class UDSClient(QtGui.QMainWindow):
|
||||
try:
|
||||
self.processError(data)
|
||||
|
||||
self.ui.progressBar.setValue(80)
|
||||
|
||||
script = data['result'].decode('base64').decode('bz2')
|
||||
|
||||
self.ui.progressBar.setValue(100)
|
||||
self.stopAnim()
|
||||
|
||||
if 'darwin' in sys.platform:
|
||||
self.showMinimized()
|
||||
|
||||
@@ -161,7 +185,6 @@ class UDSClient(QtGui.QMainWindow):
|
||||
six.exec_(script, globals(), {'parent': self})
|
||||
|
||||
except RetryException as e:
|
||||
self._updateProgressBar(5, 80)
|
||||
self.ui.info.setText(six.text_type(e) + ', retrying access...')
|
||||
# Retry operation in ten seconds
|
||||
QtCore.QTimer.singleShot(10000, self.getTransportData)
|
||||
@@ -201,6 +224,23 @@ def done(data):
|
||||
QtGui.QMessageBox.critical(None, 'Notice', six.text_type(data.data), QtGui.QMessageBox.Ok)
|
||||
sys.exit(0)
|
||||
|
||||
# Ask user to aprobe endpoint
|
||||
def approveHost(host, parentWindow=None):
|
||||
settings = QtCore.QSettings()
|
||||
settings.beginGroup('endpoints')
|
||||
|
||||
approved = settings.value(host, False).toBool()
|
||||
|
||||
errorString = '<p>The server <b>{}</b> must be approved:</p>'.format(host)
|
||||
errorString += '<p>Only approve UDS servers that you trust to avoid security issues.</p>'
|
||||
|
||||
if approved or QtGui.QMessageBox.warning(parentWindow, 'ACCESS Warning', errorString, QtGui.QMessageBox.Yes | QtGui.QMessageBox.No) == QtGui.QMessageBox.Yes:
|
||||
settings.setValue(host, True)
|
||||
approved = True
|
||||
|
||||
settings.endGroup()
|
||||
return approved
|
||||
|
||||
if __name__ == "__main__":
|
||||
logger.debug('Initializing connector')
|
||||
# Initialize app
|
||||
@@ -222,6 +262,10 @@ if __name__ == "__main__":
|
||||
# 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()
|
||||
@@ -232,18 +276,25 @@ if __name__ == "__main__":
|
||||
|
||||
except Exception:
|
||||
logger.debug('Detected execution without valid URI, exiting')
|
||||
QtGui.QMessageBox.critical(None, 'Notice', 'This program is designed to be used by UDS', QtGui.QMessageBox.Ok)
|
||||
QtGui.QMessageBox.critical(None, 'Notice', 'UDS Client Version {}'.format(VERSION), QtGui.QMessageBox.Ok)
|
||||
sys.exit(1)
|
||||
|
||||
# Setup REST api endpoint
|
||||
RestRequest.restApiUrl = '{}://{}/rest/client'.format(['http', 'https'][ssl], host)
|
||||
logger.debug('Setting requert URL to {}'.format(RestRequest.restApiUrl))
|
||||
logger.debug('Setting request URL to {}'.format(RestRequest.restApiUrl))
|
||||
# RestRequest.restApiUrl = 'https://172.27.0.1/rest/client'
|
||||
|
||||
try:
|
||||
logger.debug('Starting execution')
|
||||
|
||||
# Approbe before going on
|
||||
if approveHost(host) is False:
|
||||
raise Exception('Host {} was not approved'.format(host))
|
||||
|
||||
win = UDSClient()
|
||||
win.show()
|
||||
|
||||
|
||||
win.start()
|
||||
|
||||
exitVal = app.exec_()
|
||||
@@ -257,12 +308,3 @@ if __name__ == "__main__":
|
||||
logger.debug('Exiting')
|
||||
sys.exit(exitVal)
|
||||
|
||||
# Build base REST
|
||||
|
||||
# v = RestRequest('', done)
|
||||
# v.get()
|
||||
|
||||
# sys.exit(1)
|
||||
|
||||
# myapp = UDSConfigDialog(cfg)
|
||||
# myapp.show()
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 7.6 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 137 KiB After Width: | Height: | Size: 137 KiB |
@@ -34,7 +34,7 @@ from __future__ import unicode_literals
|
||||
# On centos, old six release does not includes byte2int, nor six.PY2
|
||||
import six
|
||||
|
||||
VERSION = '1.9.0'
|
||||
VERSION = '2.1.0'
|
||||
|
||||
__title__ = 'udclient'
|
||||
__version__ = VERSION
|
@@ -14,15 +14,15 @@ import time
|
||||
|
||||
from .log import logger
|
||||
|
||||
class ForwardServer (SocketServer.ThreadingTCPServer):
|
||||
class ForwardServer(SocketServer.ThreadingTCPServer):
|
||||
daemon_threads = True
|
||||
allow_reuse_address = True
|
||||
|
||||
|
||||
class Handler (SocketServer.BaseRequestHandler):
|
||||
class Handler(SocketServer.BaseRequestHandler):
|
||||
|
||||
def handle(self):
|
||||
self.thread.isConnected = True
|
||||
self.thread.currentConnections += 1
|
||||
|
||||
try:
|
||||
chan = self.ssh_transport.open_channel('direct-tcpip',
|
||||
@@ -65,10 +65,11 @@ class Handler (SocketServer.BaseRequestHandler):
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if self.thread.stoppable is True:
|
||||
self.thread.currentConnections -= 1
|
||||
|
||||
if self.thread.stoppable is True and self.thread.currentConnections == 0:
|
||||
self.thread.stop()
|
||||
|
||||
self.thread.isConnected = False
|
||||
|
||||
class ForwardThread(threading.Thread):
|
||||
status = 0 # Connecting
|
||||
@@ -92,31 +93,49 @@ class ForwardThread(threading.Thread):
|
||||
self.stopEvent = threading.Event()
|
||||
|
||||
self.timer = None
|
||||
self.isConnected = False
|
||||
self.currentConnections = 0
|
||||
self.stoppable = False
|
||||
self.client = None
|
||||
|
||||
def clone(self, redirectHost, redirectPort, localPort=None):
|
||||
if localPort is None:
|
||||
localPort = random.randrange(40000, 50000)
|
||||
|
||||
ft = ForwardThread(self.server, self.port, self.username, self.password, localPort, redirectHost, redirectPort, self.waitTime)
|
||||
ft.client = self.client
|
||||
self.client.useCount += 1 # One more using this client
|
||||
ft.start()
|
||||
|
||||
while ft.status == 0:
|
||||
time.sleep(0.1)
|
||||
|
||||
return (ft, localPort)
|
||||
|
||||
|
||||
def _timerFnc(self):
|
||||
self.timer = None
|
||||
logger.debug('Timer fnc: {}'.format(self.isConnected))
|
||||
logger.debug('Timer fnc: {}'.format(self.currentConnections))
|
||||
self.stoppable = True
|
||||
if self.isConnected is False:
|
||||
if self.currentConnections <= 0:
|
||||
self.stop()
|
||||
|
||||
def run(self):
|
||||
self.client = paramiko.SSHClient()
|
||||
self.client.load_system_host_keys()
|
||||
self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
if self.client is None:
|
||||
self.client = paramiko.SSHClient()
|
||||
self.client.useCount = 1 # Custom added variable, to keep track on when to close tunnel
|
||||
self.client.load_system_host_keys()
|
||||
self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
|
||||
logger.debug('Connecting to ssh host %s:%d ...' % (self.server, self.port))
|
||||
logger.debug('Connecting to ssh host %s:%d ...' % (self.server, self.port))
|
||||
|
||||
try:
|
||||
self.client.connect(self.server, self.port, username=self.username, password=self.password, timeout=5)
|
||||
except Exception as e:
|
||||
logger.exception('Exception connecting: ')
|
||||
self.status = 2 # Error
|
||||
return
|
||||
try:
|
||||
self.client.connect(self.server, self.port, username=self.username, password=self.password, timeout=5)
|
||||
except Exception as e:
|
||||
logger.exception('Exception connecting: ')
|
||||
self.status = 2 # Error
|
||||
return
|
||||
|
||||
class SubHandler (Handler):
|
||||
class SubHandler(Handler):
|
||||
chain_host = self.redirectHost
|
||||
chain_port = self.redirectPort
|
||||
ssh_transport = self.client.get_transport()
|
||||
@@ -141,7 +160,10 @@ class ForwardThread(threading.Thread):
|
||||
self.fs.shutdown()
|
||||
|
||||
if self.client is not None:
|
||||
self.client.close()
|
||||
self.client.useCount -= 1
|
||||
if self.client.useCount == 0:
|
||||
self.client.close()
|
||||
self.client = None # Clean up
|
||||
except Exception:
|
||||
logger.exception('Exception stopping')
|
||||
pass
|
@@ -33,13 +33,20 @@ 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=os.path.join(tempfile.gettempdir(), b'udsclient.log'),
|
||||
filename=logFile,
|
||||
filemode='a',
|
||||
format='%(levelname)s %(asctime)s %(message)s',
|
||||
level=logging.INFO
|
||||
level=logging.DEBUG
|
||||
)
|
||||
|
||||
logger = logging.getLogger('udsclient')
|
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2015 Virtual Cable S.L.
|
||||
# Copyright (c) 2017 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2015 Virtual Cable S.L.
|
||||
# Copyright (c) 2017 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@@ -56,7 +56,7 @@ class RestRequest(QObject):
|
||||
# private
|
||||
self._manager = QNetworkAccessManager()
|
||||
if params is not None:
|
||||
url += '?' + '&'.join('{}={}'.format(k, urllib.quote(six.text_type(v))) for k, v in params.iteritems())
|
||||
url += '?' + '&'.join('{}={}'.format(k, urllib.quote(six.text_type(v).encode('utf8'))) for k, v in params.iteritems())
|
||||
|
||||
self.url = QUrl(RestRequest.restApiUrl + url)
|
||||
|
||||
@@ -91,6 +91,7 @@ class RestRequest(QObject):
|
||||
@pyqtSlot(QNetworkReply, list)
|
||||
def _sslError(self, reply, errors):
|
||||
settings = QSettings()
|
||||
settings.beginGroup('ssl')
|
||||
cert = errors[0].certificate()
|
||||
digest = six.text_type(cert.digest().toHex())
|
||||
|
||||
@@ -107,6 +108,8 @@ class RestRequest(QObject):
|
||||
settings.setValue(digest, True)
|
||||
reply.ignoreSslErrors()
|
||||
|
||||
settings.endGroup()
|
||||
|
||||
def get(self):
|
||||
request = QNetworkRequest(self.url)
|
||||
request.setRawHeader('User-Agent', osDetector.getOs() + " - UDS Connector " + VERSION)
|
@@ -40,28 +40,57 @@ import socket
|
||||
import stat
|
||||
import six
|
||||
import sys
|
||||
import time
|
||||
|
||||
from log import logger
|
||||
|
||||
_unlinkFiles = []
|
||||
_tasksToWait = []
|
||||
_execBeforeExit = []
|
||||
|
||||
|
||||
sys_fs_enc = sys.getfilesystemencoding() or 'mbcs'
|
||||
|
||||
def saveTempFile(content, filename=None):
|
||||
if filename is None:
|
||||
filename = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(16))
|
||||
filename = b''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(16))
|
||||
filename = filename + '.uds'
|
||||
|
||||
if 'win32' in sys.platform:
|
||||
filename = filename.encode('utf-8')
|
||||
logger.info('Fixing for win32')
|
||||
filename = filename.encode(sys_fs_enc)
|
||||
|
||||
filename = os.path.join(tempfile.gettempdir(), filename)
|
||||
|
||||
with open(filename, 'w') as f:
|
||||
f.write(content)
|
||||
|
||||
logger.info('Returning filename')
|
||||
return filename
|
||||
|
||||
def readTempFile(filename):
|
||||
if 'win32' in sys.platform:
|
||||
filename = filename.encode('utf-8')
|
||||
|
||||
filename = os.path.join(tempfile.gettempdir(), filename)
|
||||
try:
|
||||
with open(filename, 'r') as f:
|
||||
return f.read()
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def testServer(host, port, timeOut=4):
|
||||
try:
|
||||
sock = socket.create_connection((host, int(port)), timeOut)
|
||||
sock.close()
|
||||
except Exception:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def findApp(appName, extraPath=None):
|
||||
if 'win32' in sys.platform and isinstance(appName, six.text_type):
|
||||
appName = six.binary_type(appName)
|
||||
appName = appName.encode(sys_fs_enc)
|
||||
searchPath = os.environ['PATH'].split(os.pathsep)
|
||||
if extraPath is not None:
|
||||
searchPath += list(extraPath)
|
||||
@@ -78,7 +107,13 @@ def getHostName():
|
||||
Returns current host name
|
||||
In fact, it's a wrapper for socket.gethostname()
|
||||
'''
|
||||
return six.text_type(socket.gethostname())
|
||||
hostname = socket.gethostname()
|
||||
if 'win32' in sys.platform:
|
||||
hostname = hostname.decode(sys_fs_enc)
|
||||
|
||||
hostname = six.text_type(hostname)
|
||||
logger.info('Hostname: {}'.format(hostname))
|
||||
return hostname
|
||||
|
||||
# Queing operations (to be executed before exit)
|
||||
|
||||
@@ -94,6 +129,8 @@ def unlinkFiles():
|
||||
'''
|
||||
Removes all wait-and-unlink files
|
||||
'''
|
||||
if len(_unlinkFiles) > 0:
|
||||
time.sleep(5) # Wait 5 seconds before deleting anything
|
||||
for f in _unlinkFiles:
|
||||
try:
|
||||
os.unlink(f)
|
||||
@@ -107,7 +144,13 @@ def addTaskToWait(taks):
|
||||
|
||||
def waitForTasks():
|
||||
for t in _tasksToWait:
|
||||
t.wait()
|
||||
try:
|
||||
if hasattr(t, 'join'):
|
||||
t.join()
|
||||
elif hasattr(t, 'wait'):
|
||||
t.wait()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def addExecBeforeExit(fnc):
|
@@ -1 +0,0 @@
|
||||
udsclient_1.9.1_all.deb admin optional
|
17
client/thin/.project
Normal file
@@ -0,0 +1,17 @@
|
||||
<?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>
|
8
client/thin/.pydevproject
Normal file
@@ -0,0 +1,8 @@
|
||||
<?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>
|
158
client/thin/src/UDSClient.py
Normal file
@@ -0,0 +1,158 @@
|
||||
# -*- 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
|
1
client/thin/src/uds/__init__.py
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../full/src/uds/__init__.py
|
1
client/thin/src/uds/forward.py
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../full/src/uds/forward.py
|
1
client/thin/src/uds/log.py
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../full/src/uds/log.py
|
1
client/thin/src/uds/osDetector.py
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../full/src/uds/osDetector.py
|
86
client/thin/src/uds/rest.py
Normal file
@@ -0,0 +1,86 @@
|
||||
# -*- 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
|
1
client/thin/src/uds/tools.py
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../full/src/uds/tools.py
|
44
client/thin/src/uds/ui/__init__.py
Normal file
@@ -0,0 +1,44 @@
|
||||
# -*- 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)
|
||||
|
58
client/thin/src/uds/ui/browser.py
Normal file
@@ -0,0 +1,58 @@
|
||||
# -*- 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)
|
49
client/thin/src/uds/ui/consoleui.py
Normal file
@@ -0,0 +1,49 @@
|
||||
# -*- 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
|
143
client/thin/src/uds/ui/gtkui.py
Normal file
@@ -0,0 +1,143 @@
|
||||
# -*- 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
|
4
client/thin/src/udsclient
Normal file
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
cd /lib/UDSClient
|
||||
exec python UDSClient.pyc $@
|
11
client/thin/thinstation/README.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
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!!!
|
2
client/thin/thinstation/udsclient/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
lib
|
||||
src
|
11
client/thin/thinstation/udsclient/UDSClient.desktop
Normal file
@@ -0,0 +1,11 @@
|
||||
[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;
|
4
client/thin/thinstation/udsclient/bin/udsclient
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
cd /lib/UDSClient
|
||||
exec python UDSClient.pyc $@
|
13
client/thin/thinstation/udsclient/build.sh
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/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
|
5
client/thin/thinstation/udsclient/dependencies
Normal file
@@ -0,0 +1,5 @@
|
||||
base
|
||||
#add your own dependancies to this file, base should always be included.
|
||||
python
|
||||
pygtk
|
||||
freerdp
|
15
client/thin/thinstation/udsclient/etc/cmd/README
Normal file
@@ -0,0 +1,15 @@
|
||||
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
|
1
client/thin/thinstation/udsclient/etc/cmd/example.fullscreen
Executable file
@@ -0,0 +1 @@
|
||||
CMD_FULLSCREEN="example -FULLSCREEN"
|
1
client/thin/thinstation/udsclient/etc/cmd/example.global
Executable file
@@ -0,0 +1 @@
|
||||
CMD_GLOBAL="example -startapp"
|
1
client/thin/thinstation/udsclient/etc/console/README
Normal file
@@ -0,0 +1 @@
|
||||
Place a 0 length file in here as the same name as the package if your application is a Console App
|
34
client/thin/thinstation/udsclient/etc/init.d/your_start_up_script
Executable file
@@ -0,0 +1,34 @@
|
||||
#! /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
|