diff --git a/ui/opensnitch/actions/__init__.py b/ui/opensnitch/actions/__init__.py
new file mode 100644
index 00000000..7348be4b
--- /dev/null
+++ b/ui/opensnitch/actions/__init__.py
@@ -0,0 +1,157 @@
+from PyQt5.QtCore import QObject
+
+import json
+import os
+import glob
+import sys
+
+from opensnitch.actions import highlight
+from opensnitch.actions.default_configs import commonDelegateConfig, rulesDelegateConfig, fwDelegateConfig
+
+class Actions(QObject):
+ """List of actions to perform on the data that is displayed on the GUI.
+ Whenever an item matches a condition an action is applied, for example:
+ - if the text of a cell matches a condition for the given columns,
+ then the properties of the cell/row and the text are customized.
+
+ There's only 1 action supported right now:
+ - highlight: for customizing rows and cells appearance.
+
+ There're 3 actions by default of type Highlight:
+ - rules: applied to the rules to colorize the columns Enabled and
+ Action
+ - firewall: applied to the fw rules to colorize the columns Action and
+ Target.
+ - common: applied to the rest of the views to colorize the column
+ Action.
+
+ Users can modify the default actions, by adding more patterns to colorize.
+ At the same time they can also create new actions to be applied on certain views.
+
+ The format of the actions is JSON:
+ {
+ "created": "....",
+ "name": "...",
+ "actions": {
+ "highlight": {
+ "cells": [
+ {
+ "text": ["allow", "True", "online"],
+ "cols": [3,5,6],
+ "color": "green",
+ },
+ {
+ "text": ["deny", "False", "offline"],
+ "cols": [3,5,6],
+ "color": "red",
+ }
+ ],
+ "rows": []
+ }
+ }
+
+ """
+ __instance = None
+
+ # list of loaded actions
+ _actions = None
+
+
+ KEY_ACTIONS = "actions"
+ KEY_NAME = "name"
+ KEY_TYPE = "type"
+
+ # TODO: emit a signal when the actions are (re)loaded
+ # reloaded_signal = pyQtSignal()
+
+ # default paths to look for actions
+ _paths = [
+ os.path.dirname(sys.modules[__name__].__file__) + "/data/",
+ os.path.expanduser("~/.config/opensnitch/actions/")
+ ]
+
+ @staticmethod
+ def instance():
+ if Actions.__instance == None:
+ Actions.__instance = Actions()
+ return Actions.__instance
+
+ def __init__(self, parent=None):
+ QObject.__init__(self)
+ self._actions_list = {}
+ try:
+ os.makedirs(os.path.expanduser("~/.config/opensnitch/actions/"), 0o700)
+ except:
+ pass
+
+ def _load_default_configs(self):
+ self._actions_list[commonDelegateConfig[Actions.KEY_NAME]] = self.compile(commonDelegateConfig)
+ self._actions_list[rulesDelegateConfig[Actions.KEY_NAME]] = self.compile(rulesDelegateConfig)
+ self._actions_list[fwDelegateConfig[Actions.KEY_NAME]] = self.compile(fwDelegateConfig)
+
+ def loadAll(self):
+ """look for actions firstly on default system path, secondly on
+ user's home.
+ If a user customizes existing configurations, they'll be saved under
+ the user's home directory.
+
+ Action files are .json files.
+ """
+ self._load_default_configs()
+
+ for path in self._paths:
+ for jfile in glob.glob(os.path.join(path, '*.json')):
+ self.load(jfile)
+
+ def load(self, action_file):
+ """read a json file from disk and create the action."""
+ with open(action_file, 'r') as fd:
+ data=fd.read()
+ obj = json.loads(data)
+ self._actions_list[obj[Actions.KEY_NAME]] = self.compile(obj)
+
+ def compile(self, obj):
+ try:
+ if Actions.KEY_NAME not in obj or obj[Actions.KEY_NAME] == "":
+ return None
+ if obj.get(Actions.KEY_ACTIONS) == None:
+ return None
+
+ for action in obj[Actions.KEY_ACTIONS]:
+ if action == highlight.Highlight.NAME:
+ h = highlight.Highlight(obj[Actions.KEY_ACTIONS][action])
+ h.compile()
+ obj[Actions.KEY_ACTIONS][action]= h
+ else:
+ print("Actions exception: Action '{0}' not supported yet".format(obj[Actions.KEY_NAME]))
+
+ return obj
+ except Exception as e:
+ print("Actions.compile() exception:", e)
+ return None
+
+
+
+ def getAll(self):
+ return self._actions_list
+
+ def deleteAll(self):
+ self._actions_list = {}
+
+ def get(self, name):
+ try:
+ return self._actions_list[name]
+ except Exception as e:
+ print("get() exception:", e)
+ return None
+
+ def delete(self, name):
+ try:
+ del self._actions_list[name]
+ # TODO:
+ # self.reloaded_signal.emit()
+ except:
+ pass
+
+ def isValid(self):
+ pass
diff --git a/ui/opensnitch/actions/default_configs.py b/ui/opensnitch/actions/default_configs.py
new file mode 100644
index 00000000..fae8b8b3
--- /dev/null
+++ b/ui/opensnitch/actions/default_configs.py
@@ -0,0 +1,128 @@
+
+# common configuration to highlight Action column
+commonDelegateConfig = {
+ "name": "commonDelegateConfig",
+ "created": "",
+ "updated": "",
+ "actions": {
+ "highlight": {
+ "cells": [
+ {
+ "text": ["allow", "\u2713 online"],
+ "operator": "==",
+ "cols": [1, 2, 3],
+ "color": "green",
+ "bgcolor": "",
+ "alignment": ["center"]
+ },
+ {
+ "text": ["deny", "\u2613 offline"],
+ "cols": [1, 2, 3],
+ "color": "red",
+ "bgcolor": "",
+ "alignment": ["center"]
+ },
+ {
+ "text": ["reject"],
+ "cols": [1, 2, 3],
+ "color": "purple",
+ "bgcolor": "",
+ "alignment": ["center"]
+ }
+ ],
+ "rows": []
+ }
+ }
+}
+
+# firewall rules configuration to highlight Enabled and Action columns
+fwDelegateConfig = {
+ "name": "defaultFWDelegateConfig",
+ "created": "",
+ "updated": "",
+ "actions": {
+ "highlight": {
+ "cells": [
+ {
+ "text": [
+ "allow",
+ "True",
+ "accept",
+ "jump",
+ "masquerade",
+ "snat",
+ "dnat",
+ "tproxy",
+ "queue",
+ "redirect",
+ "True",
+ "ACCEPT"
+ ],
+ "cols": [7, 10],
+ "color": "green",
+ "bgcolor": "",
+ "alignment": ["center"]
+ },
+ {
+ "text": [
+ "deny",
+ "False",
+ "drop",
+ "DROP",
+ "stop"
+ ],
+ "cols": [7, 10],
+ "color": "red",
+ "bgcolor": "",
+ "alignment": ["center"]
+ },
+ {
+ "text": [
+ "reject",
+ "return"
+ ],
+ "cols": [7, 10],
+ "color": "purple",
+ "bgcolor": "",
+ "alignment": ["center"]
+ }
+ ],
+ "rows": []
+ }
+ }
+}
+
+# rules configuration to highlight Enabled and Action columns
+rulesDelegateConfig = {
+ "name": "defaultRulesDelegateConfig",
+ "created": "",
+ "updated": "",
+ "actions": {
+ "highlight": {
+ "cells": [
+ {
+ "text": ["allow", "True"],
+ "cols": [3, 4],
+ "color": "green",
+ "bgcolor": "",
+ "alignment": ["center"]
+ },
+ {
+ "text": ["deny", "False"],
+ "cols": [3, 4],
+ "color": "red",
+ "bgcolor": "",
+ "alignment": ["center"]
+ },
+ {
+ "text": ["reject"],
+ "cols": [3, 4],
+ "color": "purple",
+ "bgcolor": "",
+ "alignment": ["center"]
+ }
+ ],
+ "rows": []
+ }
+ }
+}
diff --git a/ui/opensnitch/actions/highlight.py b/ui/opensnitch/actions/highlight.py
new file mode 100644
index 00000000..e373702a
--- /dev/null
+++ b/ui/opensnitch/actions/highlight.py
@@ -0,0 +1,234 @@
+from PyQt5 import Qt, QtCore
+from PyQt5.QtGui import QColor, QStandardItemModel, QStandardItem
+
+class Highlight():
+ """Customizes QTablewView cells via QItemDelegates.
+ Format:
+ [
+ {
+ 'text': {"allow", "True", "online"},
+ 'cols': {1,4,5},
+ 'color': "green",
+ 'bgcolor': None,
+ 'alignment': ["center"],
+ #"margins': [0, 0]
+ #'font': {}
+ },
+ ]
+
+ text: will match any of the given texts.
+ cols: look for patterns on these columns.
+ color: colorizes the color of the text.
+ bgcolor: colorizes the background color of the cell.
+ etc.
+ """
+
+ NAME = "highlight"
+
+ MARGINS = "margins"
+ ALIGNMENT = "alignment"
+ # QtCore.Qt.AlignCenter
+ ALIGN_CENTER = "center"
+ # QtCore.Qt.AlignHCenter
+ ALIGN_HCENTER = "hcenter"
+ # QtCore.Qt.AlignVCenter
+ ALIGN_VCENTER = "vcenter"
+
+ COLOR = "color"
+ BGCOLOR = "bgcolor"
+ FONT = "font"
+ CELLS = "cells"
+ ROWS = "rows"
+ COLS = "cols"
+ TEXT = "text"
+
+ def __init__(self, config):
+ # original json config received
+ self._config = config
+ self._last_visited_row = -1
+ self._rowcells = ""
+
+ def compile(self):
+ """transform json items to Qt objects.
+ These items are transformed:
+ - color (to QColor), bgcolor (to QColor), alignment (to Qt.Align*),
+ font (to QFont TODO)
+
+ Return the original json object transformed.
+ """
+ # cells, rows
+ for idx in self._config:
+ cells = self._config[idx]
+ for cell in cells:
+ for item in cell:
+ # colors
+ if (item == Highlight.COLOR or item == Highlight.BGCOLOR):
+ if cell[item] != "" and cell[item] is not None:
+ cell[item] = QColor(cell[item])
+ else:
+ cell[item] = None
+
+ # alignments
+ if item == Highlight.ALIGNMENT:
+ cell[item] = self.getAlignment(cell[item])
+
+ # fonts
+ if item == Highlight.FONT:
+ self.getFont(cell[item])
+
+ return self._config
+
+
+ def run(self, args):
+ """Highlight cells or rows based on patterns.
+
+ Return if the cell was modified.
+
+ Keyword arguments:
+ args -- tuple of options.
+ """
+ painter = args[0]
+ option = args[1]
+ index = args[2]
+ style = args[3]
+ modelColumns = args[4]
+ curRow = args[5]
+ curColumn = args[6]
+ defaultPen = args[7]
+ defaultBrush = args[8]
+ cellAlignment = args[9]
+ cellRect = args[10]
+ cellValue = args[11]
+
+ # signal that this cell has been modified
+ modified = False
+
+ cells = self._config.get(Highlight.CELLS)
+ rows = self._config.get(Highlight.ROWS)
+
+ if cells:
+ for cell in cells:
+ if curColumn not in cell[Highlight.COLS]:
+ continue
+ if cellValue not in cell[Highlight.TEXT]:
+ continue
+# TODO
+# if cell['operator'] == 'simple' and cellValue != cell['text']:
+# continue
+# elif cell['text'] not in cellValue:
+# continue
+
+ cellColor = cell.get(Highlight.COLOR)
+ cellBgColor = cell.get(Highlight.BGCOLOR)
+ if cell.get(Highlight.ALIGNMENT) != None:
+ cellAlignment = cell[Highlight.ALIGNMENT]
+ if cell.get(Highlight.MARGINS) != None:
+ cellRect.adjust(
+ int(cell[Highlight.MARGINS][self.HMARGIN]),
+ int(cell[Highlight.MARGINS][self.VMARGIN]),
+ -defaultPen.width(),
+ -defaultPen.width()
+ )
+
+ modified=True
+ self.paintCell(
+ style,
+ painter,
+ option,
+ index,
+ defaultPen,
+ defaultBrush,
+ cellAlignment,
+ cellRect,
+ cellColor,
+ cellBgColor,
+ cellValue)
+
+ if len(rows) == 0:
+ return (modified,)
+
+ # get row's cells only for the first cell of the row,
+ # then reuse them for the rest of the cells of the current row.
+ if curRow != self._last_visited_row:
+ self._rowcells = " ".join(
+ [index.sibling(curRow, col).data() for col in range(0, modelColumns)]
+ )
+ self._last_visited_row = curRow
+
+ for row in rows:
+ skip = True
+ for text in row[Highlight.TEXT]:
+ if text in self._rowcells:
+ skip = False
+ if skip:
+ continue
+
+ cellColor = row.get(Highlight.COLOR)
+ cellBgColor = row.get(Highlight.BGCOLOR)
+ if row.get(Highlight.ALIGNMENT) != None:
+ cellAlignment = row[Highlight.ALIGNMENT]
+ if row.get(Highlight.MARGINS) != None:
+ cellRect.adjust(
+ int(row[Highlight.MARGINS][self.HMARGIN]),
+ int(row[Highlight.MARGINS][self.VMARGIN]),
+ -defaultPen.width(),
+ -defaultPen.width()
+ )
+
+ modified=True
+ self.paintCell(
+ style,
+ painter,
+ option,
+ index,
+ defaultPen,
+ defaultBrush,
+ cellAlignment,
+ cellRect,
+ cellColor,
+ cellBgColor,
+ cellValue)
+
+ return (modified,)
+
+ def paintCell(self, style, painter, option, index, defaultPen, defaultBrush, cellAlignment, cellRect, cellColor, cellBgColor, cellValue):
+ cellSelected = option.state & Qt.QStyle.State_Selected
+
+ painter.save()
+ # don't customize selected state
+ if not cellSelected:
+ if cellBgColor != None:
+ painter.fillRect(option.rect, cellBgColor)
+
+ if cellColor is not None:
+ defaultPen.setColor(cellColor)
+ painter.setPen(defaultPen)
+
+ # setting option.displayAlignment has no effect here, so we need to
+ # draw the text.
+ # FIXME: Drawing the text though, the background color of the SelectedState is
+ # altered.
+ # If we called super().paint(), modifying option.palette.* would be
+ # enough to change the text color, but it wouldn't be aligned:
+ # option.palette.setColor(QPalette.Text, cellColor)
+ style.drawItemText(painter, cellRect, cellAlignment, option.palette, True, cellValue)
+ painter.restore()
+
+ def getAlignment(self, alignments):
+ alignFlags = 0
+ for align in alignments:
+ if align == Highlight.ALIGN_CENTER:
+ alignFlags |= QtCore.Qt.AlignCenter
+ elif align == Highlight.ALIGN_HCENTER:
+ alignFlags |= QtCore.Qt.AlignHCenter
+ elif align == Highlight.ALIGN_VCENTER:
+ alignFlags |= QtCore.Qt.AlignVCenter
+
+ if alignFlags == 0:
+ return None
+
+ return alignFlags
+
+ def getFont(self, font):
+ # TODO
+ pass
diff --git a/ui/opensnitch/actions/utils.py b/ui/opensnitch/actions/utils.py
new file mode 100644
index 00000000..5c435bed
--- /dev/null
+++ b/ui/opensnitch/actions/utils.py
@@ -0,0 +1,8 @@
+from PyQt5.QtGui import QColor
+
+def getColorNames():
+ """Return the built-in color names that can be used to choose new colors:
+ https://doc.qt.io/qtforpython-5/PySide2/QtGui/QColor.html#predefined-colors
+ https://www.w3.org/TR/SVG11/types.html#ColorKeywords
+ """
+ return QColor.colorNames()
diff --git a/ui/opensnitch/customwidgets/colorizeddelegate.py b/ui/opensnitch/customwidgets/colorizeddelegate.py
new file mode 100644
index 00000000..24e0910b
--- /dev/null
+++ b/ui/opensnitch/customwidgets/colorizeddelegate.py
@@ -0,0 +1,77 @@
+from PyQt5 import Qt, QtCore
+from PyQt5.QtWidgets import QApplication
+
+class ColorizedDelegate(Qt.QItemDelegate):
+ HMARGIN = 0
+ VMARGIN = 1
+
+ def __init__(self, parent=None, *args, actions={}):
+ Qt.QItemDelegate.__init__(self, parent, *args)
+ self._actions = actions
+ self.modelColumns = parent.model().columnCount()
+ self._style = QApplication.style()
+
+ def setConfig(self, actions):
+ self._actions = actions
+
+ #@profile_each_line
+ def paint(self, painter, option, index):
+ """Override default widget style to personalize it with our own.
+ """
+ if self._actions.get('actions') == None:
+ return super().paint(painter, option, index)
+ if not index.isValid():
+ return super().paint(painter, option, index)
+ cellValue = index.data(QtCore.Qt.DisplayRole)
+ if cellValue == None:
+ return super().paint(painter, option, index)
+
+ # initialize new QStyleOptionViewItem with the default options of this
+ # cell.
+ option = Qt.QStyleOptionViewItem(option)
+
+ # by default use item's default attributes.
+ # if we modify any of them, set it to False
+ nocolor=True
+
+ # don't call these functions in for-loops
+ cellRect = Qt.QRect(option.rect)
+ curColumn = index.column()
+ curRow = index.row()
+ cellAlignment = option.displayAlignment
+ defaultPen = painter.pen()
+ defaultBrush = painter.brush()
+
+ # get default margins in order to respect them.
+ # option.widget is the QTableView
+ hmargin = self._style.pixelMetric(
+ self._style.PM_FocusFrameHMargin, None, option.widget
+ ) + 1
+ vmargin = self._style.pixelMetric(
+ self._style.PM_FocusFrameVMargin, None, option.widget
+ ) + 1
+
+ # set default margins for this cell
+ cellRect.adjust(hmargin, vmargin, -painter.pen().width(), -painter.pen().width())
+
+ for a in self._actions['actions']:
+ action = self._actions['actions'][a]
+ modified = action.run(
+ (painter,
+ option,
+ index,
+ self._style,
+ self.modelColumns,
+ curRow,
+ curColumn,
+ defaultPen,
+ defaultBrush,
+ cellAlignment,
+ cellRect,
+ cellValue)
+ )
+ if modified[0]:
+ nocolor=False
+
+ if nocolor:
+ super().paint(painter, option, index)
diff --git a/ui/opensnitch/customwidgets/main.py b/ui/opensnitch/customwidgets/main.py
index 4d096f00..4812c49d 100644
--- a/ui/opensnitch/customwidgets/main.py
+++ b/ui/opensnitch/customwidgets/main.py
@@ -1,4 +1,4 @@
-from PyQt5 import Qt, QtCore
+from PyQt5 import QtCore
from PyQt5.QtGui import QColor, QStandardItemModel, QStandardItem
from PyQt5.QtSql import QSqlQueryModel, QSqlQuery, QSql
from PyQt5.QtWidgets import QTableView
@@ -7,37 +7,6 @@ import time
import math
from PyQt5.QtCore import QCoreApplication as QC
-
-class ColorizedDelegate(Qt.QItemDelegate):
- def __init__(self, parent=None, *args, config=None):
- Qt.QItemDelegate.__init__(self, parent, *args)
- self._config = config
- self._alignment = QtCore.Qt.AlignLeft | QtCore.Qt.AlignHCenter
-
- def paint(self, painter, option, index):
- if not index.isValid():
- return super().paint(painter, option, index)
-
- nocolor=True
-
- value = index.data(QtCore.Qt.DisplayRole)
- for _, what in enumerate(self._config):
- if what == value:
- nocolor=False
- painter.save()
- painter.setPen(self._config[what])
- if 'alignment' in self._config:
- self._alignment = self._config['alignment']
-
- if option.state & Qt.QStyle.State_Selected:
- painter.setBrush(painter.brush())
- painter.setPen(painter.pen())
- painter.drawText(option.rect, self._alignment, value)
- painter.restore()
-
- if nocolor == True:
- super().paint(painter, option, index)
-
class ColorizedQSqlQueryModel(QSqlQueryModel):
"""
model=CustomQSqlQueryModel(
diff --git a/ui/opensnitch/dialogs/stats.py b/ui/opensnitch/dialogs/stats.py
index 3fdc0c3f..0520b6a5 100644
--- a/ui/opensnitch/dialogs/stats.py
+++ b/ui/opensnitch/dialogs/stats.py
@@ -17,17 +17,15 @@ from opensnitch.dialogs.firewall import FirewallDialog
from opensnitch.dialogs.preferences import PreferencesDialog
from opensnitch.dialogs.ruleseditor import RulesEditorDialog
from opensnitch.dialogs.processdetails import ProcessDetailsDialog
-from opensnitch.customwidgets.main import ColorizedDelegate, ConnectionsTableModel
+from opensnitch.customwidgets.colorizeddelegate import ColorizedDelegate
from opensnitch.customwidgets.firewalltableview import FirewallTableModel
from opensnitch.customwidgets.generictableview import GenericTableModel
from opensnitch.customwidgets.addresstablemodel import AddressTableModel
from opensnitch.utils import Message, QuickHelp, AsnDB, Icons
+from opensnitch.actions import Actions
DIALOG_UI_PATH = "%s/../res/stats.ui" % os.path.dirname(sys.modules[__name__].__file__)
class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
- RED = QtGui.QColor(0xff, 0x63, 0x47)
- GREEN = QtGui.QColor(0x2e, 0x90, 0x59)
- PURPLE = QtGui.QColor(0x7f, 0x00, 0xff)
_trigger = QtCore.pyqtSignal(bool, bool)
settings_saved = QtCore.pyqtSignal()
@@ -120,205 +118,162 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
# try to restore last selection
LAST_SELECTED_ITEM = ""
- commonDelegateConf = {
- Config.ACTION_DENY: RED,
- Config.ACTION_REJECT: PURPLE,
- Config.ACTION_ALLOW: GREEN,
- 'alignment': QtCore.Qt.AlignCenter | QtCore.Qt.AlignHCenter
- }
-
- commonTableConf = {
- "name": "",
+ TABLES = {
+ TAB_MAIN: {
+ "name": "connections",
"label": None,
"cmd": None,
+ "cmdCleanStats": None,
"view": None,
+ "filterLine": None,
"model": None,
- "delegate": commonDelegateConf,
- "display_fields": "*"
- }
-
- TABLES = {
- TAB_MAIN: {
- "name": "connections",
- "label": None,
- "cmd": None,
- "cmdCleanStats": None,
- "view": None,
- "filterLine": None,
- "model": None,
- "delegate": commonDelegateConf,
- "display_fields": "time as Time, " \
- "node as Node, " \
- "action as Action, " \
- "CASE dst_host WHEN ''" \
- " THEN dst_ip || ' -> ' || dst_port " \
- " ELSE dst_host || ' -> ' || dst_port " \
- "END Destination, " \
- "protocol as Protocol, " \
- "process as Process, " \
- "process_args as Cmdline, " \
- "rule as Rule",
- "group_by": LAST_GROUP_BY,
- "last_order_by": "1",
- "last_order_to": 1
- },
- TAB_NODES: {
- "name": "nodes",
- "label": None,
- "cmd": None,
- "cmdCleanStats": None,
- "view": None,
- "filterLine": None,
- "model": None,
- "delegate": {
- Config.ACTION_DENY: RED,
- Config.ACTION_REJECT: PURPLE,
- Config.ACTION_ALLOW: GREEN,
- Nodes.OFFLINE: RED,
- Nodes.ONLINE: GREEN,
- 'alignment': QtCore.Qt.AlignCenter | QtCore.Qt.AlignHCenter
- },
- "display_fields": "last_connection as LastConnection, "\
- "addr as Addr, " \
- "status as Status, " \
- "hostname as Hostname, " \
- "daemon_version as Version, " \
- "daemon_uptime as Uptime, " \
- "daemon_rules as Rules," \
- "cons as Connections," \
- "cons_dropped as Dropped," \
- "version as Version",
- "header_labels": [],
- "last_order_by": "1",
- "last_order_to": 1
- },
- TAB_RULES: {
- "name": "rules",
- "label": None,
- "cmd": None,
- "cmdCleanStats": None,
- "view": None,
- "filterLine": None,
- "model": None,
- "delegate": commonDelegateConf,
- "display_fields": "time as Time," \
- "node as Node," \
- "name as Name," \
- "enabled as Enabled," \
- "action as Action," \
- "duration as Duration," \
- "description as Description",
- "header_labels": [],
- "last_order_by": "2",
- "last_order_to": 0
- },
- TAB_FIREWALL: {
- "name": "firewall",
- "label": None,
- "cmd": None,
- "cmdCleanStats": None,
- "view": None,
- "filterLine": None,
- "model": None,
- "delegate": {
- Config.ACTION_DENY: RED,
- Config.ACTION_DROP: RED,
- Config.ACTION_STOP: RED,
- "DROP": RED,
- "ACCEPT": GREEN,
- Config.ACTION_REJECT: PURPLE,
- Config.ACTION_RETURN: PURPLE,
- Config.ACTION_ACCEPT: GREEN,
- Config.ACTION_JUMP: GREEN,
- Config.ACTION_MASQUERADE: GREEN,
- Config.ACTION_SNAT: GREEN,
- Config.ACTION_DNAT: GREEN,
- Config.ACTION_TPROXY: GREEN,
- Config.ACTION_QUEUE: GREEN,
- "True": GREEN,
- "False": RED,
- 'alignment': QtCore.Qt.AlignCenter | QtCore.Qt.AlignHCenter
- },
- "display_fields": "*",
- "header_labels": [],
- "last_order_by": "2",
- "last_order_to": 0
- },
- TAB_HOSTS: {
- "name": "hosts",
- "label": None,
- "cmd": None,
- "cmdCleanStats": None,
- "view": None,
- "filterLine": None,
- "model": None,
- "delegate": commonDelegateConf,
- "display_fields": "*",
- "header_labels": [],
- "last_order_by": "2",
- "last_order_to": 1
- },
- TAB_PROCS: {
- "name": "procs",
- "label": None,
- "cmd": None,
- "cmdCleanStats": None,
- "view": None,
- "filterLine": None,
- "model": None,
- "delegate": commonDelegateConf,
- "display_fields": "*",
- "header_labels": [],
- "last_order_by": "2",
- "last_order_to": 1
- },
- TAB_ADDRS: {
- "name": "addrs",
- "label": None,
- "cmd": None,
- "cmdCleanStats": None,
- "view": None,
- "filterLine": None,
- "model": None,
- "delegate": commonDelegateConf,
- "display_fields": "*",
- "header_labels": [],
- "last_order_by": "2",
- "last_order_to": 1
- },
- TAB_PORTS: {
- "name": "ports",
- "label": None,
- "cmd": None,
- "cmdCleanStats": None,
- "view": None,
- "filterLine": None,
- "model": None,
- "delegate": commonDelegateConf,
- "display_fields": "*",
- "header_labels": [],
- "last_order_by": "2",
- "last_order_to": 1
- },
- TAB_USERS: {
- "name": "users",
- "label": None,
- "cmd": None,
- "cmdCleanStats": None,
- "view": None,
- "filterLine": None,
- "model": None,
- "delegate": commonDelegateConf,
- "display_fields": "*",
- "header_labels": [],
- "last_order_by": "2",
- "last_order_to": 1
- }
- }
+ "delegate": "commonDelegateConfig",
+ "display_fields": "time as Time, " \
+ "node as Node, " \
+ "action as Action, " \
+ "CASE dst_host WHEN ''" \
+ " THEN dst_ip || ' -> ' || dst_port " \
+ " ELSE dst_host || ' -> ' || dst_port " \
+ "END Destination, " \
+ "protocol as Protocol, " \
+ "process as Process, " \
+ "process_args as Cmdline, " \
+ "rule as Rule",
+ "group_by": LAST_GROUP_BY,
+ "last_order_by": "1",
+ "last_order_to": 1
+ },
+ TAB_NODES: {
+ "name": "nodes",
+ "label": None,
+ "cmd": None,
+ "cmdCleanStats": None,
+ "view": None,
+ "filterLine": None,
+ "model": None,
+ "delegate": "commonDelegateConfig",
+ "display_fields": "last_connection as LastConnection, "\
+ "addr as Addr, " \
+ "status as Status, " \
+ "hostname as Hostname, " \
+ "daemon_version as Version, " \
+ "daemon_uptime as Uptime, " \
+ "daemon_rules as Rules," \
+ "cons as Connections," \
+ "cons_dropped as Dropped," \
+ "version as Version",
+ "header_labels": [],
+ "last_order_by": "1",
+ "last_order_to": 1
+ },
+ TAB_RULES: {
+ "name": "rules",
+ "label": None,
+ "cmd": None,
+ "cmdCleanStats": None,
+ "view": None,
+ "filterLine": None,
+ "model": None,
+ "delegate": "defaultRulesDelegateConfig",
+ "display_fields": "time as Time," \
+ "node as Node," \
+ "name as Name," \
+ "enabled as Enabled," \
+ "action as Action," \
+ "duration as Duration," \
+ "description as Description",
+ "header_labels": [],
+ "last_order_by": "2",
+ "last_order_to": 0
+ },
+ TAB_FIREWALL: {
+ "name": "firewall",
+ "label": None,
+ "cmd": None,
+ "cmdCleanStats": None,
+ "view": None,
+ "filterLine": None,
+ "model": None,
+ "delegate": "defaultFWDelegateConfig",
+ "display_fields": "*",
+ "header_labels": [],
+ "last_order_by": "2",
+ "last_order_to": 0
+ },
+ TAB_HOSTS: {
+ "name": "hosts",
+ "label": None,
+ "cmd": None,
+ "cmdCleanStats": None,
+ "view": None,
+ "filterLine": None,
+ "model": None,
+ "delegate": "commonDelegateConfig",
+ "display_fields": "*",
+ "header_labels": [],
+ "last_order_by": "2",
+ "last_order_to": 1
+ },
+ TAB_PROCS: {
+ "name": "procs",
+ "label": None,
+ "cmd": None,
+ "cmdCleanStats": None,
+ "view": None,
+ "filterLine": None,
+ "model": None,
+ "delegate": "commonDelegateConfig",
+ "display_fields": "*",
+ "header_labels": [],
+ "last_order_by": "2",
+ "last_order_to": 1
+ },
+ TAB_ADDRS: {
+ "name": "addrs",
+ "label": None,
+ "cmd": None,
+ "cmdCleanStats": None,
+ "view": None,
+ "filterLine": None,
+ "model": None,
+ "delegate": "commonDelegateConfig",
+ "display_fields": "*",
+ "header_labels": [],
+ "last_order_by": "2",
+ "last_order_to": 1
+ },
+ TAB_PORTS: {
+ "name": "ports",
+ "label": None,
+ "cmd": None,
+ "cmdCleanStats": None,
+ "view": None,
+ "filterLine": None,
+ "model": None,
+ "delegate": "commonDelegateConfig",
+ "display_fields": "*",
+ "header_labels": [],
+ "last_order_by": "2",
+ "last_order_to": 1
+ },
+ TAB_USERS: {
+ "name": "users",
+ "label": None,
+ "cmd": None,
+ "cmdCleanStats": None,
+ "view": None,
+ "filterLine": None,
+ "model": None,
+ "delegate": "commonDelegateConfig",
+ "display_fields": "*",
+ "header_labels": [],
+ "last_order_by": "2",
+ "last_order_to": 1
+ }
+ }
def __init__(self, parent=None, address=None, db=None, dbname="db", appicon=None):
super(StatsDialog, self).__init__(parent)
- QtWidgets.QDialog.__init__(self, parent, QtCore.Qt.WindowStaysOnTopHint)
self._current_desktop = os.environ['XDG_CURRENT_DESKTOP'] if os.environ.get("XDG_CURRENT_DESKTOP") != None else None
@@ -367,6 +322,8 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
self._nodes = Nodes.instance()
self._fw = Firewall().instance()
self._fw.rules.rulesUpdated.connect(self._cb_fw_rules_updated)
+ self._actions = Actions().instance()
+ self._actions.loadAll()
# TODO: allow to display multiples dialogs
self._proc_details_dialog = ProcessDetailsDialog(appicon=appicon)
@@ -2474,9 +2431,6 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
tableWidget.setSortingEnabled(True)
if model == None:
model = self._db.get_new_qsql_model()
- if delegate != None:
- tableWidget.setItemDelegate(ColorizedDelegate(self, config=delegate))
-
if verticalScrollBar != None:
tableWidget.setVerticalScrollBar(verticalScrollBar)
tableWidget.verticalScrollBar().sliderPressed.connect(self._cb_scrollbar_pressed)
@@ -2485,6 +2439,11 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
self.setQuery(model, "SELECT " + fields + " FROM " + table_name + group_by + " ORDER BY " + order_by + " " + sort_direction + limit)
tableWidget.setModel(model)
+ if delegate != None:
+ action = self._actions.get(delegate)
+ if action != None:
+ tableWidget.setItemDelegate(ColorizedDelegate(tableWidget, actions=action))
+
header = tableWidget.horizontalHeader()
if header != None:
header.sortIndicatorChanged.connect(self._cb_table_header_clicked)
diff --git a/ui/opensnitch/res/stats.ui b/ui/opensnitch/res/stats.ui
index ef093d40..25c9f5f8 100644
--- a/ui/opensnitch/res/stats.ui
+++ b/ui/opensnitch/res/stats.ui
@@ -897,6 +897,9 @@
QAbstractItemView::SelectRows
+
+ QAbstractItemView::ScrollPerPixel
+
false