#!@PYTHON@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2010-2015 Red Hat, Inc.
#
# Authors:
# Thomas Woerner <twoerner@redhat.com>
#
# This program 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 2 of the License, or
# (at your option) any later version.
#
# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
#
import sys
from PyQt5 import QtGui, QtCore, QtWidgets
import gi
gi.require_version('Notify', '0.7')
from gi.repository import Notify
import os
from dbus.mainloop.pyqt5 import DBusQtMainLoop
import functools
from firewall import config
from firewall.core.fw_nm import nm_is_imported, nm_get_zone_of_connection, \
nm_set_zone_of_connection, \
nm_get_dbus_interface, \
nm_get_connections
from firewall.core.watcher import Watcher
from firewall.client import FirewallClient
import slip.dbus
import dbus
import signal
import gettext
gettext.textdomain(config.DOMAIN)
_ = gettext.gettext
PATH = [ ]
for p in os.getenv("PATH").split(":"):
if p not in PATH:
PATH.append(p)
def search_app(app):
for p in PATH:
_app = "%s/%s" % (p, app)
if os.path.exists(_app):
return _app
return None
NM_CONNECTION_EDITOR = ""
for binary in [ "/usr/bin/nm-connection-editor",
"/bin/nm-connection-editor",
"/usr/bin/kde5-nm-connection-editor",
"/bin/kde5-nm-connection-editor",
"/usr/bin/kde-nm-connection-editor",
"/bin/kde-nm-connection-editor" ]:
if os.path.exists(binary):
NM_CONNECTION_EDITOR = binary
break
PY2 = sys.version < '3'
def escape(text):
text = text.replace('&', '&')
text = text.replace('>', '>')
text = text.replace('<', '<')
return text
def fromUTF8(text):
if PY2 and QtCore.QT_VERSION < 0x050000:
return QtCore.QString.fromUtf8(text)
return text
# ZoneInterfaceEditor #########################################################
class ZoneInterfaceEditor(QtWidgets.QDialog):
def __init__(self, fw, interface, zone):
self.fw = fw
self.interface = interface
self.zone = None
self.title = _("Select zone for interface '%s'") % self.interface
QtWidgets.QDialog.__init__(self)
self.create_ui(zone)
def create_ui(self, zone):
self.setWindowTitle(fromUTF8(escape(self.title)))
self.rejected.connect(self.hide)
self.resize(100, 50)
vbox = QtWidgets.QVBoxLayout()
vbox.setSpacing(6)
label = QtWidgets.QLabel(fromUTF8(escape(self.title)))
vbox.addWidget(label)
self.combo = QtWidgets.QComboBox()
self.fill_zone_combo()
vbox.addWidget(self.combo)
buttonBox = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok
| QtWidgets.QDialogButtonBox.Cancel)
self.ok_button = buttonBox.button(QtWidgets.QDialogButtonBox.Ok)
buttonBox.accepted.connect(self.ok)
buttonBox.rejected.connect(self.hide)
vbox.addWidget(buttonBox)
self.ok_button.setDisabled(True)
self.combo.activated.connect(self.combo_changed)
self.setLayout(vbox)
self.set_zone(zone)
def combo_changed(self):
self.ok_button.setDisabled(self.get_zone() == self.zone)
def set_zone(self, zone):
self.zone = zone
if zone == "":
self.combo.setCurrentIndex(self.combo.findText(
escape(_("Default Zone"))))
else:
self.combo.setCurrentIndex(self.combo.findText(self.zone))
self.combo_changed()
def get_zone(self):
text = str(self.combo.currentText())
if text == escape(_("Default Zone")):
text = ""
return text
def fill_zone_combo(self):
self.combo.clear()
self.combo.addItem(fromUTF8(escape(_("Default Zone"))))
for z in self.fw.getZones():
self.combo.addItem(z)
def zones_changed(self):
zone = self.get_zone()
self.fill_zone_combo()
self.set_zone(zone)
def ok(self):
self.fw.changeZoneOfInterface(self.get_zone(), self.interface)
self.hide()
# ZoneConnectionEditor ########################################################
class ZoneConnectionEditor(ZoneInterfaceEditor):
def __init__(self, fw, connection, connection_name, zone):
self.fw = fw
self.connection = connection
self.connection_name = connection_name
self.zone = None
self.title = _("Select zone for connection '%s'") % self.connection_name
QtWidgets.QDialog.__init__(self)
self.create_ui(zone)
def ok(self):
# apply changes
try:
nm_set_zone_of_connection(self.get_zone(), self.connection)
except Exception:
text = _("Failed to set zone {zone} for connection {connection_name}")
QtWidgets.QMessageBox.warning(None, fromUTF8(escape(self.title)),
escape(text.format(
zone=self.get_zone(),
connection_name=self.connection_name)))
self.hide()
# ZoneSourceEditor ############################################################
class ZoneSourceEditor(ZoneInterfaceEditor):
def __init__(self, fw, source, zone):
self.fw = fw
self.source = source
self.zone = None
self.title = _("Select zone for source '%s'") % self.source
QtWidgets.QDialog.__init__(self)
self.create_ui(zone)
def ok(self):
self.fw.changeZoneOfSource(self.get_zone(), self.source)
self.hide()
# ShieldsEditor #########################################################
class ShieldsEditor(QtWidgets.QDialog):
def __init__(self, fw, settings, shields_up, shields_down):
self.fw = fw
self.settings = settings
self.shields_up = shields_up
self.shields_down = shields_down
self.title = _("Configure Shields Up/Down Zones")
QtWidgets.QDialog.__init__(self)
self.create_ui()
def create_ui(self):
self.setWindowTitle(fromUTF8(escape(self.title)))
self.rejected.connect(self.hide)
vbox = QtWidgets.QVBoxLayout()
vbox.setSpacing(6)
label = QtWidgets.QLabel(fromUTF8(escape(
_("Here you can select the zones used for Shields Up and "
"Shields Down."))))
label.setWordWrap(True)
vbox.addWidget(label)
label = QtWidgets.QLabel(fromUTF8(escape(
_("This feature is useful for people using the default zones "
"mostly. For users, that are changing zones of connections, it "
"might be of limited use."))))
label.setWordWrap(True)
vbox.addWidget(label)
grid = QtWidgets.QGridLayout()
grid.setSpacing(6)
label = QtWidgets.QLabel(fromUTF8(escape(_("Shields Up Zone:"))))
label.setWordWrap(True)
grid.addWidget(label, 0, 0, 1, 1)
self.shields_up_combo = QtWidgets.QComboBox()
#self.fill_combo(self.shields_up_combo)
#self.set_shields_up(self.shields_up)
grid.addWidget(self.shields_up_combo, 0, 1, 1, 1)
button = QtWidgets.QPushButton(_("Reset To Default"))
button.clicked.connect(self.reset_shields_up)
grid.addWidget(button, 0, 2, 1, 1)
label = QtWidgets.QLabel(fromUTF8(escape(_("Shields Down Zone:"))))
label.setWordWrap(True)
grid.addWidget(label, 1, 0, 1, 1)
self.shields_down_combo = QtWidgets.QComboBox()
#self.fill_combo(self.shields_down_combo)
#self.set_shields_down(self.shields_down)
grid.addWidget(self.shields_down_combo, 1, 1, 1, 1)
button = QtWidgets.QPushButton(_("Reset To Default"))
button.clicked.connect(self.reset_shields_down)
grid.addWidget(button, 1, 2, 1, 1)
vbox.addLayout(grid)
buttonBox = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok
| QtWidgets.QDialogButtonBox.Cancel)
self.ok_button = buttonBox.button(QtWidgets.QDialogButtonBox.Ok)
buttonBox.accepted.connect(self.ok)
buttonBox.rejected.connect(self.hide)
vbox.addWidget(buttonBox)
self.ok_button.setDisabled(True)
self.shields_up_combo.activated.connect(self.shields_combo_changed)
self.shields_down_combo.activated.connect(self.shields_combo_changed)
self.setLayout(vbox)
def shields_combo_changed(self):
self.ok_button.setDisabled(
self.get_shields_up() == self.shields_up and \
self.get_shields_down() == self.shields_down)
def set_shields_up(self, zone):
self.shields_up = zone
if self.shields_up_combo.count() > 0:
self.shields_up_combo.setCurrentIndex(
self.shields_up_combo.findText(self.shields_up))
self.shields_combo_changed()
def set_shields_down(self, zone):
self.shields_down = zone
if self.shields_down_combo.count() > 0:
self.shields_down_combo.setCurrentIndex(
self.shields_down_combo.findText(self.shields_down))
self.shields_combo_changed()
def reset_shields_up(self):
self.set_shields_up(self.shields_up)
# remove user key to get fallback again
self.settings.remove("shields-up")
def reset_shields_down(self):
self.set_shields_down(self.shields_down)
# remove user key to get fallback again
self.settings.remove("shields-down")
def get_shields_up(self):
return str(self.shields_up_combo.currentText())
def get_shields_down(self):
return str(self.shields_down_combo.currentText())
def zones_changed(self):
up_zone = self.shields_up
if self.get_shields_up():
up_zone = self.get_shields_up()
down_zone = self.shields_down
if self.get_shields_down():
down_zone = self.get_shields_down()
for z in self.fw.getZones():
self.shields_up_combo.addItem(z)
self.shields_down_combo.addItem(z)
self.set_shields_up(up_zone)
self.set_shields_down(down_zone)
def ok(self):
if self.shields_up != self.get_shields_up():
self.settings.setValue("shields-up", self.get_shields_up())
if self.shields_down != self.get_shields_down():
self.settings.setValue("shields-down", self.get_shields_down())
self.settings.sync()
self.hide()
# AboutDialog #################################################################
class AboutDialog(QtWidgets.QDialog):
def __init__(self, name, icon, version, url, copyright, authors, license):
QtWidgets.QDialog.__init__(self)
self.setWindowIcon(icon)
self.setWindowTitle(fromUTF8(escape(_("About %s" % name))))
self.resize(500, 250)
vbox = QtWidgets.QVBoxLayout()
vbox.setSpacing(6)
hbox = QtWidgets.QHBoxLayout()
hbox.setSpacing(24)
label = QtWidgets.QLabel()
label.setPixmap(icon.pixmap(96))
label.setMinimumSize(96, 96)
label.setMaximumSize(96, 96)
hbox.addWidget(label)
vbox2 = QtWidgets.QVBoxLayout()
vbox2.setSpacing(3)
label = QtWidgets.QLabel(name)
font = label.font()
font.setPointSize(font.pointSize()*2)
font.setBold(True)
label.setFont(font)
vbox2.addWidget(label)
vbox2.addWidget(QtWidgets.QLabel(version))
label = QtWidgets.QLabel("<a href=\"%s\">%s</a>" % (url, url))
label.setTextFormat(QtCore.Qt.RichText)
label.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction)
label.setOpenExternalLinks(True)
vbox2.addWidget(label)
vbox2.addWidget(QtWidgets.QLabel(copyright))
hbox.addLayout(vbox2)
vbox.addLayout(hbox)
tabs = QtWidgets.QTabWidget()
tabs.setStyleSheet("QTabWidget::tab { padding: 1px 1px 1px 1px; }")
tab = QtWidgets.QWidget()
vbox3 = QtWidgets.QVBoxLayout()
textedit = QtWidgets.QPlainTextEdit()
#textedit.setStyleSheet("QPlainTextEdit { border: 0; padding: 0; }")
textedit.setReadOnly(True)
textedit.setPlainText(fromUTF8("\n".join(authors)))
vbox3.addWidget(textedit)
tab.setLayout(vbox3)
tabs.addTab(tab, fromUTF8(escape(_("Authors"))))
tab = QtWidgets.QWidget()
vbox3 = QtWidgets.QVBoxLayout()
textedit = QtWidgets.QPlainTextEdit()
#textedit.setStyleSheet("QPlainTextEdit { border: 0; padding: 0; }")
textedit.setReadOnly(True)
textedit.setPlainText(license)
vbox3.addWidget(textedit)
tab.setLayout(vbox3)
tabs.addTab(tab, fromUTF8(escape(_("License"))))
vbox.addWidget(tabs)
buttonBox = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Close)
buttonBox.rejected.connect(self.hide)
vbox.addWidget(buttonBox)
self.setLayout(vbox)
# TrayApplet ##################################################################
class TrayApplet(QtWidgets.QSystemTrayIcon):
def __init__(self):
super(TrayApplet, self).__init__()
self.name = _("Firewall Applet")
self.prog = "firewall-applet"
self.icon_name = "firewall-applet"
self.icons = {
"normal": QtGui.QIcon.fromTheme(self.icon_name),
"error": QtGui.QIcon.fromTheme(self.icon_name+"-error"),
"panic": QtGui.QIcon.fromTheme(self.icon_name+"-panic"),
"normal-shields_up": QtGui.QIcon.fromTheme(self.icon_name+"-shields_up"),
"normal-shields_down": QtGui.QIcon.fromTheme(self.icon_name+"-shields_down"),
}
self.timer = None
self.mode = None
self.blink = False
self.blink_count = 0
self._blink = False
self._blink_count = 0
self.show_inactive = False
self.tooltip_messages = [ ]
self.active_zones = { }
self.connections = { }
self.connections_name = { }
self.default_zone = None
self.zone_connection_editors = { }
self.zone_interface_editors = { }
self.zone_source_editors = { }
# settings
self.settings = QtCore.QSettings("firewall", "applet")
# file system watcher
self.watcher = Watcher(self.load_settings, 2)
self.watcher.add_watch_file("/etc/firewall/applet.conf")
self.watcher.add_watch_file(str(self.settings.fileName()))
# about dialog
self.about_dialog = AboutDialog(self.name,
self.icons["normal"],
config.VERSION, config.WEBSITE,
config.COPYRIGHT, config.AUTHORS,
config.LICENSE)
# urgencies
self.urgencies = { "noicon": QtWidgets.QSystemTrayIcon.NoIcon,
"information": QtWidgets.QSystemTrayIcon.Information,
"warning": QtWidgets.QSystemTrayIcon.Warning,
"critical": QtWidgets.QSystemTrayIcon.Critical }
# actions
self.shieldsupAction = QtWidgets.QAction(fromUTF8(escape(_("Shields Up"))),
self)
self.shieldsupAction.setCheckable(True)
self.shieldsupAction.setChecked(False)
self.shieldsupAction.triggered.connect(self.shieldsup_changed_cb)
self.notificationsAction = QtWidgets.QAction(
fromUTF8(escape(_("Enable Notifications"))), self)
self.notificationsAction.setCheckable(True)
self.notificationsAction.setChecked(False)
self.notificationsAction.triggered.connect(self.notification_changed_cb)
self.settingsAction = QtWidgets.QAction(
fromUTF8(escape(_("Edit Firewall Settings..."))), self)
self.settingsAction.triggered.connect(self.configure_cb)
self.changeZonesAction = QtWidgets.QAction(
fromUTF8(escape(_("Change Zones of Connections..."))), self)
self.changeZonesAction.triggered.connect(self.nm_connection_editor)
self.shieldsAction = QtWidgets.QAction(
fromUTF8(escape(_("Configure Shields UP/Down Zones..."))), self)
self.shieldsAction.triggered.connect(self.configure_shields)
self.panicAction = QtWidgets.QAction(
fromUTF8(escape(_("Block all network traffic"))), self)
self.panicAction.setCheckable(True)
self.panicAction.setChecked(False)
self.panicAction.triggered.connect(self.panic_mode_cb)
self.aboutAction = QtWidgets.QAction(fromUTF8(escape(_("About"))), self)
self.aboutAction.triggered.connect(self.about_dialog.exec_)
#self.quitAction = QtWidgets.QAction(fromUTF8(escape(_("Quit"))), self,
# triggered=self.quit)
self.connectionsAction = QtWidgets.QWidgetAction(self)
self.connectionsAction.setDefaultWidget(QtWidgets.QLabel(
fromUTF8("<b>"+escape(_("Connections"))+"</b> ")))
self.interfacesAction = QtWidgets.QWidgetAction(self)
self.interfacesAction.setDefaultWidget(QtWidgets.QLabel(
fromUTF8("<b>"+escape(_("Interfaces"))+"</b> ")))
self.sourcesAction = QtWidgets.QWidgetAction(self)
self.sourcesAction.setDefaultWidget(QtWidgets.QLabel(
fromUTF8("<b>"+escape(_("Sources"))+"</b> ")))
# init
self.left_menu = QtWidgets.QMenu()
self.left_menu.setStyleSheet('QMenu { margin: 5px; }')
self.right_menu = QtWidgets.QMenu()
self.right_menu.addAction(self.shieldsupAction)
self.right_menu.addAction(self.notificationsAction)
self.right_menu.addSeparator()
self.right_menu.addAction(self.settingsAction)
self.right_menu.addAction(self.changeZonesAction)
self.right_menu.addAction(self.shieldsAction)
self.right_menu.addSeparator()
self.right_menu.addAction(self.panicAction)
self.right_menu.addSeparator()
self.right_menu.addAction(self.aboutAction)
#self.right_menu.addSeparator()
#self.right_menu.addAction(self.quitAction)
self.setContextMenu(self.right_menu)
self.activated.connect(self.activated_cb)
self.set_mode("error")
self.set_icon()
self.setVisible(self.show_inactive)
# init notification
Notify.init(self.prog)
# connect to firewalld
DBusQtMainLoop(set_as_default=True)
try:
self.bus = slip.dbus.SystemBus()
self.bus.default_timeout = None
except Exception as msg:
print("Not using slip", msg)
self.bus = dbus.SystemBus()
if nm_is_imported():
self.bus.add_signal_receiver(
self.nm_signal_receiver,
dbus_interface=nm_get_dbus_interface(),
signal_name='PropertiesChanged',
member_keyword='member')
self.nm_signal_receiver()
self.fw = FirewallClient(self.bus, wait=1)
self.fw.setExceptionHandler(self._exception_handler)
self.fw.connect("connection-established", self.connection_established)
self.fw.connect("connection-lost", self.connection_lost)
self.fw.connect("reloaded", self.reloaded),
self.fw.connect("default-zone-changed", self.default_zone_changed)
self.fw.connect("panic-mode-enabled", self.panic_mode_enabled)
self.fw.connect("panic-mode-disabled", self.panic_mode_disabled)
self.fw.connect("interface-added", self.interface_added)
self.fw.connect("interface-removed", self.interface_removed)
self.fw.connect("zone-of-interface-changed",
self.zone_of_interface_changed)
self.fw.connect("source-added", self.source_added)
self.fw.connect("source-removed", self.source_removed)
self.fw.connect("zone-of-source-changed",
self.zone_of_source_changed)
self.shields_editor = ShieldsEditor(self.fw, self.settings, None, None)
self.load_settings()
def _exception_handler(self, exception_message):
if "NotAuthorizedException" in exception_message:
self.error(fromUTF8(escape(_("Authorization failed."))))
elif "INVALID_NAME" in exception_message:
msg = exception_message.replace("INVALID_NAME", _("Invalid name"))
self.warning(fromUTF8(escape(msg)))
elif "NAME_CONFLICT" in exception_message:
msg = exception_message.replace("NAME_CONFLICT",
_("Name already exists"))
self.warning(fromUTF8(escape(msg)))
elif "NO_DEFAULTS" in exception_message:
pass
else:
self.error(fromUTF8(exception_message))
def quit(self):
sys.exit(1)
def set_icon(self, mode=None):
if mode is not None:
self.setIcon(self.icons[mode])
elif self.mode != "normal":
self.setIcon(self.icons[self.mode])
elif self.default_zone == self.shields_up:
self.setIcon(self.icons["normal-shields_up"])
else:
self.setIcon(self.icons["normal-shields_down"])
def load_settings(self, name=None):
self.settings.sync()
notifications = self.settings.value("notifications", False, type=bool)
self.notificationsAction.setChecked(notifications)
self.show_inactive = self.settings.value("show-inactive", False,
type=bool)
self.blink = self.settings.value("blink", False, type=bool)
self.blink_count = self.settings.value("blink-count", 5, type=int)
self.shields_up = self.settings.value("shields-up", "block", type=str)
if self.default_zone:
self.shieldsupAction.setChecked(
self.default_zone == self.shields_up)
self.shields_editor.set_shields_up(self.shields_up)
self.shields_down = self.settings.value("shields-down", "public",
type=str)
self.shields_editor.set_shields_down(self.shields_down)
#print("shields-up=%s" % self.shields_up)
#print("notifications=%s" % notifications)
#print("blink=%s" % self.blink)
#print("blink-count=%s" % self.blink_count)
#print("show-inactive=%s" % self.show_inactive)
if not self.fw.connected:
self.setVisible(self.show_inactive)
else:
self.setVisible(True)
def activated_cb(self, reason):
if reason == QtWidgets.QSystemTrayIcon.Trigger:
self.left_menu.popup(QtGui.QCursor.pos())
def update_active_zones(self):
self.active_zones.clear()
# remove all entries for the left menu
self.left_menu.clear()
# add connections entry
self.left_menu.addAction(self.connectionsAction)
if not self.fw.connected:
return
active_zones = self.fw.getActiveZones()
if active_zones:
self.active_zones = active_zones
# get all active connections (NM) and interfaces
connections = { }
interfaces = { }
sources = { }
for zone in sorted(self.active_zones):
if "interfaces" in self.active_zones[zone]:
for interface in sorted(self.active_zones[zone]["interfaces"]):
if interface not in self.connections:
interfaces[interface] = zone
if "sources" in self.active_zones[zone]:
for source in sorted(self.active_zones[zone]["sources"]):
sources[source] = zone
# NM controlled connections
for interface in self.connections:
connection = self.connections[interface]
if connection not in self.connections_name:
connection_name = None
else:
connection_name = self.connections_name[connection]
zone = nm_get_zone_of_connection(connection)
connections[connection] = [ zone, connection_name ]
binding = _("{entry} (Zone: {zone})")
# add NM controlled bindings
for connection in sorted(connections):
zone = connections[connection][0]
connection_name = connections[connection][1]
if zone == "":
_binding = _("{entry} (Default Zone: {default_zone})")
action = QtWidgets.QAction(
fromUTF8(escape(
_binding.format(default_zone=self.default_zone,
entry=connection_name))), self)
else:
action = QtWidgets.QAction(
fromUTF8(escape(binding.format(zone=zone,
entry=connection_name))), self)
action.triggered.connect(functools.partial(
self.zone_connection_editor, connection, connection_name, zone))
self.left_menu.addAction(action)
# add interfaces entry
self.left_menu.addAction(self.interfacesAction)
# add other interfaces
for interface in sorted(interfaces):
zone = interfaces[interface]
action = QtWidgets.QAction(
fromUTF8(escape(binding.format(zone=zone, entry=interface))),
self)
action.triggered.connect(functools.partial(
self.zone_interface_editor, interface, zone))
self.left_menu.addAction(action)
# add interfaces entry
self.left_menu.addAction(self.sourcesAction)
for source in sorted(sources):
zone = sources[source]
action = QtWidgets.QAction(
fromUTF8(escape(binding.format(zone=zone, entry=source))),
self)
action.triggered.connect(functools.partial(
self.zone_source_editor, source, zone))
self.left_menu.addAction(action)
def zone_interface_editor(self, interface, zone):
if interface in self.zone_interface_editors:
self.zone_interface_editors[interface].set_zone(zone)
self.zone_interface_editors[interface].show()
return self.zone_interface_editors[interface].raise_()
editor = ZoneInterfaceEditor(self.fw, interface, zone)
self.zone_interface_editors[interface] = editor
editor.show()
editor.raise_()
editor.show()
def zone_connection_editor(self, connection, connection_name, zone):
if connection in self.zone_connection_editors:
self.zone_connection_editors[connection].set_zone(zone)
self.zone_connection_editors[connection].show()
return self.zone_connection_editors[connection].raise_()
editor = ZoneConnectionEditor(self.fw, connection, connection_name, zone)
self.zone_connection_editors[connection] = editor
editor.show()
editor.raise_()
editor.show()
def zone_source_editor(self, source, zone):
if source in self.zone_source_editors:
self.zone_source_editors[source].set_zone(zone)
self.zone_source_editors[source].show()
return self.zone_source_editors[source].raise_()
editor = ZoneSourceEditor(self.fw, source, zone)
self.zone_source_editors[source] = editor
editor.show()
editor.raise_()
editor.show()
def nm_signal_receiver(self, *args, **kwargs):
self.connections.clear()
self.connections_name.clear()
# do not use NMClient could result in python core dump
if nm_is_imported():
text = _("Failed to get connections from NetworkManager")
try:
nm_get_connections(self.connections, self.connections_name)
except Exception:
self.notify(escape(text), urgency=Notify.Urgency.CRITICAL)
if text not in self.tooltip_messages:
self.tooltip_messages.append(text)
else:
if text in self.tooltip_messages:
self.tooltip_messages.remove(text)
else:
text = _("No NetworkManager imports available")
self.notify(escape(text), urgency=Notify.Urgency.CRITICAL)
if text not in self.tooltip_messages:
self.tooltip_messages.append(text)
self.update_tooltip()
def notify(self, msg, urgency="noicon", timeout=5):
#self.showMessage(fromUTF8(escape(self.name)), msg, self.urgencies[urgency], timeout*1000)
n = Notify.Notification.new(escape(self.name), msg, self.icon_name)
n.set_urgency(Notify.Urgency.NORMAL)
try:
n.show()
except:
return
def shieldsup_changed_cb(self):
if self.shieldsupAction.isChecked():
zone = str(self.shields_up)
else:
zone = str(self.shields_down)
if self.fw.connected and self.default_zone != zone:
try:
self.fw.setDefaultZone(zone)
except dbus.exceptions.DBusException as e:
print("Error: %s" % e.get_dbus_message())
def notification_changed_cb(self):
self.settings.setValue("notifications",
self.notificationsAction.isChecked())
self.settings.sync()
def __blink(self, arg=None):
if self._blink_count != 0:
if self._blink_count > 0 and self._blink:
self._blink_count -= 1
self._blink = not self._blink
if not self.timer:
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self.__blink)
self.timer.setInterval(1000)
self.timer.start()
if not self._blink:
self.set_icon()
else:
self.set_icon("normal")
def get_mode(self):
return self.mode
def set_mode(self, mode):
if self.mode != mode:
if self.timer and self.timer.isActive():
self.timer.stop()
self._blink = False
self.mode = mode
elif self.mode == mode and self.timer:
if self._blink_count == 0:
self._blink_count += 1
return
if mode == "normal":
self.set_icon()
return
if self.blink:
if self.blink_count != 0:
self._blink = True
self._blink_count = self.blink_count
self.__blink()
else:
self.set_icon()
def update_tooltip(self):
if self.get_mode() == "error":
self.setToolTip(fromUTF8("<big><b>" + \
_("No connection to firewall daemon") + \
"</b></big>"))
return
messages = [ ]
if self.panicAction.isChecked():
messages.append("<big><b>" + \
_("All network traffic is blocked.") + \
"</b></big>")
if self.default_zone:
messages.append(_("Default Zone: '%s'") % self.default_zone)
for interface in self.connections:
connection = self.connections[interface]
zone = nm_get_zone_of_connection(connection)
if zone == "":
text = _("Default Zone '{default_zone}' active for connection "
"'{connection}' on interface '{interface}'")
else:
text = _("Zone '{zone}' active for connection "
"'{connection}' on interface '{interface}'")
messages.append(text.format(zone=zone,
default_zone=self.default_zone,
connection=connection,
interface=interface))
if len(self.active_zones) > 0:
for zone in sorted(self.active_zones):
if "interfaces" in self.active_zones[zone]:
for interface in sorted(self.active_zones[zone]["interfaces"]):
if interface not in self.connections:
text = _("Zone '{zone}' active for interface "
"'{interface}'")
connection = None
messages.append(text.format(zone=zone,
connection=connection,
interface=interface))
if "sources" in self.active_zones[zone]:
for source in sorted(self.active_zones[zone]["sources"]):
text = _("Zone '{zone}' active for source {source}")
connection = None
messages.append(text.format(zone=zone, source=source))
else:
messages.append(_("No Active Zones."))
messages.extend(self.tooltip_messages)
tooltip = "<nobr>"+"</nobr><br><nobr>".join(messages)+"</nobr>"
self.setToolTip(fromUTF8("<font size=\"3\">"+tooltip+"</font>"))
self.set_icon()
def show(self):
# do not automatically show the applet
pass
def panic_mode_cb(self):
if not self.fw or not self.fw.connected:
return
if self.panicAction.isChecked():
self.fw.enablePanicMode()
else:
self.fw.disablePanicMode()
self.panicAction.setChecked(not self.panicAction.isChecked())
def configure_shields(self):
self.shields_editor.show()
self.shields_editor.raise_()
def nm_connection_editor(self, item, uuid=None):
if NM_CONNECTION_EDITOR == "":
self.warning("NetworkManager connection editor is missing.")
return
if uuid:
if "kde-" in NM_CONNECTION_EDITOR:
os.system("%s %s &" % (NM_CONNECTION_EDITOR, uuid))
else:
os.system("%s --edit=%s &" % (NM_CONNECTION_EDITOR, uuid))
else:
os.system("%s &" % NM_CONNECTION_EDITOR)
def warning(self, text):
QtWidgets.QMessageBox.warning(None, fromUTF8(escape(self.name)), text)
def error(self, text):
QtWidgets.QMessageBox.critical(None, fromUTF8(escape(self.name)), text)
def configure_cb(self, widget):
os.system("firewall-config &")
# firewallClient signal receivers
def connection_established(self, first=False):
self.default_zone = self.fw.getDefaultZone()
self.panicAction.setChecked(self.fw.queryPanicMode())
self.update_active_zones()
self.shields_editor.zones_changed()
if self.shields_up:
self.shieldsupAction.setChecked(
self.default_zone == self.shields_up)
if self.notificationsAction.isChecked():
self.notify(escape(_("Connection to FirewallD established.")))
self.setVisible(True)
self.set_mode("normal")
self.update_tooltip()
def connection_lost(self):
self.default_zone = None
self.set_mode("error")
self.update_active_zones()
self.update_tooltip()
self.panicAction.setChecked(False)
if self.notificationsAction.isChecked():
self.notify(escape(_("Connection to FirewallD lost.")))
self.setVisible(self.show_inactive)
def reloaded(self):
if self.notificationsAction.isChecked():
self.notify(escape(_("FirewallD has been reloaded.")))
self.update_active_zones()
self.update_tooltip()
def default_zone_changed(self, zone):
self.default_zone = zone
if self.notificationsAction.isChecked():
self.notify(escape(_("Default zone changed to '%s'.") % zone))
if self.shields_up:
self.shieldsupAction.setChecked(
self.default_zone == self.shields_up)
self.update_active_zones()
self.update_tooltip()
def _panic_mode(self, enable):
self.panicAction.setChecked(enable)
self.update_tooltip()
if enable:
self.set_mode("panic")
else:
self.set_mode("normal")
if self.notificationsAction.isChecked():
ed = { 1: _("All network traffic is blocked."),
0: _("Network traffic is not blocked anymore.") }
self.notify(escape(ed[enable]))
def panic_mode_enabled(self):
self._panic_mode(True)
def panic_mode_disabled(self):
self._panic_mode(False)
def _interface(self, zone, interface, enable):
self.update_active_zones()
self.update_tooltip()
# close dialog of removed interface
if not enable:
if interface in self.connections:
connection = self.connections[interface]
if connection in self.zone_connection_editors:
self.zone_connection_editors[connection].hide()
del self.zone_connection_editors[connection]
elif interface in self.zone_interface_editors:
self.zone_interface_editors[interface].hide()
del self.zone_interface_editors[interface]
# send notification if enabled
if self.notificationsAction.isChecked():
ed = { 1: _("activated"),
0: _("deactivated") }
if interface in self.connections:
connection = self.connections[interface]
zone = nm_get_zone_of_connection(connection)
if zone == "":
text = _("Default zone '{default_zone}' "
"{activated_deactivated} for "
"connection '{connection}' on "
"interface '{interface}'")
else:
text = _("Zone '{zone}' {activated_deactivated} for "
"connection '{connection}' on "
"interface '{interface}'")
else:
connection = None
text = _("Zone '{zone}' {activated_deactivated} for "
"interface '{interface}'")
self.notify(escape(text.format(
zone=zone,
default_zone=self.default_zone,
activated_deactivated=ed[enable],
connection=connection, interface=interface)))
def interface_added(self, zone, interface):
self._interface(zone, interface, True)
def interface_removed(self, zone, interface):
self._interface(zone, interface, False)
def zone_of_interface_changed(self, zone, interface):
# update zone editor
if interface in self.zone_interface_editors:
self.zone_interface_editors[interface].set_zone(zone)
self.update_active_zones()
self.update_tooltip()
if self.notificationsAction.isChecked():
self.notify(escape(_("Zone '%s' activated for interface '%s'") % \
(zone, interface)))
def _source(self, zone, source, enable):
self.update_active_zones()
self.update_tooltip()
# close dialog of removed source
if not enable:
if source in self.zone_source_editors:
self.zone_source_editors[source].hide()
del self.zone_source_editors[source]
# send notification if enabled
if self.notificationsAction.isChecked():
ed = { 1: _("activated"),
0: _("deactivated") }
text = _("Zone '{zone}' {activated_deactivated} for "
"source '{source}'")
self.notify(escape(text.format(
zone=zone, activated_deactivated=ed[enable], source=source)))
def source_added(self, zone, source):
self._source(zone, source, True)
def source_removed(self, zone, source):
self._source(zone, source, False)
def zone_of_source_changed(self, zone, source):
index = source
if source in self.zone_source_editors:
self.zone_source_editors[source].set_zone(zone)
# update zone editor
if index in self.zone_interface_editors:
self.zone_interface_editors[index].set_zone(zone)
self.update_active_zones()
self.update_tooltip()
if self.notificationsAction.isChecked():
self.notify(escape(_("Zone '%s' activated for source '%s'") % \
(zone, source)))
# MAIN ########################################################################
if len(sys.argv) > 1:
print("""Usage: %s [options]
Options:
-h, --help show this help message and exit
""" % sys.argv[0])
sys.exit(1)
# reset SIGINT signal to default
signal.signal(signal.SIGINT, signal.SIG_DFL)
app = QtWidgets.QApplication(sys.argv)
app.setQuitOnLastWindowClosed(False)
applet = TrayApplet()
applet.show()
sys.exit(app.exec_())