diff --git a/ui/opensnitch/nodes.py b/ui/opensnitch/nodes.py
index 4370b051..6782b987 100644
--- a/ui/opensnitch/nodes.py
+++ b/ui/opensnitch/nodes.py
@@ -134,6 +134,9 @@ class Nodes(QObject):
return nid, notif, rules_list
+ def disable_rule(self, addr, rule_name):
+ self._rules.disable(addr, rule_name)
+
def update_rule_time(self, time, rule_name, addr):
self._rules.update_time(time, rule_name, addr)
diff --git a/ui/opensnitch/rules.py b/ui/opensnitch/rules.py
index ca487d48..a0939c42 100644
--- a/ui/opensnitch/rules.py
+++ b/ui/opensnitch/rules.py
@@ -165,6 +165,16 @@ class Rules(QObject):
return rule_name
+ def disable(self, addr, name):
+ """Mark a rule as not enabled in the DB"""
+ self._db.update(
+ "rules",
+ "enabled='False'",
+ (name, addr),
+ "name=? AND node=?",
+ action_on_conflict="OR REPLACE"
+ )
+
def update_time(self, time, name, addr):
"""Updates the time of a rule, whenever a new connection matched a
rule.
diff --git a/ui/opensnitch/service.py b/ui/opensnitch/service.py
index da184fcd..8d9890ae 100644
--- a/ui/opensnitch/service.py
+++ b/ui/opensnitch/service.py
@@ -26,8 +26,15 @@ from opensnitch.nodes import Nodes
from opensnitch.config import Config
from opensnitch.version import version
from opensnitch.database import Database
-from opensnitch.utils import Utils, CleanerTask, Themes
-from opensnitch.utils import Message, languages
+from opensnitch.utils import (
+ Utils,
+ CleanerTask,
+ Themes,
+ OneshotTimer,
+ languages,
+ Message
+)
+from opensnitch.utils.duration import duration
from opensnitch.utils.xdg import Autostart
class UIService(ui_pb2_grpc.UIServicer, QtWidgets.QGraphicsObject):
@@ -738,6 +745,16 @@ class UIService(ui_pb2_grpc.UIServicer, QtWidgets.QGraphicsObject):
# daemon.
rule.operator.data = ""
+ # disable temporal rules in the db
+ def _disable_temp_rule(args):
+ self._nodes.disable_rule(args[0], args[1].name)
+ timeout = duration.to_seconds(rule.duration)
+ if rule.duration == Config.DURATION_ONCE:
+ timeout = 1
+ if timeout > 0:
+ ost = OneshotTimer(timeout, _disable_temp_rule, (kwargs['peer'], rule,))
+ ost.start()
+
elif kwargs['action'] == self.DELETE_RULE:
self._db.delete_rule(kwargs['name'], kwargs['addr'])
diff --git a/ui/opensnitch/utils/__init__.py b/ui/opensnitch/utils/__init__.py
index 03793949..668d7a53 100644
--- a/ui/opensnitch/utils/__init__.py
+++ b/ui/opensnitch/utils/__init__.py
@@ -227,7 +227,13 @@ class OneshotTimer(GenericTimer):
def run(self):
self.stop_flag.wait(self.interval)
+ if self.stop_flag.is_set():
+ return
self.callback(self.args)
+ self.stop()
+
+ def stop(self):
+ self.stop_flag.set()
class CleanerTask(Thread):
interval = 1
diff --git a/ui/opensnitch/utils/duration/__init__.py b/ui/opensnitch/utils/duration/__init__.py
new file mode 100644
index 00000000..aee2b892
--- /dev/null
+++ b/ui/opensnitch/utils/duration/__init__.py
@@ -0,0 +1,20 @@
+# Copyright (C) 2018 Simone Margaritelli
+# 2018 MiWCryptAnalytics
+# 2023 munix9
+# 2023 Wojtek Widomski
+# 2019-2025 Gustavo Iñiguez Goia
+#
+# This file is part of OpenSnitch.
+#
+# OpenSnitch 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 3 of the License, or
+# (at your option) any later version.
+#
+# OpenSnitch 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 OpenSnitch. If not, see .
diff --git a/ui/opensnitch/utils/duration/duration.py b/ui/opensnitch/utils/duration/duration.py
new file mode 100644
index 00000000..fb614fa1
--- /dev/null
+++ b/ui/opensnitch/utils/duration/duration.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2018 Simone Margaritelli
+# 2018 MiWCryptAnalytics
+# 2023 munix9
+# 2023 Wojtek Widomski
+# 2019-2025 Gustavo Iñiguez Goia
+#
+# This file is part of OpenSnitch.
+#
+# OpenSnitch 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 3 of the License, or
+# (at your option) any later version.
+#
+# OpenSnitch 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 OpenSnitch. If not, see .
+
+
+import re
+
+r = re.compile(r'(\d+)([smhdw]+)')
+
+_second = 1
+_minute = 60
+_hour = 60 * _minute
+_day = 60 * _hour
+_week = 60 * _day
+
+_units = {
+ 's': _second,
+ 'm': _minute,
+ 'h': _hour,
+ 'd': _day,
+ 'w': _week
+}
+
+def to_seconds(dur_str):
+ """converts a Golang duration string to seconds:
+ "20s" -> 20 seconds
+ "2m" -> 120 seconds
+ ...
+ """
+ secs = 0
+ try:
+ finds = r.findall(dur_str)
+ for d in finds:
+ try:
+ unit = _units[d[1]]
+ secs += (unit * int(d[0]))
+ except:
+ print("duration.to_seconds(): invalid unit:", d)
+
+ return secs
+ except:
+ return secs