mirror of
https://github.com/dkmstr/openuds.git
synced 2025-10-07 15:33:51 +03:00
Compare commits
2290 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
cd60b398a9 | ||
|
979f992b6d | ||
|
83689dddaa | ||
|
037b4abad1 | ||
|
839e4c6b1d | ||
|
2fd157e463 | ||
|
7e51c1fd93 | ||
|
b6af59cc44 | ||
|
50072e948e | ||
|
c5e0d0721f | ||
|
afbd4c5355 | ||
|
e4377b83e4 | ||
|
bf97c6f2dc | ||
|
6763de2bab | ||
|
b4ca743d7c | ||
|
20f7ae7fcd | ||
|
8aac4f9aa5 | ||
|
f494c706fc | ||
|
76b488dc1d | ||
|
826cc7aed8 | ||
|
4da15d66fe | ||
|
79495fc3b1 | ||
|
f438a9241e | ||
|
e37b345aff | ||
|
ce1330066f | ||
|
20e86cd8c7 | ||
|
dc52e37abc | ||
|
69fae6a1a6 | ||
|
7c14923afe | ||
|
9e66583b4e | ||
|
34676c817f | ||
|
d17224c9cb | ||
|
b57b00f3fc | ||
|
f82041da1e | ||
|
03a837f865 | ||
|
473dc2577f | ||
|
49dfaf3709 | ||
|
f5afb79a2b | ||
|
bd26fb38d9 | ||
|
95f0b0ab26 | ||
|
28433fc33e | ||
|
fc4e7414df | ||
|
e61cb1f855 | ||
|
689214cf84 | ||
|
d268478767 | ||
|
4a5ad5dc09 | ||
|
7365ee8cc6 | ||
|
5a93aa15e8 | ||
|
1fddc17b75 | ||
|
ca540d7725 | ||
|
fe11b485ed | ||
|
12394c873c | ||
|
5904a9c9c5 | ||
|
6ecefbabe8 | ||
|
74908f186c | ||
|
102b36d090 | ||
|
52f8a91f75 | ||
|
ad9a0c3f11 | ||
|
c5299378cf | ||
|
6c315ef8a8 | ||
|
6f192a8f7c | ||
|
890f00b111 | ||
|
3e67e3ba30 | ||
|
e5ea6d6a33 | ||
|
3009ae601a | ||
|
ed2e3c557d | ||
|
7ca96e4262 | ||
|
46546a837e | ||
|
f1ffe0cb9c | ||
|
7ce0fb0f22 | ||
|
a90d2719a3 | ||
|
e8d7da650c | ||
|
6f4d14bdf2 | ||
|
9f303b8cfa | ||
|
8db36ac964 | ||
|
921abfe0bb | ||
|
15b13b00ae | ||
|
763ca4bd07 | ||
|
faf852cdaa | ||
|
35e4422e0e | ||
|
288757c984 | ||
|
9b0ea5f893 | ||
|
af2178d338 | ||
|
3ec748eba6 | ||
|
429700d4c1 | ||
|
91184a24b0 | ||
|
8ae073bc27 | ||
|
d4b90c387b | ||
|
bf1b4a3d31 | ||
|
ed99412e71 | ||
|
d387366d15 | ||
|
f781a8001c | ||
|
1be33751d9 | ||
|
b2baf61d0e | ||
|
6d1e999b1f | ||
|
076a300751 | ||
|
43406e10f6 | ||
|
87974bf745 | ||
|
95e02593c5 | ||
|
0f7f08fb4f | ||
|
2be6f93964 | ||
|
28cbf6196a | ||
|
4ca4981d97 | ||
|
a06d9c0d5c | ||
|
889c10b451 | ||
|
dcbc252df6 | ||
|
2e29790d57 | ||
|
85b940375d | ||
|
74abc6c3e7 | ||
|
de86a999b1 | ||
|
583e6220bd | ||
|
11d7a1cad1 | ||
|
cd82071bf0 | ||
|
d572d0bff7 | ||
|
28d5895edd | ||
|
22a5d3dee5 | ||
|
ca63c3decf | ||
|
3072eabc74 | ||
|
bf93c2cbe4 | ||
|
ae1009d1a0 | ||
|
7347c6fb49 | ||
|
46c03a84f5 | ||
|
ed739c62ac | ||
|
52d947cb7d | ||
|
2f23b12f4e | ||
|
f73a727107 | ||
|
f6e7143415 | ||
|
c6ef43826a | ||
|
439a07297f | ||
|
bef5896c32 | ||
|
086366a2a1 | ||
|
10b6850a64 | ||
|
1bf80722a0 | ||
|
177536e77a | ||
|
166c407db9 | ||
|
2d45e43cd1 | ||
|
b5b1cc276a | ||
|
decb02ed64 | ||
|
85e948a447 | ||
|
e2f8d47b7f | ||
|
f592647f70 | ||
|
b5a91d5ea8 | ||
|
938dfeaa15 | ||
|
cffc8ff368 | ||
|
78f5e59a40 | ||
|
47e329fdf4 | ||
|
ff21166e87 | ||
|
eec65716d9 | ||
|
eba9fb02d6 | ||
|
47f835186c | ||
|
99855aa982 | ||
|
26ee2dbc9c | ||
|
08e11b0287 | ||
|
82fda13468 | ||
|
eabcda81af | ||
|
15d975d3f8 | ||
|
7c75cbd211 | ||
|
a7d948de85 | ||
|
668a3f5353 | ||
|
a760c18072 | ||
|
1098152054 | ||
|
aefa4e830e | ||
|
8699bfb64c | ||
|
279b2a74f7 | ||
|
d34e7e32e8 | ||
|
255d0c9abd | ||
|
af9e8b9faa | ||
|
aaed98d3b8 | ||
|
d94e15a340 | ||
|
a93e31cb93 | ||
|
813e85a4f6 | ||
|
d96ed763ec | ||
|
fcc141138c | ||
|
0b80ce9843 | ||
|
0085dfc41d | ||
|
3f7e194713 | ||
|
1be79a8246 | ||
|
393e2f41c9 | ||
|
14fe9e19cf | ||
|
cc2fb5ddaa | ||
|
9c45afaa23 | ||
|
208c96bad9 | ||
|
7791a67ed5 | ||
|
bf62e390df | ||
|
3efeeb4df5 | ||
|
2c2c6d5fa5 | ||
|
0d614aaef5 | ||
|
6f692e439f | ||
|
01a46600dc | ||
|
cddb096048 | ||
|
5d8f9efaa5 | ||
|
91b4eae2d5 | ||
|
0c31080765 | ||
|
e3be9aeefc | ||
|
3c1266cc11 | ||
|
f3a8335998 | ||
|
a10f05766d | ||
|
419c828cc0 | ||
|
1c81c4e76c | ||
|
67b58f62ee | ||
|
a25b510f0e | ||
|
3edabec51e | ||
|
92eee92b29 | ||
|
0a45c06284 | ||
|
b9594013a0 | ||
|
354040c051 | ||
|
45d1ad17e9 | ||
|
0964a38aef | ||
|
77f582a8cf | ||
|
0f218cbeba | ||
|
2d83f3a32b | ||
|
0fefd8864a | ||
|
f5dedb1cde | ||
|
70c865c862 | ||
|
defd2ddd7c | ||
|
79a1fe5e80 | ||
|
793f6fda18 | ||
|
4224f9be66 | ||
|
5d2ae3285c | ||
|
735ccd22c0 | ||
|
a5e94bfa9a | ||
|
a449c93464 | ||
|
59af07590b | ||
|
08b7fd549a | ||
|
4d2ddd3909 | ||
|
99305c17e7 | ||
|
9b3caed641 | ||
|
a881509502 | ||
|
a4cc9fc750 | ||
|
51fbb7b613 | ||
|
3863236afd | ||
|
ad33b1f57a | ||
|
f97c776399 | ||
|
1962f1a55f | ||
|
5a788e0ed5 | ||
|
571c56e64b | ||
|
2e7032b980 | ||
|
fdbf91cbc1 | ||
|
f868607740 | ||
|
cef086a3c3 | ||
|
176cf31e48 | ||
|
0d604d04b6 | ||
|
d19991523c | ||
|
6bf7055ac2 | ||
|
3b34990843 | ||
|
70d3e386af | ||
|
f6f7f7d7b5 | ||
|
2f5e8c5f53 | ||
|
a1daeb78b4 | ||
|
1485d7dd9e | ||
|
d0e13b5643 | ||
|
a7b64504fe | ||
|
90ba463fb9 | ||
|
553bff614d | ||
|
78aefbcfec | ||
|
a8f77a05de | ||
|
beea237758 | ||
|
4a32800545 | ||
|
c7014c95ff | ||
|
5a5c68fef7 | ||
|
6ba08a361a | ||
|
14278daa14 | ||
|
75852519aa | ||
|
d697ac5ee4 | ||
|
8680146fba | ||
|
a1b63920ab | ||
|
7b1e55cf8f | ||
|
6c8d7b1bd9 | ||
|
8eedbe7b09 | ||
|
c06bf292ce | ||
|
b8825c19af | ||
|
93db2388bb | ||
|
7311044165 | ||
|
5a9f6e46d9 | ||
|
f4ea7e88cc | ||
|
d2a4db2c39 | ||
|
885db91705 | ||
|
d03af24e23 | ||
|
0ccd8adcde | ||
|
9ee2bd94ea | ||
|
de54abcbdd | ||
|
b3d11dee7b | ||
|
4861a10134 | ||
|
e1747bee13 | ||
|
530b3acfb1 | ||
|
6f1bf4a55d | ||
|
4eb4c2c7d7 | ||
|
053601a4e1 | ||
|
37956262ef | ||
|
3fbdea1e23 | ||
|
5ac2e2a73d | ||
|
e8db21b7ed | ||
|
2cb99669ed | ||
|
354ff27e2a | ||
|
c3d9a34c4a | ||
|
3d57cb9bec | ||
|
8c2ba3403d | ||
|
e1ac39e67f | ||
|
cfd2ad6b57 | ||
|
1e893240cd | ||
|
051b23eb92 | ||
|
4388ed0f1b | ||
|
f7d10a4f4b | ||
|
9c70a84977 | ||
|
a165122422 | ||
|
1bf44bd5a2 | ||
|
14a58dc423 | ||
|
a318e8ca2b | ||
|
418e1f7c0b | ||
|
e1c68f3895 | ||
|
9641776ce1 | ||
|
83521cfd7a | ||
|
9357d61e02 | ||
|
ec4d8cc8ad | ||
|
b2e3471512 | ||
|
c976716871 | ||
|
58fb367a60 | ||
|
2f69c18ad9 | ||
|
aa7ce6aad2 | ||
|
ac450880c9 | ||
|
bd53926c81 | ||
|
25aa09309b | ||
|
203a46a804 | ||
|
c041284588 | ||
|
dd71811045 | ||
|
c42b5f270b | ||
|
cd136fa750 | ||
|
5b29db9fe8 | ||
|
3f8554fd87 | ||
|
a39da82971 | ||
|
3b5128c5fd | ||
|
5edc920e01 | ||
|
0e3120f3fe | ||
|
ed9499211d | ||
|
1fd286c73a | ||
|
64d91a7d70 | ||
|
b38c1c591a | ||
|
a263bdc100 | ||
|
83a61b57cc | ||
|
a8f8568c34 | ||
|
66dcad2d8d | ||
|
bfa474d3f1 | ||
|
a8b1eeb291 | ||
|
a12998bef8 | ||
|
7d76704e7b | ||
|
cce564371c | ||
|
c21652fe83 | ||
|
9e0266d26b | ||
|
fa24a5e4f4 | ||
|
1a7a4865af | ||
|
b473160400 | ||
|
1c388f62c1 | ||
|
4a68ebfb6d | ||
|
752ae78ce7 | ||
|
1c80fb6ac9 | ||
|
e05678dbcd | ||
|
ccefa7867c | ||
|
e2ec608ead | ||
|
33d3b5d14a | ||
|
ad2b746f03 | ||
|
81c5429f40 | ||
|
093c7b3094 | ||
|
0306ed567f | ||
|
d4696b1c12 | ||
|
3461bd4963 | ||
|
9b5655fa41 | ||
|
f7df9b2ae8 | ||
|
f53c9bb793 | ||
|
69087f46fe | ||
|
c60332a4aa | ||
|
602374dad6 | ||
|
4af39858bd | ||
|
fa31a30ba2 | ||
|
dca747969e | ||
|
386a0650b9 | ||
|
e4a9b89d32 | ||
|
6b002a540e | ||
|
f8ab0b09d3 | ||
|
fce9e81509 | ||
|
c04a2b7396 | ||
|
d973e89842 | ||
|
821ee3ecc1 | ||
|
af3a03d7f7 | ||
|
58b845efd2 | ||
|
27d8432d6d | ||
|
55f3a697ca | ||
|
6ce5cbe10e | ||
|
fc671f2a7a | ||
|
ec52e0a16f | ||
|
1d5cd65858 | ||
|
95b79f6af1 | ||
|
50449c79f2 | ||
|
3fbd033964 | ||
|
4d9ce8b11d | ||
|
8e51026336 | ||
|
fcac2d4d23 | ||
|
c6d9dac602 | ||
|
ac72b88554 | ||
|
e4a49ed763 | ||
|
2cb262a6db | ||
|
72e2204c3e | ||
|
2e541c6cbf | ||
|
aaeed2d24c | ||
|
7365364b36 | ||
|
e9c6b117b8 | ||
|
b6a8892a63 | ||
|
27f950e2b6 | ||
|
735a447334 | ||
|
c7666bb4f2 | ||
|
c67eee8b73 | ||
|
33508e0ba0 | ||
|
3bbf9ec99a | ||
|
a170596f1d | ||
|
ba78eb7f33 | ||
|
0524a1bc78 | ||
|
efc4244367 | ||
|
a61514cd3d | ||
|
e0fdd8878a | ||
|
ab1e807f36 | ||
|
4c755f548e | ||
|
b0ffdb5492 | ||
|
0c6afef707 | ||
|
a3868190a1 | ||
|
6544f194db | ||
|
b01cd25648 | ||
|
320e97b85d | ||
|
6a0244e83d | ||
|
3f8797f4d0 | ||
|
e42e3e4fbd | ||
|
1e028d5fdc | ||
|
6248ac69af | ||
|
c794b2befa | ||
|
01c41704a9 | ||
|
e90099bcac | ||
|
8e5418d792 | ||
|
20496327bb | ||
|
ba0dd1f46a | ||
|
a47e5b2d93 | ||
|
722cfae7ae | ||
|
f836bbf73a | ||
|
aab94bffbc | ||
|
207a784b8f | ||
|
7c2a8168fa | ||
|
0a39939659 | ||
|
629d4622a7 | ||
|
457c1e25e8 | ||
|
f4ee6e50c0 | ||
|
ef4b7e5bac | ||
|
3f60e7509e | ||
|
eb62997bde | ||
|
e19805f081 | ||
|
bf6e1674d2 | ||
|
c69d880c40 | ||
|
206947cbe3 | ||
|
7526132f96 | ||
|
1fd5c5b49a | ||
|
a4a07be77f | ||
|
6d6b8e07d9 | ||
|
bb9073891c | ||
|
708a3e22af | ||
|
022b620edc | ||
|
fb83d801b4 | ||
|
40f24fd316 | ||
|
155a74178a | ||
|
0001fca79e | ||
|
bb448d2876 | ||
|
a568ff9a28 | ||
|
ccc1aa66ce | ||
|
aba2ea1245 | ||
|
338a4d1ecc | ||
|
8aaed4d41e | ||
|
6df00254af | ||
|
2cd6f21c07 | ||
|
0d79b90b84 | ||
|
94a3a791cd | ||
|
afa8757264 | ||
|
fd66a226da | ||
|
4bc3947dc1 | ||
|
30fb0d0183 | ||
|
5ba08e2896 | ||
|
6f8eb00ad0 | ||
|
23f52b363c | ||
|
8234becde2 | ||
|
01353528dd | ||
|
57f70b9d4f | ||
|
b402e2e117 | ||
|
2ec731896f | ||
|
9cc2afdb1a | ||
|
4780c58e6a | ||
|
b41a0a9331 | ||
|
bd467a0bfb | ||
|
c04a40a468 | ||
|
cdb0f110cf | ||
|
25c5c3a4d1 | ||
|
33554dc724 | ||
|
0e097e3a12 | ||
|
abedf042da | ||
|
cfdf622447 | ||
|
4fea6e6a3e | ||
|
43d0f3b4d4 | ||
|
7566071270 | ||
|
9d51963903 | ||
|
b303ffc858 | ||
|
286b7cb09f | ||
|
2a4ccac195 | ||
|
773c3b5b50 | ||
|
c1c922b87b | ||
|
f6843fe11a | ||
|
31c015cb41 | ||
|
78352cfe8b | ||
|
e880bdb228 | ||
|
c61e7e5444 | ||
|
72407449be | ||
|
d974d5904c | ||
|
8c5e2d6552 | ||
|
b0cf8c5ddf | ||
|
e4d5bef48a | ||
|
3016b0845c | ||
|
930c22d8fa | ||
|
8778fa3314 | ||
|
aa25ba9575 | ||
|
1e64b957a1 | ||
|
a25184af52 | ||
|
1a263bf668 | ||
|
4f6f93bba3 | ||
|
b1ee7656c8 | ||
|
ad9d3532ee | ||
|
6a4c9a7454 | ||
|
84e24fd3ae | ||
|
bf0d094c67 | ||
|
525e9b9103 | ||
|
1cb96fd727 | ||
|
dd9a02faa9 | ||
|
adf724101b | ||
|
856563a65c | ||
|
3c7ec1067f | ||
|
ea5730c857 | ||
|
a1499b0895 | ||
|
28ed59e185 | ||
|
2c8ef0cbe9 | ||
|
0b7adc8b19 | ||
|
29179a31df | ||
|
6e59402747 | ||
|
1873ed46fe | ||
|
e5035efafb | ||
|
4163b5aae2 | ||
|
43389248c8 | ||
|
d60f47aa7a | ||
|
e62e9875da | ||
|
65d7e81263 | ||
|
06bc4cae4a | ||
|
3f7cb8e3db | ||
|
1218904895 | ||
|
17a184752c | ||
|
fc1ca9c3df | ||
|
f23d8dd66b | ||
|
b5fd7c39de | ||
|
96e853b5a9 | ||
|
3ce03fa238 | ||
|
e4be7859e0 | ||
|
d279a44c24 | ||
|
17027e50f4 | ||
|
e90193ce2a | ||
|
97cfd75066 | ||
|
4ed71eda7e | ||
|
2ee436e75c | ||
|
14694cdaf8 | ||
|
dec744a0e2 | ||
|
a538827155 | ||
|
a2e211715a | ||
|
263e9f4b5d | ||
|
711869c657 | ||
|
4562a38232 | ||
|
c0116199ed | ||
|
e96e1b6d6c | ||
|
9ad9ed0680 | ||
|
8108946342 | ||
|
261183668f | ||
|
9ee7e7fa8e | ||
|
cd0827af07 | ||
|
8bad7d576a | ||
|
8c702df0c6 | ||
|
7cb494c4d5 | ||
|
2d2c20909d | ||
|
57284fbf50 | ||
|
d5c3519d18 | ||
|
570c15be1b | ||
|
1a33f3d6d5 | ||
|
50f02d1169 | ||
|
98ff066fe7 | ||
|
fe91ecbad6 | ||
|
ae164f7236 | ||
|
e5eaaf74a0 | ||
|
57e5412553 | ||
|
7db960ca92 | ||
|
61689ad638 | ||
|
e3c839d70f | ||
|
ae240a36ed | ||
|
5de5ba1585 | ||
|
d4ca92fcd5 | ||
|
26f48d22f8 | ||
|
7b7e9193c0 | ||
|
c133a74145 | ||
|
a6deda2004 | ||
|
3fd1fc8b79 | ||
|
4d98cc3f34 | ||
|
9d0a6c5b6f | ||
|
7851470059 | ||
|
94186794f6 | ||
|
f2c7afbd44 | ||
|
7281fa3494 | ||
|
211cf4d5e6 | ||
|
4097c66c0a | ||
|
850b5e35e1 | ||
|
6d4d79f0b7 | ||
|
6b8beb27cb | ||
|
2a93c14398 | ||
|
67bd9b8bd3 | ||
|
40f5941db1 | ||
|
83abebd574 | ||
|
77e45099d1 | ||
|
56cdee2b0d | ||
|
6de7c65a0f | ||
|
7737bbf758 | ||
|
579e1c2a36 | ||
|
397523c9a3 | ||
|
029224c749 | ||
|
7b5e87dd15 | ||
|
0570a1ae27 | ||
|
04f144cd2d | ||
|
6adcc3e1f4 | ||
|
7316953e75 | ||
|
eb86784c62 | ||
|
628f43a2e7 | ||
|
53e0cefc21 | ||
|
6694b9d5bc | ||
|
30bbf1ef0f | ||
|
c39c8c9583 | ||
|
3f70576002 | ||
|
156608b6ae | ||
|
1dcc1f71df | ||
|
b2679104e2 | ||
|
d93ed52884 | ||
|
d84e5ac38c | ||
|
2822013b36 | ||
|
60c5f37591 | ||
|
229962cbd2 | ||
|
a5bfec3aa9 | ||
|
c02c9f45a4 | ||
|
fb3b02ac2d | ||
|
8f17f2e545 | ||
|
d62afebef8 | ||
|
871538f341 | ||
|
839d798e56 | ||
|
cf8498ad37 | ||
|
e6778eda38 | ||
|
7c16960d3a | ||
|
e5e72de4b4 | ||
|
c95d54eea8 | ||
|
339eeb7967 | ||
|
fca36ead57 | ||
|
3dedd9b7e0 | ||
|
bab8f3965a | ||
|
4b5913183c | ||
|
5718c0d8c0 | ||
|
0554089ecc | ||
|
05c434eb8a | ||
|
8b3c8fe81e | ||
|
fb3945975c | ||
|
1df9ab40a6 | ||
|
02fb954e29 | ||
|
d264cffca2 | ||
|
750715f8a2 | ||
|
e0acbabcd2 | ||
|
a13b4431d0 | ||
|
2290b4f235 | ||
|
fc230d7205 | ||
|
d598dd5d0d | ||
|
79ddb6016f | ||
|
37b0864c12 | ||
|
4457c4e84d | ||
|
c8f402a419 | ||
|
1780e6c2d9 | ||
|
83fc9f2e7f | ||
|
93ebf82c79 | ||
|
0322df85d5 | ||
|
fedd6507ae | ||
|
4a05ebe074 | ||
|
8ec010a9bb | ||
|
885fe8f31f | ||
|
262f63d926 | ||
|
9320eb3adf | ||
|
8f98314c97 | ||
|
e70dcd4f23 | ||
|
15d84816fa | ||
|
967a6fe925 | ||
|
dfb15e8920 | ||
|
ccce6650ba | ||
|
165d3bde21 | ||
|
83f4359f90 | ||
|
08618f5dbb | ||
|
5c9bf779e3 | ||
|
4fb1da1554 | ||
|
42556d9e33 | ||
|
4835035156 | ||
|
70738e7801 | ||
|
6e2980cffc | ||
|
188b27eb90 | ||
|
f497c388ba | ||
|
3e8405482e | ||
|
a0cb8d4778 | ||
|
ce1d1eba19 | ||
|
cbacc20909 | ||
|
760bb169bf | ||
|
e70f2cd07b | ||
|
9f4c1ea28d | ||
|
5e96965231 | ||
|
1a0b83327c | ||
|
bc12b4ef3a | ||
|
8512435a22 | ||
|
4ee8697827 | ||
|
cf8660aa78 | ||
|
df5d18710a | ||
|
24c2da1b4b | ||
|
123d3ca405 | ||
|
e1810a3695 | ||
|
cbd67c5c71 | ||
|
189e530c3a | ||
|
7ecadee56f | ||
|
7be200f173 | ||
|
c64a788523 | ||
|
12548b44cc | ||
|
d542e65a9c | ||
|
f760b55561 | ||
|
a3f50e739a | ||
|
6ab0307bdd | ||
|
934c43636e | ||
|
db77332c17 | ||
|
947b35bcf5 | ||
|
9d00766200 | ||
|
62742e9438 | ||
|
466bda440e | ||
|
ac116697f4 | ||
|
14745e7f1b | ||
|
063385a8f1 | ||
|
ef70b5ce76 | ||
|
2d40b5ea5b | ||
|
16e99aad1d | ||
|
ef73487473 | ||
|
f67b34e62b | ||
|
38340ef709 | ||
|
9d97206806 | ||
|
071f92ab49 | ||
|
beb3c905d8 | ||
|
20711c5c4d | ||
|
5ba9fdc2a8 | ||
|
cc8962c9cb | ||
|
3cc36891c3 | ||
|
3f7d5f4b2e | ||
|
7250a1d499 | ||
|
160dda7236 | ||
|
ecdd311837 | ||
|
6e7700c7e2 | ||
|
a2fc3130cd | ||
|
05d26c732e | ||
|
09858a165c | ||
|
255a7c50c3 | ||
|
95a9807d6a | ||
|
c8d6fbcbd1 | ||
|
69ffbe384c | ||
|
7ef34f3897 | ||
|
86fe499d3a | ||
|
5d21a9086a | ||
|
552bdfc0cf | ||
|
5c7d13e890 | ||
|
f9e2d88529 | ||
|
3bf7af2e3d | ||
|
fc35c18fea | ||
|
0aec89b76b | ||
|
4ad32931ee | ||
|
975dd80c5d | ||
|
6f85a42673 | ||
|
bedadf24ac | ||
|
02f367efd8 | ||
|
dc8df5bb79 | ||
|
73042dd76f | ||
|
7e33f381d4 | ||
|
beae9271cd | ||
|
1633d0d1b1 | ||
|
05df32a739 | ||
|
75b27af0fe | ||
|
57a8d26adc | ||
|
8f902b36fa | ||
|
6fca6c2d0a | ||
|
4b85424f56 | ||
|
05038e2e6f | ||
|
3a9e48d4b6 | ||
|
bf8b40be6f | ||
|
202444f64e | ||
|
500cb2ed96 | ||
|
a3fa9f604a | ||
|
b58d3e210c | ||
|
57be9c80b6 | ||
|
9614323a1b | ||
|
009d10ad56 | ||
|
96db6ded98 | ||
|
8d26fff400 | ||
|
042a27737f | ||
|
e6c7a80ac5 | ||
|
f279a503aa | ||
|
511833d2fd | ||
|
77e7df76bf | ||
|
9defc46083 | ||
|
97fc8f844e | ||
|
9800218df9 | ||
|
3d02a99b42 | ||
|
34005c2af3 | ||
|
e921c76530 | ||
|
1d569d009a | ||
|
c4a5beafd4 | ||
|
434a20254e | ||
|
70ccbc3591 | ||
|
d4ae8f589f | ||
|
8ec94e0cdf | ||
|
06f3487d2c | ||
|
4a92cdb1e5 | ||
|
9522989679 | ||
|
33cdf27375 | ||
|
b42f93839f | ||
|
dfedbd6bf3 | ||
|
af441c13e7 | ||
|
266b0c2d1e | ||
|
bd8562a58e | ||
|
cf34718cf4 | ||
|
db082109bd | ||
|
0c3a1d746e | ||
|
ea91006b77 | ||
|
7c79a716a3 | ||
|
45a409f72b | ||
|
7cfa9ca9f4 | ||
|
e88c3f2fff | ||
|
01e765a1db | ||
|
f2c5ca2e92 | ||
|
63d1693fea | ||
|
2ce03b9438 | ||
|
72b02d2560 | ||
|
d003e48a42 | ||
|
485520f402 | ||
|
42e042d4d4 | ||
|
e115554b32 | ||
|
34c19799da | ||
|
f034f5eb7f | ||
|
d2c466c542 | ||
|
7cdc11219c | ||
|
817100cf5b | ||
|
92042dd55a | ||
|
8e23f7c818 | ||
|
81abe1d99f | ||
|
3afeb4869c | ||
|
5cf75a13f1 | ||
|
9a6ac745a0 | ||
|
f4b38af989 | ||
|
ad8f5e01c1 | ||
|
59691d923d | ||
|
b60af290a0 | ||
|
cae6a1e8d2 | ||
|
4fe67ae697 | ||
|
de3985c841 | ||
|
c2024c23b8 | ||
|
641497826a | ||
|
2beb9cfdb7 | ||
|
98d0306846 | ||
|
cc89e7f5db | ||
|
0d3504e11f | ||
|
63a3469c91 | ||
|
009df1f4cb | ||
|
0495c4af1b | ||
|
1e211997b8 | ||
|
3c6044c0ed | ||
|
9dbb47a4ad | ||
|
d21f66408f | ||
|
65dc8acdec | ||
|
88ee934376 | ||
|
894fecb861 | ||
|
8f7dca32b0 | ||
|
a7ee5ecb88 | ||
|
3f434f1347 | ||
|
f2377ebfec | ||
|
4f2719aa29 | ||
|
ce888a6013 | ||
|
cd737c4b5f | ||
|
698d9cbcc7 | ||
|
f1c1cbae06 | ||
|
e4d2d2a843 | ||
|
27d91154b6 | ||
|
1d4896c089 | ||
|
84f1fef5d7 | ||
|
622acdb1a5 | ||
|
ad1b8cb51d | ||
|
0b66300805 | ||
|
bf3d36c901 | ||
|
1715e1a7a1 | ||
|
f6c6578b00 | ||
|
4f2b331bb8 | ||
|
6ae18f4a58 | ||
|
5c6ed274c8 | ||
|
abe9228551 | ||
|
b9b3889f35 | ||
|
cf4a7e0ef9 | ||
|
bc61814ec4 | ||
|
4cf261678f | ||
|
927e2ee5a9 | ||
|
7db6cba256 | ||
|
b7e828b57c | ||
|
b8d2c0277e | ||
|
5bba681b9d | ||
|
f63e6093eb | ||
|
bac161b46c | ||
|
a7dc47dd6e | ||
|
722aee2543 | ||
|
bb301d510c | ||
|
2bd4d4408c | ||
|
494e24691b | ||
|
233d66921e | ||
|
4a1a1c3e43 | ||
|
914d541748 | ||
|
164403ed49 | ||
|
6814796ba5 | ||
|
92f83ebe52 | ||
|
bfdae32c15 | ||
|
150d8c4197 | ||
|
499c5f8ec4 | ||
|
48be614e20 | ||
|
500d708653 | ||
|
dc5cc99afd | ||
|
974f652df0 | ||
|
c5171c014b | ||
|
fcf6e31486 | ||
|
0b4656e21c | ||
|
03af400635 | ||
|
0093cd8056 | ||
|
df5496d0e4 | ||
|
cad3d08c83 | ||
|
3f9f641f6a | ||
|
2f272da073 | ||
|
cf245e47be | ||
|
a6e65b62b0 | ||
|
695a91eb38 | ||
|
74ff4c17cd | ||
|
193c3fb544 | ||
|
a48f5e6241 | ||
|
3e0099ed16 | ||
|
58a50c6983 | ||
|
029114a95d | ||
|
73b14c349a | ||
|
d1f6a2473e | ||
|
4feec0a167 | ||
|
197fdcd6e2 | ||
|
42c2fd0640 | ||
|
d492d87118 | ||
|
740f20dc0e | ||
|
c43b196961 | ||
|
31ffcfaf4b | ||
|
efd78ddffb | ||
|
5fab570b92 | ||
|
4b2d3b3dfa | ||
|
59ae21a654 | ||
|
e3518c56e6 | ||
|
51891d2f31 | ||
|
11a1f626db | ||
|
89bf7c29ee | ||
|
a18a0de01c | ||
|
68433cd1b8 | ||
|
1aa48cd335 | ||
|
3873fe0515 | ||
|
308fb7cbe7 | ||
|
fb68b15702 | ||
|
6cbe329979 | ||
|
8def04ccd7 | ||
|
f3ddb68381 | ||
|
cc3e5acd52 | ||
|
5649b5d0f1 | ||
|
98757b2339 | ||
|
79e8a78351 | ||
|
a23ca0eb73 | ||
|
3451719675 | ||
|
8997d2fac0 | ||
|
1d79f89002 | ||
|
29ff0081a4 | ||
|
d4ea0ad8c6 | ||
|
85e20f1152 | ||
|
f5e5ed932a | ||
|
993ae107fd | ||
|
6f672234e5 | ||
|
e15c85150e | ||
|
af73b890cf | ||
|
844bab487a | ||
|
fd760b2af0 | ||
|
b072d1d95b | ||
|
30efde68fe | ||
|
65956d5ec5 | ||
|
df82362534 | ||
|
ad9755177a | ||
|
9419e0f69e | ||
|
d0da06c492 | ||
|
7f62bb29c0 | ||
|
45f4f7e7d4 | ||
|
499627c147 | ||
|
bb91190a4e | ||
|
85d71554f6 | ||
|
6a3af87831 | ||
|
e678f9bebb | ||
|
de33a96c45 | ||
|
d21c371bf7 | ||
|
941d07b4e8 | ||
|
d0fb2302a2 | ||
|
cd15d681fc | ||
|
02991d0df1 | ||
|
fca5fd1d1d | ||
|
73eb15fd13 | ||
|
0df92bc3cd | ||
|
23d4d572d7 | ||
|
ca3598a828 | ||
|
c7219ae34f | ||
|
d230fe7dd9 | ||
|
dcbaca1f7d | ||
|
fb36a46a06 | ||
|
0403c28541 | ||
|
219acc14e5 | ||
|
72ac4e59ca | ||
|
eff1843304 | ||
|
842626ec9c | ||
|
a648f4a210 | ||
|
9f3ca2dbf7 | ||
|
49469caa2a | ||
|
639273fd35 | ||
|
b8dd4ecc08 | ||
|
eab6703f2f | ||
|
851e58fb40 | ||
|
4d98f21286 | ||
|
7c09029c40 | ||
|
812f31c31f | ||
|
6e40e56d24 | ||
|
0eef9c2f09 | ||
|
d9b7771f21 | ||
|
e96369bd71 | ||
|
dedf271b54 | ||
|
fc09abe960 | ||
|
d5dd2993b8 | ||
|
dc901b4473 | ||
|
dda77ab2a3 | ||
|
0f41b8ac76 | ||
|
76c2207b12 | ||
|
1b2b1f81b3 | ||
|
c4850e0aed | ||
|
8f76bd511c | ||
|
d009a7abdc | ||
|
9deaef7eb5 | ||
|
0a8836410d | ||
|
a47a886179 | ||
|
54720723a4 | ||
|
ef59af60db | ||
|
33c263d6a8 | ||
|
f24b801f04 | ||
|
66090e181d | ||
|
412f1e36b4 | ||
|
8370194033 | ||
|
0f16e77b82 | ||
|
f8bad512a5 | ||
|
c14354e1c7 | ||
|
d68b815f0c | ||
|
07ab6526d7 | ||
|
4983a4a1be | ||
|
a169c4b8c4 | ||
|
f67d4d9a41 | ||
|
7956764b01 | ||
|
4a3c27ba11 | ||
|
52effddf0c | ||
|
8d77d45115 | ||
|
88a7cdd58b | ||
|
f5b248c52e | ||
|
b639be5fcd | ||
|
f47e0fb039 | ||
|
2129e2d93d | ||
|
3d4ccb1267 | ||
|
08d693e019 | ||
|
cdde576aa9 | ||
|
4e4d0253ad | ||
|
604b77077f | ||
|
3379126959 | ||
|
d823ebb253 | ||
|
a747777753 | ||
|
51a9126ba7 | ||
|
be4de2d9ce | ||
|
743a1adabe | ||
|
31c67ffddf | ||
|
94d5603d29 | ||
|
a2ee3267ae | ||
|
942404f2ed | ||
|
6512644d9d | ||
|
c4f316bc88 | ||
|
92c4168e52 | ||
|
5cf4733847 | ||
|
e9b3706efc | ||
|
b375ee3f80 | ||
|
3b7f70709d | ||
|
80e71874a8 | ||
|
bfed3e39ed | ||
|
ee03d7c3be | ||
|
9da669b50c | ||
|
9062a84bf4 | ||
|
544812e019 | ||
|
c921d667b0 | ||
|
cc85b9248b | ||
|
3cbf1eb8bb | ||
|
5a420e0967 | ||
|
9879eaf8d6 | ||
|
4b54a3795b | ||
|
11b70385f6 | ||
|
1a72b70175 | ||
|
0d6ec1c779 | ||
|
f8fb32342f | ||
|
785069575e | ||
|
51590bdd80 | ||
|
e621487b6e | ||
|
0102399e9e | ||
|
96d0757801 | ||
|
8ce84f3c10 | ||
|
a45baffa51 | ||
|
b18e9c3aa8 | ||
|
bcf01492c0 | ||
|
98d7a16813 | ||
|
de533acdcd | ||
|
11494a6cdd | ||
|
f67de32f92 | ||
|
4c21919846 | ||
|
d942b32a9e | ||
|
6d492cd0e8 | ||
|
902c004b47 | ||
|
28696756d0 | ||
|
8e5610bc50 | ||
|
28b03427f2 | ||
|
23a72367ff | ||
|
5bca545335 | ||
|
9caf43c36a | ||
|
05cc12a32c | ||
|
fd3154946d | ||
|
5c0bf00344 | ||
|
7fa8cb5183 | ||
|
94a0fb337f | ||
|
c9a8a710d0 | ||
|
c12091abbd | ||
|
17d684f1ce | ||
|
48afe3a60c | ||
|
da42ea8c42 | ||
|
7e99bfebb3 | ||
|
32c88e1543 | ||
|
3dc3f4ad56 | ||
|
0ad2c418f5 | ||
|
089ba1aecb | ||
|
3ae9d13b2a | ||
|
6ee7023360 | ||
|
1dc763d77c | ||
|
651de0083f | ||
|
e911db2857 | ||
|
7e8f44ebf8 | ||
|
33e8bfc286 | ||
|
7e830e2c9c | ||
|
0f90492ec7 | ||
|
8137373c40 | ||
|
b5e24f84a3 | ||
|
36deb637fb | ||
|
833f8a0a3e | ||
|
a5b5899c93 | ||
|
9738f793e6 | ||
|
c0484d628d | ||
|
7ca11fdb53 | ||
|
ae9fc182bc | ||
|
a38716e1b0 | ||
|
d81b68eaa7 | ||
|
0173a06411 | ||
|
b614c8fff6 | ||
|
9aa8d0da86 | ||
|
012f8de6c5 | ||
|
81db3278bd | ||
|
5da7b06d62 | ||
|
4e28bd4c9c | ||
|
80bf97a8b1 | ||
|
73cd338325 | ||
|
bed57c4133 | ||
|
7d63185fd1 | ||
|
eeae7f7039 | ||
|
e5b2e7351d | ||
|
a78c8f3912 | ||
|
13cce40a3d | ||
|
bd37201109 | ||
|
d95cc05669 | ||
|
841097d4c3 | ||
|
d4d929c28f | ||
|
4374a94bbf | ||
|
a2dbebae14 | ||
|
f12b795fce | ||
|
afacb7d363 | ||
|
96800740e8 | ||
|
68f6f00cc2 | ||
|
77022b81f2 | ||
|
57e4999f2b | ||
|
77d4f9e2bd | ||
|
e98d40a8cd | ||
|
9bb6f65efa | ||
|
7fd2d1162d | ||
|
70c8a296ca | ||
|
1a7e7b9cd3 | ||
|
4646e221c0 | ||
|
64208edd90 | ||
|
12ff44d465 | ||
|
d22f9dc446 | ||
|
311ff22a3f | ||
|
41b0ba5c3c | ||
|
30131b0da1 | ||
|
5647b97b42 | ||
|
09c33baa02 | ||
|
0f2c1f50db | ||
|
75cef5b1ab | ||
|
53c964c77e | ||
|
3a239d5f80 | ||
|
6a1ad3f44e | ||
|
96d7146daf | ||
|
b5d38e3832 | ||
|
ba1cc3d372 | ||
|
508e9e92f8 | ||
|
99645044f0 | ||
|
5770d94c33 | ||
|
e6b707868f | ||
|
88c7928408 | ||
|
e4a4e0d958 | ||
|
64f4f82540 | ||
|
f099870c60 | ||
|
0363ac3a6a | ||
|
606b3f205d | ||
|
41697a4bab | ||
|
45a4dec18f | ||
|
1a30806f63 | ||
|
23a9465ae1 | ||
|
76aec4b4de | ||
|
084e0cc2a0 | ||
|
2da927d82b | ||
|
2c77d361d7 | ||
|
c33c1501f5 | ||
|
99cd7030e0 | ||
|
379ce8a094 | ||
|
cb76078758 | ||
|
ea190c481d | ||
|
97156dcca5 | ||
|
f810fc1cd6 | ||
|
db73bf1c2d | ||
|
e47831ebc2 | ||
|
c264ea9c13 | ||
|
880aa24dbb | ||
|
3ee77fb034 | ||
|
4f6a7b44ab | ||
|
57d7b442f6 | ||
|
98f79f83f1 | ||
|
afb1d2cafe | ||
|
d8f41a048e | ||
|
f8a9a9d633 | ||
|
277d9f9b88 | ||
|
f3ba042140 | ||
|
2d30362301 | ||
|
d6e2373de4 | ||
|
47dc43f601 | ||
|
9a2a19c694 | ||
|
392cb6e406 | ||
|
28ce61c983 | ||
|
78c48dc26a | ||
|
33f90f74d8 | ||
|
4df4892111 | ||
|
b6ad6d80d5 | ||
|
7f274ad5b2 | ||
|
6bc6f2d171 | ||
|
20fdd56ca2 | ||
|
03d705639a | ||
|
0fc6db187d | ||
|
8e4615de19 | ||
|
d8ad7ddd22 | ||
|
59b73a41b8 | ||
|
4d26df9580 | ||
|
24c9a18ea8 | ||
|
004ea3e7df | ||
|
56ad195878 | ||
|
ddf07eb68b | ||
|
6fc4cfb26d | ||
|
ba28ab78ed | ||
|
be66921b91 | ||
|
197bbe267c | ||
|
e42ab76088 | ||
|
d72723d6f2 | ||
|
66a58a90a3 | ||
|
2360c293bd | ||
|
a9522b88ab | ||
|
8feef1d3f9 | ||
|
0c2ee7906b | ||
|
c3523bcf8c | ||
|
ca0df327d9 | ||
|
fd85e3a202 | ||
|
d25f278230 | ||
|
159414f8e4 | ||
|
8891da5987 | ||
|
3898cf7253 | ||
|
62540b2773 | ||
|
8c9b326c3c | ||
|
1c301d890d | ||
|
db70f02df0 | ||
|
0b6d2f8eb5 | ||
|
a0250788b9 | ||
|
f502f4ceb9 | ||
|
5fdf614cfd | ||
|
a2bfcd3d5a | ||
|
0f41544830 | ||
|
c04d8a1a89 | ||
|
684ca07750 | ||
|
b71797c97f | ||
|
eaa05ead0b | ||
|
9fc3b456c8 | ||
|
2d6a381321 | ||
|
976b09785a | ||
|
ea3298359c | ||
|
84b0bd1de2 | ||
|
68f7e25d8d | ||
|
3423ea22fa | ||
|
0aeb9b923f | ||
|
e57d593c09 | ||
|
55ed118ae9 | ||
|
5d88f90010 | ||
|
90213a75e8 | ||
|
66170448ab | ||
|
c4690a25bb | ||
|
df5622c889 | ||
|
902f838178 | ||
|
36a4f9a68b | ||
|
96468a935d | ||
|
59141a9f03 | ||
|
7a6c5966d9 | ||
|
a7a658e42d | ||
|
3fa6478f94 | ||
|
7244e8dec5 | ||
|
1c0bdf189e | ||
|
1e85965d10 | ||
|
646ec42dd6 | ||
|
ac4177f22b | ||
|
21e8b5aa56 | ||
|
48aec57256 | ||
|
122d5c5634 | ||
|
983320e70b | ||
|
916e34a138 | ||
|
44cb9a2df3 | ||
|
91224a3787 | ||
|
e81982dd41 | ||
|
b9788e8f42 | ||
|
c4703a5e91 | ||
|
713e793f2d | ||
|
7fe5f3d02d | ||
|
c8982cf677 | ||
|
a75e250aeb | ||
|
5eb058e44a | ||
|
87b47bb8d1 | ||
|
6b4edc8e7a | ||
|
9b4d1139d1 | ||
|
41a499a35c | ||
|
06a598d577 | ||
|
9030ff3ab3 | ||
|
3a0efbb998 | ||
|
b582a26be2 | ||
|
60fd7edc7b | ||
|
cfab4fd29b | ||
|
d487dc2b4c | ||
|
4756437d9f | ||
|
ffa8d80ef7 | ||
|
1b3b78dc78 | ||
|
9e61d142e2 | ||
|
79a285919b | ||
|
103f8443b8 | ||
|
d98be68d96 | ||
|
4c759c3367 | ||
|
e70146fad6 | ||
|
b69076ac1a | ||
|
a88b0b59a5 | ||
|
4e385f2074 | ||
|
030e3785e9 | ||
|
fa8e77c750 | ||
|
8adc3ca40d | ||
|
a058b61276 | ||
|
ed90cd3995 | ||
|
c7e1f36cb3 | ||
|
0de85a8f19 | ||
|
025375e2fe | ||
|
54efa40eef | ||
|
8c0f3b68ed | ||
|
8eba5127ae | ||
|
da7a5e22e3 | ||
|
6db9b4d75d | ||
|
a8b30bcc14 | ||
|
a02175c2e3 | ||
|
92e41c3d6e | ||
|
e149c367f0 | ||
|
6b03d05ef5 | ||
|
f2cfaa2b40 | ||
|
7e03b63816 | ||
|
f78053fc0c | ||
|
e15746b4a4 | ||
|
d55c0e9593 | ||
|
14dd5aca64 | ||
|
7bf4859399 | ||
|
db47235354 | ||
|
846f9225f1 | ||
|
71638a13a4 | ||
|
b0bd76d5df | ||
|
09c44ac0b6 | ||
|
c9133afcbb | ||
|
9db8e8d7ec | ||
|
09473d627a | ||
|
15bc3a0b6f | ||
|
831db0acf3 | ||
|
79b350486a | ||
|
139cb1f02f | ||
|
321255a1b0 | ||
|
f31b707fdc | ||
|
59d578f292 | ||
|
4bf268764f | ||
|
ea343659ff | ||
|
636b72a471 | ||
|
31104c3fc2 | ||
|
9d9a764a81 | ||
|
e2f62ed18e | ||
|
a475addade | ||
|
8aa04c6a9c | ||
|
1380cbde3e | ||
|
0ac4fe60a8 | ||
|
d6244c814e | ||
|
9d5e983847 | ||
|
f4d0603cdb | ||
|
c11ea77f22 | ||
|
f8e9c99736 | ||
|
6cbb497902 | ||
|
c3456696ad | ||
|
8fc9495d5e | ||
|
a19629bd0e | ||
|
d43167707c | ||
|
3fc86482dc | ||
|
6256fc3d54 | ||
|
297d5196b8 | ||
|
7eaf0c8126 | ||
|
3ba4ed5f7d | ||
|
e9e8555f51 | ||
|
1c6d29b361 | ||
|
df0e1bde96 | ||
|
14a8f1f5e1 | ||
|
8f132e7524 | ||
|
8b12218f09 | ||
|
ab7b4c78ef | ||
|
8eef064bf2 | ||
|
276f634035 | ||
|
f5af2b12d2 | ||
|
1d0a3927a9 | ||
|
f11da32f0d | ||
|
8e39ff5c81 | ||
|
bb0c4e3fe7 | ||
|
55b8763f72 | ||
|
e48237f6d4 | ||
|
5694420f89 | ||
|
01fb43d51b | ||
|
1b7921b5f8 | ||
|
e1a7096d1f | ||
|
76f7b36508 | ||
|
254bbc444c | ||
|
dcebdfbeda | ||
|
d654d4f36b | ||
|
2269f8c770 | ||
|
6f4d84a08e | ||
|
2ce2817eac | ||
|
62e27da7da | ||
|
b983d5d409 | ||
|
ec9811dc34 | ||
|
562e9201c8 | ||
|
f02a8b8720 | ||
|
7f8e06a090 | ||
|
895ca38595 | ||
|
a09452fe49 | ||
|
e2814f2674 | ||
|
ef9a0ce0b2 | ||
|
77bc47671e | ||
|
0ff33edc6b | ||
|
7415dcbdb0 | ||
|
f7886abfbc | ||
|
5c9dd741d3 | ||
|
0ba381dbc4 | ||
|
13d62a6318 | ||
|
8abe2ad31b | ||
|
f82126153a | ||
|
6754c02a3d | ||
|
58446a992c | ||
|
4386c5567a | ||
|
e1c0ee8033 | ||
|
5da71a4f6e | ||
|
6bb4c3bd5e | ||
|
460b0fe2ba | ||
|
b9a01e686f | ||
|
011223ec05 | ||
|
e4445f0278 | ||
|
e1ff1207b6 | ||
|
a12aa1f3d4 | ||
|
8ca5f611d2 | ||
|
666b982c50 | ||
|
52dca21c8c | ||
|
cf1048afcb | ||
|
95527c6682 | ||
|
2c928fc210 | ||
|
7985f44389 | ||
|
4517b781cf | ||
|
9de5387fd6 | ||
|
abf328c564 | ||
|
304f5dd686 | ||
|
d6821e4385 | ||
|
2699c090f8 | ||
|
ac6b813481 | ||
|
43e1353154 | ||
|
58877d8844 | ||
|
ba5be7e2fb | ||
|
a36290fbcd | ||
|
3f49cdcb2d | ||
|
62a401f9b4 | ||
|
6d449ce291 | ||
|
ddc9d5a434 | ||
|
485048abf1 | ||
|
0e9b6e3cf3 | ||
|
8f2b9bf136 | ||
|
e4a1d0ab29 | ||
|
f4d15e0fca | ||
|
4a805f74b0 | ||
|
002321c339 | ||
|
bcdbfa67d7 | ||
|
e8022389fb | ||
|
7c47dd9122 | ||
|
12c59f66e2 | ||
|
21a14fc249 | ||
|
2541642160 | ||
|
56d7619e9f | ||
|
e018ea5143 | ||
|
a7ae7d3771 | ||
|
f541fd6a0f | ||
|
e47e0ee69f | ||
|
2ac625a511 | ||
|
d6fd263e9e | ||
|
9d6a74faa3 | ||
|
92cd48e512 | ||
|
56ab199856 | ||
|
9be108dbd5 | ||
|
664231f369 | ||
|
5bf481dc4e | ||
|
165bd89829 | ||
|
6c96da59a7 | ||
|
7e84d183ca | ||
|
f9f665d134 | ||
|
52096b1eff | ||
|
ac49786492 | ||
|
8820531e9f | ||
|
28bff050c6 | ||
|
b14581c522 | ||
|
4e00e66611 | ||
|
98954b5e3b | ||
|
762c0e5392 | ||
|
0aaa734030 | ||
|
2edbad3548 | ||
|
2d48320ac8 | ||
|
c58aa9596c | ||
|
9192a0a822 | ||
|
468cdb5497 | ||
|
5907985719 | ||
|
bb507959b9 | ||
|
22116aba27 | ||
|
6165d2db15 | ||
|
2961469244 | ||
|
a5192b8dbe | ||
|
cf0f6a0cce | ||
|
c8df5de095 | ||
|
bf21aad72f | ||
|
d2d13d1089 | ||
|
e5e204fca0 | ||
|
a07f76f540 | ||
|
881abecfc2 | ||
|
fd73722abd | ||
|
22b624e3b8 | ||
|
da823d2e6c | ||
|
4385fdf358 | ||
|
978c39edd1 | ||
|
b4b04f801c | ||
|
b8b6ede6f7 | ||
|
fe91fa635b | ||
|
dc57b87d42 | ||
|
39cba62009 | ||
|
a75bc4a4b5 | ||
|
44ff00025f | ||
|
8aa94fd0c5 | ||
|
0db41e1a14 | ||
|
659d6167c8 | ||
|
d1645b5bbb | ||
|
7d19b21638 | ||
|
b1cf1c405f | ||
|
f71659b5a6 | ||
|
5b5beb30d6 | ||
|
8cafc6d797 | ||
|
4f12602db3 | ||
|
02877f9095 | ||
|
e571f2ac58 | ||
|
1801feec6c | ||
|
1fbc4b9bfd | ||
|
ab1609ab49 | ||
|
d8fce3bb73 | ||
|
fb7fcd953e | ||
|
e94b558ae9 | ||
|
ad4ead570b | ||
|
e218b2432d | ||
|
990a5335d6 | ||
|
01e776edfb | ||
|
58c4c4e68f | ||
|
aca3073eea | ||
|
597d72a609 | ||
|
5b5de7ce90 | ||
|
44be2f4df5 | ||
|
5f8854335e | ||
|
82234ff0dd | ||
|
4a31d3a698 | ||
|
f2f7edbd67 | ||
|
384fa0b4c0 | ||
|
bf5a0069b8 | ||
|
db1d0e2640 | ||
|
56cf0049db | ||
|
d9d6d7e7c8 | ||
|
e15ba650f9 | ||
|
019523131d | ||
|
dc4ecc5940 | ||
|
688c0e7630 | ||
|
ed692c3d27 | ||
|
48cd8240e9 | ||
|
de03b67085 | ||
|
ec0a7d78c0 | ||
|
5fb8130baa | ||
|
426ac7048c | ||
|
01144fe9ba | ||
|
75005399aa | ||
|
b06c804bfc | ||
|
7c1b4ab61e | ||
|
018ba4a3f8 | ||
|
e532780a6e | ||
|
6bd6944c62 | ||
|
cfc81004bc | ||
|
da02ad0c7c | ||
|
0eb86db282 | ||
|
0a15f7bdce | ||
|
ba5f9120dc | ||
|
7a41cc3d4d | ||
|
2189267358 | ||
|
beac5caa09 | ||
|
99f52844ac | ||
|
efd132bd39 | ||
|
1a9517675d | ||
|
9767873cbc | ||
|
852ae43b96 | ||
|
4a8a13fe73 | ||
|
50bed7268a | ||
|
d76759bb2c | ||
|
f91aeb3ffd | ||
|
aa97309db9 | ||
|
3e947e1d82 | ||
|
796d000f8e | ||
|
19709bfe3b | ||
|
dcedb268dd | ||
|
645f61c8e6 | ||
|
97c72ee7ac | ||
|
c800e18f5b | ||
|
d462ecbb32 | ||
|
5f60058858 | ||
|
c1e4c5b81a | ||
|
a387ec9757 | ||
|
e1ffbcc54b | ||
|
d707771fe5 | ||
|
472299f878 | ||
|
fdd8ac00c9 | ||
|
ab0626977b | ||
|
544fb21a01 | ||
|
4da813e65f | ||
|
159aa3c6ec | ||
|
46c3ef337a | ||
|
8cc17464c3 | ||
|
ab5a4e36bc | ||
|
52773afd2f | ||
|
adeb6b2a46 | ||
|
6fb89f2b47 | ||
|
49dddbfce7 | ||
|
0f5f3df3f0 | ||
|
370799912f | ||
|
9f4159f18d | ||
|
63d51c530b | ||
|
081dfc9995 | ||
|
406f32c2fa | ||
|
b7962a24f1 | ||
|
0f79d45b23 | ||
|
94cab0c204 | ||
|
33ed68b2d0 | ||
|
651cb5802e | ||
|
fa3e6cb5a2 | ||
|
a1782aa626 | ||
|
36fca66c9a | ||
|
f12ce12155 | ||
|
5ffc0bda06 | ||
|
d7f2571c0c | ||
|
e0cce7f48c | ||
|
5c8afb4d0d | ||
|
57d2c40947 | ||
|
c0c3e7af13 | ||
|
2a9900d2eb | ||
|
483b3c6ce7 | ||
|
42d22bd6b7 | ||
|
ccf75e7104 | ||
|
735aacaade | ||
|
c208814dd0 | ||
|
5122d7eafb | ||
|
6081122311 | ||
|
84c7836e8c | ||
|
e37a3cef2e | ||
|
d22f606f49 | ||
|
97d34ed827 | ||
|
2fed4a5df4 | ||
|
1335b1c1c7 | ||
|
859f09aa0b | ||
|
a2dce46e38 | ||
|
a1c46a884b | ||
|
ca2227490e | ||
|
0e94fae1bf | ||
|
0fb6b2a02d | ||
|
b4504a2b46 | ||
|
4e863fd282 | ||
|
dba2526ffb | ||
|
f6e90d54fe | ||
|
4d38b61abc | ||
|
e292e726d3 | ||
|
e325653019 | ||
|
2cc7cacb92 | ||
|
6d9e683b20 | ||
|
b19bf1a6e0 | ||
|
8ace6e946b | ||
|
ffe2e33fa0 | ||
|
a7f853925a | ||
|
d17dae8bdd | ||
|
9af6b91ed0 | ||
|
c6b9817749 | ||
|
b036c4627e | ||
|
b34f09e58e | ||
|
a1fb0bead7 | ||
|
053fcfd3e3 | ||
|
6f9976b159 | ||
|
36e3c7eea9 | ||
|
9bdfe639ac | ||
|
e80bad1f0c | ||
|
0a41db6b53 | ||
|
d1e4f4d222 | ||
|
43a1aee28c | ||
|
0045e648bb | ||
|
03451b7a4f | ||
|
61383fd385 | ||
|
6772455111 | ||
|
5437e4ac7a | ||
|
b69efb5426 | ||
|
89e1c2eac5 | ||
|
5c4141f9a2 | ||
|
659013db56 | ||
|
d8e8ddd1bb | ||
|
2c34df7c71 | ||
|
3ec78bc55c | ||
|
c8770e5f4f | ||
|
aa07fd4482 | ||
|
4be989dd17 | ||
|
bb0cb0012d | ||
|
f2e3a2468a | ||
|
2311422c2d | ||
|
106c28cb34 | ||
|
ccb7b3603f | ||
|
2165798408 | ||
|
9192f5e1ab | ||
|
271351b3d6 | ||
|
45c1b70698 | ||
|
d0d6892858 | ||
|
8b4a5e540a | ||
|
80e53615c5 | ||
|
c7924e29f3 | ||
|
899156843e | ||
|
81ef875f72 | ||
|
139ca448ff | ||
|
2b917839c7 | ||
|
31e8e5f0af | ||
|
8d6f319489 | ||
|
9ca30b5c30 | ||
|
57013ed1e1 | ||
|
d96a639a3d | ||
|
2f0fd0f083 | ||
|
721d32c972 | ||
|
4bf76143f2 | ||
|
1cf2e2cd76 | ||
|
e98483aa90 | ||
|
7799dcd58e | ||
|
8d0a3a7405 | ||
|
40364cdcce | ||
|
35640260f5 | ||
|
54663e756d | ||
|
39fd5dbf3f | ||
|
396e0f0c38 | ||
|
fc6224dada | ||
|
ab564b77de | ||
|
1d8d1fe2c9 | ||
|
d43edf2fba | ||
|
d2cae41ece | ||
|
0923a3fbca | ||
|
a068838e0e | ||
|
7853f6b9b1 | ||
|
65614d68a7 | ||
|
114247e407 | ||
|
c07c21b6a9 | ||
|
937240a9fc | ||
|
0cf33501b6 | ||
|
15576fdc22 | ||
|
adb4b5326a | ||
|
284508632c | ||
|
dd08257fb9 | ||
|
71a0a228fb | ||
|
910094065e | ||
|
7bd0d571e6 | ||
|
ab6c55ec58 | ||
|
ad269b3c28 | ||
|
00ae824baf | ||
|
f3dd5753a3 | ||
|
c4fcf7c57e | ||
|
13336b966e | ||
|
7a64235440 | ||
|
a76989d885 | ||
|
6b712d59a3 | ||
|
5f0e5a5dfe | ||
|
92960d47ed | ||
|
8648fc605d | ||
|
6a71a505b3 | ||
|
d2cb4356f0 | ||
|
4153fa0fd9 | ||
|
daf4bbcc37 | ||
|
4f4f1f24fd | ||
|
ffde47af94 | ||
|
65d38d8722 | ||
|
db5a08b0c7 | ||
|
2b767e6bf9 | ||
|
b16cea984c | ||
|
7769351d42 | ||
|
c2c5bc8aa1 | ||
|
bf635a5e9a | ||
|
ae2ffccbc3 | ||
|
a005bf1ca0 | ||
|
013558eb77 | ||
|
4de443395d | ||
|
9f2bc5417f | ||
|
9deb2dcd74 | ||
|
2908b99435 | ||
|
c6d1bf450c | ||
|
26d26315ac | ||
|
cf21936f41 | ||
|
bcf185d54a | ||
|
5d9c8ee53f | ||
|
e1acdf1c85 | ||
|
7d3bfb5d3b | ||
|
186bbb11e3 | ||
|
b474e63924 | ||
|
09bbc7f22b | ||
|
d48747abff | ||
|
57f2c35af0 | ||
|
8c40320b64 | ||
|
8b3ad295cc | ||
|
c03c479a14 | ||
|
aa677353ad | ||
|
9c6c4078b1 | ||
|
22cee394ee | ||
|
9fba2b45ad | ||
|
8f4f6bc5cb | ||
|
71582fc415 | ||
|
0d1d38c18a | ||
|
4ec8841a57 | ||
|
2ecab27b16 | ||
|
8c6390733c | ||
|
ade53fc893 | ||
|
98f56ee58b | ||
|
1c01c35a87 | ||
|
673d1b6813 | ||
|
1ba12bb82d | ||
|
c6dfd3f362 | ||
|
f7888468c0 | ||
|
ce8bb30cf1 | ||
|
f90f108869 | ||
|
115d8562e1 | ||
|
f6607d0416 | ||
|
411bddd79d | ||
|
88c3f9077b | ||
|
bd13a39845 | ||
|
2a01df542d | ||
|
c976536b5f | ||
|
0721c833fb | ||
|
7b877e3aea | ||
|
2733444355 | ||
|
62f9f8f676 | ||
|
6692e5ce6d | ||
|
2d6f55ef0b | ||
|
42b3826115 | ||
|
cb722c5e89 | ||
|
32b35c9651 | ||
|
e33ea33797 | ||
|
38b3318704 | ||
|
ccec281e0d | ||
|
4611c35b0d | ||
|
8473b80978 | ||
|
230187d9ee | ||
|
c06c3df327 | ||
|
092bb83001 | ||
|
5c6303fec0 | ||
|
b05c5b1396 | ||
|
ac62aed420 | ||
|
8b540c350a | ||
|
db12077304 | ||
|
70488199b9 | ||
|
3bdf4c18ad | ||
|
e16be78ad5 | ||
|
09c5cc2f43 | ||
|
28319b216f | ||
|
07e6c3d0a8 | ||
|
96abb71d9c | ||
|
7de259af87 | ||
|
1567996ebc | ||
|
4edb3b3f0c | ||
|
07987504c3 | ||
|
5f93b68c25 | ||
|
d0e9c96573 | ||
|
a52ad44ef1 | ||
|
69b778c922 | ||
|
5f12c2e7b3 | ||
|
1a4a41a6b9 | ||
|
739b0c7f81 | ||
|
78c7039c54 | ||
|
e5e8ad5fbd | ||
|
21d2a6fb27 | ||
|
fa3e30c518 | ||
|
c6be2562ee | ||
|
4c19247a2c | ||
|
a6b502e0ba | ||
|
b12730d160 | ||
|
07031850e5 | ||
|
a42857038c | ||
|
d19969fce3 | ||
|
195e13682c | ||
|
95b9b79b02 | ||
|
225fed3012 | ||
|
86ebd7766e | ||
|
d5eafb1179 | ||
|
11b56571d2 | ||
|
18de1193b4 | ||
|
d8164a9b4d | ||
|
4f0ea76666 | ||
|
18e9cab9ef | ||
|
4d3aa1d17c | ||
|
6053e34d1d | ||
|
f274ae2fe3 | ||
|
195d2a1336 | ||
|
12846f9b1f | ||
|
748d8d7464 | ||
|
0ed1f3ccee | ||
|
fe712d27b7 | ||
|
c56666ed4b | ||
|
01184582b3 | ||
|
76745e8624 | ||
|
17b040d9b3 | ||
|
07e1c6214e | ||
|
de9bbfc896 | ||
|
d84977dee0 | ||
|
11041ff44f | ||
|
98826504d6 | ||
|
54ea57f330 | ||
|
cb4b2184a4 | ||
|
99fbb35512 | ||
|
1374375ab9 | ||
|
909ef91181 | ||
|
01f9a1f9cd | ||
|
78a3216b51 | ||
|
3a990e19a6 | ||
|
e5c8b9c763 | ||
|
e51e13b821 | ||
|
8a150439ae | ||
|
ec89a2cfa8 | ||
|
e79753748e | ||
|
0ed8bd6027 | ||
|
ff25b4945a | ||
|
3a2a2a4669 | ||
|
a8a9b24596 | ||
|
552ba3796b | ||
|
a6c6bca2fd | ||
|
f24c77f20a | ||
|
a3c27dbd72 | ||
|
ec7cf91579 | ||
|
d2fa5e38d0 | ||
|
c98f8e31d5 | ||
|
ada5374db5 | ||
|
301d68b1a2 | ||
|
46b6bb2305 | ||
|
93ba05f6cb | ||
|
e3976794d9 | ||
|
94cf5582e2 | ||
|
f10b370eff | ||
|
692c8601bf | ||
|
afcfffbd29 | ||
|
063fa46899 | ||
|
d1329849f3 | ||
|
02aec9ebea | ||
|
e33d63158a | ||
|
33258b0dcc | ||
|
f5d2776478 | ||
|
0496117fc1 | ||
|
b8e0dbf0c5 | ||
|
fcdf599e18 | ||
|
05b6bebf36 | ||
|
cdbc8d7ba1 | ||
|
072a722b09 | ||
|
2d2e2d7b1f | ||
|
1008bf988a | ||
|
8ec815a75b | ||
|
f4da75cea9 | ||
|
1c65722d24 | ||
|
2736390f95 | ||
|
8783db925f | ||
|
8b8bf7a321 | ||
|
5e61871091 | ||
|
9a65d13890 | ||
|
af2c01af66 | ||
|
80b26446f6 | ||
|
a0ac50d9c2 | ||
|
091a834074 | ||
|
6094f55182 | ||
|
11d9c77a79 | ||
|
76e67b1f63 | ||
|
64fc61a2d6 | ||
|
7b921ac161 | ||
|
57b19757b9 | ||
|
0d281af982 | ||
|
365e2c56d3 | ||
|
29fd2c068a | ||
|
aec2f5b57f | ||
|
4ba8bc9c5a | ||
|
77e021a371 | ||
|
2a25a8f70a | ||
|
b99a8de8c6 | ||
|
f922bbcd2e | ||
|
4db98684d3 | ||
|
a948d5eeb1 | ||
|
c7e6857492 | ||
|
aaa4216862 | ||
|
098396be87 | ||
|
d02c693202 | ||
|
cb11a26fbe | ||
|
43934d425f | ||
|
5b499de983 | ||
|
00d9f5759d | ||
|
a3bcedc38f | ||
|
ec02f63cac | ||
|
0de655d14f | ||
|
7ab6d439cb | ||
|
68e327847b | ||
|
81ea07f0a0 | ||
|
d7540c3305 | ||
|
2eff59908a | ||
|
a135e3ae9b | ||
|
28db2f4e93 | ||
|
fefd53c84f | ||
|
1fb8956679 | ||
|
d96a5e69b0 | ||
|
b9330fd28a | ||
|
b7d0fef8bb | ||
|
6cf8eb3e77 | ||
|
214c04d636 | ||
|
8f260f5efb | ||
|
2695a5ccdc | ||
|
3b0e100021 | ||
|
123b2243db | ||
|
c9ef435d78 | ||
|
2f403d3336 | ||
|
9c0f2a1859 | ||
|
ab35451121 | ||
|
b0327c4fa0 | ||
|
928a7aa58f | ||
|
f9e2ccb7d3 | ||
|
8db0e515a5 | ||
|
fd0fbf63f0 | ||
|
8d3653825a | ||
|
8836815186 | ||
|
2eb840e1c7 | ||
|
859f18883f | ||
|
66f62bcda0 | ||
|
6b8e828835 | ||
|
d56ce0c653 | ||
|
801a4ef1a7 | ||
|
12c32bde14 | ||
|
002b51a656 | ||
|
3405797bb8 | ||
|
5ce7d008e6 | ||
|
3ff7d1ea88 | ||
|
91921537ce | ||
|
0f587a4ec1 | ||
|
6a178478b4 | ||
|
f3af6a13ff | ||
|
4067a5c423 | ||
|
fbd7edf709 | ||
|
e8400b8702 | ||
|
3a16b75e82 | ||
|
586e1c3789 | ||
|
67f115e50f | ||
|
b0255a980c | ||
|
aaa0f004ee | ||
|
5a47d09c1a | ||
|
0525bd4317 | ||
|
7d5ff93946 | ||
|
4e78c2837c | ||
|
502cebfc3a | ||
|
0f3f50f63c | ||
|
69192a2a1b | ||
|
0ca85c5749 | ||
|
4577552773 | ||
|
bec985e360 | ||
|
ccb2dfbdbe | ||
|
b50b27d96e | ||
|
bf4bb3f704 | ||
|
5b4d017c53 | ||
|
70b3544f2a | ||
|
564b9db013 | ||
|
1ff0a623db | ||
|
a9c32debd2 | ||
|
c675acc2e7 | ||
|
0662100c30 | ||
|
74ad88d1bc | ||
|
de71785c76 | ||
|
27cbf920d5 | ||
|
9ca143c2db | ||
|
72700075ef | ||
|
ccdd15ed50 | ||
|
1d62dbbacd | ||
|
d04068e19f | ||
|
66aa7ccef3 | ||
|
c0c54f9ccd | ||
|
b41a8e38cf | ||
|
410b0952e9 | ||
|
b9f57c47b0 | ||
|
07b0b00b90 | ||
|
6e69246ca8 | ||
|
a5554ed85a | ||
|
6c17b77841 | ||
|
05a04997d6 | ||
|
b477b78d61 | ||
|
2b6193a1ec | ||
|
0e257c779b | ||
|
43eb09e9e2 | ||
|
6eab070bd1 | ||
|
3f148f3bf0 | ||
|
f3c2015210 | ||
|
afd653422e | ||
|
aad3336717 | ||
|
29e1694fce | ||
|
0ce1a565ab | ||
|
6cc1a8823d | ||
|
3667c82529 | ||
|
8cd050f71d | ||
|
88905b4e63 | ||
|
67db932b0f | ||
|
1be8edec92 | ||
|
65087f5d47 | ||
|
5388d03213 | ||
|
6da7a4134e | ||
|
b77330ac8f | ||
|
7fca229537 | ||
|
a2e5e00aa6 | ||
|
02fcff2c94 | ||
|
9e5b06e835 | ||
|
c7f96251ac | ||
|
1b9fd22ba1 | ||
|
fdd1b7235a | ||
|
0d50e5ef93 | ||
|
c4a886c28d | ||
|
0355ef9591 | ||
|
e0fa75cb00 | ||
|
3b991e0749 | ||
|
dd4dc738b4 | ||
|
d5c9ee341d | ||
|
f58cc159a7 | ||
|
fee413aa23 | ||
|
189db28dc5 | ||
|
86a237ccac | ||
|
9041e48080 | ||
|
4893fa4fbe | ||
|
082ef6830d | ||
|
2a7b112437 | ||
|
a4390f7918 | ||
|
e043a79721 | ||
|
2f37caaf22 | ||
|
77b6eff8e4 | ||
|
7d8ae689b5 | ||
|
143b9b675b | ||
|
ecb876157a | ||
|
f40c2ed618 | ||
|
61a32e13a2 | ||
|
c14ae284b6 | ||
|
7fc51ce513 | ||
|
0208c9b3f9 | ||
|
18a49cece3 | ||
|
03cdec8c31 | ||
|
1667c8b4a8 | ||
|
8018c7bed7 | ||
|
58f4cd8356 | ||
|
e41c0f32bb | ||
|
5de90bf421 | ||
|
493abb6228 | ||
|
a071e2e8f5 | ||
|
12e74c16b5 | ||
|
6d873ceccd | ||
|
26f45b429f | ||
|
49b2bd8772 | ||
|
354061f55c | ||
|
49975ab5d2 | ||
|
44f219402e | ||
|
b3a8ae8abe | ||
|
9ac2aaef81 | ||
|
de0a2e2a34 | ||
|
1702cd910a | ||
|
78639af1a5 | ||
|
8bebce4c6e | ||
|
e18f3746b4 | ||
|
10bde45004 | ||
|
057a26ea7b | ||
|
e8dae69f6f | ||
|
91923654f0 | ||
|
a0365e1277 | ||
|
4b6ad6a2ed | ||
|
e06f13fc84 | ||
|
6a14917ec5 | ||
|
85aab86b02 | ||
|
eeae98ca79 | ||
|
11f6eec913 | ||
|
e4d40b651e | ||
|
a05390ed6c | ||
|
c89c8d0523 | ||
|
7f4453bd37 | ||
|
e3568bc580 | ||
|
f273d262ac | ||
|
b55df32db0 | ||
|
6792283e44 | ||
|
33764e5c1c | ||
|
2d1897f720 | ||
|
be013ac6dc | ||
|
398837c20f | ||
|
a6f1e95cc0 | ||
|
a957e368e7 | ||
|
937465b2f2 | ||
|
e9df8c5cb1 | ||
|
4f107ad464 | ||
|
5b5a4e31e7 | ||
|
98685c6e6a | ||
|
cb064f9af5 | ||
|
604f1c4a89 | ||
|
590f3191ac | ||
|
e999e5acf8 | ||
|
101f846f0d | ||
|
3ec0c15039 | ||
|
ce433c0661 | ||
|
b185024aeb | ||
|
3a9312efe0 | ||
|
27535d4b43 | ||
|
1eeab5f75f | ||
|
b2adafdec7 | ||
|
9c6a72d3ac | ||
|
11342914cc | ||
|
b2f2deebce | ||
|
840a7a02bd | ||
|
f80b00667e | ||
|
ef9325aab9 |
11
.github/dependabot.yml
vendored
Normal file
11
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "pip" # See documentation for possible values
|
||||
directory: "/server" # Location of package manifests
|
||||
schedule:
|
||||
interval: "weekly"
|
19
.github/workflows/auto-assign.yml
vendored
Normal file
19
.github/workflows/auto-assign.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
name: Auto Assign
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
pull_request:
|
||||
types: [opened]
|
||||
jobs:
|
||||
run:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: 'Auto-assign issue'
|
||||
uses: pozil/auto-assign-issue@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
assignees: dkmstr
|
||||
numOfAssignee: 1
|
20
.github/workflows/dependency-review.yml
vendored
Normal file
20
.github/workflows/dependency-review.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
# Dependency Review Action
|
||||
#
|
||||
# This Action will scan dependency manifest files that change as part of a Pull Request, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging.
|
||||
#
|
||||
# Source repository: https://github.com/actions/dependency-review-action
|
||||
# Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement
|
||||
name: 'Dependency Review'
|
||||
on: [pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
dependency-review:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 'Checkout Repository'
|
||||
uses: actions/checkout@v3
|
||||
- name: 'Dependency Review'
|
||||
uses: actions/dependency-review-action@v3
|
92
.gitignore
vendored
92
.gitignore
vendored
@@ -13,59 +13,10 @@
|
||||
*.debhelper*
|
||||
*-stamp
|
||||
*.substvars
|
||||
|
||||
# /client/administration/
|
||||
/client/administration/*.suo
|
||||
|
||||
# /client/administration/UdsAdmin/
|
||||
/client/administration/UdsAdmin/*.user
|
||||
|
||||
# /client/administration/UdsAdmin/bin/
|
||||
/client/administration/UdsAdmin/bin/Debug
|
||||
/client/administration/UdsAdmin/bin/Release
|
||||
|
||||
# /client/administration/UdsAdmin/obj/x86/
|
||||
/client/administration/UdsAdmin/obj/x86/Debug
|
||||
/client/administration/UdsAdmin/obj/x86/Release
|
||||
|
||||
# /client/administration/installer/UDSAdminInstaller/
|
||||
/client/administration/installer/UDSAdminInstaller/MSChart.exe
|
||||
/client/administration/installer/UDSAdminInstaller/UDSAdminSetup.exe
|
||||
|
||||
# /linuxActor/
|
||||
/linuxActor/udsactor_*
|
||||
|
||||
# /linuxActor/src/debian/
|
||||
/linuxActor/src/debian/udsactor
|
||||
|
||||
# /linuxActorNX/
|
||||
/linuxActorNX/udsactor-nx_*
|
||||
|
||||
# /linuxActorNX/src/debian/
|
||||
/linuxActorNX/src/debian/udsactor-nx
|
||||
|
||||
# /linuxActorXRDP/
|
||||
/linuxActorXRDP/udsactor-xrdp_*
|
||||
|
||||
# /linuxActorXRDP/src/debian/
|
||||
/linuxActorXRDP/src/debian/udsactor-xrdp
|
||||
|
||||
# /nxtransport/jar/
|
||||
/nxtransport/jar/*.sh
|
||||
/nxtransport/jar/*.jar
|
||||
|
||||
# /nxtuntransport/jar/
|
||||
/nxtuntransport/jar/*.sh
|
||||
/nxtuntransport/jar/*.jar
|
||||
|
||||
# /rdptransport/java/jar/
|
||||
/rdptransport/java/jar/*.sh
|
||||
/rdptransport/java/jar/*.jar
|
||||
.coverage*
|
||||
|
||||
# /server/
|
||||
*_enterprise
|
||||
/server/openuds.sublime-project
|
||||
/server/openuds.sublime-workspace
|
||||
|
||||
# /server/src/
|
||||
/server/src/taskmanager.pid
|
||||
@@ -88,7 +39,6 @@
|
||||
# /server/src/uds/
|
||||
/server/src/uds/*_enterprise.py
|
||||
/server/src/uds/fixtures
|
||||
/server/src/uds/tests
|
||||
|
||||
# /server/src/uds/auths/
|
||||
/server/src/uds/auths/*-enterprise
|
||||
@@ -128,40 +78,10 @@
|
||||
# /server/src/uds/transports/
|
||||
/server/src/uds/transports/*-enterprise
|
||||
|
||||
# /udsService/
|
||||
/udsService/*.suo
|
||||
|
||||
# /udsService/installer/
|
||||
/udsService/installer/UDSActorSetup.exe
|
||||
|
||||
# /udsService/rpc/bin/
|
||||
/udsService/rpc/bin/Debug
|
||||
/udsService/rpc/bin/Release
|
||||
/udsService/rpc/bin/x86
|
||||
|
||||
# /udsService/rpc/obj/
|
||||
/udsService/rpc/obj/Debug
|
||||
/udsService/rpc/obj/Release
|
||||
/udsService/rpc/obj/x86
|
||||
|
||||
# /udsService/udsService/bin/
|
||||
/udsService/udsService/bin/Debug
|
||||
/udsService/udsService/bin/Release
|
||||
/udsService/udsService/bin/x64
|
||||
|
||||
# /udsService/udsService/obj/
|
||||
/udsService/udsService/obj/x64
|
||||
/udsService/udsService/obj/x86
|
||||
|
||||
# /udsService/udsgui/bin/
|
||||
/udsService/udsgui/bin/Debug
|
||||
/udsService/udsgui/bin/Release
|
||||
/udsService/udsgui/bin/x86
|
||||
|
||||
# /udsService/udsgui/obj/
|
||||
/udsService/udsgui/obj/Debug
|
||||
/udsService/udsgui/obj/Release
|
||||
/udsService/udsgui/obj/x86
|
||||
|
||||
.vscode
|
||||
.mypy_cache
|
||||
.pytest_cache
|
||||
.python-version
|
||||
|
||||
target/
|
||||
server/test-vars.ini
|
||||
|
16
.gitmodules
vendored
Normal file
16
.gitmodules
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
[submodule "guacamole-auth-uds"]
|
||||
path = guacamole-auth-uds
|
||||
url = git@github.com:VirtualCable/guacamole-auth-uds.git
|
||||
branch = master
|
||||
[submodule "tunnel-server"]
|
||||
path = tunnel-server
|
||||
url = git@github.com:VirtualCable/uds-tunnel-server.git
|
||||
branch = master
|
||||
[submodule "actor"]
|
||||
path = actor
|
||||
url = git@github.com:VirtualCable/uds-actor.git
|
||||
branch = master
|
||||
[submodule "client-py3"]
|
||||
path = client
|
||||
url = git@github.com:VirtualCable/uds-client.git
|
||||
branch = master
|
29
LICENSE
Normal file
29
LICENSE
Normal file
@@ -0,0 +1,29 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2022-2024, Virtual Cable S.L.U.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. 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.
|
||||
|
||||
3. Neither the name of the copyright holder 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.
|
14
README.md
14
README.md
@@ -1,15 +1,17 @@
|
||||

|
||||
|
||||
openuds
|
||||
OpenUDS
|
||||
=======
|
||||
|
||||
OpenUDS (Universal Desktop Services) is a multiplatform connection broker for:
|
||||
- VDI: Windows and Linux virtual desktops administration and deployment
|
||||
- App virtualization
|
||||
- Desktop services consolidation
|
||||
- ...
|
||||
|
||||
This is an Open Source Source project, initiated by Spanish Company Virtualcable and released Open Source with the help of several Spanish Universities.
|
||||
This is an Open Source project, initiated by Spanish Company Virtual Cable and released Open Source with the help of several Spanish Universities.
|
||||
|
||||
Any help provided will be welcome.
|
||||
Please feel free to contribute to this project.
|
||||
|
||||
**Note: Master version is always under heavy development and it is not recommended for use, it will probably have unfixed bugs. Please use the latest stable branch.**
|
||||
Notes
|
||||
=====
|
||||
* From 4.0 onwards (current master), OpenUDS has been splitted in several repositories and contains submodules. Remember to use "git clone --resursive ..." to fetch it ;-).
|
||||
* 4.0 version is tested on Python 3.11. It will probably work on 3.12 and 3.13 too (maybe 3.10, but not tested also)
|
||||
|
11
SECURITY.md
Normal file
11
SECURITY.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Security
|
||||
|
||||
Virtual Cable takes the security of our software products and services seriously.
|
||||
|
||||
If you find any vulnerability, please, report it to "agomez@virtualcable.net".
|
||||
|
||||
Please, do not use the issue tracker for security vulnerabilities.
|
||||
|
||||
[]: # Path: README.md
|
||||
|
||||
Thank you very much for your interest in OpenUDS.
|
1
actor
Submodule
1
actor
Submodule
Submodule actor added at 04ce3fc2d1
@@ -1,2 +0,0 @@
|
||||
PYTHONPATH=./src:${PYTHONPATH}
|
||||
|
8
actor/.gitignore
vendored
8
actor/.gitignore
vendored
@@ -1,8 +0,0 @@
|
||||
# Debian source builds
|
||||
udsactor_*.dsc
|
||||
udsactor_*.tar.xz
|
||||
udsactor_*.buildinfo
|
||||
udsactor_*.changes
|
||||
# And binaries
|
||||
udsactor*.deb
|
||||
udsactor*.rpm
|
@@ -1,4 +0,0 @@
|
||||
Linux:
|
||||
python3-prctl (recommended, but not required in fact)
|
||||
python3-pyqt5
|
||||
|
1
actor/linux/.gitignore
vendored
1
actor/linux/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
/udsactor-*[1-9].*.spec
|
@@ -1,91 +0,0 @@
|
||||
#!/usr/bin/make -f
|
||||
# -*- makefile -*-
|
||||
|
||||
.PHONY: install-udsactor install-udsactor-unmanaged
|
||||
|
||||
# Directories
|
||||
SOURCEDIR := ../src
|
||||
LIBDIR := $(DESTDIR)/usr/share/UDSActor
|
||||
BINDIR := $(DESTDIR)/usr/bin
|
||||
SBINDIR = $(DESTDIR)/usr/sbin
|
||||
APPSDIR := $(DESTDIR)/usr/share/applications
|
||||
CFGDIR := $(DESTDIR)/etc/udsactor
|
||||
SYSTEMDIR := $(DESTDIR)/etc/systemd/system
|
||||
POLKITDIR := $(DESTDIR)/usr/share/polkit-1/actions/
|
||||
XDGAUTOSTARTDIR := $(DESTDIR)/etc/xdg/autostart
|
||||
KDEAUTOSTARTDIR := $(DESTDIR)/usr/share/autostart
|
||||
|
||||
PYC := $(shell find $(SOURCEDIR) -name '*.py[co]')
|
||||
CACHES := $(shell find $(SOURCEDIR) -name '__pycache__' -o -name '.mypy_cache')
|
||||
|
||||
clean:
|
||||
rm -rf $(PYC) $(CACHES) $(DESTDIR)
|
||||
|
||||
install-udsactor: udsactor
|
||||
|
||||
install-udsactor-unmanaged: udsactor udsactor-unmanaged
|
||||
|
||||
udsactor-unmanaged:
|
||||
# Overwrite udsactor config is what to be done
|
||||
cp scripts/UDSActorConfig-unmanaged $(SBINDIR)/UDSActorConfig
|
||||
|
||||
udsactor:
|
||||
rm -rf $(DESTDIR)
|
||||
mkdir -p $(LIBDIR)
|
||||
mkdir -p $(BINDIR)
|
||||
mkdir -p $(SBINDIR)
|
||||
mkdir -p $(APPSDIR)
|
||||
mkdir -p $(CFGDIR)
|
||||
mkdir -p $(POLKITDIR)
|
||||
mkdir -p $(XDGAUTOSTARTDIR)
|
||||
mkdir -p $(KDEAUTOSTARTDIR)
|
||||
|
||||
mkdir $(LIBDIR)/img
|
||||
|
||||
# Cleans up .pyc and cache folders
|
||||
rm -f $(PYC) $(CACHES)
|
||||
|
||||
cp -r $(SOURCEDIR)/udsactor $(LIBDIR)/udsactor
|
||||
cp $(SOURCEDIR)/img/uds-icon.png $(LIBDIR)/img
|
||||
|
||||
cp $(SOURCEDIR)/actor_*.py $(LIBDIR)
|
||||
# QT Dialogs & resources
|
||||
cp -r $(SOURCEDIR)/ui $(LIBDIR)/ui
|
||||
|
||||
# Menu GUI app
|
||||
cp desktop/UDS_Actor_Configuration.desktop $(APPSDIR)
|
||||
|
||||
# Autostart elements for gnome/kde
|
||||
cp desktop/UDSActorTool.desktop $(XDGAUTOSTARTDIR)
|
||||
cp desktop/UDSActorTool.desktop $(KDEAUTOSTARTDIR)
|
||||
|
||||
# scripts
|
||||
cp scripts/udsactor $(BINDIR)
|
||||
cp scripts/UDSActorConfig $(SBINDIR)
|
||||
cp scripts/UDSActorConfig-pkexec $(SBINDIR)
|
||||
cp scripts/UDSActorTool $(BINDIR)
|
||||
cp scripts/UDSActorTool-startup $(BINDIR)
|
||||
cp scripts/udsvapp ${BINDIR}
|
||||
|
||||
# Policy to run as administrator
|
||||
cp policy/org.openuds.pkexec.UDSActorConfig.policy $(POLKITDIR)
|
||||
|
||||
# Fix permissions
|
||||
chmod 755 $(BINDIR)/udsactor
|
||||
chmod 755 $(BINDIR)/udsvapp
|
||||
chmod 755 $(BINDIR)/UDSActorTool-startup
|
||||
chmod 755 $(SBINDIR)/UDSActor*
|
||||
chmod 755 $(LIBDIR)/actor_*.py
|
||||
chmod 644 $(POLKITDIR)/org.openuds.pkexec.UDSActorConfig.policy
|
||||
|
||||
# If for red hat based, copy init.d
|
||||
ifeq ($(DISTRO),rh)
|
||||
mkdir -p $(SYSTEMDIR)
|
||||
cp debian/udsactor.service $(SYSTEMDIR)/
|
||||
endif
|
||||
|
||||
# chmod 0755 $(BINDIR)/udsactor
|
||||
uninstall:
|
||||
rm -rf $(LIBDIR)
|
||||
# rm -f $(BINDIR)/udsactor
|
||||
rm -rf $(CFGDIR)
|
@@ -1,36 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
VERSION=`cat ../../VERSION`
|
||||
RELEASE=1
|
||||
|
||||
top=`pwd`
|
||||
|
||||
# Debian based
|
||||
dpkg-buildpackage -b
|
||||
|
||||
cat udsactor-template.spec |
|
||||
sed -e s/"version 0.0.0"/"version ${VERSION}"/g |
|
||||
sed -e s/"release 1"/"release ${RELEASE}"/g > udsactor-$VERSION.spec
|
||||
|
||||
# Now fix dependencies for opensuse
|
||||
# Note that, although on opensuse the library is "libXss1" on newer,
|
||||
# the LibXscrnSaver is a "capability" and gets libXss1 installed
|
||||
# So right now, we only need 1 uds actor for both platforms.
|
||||
# cat udsactor-template.spec |
|
||||
# sed -e s/"version 0.0.0"/"version ${VERSION}"/g |
|
||||
# sed -e s/"name udsactor"/"name udsactor-opensuse"/g |
|
||||
# sed -e s/"libXScrnSaver"/"libXss1"/g > udsactor-opensuse-$VERSION.spec
|
||||
|
||||
#for pkg in udsactor-$VERSION.spec udsactor-opensuse-$VERSION.spec; do
|
||||
for pkg in udsactor-$VERSION.spec; do
|
||||
|
||||
rm -rf rpm
|
||||
for folder in SOURCES BUILD RPMS SPECS SRPMS; do
|
||||
mkdir -p rpm/$folder
|
||||
done
|
||||
|
||||
rpmbuild -v -bb --clean --buildroot=$top/rpm/BUILD/$pkg-root --target noarch $pkg 2>&1
|
||||
done
|
||||
|
||||
rpm --addsign ../*rpm
|
||||
#rm udsactor-$VERSION
|
2
actor/linux/debian/.gitignore
vendored
2
actor/linux/debian/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
/udsactor/
|
||||
/udsactor-unmanaged/
|
@@ -1,59 +0,0 @@
|
||||
udsactor (3.5.0) stable; urgency=medium
|
||||
|
||||
* Upgraded to 3.5.0 release
|
||||
|
||||
-- Adolfo Gómez García <agomez@virtualcable.es> Fri, 23 Oct 2020 8:00:00 +0200
|
||||
|
||||
udsactor (3.0.0) stable; urgency=medium
|
||||
|
||||
* Upgraded to 3.0.0 release
|
||||
|
||||
-- Adolfo Gómez García <agomez@virtualcable.es> Wed, 10 Jul 2019 9:24:10 +0200
|
||||
|
||||
udsactor (2.2.1) stable; urgency=medium
|
||||
|
||||
* Upgraded to 2.2.1 release
|
||||
|
||||
-- Adolfo Gómez García <agomez@virtualcable.es> Thu, 2 Oct 2018 12:44:12 +0200
|
||||
|
||||
udsactor (2.2.0) stable; urgency=medium
|
||||
|
||||
* Upgraded to 2.2.0 release
|
||||
|
||||
-- Adolfo Gómez García <agomez@virtualcable.es> Thu, 19 Oct 2017 16:44:12 +0200
|
||||
|
||||
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
|
||||
|
||||
-- Adolfo Gómez García <agomez@virtualcable.es> Tue, 01 Mar 2016 03:19:21 +0100
|
||||
|
||||
udsactor (1.9.0) stable; urgency=medium
|
||||
|
||||
* Upgrade for 1.9.0 (fixed package version)
|
||||
|
||||
-- Adolfo Gómez García <agomez@virtualcable.es> Tue, 05 May 2015 07:10:27 +0200
|
||||
|
||||
udsactor (1.7.5) stable; urgency=medium
|
||||
|
||||
* Upgrade for 1.7.5
|
||||
|
||||
-- Adolfo Gómez García <agomez@virtualcable.es> Thu, 23 Apr 2015 06:08:53 +0200
|
||||
|
||||
udsactor (1.7.0) stable; urgency=medium
|
||||
|
||||
* Initial release.
|
||||
|
||||
-- Adolfo Gómez García <agomez@virtualcable.es> Mon, 17 Nov 2014 05:32:41 +0100
|
@@ -1 +0,0 @@
|
||||
10
|
@@ -1,26 +0,0 @@
|
||||
Source: udsactor
|
||||
Section: admin
|
||||
Priority: optional
|
||||
Maintainer: Adolfo Gómez García <agomez@virtualcable.net>
|
||||
Build-Depends: debhelper (>= 7), po-debconf, dh-systemd (>= 1.5)
|
||||
Standards-Version: 4.1.4
|
||||
Homepage: http://www.udsenterprise.com
|
||||
|
||||
Package: udsactor
|
||||
Section: admin
|
||||
Priority: optional
|
||||
Architecture: all
|
||||
Depends: policykit-1(>=0.100), python3-requests (>=0.8.2), python3-pyqt5 (>=4.9), python3-six(>=1.1), python3 (>=3.6), libxss1, xscreensaver, ${misc:Depends}
|
||||
Recommends: python3-prctl(>=1.1.1)
|
||||
Description: Actor for Universal Desktop Services (UDS) Broker
|
||||
This package provides the required components to allow managed machines to work on an environment managed by UDS Broker.
|
||||
|
||||
Package: udsactor-unmanaged
|
||||
Section: admin
|
||||
Priority: optional
|
||||
Architecture: all
|
||||
Depends: policykit-1(>=0.100), python3-requests (>=0.8.2), python3-pyqt5 (>=4.9), python3-six(>=1.1), python3 (>=3.6), libxss1, xscreensaver, ${misc:Depends}
|
||||
Recommends: python3-prctl(>=1.1.1)
|
||||
Description: Actor for Universal Desktop Services (UDS) Broker Static Unmanaged machines
|
||||
This package provides the required components to allow unmanaged machines (static, independent machines) to work on an environment managed by UDS Broker.
|
||||
|
@@ -1,26 +0,0 @@
|
||||
Format-Specification: http://svn.debian.org/wsvn/dep/web/deps/dep5.mdwn?op=file&rev=135
|
||||
Name: udsactor
|
||||
Maintainer: Adolfo Gómez García
|
||||
Source: http://www.udsenterprise.com/
|
||||
|
||||
Copyright: 2014-2019 Virtual Cable S.L.U.
|
||||
License: BSD-3-clause
|
||||
|
||||
License: GPL-2+
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
.
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
.
|
||||
On Debian systems, the full text of the GNU General Public
|
||||
License version 2 can be found in the file
|
||||
`/usr/share/common-licenses/GPL-2'.
|
@@ -1 +0,0 @@
|
||||
readme.txt
|
@@ -1,3 +0,0 @@
|
||||
udsactor-unmanaged_3.5.0_all.deb admin optional
|
||||
udsactor_3.5.0_all.deb admin optional
|
||||
udsactor_3.5.0_amd64.buildinfo admin optional
|
@@ -1,47 +0,0 @@
|
||||
#!/usr/bin/make -f
|
||||
# -*- makefile -*-
|
||||
configure: configure-stamp
|
||||
configure-stamp:
|
||||
dh_testdir
|
||||
touch configure-stamp
|
||||
build: build-arch build-indep
|
||||
build-arch: build-stamp
|
||||
build-indep: build-stamp
|
||||
build-stamp: configure-stamp
|
||||
dh_testdir
|
||||
$(MAKE)
|
||||
touch $@
|
||||
clean:
|
||||
dh_testdir
|
||||
dh_testroot
|
||||
rm -f build-stamp configure-stamp
|
||||
dh_clean
|
||||
install: build
|
||||
dh_testdir
|
||||
dh_testroot
|
||||
dh_prep
|
||||
dh_installdirs
|
||||
$(MAKE) DESTDIR=$(CURDIR)/debian/udsactor install-udsactor
|
||||
$(MAKE) DESTDIR=$(CURDIR)/debian/udsactor-unmanaged install-udsactor-unmanaged
|
||||
binary-arch: build install
|
||||
# emptyness
|
||||
binary-indep: build install
|
||||
dh_testdir
|
||||
dh_testroot
|
||||
dh_installchangelogs
|
||||
dh_installdocs
|
||||
dh_installdebconf
|
||||
dh_systemd_enable
|
||||
dh_installinit --no-restart-on-upgrade --no-start --name=udsactor
|
||||
dh_systemd_start
|
||||
dh_python2=python
|
||||
dh_compress
|
||||
dh_link
|
||||
dh_fixperms
|
||||
dh_installdeb
|
||||
dh_shlibdeps
|
||||
dh_gencontrol
|
||||
dh_md5sums
|
||||
dh_builddeb
|
||||
binary: binary-indep
|
||||
.PHONY: build clean binary-indep binary install configure
|
@@ -1 +0,0 @@
|
||||
3.0 (native)
|
@@ -1 +0,0 @@
|
||||
udsactor.postinst
|
@@ -1 +0,0 @@
|
||||
udsactor.service
|
@@ -1,3 +0,0 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
exit 0
|
@@ -1,28 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
. /usr/share/debconf/confmodule
|
||||
|
||||
set -e
|
||||
case "$1" in
|
||||
configure)
|
||||
/usr/bin/python3 -m compileall /usr/share/UDSActor > /dev/nul 2>&1
|
||||
|
||||
# Fix perms so only root can access "masterKey"
|
||||
chmod 0700 /etc/udsactor
|
||||
# chmod 0600 /etc/udsactor/udsactor.cfg
|
||||
chown root:root /etc/udsactor
|
||||
# chown root:root /etc/udsactor/udsactor.cfg
|
||||
;;
|
||||
|
||||
abort-upgrade|abort-remove|abort-deconfigure)
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "postinst called with unknown argument \`$1'" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
#DEBHELPER#
|
||||
|
||||
exit 0
|
@@ -1,14 +0,0 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
. /usr/share/debconf/confmodule
|
||||
|
||||
set -e
|
||||
|
||||
if [ "$1" = "purge" ] ; then
|
||||
if [ -f /etc/udsactor/udsactor.cfg ]; then
|
||||
mv /etc/udsactor/udsactor.cfg /etc/udsactor/udsactor.cfg.dpkg-backup
|
||||
# Remove .pyc leaved behind
|
||||
rm -rf /usr/share/UDSActor || true > /dev/null 2>&1
|
||||
fi
|
||||
fi
|
||||
|
@@ -1 +0,0 @@
|
||||
#! /bin/bash -e
|
@@ -1,14 +0,0 @@
|
||||
[Unit]
|
||||
Description=UDS Broker actor
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
Group=root
|
||||
WorkingDirectory=/usr/share/UDSActor
|
||||
ExecStart=/usr/bin/python3 actor_service.py start-foreground
|
||||
PrivateTmp=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
@@ -1,12 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Name=UDS Actor Tool
|
||||
Comment=UDS Actor Userspace tools
|
||||
Exec=/usr/bin/UDSActorTool-startup
|
||||
Icon=/usr/share/UDSActor/img/uds-icon.png
|
||||
Terminal=false
|
||||
Type=Application
|
||||
NoDisplay=true
|
||||
X-KDE-autostart-after=panel
|
||||
X-KDE-StartupNotify=false
|
||||
X-DBUS-StartupType=None
|
||||
X-KDE-UniqueApplet=false
|
@@ -1,11 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Name=UDS Actor Configuration
|
||||
Version=1.0
|
||||
Exec=/usr/sbin/UDSActorConfig-pkexec
|
||||
Comment=UDS Actor Configuration Application. (Must be executed as root)
|
||||
Icon=/usr/share/UDSActor/img/uds-icon.png
|
||||
Type=Application
|
||||
Terminal=false
|
||||
StartupNotify=true
|
||||
Encoding=UTF-8
|
||||
Categories=Settings;System;
|
@@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE policyconfig PUBLIC
|
||||
"-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
|
||||
|
||||
<policyconfig>
|
||||
|
||||
<action id="org.freedesktop.policykit.pkexec.run-UDSActorConfig">
|
||||
<description>Run UDS Actor Configuration Program</description>
|
||||
<message>Authentication is required to run UDS Actor Configuration</message>
|
||||
<defaults>
|
||||
<allow_any>no</allow_any>
|
||||
<allow_inactive>no</allow_inactive>
|
||||
<allow_active>auth_admin_keep</allow_active>
|
||||
</defaults>
|
||||
<annotate key="org.freedesktop.policykit.exec.path">/usr/sbin/UDSActorConfig</annotate>
|
||||
<annotate key="org.freedesktop.policykit.exec.allow_gui">TRUE</annotate>
|
||||
</action>
|
||||
|
||||
</policyconfig>
|
@@ -1,3 +0,0 @@
|
||||
UDSActor is the client actor needed to get machines managed by UDS Broker.
|
||||
|
||||
Please, visit http://www.udsenterprise.com for more information
|
@@ -1,6 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
FOLDER=/usr/share/UDSActor
|
||||
|
||||
cd $FOLDER
|
||||
exec python3 actor_config.py -platform xcb $@
|
@@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
# pkexec env DISPLAY=$DISPLAY QT_X11_NO_MITSHM=1 "/usr/sbin/UDSActorConfig" "$@"
|
||||
pkexec "/usr/sbin/UDSActorConfig" "$@"
|
@@ -1,6 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
FOLDER=/usr/share/UDSActor
|
||||
|
||||
cd $FOLDER
|
||||
exec python3 actor_config_unmanaged.py -platform xcb $@
|
@@ -1,6 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
FOLDER=/usr/share/UDSActor
|
||||
|
||||
cd $FOLDER
|
||||
exec python3 -s actor_client.py -platform xcb $@
|
@@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
exec /usr/bin/UDSActorTool
|
@@ -1,13 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
env > /tmp/env.txt
|
||||
|
||||
if [ "$PAM_TYPE" = "open_session" ]; then
|
||||
nohup /usr/bin/udsactor login $PAM_USER &
|
||||
# Wait in backgroud to TTY to close (close_session is not being invoked right now)
|
||||
nohup /usr/bin/uds-wait-session &
|
||||
elif [ "$PAM_TYPE" = "close_session" ]; then
|
||||
nohup /usr/bin/udsactor logout $PAM_USER &
|
||||
fi
|
||||
|
||||
return 0
|
@@ -1,12 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
while :
|
||||
do
|
||||
sleep 5 # Wait 5 seconds between checks
|
||||
found=`ps -f -u$PAM_USER | grep -v grep | grep -v uds-wait-session | grep "$PAM_TTY" | wc -l`
|
||||
|
||||
if [ "$found" = "0" ]; then
|
||||
/usr/bin/udsactor logout $PAM_USER
|
||||
exit 0
|
||||
fi
|
||||
done
|
@@ -1,6 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
FOLDER=/usr/share/UDSActor
|
||||
|
||||
cd $FOLDER
|
||||
exec python3 actor_service.py $@
|
@@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
exec /usr/bin/udsactor login $2 &
|
@@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
exec /usr/bin/udsactor logout $2 &
|
@@ -1,5 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
/usr/bin/udsactor login "$USER"
|
||||
$@
|
||||
/usr/bin/udsactor logout "$USER"
|
@@ -1,70 +0,0 @@
|
||||
%define _topdir %(echo $PWD)/rpm
|
||||
%define name udsactor
|
||||
%define version 0.0.0
|
||||
%define release 1
|
||||
%define buildroot %{_topdir}/%{name}-%{version}-%{release}-root
|
||||
|
||||
BuildRoot: %{buildroot}
|
||||
Name: %{name}
|
||||
Version: %{version}
|
||||
Release: %{release}
|
||||
Summary: Actor for Universal Desktop Services (UDS) Broker
|
||||
License: BSD3
|
||||
Group: Admin
|
||||
Requires: python3-six python3-requests python3-qt5 libXScrnSaver
|
||||
Vendor: Virtual Cable S.L.U.
|
||||
URL: http://www.udsenterprise.com
|
||||
Provides: udsactor
|
||||
|
||||
%define _rpmdir ../
|
||||
%define _rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm
|
||||
|
||||
|
||||
%install
|
||||
curdir=`pwd`
|
||||
cd ../..
|
||||
make DESTDIR=$RPM_BUILD_ROOT DISTRO=rh install-udsactor
|
||||
cd $curdir
|
||||
|
||||
%clean
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
curdir=`pwd`
|
||||
cd ../..
|
||||
make DESTDIR=$RPM_BUILD_ROOT DISTRO=rh clean
|
||||
cd $curdir
|
||||
|
||||
|
||||
%post
|
||||
systemctl enable udsactor.service > /dev/null 2>&1
|
||||
|
||||
%preun
|
||||
systemctl disable udsactor.service > /dev/null 2>&1
|
||||
systemctl stop udsactor.service > /dev/null 2>&1
|
||||
|
||||
%postun
|
||||
# $1 == 0 on uninstall, == 1 on upgrade for preun and postun (just a reminder for me... :) )
|
||||
if [ $1 -eq 0 ]; then
|
||||
rm -rf /etc/udsactor
|
||||
rm /var/log/udsactor.log
|
||||
fi
|
||||
# And, posibly, the .pyc leaved behind on /usr/share/UDSActor
|
||||
rm -rf /usr/share/UDSActor > /dev/null 2>&1
|
||||
|
||||
%description
|
||||
This package provides the required components to allow this machine to work on an environment managed by UDS Broker.
|
||||
|
||||
%files
|
||||
%defattr(-,root,root)
|
||||
/etc/udsactor
|
||||
/etc/xdg/autostart/UDSActorTool.desktop
|
||||
/etc/systemd/system/udsactor.service
|
||||
/usr/bin/UDSActorTool-startup
|
||||
/usr/bin/udsactor
|
||||
/usr/bin/udsvapp
|
||||
/usr/bin/UDSActorTool
|
||||
/usr/sbin/UDSActorConfig
|
||||
/usr/sbin/UDSActorConfig-pkexec
|
||||
/usr/share/UDSActor/*
|
||||
/usr/share/applications/UDS_Actor_Configuration.desktop
|
||||
/usr/share/autostart/UDSActorTool.desktop
|
||||
/usr/share/polkit-1/actions/org.openuds.pkexec.UDSActorConfig.policy
|
3
actor/src/.gitignore
vendored
3
actor/src/.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
dist
|
||||
build
|
||||
*.spec
|
@@ -1,77 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2020 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
|
||||
'''
|
||||
# pylint: disable=invalid-name
|
||||
import sys
|
||||
import os
|
||||
|
||||
import PyQt5 # pylint: disable=unused-import
|
||||
from PyQt5.QtCore import QTimer
|
||||
from PyQt5.QtWidgets import QMainWindow
|
||||
|
||||
from udsactor.log import logger, INFO
|
||||
from udsactor.client import UDSClientQApp
|
||||
from udsactor.platform import operations
|
||||
|
||||
if __name__ == "__main__":
|
||||
logger.setLevel(INFO)
|
||||
|
||||
# Ensure idle operations is initialized on start
|
||||
operations.initIdleDuration(0)
|
||||
|
||||
if 'linux' in sys.platform:
|
||||
os.environ['QT_X11_NO_MITSHM'] = '1'
|
||||
|
||||
UDSClientQApp.setQuitOnLastWindowClosed(False)
|
||||
|
||||
qApp = UDSClientQApp(sys.argv)
|
||||
|
||||
if 'linux' not in sys.platform:
|
||||
# The "hidden window" is only needed to process events on Windows
|
||||
# Not needed on Linux
|
||||
mw = QMainWindow()
|
||||
mw.showMinimized() # Start minimized, will be hidden (not destroyed) as soon as qApp.init is invoked
|
||||
qApp.setMainWindow(mw)
|
||||
|
||||
qApp.init()
|
||||
|
||||
# Crate a timer to a "dummy" function, so python can check signals from time to time by executing the python interpreter
|
||||
# Note: Signals are only checked on python code execution, so we create a timer to force call back to python
|
||||
timer = QTimer(qApp)
|
||||
timer.start(1000)
|
||||
timer.timeout.connect(lambda *a: None) # type: ignore # timeout can be connected to a callable
|
||||
|
||||
qApp.exec()
|
||||
|
||||
# On windows, if no window is created, this point will never be reached.
|
||||
qApp.end()
|
||||
|
||||
logger.debug('Exiting...')
|
@@ -1,195 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2020 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
|
||||
'''
|
||||
# pylint: disable=invalid-name
|
||||
import sys
|
||||
import os
|
||||
import logging
|
||||
import typing
|
||||
|
||||
import PyQt5 # pylint: disable=unused-import
|
||||
from PyQt5.QtWidgets import QApplication, QDialog, QFileDialog, QMessageBox
|
||||
|
||||
import udsactor
|
||||
|
||||
from ui.setup_dialog_ui import Ui_UdsActorSetupDialog
|
||||
|
||||
# Not imported at runtime, just for type checking
|
||||
if typing.TYPE_CHECKING:
|
||||
from PyQt5.QtWidgets import QLineEdit # pylint: disable=ungrouped-imports
|
||||
|
||||
logger = logging.getLogger('actor')
|
||||
|
||||
class UDSConfigDialog(QDialog):
|
||||
_host: str = ''
|
||||
|
||||
def __init__(self) -> None:
|
||||
QDialog.__init__(self, None)
|
||||
# Get local config config
|
||||
config: udsactor.types.ActorConfigurationType = udsactor.platform.store.readConfig()
|
||||
self.ui = Ui_UdsActorSetupDialog()
|
||||
self.ui.setupUi(self)
|
||||
self.ui.host.setText(config.host)
|
||||
self.ui.validateCertificate.setCurrentIndex(1 if config.validateCertificate else 0)
|
||||
self.ui.postConfigCommand.setText(config.post_command or '')
|
||||
self.ui.preCommand.setText(config.pre_command or '')
|
||||
self.ui.runonceCommand.setText(config.runonce_command or '')
|
||||
self.ui.logLevelComboBox.setCurrentIndex(config.log_level)
|
||||
|
||||
if config.host:
|
||||
self.updateAuthenticators()
|
||||
|
||||
self.ui.username.setText('')
|
||||
self.ui.password.setText('')
|
||||
|
||||
self.ui.testButton.setEnabled(bool(config.master_token and config.host))
|
||||
|
||||
@property
|
||||
def api(self) -> udsactor.rest.UDSServerApi:
|
||||
return udsactor.rest.UDSServerApi(self.ui.host.text(), self.ui.validateCertificate.currentIndex() == 1)
|
||||
|
||||
def browse(self, lineEdit: 'QLineEdit', caption: str) -> None:
|
||||
name = QFileDialog.getOpenFileName(parent=self, caption=caption, directory=os.path.dirname(lineEdit.text()))[0]
|
||||
if name:
|
||||
if ' ' in name:
|
||||
name = '"' + name + '"'
|
||||
lineEdit.setText(os.path.normpath(name))
|
||||
|
||||
def browsePreconnect(self) -> None:
|
||||
self.browse(self.ui.preCommand, 'Select Preconnect command')
|
||||
|
||||
def browseRunOnce(self) -> None:
|
||||
self.browse(self.ui.runonceCommand, 'Select Runonce command')
|
||||
|
||||
def browsePostConfig(self) -> None:
|
||||
self.browse(self.ui.postConfigCommand, 'Select Postconfig command')
|
||||
|
||||
def updateAuthenticators(self) -> None:
|
||||
if self.ui.host.text() != self._host:
|
||||
self._host = self.ui.host.text()
|
||||
self.ui.authenticators.clear()
|
||||
auth: udsactor.types.AuthenticatorType
|
||||
auths = list(self.api.enumerateAuthenticators())
|
||||
if auths:
|
||||
for auth in auths:
|
||||
self.ui.authenticators.addItem(auth.auth, userData=auth)
|
||||
# Last, add "admin" authenticator (for uds root user)
|
||||
self.ui.authenticators.addItem('Administration', userData=udsactor.types.AuthenticatorType('admin', 'admin', 'admin', 'admin', 1, False))
|
||||
|
||||
def textChanged(self) -> None:
|
||||
enableButtons = bool(self.ui.host.text() and self.ui.username.text() and self.ui.password.text() and self.ui.authenticators.currentText())
|
||||
self.ui.registerButton.setEnabled(enableButtons)
|
||||
self.ui.testButton.setEnabled(False) # Only registered information can be checked
|
||||
|
||||
def finish(self) -> None:
|
||||
self.close()
|
||||
|
||||
def testUDSServer(self) -> None:
|
||||
config: udsactor.types.ActorConfigurationType = udsactor.platform.store.readConfig()
|
||||
if not config.master_token or not config.host:
|
||||
self.ui.testButton.setEnabled(False)
|
||||
return
|
||||
try:
|
||||
api = udsactor.rest.UDSServerApi(config.host, config.validateCertificate)
|
||||
if not api.test(config.master_token, udsactor.types.MANAGED):
|
||||
QMessageBox.information(
|
||||
self,
|
||||
'UDS Test',
|
||||
'Current configured token seems to be invalid for {}. Please, request a new one.'.format(config.host),
|
||||
QMessageBox.Ok
|
||||
)
|
||||
else:
|
||||
QMessageBox.information(
|
||||
self,
|
||||
'UDS Test',
|
||||
'Configuration for {} seems to be correct.'.format(config.host),
|
||||
QMessageBox.Ok
|
||||
)
|
||||
except Exception:
|
||||
QMessageBox.information(
|
||||
self,
|
||||
'UDS Test',
|
||||
'Configured host {} seems to be inaccesible.'.format(config.host),
|
||||
QMessageBox.Ok
|
||||
)
|
||||
|
||||
def registerWithUDS(self) -> None:
|
||||
# Get network card. Will fail if no network card is available, but don't mind (not contempled)
|
||||
data: udsactor.types.InterfaceInfoType = next(udsactor.platform.operations.getNetworkInfo())
|
||||
try:
|
||||
token = self.api.register(
|
||||
self.ui.authenticators.currentData().auth,
|
||||
self.ui.username.text(),
|
||||
self.ui.password.text(),
|
||||
udsactor.platform.operations.getComputerName(),
|
||||
data.ip or '', # IP
|
||||
data.mac or '', # MAC
|
||||
self.ui.preCommand.text(),
|
||||
self.ui.runonceCommand.text(),
|
||||
self.ui.postConfigCommand.text(),
|
||||
self.ui.logLevelComboBox.currentIndex() # Loglevel
|
||||
)
|
||||
# Store parameters on register for later use, notify user of registration
|
||||
udsactor.platform.store.writeConfig(
|
||||
udsactor.types.ActorConfigurationType(
|
||||
actorType=udsactor.types.MANAGED,
|
||||
host=self.ui.host.text(),
|
||||
validateCertificate=self.ui.validateCertificate.currentIndex() == 1,
|
||||
master_token=token,
|
||||
pre_command=self.ui.preCommand.text(),
|
||||
post_command=self.ui.postConfigCommand.text(),
|
||||
runonce_command=self.ui.runonceCommand.text(),
|
||||
log_level=self.ui.logLevelComboBox.currentIndex()
|
||||
)
|
||||
)
|
||||
# Enables test button
|
||||
self.ui.testButton.setEnabled(True)
|
||||
# Informs the user
|
||||
QMessageBox.information(self, 'UDS Registration', 'Registration with UDS completed.', QMessageBox.Ok)
|
||||
except udsactor.rest.RESTError as e:
|
||||
self.ui.testButton.setEnabled(False)
|
||||
QMessageBox.critical(self, 'UDS Registration', 'UDS Registration error: {}'.format(e), QMessageBox.Ok)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# If to be run as "sudo" on linux, we will need this to avoid problems
|
||||
if 'linux' in sys.platform:
|
||||
os.environ['QT_X11_NO_MITSHM'] = '1'
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
if udsactor.platform.operations.checkPermissions() is False:
|
||||
QMessageBox.critical(None, 'UDS Actor', 'This Program must be executed as administrator', QMessageBox.Ok) # type: ignore
|
||||
sys.exit(1)
|
||||
|
||||
myapp = UDSConfigDialog()
|
||||
myapp.show()
|
||||
sys.exit(app.exec())
|
@@ -1,192 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2020 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
|
||||
'''
|
||||
# pylint: disable=invalid-name
|
||||
import sys
|
||||
import os
|
||||
import pickle
|
||||
import logging
|
||||
import typing
|
||||
|
||||
import PyQt5 # pylint: disable=unused-import
|
||||
from PyQt5.QtWidgets import QApplication, QDialog, QMessageBox
|
||||
|
||||
import udsactor
|
||||
import udsactor.tools
|
||||
|
||||
from ui.setup_dialog_unmanaged_ui import Ui_UdsActorSetupDialog
|
||||
|
||||
# Not imported at runtime, just for type checking
|
||||
if typing.TYPE_CHECKING:
|
||||
from PyQt5.QtWidgets import QLineEdit # pylint: disable=ungrouped-imports
|
||||
|
||||
logger = logging.getLogger('actor')
|
||||
|
||||
|
||||
class UDSConfigDialog(QDialog):
|
||||
_host: str = ''
|
||||
_config: udsactor.types.ActorConfigurationType
|
||||
|
||||
def __init__(self) -> None:
|
||||
QDialog.__init__(self, None)
|
||||
# Get local config config
|
||||
self._config = udsactor.platform.store.readConfig()
|
||||
self.ui = Ui_UdsActorSetupDialog()
|
||||
self.ui.setupUi(self)
|
||||
self.ui.host.setText(self._config.host)
|
||||
self.ui.validateCertificate.setCurrentIndex(
|
||||
1 if self._config.validateCertificate else 0
|
||||
)
|
||||
self.ui.logLevelComboBox.setCurrentIndex(self._config.log_level)
|
||||
self.ui.serviceToken.setText(self._config.master_token or '')
|
||||
self.ui.restrictNet.setText(self._config.restrict_net or '')
|
||||
|
||||
self.ui.testButton.setEnabled(
|
||||
bool(self._config.master_token and self._config.host)
|
||||
)
|
||||
|
||||
@property
|
||||
def api(self) -> udsactor.rest.UDSServerApi:
|
||||
return udsactor.rest.UDSServerApi(
|
||||
self.ui.host.text(), self.ui.validateCertificate.currentIndex() == 1
|
||||
)
|
||||
|
||||
def finish(self) -> None:
|
||||
self.close()
|
||||
|
||||
def configChanged(self, text: str) -> None:
|
||||
self.ui.testButton.setEnabled(
|
||||
self.ui.host.text() == self._config.host
|
||||
and self.ui.serviceToken.text() == self._config.master_token
|
||||
and self.ui.restrictNet.text() == self._config.restrict_net
|
||||
)
|
||||
|
||||
def testUDSServer(self) -> None:
|
||||
if not self._config.master_token or not self._config.host:
|
||||
self.ui.testButton.setEnabled(False)
|
||||
return
|
||||
try:
|
||||
api = udsactor.rest.UDSServerApi(
|
||||
self._config.host, self._config.validateCertificate
|
||||
)
|
||||
if not api.test(self._config.master_token, udsactor.types.UNMANAGED):
|
||||
QMessageBox.information(
|
||||
self,
|
||||
'UDS Test',
|
||||
'Service token seems to be invalid . Please, check token validity.',
|
||||
QMessageBox.Ok,
|
||||
)
|
||||
else:
|
||||
QMessageBox.information(
|
||||
self,
|
||||
'UDS Test',
|
||||
'Configuration for {} seems to be correct.'.format(
|
||||
self._config.host
|
||||
),
|
||||
QMessageBox.Ok,
|
||||
)
|
||||
except Exception:
|
||||
QMessageBox.information(
|
||||
self,
|
||||
'UDS Test',
|
||||
'Configured host {} seems to be inaccesible.'.format(self._config.host),
|
||||
QMessageBox.Ok,
|
||||
)
|
||||
|
||||
def saveConfig(self) -> None:
|
||||
# Ensure restrict_net is empty or a valid subnet
|
||||
restrictNet = self.ui.restrictNet.text().strip()
|
||||
if restrictNet:
|
||||
try:
|
||||
subnet = udsactor.tools.strToNoIPV4Network(restrictNet)
|
||||
if not subnet:
|
||||
raise Exception('Invalid subnet')
|
||||
except Exception:
|
||||
QMessageBox.information(
|
||||
self,
|
||||
'Invalid subnet',
|
||||
'Invalid subnet {}. Please, check it.'.format(restrictNet),
|
||||
QMessageBox.Ok,
|
||||
)
|
||||
return
|
||||
|
||||
# Store parameters on register for later use, notify user of registration
|
||||
self._config = udsactor.types.ActorConfigurationType(
|
||||
actorType=udsactor.types.UNMANAGED,
|
||||
host=self.ui.host.text(),
|
||||
validateCertificate=self.ui.validateCertificate.currentIndex() == 1,
|
||||
master_token=self.ui.serviceToken.text().strip(),
|
||||
restrict_net=restrictNet,
|
||||
log_level=self.ui.logLevelComboBox.currentIndex(),
|
||||
)
|
||||
|
||||
udsactor.platform.store.writeConfig(self._config)
|
||||
# Enables test button
|
||||
self.ui.testButton.setEnabled(True)
|
||||
# Informs the user
|
||||
QMessageBox.information(
|
||||
self, 'UDS Configuration', 'Configuration saved.', QMessageBox.Ok
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# If to be run as "sudo" on linux, we will need this to avoid problems
|
||||
if 'linux' in sys.platform:
|
||||
os.environ['QT_X11_NO_MITSHM'] = '1'
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
if udsactor.platform.operations.checkPermissions() is False:
|
||||
QMessageBox.critical(None, 'UDS Actor', 'This Program must be executed as administrator', QMessageBox.Ok) # type: ignore
|
||||
sys.exit(1)
|
||||
|
||||
if len(sys.argv) > 2:
|
||||
if sys.argv[1] == 'export':
|
||||
try:
|
||||
with open(sys.argv[2], 'wb') as f:
|
||||
pickle.dump(udsactor.platform.store.readConfig(), f, protocol=3)
|
||||
except Exception as e:
|
||||
print('Error exporting configuration file: {}'.format(e))
|
||||
sys.exit(1)
|
||||
sys.exit(0)
|
||||
if sys.argv[1] == 'import':
|
||||
try:
|
||||
with open(sys.argv[2], 'rb') as f:
|
||||
config = pickle.load(f)
|
||||
udsactor.platform.store.writeConfig(config)
|
||||
except Exception as e:
|
||||
print('Error importing configuration file: {}'.format(e))
|
||||
sys.exit(1)
|
||||
sys.exit(0)
|
||||
|
||||
myapp = UDSConfigDialog()
|
||||
myapp.show()
|
||||
sys.exit(app.exec())
|
@@ -1,40 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2020 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
|
||||
'''
|
||||
import sys
|
||||
|
||||
if sys.platform == 'win32':
|
||||
from udsactor.windows import runner
|
||||
else:
|
||||
from udsactor.linux import runner
|
||||
|
||||
if __name__ == "__main__":
|
||||
runner.run()
|
@@ -1,398 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<author>Adolfo Gómez</author>
|
||||
<class>UdsActorSetupDialog</class>
|
||||
<widget class="QDialog" name="UdsActorSetupDialog">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::WindowModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>601</width>
|
||||
<height>243</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Verdana</family>
|
||||
<pointsize>9</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::DefaultContextMenu</enum>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>UDS Actor Configuration Tool</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset resource="uds.qrc">
|
||||
<normaloff>:/img/img/uds-icon.png</normaloff>:/img/img/uds-icon.png</iconset>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="locale">
|
||||
<locale language="English" country="UnitedStates"/>
|
||||
</property>
|
||||
<property name="sizeGripEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QPushButton" name="saveButton">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>210</y>
|
||||
<width>181</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>181</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::DefaultContextMenu</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Click to register Actor with UDS Broker</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string><html><head/><body><p>Click on this button to register Actor with UDS Broker.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Save Configuration</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="closeButton">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>410</x>
|
||||
<y>210</y>
|
||||
<width>171</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>171</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Closes UDS Actor Configuration (discard pending changes if any)</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string><html><head/><body><p>Exits the UDS Actor Configuration Tool</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Close</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="testButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>210</x>
|
||||
<y>210</y>
|
||||
<width>181</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>181</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Click to test existing configuration (disabled if no config found)</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string><html><head/><body><p>Click on this button to test the server host and assigned toen.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Test configuration</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QWidget" name="layoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>10</y>
|
||||
<width>571</width>
|
||||
<height>191</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetDefaultConstraint</enum>
|
||||
</property>
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
|
||||
</property>
|
||||
<property name="verticalSpacing">
|
||||
<number>16</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_security">
|
||||
<property name="text">
|
||||
<string>SSL Validation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="validateCertificate">
|
||||
<property name="toolTip">
|
||||
<string>Select communication security with broker</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string><html><head/><body><p>Select the security for communications with UDS Broker.</p><p>The recommended method of communication is <span style=" font-weight:600;">Use SSL</span>, but selection needs to be acording to your broker configuration.</p></body></html></string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Ignore certificate</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Verify certificate</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_host">
|
||||
<property name="text">
|
||||
<string>UDS Server</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="host">
|
||||
<property name="acceptDrops">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Uds Broker Server Addres. Use IP or FQDN</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string>Enter here the UDS Broker Addres using either its IP address or its FQDN address</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_serviceToken">
|
||||
<property name="text">
|
||||
<string>Service Token</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="serviceToken">
|
||||
<property name="toolTip">
|
||||
<string>UDS user with administration rights (Will not be stored on template)</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string><html><head/><body><p>Administrator user on UDS Server.</p><p>Note: This credential will not be stored on client. Will be used to obtain an unique token for this image.</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_loglevel">
|
||||
<property name="text">
|
||||
<string>Log Level</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QComboBox" name="logLevelComboBox">
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="frame">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">DEBUG</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">INFO</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">ERROR</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">FATAL</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_restrictNet">
|
||||
<property name="text">
|
||||
<string>Restrict Net</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLineEdit" name="restrictNet">
|
||||
<property name="toolTip">
|
||||
<string>UDS user with administration rights (Will not be stored on template)</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string><html><head/><body><p>Administrator user on UDS Server.</p><p>Note: This credential will not be stored on client. Will be used to obtain an unique token for this image.</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<zorder>label_host</zorder>
|
||||
<zorder>host</zorder>
|
||||
<zorder>label_serviceToken</zorder>
|
||||
<zorder>serviceToken</zorder>
|
||||
<zorder>validateCertificate</zorder>
|
||||
<zorder>label_security</zorder>
|
||||
<zorder>label_loglevel</zorder>
|
||||
<zorder>logLevelComboBox</zorder>
|
||||
<zorder>label_restrictNet</zorder>
|
||||
<zorder>restrictNet</zorder>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="uds.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>closeButton</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>UdsActorSetupDialog</receiver>
|
||||
<slot>finish()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>315</x>
|
||||
<y>165</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>231</x>
|
||||
<y>161</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>testButton</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>UdsActorSetupDialog</receiver>
|
||||
<slot>testUDSServer()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>300</x>
|
||||
<y>281</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>294</x>
|
||||
<y>153</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>saveButton</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>UdsActorSetupDialog</receiver>
|
||||
<slot>saveConfig()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>100</x>
|
||||
<y>191</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>297</x>
|
||||
<y>109</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>host</sender>
|
||||
<signal>textChanged(QString)</signal>
|
||||
<receiver>UdsActorSetupDialog</receiver>
|
||||
<slot>configChanged()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>341</x>
|
||||
<y>61</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>297</x>
|
||||
<y>109</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>serviceToken</sender>
|
||||
<signal>textChanged(QString)</signal>
|
||||
<receiver>UdsActorSetupDialog</receiver>
|
||||
<slot>configChanged()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>341</x>
|
||||
<y>100</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>297</x>
|
||||
<y>109</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>restrictNet</sender>
|
||||
<signal>textChanged(QString)</signal>
|
||||
<receiver>UdsActorSetupDialog</receiver>
|
||||
<slot>configChanged()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>341</x>
|
||||
<y>139</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>295</x>
|
||||
<y>121</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
<slots>
|
||||
<slot>finish()</slot>
|
||||
<slot>saveConfig()</slot>
|
||||
<slot>testUDSServer()</slot>
|
||||
<slot>configChanged()</slot>
|
||||
</slots>
|
||||
</ui>
|
@@ -1,660 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<author>Adolfo Gómez</author>
|
||||
<class>UdsActorSetupDialog</class>
|
||||
<widget class="QDialog" name="UdsActorSetupDialog">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::WindowModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>590</width>
|
||||
<height>307</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Verdana</family>
|
||||
<pointsize>9</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::DefaultContextMenu</enum>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>UDS Actor Configuration Tool</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset resource="uds.qrc">
|
||||
<normaloff>:/img/img/uds-icon.png</normaloff>:/img/img/uds-icon.png</iconset>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="locale">
|
||||
<locale language="English" country="UnitedStates"/>
|
||||
</property>
|
||||
<property name="sizeGripEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QPushButton" name="registerButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>270</y>
|
||||
<width>181</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>181</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::DefaultContextMenu</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Click to register Actor with UDS Broker</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string><html><head/><body><p>Click on this button to register Actor with UDS Broker.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Register with UDS</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="closeButton">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>410</x>
|
||||
<y>270</y>
|
||||
<width>171</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>171</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Closes UDS Actor Configuration (discard pending changes if any)</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string><html><head/><body><p>Exits the UDS Actor Configuration Tool</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Close</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>10</y>
|
||||
<width>571</width>
|
||||
<height>241</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tab_uds">
|
||||
<attribute name="title">
|
||||
<string>UDS Server</string>
|
||||
</attribute>
|
||||
<widget class="QWidget" name="layoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>10</y>
|
||||
<width>551</width>
|
||||
<height>191</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetDefaultConstraint</enum>
|
||||
</property>
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
|
||||
</property>
|
||||
<property name="verticalSpacing">
|
||||
<number>16</number>
|
||||
</property>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_host">
|
||||
<property name="text">
|
||||
<string>UDS Server</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="host">
|
||||
<property name="acceptDrops">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Uds Broker Server Addres. Use IP or FQDN</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string>Enter here the UDS Broker Addres using either its IP address or its FQDN address</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_auth">
|
||||
<property name="text">
|
||||
<string>Authenticator</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="authenticators">
|
||||
<property name="whatsThis">
|
||||
<string><html><head/><body><p>Select the UDS Broker authenticator for credentials validation</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_username">
|
||||
<property name="text">
|
||||
<string>Username</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLineEdit" name="username">
|
||||
<property name="toolTip">
|
||||
<string>UDS user with administration rights (Will not be stored on template)</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string><html><head/><body><p>Administrator user on UDS Server.</p><p>Note: This credential will not be stored on client. Will be used to obtain an unique token for this image.</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_password">
|
||||
<property name="text">
|
||||
<string>Password</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLineEdit" name="password">
|
||||
<property name="toolTip">
|
||||
<string>Password for user (Will not be stored on template)</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string><html><head/><body><p>Administrator password for the user on UDS Server.</p><p>Note: This credential will not be stored on client. Will be used to obtain an unique key for this image.</p></body></html></string>
|
||||
</property>
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="validateCertificate">
|
||||
<property name="toolTip">
|
||||
<string>Select communication security with broker</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string><html><head/><body><p>Select the security for communications with UDS Broker.</p><p>The recommended method of communication is <span style=" font-weight:600;">Use SSL</span>, but selection needs to be acording to your broker configuration.</p></body></html></string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Ignore certificate</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Verify certificate</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_security">
|
||||
<property name="text">
|
||||
<string>SSL Validation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<zorder>label_host</zorder>
|
||||
<zorder>host</zorder>
|
||||
<zorder>label_auth</zorder>
|
||||
<zorder>label_username</zorder>
|
||||
<zorder>username</zorder>
|
||||
<zorder>label_password</zorder>
|
||||
<zorder>password</zorder>
|
||||
<zorder>validateCertificate</zorder>
|
||||
<zorder>label_security</zorder>
|
||||
<zorder>authenticators</zorder>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_advanced">
|
||||
<attribute name="title">
|
||||
<string>Advanced</string>
|
||||
</attribute>
|
||||
<widget class="QWidget" name="layoutWidget_2">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>10</y>
|
||||
<width>551</width>
|
||||
<height>161</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
|
||||
</property>
|
||||
<property name="verticalSpacing">
|
||||
<number>16</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_host_2">
|
||||
<property name="text">
|
||||
<string>Preconnect</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="preCommand">
|
||||
<property name="acceptDrops">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Pre connection command. Executed just before the user is connected to machine.</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="browsePreconnectButton">
|
||||
<property name="text">
|
||||
<string>Browse</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_username_2">
|
||||
<property name="text">
|
||||
<string>Runonce</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="runonceCommand">
|
||||
<property name="toolTip">
|
||||
<string>Run once command. Executed on first boot, just before UDS does anything.</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="browseRunOnceButton">
|
||||
<property name="text">
|
||||
<string>Browse</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_password_2">
|
||||
<property name="text">
|
||||
<string>Postconfig</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<property name="spacing">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="postConfigCommand">
|
||||
<property name="toolTip">
|
||||
<string>Command to execute after UDS finalizes the VM configuration.</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Normal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="browsePostConfigButton">
|
||||
<property name="text">
|
||||
<string>Browse</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_loglevel">
|
||||
<property name="text">
|
||||
<string>Log Level</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QComboBox" name="logLevelComboBox">
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="frame">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">DEBUG</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">INFO</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">ERROR</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">FATAL</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="testButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>210</x>
|
||||
<y>270</y>
|
||||
<width>181</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>181</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Click to test existing configuration (disabled if no config found)</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string><html><head/><body><p>Click on this button to test the server host and assigned toen.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Test configuration</string>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="uds.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>closeButton</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>UdsActorSetupDialog</receiver>
|
||||
<slot>finish()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>315</x>
|
||||
<y>165</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>231</x>
|
||||
<y>161</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>registerButton</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>UdsActorSetupDialog</receiver>
|
||||
<slot>registerWithUDS()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>239</x>
|
||||
<y>132</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>285</x>
|
||||
<y>185</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>host</sender>
|
||||
<signal>textChanged(QString)</signal>
|
||||
<receiver>UdsActorSetupDialog</receiver>
|
||||
<slot>textChanged()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>239</x>
|
||||
<y>59</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>199</x>
|
||||
<y>150</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>username</sender>
|
||||
<signal>textChanged(QString)</signal>
|
||||
<receiver>UdsActorSetupDialog</receiver>
|
||||
<slot>textChanged()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>239</x>
|
||||
<y>98</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>199</x>
|
||||
<y>150</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>password</sender>
|
||||
<signal>textChanged(QString)</signal>
|
||||
<receiver>UdsActorSetupDialog</receiver>
|
||||
<slot>textChanged()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>239</x>
|
||||
<y>137</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>199</x>
|
||||
<y>150</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>browsePreconnectButton</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>UdsActorSetupDialog</receiver>
|
||||
<slot>browsePreconnect()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>430</x>
|
||||
<y>60</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>243</x>
|
||||
<y>150</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>browsePostConfigButton</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>UdsActorSetupDialog</receiver>
|
||||
<slot>browsePostConfig()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>430</x>
|
||||
<y>142</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>243</x>
|
||||
<y>150</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>browseRunOnceButton</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>UdsActorSetupDialog</receiver>
|
||||
<slot>browseRunOnce()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>430</x>
|
||||
<y>101</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>243</x>
|
||||
<y>150</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>host</sender>
|
||||
<signal>editingFinished()</signal>
|
||||
<receiver>UdsActorSetupDialog</receiver>
|
||||
<slot>updateAuthenticators()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>343</x>
|
||||
<y>98</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>294</x>
|
||||
<y>153</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>authenticators</sender>
|
||||
<signal>currentTextChanged(QString)</signal>
|
||||
<receiver>UdsActorSetupDialog</receiver>
|
||||
<slot>textChanged()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>343</x>
|
||||
<y>137</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>294</x>
|
||||
<y>153</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>testButton</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>UdsActorSetupDialog</receiver>
|
||||
<slot>testUDSServer()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>300</x>
|
||||
<y>281</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>294</x>
|
||||
<y>153</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
<slots>
|
||||
<slot>textChanged()</slot>
|
||||
<slot>finish()</slot>
|
||||
<slot>registerWithUDS()</slot>
|
||||
<slot>browsePreconnect()</slot>
|
||||
<slot>browseRunOnce()</slot>
|
||||
<slot>browsePostConfig()</slot>
|
||||
<slot>updateAuthenticators()</slot>
|
||||
<slot>testUDSServer()</slot>
|
||||
</slots>
|
||||
</ui>
|
@@ -1,5 +0,0 @@
|
||||
<RCC>
|
||||
<qresource prefix="img">
|
||||
<file>../img/uds-icon.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
@@ -1,16 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
|
||||
function process {
|
||||
# pyuic4 about-dialog.ui -o about_dialog_ui.py -x
|
||||
# pyuic4 message-dialog.ui -o message_dialog_ui.py
|
||||
pyuic5 setup-dialog.ui -o ../ui/setup_dialog_ui.py --import-from=ui
|
||||
pyuic5 setup-dialog-unmanaged.ui -o ../ui/setup_dialog_unmanaged_ui.py --import-from=ui
|
||||
}
|
||||
|
||||
pyrcc5 uds.qrc -o ../ui/uds_rc.py
|
||||
|
||||
|
||||
# process current directory ui's
|
||||
process
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 2.1 KiB |
Binary file not shown.
Before Width: | Height: | Size: 9.4 KiB |
@@ -1,38 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014 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 . import types
|
||||
from . import rest
|
||||
from . import platform
|
||||
|
||||
__title__ = 'udsactor'
|
||||
__author__ = 'Adolfo Gómez <dkmaster@dkmon.com>'
|
||||
__license__ = "BSD 3-clause"
|
||||
__copyright__ = "Copyright 2014-2020 VirtualCable S.L.U."
|
@@ -1,251 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2019 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
|
||||
'''
|
||||
import threading
|
||||
import time
|
||||
import datetime
|
||||
import signal
|
||||
import typing
|
||||
|
||||
from PyQt5.QtWidgets import QApplication, QMessageBox
|
||||
from PyQt5.QtCore import QByteArray, QBuffer, QIODevice, pyqtSignal
|
||||
|
||||
from . import rest
|
||||
from . import tools
|
||||
from . import platform
|
||||
|
||||
from .log import logger
|
||||
|
||||
from .http import client
|
||||
|
||||
# Not imported at runtime, just for type checking
|
||||
if typing.TYPE_CHECKING:
|
||||
from . import types
|
||||
from PyQt5.QtGui import QPixmap
|
||||
from PyQt5.QtWidgets import QMainWindow
|
||||
|
||||
class UDSClientQApp(QApplication):
|
||||
_app: 'UDSActorClient'
|
||||
_initialized: bool
|
||||
_mainWindow: typing.Optional['QMainWindow']
|
||||
|
||||
message = pyqtSignal(str, name='message')
|
||||
|
||||
def __init__(self, args) -> None:
|
||||
super().__init__(args)
|
||||
|
||||
self._mainWindow = None
|
||||
self._initialized = False
|
||||
|
||||
# This will be invoked on session close
|
||||
self.commitDataRequest.connect(self.end) # type: ignore # Will be invoked on session close, to gracely close app
|
||||
# self.aboutToQuit.connect(self.end)
|
||||
self.message.connect(self.showMessage) # type: ignore # there are problems with Pylance and connects on PyQt5... :)
|
||||
|
||||
# Execute backgroup thread for actions
|
||||
self._app = UDSActorClient(self)
|
||||
|
||||
def init(self) -> None:
|
||||
# Notify loging and mark it
|
||||
logger.debug('Starting APP')
|
||||
|
||||
if self._mainWindow:
|
||||
self._mainWindow.hide()
|
||||
|
||||
self._app.start()
|
||||
self._initialized = True
|
||||
|
||||
def end(self, sessionManager=None) -> None: # pylint: disable=unused-argument
|
||||
if not self._initialized:
|
||||
return
|
||||
|
||||
self._initialized = False
|
||||
|
||||
logger.debug('Stopping app thread')
|
||||
self._app.stop()
|
||||
|
||||
self._app.join()
|
||||
|
||||
def showMessage(self, message: str) -> None:
|
||||
QMessageBox.information(None, 'Message', message) # type: ignore
|
||||
|
||||
def setMainWindow(self, mw: 'QMainWindow'):
|
||||
self._mainWindow = mw
|
||||
|
||||
|
||||
class UDSActorClient(threading.Thread): # pylint: disable=too-many-instance-attributes
|
||||
_running: bool
|
||||
_forceLogoff: bool
|
||||
_extraLogoff: str
|
||||
_qApp: UDSClientQApp
|
||||
_listener: client.HTTPServerThread
|
||||
_loginInfo: typing.Optional['types.LoginResultInfoType']
|
||||
_notified: bool
|
||||
_notifiedDeadline: bool
|
||||
_sessionStartTime: datetime.datetime
|
||||
api: rest.UDSClientApi
|
||||
|
||||
def __init__(self, qApp: QApplication):
|
||||
super().__init__()
|
||||
|
||||
self.api = rest.UDSClientApi() # Self initialized
|
||||
self._qApp = typing.cast(UDSClientQApp, qApp)
|
||||
self._running = False
|
||||
self._forceLogoff = False
|
||||
self._extraLogoff = ''
|
||||
self._listener = client.HTTPServerThread(self)
|
||||
self._loginInfo = None
|
||||
self._notified = False
|
||||
self._notifiedDeadline = False
|
||||
|
||||
# Capture stop signals..
|
||||
logger.debug('Setting signals...')
|
||||
signal.signal(signal.SIGINT, self.stopSignal)
|
||||
signal.signal(signal.SIGTERM, self.stopSignal)
|
||||
|
||||
def stopSignal(self, signum, frame) -> None: # pylint: disable=unused-argument
|
||||
logger.info('Stop signal received')
|
||||
self.stop()
|
||||
|
||||
def checkDeadLine(self):
|
||||
if self._loginInfo is None or not self._loginInfo.dead_line: # No deadline check
|
||||
return
|
||||
|
||||
remainingTime = self._loginInfo.dead_line - (datetime.datetime.now() - self._sessionStartTime).total_seconds()
|
||||
logger.debug('Remaining time: {}'.format(remainingTime))
|
||||
|
||||
if not self._notifiedDeadline and remainingTime < 300: # With five minutes, show a warning message
|
||||
self._notifiedDeadline = True
|
||||
self._showMessage('Your session will expire in less that 5 minutes. Please, save your work and disconnect.')
|
||||
return
|
||||
|
||||
if remainingTime <= 0:
|
||||
logger.debug('Session dead line reached. Logging out')
|
||||
self._running = False
|
||||
self._forceLogoff = True
|
||||
|
||||
def checkIdle(self) -> None:
|
||||
if self._loginInfo is None or not self._loginInfo.max_idle: # No idle check
|
||||
return
|
||||
|
||||
idleTime = platform.operations.getIdleDuration()
|
||||
remainingTime = self._loginInfo.max_idle - idleTime
|
||||
|
||||
logger.debug('Idle: %s Remaining: %s', idleTime, remainingTime)
|
||||
|
||||
if remainingTime > 120: # Reset show Warning dialog if we have more than 5 minutes left
|
||||
self._notified = False
|
||||
return
|
||||
|
||||
if not self._notified and remainingTime < 120: # With two minutes, show a warning message
|
||||
self._notified = True
|
||||
self._showMessage('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, exiting from session')
|
||||
self._extraLogoff = ' (idle: {} vs {})'.format(int(idleTime), self._loginInfo.max_idle)
|
||||
self._running = False
|
||||
self._forceLogoff = True
|
||||
|
||||
def run(self) -> None:
|
||||
logger.debug('UDS Actor thread')
|
||||
self._listener.start() # async listener for service
|
||||
self._running = True
|
||||
|
||||
self._sessionStartTime = datetime.datetime.now()
|
||||
|
||||
time.sleep(0.4) # Wait a bit before sending login
|
||||
|
||||
try:
|
||||
# Notify loging and mark it
|
||||
user, sessionType = platform.operations.getCurrentUser(), platform.operations.getSessionType()
|
||||
self._loginInfo = self.api.login(user, sessionType)
|
||||
|
||||
if self._loginInfo.max_idle:
|
||||
platform.operations.initIdleDuration(self._loginInfo.max_idle)
|
||||
|
||||
while self._running:
|
||||
# Check Idle & dead line
|
||||
self.checkIdle()
|
||||
self.checkDeadLine()
|
||||
|
||||
time.sleep(1.3) # Sleeps between loop iterations
|
||||
|
||||
self.api.logout(user + self._extraLogoff, sessionType)
|
||||
logger.info('Notified logout for %s (%s)', user, sessionType) # Log logout
|
||||
|
||||
# Clean up login info
|
||||
self._loginInfo = None
|
||||
except Exception as e:
|
||||
logger.error('Error on client loop: %s', e)
|
||||
|
||||
self._listener.stop() # async listener for service
|
||||
|
||||
# Notify exit to qt
|
||||
QApplication.quit()
|
||||
|
||||
if self._forceLogoff:
|
||||
time.sleep(1.3) # Wait a bit before forcing logoff
|
||||
platform.operations.loggoff()
|
||||
|
||||
def _showMessage(self, message: str) -> None:
|
||||
self._qApp.message.emit(message) # type: ignore # there are problems with Pylance and connects on PyQt5... :)
|
||||
|
||||
def stop(self) -> None:
|
||||
logger.debug('Stopping client Service')
|
||||
self._running = False
|
||||
|
||||
def logout(self) -> typing.Any:
|
||||
self._forceLogoff = True
|
||||
self._running = False
|
||||
return 'ok'
|
||||
|
||||
def message(self, msg: str) -> typing.Any:
|
||||
threading.Thread(target=self._showMessage, args=(msg,)).start()
|
||||
return 'ok'
|
||||
|
||||
def screenshot(self) -> typing.Any:
|
||||
'''
|
||||
On windows, an RDP session with minimized screen will render "black screen"
|
||||
So only when user is using RDP connection will return an "actual" screenshot
|
||||
'''
|
||||
pixmap: 'QPixmap' = self._qApp.primaryScreen().grabWindow(0) # type: ignore
|
||||
ba = QByteArray()
|
||||
buffer = QBuffer(ba)
|
||||
buffer.open(QIODevice.WriteOnly)
|
||||
pixmap.save(buffer, 'PNG')
|
||||
buffer.close()
|
||||
scrBase64 = bytes(ba.toBase64()).decode() # type: ignore # there are problems with Pylance and connects on PyQt5... :)
|
||||
logger.debug('Screenshot length: %s', len(scrBase64))
|
||||
return scrBase64 # 'result' of JSON will contain base64 of screen
|
||||
|
||||
def script(self, script: str) -> typing.Any:
|
||||
tools.ScriptExecutorThread(script).start()
|
||||
return 'ok'
|
@@ -1,7 +0,0 @@
|
||||
from .. import types
|
||||
|
||||
defaultCertificate = types.CertificateInfoType(
|
||||
private_key='-----BEGIN ENCRYPTED PRIVATE KEY-----\nMIIFHTBPBgkqhkiG9w0BBQ0wQjApBgkqhkiG9w0BBQwwHAQIfG2+iMYJBswCAggA\nMAwGCCqGSIb3DQIJBQAwFQYJKwYBBAGXVQECBAhCusU5R8ulZQSCBMgheyZ81Qkq\n+TcbPeBlUGCFllSUOo7xQ/OuwYSmzLx8LpN0hQNv4azF6MYH+I8eMSPd3A547yW3\nJE4GjIBfRvcq2X1UZ2FQfECU9UP0ShPuPrVhIh6ZZklmlRjbIF8hGfSzXAuafQb+\n4wXXsofahi/SPgqK1Gw65nRiMcoeRZchJkx8pBgKVWED6Cbh6aAkeqkVKPnsebiV\n6kE+0C7+hgNUbyRd46R+/5NXzPjg4ItfSak+PLzQ1KeRv4Cu6DdzRKJ4V9/MlNdU\nNNEkSVSEaRn4sv+eByU4uxBMaSmD1tLc/A7OmaAeRpIQvls3Zcf2+V0+anAtjbjd\n6eIb2nceey+dKFm4ewlR4mXuzj1QowRTHceOIkvKIrOODxdy9M5hNBZ7VLum29tY\nRhqtmEH2BZZJ8SpM2SsEZzPxqJFiVZbvpeOKjxlMyn1dFWn1rP8uMnfuMKqBaj5D\nd5clOPlwebYw5UpM6Vvawu4nGqxECTSWcfNlDYO5U/0Fsm9+JIrJ7Buukgv2+rhs\nD/6oUK9NB8AW9qnDr7UxbC/ujhkKQG3woaZlPbiMs5WQaS+DrTg4N49wPzS0h+ME\nF8ZzuPnd6+sMGQioCIrQAZ08rk54oCijBhFh8/EQhQKGsMFw2swi9t6+FVU5Bvil\nlhmBd3LA5EuQ5y1X0jRL/+GDiUiZw1gOJP8d/XzhUJL9AmamdqJ6/rAU7lUTNWkM\ndzmFonUO2Mh2zgEEudHsTOH8udZ2l64LIHc6fCkDmM8QzghjrEFyci6R8333DSSM\nwbM0MvyTLM7TTqZUD60EgD+Ihyr/wJcBZY7GVn7hTq7ee14zeI+dZFmTMYOnt0mA\ngof19t0naPPZU+zyl/ambNF5mmSkGOAl4IBHNvPt5ztEVbNpwW3DHbmdYW71Ax+z\nCDlr4iKZahv21o1PCesPV2IlaHZFD6aBRt0DxzMqtq9cpWsI1g7aEaAjRbSvqhMY\npUeqFXz/GfR9rjRkufr48//ll0/Q/Ogx7m1TjQ6mAEQrklI7pa2W0u3H0BpSZSis\nR6ST3ulE+wfsp8cau6q2er+BSsDhBjSn9FeCUjHzY56u9ud/kb6/jLEdgxNpj0na\n3WVqCCCL/dAFSWznBmdracZsRMXapXInHCiiOEkXXbXIXvRKiTPJXdN+w2/U2j2B\nwXZuazVSpmM+xAZTAS9dtBUQJo+5px9b6P09uagvTA32ezbpPXf+hSfmTdUwbmAY\nrmE9SW85tzX+cD17loygBBRrjOr4uQy/s/9FqLx8bM73jly05rdOmX28ECKwEA05\n8aCFkfqrl9J9doVapaUlywpJVPFtE6W6tCF+ULMfb16vEjT1du1+epEnbGGLRQxg\n3aFLyKlvFaNvR38fiQFUGtBgGOaBN3rhGpbMwjch3oReXv9X/4UCL6sVIiOH2H3c\nVSZdC3O5g6CMVe4zckUe1k9mLDb5524IHDFfptZ6Bw+uzrqIy3GHW8dJF2AK471b\nMUnCojTpdbFHaUs2u/rNKVUyY+vLf8hkyP+znBUoPxSJtty53EWNukxjjsxx0lx3\niZGqN72lXlXuSFZAIxi307+xxE21cbzDsMidyJkbKKGm/F4BOKvX9jWmAyYmBG6A\n1L3yNRouFWsYDwYAX2nZ1is=\n-----END ENCRYPTED PRIVATE KEY-----\n',
|
||||
server_certificate='-----BEGIN CERTIFICATE-----\nMIIDcTCCAlkCBDfnXU8wDQYJKoZIhvcNAQELBQAwfTELMAkGA1UEBhMCRVMxDzAN\nBgNVBAgMBk1hZHJpZDEPMA0GA1UEBwwGTWFkcmlkMREwDwYDVQQKDAhVRFMgQ2Vy\ndDERMA8GA1UECwwIVURTIENlcnQxEjAQBgNVBAMMCTEyNy4wLjAuMTESMBAGA1Ud\nEQwJMTI3LjAuMC4xMB4XDTIwMDIxNzExNTkzMloXDTMwMDIxNDExNTkzMlowfTEL\nMAkGA1UEBhMCRVMxDzANBgNVBAgMBk1hZHJpZDEPMA0GA1UEBwwGTWFkcmlkMREw\nDwYDVQQKDAhVRFMgQ2VydDERMA8GA1UECwwIVURTIENlcnQxEjAQBgNVBAMMCTEy\nNy4wLjAuMTESMBAGA1UdEQwJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0BAQEFAAOC\nAQ8AMIIBCgKCAQEA2e1cW7YtRpNLazR3f/LqLv8OB0rKh8cUPH4wuQhbBTkee8Wu\n5eMSadRCIyRbKj4b8dtVfI9QW0SrmhGuMx1KCh3CsYd9XsWiKbGkiRBHIDOn5pkF\n6PUayDJ8KjnGbfnZjp0AmxXP4r1OO8jUPqzKS9Ubf5PgwcwdFiUKVfVPwGwctwt5\nt9YpSRONw0rTsCjVHvO2dd9h6EopskLCWxpN8l9kNLwLM/6t0IqVKmn5/IYPKKN2\nCX8a7IXpxwoiUs4sBZYhUMBWikB1hKQRSYafp1Xvc5PeTFXTFqGANnqz0NoZ8tqL\n8qjQUN/PCdtzhfcP5RgT2g1qyS2RBCMYH7Zs0wIDAQABMA0GCSqGSIb3DQEBCwUA\nA4IBAQCUt+qlLA1N9VXMwDQAYG4Kt6/UlMHCXAajHQQGtjdyGJ4++m7EIjI96hMU\n3Cx2gp2ggR3JGnuSR+DdBvPl5iGku7J8KV0JiJg30gTY8JuUIy/PMLZWloYKrBHV\nlin2GujQ4OsIt3dbr4XtcKW1Wd7L6fBzHlq7Xyxh+gcTzTvTmq67Q9XKlBWsegMf\nv4FKy0lfcSFK3vTzswQtuTontG4TqLiT/4AnMt3D0cTQ6b6KoZwUUX/TDNhau06d\nQ4Ilz8X61ka+4HBkFSR5ahP9noCVhwO329h+6epO141E5Tep3OLc/GCF4oaKOlMR\nfqxf5f2bghU0fxmtEoNJTZkBsN1S\n-----END CERTIFICATE-----\n',
|
||||
password='Pw7qbatz5u-y-Z5ora2D2ZuBCm95AHnKRcpze53k8tw'
|
||||
)
|
@@ -1,166 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2019 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
|
||||
'''
|
||||
# pylint: disable=invalid-name
|
||||
import threading
|
||||
import http.server
|
||||
import secrets
|
||||
import json
|
||||
import typing
|
||||
|
||||
from ..log import logger
|
||||
|
||||
# Not imported at runtime, just for type checking
|
||||
if typing.TYPE_CHECKING:
|
||||
from ..client import UDSActorClient
|
||||
|
||||
class HTTPServerHandler(http.server.BaseHTTPRequestHandler):
|
||||
protocol_version = 'HTTP/1.0'
|
||||
server_version = 'UDS Actor Server'
|
||||
sys_version = ''
|
||||
|
||||
_id: typing.ClassVar[str] # Random id for server
|
||||
_app: typing.ClassVar['UDSActorClient']
|
||||
|
||||
def sendJsonResponse(self, result: typing.Optional[typing.Any] = None, error: typing.Optional[str] = None, code: int = 200) -> None:
|
||||
data = json.dumps({'result': result, 'error': error})
|
||||
self.send_response(code)
|
||||
self.send_header('Content-type', 'application/json')
|
||||
self.send_header('Content-Length', str(len(data)))
|
||||
self.send_header('Server: ', self.server_version)
|
||||
self.end_headers()
|
||||
try:
|
||||
self.wfile.write(data.encode())
|
||||
except Exception:
|
||||
pass # Evict "broken pipe" when sending errors
|
||||
|
||||
def do_POST(self) -> None:
|
||||
# Only allows requests from localhost
|
||||
if self.client_address[0][:3] != '127':
|
||||
self.sendJsonResponse(error='Forbidden', code=403)
|
||||
|
||||
# Very simple path & params splitter
|
||||
path = self.path.split('?')[0][1:].split('/')
|
||||
if len(path) != 2 or path[0] != HTTPServerHandler._id:
|
||||
self.sendJsonResponse(error='Forbidden', code=403)
|
||||
|
||||
try:
|
||||
length = int(str(self.headers.get('content-length', '0')))
|
||||
content = self.rfile.read(length)
|
||||
params: typing.MutableMapping[str, str] = json.loads(content or '{}')
|
||||
except Exception as e:
|
||||
logger.error('Got exception executing POST {}: {}'.format(self.path, str(e)))
|
||||
self.sendJsonResponse(error='Invalid request', code=400)
|
||||
return
|
||||
|
||||
try:
|
||||
result = getattr(self, 'method_' + path[1])(params) # last part of path is method
|
||||
except AttributeError as e:
|
||||
logger.error('Invoked invalid method: %s: %s', path[1], e)
|
||||
self.sendJsonResponse(error='Invalid request', code=400)
|
||||
return
|
||||
except Exception as e:
|
||||
logger.error('Got exception executing {}: {}'.format('/'.join(path), str(e)))
|
||||
self.sendJsonResponse(error='Internal error', code=500)
|
||||
return
|
||||
|
||||
self.sendJsonResponse(result)
|
||||
|
||||
# Internal methods
|
||||
def method_ping(self, params: typing.MutableMapping[str, str]) -> typing.Any:
|
||||
return 'pong'
|
||||
|
||||
def method_logout(self, params: typing.MutableMapping[str, str]) -> typing.Any:
|
||||
return self._app.logout()
|
||||
|
||||
def method_message(self, params: typing.MutableMapping[str, str]) -> typing.Any:
|
||||
return self._app.message(params['message'])
|
||||
|
||||
def method_screenshot(self, params: typing.MutableMapping[str, str]) -> typing.Any:
|
||||
return self._app.screenshot()
|
||||
|
||||
def method_script(self, params: typing.MutableMapping[str, str]) -> typing.Any:
|
||||
return self._app.script(params['script'])
|
||||
|
||||
def do_GET(self) -> None:
|
||||
self.sendJsonResponse(error='Forbidden', code=403)
|
||||
|
||||
def log_error(self, format: str, *args): # pylint: disable=redefined-builtin
|
||||
logger.error(format, *args)
|
||||
|
||||
def log_message(self, format: str, *args): # pylint: disable=redefined-builtin
|
||||
logger.debug(format, *args)
|
||||
|
||||
class HTTPServerThread(threading.Thread):
|
||||
_server: typing.Optional[http.server.HTTPServer]
|
||||
_app: 'UDSActorClient'
|
||||
|
||||
port: int
|
||||
id: str
|
||||
|
||||
def __init__(self, app: 'UDSActorClient'):
|
||||
super().__init__()
|
||||
|
||||
self._server = None
|
||||
self._app = app
|
||||
|
||||
self.port = -1
|
||||
self.id = secrets.token_urlsafe(16)
|
||||
|
||||
@property
|
||||
def url(self) -> str:
|
||||
return 'http://127.0.0.1:{}/{}'.format(self.port, self.id)
|
||||
|
||||
def stop(self) -> None:
|
||||
if self._server:
|
||||
logger.debug('Stopping Http-client Service')
|
||||
try:
|
||||
self._app.api.unregister(self.url)
|
||||
except Exception as e:
|
||||
logger.error('Error unregistering on actor service: %s', e)
|
||||
self._server.shutdown()
|
||||
self._server = None
|
||||
|
||||
def run(self):
|
||||
HTTPServerHandler._app = self._app # pylint: disable=protected-access
|
||||
HTTPServerHandler._id = self.id # pylint: disable=protected-access
|
||||
|
||||
self._server = http.server.HTTPServer(('127.0.0.1', 0), HTTPServerHandler)
|
||||
|
||||
self.port = self._server.socket.getsockname()[1]
|
||||
|
||||
# Register using app api
|
||||
logger.debug('Registered %s', self.url)
|
||||
try:
|
||||
self._app.api.register(self.url)
|
||||
except Exception as e:
|
||||
logger.error('Error registering on actor service: %s', e)
|
||||
|
||||
self._server.serve_forever()
|
@@ -1,98 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2019 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
|
||||
'''
|
||||
# pylint: disable=invalid-name
|
||||
import json
|
||||
import typing
|
||||
|
||||
import requests
|
||||
|
||||
from ..log import logger
|
||||
|
||||
# For avoid proxy on localhost connections
|
||||
NO_PROXY = {
|
||||
'http': None,
|
||||
'https': None,
|
||||
}
|
||||
|
||||
class UDSActorClientPool:
|
||||
_clientUrl: typing.List[str]
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._clientUrl = []
|
||||
|
||||
def _post(self, method: str, data: typing.MutableMapping[str, str], timeout=2) -> typing.List[requests.Response]:
|
||||
removables: typing.List[str] = []
|
||||
result: typing.List[typing.Any] = []
|
||||
for clientUrl in self._clientUrl:
|
||||
try:
|
||||
result.append(requests.post(clientUrl + '/' + method, data=json.dumps(data), verify=False, timeout=timeout, proxies=NO_PROXY))
|
||||
except Exception as e:
|
||||
# If cannot request to a clientUrl, remove it from list
|
||||
logger.info('Could not connect with client %s: %s. Removed from registry.', e, clientUrl)
|
||||
removables.append(clientUrl)
|
||||
|
||||
# Remove failed connections
|
||||
for clientUrl in removables:
|
||||
self.unregister(clientUrl)
|
||||
|
||||
return result
|
||||
|
||||
def register(self, clientUrl: str) -> None:
|
||||
# Remove first if exists, to avoid duplicates
|
||||
self.unregister(clientUrl)
|
||||
# And add it again
|
||||
self._clientUrl.append(clientUrl)
|
||||
|
||||
def unregister(self, clientUrl: str) -> None:
|
||||
self._clientUrl = list((i for i in self._clientUrl if i != clientUrl))
|
||||
|
||||
def executeScript(self, script: str) -> None:
|
||||
self._post('script', {'script': script}, timeout=30)
|
||||
|
||||
def logout(self) -> None:
|
||||
self._post('logout', {})
|
||||
|
||||
def message(self, message: str) -> None:
|
||||
self._post('message', {'message': message})
|
||||
|
||||
def ping(self) -> bool:
|
||||
if not self._clientUrl:
|
||||
return True # No clients, ping ok
|
||||
self._post('ping', {}, timeout=1)
|
||||
return bool(self._clientUrl) # There was clients, but they are now lost!!!
|
||||
|
||||
def screenshot(self) -> typing.Optional[str]: # Screenshot are returned as base64
|
||||
for r in self._post('screenshot', {}, timeout=3):
|
||||
try:
|
||||
return r.json()['result']
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
@@ -1,44 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2019 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
|
||||
'''
|
||||
import typing
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from ..service import CommonService
|
||||
|
||||
class Handler:
|
||||
_service: 'CommonService'
|
||||
_method: str
|
||||
_params: typing.MutableMapping[str, str]
|
||||
|
||||
def __init__(self, service: 'CommonService', method: str, params: typing.MutableMapping[str, str]):
|
||||
self._service = service
|
||||
self._method = method
|
||||
self._params = params
|
@@ -1,56 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2019 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
|
||||
'''
|
||||
import typing
|
||||
|
||||
from . import handler
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from ..service import CommonService
|
||||
|
||||
class LocalProvider(handler.Handler):
|
||||
|
||||
def post_login(self) -> typing.Any:
|
||||
result = self._service.login(self._params['username'], self._params['session_type'])
|
||||
return result._asdict()
|
||||
|
||||
def post_logout(self) -> typing.Any:
|
||||
self._service.logout(self._params['username'], self._params['session_type'])
|
||||
return 'ok'
|
||||
|
||||
def post_ping(self) -> typing.Any:
|
||||
return 'pong'
|
||||
|
||||
def post_register(self) -> typing.Any:
|
||||
self._service._clientsPool.register(self._params['callback_url']) # pylint: disable=protected-access
|
||||
return 'ok'
|
||||
|
||||
def post_unregister(self) -> typing.Any:
|
||||
self._service._clientsPool.unregister(self._params['callback_url']) # pylint: disable=protected-access
|
@@ -1,100 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2019 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
|
||||
'''
|
||||
import typing
|
||||
|
||||
from .. import tools
|
||||
from . import handler
|
||||
|
||||
from ..log import logger
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from ..service import CommonService
|
||||
|
||||
|
||||
class PublicProvider(handler.Handler):
|
||||
def post_logout(self) -> typing.Any:
|
||||
logger.debug('Sending LOGOFF to clients')
|
||||
self._service._clientsPool.logout() # pylint: disable=protected-access
|
||||
return 'ok'
|
||||
|
||||
# Alias
|
||||
post_logoff = post_logout
|
||||
|
||||
def post_message(self) -> typing.Any:
|
||||
logger.debug('Sending MESSAGE to clients')
|
||||
if 'message' not in self._params:
|
||||
raise Exception('Invalid message parameters')
|
||||
self._service._clientsPool.message(
|
||||
self._params['message']
|
||||
) # pylint: disable=protected-access
|
||||
return 'ok'
|
||||
|
||||
def post_script(self) -> typing.Any:
|
||||
logger.debug('Received script: {}'.format(self._params))
|
||||
if 'script' not in self._params:
|
||||
raise Exception('Invalid script parameters')
|
||||
if self._params.get('user', False):
|
||||
logger.debug('Sending SCRIPT to client')
|
||||
self._service._clientsPool.executeScript(
|
||||
self._params['script']
|
||||
) # pylint: disable=protected-access
|
||||
else:
|
||||
# Execute script at server space, that is, here
|
||||
# as a parallel thread
|
||||
th = tools.ScriptExecutorThread(self._params['script'])
|
||||
th.start()
|
||||
return 'ok'
|
||||
|
||||
def post_preConnect(self) -> typing.Any:
|
||||
logger.debug('Received Pre connection')
|
||||
if 'user' not in self._params or 'protocol' not in self._params:
|
||||
raise Exception('Invalid preConnect parameters')
|
||||
return self._service.preConnect(
|
||||
self._params['user'],
|
||||
self._params['protocol'],
|
||||
self._params.get('ip', 'unknown'),
|
||||
self._params.get('hostname', 'unknown'),
|
||||
self._params.get('udsuser', 'unknown'),
|
||||
)
|
||||
|
||||
def get_information(self) -> typing.Any:
|
||||
# Return something useful? :)
|
||||
return 'UDS Actor Secure Server'
|
||||
|
||||
def get_screenshot(self) -> typing.Any:
|
||||
return (
|
||||
self._service._clientsPool.screenshot()
|
||||
) # pylint: disable=protected-access
|
||||
|
||||
def get_uuid(self) -> typing.Any:
|
||||
if self._service.isManaged():
|
||||
return self._service._cfg.own_token # pylint: disable=protected-access
|
||||
return ''
|
@@ -1,166 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2019 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
|
||||
'''
|
||||
import os
|
||||
import threading
|
||||
import http.server
|
||||
import json
|
||||
import ssl
|
||||
import typing
|
||||
|
||||
from ..log import logger
|
||||
from .. import certs
|
||||
from .. import rest
|
||||
|
||||
from .public import PublicProvider
|
||||
from .local import LocalProvider
|
||||
|
||||
# Not imported at runtime, just for type checking
|
||||
if typing.TYPE_CHECKING:
|
||||
from ..service import CommonService
|
||||
from .handler import Handler
|
||||
|
||||
class HTTPServerHandler(http.server.BaseHTTPRequestHandler):
|
||||
protocol_version = 'HTTP/1.0'
|
||||
server_version = 'UDS Actor Server'
|
||||
sys_version = ''
|
||||
|
||||
_service: typing.Optional['CommonService'] = None
|
||||
|
||||
def sendJsonResponse(self, result: typing.Optional[typing.Any] = None, error: typing.Optional[str] = None, code: int = 200) -> None:
|
||||
data = json.dumps({'result': result, 'error': error})
|
||||
self.send_response(code)
|
||||
self.send_header('Content-type', 'application/json')
|
||||
self.send_header('Content-Length', str(len(data)))
|
||||
self.send_header('Server: ', self.server_version)
|
||||
self.end_headers()
|
||||
self.wfile.write(data.encode())
|
||||
|
||||
def process(self, method: str, params: typing.MutableMapping[str, str]) -> None:
|
||||
if not self._service:
|
||||
self.sendJsonResponse(error='Not initialized', code=500)
|
||||
return
|
||||
|
||||
# Very simple path & params splitter
|
||||
path = self.path.split('?')[0][1:].split('/')
|
||||
|
||||
logger.debug('Path: %s, params: %s', path, params)
|
||||
|
||||
handlerType: typing.Optional[typing.Type['Handler']] = None
|
||||
|
||||
if len(path) == 3 and path[0] == 'actor' and path[1] == self._service._secret: # pylint: disable=protected-access
|
||||
# public method
|
||||
handlerType = PublicProvider
|
||||
elif len(path) == 2 and path[0] == 'ui':
|
||||
# private method, only from localhost
|
||||
if self.client_address[0][:3] == '127':
|
||||
handlerType = LocalProvider
|
||||
|
||||
if not handlerType:
|
||||
self.sendJsonResponse(error='Forbidden', code=403)
|
||||
return
|
||||
|
||||
try:
|
||||
result = getattr(handlerType(self._service, method, params), method + '_' + path[-1])() # last part of path is method
|
||||
except AttributeError:
|
||||
self.sendJsonResponse(error='Method not found', code=404)
|
||||
return
|
||||
except Exception as e:
|
||||
logger.error('Got exception executing {} {}: {}'.format(method, '/'.join(path), str(e)))
|
||||
self.sendJsonResponse(error=str(e), code=500)
|
||||
return
|
||||
|
||||
self.sendJsonResponse(result)
|
||||
|
||||
def do_GET(self) -> None:
|
||||
try:
|
||||
params = {v.split('=')[0]: v.split('=')[1] for v in self.path.split('?')[1].split('&')}
|
||||
except Exception:
|
||||
params = {}
|
||||
|
||||
self.process('get', params)
|
||||
|
||||
def do_POST(self) -> None:
|
||||
try:
|
||||
length = int(str(self.headers.get('content-length', '0')))
|
||||
content = self.rfile.read(length)
|
||||
params: typing.MutableMapping[str, str] = json.loads(content)
|
||||
except Exception as e:
|
||||
logger.error('Got exception executing POST {}: {}'.format(self.path, str(e)))
|
||||
self.sendJsonResponse(error='Invalid parameters', code=400)
|
||||
return
|
||||
|
||||
self.process('post', params)
|
||||
|
||||
def log_error(self, format, *args): # pylint: disable=redefined-builtin
|
||||
logger.error(format, *args)
|
||||
|
||||
def log_message(self, format, *args): # pylint: disable=redefined-builtin
|
||||
logger.debug(format, *args)
|
||||
|
||||
class HTTPServerThread(threading.Thread):
|
||||
_server: typing.Optional[http.server.HTTPServer]
|
||||
_service: 'CommonService'
|
||||
_certFile: typing.Optional[str]
|
||||
|
||||
def __init__(self, service: 'CommonService'):
|
||||
super().__init__()
|
||||
|
||||
self._server = None
|
||||
self._service = service
|
||||
self._certFile = None
|
||||
|
||||
def stop(self) -> None:
|
||||
logger.debug('Stopping Http-server Service')
|
||||
if self._server:
|
||||
self._server.shutdown()
|
||||
self._server = None
|
||||
|
||||
if self._certFile:
|
||||
try:
|
||||
os.unlink(self._certFile)
|
||||
except Exception as e:
|
||||
logger.error('Error removing certificate file: %s', e)
|
||||
logger.debug('Http-server stopped')
|
||||
|
||||
def run(self):
|
||||
HTTPServerHandler._service = self._service # pylint: disable=protected-access
|
||||
|
||||
self._certFile, password = certs.saveCertificate(self._service._certificate) # pylint: disable=protected-access
|
||||
|
||||
self._server = http.server.HTTPServer(('0.0.0.0', rest.LISTEN_PORT), HTTPServerHandler)
|
||||
# self._server.socket = ssl.wrap_socket(self._server.socket, certfile=self.certFile, server_side=True)
|
||||
|
||||
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
||||
# context.options = ssl.CERT_NONE
|
||||
context.load_cert_chain(certfile=self._certFile, password=password)
|
||||
self._server.socket = context.wrap_socket(self._server.socket, server_side=True)
|
||||
|
||||
self._server.serve_forever()
|
@@ -1,173 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014-2019 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
|
||||
'''
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import atexit
|
||||
from signal import SIGTERM, SIGKILL
|
||||
|
||||
from udsactor.log import logger
|
||||
|
||||
|
||||
class Daemon:
|
||||
"""
|
||||
A generic daemon class.
|
||||
|
||||
Usage: subclass the Daemon class and override the run() method
|
||||
"""
|
||||
|
||||
def __init__(self, pidfile: str, stdin: str = '/dev/null', stdout: str = '/dev/null', stderr: str = '/dev/null'):
|
||||
self.stdin = stdin
|
||||
self.stdout = stdout
|
||||
self.stderr = stderr
|
||||
self.pidfile = pidfile
|
||||
|
||||
def daemonize(self) -> None:
|
||||
"""
|
||||
do the UNIX double-fork magic, see Stevens' "Advanced
|
||||
Programming in the UNIX Environment" for details (ISBN 0201563177)
|
||||
http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
|
||||
"""
|
||||
try:
|
||||
pid = os.fork()
|
||||
if pid > 0:
|
||||
# exit first parent
|
||||
sys.exit(0)
|
||||
except OSError as e:
|
||||
logger.error("fork #1 error: {}".format(e))
|
||||
sys.stderr.write("fork #1 failed: {}\n".format(e))
|
||||
sys.exit(1)
|
||||
|
||||
# decouple from parent environment
|
||||
os.chdir("/")
|
||||
os.setsid()
|
||||
os.umask(0)
|
||||
|
||||
# do second fork
|
||||
try:
|
||||
pid = os.fork()
|
||||
if pid > 0:
|
||||
# exit from second parent
|
||||
sys.exit(0)
|
||||
except OSError as e:
|
||||
logger.error("fork #2 error: {}".format(e))
|
||||
sys.stderr.write("fork #2 failed: {}\n".format(e))
|
||||
sys.exit(1)
|
||||
|
||||
# redirect standard file descriptors
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
si = open(self.stdin, 'r')
|
||||
so = open(self.stdout, 'ab+')
|
||||
se = open(self.stderr, 'ab+', 0)
|
||||
os.dup2(si.fileno(), sys.stdin.fileno())
|
||||
os.dup2(so.fileno(), sys.stdout.fileno())
|
||||
os.dup2(se.fileno(), sys.stderr.fileno())
|
||||
|
||||
# write pidfile
|
||||
atexit.register(self.removePidFile)
|
||||
pidStr = str(os.getpid())
|
||||
with open(self.pidfile, 'w+') as f:
|
||||
f.write("{}\n".format(pidStr))
|
||||
|
||||
def removePidFile(self) -> None:
|
||||
try:
|
||||
os.remove(self.pidfile)
|
||||
except Exception:
|
||||
# Not found/not permissions or whatever, ignore it
|
||||
pass
|
||||
|
||||
def start(self) -> None:
|
||||
"""
|
||||
Start the daemon
|
||||
"""
|
||||
logger.debug('Starting daemon')
|
||||
# Check for a pidfile to see if the daemon already runs
|
||||
if os.path.exists(self.pidfile):
|
||||
message = "pidfile {} already exist. Daemon already running?\n".format(self.pidfile)
|
||||
logger.error(message)
|
||||
sys.stderr.write(message)
|
||||
sys.exit(1)
|
||||
|
||||
# Start the daemon
|
||||
self.daemonize()
|
||||
try:
|
||||
self.run()
|
||||
except Exception as e:
|
||||
logger.error('Exception running process: {}'.format(e))
|
||||
|
||||
self.removePidFile()
|
||||
|
||||
def stop(self) -> None:
|
||||
"""
|
||||
Stop the daemon
|
||||
"""
|
||||
# Get the pid from the pidfile
|
||||
try:
|
||||
pf = open(self.pidfile, 'r')
|
||||
pid = int(pf.read().strip())
|
||||
pf.close()
|
||||
except IOError:
|
||||
message = "pidfile {} does not exist. Daemon not running?\n".format(self.pidfile)
|
||||
logger.error(message)
|
||||
# sys.stderr.write(message)
|
||||
return # not an error in a restart
|
||||
|
||||
# Try killing the daemon process
|
||||
try:
|
||||
cnt = 10
|
||||
while cnt:
|
||||
cnt -= 1
|
||||
os.kill(pid, SIGTERM)
|
||||
time.sleep(1)
|
||||
|
||||
if not cnt:
|
||||
os.kill(pid, SIGKILL)
|
||||
except OSError as err:
|
||||
if err.errno == 3: # No such process
|
||||
if os.path.exists(self.pidfile):
|
||||
os.remove(self.pidfile)
|
||||
else:
|
||||
sys.stderr.write('Error: {}'.format(err))
|
||||
sys.exit(1)
|
||||
|
||||
def restart(self) -> None:
|
||||
"""
|
||||
Restart the daemon
|
||||
"""
|
||||
self.stop()
|
||||
self.start()
|
||||
|
||||
# Overridables
|
||||
def run(self) -> None:
|
||||
"""
|
||||
override this to provide your own daemon
|
||||
"""
|
@@ -1,74 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014-2019 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
|
||||
'''
|
||||
# pylint: disable=invalid-name
|
||||
import os
|
||||
import tempfile
|
||||
import logging
|
||||
import typing
|
||||
|
||||
class LocalLogger: # pylint: disable=too-few-public-methods
|
||||
linux = False
|
||||
windows = True
|
||||
serviceLogger = False
|
||||
|
||||
logger: typing.Optional[logging.Logger]
|
||||
|
||||
def __init__(self) -> None:
|
||||
# tempdir is different for "user application" and "service"
|
||||
# service wil get c:\windows\temp, while user will get c:\users\XXX\temp
|
||||
# Try to open logger at /var/log path
|
||||
# If it fails (access denied normally), will try to open one at user's home folder, and if
|
||||
# agaim it fails, open it at the tmpPath
|
||||
for logDir in ('/var/log', os.path.expanduser('~'), tempfile.gettempdir()):
|
||||
try:
|
||||
fname = os.path.join(logDir, 'udsactor.log')
|
||||
logging.basicConfig(
|
||||
filename=fname,
|
||||
filemode='a',
|
||||
format='%(levelname)s %(asctime)s %(message)s',
|
||||
level=logging.DEBUG
|
||||
)
|
||||
self.logger = logging.getLogger('udsactor')
|
||||
os.chmod(fname, 0o0600)
|
||||
return
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Logger can't be set
|
||||
self.logger = None
|
||||
|
||||
def log(self, level: int, message: str) -> None:
|
||||
# Debug messages are logged to a file
|
||||
# our loglevels are 0 (other), 10000 (debug), ....
|
||||
# logging levels are 10 (debug), 20 (info)
|
||||
# OTHER = logging.NOTSET
|
||||
if self.logger:
|
||||
self.logger.log(int(level / 1000), message)
|
@@ -1,196 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014-2019 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
|
||||
'''
|
||||
# pylint: disable=invalid-name
|
||||
import configparser
|
||||
import platform
|
||||
import socket
|
||||
import fcntl # Only available on Linux. Expect complains if edited from windows
|
||||
import os
|
||||
import subprocess
|
||||
import struct
|
||||
import array
|
||||
import typing
|
||||
|
||||
from .. import types
|
||||
|
||||
from .renamer import rename
|
||||
from . import xss
|
||||
|
||||
|
||||
def _getMacAddr(ifname: str) -> typing.Optional[str]:
|
||||
'''
|
||||
Returns the mac address of an interface
|
||||
Mac is returned as unicode utf-8 encoded
|
||||
'''
|
||||
ifnameBytes = ifname.encode('utf-8')
|
||||
try:
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
info = bytearray(fcntl.ioctl(s.fileno(), 0x8927, struct.pack(str('256s'), ifnameBytes[:15])))
|
||||
return str(''.join(['%02x:' % char for char in info[18:24]])[:-1]).upper()
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def _getIpAddr(ifname: str) -> typing.Optional[str]:
|
||||
'''
|
||||
Returns the ip address of an interface
|
||||
Ip is returned as unicode utf-8 encoded
|
||||
'''
|
||||
ifnameBytes = ifname.encode('utf-8')
|
||||
try:
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
return str(socket.inet_ntoa(fcntl.ioctl(
|
||||
s.fileno(),
|
||||
0x8915, # SIOCGIFADDR
|
||||
struct.pack(str('256s'), ifnameBytes[:15])
|
||||
)[20:24]))
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def _getInterfaces() -> typing.List[str]:
|
||||
'''
|
||||
Returns a list of interfaces names coded in utf-8
|
||||
'''
|
||||
max_possible = 128 # arbitrary. raise if needed.
|
||||
space = max_possible * 16
|
||||
if platform.architecture()[0] == '32bit':
|
||||
offset, length = 32, 32
|
||||
elif platform.architecture()[0] == '64bit':
|
||||
offset, length = 16, 40
|
||||
else:
|
||||
raise OSError('Unknown arquitecture {0}'.format(platform.architecture()[0]))
|
||||
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
names = array.array(str('B'), b'\0' * space)
|
||||
outbytes = struct.unpack('iL', fcntl.ioctl(
|
||||
s.fileno(),
|
||||
0x8912, # SIOCGIFCONF
|
||||
struct.pack('iL', space, names.buffer_info()[0])
|
||||
))[0]
|
||||
namestr = names.tobytes()
|
||||
# return namestr, outbytes
|
||||
return [namestr[i:i + offset].split(b'\0', 1)[0].decode('utf-8') for i in range(0, outbytes, length)]
|
||||
|
||||
|
||||
def _getIpAndMac(ifname: str) -> typing.Tuple[typing.Optional[str], typing.Optional[str]]:
|
||||
ip, mac = _getIpAddr(ifname), _getMacAddr(ifname)
|
||||
return (ip, mac)
|
||||
|
||||
def checkPermissions() -> bool:
|
||||
return os.getuid() == 0 # getuid only available on linux. Expect "complaioins" if edited from Windows
|
||||
|
||||
def getComputerName() -> str:
|
||||
'''
|
||||
Returns computer name, with no domain
|
||||
'''
|
||||
return socket.gethostname().split('.')[0]
|
||||
|
||||
def getNetworkInfo() -> typing.Iterator[types.InterfaceInfoType]:
|
||||
for ifname in _getInterfaces():
|
||||
ip, mac = _getIpAndMac(ifname)
|
||||
if mac != '00:00:00:00:00:00' and mac and ip and ip.startswith('169.254') is False: # Skips local interfaces & interfaces with no dhcp IPs
|
||||
yield types.InterfaceInfoType(name=ifname, mac=mac, ip=ip)
|
||||
|
||||
def getDomainName() -> str:
|
||||
return ''
|
||||
|
||||
def getLinuxOs() -> str:
|
||||
try:
|
||||
with open('/etc/os-release', 'r') as f:
|
||||
data = f.read()
|
||||
cfg = configparser.ConfigParser()
|
||||
cfg.read_string('[os]\n' + data)
|
||||
return cfg['os'].get('id', 'unknown').replace('"', '')
|
||||
except Exception:
|
||||
return 'unknown'
|
||||
|
||||
def reboot(flags: int = 0):
|
||||
'''
|
||||
Simple reboot using os command
|
||||
'''
|
||||
subprocess.call(['/sbin/shutdown', 'now', '-r'])
|
||||
|
||||
|
||||
def loggoff() -> None:
|
||||
'''
|
||||
Right now restarts the machine...
|
||||
'''
|
||||
subprocess.call(['/usr/bin/pkill', '-u', os.environ['USER']])
|
||||
# subprocess.call(['/sbin/shutdown', 'now', '-r'])
|
||||
# subprocess.call(['/usr/bin/systemctl', 'reboot', '-i'])
|
||||
|
||||
|
||||
def renameComputer(newName: str) -> bool:
|
||||
'''
|
||||
Changes the computer name
|
||||
Returns True if reboot needed
|
||||
'''
|
||||
rename(newName)
|
||||
return True # Always reboot right now. Not much slower but much more convenient
|
||||
|
||||
|
||||
def joinDomain(domain: str, ou: str, account: str, password: str, executeInOneStep: bool = False):
|
||||
pass
|
||||
|
||||
|
||||
def changeUserPassword(user: str, oldPassword: str, newPassword: str) -> None:
|
||||
'''
|
||||
Simple password change for user using command line
|
||||
'''
|
||||
os.system('echo "{1}\n{1}" | /usr/bin/passwd {0} 2> /dev/null'.format(user, newPassword))
|
||||
|
||||
|
||||
def initIdleDuration(atLeastSeconds: int) -> None:
|
||||
xss.initIdleDuration(atLeastSeconds)
|
||||
|
||||
|
||||
def getIdleDuration() -> float:
|
||||
return xss.getIdleDuration()
|
||||
|
||||
|
||||
def getCurrentUser() -> str:
|
||||
'''
|
||||
Returns current logged in user
|
||||
'''
|
||||
return os.environ['USER']
|
||||
|
||||
def getSessionType() -> str:
|
||||
'''
|
||||
Known values:
|
||||
* Unknown -> No XDG_SESSION_TYPE environment variable
|
||||
* xrdp --> xrdp session
|
||||
* other types
|
||||
'''
|
||||
return 'xrdp' if 'XRDP_SESSION' in os.environ else os.environ.get('XDG_SESSION_TYPE', 'unknown')
|
||||
|
||||
def forceTimeSync() -> None:
|
||||
return
|
@@ -1,35 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014-2019 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 .common import rename
|
||||
|
||||
# Import packages
|
||||
from . import debian, opensuse, redhat, alt
|
@@ -1,52 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014-2019 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
|
||||
'''
|
||||
import os
|
||||
import sys
|
||||
import pkgutil
|
||||
import typing
|
||||
|
||||
from .. import operations
|
||||
from ...log import logger
|
||||
|
||||
renamers: typing.MutableMapping[str, typing.Callable[[str], bool]] = {}
|
||||
|
||||
# Renamers now are for IPv4 only addresses
|
||||
def rename(newName: str) -> bool:
|
||||
distribution = operations.getLinuxOs().lower().strip()
|
||||
if distribution in renamers:
|
||||
logger.info('Renaming to {}'.format(newName))
|
||||
return renamers[distribution](newName)
|
||||
|
||||
# Try Debian renamer, simplest one
|
||||
logger.info('Renamer for platform "{0}" not found, trying debian renamer'.format(distribution))
|
||||
return renamers['debian'](newName)
|
||||
|
@@ -1,65 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014-2019 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
|
||||
'''
|
||||
import os
|
||||
|
||||
from .common import renamers
|
||||
from ...log import logger
|
||||
|
||||
|
||||
def rename(newName: str) -> bool:
|
||||
'''
|
||||
Debian renamer
|
||||
Expects new host name on newName
|
||||
Host does not needs to be rebooted after renaming
|
||||
'''
|
||||
with open('/etc/hostname', 'w') as hostname:
|
||||
hostname.write(newName)
|
||||
|
||||
# Force system new name
|
||||
os.system('/bin/hostname {}'.format(newName))
|
||||
os.system('/usr/bin/hostnamectl set-hostname {}'.format(newName))
|
||||
|
||||
# add name to "hosts"
|
||||
with open('/etc/hosts', 'r') as hosts:
|
||||
lines = hosts.readlines()
|
||||
with open('/etc/hosts', 'w') as hosts:
|
||||
hosts.write("127.0.1.1\t%s\n" % newName)
|
||||
for l in lines:
|
||||
if l[:9] == '127.0.1.1': # Skips existing 127.0.1.1. if it already exists
|
||||
continue
|
||||
hosts.write(l)
|
||||
|
||||
return True
|
||||
|
||||
# All names in lower case
|
||||
renamers['debian'] = rename
|
||||
renamers['ubuntu'] = rename
|
@@ -1,66 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014-2019 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
|
||||
'''
|
||||
import os
|
||||
|
||||
from .common import renamers
|
||||
from ...log import logger
|
||||
|
||||
|
||||
def rename(newName: str) -> bool:
|
||||
'''
|
||||
RH, Centos, Fedora Renamer
|
||||
Expects new host name on newName
|
||||
Host does not needs to be rebooted after renaming
|
||||
'''
|
||||
logger.debug('using SUSE renamer')
|
||||
|
||||
with open('/etc/hostname', 'w') as hostname:
|
||||
hostname.write(newName)
|
||||
|
||||
# Force system new name
|
||||
os.system('/bin/hostname {}'.format(newName))
|
||||
os.system('/usr/bin/hostnamectl set-hostname {}'.format(newName))
|
||||
|
||||
# add name to "hosts"
|
||||
with open('/etc/hosts', 'r') as hosts:
|
||||
lines = hosts.readlines()
|
||||
with open('/etc/hosts', 'w') as hosts:
|
||||
hosts.write("127.0.1.1\t{}\n".format(newName))
|
||||
for l in lines:
|
||||
if l[:9] != '127.0.1.1': # Skips existing 127.0.1.1. if it already exists
|
||||
hosts.write(l)
|
||||
|
||||
return True
|
||||
|
||||
# All names in lower case
|
||||
renamers['opensuse'] = rename
|
||||
renamers['suse'] = rename
|
||||
renamers['opensuse-leap'] = rename
|
@@ -1,74 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014-2019 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
|
||||
'''
|
||||
import os
|
||||
|
||||
from .common import renamers
|
||||
from ...log import logger
|
||||
|
||||
|
||||
def rename(newName: str) -> bool:
|
||||
'''
|
||||
RH, Centos, Fedora Renamer
|
||||
Expects new host name on newName
|
||||
Host does not needs to be rebooted after renaming
|
||||
'''
|
||||
logger.debug('using RH renamer')
|
||||
|
||||
with open('/etc/hostname', 'w') as hostname:
|
||||
hostname.write(newName)
|
||||
|
||||
# Force system new name
|
||||
os.system('/bin/hostname {}'.format(newName))
|
||||
os.system('/usr/bin/hostnamectl set-hostname {}'.format(newName))
|
||||
|
||||
# add name to "hosts"
|
||||
with open('/etc/hosts', 'r') as hosts:
|
||||
lines = hosts.readlines()
|
||||
with open('/etc/hosts', 'w') as hosts:
|
||||
hosts.write("127.0.1.1\t{}\n".format(newName))
|
||||
for l in lines:
|
||||
if l[:9] != '127.0.1.1': # Skips existing 127.0.1.1. if it already exists
|
||||
hosts.write(l)
|
||||
|
||||
with open('/etc/sysconfig/network', 'r') as net:
|
||||
lines = net.readlines()
|
||||
with open('/etc/sysconfig/network', 'w') as net:
|
||||
net.write('HOSTNAME={}\n'.format(newName))
|
||||
for l in lines:
|
||||
if l[:8] != 'HOSTNAME':
|
||||
net.write(l)
|
||||
|
||||
return True
|
||||
|
||||
# All names in lower case
|
||||
renamers['centos linux'] = rename
|
||||
renamers['centos'] = rename
|
||||
renamers['fedora'] = rename
|
@@ -1,76 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014-2020 Virtual Cable S.L.U.
|
||||
# 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
|
||||
'''
|
||||
import sys
|
||||
|
||||
from .. import rest
|
||||
from .. import platform
|
||||
from ..log import logger
|
||||
from .service import UDSActorSvc
|
||||
|
||||
def usage():
|
||||
sys.stderr.write('usage: udsactor start|stop|restart|login "username"|logout "username"\n')
|
||||
sys.exit(2)
|
||||
|
||||
def run() -> None:
|
||||
logger.setLevel(20000)
|
||||
|
||||
if len(sys.argv) == 3 and sys.argv[1] in ('login', 'logout'):
|
||||
logger.debug('Running client udsactor')
|
||||
try:
|
||||
client: rest.UDSClientApi = rest.UDSClientApi()
|
||||
if sys.argv[1] == 'login':
|
||||
r = client.login(sys.argv[2], platform.operations.getSessionType())
|
||||
print('{},{},{},{}\n'.format(r.ip, r.hostname, r.max_idle, r.dead_line or ''))
|
||||
elif sys.argv[1] == 'logout':
|
||||
client.logout(sys.argv[2])
|
||||
except Exception as e:
|
||||
logger.exception()
|
||||
logger.error('Got exception while processing command: %s', e)
|
||||
sys.exit(0)
|
||||
elif len(sys.argv) != 2:
|
||||
usage()
|
||||
|
||||
daemonSvr = UDSActorSvc()
|
||||
if len(sys.argv) == 2:
|
||||
# Daemon mode...
|
||||
if sys.argv[1] == 'start':
|
||||
daemonSvr.start()
|
||||
elif sys.argv[1] == 'stop':
|
||||
daemonSvr.stop()
|
||||
elif sys.argv[1] == 'restart':
|
||||
daemonSvr.restart()
|
||||
elif sys.argv[1] == 'start-foreground':
|
||||
daemonSvr.run() # Execute in foreground
|
||||
else:
|
||||
usage()
|
||||
sys.exit(0)
|
||||
else:
|
||||
usage()
|
@@ -1,106 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014-2019 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
|
||||
'''
|
||||
import signal
|
||||
|
||||
from . import daemon
|
||||
|
||||
from ..log import logger
|
||||
from ..service import CommonService
|
||||
|
||||
try:
|
||||
from prctl import set_proctitle # @UnresolvedImport
|
||||
except ImportError: # 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.Daemon, CommonService):
|
||||
def __init__(self) -> None:
|
||||
daemon.Daemon.__init__(self, '/run/udsactor.pid')
|
||||
CommonService.__init__(self)
|
||||
|
||||
# Captures signals so we can stop gracefully
|
||||
signal.signal(signal.SIGINT, self.markForExit)
|
||||
signal.signal(signal.SIGTERM, self.markForExit)
|
||||
|
||||
def markForExit(self, signum, frame) -> None: # pylint: disable=unused-argument
|
||||
self._isAlive = False
|
||||
|
||||
def joinDomain( # pylint: disable=unused-argument, too-many-arguments
|
||||
self,
|
||||
name: str,
|
||||
domain: str,
|
||||
ou: str,
|
||||
account: str,
|
||||
password: str
|
||||
) -> None:
|
||||
logger.info('Join domain is not supported on linux platforms right now. Just renaming.')
|
||||
self.rename(name)
|
||||
|
||||
def run(self) -> None:
|
||||
logger.debug('Running Daemon: {}'.format(self._isAlive))
|
||||
set_proctitle('UDSActorDaemon')
|
||||
|
||||
# Linux daemon will continue running unless something is requested to
|
||||
# Unmanaged services does not initializes "on start", but rather when user logs in (because userservice does not exists "as such" before that)
|
||||
if self.isManaged():
|
||||
if not self.initialize():
|
||||
self.finish()
|
||||
return # Stop daemon if initializes told to do so
|
||||
|
||||
# logger.debug('Initialized, setting ready')
|
||||
# Initialization is done, set machine to ready for UDS, communicate urls, etc...
|
||||
self.setReady()
|
||||
else:
|
||||
if not self.initializeUnmanaged():
|
||||
self.finish()
|
||||
return
|
||||
|
||||
# Start listening for petitions
|
||||
self.startHttpServer()
|
||||
|
||||
# *********************
|
||||
# * Main Service loop *
|
||||
# *********************
|
||||
# Counter used to check ip changes only once every 10 seconds, for
|
||||
# example
|
||||
counter = 0
|
||||
while self._isAlive:
|
||||
counter += 1
|
||||
try:
|
||||
if counter % 5 == 0:
|
||||
self.loop()
|
||||
except Exception as e:
|
||||
logger.error('Got exception on main loop: %s', e)
|
||||
# In milliseconds, will break
|
||||
self.doWait(1000)
|
||||
|
||||
self.finish()
|
@@ -1,107 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014-2019 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
|
||||
'''
|
||||
# pylint: disable=invalid-name
|
||||
import os
|
||||
import configparser
|
||||
import base64
|
||||
import pickle
|
||||
|
||||
from .. import types
|
||||
|
||||
CONFIGFILE = '/etc/udsactor/udsactor.cfg'
|
||||
|
||||
def readConfig() -> types.ActorConfigurationType:
|
||||
try:
|
||||
cfg = configparser.ConfigParser()
|
||||
cfg.read(CONFIGFILE)
|
||||
uds: configparser.SectionProxy = cfg['uds']
|
||||
# Extract data:
|
||||
base64Config = uds.get('config', None)
|
||||
config = pickle.loads(base64.b64decode(base64Config.encode())) if base64Config else None
|
||||
|
||||
base64Data = uds.get('data', None)
|
||||
data = pickle.loads(base64.b64decode(base64Data.encode())) if base64Data else None
|
||||
|
||||
return types.ActorConfigurationType(
|
||||
actorType=uds.get('type', types.MANAGED),
|
||||
host=uds.get('host', ''),
|
||||
validateCertificate=uds.getboolean('validate', fallback=False),
|
||||
master_token=uds.get('master_token', None),
|
||||
own_token=uds.get('own_token', None),
|
||||
restrict_net=uds.get('restrict_net', None),
|
||||
pre_command=uds.get('pre_command', None),
|
||||
runonce_command=uds.get('runonce_command', None),
|
||||
post_command=uds.get('post_command', None),
|
||||
log_level=int(uds.get('log_level', '2')),
|
||||
config=config,
|
||||
data=data
|
||||
)
|
||||
except Exception:
|
||||
return types.ActorConfigurationType('', False)
|
||||
|
||||
def writeConfig(config: types.ActorConfigurationType) -> None:
|
||||
cfg = configparser.ConfigParser()
|
||||
cfg.add_section('uds')
|
||||
uds: configparser.SectionProxy = cfg['uds']
|
||||
uds['host'] = config.host
|
||||
uds['validate'] = 'yes' if config.validateCertificate else 'no'
|
||||
def writeIfValue(val, name):
|
||||
if val:
|
||||
uds[name] = val
|
||||
writeIfValue(config.actorType, 'type')
|
||||
writeIfValue(config.master_token, 'master_token')
|
||||
writeIfValue(config.own_token, 'own_token')
|
||||
writeIfValue(config.restrict_net, 'restrict_net')
|
||||
writeIfValue(config.pre_command, 'pre_command')
|
||||
writeIfValue(config.post_command, 'post_command')
|
||||
writeIfValue(config.runonce_command, 'runonce_command')
|
||||
uds['log_level'] = str(config.log_level)
|
||||
if config.config: # Special case, encoded & dumped
|
||||
uds['config'] = base64.b64encode(pickle.dumps(config.config)).decode()
|
||||
|
||||
if config.data: # Special case, encoded & dumped
|
||||
uds['data'] = base64.b64encode(pickle.dumps(config.data)).decode()
|
||||
|
||||
# Ensures exists destination folder
|
||||
dirname = os.path.dirname(CONFIGFILE)
|
||||
if not os.path.exists(dirname):
|
||||
os.mkdir(dirname, mode=0o700) # Will create only if route to path already exists, for example, /etc (that must... :-))
|
||||
|
||||
with open(CONFIGFILE, 'w') as f:
|
||||
cfg.write(f)
|
||||
|
||||
os.chmod(CONFIGFILE, 0o0600) # Ensure only readable by root
|
||||
|
||||
def useOldJoinSystem() -> bool:
|
||||
return False
|
||||
|
||||
def invokeScriptOnLogin() -> str:
|
||||
return ''
|
@@ -1,128 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014-2019 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
|
||||
'''
|
||||
# pylint: disable=invalid-name
|
||||
import ctypes
|
||||
import ctypes.util
|
||||
import subprocess
|
||||
|
||||
xlib = None
|
||||
xss = None
|
||||
display = None
|
||||
xssInfo = None
|
||||
initialized = False
|
||||
|
||||
class XScreenSaverInfo(ctypes.Structure): # pylint: disable=too-few-public-methods
|
||||
_fields_ = [('window', ctypes.c_long),
|
||||
('state', ctypes.c_int),
|
||||
('kind', ctypes.c_int),
|
||||
('til_or_since', ctypes.c_ulong),
|
||||
('idle', ctypes.c_ulong),
|
||||
('eventMask', ctypes.c_ulong)]
|
||||
|
||||
class c_ptr(ctypes.c_void_p):
|
||||
pass
|
||||
|
||||
def _ensureInitialized():
|
||||
global xlib, xss, xssInfo, display, initialized # pylint: disable=global-statement
|
||||
|
||||
if initialized:
|
||||
return
|
||||
|
||||
initialized = True
|
||||
|
||||
try:
|
||||
xlibPath = ctypes.util.find_library('X11')
|
||||
xssPath = ctypes.util.find_library('Xss')
|
||||
xlib = xss = None
|
||||
if not xlibPath or not xssPath:
|
||||
raise Exception('Library Not found!!')
|
||||
|
||||
xlib = ctypes.cdll.LoadLibrary(xlibPath)
|
||||
xss = ctypes.cdll.LoadLibrary(xssPath)
|
||||
|
||||
# Fix result type to XScreenSaverInfo Structure
|
||||
xss.XScreenSaverQueryExtension.restype = ctypes.c_int
|
||||
xss.XScreenSaverQueryExtension.argtypes = [
|
||||
ctypes.c_void_p,
|
||||
ctypes.POINTER(ctypes.c_int),
|
||||
ctypes.POINTER(ctypes.c_int)
|
||||
]
|
||||
xss.XScreenSaverAllocInfo.restype = ctypes.POINTER(XScreenSaverInfo) # Result in a XScreenSaverInfo structure
|
||||
xss.XScreenSaverQueryInfo.argtypes = [
|
||||
ctypes.c_void_p,
|
||||
ctypes.c_void_p,
|
||||
ctypes.POINTER(XScreenSaverInfo)
|
||||
]
|
||||
xlib.XOpenDisplay.argtypes = [ctypes.c_char_p]
|
||||
xlib.XOpenDisplay.restype = c_ptr
|
||||
|
||||
display = xlib.XOpenDisplay(None)
|
||||
|
||||
if not display.value:
|
||||
raise Exception('Display not found!') # Invalid display, not accesible
|
||||
|
||||
xssInfo = xss.XScreenSaverAllocInfo()
|
||||
|
||||
# Ensures screen saver extension is available
|
||||
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:
|
||||
raise Exception('ScreenSaver not available')
|
||||
|
||||
except Exception: # Libraries not accesible, not found or whatever..
|
||||
xlib = xss = display = xssInfo = None
|
||||
|
||||
|
||||
def initIdleDuration(atLeastSeconds: int) -> None:
|
||||
_ensureInitialized()
|
||||
if atLeastSeconds:
|
||||
subprocess.call(['/usr/bin/xset', 's', '{}'.format(atLeastSeconds + 30)])
|
||||
# And now reset it
|
||||
subprocess.call(['/usr/bin/xset', 's', 'reset'])
|
||||
|
||||
|
||||
def getIdleDuration() -> float:
|
||||
'''
|
||||
Returns idle duration, in seconds
|
||||
'''
|
||||
if not initialized or not xlib or not xss or not xssInfo:
|
||||
return 0 # Libraries not available
|
||||
|
||||
xss.XScreenSaverQueryInfo(display, xlib.XDefaultRootWindow(display), xssInfo)
|
||||
|
||||
# States: 0 = off, 1 = On, 2 = Cycle, 3 = Disabled, ...?
|
||||
if xssInfo.contents.state == 1: # state = 1 means "active", so idle is not a valid state
|
||||
return 3600 * 100 * 1000 # If screen saver is active, return a high enough value
|
||||
|
||||
return xssInfo.contents.idle / 1000.0
|
@@ -1,117 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014-2019 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
|
||||
'''
|
||||
# pylint: disable=invalid-name
|
||||
import traceback
|
||||
import sys
|
||||
import typing
|
||||
|
||||
if sys.platform == 'win32':
|
||||
from .windows.log import LocalLogger
|
||||
else:
|
||||
from .linux.log import LocalLogger
|
||||
|
||||
# Not imported at runtime, just for type checking
|
||||
if typing.TYPE_CHECKING:
|
||||
from . import rest
|
||||
|
||||
# Valid logging levels, from UDS Broker (uds.core.utils.log)
|
||||
from .loglevel import OTHER, DEBUG, INFO, WARN, ERROR, FATAL
|
||||
|
||||
class Logger:
|
||||
remoteLogger: typing.Optional['rest.UDSServerApi']
|
||||
own_token: str
|
||||
logLevel: int
|
||||
localLogger: LocalLogger
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.logLevel = INFO
|
||||
self.localLogger = LocalLogger()
|
||||
self.remoteLogger = None
|
||||
self.own_token = ''
|
||||
|
||||
def setLevel(self, level: typing.Union[str, int]) -> None:
|
||||
'''
|
||||
Sets log level filter (minimum level required for a log message to be processed)
|
||||
:param level: Any message with a level below this will be filtered out
|
||||
'''
|
||||
self.logLevel = int(level) # Ensures level is an integer or fails
|
||||
|
||||
def setRemoteLogger(self, remoteLogger: 'rest.UDSServerApi', own_token: str) -> None:
|
||||
self.remoteLogger = remoteLogger
|
||||
self.own_token = own_token
|
||||
|
||||
def enableServiceLogger(self):
|
||||
if self.localLogger.windows:
|
||||
self.localLogger.serviceLogger = True
|
||||
|
||||
def log(self, level: typing.Union[str, int], message: str, *args) -> None:
|
||||
level = int(level)
|
||||
if level < self.logLevel: # Skip not wanted messages
|
||||
return
|
||||
|
||||
msg = message % args
|
||||
# If remote logger is available, notify message to it (except DEBUG messages OFC)
|
||||
try:
|
||||
if self.remoteLogger and level >= DEBUG:
|
||||
self.remoteLogger.log(self.own_token, level, msg)
|
||||
except Exception as e:
|
||||
self.localLogger.log(DEBUG, 'Log to broker: {}'.format(e))
|
||||
|
||||
self.localLogger.log(level, msg)
|
||||
|
||||
def debug(self, message: str, *args) -> None:
|
||||
self.log(DEBUG, message, *args)
|
||||
|
||||
def warn(self, message: str, *args) -> None:
|
||||
self.log(WARN, message, *args)
|
||||
|
||||
def info(self, message: str, *args) -> None:
|
||||
self.log(INFO, message, *args)
|
||||
|
||||
def error(self, message: str, *args) -> None:
|
||||
self.log(ERROR, message, *args)
|
||||
|
||||
def fatal(self, message: str, *args) -> None:
|
||||
self.log(FATAL, message, *args)
|
||||
|
||||
def exception(self) -> None:
|
||||
try:
|
||||
tb = traceback.format_exc()
|
||||
except Exception:
|
||||
tb = '(could not get traceback!)'
|
||||
|
||||
self.log(DEBUG, tb)
|
||||
|
||||
def flush(self) -> None:
|
||||
pass
|
||||
|
||||
|
||||
logger = Logger()
|
@@ -1,32 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2020 Virtual Cable S.L.U.
|
||||
# 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
|
||||
'''
|
||||
|
||||
OTHER, DEBUG, INFO, WARN, ERROR, FATAL = (10000 * x for x in range(6))
|
@@ -1,37 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014 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
|
||||
'''
|
||||
import sys
|
||||
|
||||
name = sys.platform
|
||||
if sys.platform == 'win32':
|
||||
from .windows import operations, store # pylint: disable=unused-import
|
||||
else:
|
||||
from .linux import operations, store # pylint: disable=unused-import
|
@@ -1,431 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2019-2021 Virtual Cable S.L.U.
|
||||
# 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.U. 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
|
||||
'''
|
||||
# pylint: disable=invalid-name
|
||||
import warnings
|
||||
import json
|
||||
import logging
|
||||
import typing
|
||||
|
||||
import requests
|
||||
|
||||
from . import types
|
||||
from .version import VERSION
|
||||
|
||||
# Default public listen port
|
||||
LISTEN_PORT = 43910
|
||||
|
||||
# Default timeout
|
||||
TIMEOUT = 5 # 5 seconds is more than enought
|
||||
|
||||
# Constants
|
||||
UNKNOWN = 'unknown'
|
||||
|
||||
|
||||
class RESTError(Exception):
|
||||
ERRCODE = 0
|
||||
|
||||
|
||||
class RESTConnectionError(RESTError):
|
||||
ERRCODE = -1
|
||||
|
||||
|
||||
# Errors ""raised"" from broker
|
||||
class RESTInvalidKeyError(RESTError):
|
||||
ERRCODE = 1
|
||||
|
||||
|
||||
class RESTUnmanagedHostError(RESTError):
|
||||
ERRCODE = 2
|
||||
|
||||
|
||||
class RESTUserServiceNotFoundError(RESTError):
|
||||
ERRCODE = 3
|
||||
|
||||
|
||||
class RESTOsManagerError(RESTError):
|
||||
ERRCODE = 4
|
||||
|
||||
|
||||
# For avoid proxy on localhost connections
|
||||
NO_PROXY = {
|
||||
'http': None,
|
||||
'https': None,
|
||||
}
|
||||
|
||||
UDS_BASE_URL = 'https://{}/uds/rest/'
|
||||
|
||||
#
|
||||
# Basic UDS Api
|
||||
#
|
||||
class UDSApi: # pylint: disable=too-few-public-methods
|
||||
"""
|
||||
Base for remote api accesses
|
||||
"""
|
||||
|
||||
_host: str
|
||||
_validateCert: bool
|
||||
_url: str
|
||||
|
||||
def __init__(self, host: str, validateCert: bool) -> None:
|
||||
self._host = host
|
||||
self._validateCert = validateCert
|
||||
self._url = UDS_BASE_URL.format(self._host)
|
||||
# Disable logging requests messages except for errors, ...
|
||||
logging.getLogger('request').setLevel(logging.CRITICAL)
|
||||
logging.getLogger('urllib3').setLevel(logging.ERROR)
|
||||
try:
|
||||
warnings.simplefilter('ignore') # Disables all warnings
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@property
|
||||
def _headers(self) -> typing.MutableMapping[str, str]:
|
||||
return {
|
||||
'Content-Type': 'application/json',
|
||||
'User-Agent': 'UDS Actor v{}'.format(VERSION),
|
||||
}
|
||||
|
||||
def _apiURL(self, method: str) -> str:
|
||||
raise NotImplementedError
|
||||
|
||||
def _doPost(
|
||||
self,
|
||||
method: str, # i.e. 'initialize', 'ready', ....
|
||||
payLoad: typing.MutableMapping[str, typing.Any],
|
||||
headers: typing.Optional[typing.MutableMapping[str, str]] = None,
|
||||
disableProxy: bool = False,
|
||||
) -> typing.Any:
|
||||
headers = headers or self._headers
|
||||
try:
|
||||
result = requests.post(
|
||||
self._apiURL(method),
|
||||
data=json.dumps(payLoad),
|
||||
headers=headers,
|
||||
verify=self._validateCert,
|
||||
timeout=TIMEOUT,
|
||||
proxies=NO_PROXY # type: ignore
|
||||
if disableProxy
|
||||
else None, # if not proxies wanted, enforce it
|
||||
)
|
||||
|
||||
if result.ok:
|
||||
j = result.json()
|
||||
if not j.get('error', None):
|
||||
return j['result']
|
||||
except requests.ConnectionError as e:
|
||||
raise RESTConnectionError(str(e))
|
||||
except Exception as e:
|
||||
raise RESTError(str(e))
|
||||
|
||||
try:
|
||||
data = result.json()
|
||||
except Exception:
|
||||
data = result.content.decode()
|
||||
|
||||
raise RESTError(data)
|
||||
|
||||
|
||||
#
|
||||
# UDS Broker API access
|
||||
#
|
||||
class UDSServerApi(UDSApi):
|
||||
def _apiURL(self, method: str) -> str:
|
||||
return self._url + 'actor/v3/' + method
|
||||
|
||||
def enumerateAuthenticators(self) -> typing.Iterable[types.AuthenticatorType]:
|
||||
try:
|
||||
result = requests.get(
|
||||
self._url + 'auth/auths',
|
||||
headers=self._headers,
|
||||
verify=self._validateCert,
|
||||
timeout=4,
|
||||
)
|
||||
if result.ok:
|
||||
for v in sorted(result.json(), key=lambda x: x['priority']):
|
||||
yield types.AuthenticatorType(
|
||||
authId=v['authId'],
|
||||
authSmallName=v['authSmallName'],
|
||||
auth=v['auth'],
|
||||
type=v['type'],
|
||||
priority=v['priority'],
|
||||
isCustom=v['isCustom'],
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def register( # pylint: disable=too-many-arguments, too-many-locals
|
||||
self,
|
||||
auth: str,
|
||||
username: str,
|
||||
password: str,
|
||||
hostname: str,
|
||||
ip: str,
|
||||
mac: str,
|
||||
preCommand: str,
|
||||
runOnceCommand: str,
|
||||
postCommand: str,
|
||||
logLevel: int,
|
||||
) -> str:
|
||||
"""
|
||||
Raises an exception if could not register, or registers and returns the "authorization token"
|
||||
"""
|
||||
data = {
|
||||
'username': username + '@' + auth,
|
||||
'hostname': hostname,
|
||||
'ip': ip,
|
||||
'mac': mac,
|
||||
'pre_command': preCommand,
|
||||
'run_once_command': runOnceCommand,
|
||||
'post_command': postCommand,
|
||||
'log_level': logLevel,
|
||||
}
|
||||
|
||||
# First, try to login to REST api
|
||||
try:
|
||||
# First, try to login
|
||||
authInfo = {'auth': auth, 'username': username, 'password': password}
|
||||
headers = self._headers
|
||||
result = requests.post(
|
||||
self._url + 'auth/login',
|
||||
data=json.dumps(authInfo),
|
||||
headers=headers,
|
||||
verify=self._validateCert,
|
||||
)
|
||||
if not result.ok or result.json()['result'] == 'error':
|
||||
raise Exception() # Invalid credentials
|
||||
|
||||
headers['X-Auth-Token'] = result.json()['token']
|
||||
|
||||
result = requests.post(
|
||||
self._apiURL('register'),
|
||||
data=json.dumps(data),
|
||||
headers=headers,
|
||||
verify=self._validateCert,
|
||||
)
|
||||
if result.ok:
|
||||
return result.json()['result']
|
||||
except requests.ConnectionError as e:
|
||||
raise RESTConnectionError(e)
|
||||
except RESTError:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise RESTError('Invalid credentials')
|
||||
|
||||
raise RESTError(result.content.decode())
|
||||
|
||||
def initialize(
|
||||
self,
|
||||
token: str,
|
||||
interfaces: typing.Iterable[types.InterfaceInfoType],
|
||||
actor_type: typing.Optional[str],
|
||||
) -> types.InitializationResultType:
|
||||
# Generate id list from netork cards
|
||||
payload = {
|
||||
'type': actor_type or types.MANAGED,
|
||||
'token': token,
|
||||
'version': VERSION,
|
||||
'id': [{'mac': i.mac, 'ip': i.ip} for i in interfaces],
|
||||
}
|
||||
r = self._doPost('initialize', payload)
|
||||
os = r['os']
|
||||
return types.InitializationResultType(
|
||||
own_token=r['own_token'],
|
||||
unique_id=r['unique_id'].lower() if r['unique_id'] else None,
|
||||
os=types.ActorOsConfigurationType(
|
||||
action=os['action'],
|
||||
name=os['name'],
|
||||
username=os.get('username'),
|
||||
password=os.get('password'),
|
||||
new_password=os.get('new_password'),
|
||||
ad=os.get('ad'),
|
||||
ou=os.get('ou'),
|
||||
)
|
||||
if r['os']
|
||||
else None,
|
||||
)
|
||||
|
||||
def ready(
|
||||
self, own_token: str, secret: str, ip: str, port: int
|
||||
) -> types.CertificateInfoType:
|
||||
payload = {'token': own_token, 'secret': secret, 'ip': ip, 'port': port}
|
||||
result = self._doPost('ready', payload)
|
||||
|
||||
return types.CertificateInfoType(
|
||||
private_key=result['private_key'],
|
||||
server_certificate=result['server_certificate'],
|
||||
password=result['password'],
|
||||
)
|
||||
|
||||
def notifyIpChange(
|
||||
self, own_token: str, secret: str, ip: str, port: int
|
||||
) -> types.CertificateInfoType:
|
||||
payload = {'token': own_token, 'secret': secret, 'ip': ip, 'port': port}
|
||||
result = self._doPost('ipchange', payload)
|
||||
|
||||
return types.CertificateInfoType(
|
||||
private_key=result['private_key'],
|
||||
server_certificate=result['server_certificate'],
|
||||
password=result['password'],
|
||||
)
|
||||
|
||||
def notifyUnmanagedCallback(
|
||||
self,
|
||||
master_token: str,
|
||||
secret: str,
|
||||
interfaces: typing.Iterable[types.InterfaceInfoType],
|
||||
port: int,
|
||||
) -> types.CertificateInfoType:
|
||||
payload = {
|
||||
'id': [{'mac': i.mac, 'ip': i.ip} for i in interfaces],
|
||||
'token': master_token,
|
||||
'secret': secret,
|
||||
'port': port,
|
||||
}
|
||||
result = self._doPost('unmanaged', payload)
|
||||
|
||||
return types.CertificateInfoType(
|
||||
private_key=result['private_key'],
|
||||
server_certificate=result['server_certificate'],
|
||||
password=result['password'],
|
||||
)
|
||||
|
||||
def login(
|
||||
self,
|
||||
actor_type: typing.Optional[str],
|
||||
token: str,
|
||||
username: str,
|
||||
sessionType: str,
|
||||
interfaces: typing.Iterable[types.InterfaceInfoType],
|
||||
secret: typing.Optional[str],
|
||||
) -> types.LoginResultInfoType:
|
||||
if not token:
|
||||
return types.LoginResultInfoType(
|
||||
ip='0.0.0.0', hostname=UNKNOWN, dead_line=None, max_idle=None
|
||||
)
|
||||
payload = {
|
||||
'type': actor_type or types.MANAGED,
|
||||
'id': [{'mac': i.mac, 'ip': i.ip} for i in interfaces],
|
||||
'token': token,
|
||||
'username': username,
|
||||
'session_type': sessionType,
|
||||
'secret': secret or '',
|
||||
}
|
||||
result = self._doPost('login', payload)
|
||||
return types.LoginResultInfoType(
|
||||
ip=result['ip'],
|
||||
hostname=result['hostname'],
|
||||
dead_line=result['dead_line'],
|
||||
max_idle=result['max_idle'],
|
||||
)
|
||||
|
||||
def logout(
|
||||
self,
|
||||
actor_type: typing.Optional[str],
|
||||
token: str,
|
||||
username: str,
|
||||
sessionType: str,
|
||||
interfaces: typing.Iterable[types.InterfaceInfoType],
|
||||
secret: typing.Optional[str],
|
||||
) -> typing.Optional[str]:
|
||||
if not token:
|
||||
return None
|
||||
payload = {
|
||||
'type': actor_type or types.MANAGED,
|
||||
'id': [{'mac': i.mac, 'ip': i.ip} for i in interfaces],
|
||||
'token': token,
|
||||
'username': username,
|
||||
'session_type': sessionType,
|
||||
'secret': secret or '',
|
||||
}
|
||||
return self._doPost('logout', payload) # Can be 'ok' or 'notified'
|
||||
|
||||
def log(self, own_token: str, level: int, message: str) -> None:
|
||||
if not own_token:
|
||||
return
|
||||
payLoad = {'token': own_token, 'level': level, 'message': message}
|
||||
self._doPost('log', payLoad) # Ignores result...
|
||||
|
||||
def test(self, master_token: str, actorType: typing.Optional[str]) -> bool:
|
||||
payLoad = {
|
||||
'type': actorType or types.MANAGED,
|
||||
'token': master_token,
|
||||
}
|
||||
return self._doPost('test', payLoad) == 'ok'
|
||||
|
||||
|
||||
class UDSClientApi(UDSApi):
|
||||
def __init__(self) -> None:
|
||||
super().__init__('127.0.0.1:{}'.format(LISTEN_PORT), False)
|
||||
# Override base url
|
||||
self._url = "https://{}/ui/".format(self._host)
|
||||
|
||||
def _apiURL(self, method: str) -> str:
|
||||
return self._url + method
|
||||
|
||||
def post(
|
||||
self,
|
||||
method: str, # i.e. 'initialize', 'ready', ....
|
||||
payLoad: typing.MutableMapping[str, typing.Any],
|
||||
) -> typing.Any:
|
||||
return self._doPost(method=method, payLoad=payLoad, disableProxy=True)
|
||||
|
||||
def register(self, callbackUrl: str) -> None:
|
||||
payLoad = {'callback_url': callbackUrl}
|
||||
self.post('register', payLoad)
|
||||
|
||||
def unregister(self, callbackUrl: str) -> None:
|
||||
payLoad = {'callback_url': callbackUrl}
|
||||
self.post('unregister', payLoad)
|
||||
|
||||
def login(
|
||||
self, username: str, sessionType: typing.Optional[str] = None
|
||||
) -> types.LoginResultInfoType:
|
||||
payLoad = {
|
||||
'username': username,
|
||||
'session_type': sessionType or UNKNOWN,
|
||||
}
|
||||
result = self.post('login', payLoad)
|
||||
return types.LoginResultInfoType(
|
||||
ip=result['ip'],
|
||||
hostname=result['hostname'],
|
||||
dead_line=result['dead_line'],
|
||||
max_idle=result['max_idle'],
|
||||
)
|
||||
|
||||
def logout(self, username: str, sessionType: typing.Optional[str]) -> None:
|
||||
payLoad = {
|
||||
'username': username,
|
||||
'session_type': sessionType or UNKNOWN
|
||||
}
|
||||
self.post('logout', payLoad)
|
||||
|
||||
def ping(self) -> bool:
|
||||
return self.post('ping', {}) == 'pong'
|
@@ -1,599 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014-2019 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
|
||||
'''
|
||||
# pylint: disable=invalid-name
|
||||
|
||||
import socket
|
||||
import time
|
||||
import secrets
|
||||
import subprocess
|
||||
import typing
|
||||
|
||||
from . import platform
|
||||
from . import rest
|
||||
from . import types
|
||||
from . import tools
|
||||
|
||||
from .log import logger, DEBUG, INFO, ERROR, FATAL
|
||||
from .http import clients_pool, server, cert
|
||||
|
||||
# def setup() -> None:
|
||||
# cfg = platform.store.readConfig()
|
||||
|
||||
# if logger.logger.windows:
|
||||
# # Logs will also go to windows event log for services
|
||||
# logger.logger.serviceLogger = True
|
||||
|
||||
# if cfg.x:
|
||||
# logger.setLevel(cfg.get('logLevel', 20000))
|
||||
# else:
|
||||
# logger.setLevel(20000)
|
||||
|
||||
|
||||
class CommonService: # pylint: disable=too-many-instance-attributes
|
||||
_isAlive: bool = True
|
||||
_rebootRequested: bool = False
|
||||
_loggedIn: bool = False
|
||||
_initialized: bool = False
|
||||
|
||||
_cfg: types.ActorConfigurationType
|
||||
_api: rest.UDSServerApi
|
||||
_interfaces: typing.List[types.InterfaceInfoType]
|
||||
_secret: str
|
||||
_certificate: types.CertificateInfoType
|
||||
_clientsPool: clients_pool.UDSActorClientPool
|
||||
_http: typing.Optional[server.HTTPServerThread]
|
||||
|
||||
@staticmethod
|
||||
def execute(cmdLine: str, section: str) -> bool:
|
||||
try:
|
||||
logger.debug('Executing command on {}: {}'.format(section, cmdLine))
|
||||
res = subprocess.check_call(cmdLine, shell=True)
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
'Got exception executing: {} - {} - {}'.format(section, cmdLine, e)
|
||||
)
|
||||
return False
|
||||
logger.debug('Result of executing cmd for {} was {}'.format(section, res))
|
||||
return True
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._cfg = platform.store.readConfig()
|
||||
self._interfaces = []
|
||||
self._api = rest.UDSServerApi(self._cfg.host, self._cfg.validateCertificate)
|
||||
self._secret = secrets.token_urlsafe(33)
|
||||
self._clientsPool = clients_pool.UDSActorClientPool()
|
||||
self._certificate = (
|
||||
cert.defaultCertificate
|
||||
) # For being used on "unmanaged" hosts only
|
||||
self._http = None
|
||||
|
||||
# Initialzies loglevel and serviceLogger
|
||||
# 0 = DEBUG, 1 = INFO, 2 = ERROR, 3 = FATAL in combobox
|
||||
# BUT!!!:
|
||||
# 0 = OTHER, 10000 = DEBUG, 20000 = WARN, 30000 = INFO, 40000 = ERROR, 50000 = FATAL
|
||||
# So this comes:
|
||||
logger.setLevel([DEBUG, INFO, ERROR, FATAL][self._cfg.log_level])
|
||||
# If windows, enable service logger
|
||||
logger.enableServiceLogger()
|
||||
|
||||
socket.setdefaulttimeout(20)
|
||||
|
||||
def startHttpServer(self):
|
||||
# Starts the http thread
|
||||
if self._http:
|
||||
try:
|
||||
self._http.stop()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self._http = server.HTTPServerThread(self)
|
||||
self._http.start()
|
||||
|
||||
def isManaged(self) -> bool:
|
||||
return (
|
||||
self._cfg.actorType != types.UNMANAGED
|
||||
) # Only "unmanaged" hosts are unmanaged, the rest are "managed"
|
||||
|
||||
def serviceInterfaceInfo(
|
||||
self, interfaces: typing.Optional[typing.List[types.InterfaceInfoType]] = None
|
||||
) -> typing.Optional[types.InterfaceInfoType]:
|
||||
"""
|
||||
returns the inteface with unique_id mac or first interface or None if no interfaces...
|
||||
"""
|
||||
interfaces = (
|
||||
interfaces or self._interfaces
|
||||
) # Emty interfaces is like "no ip change" because cannot be notified
|
||||
if self._cfg.config and interfaces:
|
||||
try:
|
||||
return next(
|
||||
x for x in interfaces if x.mac.lower() == self._cfg.config.unique_id
|
||||
)
|
||||
except StopIteration:
|
||||
return interfaces[0]
|
||||
|
||||
return None
|
||||
|
||||
def reboot(self) -> None:
|
||||
# Reboot just after renaming
|
||||
logger.info('Rebooting...')
|
||||
self._rebootRequested = True
|
||||
|
||||
def setReady(self) -> None:
|
||||
if not self._isAlive or not self.isManaged():
|
||||
return
|
||||
# Unamanged actor types does not set ready never (has no osmanagers, no needing for this)
|
||||
|
||||
# First, if postconfig is available, execute it and disable it
|
||||
if self._cfg.post_command:
|
||||
self.execute(self._cfg.post_command, 'postConfig')
|
||||
self._cfg = self._cfg._replace(post_command=None)
|
||||
platform.store.writeConfig(self._cfg)
|
||||
|
||||
if self._cfg.own_token and self._interfaces:
|
||||
srvInterface = self.serviceInterfaceInfo()
|
||||
if srvInterface:
|
||||
# Rery while RESTConnectionError (that is, cannot connect)
|
||||
counter = 60
|
||||
logged = False
|
||||
while self._isAlive:
|
||||
counter -= 1
|
||||
try:
|
||||
self._certificate = self._api.ready(
|
||||
self._cfg.own_token,
|
||||
self._secret,
|
||||
srvInterface.ip,
|
||||
rest.LISTEN_PORT,
|
||||
)
|
||||
except rest.RESTConnectionError as e:
|
||||
if not logged: # Only log connection problems ONCE
|
||||
logged = True
|
||||
logger.error('Error connecting with UDS Broker')
|
||||
self.doWait(5000)
|
||||
continue
|
||||
except Exception as e:
|
||||
logger.error('Unhandled exception while setting ready: %s', e)
|
||||
if counter > 0:
|
||||
self.doWait(10000) # A long wait on other error...
|
||||
continue
|
||||
platform.operations.reboot() # On too many errors, simply reboot
|
||||
# Success or any error that is not recoverable (retunerd by UDS). if Error, service will be cleaned in a while.
|
||||
break
|
||||
else:
|
||||
logger.error(
|
||||
'Could not locate IP address!!!. (Not registered with UDS)'
|
||||
)
|
||||
|
||||
# Do not continue if not alive...
|
||||
if not self._isAlive:
|
||||
return
|
||||
|
||||
# Cleans sensible data
|
||||
if self._cfg.config:
|
||||
self._cfg = self._cfg._replace(
|
||||
config=self._cfg.config._replace(os=None), data=None
|
||||
)
|
||||
platform.store.writeConfig(self._cfg)
|
||||
|
||||
logger.info('Service ready')
|
||||
|
||||
def configureMachine(self) -> bool:
|
||||
if not self._isAlive:
|
||||
return False
|
||||
|
||||
if not self.isManaged():
|
||||
return True
|
||||
|
||||
# First, if runonce is present, honor it and remove it from config
|
||||
# Return values is "True" for keep service (or daemon) running, False if Stop it.
|
||||
if self._cfg.runonce_command:
|
||||
runOnce = self._cfg.runonce_command
|
||||
self._cfg = self._cfg._replace(runonce_command=None)
|
||||
platform.store.writeConfig(self._cfg)
|
||||
if self.execute(runOnce, "runOnce"):
|
||||
# If runonce is present, will not do anythin more
|
||||
# So we have to ensure that, when runonce command is finished, reboots the machine.
|
||||
# That is, the COMMAND itself has to restart the machine!
|
||||
return False # If the command fails, continue with the rest of the operations...
|
||||
|
||||
# Retry configuration while not stop service, config in case of error 10 times, reboot vm
|
||||
counter = 10
|
||||
while self._isAlive:
|
||||
counter -= 1
|
||||
try:
|
||||
if self._cfg.config and self._cfg.config.os:
|
||||
osData = self._cfg.config.os
|
||||
if osData.action == 'rename':
|
||||
self.rename(
|
||||
osData.name,
|
||||
osData.username,
|
||||
osData.password,
|
||||
osData.new_password,
|
||||
)
|
||||
elif osData.action == 'rename_ad':
|
||||
self.joinDomain(
|
||||
osData.name,
|
||||
osData.ad or '',
|
||||
osData.ou or '',
|
||||
osData.username or '',
|
||||
osData.password or '',
|
||||
)
|
||||
|
||||
if self._rebootRequested:
|
||||
try:
|
||||
platform.operations.reboot()
|
||||
except Exception as e:
|
||||
logger.error('Exception on reboot: {}'.format(e))
|
||||
return False # Stops service if reboot was requested ofc
|
||||
break
|
||||
except Exception as e:
|
||||
logger.error('Got exception operating machine: {}'.format(e))
|
||||
if counter > 0:
|
||||
self.doWait(5000)
|
||||
else:
|
||||
platform.operations.reboot()
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def initializeUnmanaged(self) -> bool:
|
||||
# Notify UDS about my callback
|
||||
self.getInterfaces() # Ensure we have interfaces
|
||||
if self._cfg.master_token:
|
||||
try:
|
||||
self._certificate = self._api.notifyUnmanagedCallback(
|
||||
self._cfg.master_token,
|
||||
self._secret,
|
||||
self._interfaces,
|
||||
rest.LISTEN_PORT,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error('Couuld not notify unmanaged callback: %s', e)
|
||||
|
||||
return True
|
||||
|
||||
def getInterfaces(self) -> None:
|
||||
if self._interfaces:
|
||||
return
|
||||
|
||||
while self._isAlive:
|
||||
self._interfaces = tools.validNetworkCards(
|
||||
self._cfg.restrict_net, platform.operations.getNetworkInfo()
|
||||
)
|
||||
if self._interfaces:
|
||||
break
|
||||
self.doWait(5000)
|
||||
|
||||
def initialize(self) -> bool:
|
||||
if (
|
||||
self._initialized or not self._cfg.host or not self._isAlive
|
||||
): # Not configured or not running
|
||||
return False
|
||||
|
||||
self._initialized = True
|
||||
|
||||
# Force time sync, just in case...
|
||||
if self.isManaged():
|
||||
platform.operations.forceTimeSync()
|
||||
|
||||
# Wait for Broker to be ready
|
||||
# Ensure we have intefaces...
|
||||
self.getInterfaces()
|
||||
|
||||
while self._isAlive:
|
||||
try:
|
||||
# If master token is present, initialize and get configuration data
|
||||
if self._cfg.master_token:
|
||||
initResult: types.InitializationResultType = self._api.initialize(
|
||||
self._cfg.master_token, self._interfaces, self._cfg.actorType
|
||||
)
|
||||
if not initResult.own_token: # Not managed
|
||||
logger.debug(
|
||||
'This host is not managed by UDS Broker (ids: {})'.format(
|
||||
self._interfaces
|
||||
)
|
||||
)
|
||||
return False
|
||||
|
||||
# Only removes master token for managed machines (will need it on next client execution)
|
||||
master_token = None if self.isManaged() else self._cfg.master_token
|
||||
self._cfg = self._cfg._replace(
|
||||
master_token=master_token,
|
||||
own_token=initResult.own_token,
|
||||
config=types.ActorDataConfigurationType(
|
||||
unique_id=initResult.unique_id, os=initResult.os
|
||||
),
|
||||
)
|
||||
|
||||
# On first successfull initialization request, master token will dissapear for managed hosts so it will be no more available (not needed anyway)
|
||||
if self.isManaged():
|
||||
platform.store.writeConfig(self._cfg)
|
||||
|
||||
# Setup logger now
|
||||
if self._cfg.own_token:
|
||||
logger.setRemoteLogger(self._api, self._cfg.own_token)
|
||||
|
||||
break # Initial configuration done..
|
||||
except rest.RESTConnectionError as e:
|
||||
logger.info(
|
||||
'Trying to inititialize connection with broker (last error: {})'.format(
|
||||
e
|
||||
)
|
||||
)
|
||||
self.doWait(5000) # Wait a bit and retry
|
||||
except rest.RESTError as e: # Invalid key?
|
||||
logger.error(
|
||||
'Error validating with broker. (Invalid token?): {}'.format(e)
|
||||
)
|
||||
return False
|
||||
except Exception:
|
||||
logger.exception()
|
||||
self.doWait(5000) # Wait a bit and retry...
|
||||
|
||||
return self.configureMachine()
|
||||
|
||||
def uninitialize(self):
|
||||
self._initialized = False
|
||||
self._cfg = self._cfg._replace(
|
||||
own_token=None
|
||||
) # Ensures assigned token is cleared
|
||||
|
||||
def finish(self) -> None:
|
||||
if self._http:
|
||||
self._http.stop()
|
||||
|
||||
# If logged in, notify UDS of logout (daemon stoped = no control = logout)
|
||||
if self._loggedIn and self._cfg.own_token:
|
||||
self._loggedIn = False
|
||||
try:
|
||||
self._api.logout(
|
||||
self._cfg.actorType,
|
||||
self._cfg.own_token,
|
||||
'',
|
||||
'',
|
||||
self._interfaces,
|
||||
self._secret,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error('Error notifying final logout to UDS: %s', e)
|
||||
|
||||
self.notifyStop()
|
||||
|
||||
def checkIpsChanged(self) -> None:
|
||||
if not self.isManaged():
|
||||
return # Unamanaged hosts does not changes ips. (The full initialize-login-logout process is done in a row, so at login the IP is correct)
|
||||
|
||||
try:
|
||||
if (
|
||||
not self._cfg.own_token
|
||||
or not self._cfg.config
|
||||
or not self._cfg.config.unique_id
|
||||
):
|
||||
# Not enouth data do check
|
||||
return
|
||||
currentInterfaces = tools.validNetworkCards(
|
||||
self._cfg.restrict_net, platform.operations.getNetworkInfo()
|
||||
)
|
||||
old = self.serviceInterfaceInfo()
|
||||
new = self.serviceInterfaceInfo(currentInterfaces)
|
||||
if not new or not old:
|
||||
raise Exception(
|
||||
'No ip currently available for {}'.format(
|
||||
self._cfg.config.unique_id
|
||||
)
|
||||
)
|
||||
if old.ip != new.ip:
|
||||
self._certificate = self._api.notifyIpChange(
|
||||
self._cfg.own_token, self._secret, new.ip, rest.LISTEN_PORT
|
||||
)
|
||||
# Now store new addresses & interfaces...
|
||||
self._interfaces = currentInterfaces
|
||||
logger.info(
|
||||
'Ip changed from {} to {}. Notified to UDS'.format(old.ip, new.ip)
|
||||
)
|
||||
# Stop the running HTTP Thread and start a new one, with new generated cert
|
||||
self.startHttpServer()
|
||||
except Exception as e:
|
||||
# No ip changed, log exception for info
|
||||
logger.warn('Checking ips failed: {}'.format(e))
|
||||
|
||||
def rename(
|
||||
self,
|
||||
name: str,
|
||||
userName: typing.Optional[str] = None,
|
||||
oldPassword: typing.Optional[str] = None,
|
||||
newPassword: typing.Optional[str] = None,
|
||||
) -> None:
|
||||
'''
|
||||
Invoked when broker requests a rename action
|
||||
default does nothing
|
||||
'''
|
||||
hostName = platform.operations.getComputerName()
|
||||
|
||||
# Check for password change request for an user
|
||||
if userName and newPassword:
|
||||
logger.info('Setting password for configured user')
|
||||
try:
|
||||
platform.operations.changeUserPassword(
|
||||
userName, oldPassword or '', newPassword
|
||||
)
|
||||
except Exception as e:
|
||||
# Logs error, but continue renaming computer
|
||||
logger.error(
|
||||
'Could not change password for user {}: {}'.format(userName, e)
|
||||
)
|
||||
|
||||
if hostName.lower() == name.lower():
|
||||
logger.info('Computer name is already {}'.format(hostName))
|
||||
return
|
||||
|
||||
if platform.operations.renameComputer(name):
|
||||
self.reboot()
|
||||
|
||||
def loop(self):
|
||||
# Main common loop
|
||||
try:
|
||||
# Checks if ips has changed
|
||||
self.checkIpsChanged()
|
||||
|
||||
# Now check if every registered client is already there (if logged in OFC)
|
||||
if self._loggedIn and not self._clientsPool.ping():
|
||||
self.logout('client_unavailable', '')
|
||||
except Exception as e:
|
||||
logger.error('Exception on main service loop: %s', e)
|
||||
|
||||
# ******************************************************
|
||||
# Methods that can be overriden by linux & windows Actor
|
||||
# ******************************************************
|
||||
def joinDomain( # pylint: disable=unused-argument, too-many-arguments
|
||||
self, name: str, domain: str, ou: str, account: str, password: str
|
||||
) -> None:
|
||||
'''
|
||||
Invoked when broker requests a "domain" action
|
||||
default does nothing
|
||||
'''
|
||||
logger.debug('Base join invoked: {} on {}, {}'.format(name, domain, ou))
|
||||
|
||||
# Client notifications
|
||||
def login(
|
||||
self, username: str, sessionType: typing.Optional[str] = None
|
||||
) -> types.LoginResultInfoType:
|
||||
result = types.LoginResultInfoType(
|
||||
ip='', hostname='', dead_line=None, max_idle=None
|
||||
)
|
||||
master_token = None
|
||||
secret = None
|
||||
# If unmanaged, do initialization now, because we don't know before this
|
||||
# Also, even if not initialized, get a "login" notification token
|
||||
if not self.isManaged():
|
||||
self._initialized = (
|
||||
self.initialize()
|
||||
) # Maybe it's a local login by an unmanaged host.... On real login, will execute initilize again
|
||||
# Unamanaged, need the master token
|
||||
master_token = self._cfg.master_token
|
||||
secret = self._secret
|
||||
|
||||
# Own token will not be set if UDS did not assigned the initialized VM to an user
|
||||
# In that case, take master token (if machine is Unamanaged version)
|
||||
token = self._cfg.own_token or master_token
|
||||
if token:
|
||||
result = self._api.login(
|
||||
self._cfg.actorType,
|
||||
token,
|
||||
username,
|
||||
sessionType or '',
|
||||
self._interfaces,
|
||||
secret,
|
||||
)
|
||||
|
||||
if result.logged_in:
|
||||
logger.debug('Login successful')
|
||||
self._loggedIn = True
|
||||
script = platform.store.invokeScriptOnLogin()
|
||||
if script:
|
||||
logger.info('Executing script on login: {}'.format(script))
|
||||
script += f'{username} {sessionType or "unknown"} {self._cfg.actorType}'
|
||||
self.execute(script, 'Logon')
|
||||
|
||||
return result
|
||||
|
||||
def logout(self, username: str, sessionType: typing.Optional[str]) -> None:
|
||||
master_token = self._cfg.master_token
|
||||
|
||||
# Own token will not be set if UDS did not assigned the initialized VM to an user
|
||||
# In that case, take master token (if machine is Unamanaged version)
|
||||
token = self._cfg.own_token or master_token
|
||||
if token:
|
||||
# If logout is not processed (that is, not ok result), the logout has not been processed
|
||||
if (
|
||||
self._api.logout(
|
||||
self._cfg.actorType,
|
||||
token,
|
||||
username,
|
||||
sessionType or '',
|
||||
self._interfaces,
|
||||
self._secret,
|
||||
)
|
||||
!= 'ok'
|
||||
):
|
||||
logger.info('Logout from %s ignored as required by uds broker', username)
|
||||
return
|
||||
|
||||
self._loggedIn = False
|
||||
self.onLogout(username)
|
||||
|
||||
if not self.isManaged():
|
||||
self.uninitialize()
|
||||
|
||||
# ****************************************
|
||||
# Methods that CAN BE overriden by actors
|
||||
# ****************************************
|
||||
def doWait(self, miliseconds: int) -> None:
|
||||
'''
|
||||
Invoked to wait a bit
|
||||
CAN be OVERRIDEN
|
||||
'''
|
||||
seconds = miliseconds / 1000.0
|
||||
# So it can be broken by "stop"
|
||||
while self._isAlive and seconds > 1:
|
||||
time.sleep(1)
|
||||
seconds -= 1
|
||||
time.sleep(seconds)
|
||||
|
||||
def notifyStop(self) -> None:
|
||||
'''
|
||||
Overriden to log stop (on windows, notify to service manager)
|
||||
'''
|
||||
logger.info('Service stopped')
|
||||
|
||||
def preConnect(
|
||||
self, userName: str, protocol: str, ip: str, hostname: str, udsUserName: str
|
||||
) -> str:
|
||||
'''
|
||||
Invoked when received a PRE Connection request via REST
|
||||
Base preconnect executes the preconnect command
|
||||
'''
|
||||
if self._cfg.pre_command:
|
||||
self.execute(
|
||||
self._cfg.pre_command
|
||||
+ ' {} {} {} {} {}'.format(
|
||||
userName.replace('"', '%22'),
|
||||
protocol,
|
||||
ip,
|
||||
hostname,
|
||||
udsUserName.replace('"', '%22'),
|
||||
),
|
||||
'preConnect',
|
||||
)
|
||||
|
||||
return 'ok'
|
||||
|
||||
def onLogout(self, userName: str) -> None:
|
||||
logger.debug('On logout invoked for {}'.format(userName))
|
@@ -1,85 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2019 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
|
||||
'''
|
||||
import threading
|
||||
import ipaddress
|
||||
import typing
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from udsactor.types import InterfaceInfoType
|
||||
|
||||
|
||||
class ScriptExecutorThread(threading.Thread):
|
||||
|
||||
def __init__(self, script: str) -> None:
|
||||
super(ScriptExecutorThread, self).__init__()
|
||||
self.script = script
|
||||
|
||||
def run(self) -> None:
|
||||
from udsactor.log import logger
|
||||
|
||||
try:
|
||||
logger.debug('Executing script: {}'.format(self.script))
|
||||
exec(self.script, globals(), None) # pylint: disable=exec-used
|
||||
except Exception as e:
|
||||
logger.error('Error executing script: {}'.format(e))
|
||||
logger.exception()
|
||||
|
||||
|
||||
# Convert "X.X.X.X/X" to ipaddress.IPv4Network
|
||||
def strToNoIPV4Network(net: typing.Optional[str]) -> typing.Optional[ipaddress.IPv4Network]:
|
||||
if not net: # Empty or None
|
||||
return None
|
||||
try:
|
||||
return ipaddress.IPv4Interface(net).network
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def validNetworkCards(
|
||||
net: typing.Optional[str], cards: typing.Iterable['InterfaceInfoType']
|
||||
) -> typing.List['InterfaceInfoType']:
|
||||
try:
|
||||
subnet = strToNoIPV4Network(net)
|
||||
except Exception as e:
|
||||
subnet = None
|
||||
|
||||
if subnet is None:
|
||||
return list(cards)
|
||||
|
||||
def isValid(ip: str, subnet: ipaddress.IPv4Network) -> bool:
|
||||
if not ip:
|
||||
return False
|
||||
try:
|
||||
return ipaddress.IPv4Address(ip) in subnet
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
return [c for c in cards if isValid(c.ip, subnet)]
|
@@ -1,68 +0,0 @@
|
||||
import typing
|
||||
|
||||
MANAGED = 'managed'
|
||||
UNMANAGED = 'unmanaged'
|
||||
|
||||
class InterfaceInfoType(typing.NamedTuple):
|
||||
name: str
|
||||
mac: str
|
||||
ip: str
|
||||
|
||||
class AuthenticatorType(typing.NamedTuple):
|
||||
authId: str
|
||||
authSmallName: str
|
||||
auth: str
|
||||
type: str
|
||||
priority: int
|
||||
isCustom: bool
|
||||
|
||||
class ActorOsConfigurationType(typing.NamedTuple):
|
||||
action: str
|
||||
name: str
|
||||
username: typing.Optional[str] = None
|
||||
password: typing.Optional[str] = None
|
||||
new_password: typing.Optional[str] = None
|
||||
ad: typing.Optional[str] = None
|
||||
ou: typing.Optional[str] = None
|
||||
|
||||
class ActorDataConfigurationType(typing.NamedTuple):
|
||||
unique_id: typing.Optional[str] = None
|
||||
os: typing.Optional[ActorOsConfigurationType] = None
|
||||
|
||||
class ActorConfigurationType(typing.NamedTuple):
|
||||
host: str
|
||||
validateCertificate: bool
|
||||
actorType: typing.Optional[str] = None
|
||||
master_token: typing.Optional[str] = None
|
||||
own_token: typing.Optional[str] = None
|
||||
restrict_net: typing.Optional[str] = None
|
||||
|
||||
pre_command: typing.Optional[str] = None
|
||||
runonce_command: typing.Optional[str] = None
|
||||
post_command: typing.Optional[str] = None
|
||||
|
||||
log_level: int = 2
|
||||
|
||||
config: typing.Optional[ActorDataConfigurationType] = None
|
||||
|
||||
data: typing.Optional[typing.Dict[str, typing.Any]] = None
|
||||
|
||||
class InitializationResultType(typing.NamedTuple):
|
||||
own_token: typing.Optional[str] = None
|
||||
unique_id: typing.Optional[str] = None
|
||||
os: typing.Optional[ActorOsConfigurationType] = None
|
||||
|
||||
class LoginResultInfoType(typing.NamedTuple):
|
||||
ip: str
|
||||
hostname: str
|
||||
dead_line: typing.Optional[int]
|
||||
max_idle: typing.Optional[int] # Not provided by broker
|
||||
|
||||
@property
|
||||
def logged_in(self) -> bool:
|
||||
return self.hostname != '' or self.ip != ''
|
||||
|
||||
class CertificateInfoType(typing.NamedTuple):
|
||||
private_key: str
|
||||
server_certificate: str
|
||||
password: str
|
@@ -1 +0,0 @@
|
||||
VERSION = '3.5.0'
|
@@ -1,83 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014 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
|
||||
'''
|
||||
# pylint: disable=invalid-name
|
||||
import logging
|
||||
import os
|
||||
import tempfile
|
||||
import typing
|
||||
|
||||
import servicemanager
|
||||
|
||||
# Valid logging levels, from UDS Broker (uds.core.utils.log).
|
||||
from .. import loglevel
|
||||
|
||||
class LocalLogger: # pylint: disable=too-few-public-methods
|
||||
linux = False
|
||||
windows = True
|
||||
serviceLogger = False
|
||||
|
||||
logger: typing.Optional[logging.Logger]
|
||||
|
||||
def __init__(self):
|
||||
# tempdir is different for "user application" and "service"
|
||||
# service wil get c:\windows\temp, while user will get c:\users\XXX\temp
|
||||
try:
|
||||
logging.basicConfig(
|
||||
filename=os.path.join(tempfile.gettempdir(), 'udsactor.log'),
|
||||
filemode='a',
|
||||
format='%(levelname)s %(asctime)s %(message)s',
|
||||
level=logging.DEBUG
|
||||
)
|
||||
except Exception:
|
||||
logging.basicConfig() # basic init
|
||||
|
||||
self.logger = logging.getLogger('udsactor')
|
||||
self.serviceLogger = False
|
||||
|
||||
def log(self, level: int, message: str) -> None:
|
||||
# Debug messages are logged to a file
|
||||
# our loglevels are 0 (other), 10000 (debug), ....
|
||||
# logging levels are 10 (debug), 20 (info)
|
||||
# OTHER = logging.NOTSET
|
||||
if self.logger:
|
||||
self.logger.log(int(level / 1000), message)
|
||||
|
||||
if level < loglevel.ERROR or self.serviceLogger is False: # Only information and above will be on event log
|
||||
return
|
||||
|
||||
# In fact, we have restricted level in windows event log to ERROR or FATAL
|
||||
# but left the code for just a case in the future...
|
||||
if level < loglevel.WARN: # Info
|
||||
servicemanager.LogInfoMsg(message)
|
||||
elif level < loglevel.ERROR: # WARN
|
||||
servicemanager.LogWarningMsg(message)
|
||||
else: # Error & Fatal
|
||||
servicemanager.LogErrorMsg(message)
|
@@ -1,251 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014-2019 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
|
||||
'''
|
||||
# pylint: disable=invalid-name
|
||||
import os
|
||||
import subprocess
|
||||
import ctypes
|
||||
from ctypes.wintypes import DWORD, LPCWSTR
|
||||
import typing
|
||||
|
||||
import win32com.client
|
||||
from win32com.shell import shell # pylint: disable=no-name-in-module,import-error
|
||||
import win32net
|
||||
import win32security
|
||||
import win32api
|
||||
import win32con
|
||||
|
||||
from .. import types
|
||||
from ..log import logger
|
||||
|
||||
def checkPermissions() -> bool:
|
||||
return shell.IsUserAnAdmin()
|
||||
|
||||
def getErrorMessage(resultCode: int = 0) -> str:
|
||||
# sys_fs_enc = sys.getfilesystemencoding() or 'mbcs'
|
||||
msg = win32api.FormatMessage(resultCode)
|
||||
return msg
|
||||
|
||||
def getComputerName() -> str:
|
||||
return win32api.GetComputerNameEx(win32con.ComputerNamePhysicalDnsHostname)
|
||||
|
||||
def getNetworkInfo() -> typing.Iterator[types.InterfaceInfoType]:
|
||||
obj = win32com.client.Dispatch("WbemScripting.SWbemLocator")
|
||||
wmobj = obj.ConnectServer("localhost", "root\\cimv2")
|
||||
adapters = wmobj.ExecQuery("Select * from Win32_NetworkAdapterConfiguration where IpEnabled=True")
|
||||
try:
|
||||
for obj in adapters:
|
||||
for ip in obj.IPAddress:
|
||||
if ':' in ip: # Is IPV6, skip this
|
||||
continue
|
||||
if ip is None or ip == '' or ip.startswith('169.254') or ip.startswith('0.'): # If single link ip, or no ip
|
||||
continue
|
||||
yield types.InterfaceInfoType(name=obj.Caption, mac=obj.MACAddress, ip=ip)
|
||||
except Exception:
|
||||
return
|
||||
|
||||
def getDomainName() -> str:
|
||||
'''
|
||||
Will return the domain name if we belong a domain, else None
|
||||
(if part of a network group, will also return None)
|
||||
'''
|
||||
# Status:
|
||||
# 0 = Unknown
|
||||
# 1 = Unjoined
|
||||
# 2 = Workgroup
|
||||
# 3 = Domain
|
||||
domain, status = win32net.NetGetJoinInformation()
|
||||
if status != 3:
|
||||
domain = None
|
||||
|
||||
return domain
|
||||
|
||||
def getWindowsVersion() -> typing.Tuple[int, int, int, int, str]:
|
||||
return win32api.GetVersionEx()
|
||||
|
||||
EWX_LOGOFF = 0x00000000
|
||||
EWX_SHUTDOWN = 0x00000001
|
||||
EWX_REBOOT = 0x00000002
|
||||
EWX_FORCE = 0x00000004
|
||||
EWX_POWEROFF = 0x00000008
|
||||
EWX_FORCEIFHUNG = 0x00000010
|
||||
|
||||
def reboot(flags: int = EWX_FORCEIFHUNG | EWX_REBOOT) -> None:
|
||||
hproc = win32api.GetCurrentProcess()
|
||||
htok = win32security.OpenProcessToken(hproc, win32security.TOKEN_ADJUST_PRIVILEGES | win32security.TOKEN_QUERY)
|
||||
privs = ((win32security.LookupPrivilegeValue(None, win32security.SE_SHUTDOWN_NAME), win32security.SE_PRIVILEGE_ENABLED),)
|
||||
win32security.AdjustTokenPrivileges(htok, 0, privs)
|
||||
win32api.ExitWindowsEx(flags, 0)
|
||||
|
||||
def loggoff() -> None:
|
||||
win32api.ExitWindowsEx(EWX_LOGOFF)
|
||||
|
||||
def renameComputer(newName: str) -> bool:
|
||||
'''
|
||||
Changes the computer name
|
||||
Returns True if reboot needed
|
||||
'''
|
||||
# Needs admin privileges to work
|
||||
if ctypes.windll.kernel32.SetComputerNameExW(DWORD(win32con.ComputerNamePhysicalDnsHostname), LPCWSTR(newName)) == 0: # @UndefinedVariable
|
||||
# win32api.FormatMessage -> returns error string
|
||||
# win32api.GetLastError -> returns error code
|
||||
# (just put this comment here to remember to log this when logger is available)
|
||||
error = getErrorMessage()
|
||||
computerName = win32api.GetComputerNameEx(win32con.ComputerNamePhysicalDnsHostname)
|
||||
raise Exception('Error renaming computer from {} to {}: {}'.format(computerName, newName, error))
|
||||
return True
|
||||
|
||||
NETSETUP_JOIN_DOMAIN = 0x00000001
|
||||
NETSETUP_ACCT_CREATE = 0x00000002
|
||||
NETSETUP_ACCT_DELETE = 0x00000004
|
||||
NETSETUP_WIN9X_UPGRADE = 0x00000010
|
||||
NETSETUP_DOMAIN_JOIN_IF_JOINED = 0x00000020
|
||||
NETSETUP_JOIN_UNSECURE = 0x00000040
|
||||
NETSETUP_MACHINE_PWD_PASSED = 0x00000080
|
||||
NETSETUP_JOIN_WITH_NEW_NAME = 0x00000400
|
||||
NETSETUP_DEFER_SPN_SET = 0x1000000
|
||||
|
||||
def joinDomain(domain: str, ou: str, account: str, password: str, executeInOneStep: bool = False) -> None:
|
||||
'''
|
||||
Joins machine to a windows domain
|
||||
:param domain: Domain to join to
|
||||
:param ou: Ou that will hold machine
|
||||
:param account: Account used to join domain
|
||||
:param password: Password of account used to join domain
|
||||
:param executeInOneStep: If true, means that this machine has been renamed and wants to add NETSETUP_JOIN_WITH_NEW_NAME to request so we can do rename/join in one step.
|
||||
'''
|
||||
# If account do not have domain, include it
|
||||
if '@' not in account and '\\' not in account:
|
||||
if '.' in domain:
|
||||
account = account + '@' + domain
|
||||
else:
|
||||
account = domain + '\\' + account
|
||||
|
||||
# Do log
|
||||
flags: typing.Any = NETSETUP_ACCT_CREATE | NETSETUP_DOMAIN_JOIN_IF_JOINED | NETSETUP_JOIN_DOMAIN
|
||||
|
||||
if executeInOneStep:
|
||||
flags |= NETSETUP_JOIN_WITH_NEW_NAME
|
||||
|
||||
flags = DWORD(flags)
|
||||
|
||||
lpDomain = LPCWSTR(domain)
|
||||
|
||||
# Must be in format "ou=.., ..., dc=...,"
|
||||
lpOu = LPCWSTR(ou) if ou is not None and ou != '' else None
|
||||
lpAccount = LPCWSTR(account)
|
||||
lpPassword = LPCWSTR(password)
|
||||
|
||||
res = ctypes.windll.netapi32.NetJoinDomain(None, lpDomain, lpOu, lpAccount, lpPassword, flags)
|
||||
# Machine found in another ou, use it and warn this on log
|
||||
if res == 2224:
|
||||
flags = DWORD(NETSETUP_DOMAIN_JOIN_IF_JOINED | NETSETUP_JOIN_DOMAIN)
|
||||
res = ctypes.windll.netapi32.NetJoinDomain(None, lpDomain, None, lpAccount, lpPassword, flags)
|
||||
if res:
|
||||
# Log the error
|
||||
error = getErrorMessage(res)
|
||||
if res == 1355:
|
||||
error = "DC Is not reachable"
|
||||
logger.error('Error joining domain: {}, {}'.format(error, res))
|
||||
raise Exception('Error joining domain {}, with credentials {}/*****{}: {}, {}'.format(domain, account, ', under OU {}'.format(ou) if ou is not None else '', res, error))
|
||||
|
||||
def changeUserPassword(user: str, oldPassword: str, newPassword: str) -> None:
|
||||
# lpUser = LPCWSTR(user)
|
||||
# lpOldPassword = LPCWSTR(oldPassword)
|
||||
# lpNewPassword = LPCWSTR(newPassword)
|
||||
|
||||
# res = ctypes.windll.netapi32.NetUserChangePassword(None, lpUser, lpOldPassword, lpNewPassword)
|
||||
# Try to set new password "a las bravas", ignoring old one. This will not work with domain users
|
||||
res = win32net.NetUserSetInfo(None, user, 1003, {'password': newPassword})
|
||||
|
||||
if res:
|
||||
# Log the error, and raise exception to parent
|
||||
error = getErrorMessage(res)
|
||||
raise Exception('Error changing password for user {}: {} {}'.format(user, res, error))
|
||||
|
||||
class LASTINPUTINFO(ctypes.Structure): # pylint: disable=too-few-public-methods
|
||||
_fields_ = [
|
||||
('cbSize', ctypes.c_uint),
|
||||
('dwTime', ctypes.c_uint),
|
||||
]
|
||||
|
||||
def initIdleDuration(atLeastSeconds: int): # pylint: disable=unused-argument
|
||||
'''
|
||||
In windows, there is no need to set screensaver
|
||||
'''
|
||||
return
|
||||
|
||||
def getIdleDuration() -> float:
|
||||
try:
|
||||
lastInputInfo = LASTINPUTINFO()
|
||||
lastInputInfo.cbSize = ctypes.sizeof(lastInputInfo) # pylint: disable=attribute-defined-outside-init
|
||||
if ctypes.windll.user32.GetLastInputInfo(ctypes.byref(lastInputInfo)) == 0:
|
||||
return 0
|
||||
current = ctypes.c_uint(ctypes.windll.kernel32.GetTickCount()).value
|
||||
if current < lastInputInfo.dwTime:
|
||||
current += 4294967296 # If current has "rolled" to zero, adjust it so it is greater than lastInputInfo
|
||||
millis = current - lastInputInfo.dwTime # @UndefinedVariable
|
||||
return millis / 1000.0
|
||||
except Exception as e:
|
||||
logger.error('Getting idle duration: {}'.format(e))
|
||||
return 0
|
||||
|
||||
def getCurrentUser() -> str:
|
||||
'''
|
||||
Returns current logged in username
|
||||
'''
|
||||
return os.environ['USERNAME']
|
||||
|
||||
def getSessionType() -> str:
|
||||
'''
|
||||
Known values:
|
||||
* Unknown -> No SESSIONNAME environment variable
|
||||
* Console -> Local session
|
||||
* RDP-Tcp#[0-9]+ -> RDP Session
|
||||
'''
|
||||
return os.environ.get('SESSIONNAME', 'unknown')
|
||||
|
||||
def writeToPipe(pipeName: str, bytesPayload: bytes, waitForResponse: bool) -> typing.Optional[bytes]:
|
||||
# (str, bytes, bool) -> Optional[bytes]
|
||||
try:
|
||||
with open(pipeName, 'r+b', 0) as f:
|
||||
f.write(bytesPayload)
|
||||
# f.seek(0) # As recommended on intenet, but seems to work fin without thos
|
||||
if waitForResponse:
|
||||
return f.read()
|
||||
return b'ok'
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def forceTimeSync() -> None:
|
||||
try:
|
||||
subprocess.call([r'c:\WINDOWS\System32\w32tm.exe', ' /resync']) # , '/rediscover'])
|
||||
except Exception as e:
|
||||
logger.error('Error invoking time sync command: %s', e)
|
@@ -1,77 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2019 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
|
||||
'''
|
||||
# pylint: disable=invalid-name
|
||||
import sys
|
||||
import win32service
|
||||
import win32serviceutil
|
||||
import servicemanager
|
||||
|
||||
import win32timezone # pylint: disable=unused-import
|
||||
|
||||
from .service import UDSActorSvc
|
||||
|
||||
def setupRecoverService():
|
||||
svc_name = UDSActorSvc._svc_name_ # pylint: disable=protected-access
|
||||
|
||||
hs = None
|
||||
hscm = None
|
||||
try:
|
||||
hscm = win32service.OpenSCManager(None, None, win32service.SC_MANAGER_ALL_ACCESS)
|
||||
|
||||
try:
|
||||
hs = win32serviceutil.SmartOpenService(hscm, svc_name, win32service.SERVICE_ALL_ACCESS)
|
||||
service_failure_actions = {
|
||||
'ResetPeriod': 864000, # Time in ms after which to reset the failure count to zero.
|
||||
'RebootMsg': u'', # Not using reboot option
|
||||
'Command': u'', # Not using run-command option
|
||||
'Actions': [
|
||||
(win32service.SC_ACTION_RESTART, 5000), # action, delay in ms
|
||||
(win32service.SC_ACTION_RESTART, 5000)
|
||||
]
|
||||
}
|
||||
win32service.ChangeServiceConfig2(hs, win32service.SERVICE_CONFIG_FAILURE_ACTIONS, service_failure_actions)
|
||||
finally:
|
||||
if hs:
|
||||
win32service.CloseServiceHandle(hs)
|
||||
finally:
|
||||
if hscm:
|
||||
win32service.CloseServiceHandle(hscm)
|
||||
|
||||
|
||||
def run() -> None:
|
||||
if len(sys.argv) == 1:
|
||||
servicemanager.Initialize()
|
||||
servicemanager.PrepareToHostSingle(UDSActorSvc)
|
||||
servicemanager.StartServiceCtrlDispatcher()
|
||||
elif sys.argv[1] == '--setup-recovery':
|
||||
setupRecoverService()
|
||||
else:
|
||||
win32serviceutil.HandleCommandLine(UDSActorSvc)
|
@@ -1,276 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014 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
|
||||
'''
|
||||
# pylint: disable=invalid-name
|
||||
import struct
|
||||
import typing
|
||||
|
||||
import win32serviceutil
|
||||
import win32service
|
||||
import win32security
|
||||
import win32net
|
||||
import win32event
|
||||
import pythoncom
|
||||
import servicemanager
|
||||
import winreg as wreg
|
||||
|
||||
from . import operations
|
||||
from . import store
|
||||
from ..service import CommonService
|
||||
|
||||
from ..log import logger
|
||||
|
||||
REMOTE_USERS_SID = 'S-1-5-32-555' # Well nown sid for remote desktop users
|
||||
|
||||
class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService):
|
||||
'''
|
||||
This class represents a Windows Service for managing actor interactions
|
||||
with UDS Broker and Machine
|
||||
'''
|
||||
# ServiceeFramework related
|
||||
_svc_name_ = "UDSActorNG"
|
||||
_svc_display_name_ = "UDS Actor Service"
|
||||
_svc_description_ = "UDS Actor Management Service"
|
||||
_svc_deps_ = ['EventLog']
|
||||
|
||||
_user: typing.Optional[str]
|
||||
_hWaitStop: typing.Any
|
||||
|
||||
def __init__(self, args):
|
||||
win32serviceutil.ServiceFramework.__init__(self, args)
|
||||
CommonService.__init__(self)
|
||||
|
||||
self._hWaitStop = win32event.CreateEvent(None, 1, 0, None)
|
||||
self._user = None
|
||||
|
||||
def SvcStop(self) -> None:
|
||||
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
|
||||
self._isAlive = False
|
||||
win32event.SetEvent(self._hWaitStop)
|
||||
|
||||
SvcShutdown = SvcStop
|
||||
|
||||
def notifyStop(self) -> None:
|
||||
servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STOPPED, (self._svc_name_, ''))
|
||||
super().notifyStop()
|
||||
|
||||
def doWait(self, miliseconds: int) -> None:
|
||||
win32event.WaitForSingleObject(self._hWaitStop, miliseconds)
|
||||
# On windows, and while on tasks, ensure that our app processes waiting messages on "wait times"
|
||||
pythoncom.PumpWaitingMessages() # pylint: disable=no-member
|
||||
|
||||
def oneStepJoin(self, name: str, domain: str, ou: str, account: str, password: str) -> None: # pylint: disable=too-many-arguments
|
||||
'''
|
||||
Ejecutes the join domain in exactly one step
|
||||
'''
|
||||
currName = operations.getComputerName()
|
||||
# If name is desired, simply execute multiStepJoin, because computer
|
||||
# name will not change
|
||||
if currName.lower() == name.lower():
|
||||
self.multiStepJoin(name, domain, ou, account, password)
|
||||
return
|
||||
|
||||
operations.renameComputer(name)
|
||||
logger.debug('Computer renamed to {} without reboot'.format(name))
|
||||
operations.joinDomain(domain, ou, account, password, executeInOneStep=True)
|
||||
logger.debug('Requested join domain {} without errors'.format(domain))
|
||||
self.reboot()
|
||||
|
||||
def multiStepJoin(self, name: str, domain: str, ou: str, account: str, password: str) -> None: # pylint: disable=too-many-arguments
|
||||
currName = operations.getComputerName()
|
||||
if currName.lower() == name.lower():
|
||||
currDomain = operations.getDomainName()
|
||||
if currDomain:
|
||||
# logger.debug('Name: "{}" vs "{}", Domain: "{}" vs "{}"'.format(currName.lower(), name.lower(), currDomain.lower(), domain.lower()))
|
||||
logger.debug('Machine {} is part of domain {}'.format(name, domain))
|
||||
self.setReady()
|
||||
else:
|
||||
operations.joinDomain(domain, ou, account, password, executeInOneStep=False)
|
||||
self.reboot()
|
||||
else:
|
||||
operations.renameComputer(name)
|
||||
logger.info('Rebooting computer for activating new name {}'.format(name))
|
||||
self.reboot()
|
||||
|
||||
def joinDomain( # pylint: disable=unused-argument, too-many-arguments
|
||||
self,
|
||||
name: str,
|
||||
domain: str,
|
||||
ou: str,
|
||||
account: str,
|
||||
password: str
|
||||
) -> None:
|
||||
versionData = operations.getWindowsVersion()
|
||||
versionInt = versionData[0] * 10 + versionData[1]
|
||||
logger.debug('Starting joining domain {} with name {} (detected operating version: {})'.format(domain, name, versionData))
|
||||
# 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 versionInt >= 60 and not store.useOldJoinSystem():
|
||||
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, userName: str, protocol: str, ip: str, hostname: str, udsUserName: str) -> str:
|
||||
logger.debug('Pre connect invoked')
|
||||
|
||||
if protocol == 'rdp': # If connection is not using rdp, skip adding user
|
||||
# Well known SSID for Remote Desktop Users
|
||||
groupName = win32security.LookupAccountSid(None, win32security.GetBinarySid(REMOTE_USERS_SID))[0]
|
||||
|
||||
useraAlreadyInGroup = False
|
||||
resumeHandle = 0
|
||||
while True:
|
||||
users, _, resumeHandle = win32net.NetLocalGroupGetMembers(None, groupName, 1, resumeHandle, 32768)
|
||||
if userName.lower() in [u['name'].lower() for u in users]:
|
||||
useraAlreadyInGroup = True
|
||||
break
|
||||
if resumeHandle == 0:
|
||||
break
|
||||
|
||||
if not useraAlreadyInGroup:
|
||||
logger.debug('User not in group, adding it')
|
||||
self._user = userName
|
||||
try:
|
||||
userSSID = win32security.LookupAccountName(None, userName)[0]
|
||||
win32net.NetLocalGroupAddMembers(None, groupName, 0, [{'sid': userSSID}])
|
||||
except Exception as e:
|
||||
logger.error('Exception adding user to Remote Desktop Users: {}'.format(e))
|
||||
else:
|
||||
self._user = None
|
||||
logger.debug('User {} already in group'.format(userName))
|
||||
|
||||
return super().preConnect(userName, protocol, ip, hostname, udsUserName)
|
||||
|
||||
def ovLogon(self, username: str, password: str) -> str:
|
||||
"""
|
||||
Logon on oVirt agent
|
||||
currently not used.
|
||||
"""
|
||||
# Compose packet for ov
|
||||
usernameBytes = username.encode()
|
||||
passwordBytes = password.encode()
|
||||
packet = struct.pack('!I', len(usernameBytes)) + usernameBytes + struct.pack('!I', len(passwordBytes)) + passwordBytes
|
||||
# Send packet with username/password to ov pipe
|
||||
operations.writeToPipe("\\\\.\\pipe\\VDSMDPipe", packet, True)
|
||||
return 'done'
|
||||
|
||||
def onLogout(self, userName) -> None:
|
||||
logger.debug('Windows onLogout invoked: {}, {}'.format(userName, self._user))
|
||||
try:
|
||||
p = win32security.GetBinarySid(REMOTE_USERS_SID)
|
||||
groupName = win32security.LookupAccountSid(None, p)[0]
|
||||
except Exception:
|
||||
logger.error('Exception getting Windows Group')
|
||||
return
|
||||
|
||||
if self._user:
|
||||
try:
|
||||
win32net.NetLocalGroupDelMembers(None, groupName, [self._user])
|
||||
except Exception as e:
|
||||
logger.error('Exception removing user from Remote Desktop Users: {}'.format(e))
|
||||
|
||||
def isInstallationRunning(self):
|
||||
'''
|
||||
Detect if windows is installing anything, so we can delay the execution of Service
|
||||
'''
|
||||
try:
|
||||
key = wreg.OpenKey(wreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Setup\State')
|
||||
data, _ = wreg.QueryValueEx(key, 'ImageState')
|
||||
logger.debug('State: %s', data)
|
||||
return data != 'IMAGE_STATE_COMPLETE' # If ImageState is different of ImageStateComplete, there is something running on installation
|
||||
except Exception: # If not found, means that no installation is running
|
||||
return False
|
||||
|
||||
def SvcDoRun(self) -> None: # pylint: disable=too-many-statements, too-many-branches
|
||||
'''
|
||||
Main service loop
|
||||
'''
|
||||
servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STARTED, (self._svc_name_, ''))
|
||||
|
||||
# call the CoInitialize to allow the registration to run in an other
|
||||
# thread
|
||||
logger.debug('Initializing coms')
|
||||
|
||||
pythoncom.CoInitialize() # pylint: disable=no-member
|
||||
|
||||
# Check if some install is running on windows before proceeding
|
||||
while self._isAlive:
|
||||
if self.isInstallationRunning():
|
||||
win32event.WaitForSingleObject(self._hWaitStop, 1000) # Wait a bit, and check again
|
||||
continue
|
||||
break
|
||||
|
||||
if not self._isAlive: # Has been stopped while waiting windows installations
|
||||
self.finish()
|
||||
return
|
||||
|
||||
# Unmanaged services does not initializes "on start", but rather when user logs in (because userservice does not exists "as such" before that)
|
||||
if self.isManaged():
|
||||
if not self.initialize():
|
||||
logger.info('Service stopped due to init')
|
||||
self.finish()
|
||||
win32event.WaitForSingleObject(self._hWaitStop, 5000)
|
||||
return # Stop daemon if initializes told to do so
|
||||
|
||||
# Initialization is done, set machine to ready for UDS, communicate urls, etc...
|
||||
self.setReady()
|
||||
else:
|
||||
if not self.initializeUnmanaged():
|
||||
self.finish()
|
||||
return
|
||||
|
||||
# Start listening for petitions
|
||||
self.startHttpServer()
|
||||
|
||||
# *********************
|
||||
# * Main Service loop *
|
||||
# *********************
|
||||
# Counter used to check ip changes only once every 10 seconds
|
||||
counter = 0
|
||||
while self._isAlive:
|
||||
counter += 1
|
||||
try:
|
||||
pythoncom.PumpWaitingMessages() # pylint: disable=no-member
|
||||
|
||||
if counter % 5 == 0: # Once every 5 seconds
|
||||
self.loop()
|
||||
|
||||
except Exception as e:
|
||||
logger.error('Got exception on main loop: %s', e)
|
||||
# Continue after a while...
|
||||
|
||||
# In milliseconds, will break if event hWaitStop is set
|
||||
win32event.WaitForSingleObject(self._hWaitStop, 1000)
|
||||
|
||||
logger.debug('Exited main loop')
|
||||
|
||||
self.finish()
|
@@ -1,109 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014 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
|
||||
'''
|
||||
# pylint: disable=invalid-name
|
||||
import pickle
|
||||
|
||||
import winreg as wreg
|
||||
import win32security
|
||||
|
||||
from .. import types
|
||||
|
||||
PATH = 'Software\\UDSActor'
|
||||
BASEKEY = wreg.HKEY_LOCAL_MACHINE
|
||||
|
||||
def fixRegistryPermissions(handle) -> None:
|
||||
# Fix permissions so users can't read this key
|
||||
v = win32security.GetSecurityInfo(handle, win32security.SE_REGISTRY_KEY, win32security.DACL_SECURITY_INFORMATION)
|
||||
dacl = v.GetSecurityDescriptorDacl()
|
||||
n = 0
|
||||
# Remove all normal users access permissions to the registry key
|
||||
while n < dacl.GetAceCount():
|
||||
if str(dacl.GetAce(n)[2]) == 'PySID:S-1-5-32-545': # Whell known Users SID
|
||||
dacl.DeleteAce(n)
|
||||
else:
|
||||
n += 1
|
||||
win32security.SetSecurityInfo(
|
||||
handle,
|
||||
win32security.SE_REGISTRY_KEY,
|
||||
win32security.DACL_SECURITY_INFORMATION | win32security.PROTECTED_DACL_SECURITY_INFORMATION,
|
||||
None,
|
||||
None,
|
||||
dacl,
|
||||
None
|
||||
)
|
||||
|
||||
def readConfig() -> types.ActorConfigurationType:
|
||||
try:
|
||||
key = wreg.OpenKey(BASEKEY, PATH, 0, wreg.KEY_QUERY_VALUE)
|
||||
data, _ = wreg.QueryValueEx(key, '')
|
||||
wreg.CloseKey(key)
|
||||
return pickle.loads(data)
|
||||
except Exception:
|
||||
return types.ActorConfigurationType('', False)
|
||||
|
||||
|
||||
def writeConfig(config: types.ActorConfigurationType) -> None:
|
||||
try:
|
||||
key = wreg.OpenKey(BASEKEY, PATH, 0, wreg.KEY_ALL_ACCESS)
|
||||
except Exception:
|
||||
key = wreg.CreateKeyEx(BASEKEY, PATH, 0, wreg.KEY_ALL_ACCESS)
|
||||
|
||||
fixRegistryPermissions(key.handle) # type: ignore
|
||||
|
||||
wreg.SetValueEx(key, "", 0, wreg.REG_BINARY, pickle.dumps(config)) # type: ignore
|
||||
wreg.CloseKey(key)
|
||||
|
||||
|
||||
def useOldJoinSystem() -> bool:
|
||||
try:
|
||||
key = wreg.OpenKey(BASEKEY, PATH, 0, wreg.KEY_QUERY_VALUE)
|
||||
try:
|
||||
data, _ = wreg.QueryValueEx(key, 'join')
|
||||
except Exception:
|
||||
data = ''
|
||||
wreg.CloseKey(key)
|
||||
except Exception:
|
||||
data = ''
|
||||
|
||||
return data == 'old'
|
||||
|
||||
def invokeScriptOnLogin() -> str:
|
||||
try:
|
||||
key = wreg.OpenKey(BASEKEY, PATH, 0, wreg.KEY_QUERY_VALUE)
|
||||
try:
|
||||
data, _ = wreg.QueryValueEx(key, 'logonScript')
|
||||
except Exception:
|
||||
data = ''
|
||||
wreg.CloseKey(key)
|
||||
except Exception:
|
||||
data = ''
|
||||
|
||||
return data
|
@@ -1,249 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file 'setup-dialog.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.15.2
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_UdsActorSetupDialog(object):
|
||||
def setupUi(self, UdsActorSetupDialog):
|
||||
UdsActorSetupDialog.setObjectName("UdsActorSetupDialog")
|
||||
UdsActorSetupDialog.setWindowModality(QtCore.Qt.WindowModal)
|
||||
UdsActorSetupDialog.resize(590, 307)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(UdsActorSetupDialog.sizePolicy().hasHeightForWidth())
|
||||
UdsActorSetupDialog.setSizePolicy(sizePolicy)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Verdana")
|
||||
font.setPointSize(9)
|
||||
UdsActorSetupDialog.setFont(font)
|
||||
UdsActorSetupDialog.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu)
|
||||
icon = QtGui.QIcon()
|
||||
icon.addPixmap(QtGui.QPixmap(":/img/img/uds-icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
UdsActorSetupDialog.setWindowIcon(icon)
|
||||
UdsActorSetupDialog.setAutoFillBackground(False)
|
||||
UdsActorSetupDialog.setLocale(QtCore.QLocale(QtCore.QLocale.English, QtCore.QLocale.UnitedStates))
|
||||
UdsActorSetupDialog.setSizeGripEnabled(False)
|
||||
UdsActorSetupDialog.setModal(True)
|
||||
self.registerButton = QtWidgets.QPushButton(UdsActorSetupDialog)
|
||||
self.registerButton.setEnabled(False)
|
||||
self.registerButton.setGeometry(QtCore.QRect(10, 270, 181, 23))
|
||||
self.registerButton.setMinimumSize(QtCore.QSize(181, 0))
|
||||
self.registerButton.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu)
|
||||
self.registerButton.setObjectName("registerButton")
|
||||
self.closeButton = QtWidgets.QPushButton(UdsActorSetupDialog)
|
||||
self.closeButton.setGeometry(QtCore.QRect(410, 270, 171, 23))
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.closeButton.sizePolicy().hasHeightForWidth())
|
||||
self.closeButton.setSizePolicy(sizePolicy)
|
||||
self.closeButton.setMinimumSize(QtCore.QSize(171, 0))
|
||||
self.closeButton.setObjectName("closeButton")
|
||||
self.tabWidget = QtWidgets.QTabWidget(UdsActorSetupDialog)
|
||||
self.tabWidget.setGeometry(QtCore.QRect(10, 10, 571, 241))
|
||||
self.tabWidget.setObjectName("tabWidget")
|
||||
self.tab_uds = QtWidgets.QWidget()
|
||||
self.tab_uds.setObjectName("tab_uds")
|
||||
self.layoutWidget = QtWidgets.QWidget(self.tab_uds)
|
||||
self.layoutWidget.setGeometry(QtCore.QRect(10, 10, 551, 191))
|
||||
self.layoutWidget.setObjectName("layoutWidget")
|
||||
self.formLayout = QtWidgets.QFormLayout(self.layoutWidget)
|
||||
self.formLayout.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
|
||||
self.formLayout.setFieldGrowthPolicy(QtWidgets.QFormLayout.AllNonFixedFieldsGrow)
|
||||
self.formLayout.setContentsMargins(0, 0, 0, 0)
|
||||
self.formLayout.setVerticalSpacing(16)
|
||||
self.formLayout.setObjectName("formLayout")
|
||||
self.label_host = QtWidgets.QLabel(self.layoutWidget)
|
||||
self.label_host.setObjectName("label_host")
|
||||
self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_host)
|
||||
self.host = QtWidgets.QLineEdit(self.layoutWidget)
|
||||
self.host.setAcceptDrops(False)
|
||||
self.host.setObjectName("host")
|
||||
self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.host)
|
||||
self.label_auth = QtWidgets.QLabel(self.layoutWidget)
|
||||
self.label_auth.setObjectName("label_auth")
|
||||
self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_auth)
|
||||
self.authenticators = QtWidgets.QComboBox(self.layoutWidget)
|
||||
self.authenticators.setObjectName("authenticators")
|
||||
self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.authenticators)
|
||||
self.label_username = QtWidgets.QLabel(self.layoutWidget)
|
||||
self.label_username.setObjectName("label_username")
|
||||
self.formLayout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.label_username)
|
||||
self.username = QtWidgets.QLineEdit(self.layoutWidget)
|
||||
self.username.setObjectName("username")
|
||||
self.formLayout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.username)
|
||||
self.label_password = QtWidgets.QLabel(self.layoutWidget)
|
||||
self.label_password.setObjectName("label_password")
|
||||
self.formLayout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.label_password)
|
||||
self.password = QtWidgets.QLineEdit(self.layoutWidget)
|
||||
self.password.setEchoMode(QtWidgets.QLineEdit.Password)
|
||||
self.password.setObjectName("password")
|
||||
self.formLayout.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.password)
|
||||
self.validateCertificate = QtWidgets.QComboBox(self.layoutWidget)
|
||||
self.validateCertificate.setObjectName("validateCertificate")
|
||||
self.validateCertificate.addItem("")
|
||||
self.validateCertificate.addItem("")
|
||||
self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.validateCertificate)
|
||||
self.label_security = QtWidgets.QLabel(self.layoutWidget)
|
||||
self.label_security.setObjectName("label_security")
|
||||
self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_security)
|
||||
self.label_host.raise_()
|
||||
self.host.raise_()
|
||||
self.label_auth.raise_()
|
||||
self.label_username.raise_()
|
||||
self.username.raise_()
|
||||
self.label_password.raise_()
|
||||
self.password.raise_()
|
||||
self.validateCertificate.raise_()
|
||||
self.label_security.raise_()
|
||||
self.authenticators.raise_()
|
||||
self.tabWidget.addTab(self.tab_uds, "")
|
||||
self.tab_advanced = QtWidgets.QWidget()
|
||||
self.tab_advanced.setObjectName("tab_advanced")
|
||||
self.layoutWidget_2 = QtWidgets.QWidget(self.tab_advanced)
|
||||
self.layoutWidget_2.setGeometry(QtCore.QRect(10, 10, 551, 161))
|
||||
self.layoutWidget_2.setObjectName("layoutWidget_2")
|
||||
self.formLayout_2 = QtWidgets.QFormLayout(self.layoutWidget_2)
|
||||
self.formLayout_2.setFieldGrowthPolicy(QtWidgets.QFormLayout.AllNonFixedFieldsGrow)
|
||||
self.formLayout_2.setContentsMargins(0, 0, 0, 0)
|
||||
self.formLayout_2.setVerticalSpacing(16)
|
||||
self.formLayout_2.setObjectName("formLayout_2")
|
||||
self.label_host_2 = QtWidgets.QLabel(self.layoutWidget_2)
|
||||
self.label_host_2.setObjectName("label_host_2")
|
||||
self.formLayout_2.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_host_2)
|
||||
self.horizontalLayout = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout.setContentsMargins(-1, 0, -1, -1)
|
||||
self.horizontalLayout.setSpacing(4)
|
||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||
self.preCommand = QtWidgets.QLineEdit(self.layoutWidget_2)
|
||||
self.preCommand.setAcceptDrops(False)
|
||||
self.preCommand.setWhatsThis("")
|
||||
self.preCommand.setObjectName("preCommand")
|
||||
self.horizontalLayout.addWidget(self.preCommand)
|
||||
self.browsePreconnectButton = QtWidgets.QPushButton(self.layoutWidget_2)
|
||||
self.browsePreconnectButton.setAutoDefault(False)
|
||||
self.browsePreconnectButton.setFlat(False)
|
||||
self.browsePreconnectButton.setObjectName("browsePreconnectButton")
|
||||
self.horizontalLayout.addWidget(self.browsePreconnectButton)
|
||||
self.formLayout_2.setLayout(0, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout)
|
||||
self.label_username_2 = QtWidgets.QLabel(self.layoutWidget_2)
|
||||
self.label_username_2.setObjectName("label_username_2")
|
||||
self.formLayout_2.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_username_2)
|
||||
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout_2.setContentsMargins(-1, 0, -1, -1)
|
||||
self.horizontalLayout_2.setSpacing(4)
|
||||
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
|
||||
self.runonceCommand = QtWidgets.QLineEdit(self.layoutWidget_2)
|
||||
self.runonceCommand.setWhatsThis("")
|
||||
self.runonceCommand.setObjectName("runonceCommand")
|
||||
self.horizontalLayout_2.addWidget(self.runonceCommand)
|
||||
self.browseRunOnceButton = QtWidgets.QPushButton(self.layoutWidget_2)
|
||||
self.browseRunOnceButton.setAutoDefault(False)
|
||||
self.browseRunOnceButton.setObjectName("browseRunOnceButton")
|
||||
self.horizontalLayout_2.addWidget(self.browseRunOnceButton)
|
||||
self.formLayout_2.setLayout(1, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout_2)
|
||||
self.label_password_2 = QtWidgets.QLabel(self.layoutWidget_2)
|
||||
self.label_password_2.setObjectName("label_password_2")
|
||||
self.formLayout_2.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_password_2)
|
||||
self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout_3.setContentsMargins(-1, 0, -1, -1)
|
||||
self.horizontalLayout_3.setSpacing(4)
|
||||
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
|
||||
self.postConfigCommand = QtWidgets.QLineEdit(self.layoutWidget_2)
|
||||
self.postConfigCommand.setWhatsThis("")
|
||||
self.postConfigCommand.setEchoMode(QtWidgets.QLineEdit.Normal)
|
||||
self.postConfigCommand.setObjectName("postConfigCommand")
|
||||
self.horizontalLayout_3.addWidget(self.postConfigCommand)
|
||||
self.browsePostConfigButton = QtWidgets.QPushButton(self.layoutWidget_2)
|
||||
self.browsePostConfigButton.setAutoDefault(False)
|
||||
self.browsePostConfigButton.setObjectName("browsePostConfigButton")
|
||||
self.horizontalLayout_3.addWidget(self.browsePostConfigButton)
|
||||
self.formLayout_2.setLayout(2, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout_3)
|
||||
self.label_loglevel = QtWidgets.QLabel(self.layoutWidget_2)
|
||||
self.label_loglevel.setObjectName("label_loglevel")
|
||||
self.formLayout_2.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.label_loglevel)
|
||||
self.logLevelComboBox = QtWidgets.QComboBox(self.layoutWidget_2)
|
||||
self.logLevelComboBox.setFrame(True)
|
||||
self.logLevelComboBox.setObjectName("logLevelComboBox")
|
||||
self.logLevelComboBox.addItem("")
|
||||
self.logLevelComboBox.setItemText(0, "DEBUG")
|
||||
self.logLevelComboBox.addItem("")
|
||||
self.logLevelComboBox.setItemText(1, "INFO")
|
||||
self.logLevelComboBox.addItem("")
|
||||
self.logLevelComboBox.setItemText(2, "ERROR")
|
||||
self.logLevelComboBox.addItem("")
|
||||
self.logLevelComboBox.setItemText(3, "FATAL")
|
||||
self.formLayout_2.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.logLevelComboBox)
|
||||
self.tabWidget.addTab(self.tab_advanced, "")
|
||||
self.testButton = QtWidgets.QPushButton(UdsActorSetupDialog)
|
||||
self.testButton.setEnabled(False)
|
||||
self.testButton.setGeometry(QtCore.QRect(210, 270, 181, 23))
|
||||
self.testButton.setMinimumSize(QtCore.QSize(181, 0))
|
||||
self.testButton.setObjectName("testButton")
|
||||
|
||||
self.retranslateUi(UdsActorSetupDialog)
|
||||
self.tabWidget.setCurrentIndex(0)
|
||||
self.logLevelComboBox.setCurrentIndex(1)
|
||||
self.closeButton.clicked.connect(UdsActorSetupDialog.finish)
|
||||
self.registerButton.clicked.connect(UdsActorSetupDialog.registerWithUDS)
|
||||
self.host.textChanged['QString'].connect(UdsActorSetupDialog.textChanged)
|
||||
self.username.textChanged['QString'].connect(UdsActorSetupDialog.textChanged)
|
||||
self.password.textChanged['QString'].connect(UdsActorSetupDialog.textChanged)
|
||||
self.browsePreconnectButton.clicked.connect(UdsActorSetupDialog.browsePreconnect)
|
||||
self.browsePostConfigButton.clicked.connect(UdsActorSetupDialog.browsePostConfig)
|
||||
self.browseRunOnceButton.clicked.connect(UdsActorSetupDialog.browseRunOnce)
|
||||
self.host.editingFinished.connect(UdsActorSetupDialog.updateAuthenticators)
|
||||
self.authenticators.currentTextChanged['QString'].connect(UdsActorSetupDialog.textChanged)
|
||||
self.testButton.clicked.connect(UdsActorSetupDialog.testUDSServer)
|
||||
QtCore.QMetaObject.connectSlotsByName(UdsActorSetupDialog)
|
||||
|
||||
def retranslateUi(self, UdsActorSetupDialog):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
UdsActorSetupDialog.setWindowTitle(_translate("UdsActorSetupDialog", "UDS Actor Configuration Tool"))
|
||||
self.registerButton.setToolTip(_translate("UdsActorSetupDialog", "Click to register Actor with UDS Broker"))
|
||||
self.registerButton.setWhatsThis(_translate("UdsActorSetupDialog", "<html><head/><body><p>Click on this button to register Actor with UDS Broker.</p></body></html>"))
|
||||
self.registerButton.setText(_translate("UdsActorSetupDialog", "Register with UDS"))
|
||||
self.closeButton.setToolTip(_translate("UdsActorSetupDialog", "Closes UDS Actor Configuration (discard pending changes if any)"))
|
||||
self.closeButton.setWhatsThis(_translate("UdsActorSetupDialog", "<html><head/><body><p>Exits the UDS Actor Configuration Tool</p></body></html>"))
|
||||
self.closeButton.setText(_translate("UdsActorSetupDialog", "Close"))
|
||||
self.label_host.setText(_translate("UdsActorSetupDialog", "UDS Server"))
|
||||
self.host.setToolTip(_translate("UdsActorSetupDialog", "Uds Broker Server Addres. Use IP or FQDN"))
|
||||
self.host.setWhatsThis(_translate("UdsActorSetupDialog", "Enter here the UDS Broker Addres using either its IP address or its FQDN address"))
|
||||
self.label_auth.setText(_translate("UdsActorSetupDialog", "Authenticator"))
|
||||
self.authenticators.setWhatsThis(_translate("UdsActorSetupDialog", "<html><head/><body><p>Select the UDS Broker authenticator for credentials validation</p></body></html>"))
|
||||
self.label_username.setText(_translate("UdsActorSetupDialog", "Username"))
|
||||
self.username.setToolTip(_translate("UdsActorSetupDialog", "UDS user with administration rights (Will not be stored on template)"))
|
||||
self.username.setWhatsThis(_translate("UdsActorSetupDialog", "<html><head/><body><p>Administrator user on UDS Server.</p><p>Note: This credential will not be stored on client. Will be used to obtain an unique token for this image.</p></body></html>"))
|
||||
self.label_password.setText(_translate("UdsActorSetupDialog", "Password"))
|
||||
self.password.setToolTip(_translate("UdsActorSetupDialog", "Password for user (Will not be stored on template)"))
|
||||
self.password.setWhatsThis(_translate("UdsActorSetupDialog", "<html><head/><body><p>Administrator password for the user on UDS Server.</p><p>Note: This credential will not be stored on client. Will be used to obtain an unique key for this image.</p></body></html>"))
|
||||
self.validateCertificate.setToolTip(_translate("UdsActorSetupDialog", "Select communication security with broker"))
|
||||
self.validateCertificate.setWhatsThis(_translate("UdsActorSetupDialog", "<html><head/><body><p>Select the security for communications with UDS Broker.</p><p>The recommended method of communication is <span style=\" font-weight:600;\">Use SSL</span>, but selection needs to be acording to your broker configuration.</p></body></html>"))
|
||||
self.validateCertificate.setItemText(0, _translate("UdsActorSetupDialog", "Ignore certificate"))
|
||||
self.validateCertificate.setItemText(1, _translate("UdsActorSetupDialog", "Verify certificate"))
|
||||
self.label_security.setText(_translate("UdsActorSetupDialog", "SSL Validation"))
|
||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_uds), _translate("UdsActorSetupDialog", "UDS Server"))
|
||||
self.label_host_2.setText(_translate("UdsActorSetupDialog", "Preconnect"))
|
||||
self.preCommand.setToolTip(_translate("UdsActorSetupDialog", "Pre connection command. Executed just before the user is connected to machine."))
|
||||
self.browsePreconnectButton.setText(_translate("UdsActorSetupDialog", "Browse"))
|
||||
self.label_username_2.setText(_translate("UdsActorSetupDialog", "Runonce"))
|
||||
self.runonceCommand.setToolTip(_translate("UdsActorSetupDialog", "Run once command. Executed on first boot, just before UDS does anything."))
|
||||
self.browseRunOnceButton.setText(_translate("UdsActorSetupDialog", "Browse"))
|
||||
self.label_password_2.setText(_translate("UdsActorSetupDialog", "Postconfig"))
|
||||
self.postConfigCommand.setToolTip(_translate("UdsActorSetupDialog", "Command to execute after UDS finalizes the VM configuration."))
|
||||
self.browsePostConfigButton.setText(_translate("UdsActorSetupDialog", "Browse"))
|
||||
self.label_loglevel.setText(_translate("UdsActorSetupDialog", "Log Level"))
|
||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_advanced), _translate("UdsActorSetupDialog", "Advanced"))
|
||||
self.testButton.setToolTip(_translate("UdsActorSetupDialog", "Click to test existing configuration (disabled if no config found)"))
|
||||
self.testButton.setWhatsThis(_translate("UdsActorSetupDialog", "<html><head/><body><p>Click on this button to test the server host and assigned toen.</p></body></html>"))
|
||||
self.testButton.setText(_translate("UdsActorSetupDialog", "Test configuration"))
|
||||
from ui import uds_rc
|
@@ -1,155 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file 'setup-dialog-unmanaged.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.15.2
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_UdsActorSetupDialog(object):
|
||||
def setupUi(self, UdsActorSetupDialog):
|
||||
UdsActorSetupDialog.setObjectName("UdsActorSetupDialog")
|
||||
UdsActorSetupDialog.setWindowModality(QtCore.Qt.WindowModal)
|
||||
UdsActorSetupDialog.resize(601, 243)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(UdsActorSetupDialog.sizePolicy().hasHeightForWidth())
|
||||
UdsActorSetupDialog.setSizePolicy(sizePolicy)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Verdana")
|
||||
font.setPointSize(9)
|
||||
UdsActorSetupDialog.setFont(font)
|
||||
UdsActorSetupDialog.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu)
|
||||
icon = QtGui.QIcon()
|
||||
icon.addPixmap(QtGui.QPixmap(":/img/img/uds-icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
UdsActorSetupDialog.setWindowIcon(icon)
|
||||
UdsActorSetupDialog.setAutoFillBackground(False)
|
||||
UdsActorSetupDialog.setLocale(QtCore.QLocale(QtCore.QLocale.English, QtCore.QLocale.UnitedStates))
|
||||
UdsActorSetupDialog.setSizeGripEnabled(False)
|
||||
UdsActorSetupDialog.setModal(True)
|
||||
self.saveButton = QtWidgets.QPushButton(UdsActorSetupDialog)
|
||||
self.saveButton.setEnabled(True)
|
||||
self.saveButton.setGeometry(QtCore.QRect(10, 210, 181, 23))
|
||||
self.saveButton.setMinimumSize(QtCore.QSize(181, 0))
|
||||
self.saveButton.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu)
|
||||
self.saveButton.setObjectName("saveButton")
|
||||
self.closeButton = QtWidgets.QPushButton(UdsActorSetupDialog)
|
||||
self.closeButton.setGeometry(QtCore.QRect(410, 210, 171, 23))
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.closeButton.sizePolicy().hasHeightForWidth())
|
||||
self.closeButton.setSizePolicy(sizePolicy)
|
||||
self.closeButton.setMinimumSize(QtCore.QSize(171, 0))
|
||||
self.closeButton.setObjectName("closeButton")
|
||||
self.testButton = QtWidgets.QPushButton(UdsActorSetupDialog)
|
||||
self.testButton.setEnabled(False)
|
||||
self.testButton.setGeometry(QtCore.QRect(210, 210, 181, 23))
|
||||
self.testButton.setMinimumSize(QtCore.QSize(181, 0))
|
||||
self.testButton.setObjectName("testButton")
|
||||
self.layoutWidget = QtWidgets.QWidget(UdsActorSetupDialog)
|
||||
self.layoutWidget.setGeometry(QtCore.QRect(10, 10, 571, 191))
|
||||
self.layoutWidget.setObjectName("layoutWidget")
|
||||
self.formLayout = QtWidgets.QFormLayout(self.layoutWidget)
|
||||
self.formLayout.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
|
||||
self.formLayout.setFieldGrowthPolicy(QtWidgets.QFormLayout.AllNonFixedFieldsGrow)
|
||||
self.formLayout.setContentsMargins(0, 0, 0, 0)
|
||||
self.formLayout.setVerticalSpacing(16)
|
||||
self.formLayout.setObjectName("formLayout")
|
||||
self.label_security = QtWidgets.QLabel(self.layoutWidget)
|
||||
self.label_security.setObjectName("label_security")
|
||||
self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_security)
|
||||
self.validateCertificate = QtWidgets.QComboBox(self.layoutWidget)
|
||||
self.validateCertificate.setObjectName("validateCertificate")
|
||||
self.validateCertificate.addItem("")
|
||||
self.validateCertificate.addItem("")
|
||||
self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.validateCertificate)
|
||||
self.label_host = QtWidgets.QLabel(self.layoutWidget)
|
||||
self.label_host.setObjectName("label_host")
|
||||
self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_host)
|
||||
self.host = QtWidgets.QLineEdit(self.layoutWidget)
|
||||
self.host.setAcceptDrops(False)
|
||||
self.host.setObjectName("host")
|
||||
self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.host)
|
||||
self.label_serviceToken = QtWidgets.QLabel(self.layoutWidget)
|
||||
self.label_serviceToken.setObjectName("label_serviceToken")
|
||||
self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_serviceToken)
|
||||
self.serviceToken = QtWidgets.QLineEdit(self.layoutWidget)
|
||||
self.serviceToken.setObjectName("serviceToken")
|
||||
self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.serviceToken)
|
||||
self.label_loglevel = QtWidgets.QLabel(self.layoutWidget)
|
||||
self.label_loglevel.setObjectName("label_loglevel")
|
||||
self.formLayout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.label_loglevel)
|
||||
self.logLevelComboBox = QtWidgets.QComboBox(self.layoutWidget)
|
||||
self.logLevelComboBox.setFrame(True)
|
||||
self.logLevelComboBox.setObjectName("logLevelComboBox")
|
||||
self.logLevelComboBox.addItem("")
|
||||
self.logLevelComboBox.setItemText(0, "DEBUG")
|
||||
self.logLevelComboBox.addItem("")
|
||||
self.logLevelComboBox.setItemText(1, "INFO")
|
||||
self.logLevelComboBox.addItem("")
|
||||
self.logLevelComboBox.setItemText(2, "ERROR")
|
||||
self.logLevelComboBox.addItem("")
|
||||
self.logLevelComboBox.setItemText(3, "FATAL")
|
||||
self.formLayout.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.logLevelComboBox)
|
||||
self.label_restrictNet = QtWidgets.QLabel(self.layoutWidget)
|
||||
self.label_restrictNet.setObjectName("label_restrictNet")
|
||||
self.formLayout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.label_restrictNet)
|
||||
self.restrictNet = QtWidgets.QLineEdit(self.layoutWidget)
|
||||
self.restrictNet.setObjectName("restrictNet")
|
||||
self.formLayout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.restrictNet)
|
||||
self.label_host.raise_()
|
||||
self.host.raise_()
|
||||
self.label_serviceToken.raise_()
|
||||
self.serviceToken.raise_()
|
||||
self.validateCertificate.raise_()
|
||||
self.label_security.raise_()
|
||||
self.label_loglevel.raise_()
|
||||
self.logLevelComboBox.raise_()
|
||||
self.label_restrictNet.raise_()
|
||||
self.restrictNet.raise_()
|
||||
|
||||
self.retranslateUi(UdsActorSetupDialog)
|
||||
self.logLevelComboBox.setCurrentIndex(1)
|
||||
self.closeButton.clicked.connect(UdsActorSetupDialog.finish)
|
||||
self.testButton.clicked.connect(UdsActorSetupDialog.testUDSServer)
|
||||
self.saveButton.clicked.connect(UdsActorSetupDialog.saveConfig)
|
||||
self.host.textChanged['QString'].connect(UdsActorSetupDialog.configChanged)
|
||||
self.serviceToken.textChanged['QString'].connect(UdsActorSetupDialog.configChanged)
|
||||
self.restrictNet.textChanged['QString'].connect(UdsActorSetupDialog.configChanged)
|
||||
QtCore.QMetaObject.connectSlotsByName(UdsActorSetupDialog)
|
||||
|
||||
def retranslateUi(self, UdsActorSetupDialog):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
UdsActorSetupDialog.setWindowTitle(_translate("UdsActorSetupDialog", "UDS Actor Configuration Tool"))
|
||||
self.saveButton.setToolTip(_translate("UdsActorSetupDialog", "Click to register Actor with UDS Broker"))
|
||||
self.saveButton.setWhatsThis(_translate("UdsActorSetupDialog", "<html><head/><body><p>Click on this button to register Actor with UDS Broker.</p></body></html>"))
|
||||
self.saveButton.setText(_translate("UdsActorSetupDialog", "Save Configuration"))
|
||||
self.closeButton.setToolTip(_translate("UdsActorSetupDialog", "Closes UDS Actor Configuration (discard pending changes if any)"))
|
||||
self.closeButton.setWhatsThis(_translate("UdsActorSetupDialog", "<html><head/><body><p>Exits the UDS Actor Configuration Tool</p></body></html>"))
|
||||
self.closeButton.setText(_translate("UdsActorSetupDialog", "Close"))
|
||||
self.testButton.setToolTip(_translate("UdsActorSetupDialog", "Click to test existing configuration (disabled if no config found)"))
|
||||
self.testButton.setWhatsThis(_translate("UdsActorSetupDialog", "<html><head/><body><p>Click on this button to test the server host and assigned toen.</p></body></html>"))
|
||||
self.testButton.setText(_translate("UdsActorSetupDialog", "Test configuration"))
|
||||
self.label_security.setText(_translate("UdsActorSetupDialog", "SSL Validation"))
|
||||
self.validateCertificate.setToolTip(_translate("UdsActorSetupDialog", "Select communication security with broker"))
|
||||
self.validateCertificate.setWhatsThis(_translate("UdsActorSetupDialog", "<html><head/><body><p>Select the security for communications with UDS Broker.</p><p>The recommended method of communication is <span style=\" font-weight:600;\">Use SSL</span>, but selection needs to be acording to your broker configuration.</p></body></html>"))
|
||||
self.validateCertificate.setItemText(0, _translate("UdsActorSetupDialog", "Ignore certificate"))
|
||||
self.validateCertificate.setItemText(1, _translate("UdsActorSetupDialog", "Verify certificate"))
|
||||
self.label_host.setText(_translate("UdsActorSetupDialog", "UDS Server"))
|
||||
self.host.setToolTip(_translate("UdsActorSetupDialog", "Uds Broker Server Addres. Use IP or FQDN"))
|
||||
self.host.setWhatsThis(_translate("UdsActorSetupDialog", "Enter here the UDS Broker Addres using either its IP address or its FQDN address"))
|
||||
self.label_serviceToken.setText(_translate("UdsActorSetupDialog", "Service Token"))
|
||||
self.serviceToken.setToolTip(_translate("UdsActorSetupDialog", "UDS user with administration rights (Will not be stored on template)"))
|
||||
self.serviceToken.setWhatsThis(_translate("UdsActorSetupDialog", "<html><head/><body><p>Administrator user on UDS Server.</p><p>Note: This credential will not be stored on client. Will be used to obtain an unique token for this image.</p></body></html>"))
|
||||
self.label_loglevel.setText(_translate("UdsActorSetupDialog", "Log Level"))
|
||||
self.label_restrictNet.setText(_translate("UdsActorSetupDialog", "Restrict Net"))
|
||||
self.restrictNet.setToolTip(_translate("UdsActorSetupDialog", "UDS user with administration rights (Will not be stored on template)"))
|
||||
self.restrictNet.setWhatsThis(_translate("UdsActorSetupDialog", "<html><head/><body><p>Administrator user on UDS Server.</p><p>Note: This credential will not be stored on client. Will be used to obtain an unique token for this image.</p></body></html>"))
|
||||
from ui import uds_rc
|
@@ -1,195 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Resource object code
|
||||
#
|
||||
# Created by: The Resource Compiler for PyQt5 (Qt v5.15.2)
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
from PyQt5 import QtCore
|
||||
|
||||
qt_resource_data = b"\
|
||||
\x00\x00\x08\x7c\
|
||||
\x89\
|
||||
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
|
||||
\x00\x00\x30\x00\x00\x00\x30\x08\x06\x00\x00\x00\x57\x02\xf9\x87\
|
||||
\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0b\x13\x00\x00\x0b\x13\
|
||||
\x01\x00\x9a\x9c\x18\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xe2\x0a\
|
||||
\x04\x07\x27\x0a\x6d\xd5\xd2\x21\x00\x00\x08\x1b\x49\x44\x41\x54\
|
||||
\x68\xde\xed\x9a\x6d\x8c\x54\xd5\x19\xc7\x7f\xe7\xbe\xee\x2e\xfb\
|
||||
\xbe\x3a\x60\x2d\x52\xd6\x62\xac\x52\x23\x68\xc7\x97\xb4\x46\x8c\
|
||||
\xca\x04\x6c\x2d\xad\x56\x4d\x5d\x5a\x9b\x5a\x8d\xfd\x60\xd2\x12\
|
||||
\x9b\x94\x56\xb4\x76\xc4\xa6\x68\xda\x68\xb5\x04\x62\x90\xd1\xb8\
|
||||
\xc1\x12\xab\x06\x98\x6a\x33\x60\x41\x83\x53\x17\x50\x16\x10\xa5\
|
||||
\xb2\x14\xd4\x65\x16\x96\x65\x67\x77\xe6\xce\x7d\x3b\xfd\xb0\xb3\
|
||||
\xeb\xec\xee\xec\xb0\xb3\x77\xfa\xa1\x49\xff\xc9\x64\x66\xce\xb9\
|
||||
\xf7\x9e\xe7\xbf\xcf\xcb\x79\xce\x7f\x16\xfe\xc7\x21\x2a\xfd\xc0\
|
||||
\x78\x32\xa4\x01\x9a\xa2\xa0\x9d\xdf\xaa\x29\x5f\x68\xd2\x94\x23\
|
||||
\xdd\x8e\x78\x27\xe9\xf0\xc1\x87\x0e\xdd\x3d\x3e\x5d\x47\x1c\x5f\
|
||||
\x0c\xad\xec\x00\xd9\x44\xbb\x25\xff\xeb\x04\xe2\xc9\x90\x02\xa8\
|
||||
\x80\x96\x7f\x3f\x07\x58\x04\x2c\x00\xae\x04\xa6\x0f\x5f\xeb\x79\
|
||||
\xe0\x38\x92\x5d\x7b\x6c\xf6\x76\x3a\x9c\xea\xf3\xc9\x1b\x3c\xf2\
|
||||
\x5e\x04\x27\x80\x38\x10\x03\x76\x00\x1e\x60\x9f\x89\x9c\x98\x84\
|
||||
\xe1\x17\x00\xeb\x81\x56\xa0\x2e\x4f\x40\x2b\x76\xad\x94\xb0\x7a\
|
||||
\x6d\x9a\xf4\x80\x44\x4a\x80\xe1\xb5\x25\x82\xcf\xed\x90\x88\x31\
|
||||
\x4b\x17\x35\xc3\x06\xfa\x81\x87\x13\xed\xd6\x53\x65\x11\x88\x27\
|
||||
\x43\x73\x81\x35\xc0\x45\x40\xfd\x64\x3c\x34\x38\x28\xf9\xd3\xea\
|
||||
\x34\x00\x86\xb4\x38\x6a\xdc\x43\xbf\x3a\x1f\x57\x34\xe2\x63\x22\
|
||||
\x45\x01\x67\xcf\xc6\x6c\xa8\x47\xaf\x56\x51\xdc\x5e\x0c\xb7\x9b\
|
||||
\xba\x81\xd7\x69\xe8\x7f\x05\x09\x78\xaa\x39\xd6\xb4\xd3\xc0\xef\
|
||||
\x12\xed\xd6\xca\x33\x12\x88\x27\x43\x6b\x80\x1f\x97\x1b\x8b\x07\
|
||||
\x3e\x70\x78\x6d\xd3\x20\xbd\xfa\xcd\x74\x99\x0f\x20\x51\x11\xb8\
|
||||
\x05\x5e\x18\x0e\x21\x81\x7e\xf6\x2c\x14\x73\x1a\x79\x37\x21\x51\
|
||||
\x90\x8a\x81\x14\x1a\xd5\xd6\x01\x6a\x32\x6f\x51\x3b\x98\xa0\xb9\
|
||||
\xef\x45\xdc\xd1\x64\xfe\x96\x68\xb7\x22\x13\x12\x88\x27\x43\xcf\
|
||||
\x02\x77\x4d\x25\x99\x8e\xa7\x3c\x9e\x59\x57\xc5\xfe\x86\x2d\x28\
|
||||
\x64\x8b\xbb\x5b\xd3\x30\x67\xcc\x99\x54\x6a\x4a\x61\x20\xa4\xc7\
|
||||
\x79\x9f\xdc\x49\x5d\xfa\x15\xa4\xd0\x87\x27\xb7\x27\xda\xad\x6b\
|
||||
\x86\xbf\xa8\xc3\x1f\x36\x6e\x3b\xcb\x30\x74\xf1\x62\xe1\x58\x39\
|
||||
\x68\x6e\x52\x39\xd1\xdf\xc4\xbe\x53\xdf\x42\x88\xf1\x79\x27\x84\
|
||||
\x82\x79\xee\x85\xe3\x3c\x32\x31\x05\x0f\xf0\xe9\x6b\xb8\x15\x44\
|
||||
\x15\xb5\x99\x04\xa0\x00\xcc\x9a\x3d\x57\x7b\xfe\x70\xa7\x7b\x8a\
|
||||
\xe1\x11\x80\xf5\x2f\x9f\x52\xb3\x96\xaf\x4c\xb5\x9c\xd9\xb6\xe4\
|
||||
\x96\x85\x29\x66\x36\x1d\x2d\x3a\x6f\x9c\x7b\x01\x48\xbf\xfc\x32\
|
||||
\x29\x1d\x52\x67\xfd\x9c\xbe\x86\xa5\x85\xc3\xef\x0f\x7f\x18\x31\
|
||||
\xb8\xbe\x56\x91\x1b\xe3\xa7\xa5\xe7\x4d\xb9\x24\xe3\x49\x85\x55\
|
||||
\xb7\xdd\x4b\x8d\x6e\x15\xd6\x26\xb4\xda\x66\x44\x80\x2d\x47\xc8\
|
||||
\x1c\x3d\x2d\xcb\x50\xbd\x91\xe7\xd6\x5c\x77\x7b\x55\xf3\x28\x02\
|
||||
\x40\x4e\x08\x78\x7d\xc7\x00\x9a\x16\x64\x7f\x93\xac\xfe\xc1\x9d\
|
||||
\x05\x61\x24\xd0\x1a\xa7\x07\xde\x6f\x2d\xf3\x2b\xf8\x8a\x39\x2a\
|
||||
\x6a\x47\x11\x88\x45\x1d\x09\x2c\x3c\x79\xca\x65\x57\x67\x06\x3d\
|
||||
\x00\x09\x5d\xcd\xb1\x72\xc9\x32\x6c\xd7\x44\x28\x0a\x28\x0a\xc1\
|
||||
\xe1\x63\x9b\x5f\x2e\x1c\x30\xc7\x7a\x80\x58\xd4\xd9\x2a\x04\xbb\
|
||||
\x3a\x3a\xb3\x7c\x7c\xd4\x0e\xb4\xdc\xec\xd0\x41\xee\xbf\x7e\x15\
|
||||
\x96\x63\x80\x2f\xa9\x04\x14\x3f\x33\x7e\x6c\xec\x40\x2c\xea\x5c\
|
||||
\x66\xe8\x22\xb7\x75\x67\x1a\xcf\x0b\x10\x48\x52\x70\xc3\xc5\xaf\
|
||||
\x71\xfb\xfc\x17\xf0\x5c\xb7\x12\xe6\x53\x65\x1d\x2e\x1c\xc8\x16\
|
||||
\x25\x90\xc7\x52\x55\x11\xbc\xfc\x7a\x1f\x6a\x00\xef\xdb\xae\x49\
|
||||
\xdb\xd7\x57\x73\x51\x55\x3c\xb0\xf9\x86\x7b\x6c\x6c\x01\xee\x9f\
|
||||
\x90\x40\x2c\xea\x6c\x00\x7e\x9f\xb3\x25\x7f\xd9\xd2\x17\x68\x61\
|
||||
\xdb\x35\xf9\xe5\xc2\xfb\x69\xa9\x3e\x1e\xa8\x30\xe8\xf6\x11\x0a\
|
||||
\x8a\xbc\x5f\x92\x40\x9e\xc4\x03\xc0\xa7\x19\x4b\xf2\xee\xde\x6c\
|
||||
\xa0\x3c\x94\xc0\x13\xd7\x5d\x4d\xad\xde\x3f\x75\x0f\xd8\x87\x0a\
|
||||
\xab\x50\x6f\xa2\xdd\xb2\x4b\x12\xc8\xe3\x72\x80\x03\x87\x2c\x3e\
|
||||
\x3d\xee\xa0\x88\x20\x24\x04\x4f\x2f\xbc\x94\x46\xf3\x64\xbe\x1b\
|
||||
\x2d\xe7\x5e\x0d\xd3\x3e\x54\xd8\xf9\xfc\x71\xc2\x24\x1e\xe3\x85\
|
||||
\xcf\x80\x9b\x14\x05\x36\x6f\x4b\xd3\x3f\xe0\x05\x2b\x84\x52\xe5\
|
||||
\xa9\x1b\x2f\xa3\xb5\x7e\x3f\xae\xaf\x3b\x93\xdf\x06\x34\x4c\xfb\
|
||||
\xa3\xc2\x91\xc9\x11\xc8\x93\xd8\x54\x6d\xea\x47\x4c\x43\xb0\xf9\
|
||||
\xcd\x34\xaa\x4a\xa0\x58\x76\x7d\x8d\xe8\xb5\xd7\xf3\xdc\xa2\xd6\
|
||||
\x27\xbb\x0f\xd6\x4d\xcb\x9f\x33\x6e\x03\x36\x03\x5d\x45\xef\x12\
|
||||
\x2a\xc6\xe7\x04\x9c\xfc\xab\xf4\x81\x66\xdf\x91\xc5\x0a\x30\x0f\
|
||||
\x78\xd1\x30\xb4\x39\xab\xd6\xfe\x9d\x8c\x65\x13\x6a\xd1\x58\x78\
|
||||
\x4d\x1d\xbe\x4f\x25\xf0\x78\x24\x9c\x5a\x36\x76\x70\xc6\x8a\xfe\
|
||||
\x5b\x80\x47\x81\x39\x00\xbe\x52\xcb\xdc\x03\xf5\x80\x0b\x30\x00\
|
||||
\x34\x24\xda\x2d\x7f\x42\x02\xfb\x8e\x2c\x5e\x01\xfc\xaa\xf0\xe4\
|
||||
\x65\xdb\x69\x1e\x5b\xf3\x36\xba\x26\x68\x9d\x69\x70\xf5\x65\xd3\
|
||||
\x2a\x75\x8c\xfe\x75\x24\x9c\xfa\x6d\xb1\x89\x19\x2b\xfa\xeb\x80\
|
||||
\xcf\x84\x94\xd3\xbe\xba\xbf\x01\x57\xab\x02\xd8\x9d\x68\xb7\xe6\
|
||||
\x4f\x18\x42\x3f\x7c\xd0\x4c\xbe\xd5\xf1\xf1\x43\x9a\xaa\x68\x02\
|
||||
\x1f\x01\x6c\xda\x7b\x1f\x8f\xbe\x91\x40\x34\xaf\x44\x08\x87\x8f\
|
||||
\x8f\xda\x9c\xec\xf3\x2a\x45\xe0\x91\x78\x32\x74\x47\xb1\x89\xee\
|
||||
\x87\xeb\xd3\x9a\x7b\xac\xb9\x36\xb3\xd5\x2d\x28\xa1\xb1\x92\x27\
|
||||
\xb2\xb6\xe5\xfa\x33\x9e\xe7\xdf\x5b\x53\x05\xad\x97\x6e\xa0\xe3\
|
||||
\xd8\x77\xf0\x25\x28\x02\xa4\x80\xaa\x93\xb7\xa2\xe6\x5e\x45\x4a\
|
||||
\xc9\x1d\xdf\x6c\x44\x55\x2b\x26\x6c\xcc\x8f\x84\x53\xbb\x8b\x4d\
|
||||
\x5c\x75\xd7\x37\xb6\x54\x67\xdf\x89\x80\x42\xa2\xdd\x12\x25\x5b\
|
||||
\x09\x60\xad\xaa\x2a\xe4\x6c\x8f\xdd\x5d\xf3\x86\x2e\x1a\x56\x14\
|
||||
\x24\xe4\x9a\xd6\x01\x43\x2a\xc3\xc6\xf8\xe9\x4a\x2a\x32\xbb\xe2\
|
||||
\xc9\xd0\xf4\x78\x32\x34\xee\x2f\x52\x9d\x4d\xda\x79\x53\x8f\x4d\
|
||||
\xa6\x17\xea\x18\x99\x74\xde\x2a\x52\xd2\xa6\x91\x0d\x75\x81\x74\
|
||||
\x70\x5c\xc9\x3b\x7b\x32\x01\x2b\xd3\x28\xec\x8d\x84\x53\xb2\x68\
|
||||
\x1d\x85\x34\xb0\x7d\x52\xaa\x44\xdb\x72\xfd\x3d\xf0\x2f\xf1\xaa\
|
||||
\x96\x90\x6b\x7a\xa9\xa8\x96\xa1\xa7\x9f\x40\x4f\xff\x02\x5f\x0a\
|
||||
\xae\x9a\x57\xc3\x9c\x59\x26\x95\xe9\x39\xd9\x10\x09\xa7\x6e\x9b\
|
||||
\x7c\x8b\x57\x1c\x3b\x41\x41\x78\x5d\x13\xf6\x06\x4e\xed\xcf\x90\
|
||||
\xea\x6c\x14\x21\xf9\x47\x72\x90\x8c\xe5\x57\xca\x0b\xdf\x8b\x27\
|
||||
\x43\x37\x04\x25\xf0\x18\x80\xe2\x1e\x2a\x29\x7d\x65\xa7\x7f\x08\
|
||||
\xa2\x1e\xd3\x10\x6c\xde\xd6\x1f\xe8\x10\x34\x06\x7f\x0e\x44\x20\
|
||||
\x16\x75\x0e\x03\x5b\xf1\xfb\xd0\x06\xd7\x95\xec\xd2\xac\x96\x37\
|
||||
\x11\xd2\x21\x6b\x49\x36\x6f\xeb\xaf\xcc\xe1\x0b\x5a\xe3\xc9\xd0\
|
||||
\x45\x41\x3c\x00\xf0\x07\x84\x8e\x71\xfa\xbe\xbc\x4c\x39\x41\x7f\
|
||||
\xa3\xcf\xc5\xae\x7b\x08\x21\x1c\x8e\x9f\x70\xf9\xa8\xcb\xae\x94\
|
||||
\x17\xa2\x81\x08\xc4\xa2\xce\xab\x40\x0f\x38\xa8\xb9\x1d\xa5\x7b\
|
||||
\xfe\x86\x15\xf8\xfa\xd7\x10\x02\xde\xee\x18\xc0\xb6\x2b\x92\xce\
|
||||
\x97\x07\xf5\x00\xc0\xb5\x20\x30\x4f\x5c\x8b\x90\x13\xf7\xf2\xc2\
|
||||
\x87\x5c\xcb\x1b\x20\x1d\x14\x45\xf0\xd2\x96\xbe\x52\x2a\xf4\x64\
|
||||
\x51\x5f\x6c\x4f\x28\x8b\x40\x2c\xea\xec\x07\xb6\xa2\xe8\x18\x7d\
|
||||
\x3f\x2d\x99\xd0\x52\x34\x90\x0d\x75\x0e\x37\x5c\x74\x74\x66\x83\
|
||||
\xe6\xc3\xbf\x8a\xef\x09\xe5\x79\x00\xe0\x46\xc0\x57\xb3\xcf\xa3\
|
||||
\x65\x5e\x28\xdd\x2c\x6b\x17\xe3\xd6\x3e\x0a\x48\x3a\x0f\x66\xe9\
|
||||
\x4e\x05\x3a\xcc\x3f\x3d\x39\xc5\x68\x12\x68\x5b\xae\x5f\x02\xbc\
|
||||
\x07\x0e\x56\xcb\x4e\x7c\x23\x3c\xe1\xad\x52\x40\xcd\xf1\xf3\x11\
|
||||
\xde\xbf\x51\x55\x85\x3b\xbf\xdd\x84\xeb\x96\x9d\x13\x27\x23\xe1\
|
||||
\xd4\x59\x95\xc8\x81\xe1\x50\x7a\x1f\xf8\x0d\x18\x54\xf5\x46\x10\
|
||||
\xde\x67\x25\x64\x40\xb0\xce\xfe\x10\x30\x70\x5d\x49\xe2\xed\xf4\
|
||||
\x54\x42\x29\xf0\x46\x56\x8c\xc4\x0a\x90\x1b\x90\x19\xaa\x7b\x2e\
|
||||
\x44\xc8\x74\x89\x7c\x50\xc9\x35\xc5\x10\x38\x74\x7d\xe2\xd0\x7b\
|
||||
\xba\xac\xd6\x7b\x29\xb0\x67\xf2\xa2\x63\x99\x68\x5b\xae\xef\x04\
|
||||
\xae\x40\xba\x64\xcf\xe9\x47\x8a\xda\x09\xaf\x35\x7b\xbf\x8b\x9a\
|
||||
\x7b\x0d\x5d\x17\xdc\x71\x53\x23\xde\x99\xbb\x8d\x45\x91\x70\x6a\
|
||||
\x4b\x79\x72\x57\x99\x88\x45\x9d\x2b\x81\x83\x08\x8d\xea\xee\x46\
|
||||
\x84\xdf\x3d\xe1\xb5\x4e\xc3\xe3\x20\x1d\x6c\x5b\x72\xf8\xcc\x52\
|
||||
\xe5\xc5\xe5\x1a\x3f\x25\x02\x79\x12\x17\x0e\x1d\xc2\x15\xaa\x8f\
|
||||
\xb7\xa2\x59\x7f\x2d\xea\x4b\x4f\xff\x12\xd2\x18\xda\xe0\xb6\xbf\
|
||||
\x3b\x58\xca\xdd\x33\x23\xe1\xd4\xfe\xa9\x09\x8e\x53\x44\x2c\xea\
|
||||
\x2c\x06\x1e\x01\x17\xa3\x77\x09\x46\xdf\xdd\xe3\x9e\x26\x7c\x70\
|
||||
\xcd\xdb\x47\xbe\x0f\x64\xc6\xc5\xd0\x1e\xa0\x36\x12\x4e\x1d\x9b\
|
||||
\xba\x62\x1a\x00\xb1\xa8\xf3\x20\xf0\x13\x84\xee\x68\x99\x67\xa9\
|
||||
\xf9\xa4\x0e\xd5\xda\x34\xe2\x0d\xa9\x80\xe2\x6c\xfd\x5c\xcc\x1c\
|
||||
\xad\x2b\xad\x8b\x84\x53\xf3\x22\xe1\xd4\x60\x10\x1b\x2a\xd2\xff\
|
||||
\xb6\x2d\xd7\x67\x00\xff\x04\xbe\x38\x24\xd9\xd4\x90\x6b\x8c\xa1\
|
||||
\x59\xaf\xa2\x5a\xcf\x01\x43\x3f\xd0\x2d\x5e\x50\x4f\x63\xbd\x6a\
|
||||
\x03\x77\x47\xc2\xa9\xf5\x95\x58\xbb\xa2\xff\x6a\xd0\xb6\x5c\x5f\
|
||||
\x0a\xac\x1d\xb1\x78\xcc\x42\xdf\xbf\xb9\xd9\xf1\xa4\xdf\xb8\xe8\
|
||||
\x8a\x9e\x4c\xa5\xd6\x54\x2a\x49\x20\x16\x75\xd6\x33\xf4\x6b\xfe\
|
||||
\x8f\x46\x19\x2f\x60\xf1\x82\xfa\x65\xaa\x46\x75\x25\x8d\xaf\xb8\
|
||||
\x07\xc6\x78\x43\x07\xce\x93\x12\xf3\xfc\x59\x46\xcf\xc3\xf7\x0c\
|
||||
\xf6\xf0\x7f\x8c\xc7\x7f\x00\xf4\xc5\x17\xbc\x3a\x13\xef\x97\x00\
|
||||
\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
|
||||
"
|
||||
|
||||
qt_resource_name = b"\
|
||||
\x00\x03\
|
||||
\x00\x00\x70\x37\
|
||||
\x00\x69\
|
||||
\x00\x6d\x00\x67\
|
||||
\x00\x0c\
|
||||
\x09\x57\x90\xa7\
|
||||
\x00\x75\
|
||||
\x00\x64\x00\x73\x00\x2d\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\
|
||||
"
|
||||
|
||||
qt_resource_struct_v1 = b"\
|
||||
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
|
||||
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\
|
||||
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x03\
|
||||
\x00\x00\x00\x0c\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
|
||||
"
|
||||
|
||||
qt_resource_struct_v2 = b"\
|
||||
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
|
||||
\x00\x00\x00\x00\x00\x00\x00\x00\
|
||||
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\
|
||||
\x00\x00\x00\x00\x00\x00\x00\x00\
|
||||
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x03\
|
||||
\x00\x00\x00\x00\x00\x00\x00\x00\
|
||||
\x00\x00\x00\x0c\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
|
||||
\x00\x00\x01\x6e\x86\x31\xef\xa3\
|
||||
"
|
||||
|
||||
qt_version = [int(v) for v in QtCore.qVersion().split('.')]
|
||||
if qt_version < [5, 8, 0]:
|
||||
rcc_version = 1
|
||||
qt_resource_struct = qt_resource_struct_v1
|
||||
else:
|
||||
rcc_version = 2
|
||||
qt_resource_struct = qt_resource_struct_v2
|
||||
|
||||
def qInitResources():
|
||||
QtCore.qRegisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data)
|
||||
|
||||
def qCleanupResources():
|
||||
QtCore.qUnregisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data)
|
||||
|
||||
qInitResources()
|
1
client
Submodule
1
client
Submodule
Submodule client added at 5b044bca34
@@ -1,2 +0,0 @@
|
||||
PYTHONPATH=./src:${PYTHONPATH}
|
||||
|
4
client-py3/full/.gitignore
vendored
4
client-py3/full/.gitignore
vendored
@@ -1,4 +0,0 @@
|
||||
/bin
|
||||
/udsclient*
|
||||
/udsclient-*.tar.gz
|
||||
/*.rpm
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user