# statusPage.py - show selinux status
## Copyright (C) 2006-2009 Red Hat, Inc.

## 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, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

## Author: Dan Walsh
import os
import sys
from gi.repository import Gtk
import selinux

INSTALLPATH = '/usr/share/system-config-selinux'
sys.path.append(INSTALLPATH)

ENFORCING = 1
PERMISSIVE = 0
DISABLED = -1
modearray = ("disabled", "permissive", "enforcing")

SELINUXDIR = "/etc/selinux/"
RELABELFILE = "/.autorelabel"

##
## I18N
##
PROGNAME = "selinux-gui"
try:
    import gettext
    kwargs = {}
    if sys.version_info < (3,):
        kwargs['unicode'] = True
    t = gettext.translation(PROGNAME,
                    localedir="/usr/share/locale",
                    **kwargs,
                    fallback=True)
    _ = t.gettext
except:
    try:
        import builtins
        builtins.__dict__['_'] = str
    except ImportError:
        import __builtin__
        __builtin__.__dict__['_'] = unicode


class statusPage:

    def __init__(self, xml):
        self.xml = xml
        self.needRelabel = False

        self.type = selinux.selinux_getpolicytype()
        # Bring in widgets from glade file.
        self.selinuxTypeOptionMenu = xml.get_object("selinuxTypeOptionMenu")
        self.typeLabel = xml.get_object("typeLabel")
        self.enabledOptionMenu = xml.get_object("enabledOptionMenu")
        self.currentOptionMenu = xml.get_object("currentOptionMenu")
        self.relabel_checkbutton = xml.get_object("relabelCheckbutton")
        self.relabel_checkbutton.set_active(self.is_relabel())
        self.relabel_checkbutton.connect("toggled", self.on_relabel_toggle)
        if self.get_current_mode() == ENFORCING or self.get_current_mode() == PERMISSIVE:
            self.currentOptionMenu.append_text(_("Permissive"))
            self.currentOptionMenu.append_text(_("Enforcing"))
            self.currentOptionMenu.set_active(self.get_current_mode())
            self.currentOptionMenu.connect("changed", self.set_current_mode)
            self.currentOptionMenu.set_sensitive(True)
        else:
            self.currentOptionMenu.append_text(_("Disabled"))
            self.currentOptionMenu.set_active(0)
            self.currentOptionMenu.set_sensitive(False)

        if self.read_selinux_config() is None:
            self.selinuxsupport = False
        else:
            self.enabledOptionMenu.connect("changed", self.enabled_changed)
        #
        # This line must come after read_selinux_config
        #
        self.selinuxTypeOptionMenu.connect("changed", self.typemenu_changed)

        self.typeLabel.set_mnemonic_widget(self.selinuxTypeOptionMenu)

    def use_menus(self):
        return False

    def get_description(self):
        return _("Status")

    def get_current_mode(self):
        if selinux.is_selinux_enabled():
            if selinux.security_getenforce() > 0:
                return ENFORCING
            else:
                return PERMISSIVE
        else:
            return DISABLED

    def set_current_mode(self, menu):
        selinux.security_setenforce(menu.get_active() == 1)

    def is_relabel(self):
        return os.access(RELABELFILE, os.F_OK) != 0

    def on_relabel_toggle(self, button):
        if button.get_active():
            fd = open(RELABELFILE, "w")
            fd.close()
        else:
            if os.access(RELABELFILE, os.F_OK) != 0:
                os.unlink(RELABELFILE)

    def verify(self, message):
        dlg = Gtk.MessageDialog(None, 0, Gtk.MessageType.INFO,
                                Gtk.ButtonsType.YES_NO,
                                message)
        dlg.set_position(Gtk.WindowPosition.MOUSE)
        dlg.show_all()
        rc = dlg.run()
        dlg.destroy()
        return rc

    def typemenu_changed(self, menu):
        type = self.get_type()
        enabled = self.enabledOptionMenu.get_active()
        if self.initialtype != type:
            if self.verify(_("Changing the policy type will cause a relabel of the entire file system on the next boot. Relabeling takes a long time depending on the size of the file system.  Do you wish to continue?")) == Gtk.ResponseType.NO:
                menu.set_active(self.typeHistory)
                return None

            self.relabel_checkbutton.set_active(True)

        self.write_selinux_config(modearray[enabled], type)
        self.typeHistory = menu.get_active()

    def enabled_changed(self, combo):
        enabled = combo.get_active()
        type = self.get_type()

        if self.initEnabled != DISABLED and enabled == DISABLED:
            if self.verify(_("Changing to SELinux disabled requires a reboot.  It is not recommended.  If you later decide to turn SELinux back on, the system will be required to relabel.  If you just want to see if SELinux is causing a problem on your system, you can go to permissive mode which will only log errors and not enforce SELinux policy.  Permissive mode does not require a reboot    Do you wish to continue?")) == Gtk.ResponseType.NO:
                combo.set_active(self.enabled)
                return None

        if self.initEnabled == DISABLED and enabled < 2:
            if self.verify(_("Changing to SELinux enabled will cause a relabel of the entire file system on the next boot. Relabeling takes a long time depending on the size of the file system.  Do you wish to continue?")) == Gtk.ResponseType.NO:
                combo.set_active(self.enabled)
                return None
            self.relabel_checkbutton.set_active(True)

        self.write_selinux_config(modearray[enabled], type)
        self.enabled = enabled

    def write_selinux_config(self, enforcing, type):
        path = selinux.selinux_path() + "config"
        backup_path = path + ".bck"
        fd = open(path)
        lines = fd.readlines()
        fd.close()
        fd = open(backup_path, "w")
        for l in lines:
            if l.startswith("SELINUX="):
                fd.write("SELINUX=%s\n" % enforcing)
                continue
            if l.startswith("SELINUXTYPE="):
                fd.write("SELINUXTYPE=%s\n" % type)
                continue
            fd.write(l)
        fd.close()
        os.rename(backup_path, path)

    def read_selinux_config(self):
        self.initialtype = selinux.selinux_getpolicytype()[1]
        try:
            self.initEnabled = selinux.selinux_getenforcemode()[1]
        except:
            self.initEnabled = False
            pass
        self.enabled = self.initEnabled
        self.enabledOptionMenu.set_active(self.enabled + 1)

        self.types = []

        n = 0
        current = n

        for i in os.listdir(SELINUXDIR):
            if os.path.isdir(SELINUXDIR + i) and os.path.isdir(SELINUXDIR + i + "/policy"):
                self.types.append(i)
                self.selinuxTypeOptionMenu.append_text(i)
                if i == self.initialtype:
                    current = n
                n = n + 1
        self.selinuxTypeOptionMenu.set_active(current)
        self.typeHistory = current

        return 0

    def get_type(self):
        return self.types[self.selinuxTypeOptionMenu.get_active()]
