diff --git a/actor/src/actor_client.py b/actor/src/actor_client.py index 41a317d2..928e5988 100644 --- a/actor/src/actor_client.py +++ b/actor/src/actor_client.py @@ -37,7 +37,7 @@ import PyQt5 # pylint: disable=unused-import from PyQt5.QtWidgets import QApplication from udsactor.log import logger, DEBUG - +from udsactor.client import UDSActorClient if __name__ == "__main__": logger.setLevel(DEBUG) @@ -49,6 +49,12 @@ if __name__ == "__main__": QApplication.setQuitOnLastWindowClosed(False) - app = QApplication(sys.argv) + qApp = QApplication(sys.argv) # Execute backgroup thread for actions + app = UDSActorClient(qApp) + + app.start() + qApp.exec_() + + app.join() diff --git a/actor/src/udsactor/__init__.py b/actor/src/udsactor/__init__.py index febf3e81..630f9f4d 100644 --- a/actor/src/udsactor/__init__.py +++ b/actor/src/udsactor/__init__.py @@ -35,4 +35,4 @@ from . import platform __title__ = 'udsactor' __author__ = 'Adolfo Gómez ' __license__ = "BSD 3-clause" -__copyright__ = "Copyright 2014-2019 VirtualCable S.L.U." +__copyright__ = "Copyright 2014-2020 VirtualCable S.L.U." diff --git a/actor/src/udsactor/client.py b/actor/src/udsactor/client.py new file mode 100644 index 00000000..d09572b6 --- /dev/null +++ b/actor/src/udsactor/client.py @@ -0,0 +1,99 @@ +# -*- 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 typing + +from PyQt5.QtWidgets import QApplication, QMessageBox + +from . import rest +from . import tools +from . import platform + +from .log import logger + +# Not imported at runtime, just for type checking +if typing.TYPE_CHECKING: + from . import types + +class UDSActorClient(threading.Thread): + _running: bool + _forceLogoff: bool + _qApp: QApplication + _api: rest.UDSClientApi + + def __init__(self, qApp: QApplication): + super().__init__() + + self._api = rest.UDSClientApi() # Self initialized + self._qApp = qApp + self._running = False + self._forceLogoff = False + + def run(self): + self._running = True + + # Notify loging and mark it + self._api.login(platform.operations.getCurrentUser()) + + while self._running: + time.sleep(1.1) # Sleep between loop iteration + + self._api.logout(platform.operations.getCurrentUser()) + + # Notify Qapllication to exit + QApplication.quit() + + if self._forceLogoff: + platform.operations.loggoff() + + def _showMessage(self, message: str) -> None: + QMessageBox.information(None, 'Message', message) + + 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: + pass + + def script(self, script: str) -> typing.Any: + tools.ScriptExecutorThread(script).start() + return 'ok' diff --git a/actor/src/udsactor/http/client.py b/actor/src/udsactor/http/client.py index 7915ab84..59b8805e 100644 --- a/actor/src/udsactor/http/client.py +++ b/actor/src/udsactor/http/client.py @@ -38,7 +38,7 @@ from ..log import logger # Not imported at runtime, just for type checking if typing.TYPE_CHECKING: - UDSActorClient = typing.Any + from ..client import UDSActorClient class HTTPServerHandler(http.server.BaseHTTPRequestHandler): protocol_version = 'HTTP/1.0' @@ -101,6 +101,9 @@ class HTTPServerHandler(http.server.BaseHTTPRequestHandler): 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) diff --git a/actor/src/udsactor/http/server.py b/actor/src/udsactor/http/server.py index 66b446d4..8abd2645 100644 --- a/actor/src/udsactor/http/server.py +++ b/actor/src/udsactor/http/server.py @@ -37,6 +37,7 @@ import typing from ..log import logger from .. import certs +from .. import rest from .public import PublicProvider from .local import LocalProvider @@ -151,12 +152,12 @@ class HTTPServerThread(threading.Thread): 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(self._certFile, password=password) - - self._server = http.server.HTTPServer(('0.0.0.0', self._service._cfg.port), HTTPServerHandler) # pylint: disable=protected-access - # self._server.socket = ssl.wrap_socket(self._server.socket, certfile=self.certFile, server_side=True) self._server.socket = context.wrap_socket(self._server.socket, server_side=True) self._server.serve_forever() diff --git a/actor/src/udsactor/rest.py b/actor/src/udsactor/rest.py index 5cc4c0dd..877c7d55 100644 --- a/actor/src/udsactor/rest.py +++ b/actor/src/udsactor/rest.py @@ -39,6 +39,9 @@ import requests from . import types from .info import VERSION +# Default public listen port +LISTEN_PORT = 43910 + class RESTError(Exception): ERRCODE = 0 @@ -59,9 +62,9 @@ class RESTOsManagerError(RESTError): ERRCODE = 4 # +# Basic UDS Api # -# -class UDSApi: +class UDSApi: # pylint: disable=too-few-public-methods """ Base for remote api accesses """ @@ -258,6 +261,9 @@ class UDSServerApi(UDSApi): class UDSClientApi(UDSApi): + def __init__(self) -> None: + super().__init__('127.0.0.1:{}'.format(LISTEN_PORT), False) + def register(self, callbackUrl: str) -> None: payLoad = { 'callback_url': callbackUrl diff --git a/actor/src/udsactor/types.py b/actor/src/udsactor/types.py index 16627a70..4302cc5d 100644 --- a/actor/src/udsactor/types.py +++ b/actor/src/udsactor/types.py @@ -1,8 +1,5 @@ import typing -# Default public listen port -LISTEN_PORT = 43910 - class InterfaceInfoType(typing.NamedTuple): name: str mac: str @@ -41,8 +38,6 @@ class ActorConfigurationType(typing.NamedTuple): log_level: int = 0 - port: int = LISTEN_PORT - config: typing.Optional[ActorDataConfigurationType] = None data: typing.Optional[typing.Dict[str, typing.Any]] = None diff --git a/actor/src/udsactor/windows/operations.py b/actor/src/udsactor/windows/operations.py index 765bb2d4..410ac51c 100644 --- a/actor/src/udsactor/windows/operations.py +++ b/actor/src/udsactor/windows/operations.py @@ -208,12 +208,10 @@ def getIdleDuration() -> float: lastInputInfo.cbSize = ctypes.sizeof(lastInputInfo) # pylint: disable=attribute-defined-outside-init if ctypes.windll.user32.GetLastInputInfo(ctypes.byref(lastInputInfo)) == 0: return 0 - # if lastInputInfo.dwTime > 1000000000: # Value toooo high, nonsense... - # return 0 - current = ctypes.c_uint(ctypes.windll.kernel32.GetTickCount()) - millis = current.value - lastInputInfo.dwTime # @UndefinedVariable - if millis < 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))