diff --git a/ui/opensnitch/config.py b/ui/opensnitch/config.py
index e9c4195a08..b963c4e46b 100644
--- a/ui/opensnitch/config.py
+++ b/ui/opensnitch/config.py
@@ -60,6 +60,11 @@ class Config:
DEFAULT_DB_MAX_DAYS = "database/max_days"
DEFAULT_DB_PURGE_INTERVAL = "database/purge_interval"
+ NOTIFICATIONS_ENABLED = "notifications/enabled"
+ NOTIFICATIONS_TYPE = "notifications/type"
+ NOTIFICATION_TYPE_SYSTEM = 0
+ NOTIFICATION_TYPE_QT = 1
+
STATS_GEOMETRY = "statsDialog/geometry"
STATS_LAST_TAB = "statsDialog/last_tab"
@@ -123,8 +128,8 @@ def setSettings(self, path, value):
def getSettings(self, path):
return self.settings.value(path)
- def getBool(self, path):
- return self.settings.value(path, False, type=bool)
+ def getBool(self, path, default_value=False):
+ return self.settings.value(path, type=bool, defaultValue=default_value)
def getInt(self, path, default_value=0):
try:
diff --git a/ui/opensnitch/dialogs/preferences.py b/ui/opensnitch/dialogs/preferences.py
index 52dec7c6c8..1d2b37c3d2 100644
--- a/ui/opensnitch/dialogs/preferences.py
+++ b/ui/opensnitch/dialogs/preferences.py
@@ -10,6 +10,7 @@
from opensnitch.nodes import Nodes
from opensnitch.database import Database
from opensnitch.utils import Message, QuickHelp
+from opensnitch.notifications import DesktopNotifications
from opensnitch import ui_pb2
@@ -57,14 +58,16 @@ def __init__(self, parent=None):
self.cmdDBMaxDaysDown.clicked.connect(lambda: self._cb_cmd_spin_clicked(self.spinDBMaxDays, self.REST))
self.cmdDBPurgesUp.clicked.connect(lambda: self._cb_cmd_spin_clicked(self.spinDBPurgeInterval, self.SUM))
self.cmdDBPurgesDown.clicked.connect(lambda: self._cb_cmd_spin_clicked(self.spinDBPurgeInterval, self.REST))
- self.helpButton.setToolTipDuration(10 * 1000)
+ self.cmdTestNotifs.clicked.connect(self._cb_test_notifs_clicked)
+ self.helpButton.setToolTipDuration(30 * 1000)
if QtGui.QIcon.hasThemeIcon("emblem-default") == False:
self.applyButton.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_DialogApplyButton")))
self.cancelButton.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_DialogCloseButton")))
self.acceptButton.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_DialogSaveButton")))
self.dbFileButton.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_DirOpenIcon")))
- if QtGui.QIcon.hasThemeIcon("zoom-in") == False:
+
+ if QtGui.QIcon.hasThemeIcon("list-add") == False:
self.cmdTimeoutUp.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_ArrowUp")))
self.cmdTimeoutDown.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_ArrowDown")))
self.cmdDBMaxDaysUp.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_ArrowUp")))
@@ -76,6 +79,7 @@ def showEvent(self, event):
super(PreferencesDialog, self).showEvent(event)
try:
+ self._settingsSaved = False
self._reset_status_message()
self._hide_status_label()
self.comboNodes.clear()
@@ -143,6 +147,14 @@ def _load_settings(self):
self.dstPortCheck.setChecked(self._cfg.getBool(self._cfg.DEFAULT_POPUP_ADVANCED_DSTPORT))
self.uidCheck.setChecked(self._cfg.getBool(self._cfg.DEFAULT_POPUP_ADVANCED_UID))
+ self.groupNotifs.setChecked(self._cfg.getBool(Config.NOTIFICATIONS_ENABLED))
+ self.radioSysNotifs.setChecked(
+ True if self._cfg.getInt(Config.NOTIFICATIONS_TYPE) == Config.NOTIFICATION_TYPE_SYSTEM else False
+ )
+ self.radioQtNotifs.setChecked(
+ True if self._cfg.getInt(Config.NOTIFICATIONS_TYPE) == Config.NOTIFICATION_TYPE_QT else False
+ )
+
self.dbType = self._cfg.getInt(self._cfg.DEFAULT_DB_TYPE_KEY)
self.comboDBType.setCurrentIndex(self.dbType)
if self.comboDBType.currentIndex() != Database.DB_TYPE_MEMORY:
@@ -285,7 +297,7 @@ def _save_settings(self):
self._node_needs_update = False
self.saved.emit()
-
+ self._settingsSaved = True
def _save_db_config(self):
dbtype = self.comboDBType.currentIndex()
@@ -339,6 +351,11 @@ def _save_ui_config(self):
self._cfg.setSettings(self._cfg.DEFAULT_POPUP_ADVANCED_DSTIP, bool(self.dstIPCheck.isChecked()))
self._cfg.setSettings(self._cfg.DEFAULT_POPUP_ADVANCED_DSTPORT, bool(self.dstPortCheck.isChecked()))
self._cfg.setSettings(self._cfg.DEFAULT_POPUP_ADVANCED_UID, bool(self.uidCheck.isChecked()))
+
+ self._cfg.setSettings(self._cfg.NOTIFICATIONS_ENABLED, bool(self.groupNotifs.isChecked()))
+ self._cfg.setSettings(self._cfg.NOTIFICATIONS_TYPE,
+ int(Config.NOTIFICATION_TYPE_SYSTEM if self.radioSysNotifs.isChecked() else Config.NOTIFICATION_TYPE_QT))
+
# this is a workaround for not display pop-ups.
# see #79 for more information.
if self.popupsCheck.isChecked():
@@ -444,7 +461,8 @@ def _cb_db_type_changed(self):
def _cb_accept_button_clicked(self):
self.accept()
- self._save_settings()
+ if not self._settingsSaved:
+ self._save_settings()
def _cb_apply_button_clicked(self):
self._save_settings()
@@ -456,7 +474,8 @@ def _cb_help_button_clicked(self):
QuickHelp.show(
QC.translate("preferences",
"Hover the mouse over the texts to display the help
Don't forget to visit the wiki: {0}"
- ).format(Config.HELP_URL))
+ ).format(Config.HELP_URL)
+ )
def _cb_popups_check_toggled(self, checked):
self.spinUITimeout.setEnabled(not checked)
@@ -480,3 +499,9 @@ def _cb_cmd_spin_clicked(self, spinWidget, operation):
spinWidget.setValue(spinWidget.value() + 1)
else:
spinWidget.setValue(spinWidget.value() - 1)
+
+ def _cb_test_notifs_clicked(self):
+ if self.radioSysNotifs.isChecked():
+ DesktopNotifications().show("title", "body")
+ else:
+ pass
diff --git a/ui/opensnitch/notifications.py b/ui/opensnitch/notifications.py
new file mode 100644
index 0000000000..daa4ee1c6c
--- /dev/null
+++ b/ui/opensnitch/notifications.py
@@ -0,0 +1,129 @@
+#!/usr/bin/python
+
+from PyQt5.QtCore import QCoreApplication as QC
+import os
+from utils import Utils
+from opensnitch.config import Config
+
+class DesktopNotifications():
+ """DesktopNotifications display informative pop-ups using the system D-Bus.
+ The notifications are handled and configured by the system.
+
+ The notification daemon also decides where to show the notifications, as well
+ as how to group them.
+
+ The body of a notification supports markup (if the implementation supports it):
+ https://people.gnome.org/~mccann/docs/notification-spec/notification-spec-latest.html#markup
+ Basically: , , , and
. New lines can be added with the regular \n.
+
+ It also support actions (buttons).
+
+ https://notify2.readthedocs.io/en/latest/
+ """
+
+ _cfg = Config.init()
+
+ # list of hints:
+ # https://people.gnome.org/~mccann/docs/notification-spec/notification-spec-latest.html#hints
+ HINT_DESKTOP_ENTRY = "desktop-entry"
+ CATEGORY_NETWORK = "network"
+
+ EXPIRES_DEFAULT = 0
+ NEVER_EXPIRES = -1
+
+ def __init__(self):
+ self.ACTION_ALLOW = QC.translate("popups", "Allow")
+ self.ACTION_DENY = QC.translate("popups", "Deny")
+ self.IS_LIBNOTIFY_AVAILABLE = True
+ self.DOES_SUPPORT_ACTIONS = True
+
+ try:
+ import notify2
+ self.ntf2 = notify2
+ mloop = 'glib'
+
+ # First try to initialise the D-Bus connection with the given
+ # mainloop.
+ # If it fails, we'll try to initialise it without it.
+ try:
+ self.ntf2.init("opensnitch", mainloop=mloop)
+ except Exception:
+ self.DOES_SUPPORT_ACTIONS = False
+
+ # usually because dbus mainloop is not initiated, specially
+ # with 'qt'
+ # FIXME: figure out how to init it, or how to connect to an
+ # existing session.
+ print("DesktopNotifications(): system doesn't support actions. Available capabilities:")
+ print(self.ntf2.get_server_caps())
+
+ self.ntf2.init("opensnitch")
+
+ # Example: ['actions', 'action-icons', 'body', 'body-markup', 'icon-static', 'persistence', 'sound']
+ if ('actions' not in self.ntf2.get_server_caps()):
+ self.DOES_SUPPORT_ACTIONS = False
+
+ except Exception as e:
+ print("DesktopNotifications not available (install python3-notify2):", e)
+ self.IS_LIBNOTIFY_AVAILABLE = False
+
+ def is_available(self):
+ return self.IS_LIBNOTIFY_AVAILABLE and self._cfg.getBool(Config.NOTIFICATIONS_ENABLED)
+
+ def support_actions(self):
+ """Returns true if the notifications daemon support actions(buttons).
+ This depends on 2 factors:
+ - If the notification server actually supports it (get_server_caps()).
+ - If there's a dbus instance running.
+ """
+ return self.DOES_SUPPORT_ACTIONS
+
+ def show(self, title, body, icon="dialog-information"):
+ ntf = self.ntf2.Notification(title, body, icon)
+
+ # timeouts seems to be ignored (on Cinnamon at least)
+ timeout = self._cfg.getInt(Config.DEFAULT_TIMEOUT_KEY, 15)
+ ntf.set_timeout(timeout)
+ ntf.timeout = timeout
+
+ ntf.set_category(self.CATEGORY_NETWORK)
+ # used to display our app icon an name.
+ ntf.set_hint(self.HINT_DESKTOP_ENTRY, "opensnitch_ui")
+ ntf.show()
+
+ # TODO:
+ # - construct a rule with the default configured parameters.
+ # - create a common dialogs/prompt.py:_send_rule(), maybe in utils.py
+ def ask(self, connection, timeout, callback):
+ c = connection
+ title = QC.translate("popups", "New outgoing connection")
+ body = """
+ {0}
+
+ {1} {2}:{3} -> {4}:{5}
+ UID: {6} PID: {7}
+ """.format(c.process_path, c.protocol.upper(),
+ c.src_port, c.src_ip,
+ c.dst_host if c.dst_host != "" else c.dst_ip, c.dst_port,
+ c.user_id, c.process_id
+ )
+ ntf = self.ntf2.Notification(title, body, "dialog-warning")
+ timeout = self._cfg.getInt(Config.DEFAULT_TIMEOUT_KEY, 15)
+ ntf.set_timeout(timeout * 1000)
+ ntf.timeout = timeout * 1000
+ if self.DOES_SUPPORT_ACTIONS:
+ ntf.set_urgency(self.ntf2.URGENCY_CRITICAL)
+ ntf.add_action("allow", self.ACTION_ALLOW, callback, connection)
+ ntf.add_action("deny", self.ACTION_DENY, callback, connection)
+ #ntf.add_action("open-gui", QC.translate("popups", "View"), callback, connection)
+ ntf.set_category(self.CATEGORY_NETWORK)
+ ntf.set_hint(self.HINT_DESKTOP_ENTRY, "opensnitch_ui")
+ ntf.show()
+
+ @staticmethod
+ def areEnabled():
+ """Return if notifications are enabled.
+
+ Default: True
+ """
+ return DesktopNotifications._cfg.getBool(DesktopNotifications._cfg.NOTIFICATIONS_ENABLED, True)
diff --git a/ui/opensnitch/res/preferences.ui b/ui/opensnitch/res/preferences.ui
index 9a99a27d9d..cb04a653ea 100644
--- a/ui/opensnitch/res/preferences.ui
+++ b/ui/opensnitch/res/preferences.ui
@@ -41,6 +41,12 @@
-
+
+
+ 0
+ 0
+
+
@@ -124,7 +130,7 @@
- ../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup
+ ../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup
-
@@ -133,7 +139,7 @@
- ../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup
+ ../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup
@@ -385,7 +391,7 @@
- ..
+ ../../../../../../../../../../.designer/backup../../../../../../../../../../.designer/backup
true
@@ -424,7 +430,7 @@
- ..
+ ../../../../../../../../../../.designer/backup../../../../../../../../../../.designer/backup
true
@@ -436,7 +442,7 @@
-
-
+
0
0
@@ -449,10 +455,10 @@
- -
+
-
- Disable pop-ups, only display an alert
+ Disable pop-ups, only display an notification
true
@@ -480,14 +486,117 @@
- -
+
-
+
+
+ <html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html>
+
+
+ Don't save rules of duration
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Desktop notifications
+
+
+ true
+
+
+ true
+
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ Use system notifications
+
+
+ true
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Use Qt notifications
+
+
+
+ -
+
+
-
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 20
+ 20
+
+
+
+
+ -
+
+
+ Test
+
+
+
+
+
+
+
+
+ -
+
+
+ 0
+ 0
+
+
Events tab columns
-
+
+
+ 0
+ 0
+
+
Time
@@ -496,60 +605,96 @@
- -
-
+
-
+
+
+
+ 0
+ 0
+
+
- Destination
+ Rule
true
- -
-
+
-
+
+
+
+ 0
+ 0
+
+
- Protocol
+ Node
true
- -
-
+
-
+
+
+
+ 0
+ 0
+
+
- Process
+ Protocol
true
- -
-
+
-
+
+
+
+ 0
+ 0
+
+
- Rule
+ Action
true
- -
-
+
-
+
+
+
+ 0
+ 0
+
+
- Node
+ Destination
true
- -
-
+
-
+
+
+
+ 0
+ 0
+
+
- Action
+ Process
true
@@ -559,16 +704,6 @@
- -
-
-
- <html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html>
-
-
- Don't save rules of duration
-
-
-
@@ -771,7 +906,7 @@
- ../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup
+ ../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup
-
@@ -780,7 +915,7 @@
- ../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup
+ ../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup
@@ -966,7 +1101,7 @@
- ..
+ ../../../../../../../../../../.designer/backup../../../../../../../../../../.designer/backup
@@ -1030,7 +1165,7 @@
- ..
+ ../../../../../../../../../../.designer/backup../../../../../../../../../../.designer/backup
true
@@ -1116,7 +1251,7 @@
- ..
+ ../../../../../../../../../../.designer/backup../../../../../../../../../../.designer/backup
true
@@ -1136,7 +1271,7 @@
- ..
+ ../../../../../../../../../../.designer/backup../../../../../../../../../../.designer/backup
true
@@ -1156,7 +1291,7 @@
- ..
+ ../../../../../../../../../../.designer/backup../../../../../../../../../../.designer/backup
true
@@ -1184,7 +1319,7 @@
- ..
+ ../../../../../../../../../../.designer/backup../../../../../../../../../../.designer/backup
true
@@ -1211,7 +1346,7 @@
- ../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup
+ ../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup
@@ -1222,7 +1357,7 @@
- ..
+ ../../../../../../../../../../.designer/backup../../../../../../../../../../.designer/backup
@@ -1233,7 +1368,7 @@
- ../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup
+ ../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup
diff --git a/ui/opensnitch/service.py b/ui/opensnitch/service.py
index d9455cc4a2..63cb88169c 100644
--- a/ui/opensnitch/service.py
+++ b/ui/opensnitch/service.py
@@ -16,6 +16,7 @@
from opensnitch.dialogs.prompt import PromptDialog
from opensnitch.dialogs.stats import StatsDialog
+from opensnitch.notifications import DesktopNotifications
from opensnitch.nodes import Nodes
from opensnitch.config import Config
from opensnitch.version import version
@@ -30,7 +31,7 @@ class UIService(ui_pb2_grpc.UIServicer, QtWidgets.QGraphicsObject):
_version_warning_trigger = QtCore.pyqtSignal(str, str)
_status_change_trigger = QtCore.pyqtSignal(bool)
_notification_callback = QtCore.pyqtSignal(ui_pb2.NotificationReply)
- _show_message_trigger = QtCore.pyqtSignal(str, str, int, int)
+ _show_message_trigger = QtCore.pyqtSignal(str, str, int)
# .desktop filename located under /usr/share/applications/
DESKTOP_FILENAME = "opensnitch_ui.desktop"
@@ -69,10 +70,6 @@ def __init__(self, app, on_exit):
QtWidgets.QMessageBox.Warning)
sys.exit(-1)
- self._cleaner = None
- if self._cfg.getBool(Config.DEFAULT_DB_PURGE_OLDEST):
- self._start_db_cleaner()
-
self._db_sqlite = self._db.get_db()
self._last_ping = None
self._version_warning_shown = False
@@ -88,6 +85,7 @@ def __init__(self, app, on_exit):
self._remote_lock = Lock()
self._remote_stats = {}
+ self._desktop_notifications = DesktopNotifications()
self._setup_interfaces()
self._setup_icons()
self._stats_dialog = StatsDialog(dbname="general", db=self._db)
@@ -105,6 +103,12 @@ def __init__(self, app, on_exit):
'users':{}
}
+ self._show_gui_if_tray_not_available()
+
+ self._cleaner = None
+ if self._cfg.getBool(Config.DEFAULT_DB_PURGE_OLDEST):
+ self._start_db_cleaner()
+
# https://gist.github.com/pklaus/289646
def _setup_interfaces(self):
namestr, outbytes = Utils.get_interfaces()
@@ -146,11 +150,12 @@ def _setup_icons(self):
self._app.setWindowIcon(self.white_icon)
self._app.setDesktopFileName(self.DESKTOP_FILENAME)
- self._prompt_dialog.setWindowIcon(self.white_icon)
def _setup_tray(self):
- self._menu = QtWidgets.QMenu()
self._tray = QtWidgets.QSystemTrayIcon(self.off_icon)
+ self._tray.show()
+
+ self._menu = QtWidgets.QMenu()
self._tray.setContextMenu(self._menu)
self._tray.activated.connect(self._on_tray_icon_activated)
@@ -163,9 +168,6 @@ def _setup_tray(self):
)
self._menu.addAction(self.MENU_ENTRY_CLOSE).triggered.connect(self._on_close)
- self._tray.show()
- self._show_gui_if_tray_not_available()
-
def _show_gui_if_tray_not_available(self):
"""If the system tray is not available or ready, show the GUI after
10s. This delay helps to skip showing up the GUI when DEs' autologin is on.
@@ -260,11 +262,17 @@ def _on_notification_reply(self, reply):
def _on_remote_stats_menu(self, address):
self._remote_stats[address]['dialog'].show()
- @QtCore.pyqtSlot(str, str, int, int)
- def _show_systray_message(self, title, body, icon, timeout):
+ @QtCore.pyqtSlot(str, str, int)
+ def _show_systray_message(self, title, body, icon):
+ if DesktopNotifications.areEnabled():
+ if self._desktop_notifications.is_available() and self._cfg.getInt(Config.NOTIFICATIONS_TYPE) == Config.NOTIFICATION_TYPE_SYSTEM:
+ self._desktop_notifications.show(title, body, os.path.join(self._path, "res/icon-white.svg"))
+ else:
+ timeout = self._cfg.getInt(Config.DEFAULT_TIMEOUT_KEY, 15)
+ self._tray.showMessage(title, body, icon, timeout)
+
if icon == QtWidgets.QSystemTrayIcon.NoIcon:
self._tray.setIcon(self.alert_icon)
- self._tray.showMessage(title, body, icon, timeout)
def _on_enable_interception_clicked(self):
self._enable_interception(self._fw_enabled)
@@ -564,6 +572,12 @@ def Ping(self, request, context):
return ui_pb2.PingReply(id=request.id)
def AskRule(self, request, context):
+ #def callback(ntf, action, connection):
+ # TODO
+
+ #if self._desktop_notifications.support_actions():
+ # self._desktop_notifications.ask(request, callback)
+
# TODO: allow connections originated from ourselves: os.getpid() == request.pid)
self._asking = True
proto, addr = self._get_peer(context.peer())
@@ -576,18 +590,29 @@ def AskRule(self, request, context):
node_text = "" if self._is_local_request(proto, addr) else "on node {0}:{1}".format(proto, addr)
self._show_message_trigger.emit(_title,
- "{0} action applied {1}\nArguments: {2}"
- .format(rule.action, node_text, request.process_args),
- QtWidgets.QSystemTrayIcon.NoIcon,
- 0)
+ "{0} action applied {1}\nCommand line: {2}"
+ .format(rule.action, node_text, " ".join(request.process_args)),
+ QtWidgets.QSystemTrayIcon.NoIcon)
self._last_ping = datetime.now()
self._asking = False
if rule.duration in Config.RULES_DURATION_FILTER:
- self._node_actions_trigger.emit({'action': self.DELETE_RULE, 'name': rule.name, 'addr': context.peer()})
+ self._node_actions_trigger.emit(
+ {
+ 'action': self.DELETE_RULE,
+ 'name': rule.name,
+ 'addr': context.peer()
+ }
+ )
else:
- self._node_actions_trigger.emit({'action': self.ADD_RULE, 'peer': context.peer(), 'rule': rule})
+ self._node_actions_trigger.emit(
+ {
+ 'action': self.ADD_RULE,
+ 'peer': context.peer(),
+ 'rule': rule
+ }
+ )
return rule
@@ -599,18 +624,20 @@ def Subscribe(self, node_config, context):
@doc: https://grpc.github.io/grpc/python/grpc.html#service-side-context
"""
try:
- proto, addr = self._get_peer(context.peer())
- if self._is_local_request(proto, addr) == False:
- self._show_message_trigger.emit("New node connected",
- "({0})".format(context.peer()),
- QtWidgets.QSystemTrayIcon.Information,
- 5000)
-
self._node_actions_trigger.emit({
'action': self.NODE_ADD,
'peer': context.peer(),
'node_config': node_config
})
+ # force events processing, to add the node ^ before the
+ # Notifications() call arrives.
+ QtWidgets.QApplication.processEvents()
+
+ proto, addr = self._get_peer(context.peer())
+ if self._is_local_request(proto, addr) == False:
+ self._show_message_trigger.emit("New node connected",
+ "({0})".format(context.peer()),
+ QtWidgets.QSystemTrayIcon.Information)
except Exception as e:
print("[Notifications] exception adding new node:", e)
context.cancel()
@@ -650,8 +677,7 @@ def _on_client_closed():
if self._is_local_request(proto, addr) == False:
self._show_message_trigger.emit("node exited",
"({0})".format(context.peer()),
- QtWidgets.QSystemTrayIcon.Information,
- 5000)
+ QtWidgets.QSystemTrayIcon.Information)
context.add_callback(_on_client_closed)