source: ogAgent-Git/src/OGAgentUser.py @ 64c933f

decorare-oglive-methodsfix-urlfixes-winlgromero-filebeatmainmodulesnew-browserno-ptt-paramogadmcliogadmclient-statusogagent-jobsogagent-macosogcore1oglogoglog2override-moduleping1ping2ping3ping4py3-winpython3report-progresssched-tasktlsunification2unification3versionswindows-fixes
Last change on this file since 64c933f was 64c933f, checked in by Ramón M. Gómez <ramongomez@…>, 5 years ago

#940: Fix message encoding.

  • Property mode set to 100644
File size: 11.2 KB
Line 
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3#
4# Copyright (c) 2014 Virtual Cable S.L.
5# All rights reserved.
6#
7# Redistribution and use in source and binary forms, with or without modification,
8# are permitted provided that the following conditions are met:
9#
10#    * Redistributions of source code must retain the above copyright notice,
11#      this list of conditions and the following disclaimer.
12#    * Redistributions in binary form must reproduce the above copyright notice,
13#      this list of conditions and the following disclaimer in the documentation
14#      and/or other materials provided with the distribution.
15#    * Neither the name of Virtual Cable S.L. nor the names of its contributors
16#      may be used to endorse or promote products derived from this software
17#      without specific prior written permission.
18#
19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29"""
30@author: Adolfo Gómez, dkmaster at dkmon dot com
31"""
32import atexit
33import base64
34import json
35import sys
36import time
37from PyQt5 import QtCore, QtGui, QtWidgets
38
39from about_dialog_ui import Ui_OGAAboutDialog
40from message_dialog_ui import Ui_OGAMessageDialog
41from opengnsys import VERSION, ipc, operations, utils
42from opengnsys.config import readConfig
43from opengnsys.loader import loadModules
44from opengnsys.log import logger
45from opengnsys.scriptThread import ScriptExecutorThread
46from opengnsys.service import IPC_PORT
47
48trayIcon = None
49
50
51def sigAtExit():
52    if trayIcon:
53        trayIcon.quit()
54
55
56# About dialog
57class OGAAboutDialog(QtWidgets.QDialog):
58    def __init__(self, parent=None):
59        QtWidgets.QDialog.__init__(self, parent)
60        self.ui = Ui_OGAAboutDialog()
61        self.ui.setupUi(self)
62        self.ui.VersionLabel.setText("Version " + VERSION)
63
64    def closeDialog(self):
65        self.hide()
66
67
68class OGAMessageDialog(QtWidgets.QDialog):
69    def __init__(self, parent=None):
70        QtWidgets.QDialog.__init__(self, parent)
71        self.ui = Ui_OGAMessageDialog()
72        self.ui.setupUi(self)
73
74    def message(self, message):
75        self.ui.message.setText(message)
76        self.show()
77
78    def closeDialog(self):
79        self.hide()
80
81
82class MessagesProcessor(QtCore.QThread):
83    logoff = QtCore.pyqtSignal(name='logoff')
84    message = QtCore.pyqtSignal(tuple, name='message')
85    script = QtCore.pyqtSignal(str, name='script')
86    exit = QtCore.pyqtSignal(name='exit')
87
88    def __init__(self, port):
89        super(self.__class__, self).__init__()
90        # Retries connection for a while
91        for _ in range(10):
92            try:
93                self.ipc = ipc.ClientIPC(port)
94                self.ipc.start()
95                break
96            except Exception:
97                logger.debug('IPC Server is not reachable')
98                self.ipc = None
99                time.sleep(2)
100
101        self.running = False
102
103    def stop(self):
104        self.running = False
105        if self.ipc:
106            self.ipc.stop()
107
108    def isAlive(self):
109        return self.ipc is not None
110
111    def sendLogin(self, username, language):
112        if self.ipc:
113            self.ipc.sendLogin(username, language)
114
115    def sendLogout(self, username):
116        if self.ipc:
117            self.ipc.sendLogout(username)
118
119    def run(self):
120        if self.ipc is None:
121            return
122        self.running = True
123
124        # Wait a bit so we ensure IPC thread is running...
125        time.sleep(2)
126
127        while self.running and self.ipc.running:
128            try:
129                msg = self.ipc.getMessage()
130                if msg is None:
131                    break
132                msg_id, data = msg
133                logger.debug('Got Message on User Space: {}:{}'.format(msg_id, data))
134                if msg_id == ipc.MSG_MESSAGE:
135                    module, message, data = data.decode('utf-8').split('\0')
136                    self.message.emit((module, message, data))
137                elif msg_id == ipc.MSG_LOGOFF:
138                    self.logoff.emit()
139                elif msg_id == ipc.MSG_SCRIPT:
140                    self.script.emit(data.decode('utf-8'))
141            except Exception as e:
142                logger.error('Got error on IPC thread {}'.format(utils.exceptionToMessage(e)))
143
144        if self.ipc.running is False and self.running is True:
145            logger.warn('Lost connection with Service, closing program')
146
147        self.exit.emit()
148
149
150class OGASystemTray(QtWidgets.QSystemTrayIcon):
151    def __init__(self, app_, parent=None):
152        self.app = app_
153        self.config = readConfig(client=True)
154        self.modules = None
155        # Get opengnsys section as dict
156        cfg = dict(self.config.items('opengnsys'))
157        # Set up log level
158        logger.setLevel(cfg.get('log', 'INFO'))
159
160        self.ipcport = int(cfg.get('ipc_port', IPC_PORT))
161
162        icon = QtGui.QIcon(':/images/img/oga.png')
163
164        QtWidgets.QSystemTrayIcon.__init__(self, icon, parent)
165        self.menu = QtWidgets.QMenu(parent)
166        exit_action = self.menu.addAction("About")
167        exit_action.triggered.connect(self.about)
168        self.setContextMenu(self.menu)
169        self.ipc = MessagesProcessor(self.ipcport)
170
171        if self.ipc.isAlive() is False:
172            raise Exception('No connection to service, exiting.')
173
174        self.timer = QtCore.QTimer()
175        self.timer.timeout.connect(self.timerFnc)
176
177        self.stopped = False
178
179        self.ipc.message.connect(self.message)
180        self.ipc.exit.connect(self.quit)
181        self.ipc.script.connect(self.executeScript)
182        self.ipc.logoff.connect(self.logoff)
183
184        self.aboutDlg = OGAAboutDialog()
185        self.msgDlg = OGAMessageDialog()
186
187        self.timer.start(1000)  # Launch idle checking every 1 seconds
188
189        self.ipc.start()
190
191    def initialize(self):
192        # Load modules and activate them
193        # Also, sends "login" event to service
194        self.modules = loadModules(self, client=True)
195        logger.debug('Modules: {}'.format(list(v.name for v in self.modules)))
196
197        # Send init to all modules
198        valid_mods = []
199        for mod in self.modules:
200            try:
201                logger.debug('Activating module {}'.format(mod.name))
202                mod.activate()
203                valid_mods.append(mod)
204            except Exception as e:
205                logger.exception()
206                logger.error("Activation of {} failed: {}".format(mod.name, utils.exceptionToMessage(e)))
207        self.modules[:] = valid_mods  # copy instead of assignment
208        # If this is running, it's because he have logged in, inform service of this fact
209        self.ipc.sendLogin(operations.getCurrentUser(), operations.getSessionLanguage())
210
211    def deinitialize(self):
212        for mod in reversed(self.modules):  # Deinitialize reversed of initialization
213            try:
214                logger.debug('Deactivating module {}'.format(mod.name))
215                mod.deactivate()
216            except Exception as e:
217                logger.exception()
218                logger.error("Deactivation of {} failed: {}".format(mod.name, utils.exceptionToMessage(e)))
219
220    def timerFnc(self):
221        pass
222
223    def message(self, msg):
224        """
225        Processes the message sent asynchronously, msg is an QString
226        """
227        try:
228            logger.debug('msg: {}, {}'.format(type(msg), msg))
229            module, message, data = msg
230        except Exception as e:
231            logger.error('Got exception {} processing message {}'.format(e, msg))
232            return
233
234        for v in self.modules:
235            if v.name == module:  # Case Sensitive!!!!
236                try:
237                    logger.debug('Notifying message {} to module {} with json data {}'.format(message, v.name, data))
238                    v.processMessage(message, json.loads(data))
239                    return
240                except Exception as e:
241                    logger.error('Got exception {} processing generic message on {}'.format(e, v.name))
242
243        logger.error('Module {} not found, messsage {} not sent'.format(module, message))
244
245    def executeScript(self, script):
246        logger.debug('Executing script')
247        script = base64.b64decode(script.encode('ascii'))
248        th = ScriptExecutorThread(script)
249        th.start()
250
251    def logoff(self):
252        logger.debug('Logoff invoked')
253        operations.logoff()  # Invoke log off
254
255    def about(self):
256        self.aboutDlg.exec_()
257
258    def cleanup(self):
259        logger.debug('Quit invoked')
260        if self.stopped is False:
261            self.stopped = True
262            try:
263                self.deinitialize()
264            except Exception:
265                logger.exception()
266                logger.error('Got exception deinitializing modules')
267
268            try:
269                # If we close Client, send Logoff to Broker
270                self.ipc.sendLogout(operations.getCurrentUser())
271                time.sleep(1)
272                self.timer.stop()
273                self.ipc.stop()
274            except Exception:
275                # May we have lost connection with server, simply log and exit in that case
276                logger.exception()
277                logger.exception("Got an exception, processing quit")
278
279            try:
280                # operations.logoff()  # Uncomment this after testing to logoff user
281                pass
282            except Exception:
283                pass
284
285    def quit(self):
286        # logger.debug("Exec quit {}".format(self.stopped))
287        if self.stopped is False:
288            self.cleanup()
289            self.app.quit()
290
291    def closeEvent(self, event):
292        logger.debug("Exec closeEvent")
293        event.accept()
294        self.quit()
295
296
297if __name__ == '__main__':
298    app = QtWidgets.QApplication(sys.argv)
299
300    if not QtWidgets.QSystemTrayIcon.isSystemTrayAvailable():
301        # QtGui.QMessageBox.critical(None, "Systray", "I couldn't detect any system tray on this system.")
302        sys.exit(1)
303
304    # This is important so our app won't close on messages windows (alerts, etc...)
305    QtWidgets.QApplication.setQuitOnLastWindowClosed(False)
306
307    try:
308        trayIcon = OGASystemTray(app)
309    except Exception as e:
310        logger.exception()
311        logger.error('OGA Service is not running, or it can\'t contact with OGA Server. User Tools stopped: {}'.format(
312            utils.exceptionToMessage(e)))
313        sys.exit(1)
314
315    try:
316        trayIcon.initialize()  # Initialize modules, etc..
317    except Exception as e:
318        logger.exception()
319        logger.error('Exception initializing OpenGnsys User Agent {}'.format(utils.exceptionToMessage(e)))
320        trayIcon.quit()
321        sys.exit(1)
322
323    app.aboutToQuit.connect(trayIcon.cleanup)
324    trayIcon.show()
325
326    # Catch kill and logout user :)
327    atexit.register(sigAtExit)
328
329    res = app.exec_()
330
331    logger.debug('Exiting')
332    trayIcon.quit()
333
334    sys.exit(res)
Note: See TracBrowser for help on using the repository browser.