#!/usr/bin/python3 -Es

# Author: Petr Lautrbach <plautrba@redhat.com>
# Copyright (C) 2018 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, see <http://www.gnu.org/licenses/>.

import gettext
import sys
import gi
from gi.repository import GLib
from gi.repository import GObject
from gi.repository import Gio
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
gi.require_version('Notify', '0.7')
from gi.repository import Notify

from dasbus.connection import SystemMessageBus

import selinux

from setroubleshoot.config import get_config


class SEApplet(GObject.Object):
    notifications = {}
    notifications_number = 0
    status_icon = None

    def __init_status_icon(self):
        if self.status_icon is not None:
            return self.status_icon

        # Gtk.StatusIcon.new_from_file is deprecated but has no replacement
        # Same applies to Gtk.StatusIcon.set_title
        import warnings
        warnings.filterwarnings("ignore", category=DeprecationWarning)

        self.status_icon = Gtk.StatusIcon.new_from_file(
            "/usr/share/icons/hicolor/scalable/apps/setroubleshoot_icon.svg"
        )
        self.status_icon.set_title("SELinux troubleshooter")
        self.status_icon.connect("activate", self.status_show)
        self.status_icon.set_visible(True)

        warnings.resetwarnings()

        return self.status_icon

    def __init__(self):

        bus = SystemMessageBus()
        Setroubleshootd = bus.get_proxy(
            'org.fedoraproject.Setroubleshootd',
            '/org/fedoraproject/Setroubleshootd'
        )

        Setroubleshootd.alert.connect(self.send_notification)

        super().__init__()
        Notify.init("seapplet")
        # lets initialise with the application name

        # FIXME:
        # if (check_for_avcs(&local_id)  == TRUE) {
        # 	sedbus_send_check_new(conn, (void *) &alert, local_id);
        # }
        # read last from ~/.config/sealert.conf
        # GLib.get_user_config_dir() + "/sealert.conf
        last_id = ''
        try:
            with open(GLib.get_user_config_dir() + "/sealert.conf", encoding='utf-8') as f:
                for line in f:
                    try:
                        (key, value) = line.split('=')
                        if key == 'last':
                            last_id = value.rstrip()
                            break
                    except:
                        pass
        except OSError:
            pass

        (count, red) = Setroubleshootd.check_for_new(last_id)

        if count > 0:
            status_icon = self.__init_status_icon()
            status_icon.set_visible(True)

    def dismiss(self, notification, action_name, data):
        del self.notifications[notification]
        status_icon = self.__init_status_icon()
        status_icon.set_visible(False)

    def _close_notifications(self):
        for n in self.notifications:
            try:
                n.close()
            except IOError as e:
                print("seapplet: Can't close a notification: %s" % str(e), file=sys.stderr)
        self.notifications.clear()
        self.notifications_number = 0

    def status_show(self, status_icon):
        self._close_notifications()
        self.launch_desktop()

    def show(self, notification, action_name, data):
        self._close_notifications()
        self.launch_desktop()

    def launch_desktop(self):
        launcher = Gio.DesktopAppInfo.new("setroubleshoot.desktop")
        context = Gio.AppLaunchContext()
        launcher.launch(None, context)
        self.status_icon.set_visible(False)

    def send_notification(self, *params):
        status_icon = self.__init_status_icon()
        status_icon.set_visible(True)

        # FIXME:
        # AVC can be already ignored by a user

        # keep only one alert notification opened
        self._close_notifications()
        n = Notify.Notification.new(
            _("New SELinux security alert"),
            _("AVC denial, click icon to view"),
            "/usr/share/icons/hicolor/scalable/apps/setroubleshoot_icon.svg"
        )
        n.add_action("dismiss", _("Dismiss"), self.dismiss, None)
        n.add_action("show", _("Show"), self.show, None)
        n.add_action("default", _("Show"), self.show, None)
        n.connect("closed", self.notification_closed)
        try:
            n.show()
            self.notifications[n] = params
        except Exception as e:
            # maybe no notification daemon is running
            # see https://bugzilla.redhat.com/show_bug.cgi?id=1541624
            print("seapplet: Can't show a notification: %s" % str(e), file=sys.stderr)

    def notification_closed(self, notification):
        del self.notifications[notification]


if __name__ == '__main__':
    gettext.bindtextdomain(domain=get_config('general', 'i18n_text_domain'),
                           localedir=get_config('general', 'i18n_locale_dir'))
    gettext.textdomain(domain=get_config('general', 'i18n_text_domain'))
    _ = gettext.gettext

    if selinux.is_selinux_enabled() != 1:
        print(_("SELinux Troubleshooter: Applet requires SELinux be enabled to run"), file=sys.stderr)
        sys.exit(1)

    try:
        my = SEApplet()
    except Exception as e:
        print(_("Can't start seapplet: {error}".format(error=str(e))), file=sys.stderr)
        sys.exit(2)

    loop = GLib.MainLoop()
    loop.run()
