#!/usr/bin/python3 -su

# Copyright (C) 2024 - 2025 ENCRYPTED SUPPORT LLC <adrelanos@kicksecure.com>
## See the file COPYING for copying conditions.

import sys
import subprocess
import os
import grp
from pathlib import Path
import tempfile

from PyQt5.QtCore import Qt, pyqtSignal, QEvent, QTimer
from PyQt5.QtWidgets import (
    QApplication,
    QMainWindow,
    QDialog,
    QPushButton,
    QLabel,
    QVBoxLayout,
    QHBoxLayout,
    QGridLayout,
    QGroupBox,
    QWidget,
)

## NOTE: _ui modules are autogenerated by build-ui.sh.
## TODO: Refactor UI files into manual UI creation functions.
from sysmaint_panel.reboot_ui import Ui_RebootDialog
from sysmaint_panel.shutdown_ui import Ui_ShutdownDialog
from sysmaint_panel.managesoftware_ui import Ui_ManageSoftwareDialog
from sysmaint_panel.managesoftwarehelp_ui import Ui_ManageSoftwareHelpDialog
from sysmaint_panel.background_ui import Ui_BackgroundScreen
from sysmaint_panel.nopriv_ui import Ui_NoPrivDialog
from sysmaint_panel.wronguser_ui import Ui_WrongUserDialog
from sysmaint_panel.uninstall_ui import Ui_UninstallDialog
from sysmaint_panel.managepasswords_ui import Ui_ManagePasswordsDialog
from sysmaint_panel.searchlogs_ui import Ui_SearchLogsDialog

# Honor sigterm
import signal

signal.signal(signal.SIGINT, signal.SIG_DFL)


def is_qubes_os():
    return Path("/usr/share/qubes/marker-vm").exists()


def is_kicksecure():
    return Path("/usr/share/kicksecure/marker").exists()


def is_whonix_gateway():
    return Path("/usr/share/anon-gw-base-files/gateway").exists()


def is_whonix_workstation():
    return Path("/usr/share/anon-ws-base-files/workstation").exists()


is_non_qubes_vm_val = None
def is_non_qubes_vm():
    global is_non_qubes_vm_val

    if is_non_qubes_vm_val is None:
        systemd_detect_virt_rslt = subprocess.run(
            ["/usr/bin/systemd-detect-virt"],
            check=False,
            capture_output=True,
            encoding="utf-8",
        ).stdout.strip()
        ## Not Python value None, but the literal string "none".
        if systemd_detect_virt_rslt == "none":
            is_non_qubes_vm_val = False
        elif is_qubes_os():
            is_non_qubes_vm_val = False
        else:
            is_non_qubes_vm_val = True
    return is_non_qubes_vm_val


def is_package_installed(package_name):
    return subprocess.run(
        [
            "/usr/bin/package-installed-check",
            package_name,
        ],
        check=False
    ).returncode == 0


## The below two functions are commented out because they are not currently
## used for anything, but may be useful in the future.
#def is_secure_boot_enabled():
#    return subprocess.run(
#        ["/usr/bin/secure-boot-enabled-check"],
#        check=False,
#    ).returncode == 0
#
#
#def can_mok_be_enrolled():
#    if (
#        not Path("/usr/bin/mokutil").is_file()
#        or not os.access("/usr/bin/mokutil", os.X_OK)
#    ):
#        return False
#    if not Path("/var/lib/dkms/mok.pub").is_file():
#        ## Intentionally return True here, since the enroll application will
#        ## generate a MOK if one doesn't exist, and a newly generated MOK
#        ## surely won't be enrolled yet.
#        return True
#    ## 'mokutil --test-key' returns 0 if the MOK can be enrolled, 1 otherwise.
#    return subprocess.run(
#        ["/usr/bin/leaprun", "mokutil-test-key"],
#        check=False,
#    ).returncode == 0


def timeout_lock(button):
    button_text_parts = button.text().split(" ")
    button_text_end_number = button_text_parts[
        len(button_text_parts) - 1
    ].strip("()")

    try:
        button_unlock_time = int(button_text_end_number)
    except Exception:
        button_text_parts.append("(5)")
        button.setText(" ".join(button_text_parts))
        button.setEnabled(False)
        QTimer.singleShot(1000, lambda: timeout_lock(button))
        return

    button_text_parts.pop()
    button_unlock_time -= 1
    if button_unlock_time == 0:
        button.setText(" ".join(button_text_parts))
        button.setEnabled(True)
        return

    button_text_parts.append(f"({button_unlock_time})")
    button.setText(" ".join(button_text_parts))
    QTimer.singleShot(1000, lambda: timeout_lock(button))


def launch_swaybg():
    if is_whonix_gateway():
        subprocess.Popen(
            [
                "/usr/bin/swaybg",
                "-c77767b",
                "--mode=tile",
            ]
        )
    elif is_whonix_workstation():
        subprocess.Popen(
            [
                "/usr/bin/swaybg",
                "-c4098bf",
                "--mode=tile",
            ]
        )
    elif is_kicksecure():
        subprocess.Popen(
            [
                "/usr/bin/swaybg",
                "--image=/usr/share/kicksecure/kicksecure-desktop-background.png",
                "--mode=stretch",
            ]
        )


class NoPrivDialog(QDialog):
    def __init__(self):
        super(NoPrivDialog, self).__init__()
        self.ui = Ui_NoPrivDialog()
        self.ui.setupUi(self)
        self.resize(self.minimumWidth(), self.minimumHeight())

        self.ui.okButton.clicked.connect(self.done)


class WrongUserDialog(QDialog):
    def __init__(self):
        super(WrongUserDialog, self).__init__()
        self.ui = Ui_WrongUserDialog()
        self.ui.setupUi(self)
        self.resize(self.minimumWidth(), self.minimumHeight())

        self.ui.okButton.clicked.connect(self.done)


class BackgroundScreen(QDialog):
    def __init__(self):
        super(BackgroundScreen, self).__init__()
        self.ui = Ui_BackgroundScreen()
        self.ui.setupUi(self)
        if is_whonix_gateway():
            self.setStyleSheet("background-color: #77767b;")
        elif is_whonix_workstation():
            self.setStyleSheet("background-color: #4098bf;")
        elif is_kicksecure():
            self.setStyleSheet(
                "background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, "
                "y2:1, stop:0 #3b187b, stop:1 #a9def2);"
            )


class ManageSoftwareHelpDialog(QDialog):
    def __init__(self):
        super(ManageSoftwareHelpDialog, self).__init__()
        self.ui = Ui_ManageSoftwareHelpDialog()
        self.ui.setupUi(self)
        self.resize(self.minimumWidth(), self.minimumHeight())

        self.ui.okButton.clicked.connect(self.done)


class ManageSoftwareDialog(QDialog):
    def __init__(self):
        super(ManageSoftwareDialog, self).__init__()
        self.ui = Ui_ManageSoftwareDialog()
        self.ui.setupUi(self)
        self.resize(self.minimumWidth(), self.minimumHeight())

        self.ui.runButton.clicked.connect(self.run_action)
        self.ui.cancelButton.clicked.connect(self.cancel)

    def run_action(self):
        match self.ui.actionComboBox.currentText():
            case "Search":
                subprocess.Popen(
                    [
                        "/usr/libexec/helper-scripts/terminal-wrapper",
                        "/usr/bin/apt-cache",
                        "search",
                        self.ui.packageLineEdit.text(),
                    ]
                )
            case "Install":
                subprocess.Popen(
                    [
                        "/usr/libexec/helper-scripts/terminal-wrapper",
                        "/usr/bin/sudo",
                        "/usr/libexec/sysmaint-panel/install-helper",
                        self.ui.packageLineEdit.text(),
                    ]
                )
                self.done(0)
            case "Reinstall":
                subprocess.Popen(
                    [
                        "/usr/libexec/helper-scripts/terminal-wrapper",
                        "/usr/bin/sudo",
                        "/usr/bin/apt-get-reset",
                        self.ui.packageLineEdit.text(),
                    ]
                )
                self.done(0)
            case "Remove":
                subprocess.Popen(
                    [
                        "/usr/libexec/helper-scripts/terminal-wrapper",
                        "/usr/bin/sudo",
                        "/usr/bin/apt",
                        "remove",
                        self.ui.packageLineEdit.text(),
                    ]
                )
                self.done(0)
            case "Purge":
                subprocess.Popen(
                    [
                        "/usr/libexec/helper-scripts/terminal-wrapper",
                        "/usr/bin/sudo",
                        "/usr/bin/apt",
                        "purge",
                        self.ui.packageLineEdit.text(),
                    ]
                )
                self.done(0)
            case "Override":
                subprocess.Popen(
                    [
                        "/usr/libexec/helper-scripts/terminal-wrapper",
                        "/usr/bin/sudo",
                        "/usr/bin/dummy-dependency",
                        "--remove",
                        self.ui.packageLineEdit.text(),
                    ]
                )
                self.done(0)
            case "Help":
                help_window = ManageSoftwareHelpDialog()
                help_window.exec()

    def cancel(self):
        self.done(0)


class ManagePasswordsDialog(QDialog):
    def __init__(self):
        super(ManagePasswordsDialog, self).__init__()
        self.ui = Ui_ManagePasswordsDialog()
        self.ui.setupUi(self)
        self.resize(self.minimumWidth(), self.minimumHeight())

        self.ui.userPasswordButton.clicked.connect(self.user_password_change)
        self.ui.bootloaderPasswordButton.clicked.connect(
            self.bootloader_password_change
        )
        self.ui.diskPassphraseButton.clicked.connect(
            self.disk_passphrase_change
        )

    def user_password_change(self):
        subprocess.Popen(
            [
                "/usr/libexec/helper-scripts/terminal-wrapper",
                "/usr/bin/sudo",
                "/usr/sbin/pwchange",
            ]
        )
        self.done(0)

    def bootloader_password_change(self):
        subprocess.Popen(
            [
                "/usr/libexec/helper-scripts/terminal-wrapper",
                "/usr/bin/sudo",
                "/usr/sbin/grub-pwchange",
            ]
        )
        self.done(0)

    def disk_passphrase_change(self):
        subprocess.Popen(
            [
                "/usr/libexec/helper-scripts/terminal-wrapper",
                "/usr/bin/sudo",
                "/usr/sbin/crypt-pwchange",
            ]
        )
        self.done(0)


class ConfigureDisplaysWindow(QDialog):
    def __init__(self):
        super(ConfigureDisplaysWindow, self).__init__()
        self.setWindowTitle("Configure Displays")
        self.main_layout = QVBoxLayout()
        self.header_label = QLabel()
        self.header_label.setText("Configure Displays")
        self.main_layout.addWidget(self.header_label)
        self.main_layout.addStretch()
        self.launch_wdisplays_button = QPushButton()
        self.launch_wdisplays_button.setText("Launch wdisplays")
        self.launch_wdisplays_button.clicked.connect(self.launch_wdisplays)
        self.main_layout.addWidget(self.launch_wdisplays_button)
        self.open_kanshi_config_button = QPushButton()
        self.open_kanshi_config_button.setText("Open desktop configuration file (kanshi)")
        self.open_kanshi_config_button.clicked.connect(self.open_kanshi_config)
        self.main_layout.addWidget(self.open_kanshi_config_button)
        self.restart_kanshi_button = QPushButton()
        self.restart_kanshi_button.setText("Apply display configuration (restart kanshi)")
        self.restart_kanshi_button.clicked.connect(self.restart_kanshi)
        self.main_layout.addWidget(self.restart_kanshi_button)
        self.setLayout(self.main_layout)
        self.resize(self.minimumWidth(), self.minimumHeight())

    def launch_wdisplays(self):
        subprocess.Popen(["/usr/bin/wdisplays"])
        self.done(0)

    def open_kanshi_config(self):
        subprocess.Popen(
            ["/usr/libexec/desktop-config-dist/open-kanshi-config"]
        )
        self.done(0)

    def restart_kanshi(self):
        subprocess.Popen(
            ["/usr/libexec/desktop-config-dist/restart-kanshi"]
        )
        self.done(0)


class RebootWindow(QDialog):
    def __init__(self):
        super(RebootWindow, self).__init__()
        self.ui = Ui_RebootDialog()
        self.ui.setupUi(self)
        self.resize(self.minimumWidth(), self.minimumHeight())

        self.ui.yesButton.clicked.connect(self.reboot)
        self.ui.noButton.clicked.connect(self.cancel)

    @staticmethod
    def reboot():
        subprocess.run(["/usr/sbin/reboot"])

    def cancel(self):
        self.done(0)


class ShutdownWindow(QDialog):
    def __init__(self):
        super(ShutdownWindow, self).__init__()
        self.ui = Ui_ShutdownDialog()
        self.ui.setupUi(self)
        self.resize(self.minimumWidth(), self.minimumHeight())

        self.ui.yesButton.clicked.connect(self.shutdown)
        self.ui.noButton.clicked.connect(self.cancel)

    @staticmethod
    def shutdown():
        subprocess.run(["/usr/sbin/shutdown", "now"])

    def cancel(self):
        self.done(0)


class UninstallDialog(QDialog):
    def __init__(self):
        super(UninstallDialog, self).__init__()
        self.ui = Ui_UninstallDialog()
        self.ui.setupUi(self)
        self.resize(self.minimumWidth(), self.minimumHeight())
        self.ui.okButton.setEnabled(False)

        self.ui.okButton.clicked.connect(self.uninstall)
        self.ui.cancelButton.clicked.connect(self.cancel)
        self.ui.textField.textEdited.connect(self.check_text)

    closed = pyqtSignal()

    # Overrides QMainWindow.closeEvent
    def closeEvent(self, e):
        if xdg_current_desktop.startswith("sysmaint-session"):
            e.ignore()
            self.cancel()
        else:
            # noinspection PyUnresolvedReferences
            self.closed.emit()

    def check_text(self, text):
        if text == "yes":
            self.ui.okButton.setEnabled(True)
        else:
            self.ui.okButton.setEnabled(False)

    @staticmethod
    def uninstall():
        subprocess.run(
            [
                "/usr/libexec/helper-scripts/terminal-wrapper",
                "/usr/bin/sudo",
                "/usr/bin/dummy-dependency",
                "--yes",
                "--purge",
                "user-sysmaint-split",
            ]
        )
        subprocess.run(["/usr/sbin/reboot"])

    @staticmethod
    def cancel():
        subprocess.run(["/usr/sbin/reboot"])


class SearchLogsDialog(QDialog):
    def __init__(self):
        super(SearchLogsDialog, self).__init__()
        self.ui = Ui_SearchLogsDialog()
        self.ui.setupUi(self)
        self.resize(self.minimumWidth(), self.minimumHeight())
        self.ui.searchButton.clicked.connect(self.search_logs)
        self.ui.cancelButton.clicked.connect(self.cancel)

    def search_logs(self):
        # We use pkexec here since we have to collect logs internally before
        # showing the terminal window, but also need to prompt for
        # authorization.
        journal_logs = subprocess.run(
            [
                "/usr/bin/pkexec",
                "/usr/bin/journalctl",
                "--no-pager",
                "--boot",
            ],
            capture_output=True,
            text=False,
        ).stdout
        filtered_logs = subprocess.run(
            [
                "/usr/bin/grep",
                "--extended-regexp",
                "--",
                self.ui.searchTermLineEdit.text(),
            ],
            capture_output=True,
            text=False,
            input=journal_logs,
        ).stdout
        temp_file = tempfile.NamedTemporaryFile(delete=False)
        temp_file.write(filtered_logs)
        temp_file.close()
        subprocess.run(
            [
                "/usr/libexec/helper-scripts/terminal-wrapper",
                "/usr/bin/less",
                temp_file.name,
            ]
        )
        os.unlink(temp_file.name)

    def cancel(self):
        self.done(0)


class MainWindowLayoutInfo:
    def __init__(self, layout):
        self.layout = layout
        self.next_row = 0
        self.next_column = 0


class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setWindowTitle("System Maintenance Panel")
        self.main_widget = QWidget()
        self.main_layout = QVBoxLayout()
        self.main_layout.addStretch()

        ## ---

        self.header_label = QLabel()
        self.header_label.setText("System Maintenance Panel")
        header_label_font = self.header_label.font()
        header_label_font.setBold(True)
        header_label_font.setPointSize(18)
        self.header_label.setFont(header_label_font)
        self.header_label.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        self.main_layout.addWidget(self.header_label)
        self.main_layout.addStretch()

        # ---

        if "rd.live.image" in kernel_cmdline:
            self.installation_group_box = QGroupBox("Installation")
            self.installation_group_box_layout = QGridLayout()
            self.installation_group_box_layout.setColumnStretch(0, 1)
            ## If there is ever a need to add more than one button to this
            ## group box, uncomment the line below to ensure the second column
            ## of buttons is correctly sized.
            #self.installation_group_box_layout.setColumnStretch(1, 1)
            self.igbl_info = MainWindowLayoutInfo(
                self.installation_group_box_layout
            )
            self.install_system_button = self.make_button(
                self.igbl_info, "Install System", self.install_system
            )
            self.installation_group_box.setLayout(
                self.installation_group_box_layout
            )
            self.main_layout.addWidget(self.installation_group_box)

        # ---

        self.software_updates_group_box = QGroupBox("Software Updates")
        self.software_updates_group_box_layout = QGridLayout()
        self.software_updates_group_box_layout.setColumnStretch(0, 1)
        self.software_updates_group_box_layout.setColumnStretch(1, 1)
        self.sugbl_info = MainWindowLayoutInfo(
            self.software_updates_group_box_layout
        )
        self.check_for_updates_button = self.make_button(
            self.sugbl_info, "Check for Updates", self.check_for_updates
        )
        self.remove_unused_packages_button = self.make_button(
            self.sugbl_info,
            "Remove Unused Packages",
            self.remove_unused_packages,
        )
        self.install_updates_button = self.make_button(
            self.sugbl_info, "Install Updates", self.install_updates
        )
        self.purge_unused_packages_button = self.make_button(
            self.sugbl_info,
            "Purge Unused Packages",
            self.purge_unused_packages,
        )
        self.software_updates_group_box.setLayout(
            self.software_updates_group_box_layout
        )
        self.main_layout.addWidget(self.software_updates_group_box)

        # ---

        self.system_administration_group_box = QGroupBox(
            "System Administration"
        )
        self.system_administration_group_box_layout = QGridLayout()
        self.system_administration_group_box_layout.setColumnStretch(0, 1)
        self.system_administration_group_box_layout.setColumnStretch(1, 1)
        self.sagbl_info = MainWindowLayoutInfo(
            self.system_administration_group_box_layout
        )
        if not is_whonix_gateway() and not is_whonix_workstation():
            self.browser_install_button = self.make_button(
                self.sagbl_info, "Install a Browser", self.browser_install
            )
            self.network_conn_button = self.make_button(
                self.sagbl_info, "Network Connections", self.network_conn
            )
        if is_whonix_gateway():
            self.anon_connection_wizard_button = self.make_button(
                self.sagbl_info,
                "Anon Connection Wizard",
                self.anon_connection_wizard,
            )
            self.tor_control_panel_button = self.make_button(
                self.sagbl_info, "Tor Control Panel", self.tor_control_panel
            )
            self.onion_circuits_button = self.make_button(
                self.sagbl_info, "Onion Circuits", self.onion_circuits
            )
            self.tor_status_monitor_button = self.make_button(
                self.sagbl_info, "Tor Status Monitor", self.tor_status_monitor
            )
        self.manage_passwords_button = self.make_button(
            self.sagbl_info, "Manage Passwords", self.manage_passwords
        )
        self.create_user_button = self.make_button(
            self.sagbl_info, "Create User", self.create_user
        )
        self.manage_autologin_button = self.make_button(
            self.sagbl_info, "Manage GUI Autologin", self.manage_autologin
        )
        self.remove_user_button = self.make_button(
            self.sagbl_info, "Remove User", self.remove_user
        )
        self.check_system_status_button = self.make_button(
            self.sagbl_info, "Check System Status", self.check_system_status
        )
        self.run_repository_wizard_button = self.make_button(
            self.sagbl_info,
            "Run Repository Wizard",
            self.run_repository_wizard,
        )
        self.manage_software_button = self.make_button(
            self.sagbl_info, "Manage Software", self.manage_software
        )
        self.search_logs_button = self.make_button(
            self.sagbl_info, "Search Logs", self.search_logs
        )
        if not is_qubes_os():
            self.configure_displays_button = self.make_button(
                self.sagbl_info, "Configure Displays", self.configure_displays
            )
            if is_non_qubes_vm() and is_package_installed("vm-config-dist"):
                self.dynamic_resolution_button = self.make_button(
                    self.sagbl_info,
                    "Dynamic Resolution",
                    self.dynamic_resolution
                )
            self.set_system_keymap_button = self.make_button(
                self.sagbl_info, "Set System Keymap", self.set_system_keymap
            )
        if not "boot-role=sysmaint" in kernel_cmdline:
            self.lxqt_config_button = self.make_button(
                self.sagbl_info, "LXQt Configuration", self.lxqt_config
            )
        self.toggle_panic_on_oops_button = self.make_button(
            self.sagbl_info, "Toggle Panic-on-Oops", self.toggle_panic_on_oops
        )
        self.enroll_secure_boot_mok_button = self.make_button(
            self.sagbl_info,
            "Enroll Secure Boot MOK",
            self.enroll_secure_boot_mok,
        )
        self.reset_secure_boot_mok_button = self.make_button(
            self.sagbl_info,
            "Reset Secure Boot MOK",
            self.reset_secure_boot_mok,
        )
        self.system_administration_group_box.setLayout(
            self.system_administration_group_box_layout
        )
        self.main_layout.addWidget(self.system_administration_group_box)

        # ---

        self.misc_group_box = QGroupBox("Misc")
        self.misc_group_box_layout = QGridLayout()
        self.misc_group_box_layout.setColumnStretch(0, 1)
        self.misc_group_box_layout.setColumnStretch(1, 1)
        self.mgbl_info = MainWindowLayoutInfo(self.misc_group_box_layout)
        self.open_terminal_button = self.make_button(
            self.mgbl_info, "Open Terminal", self.open_terminal
        )
        if not is_qubes_os():
            self.toggle_osk_button = self.make_button(
                self.mgbl_info, "On-Screen Keyboard", self.toggle_osk
            )
            self.reboot_button = self.make_button(
                self.mgbl_info, "Reboot", self.reboot
            )
        self.shutdown_button = self.make_button(
            self.mgbl_info, "Shut Down", self.shutdown
        )
        if not is_qubes_os():
            self.lock_screen_button = self.make_button(
                self.mgbl_info, "Lock Screen", self.lock_screen
            )
        self.misc_group_box.setLayout(self.misc_group_box_layout)
        self.main_layout.addWidget(self.misc_group_box)

        # ---

        self.main_widget.setLayout(self.main_layout)
        self.setCentralWidget(self.main_widget)
        self.resize(self.minimumWidth(), self.minimumHeight())

    @staticmethod
    def make_button(
            layout_info, button_text, connect_func
    ):
        new_button = QPushButton()
        new_button.setText(button_text)
        new_button.clicked.connect(connect_func)
        layout_info.layout.addWidget(
            new_button, layout_info.next_row, layout_info.next_column
        )
        if layout_info.next_column == 0:
            layout_info.next_column = 1
        else:
            layout_info.next_row += 1
            layout_info.next_column = 0
        return new_button

    closed = pyqtSignal()

    # Overrides QMainWindow.closeEvent
    def closeEvent(self, e):
        if (
            xdg_current_desktop.startswith("sysmaint-session")
            and not is_qubes_os()
        ):
            e.ignore()
            shutdown_window = ShutdownWindow()
            shutdown_window.exec()
        else:
            # noinspection PyUnresolvedReferences
            self.closed.emit()

    # Overrides QMainWindow.event
    def event(self, e):
        if (
            e.type() == QEvent.WindowStateChange
            and (self.windowState() & Qt.WindowMinimized) == Qt.WindowMinimized
        ):
            if xdg_current_desktop.startswith("sysmaint-session"):
                e.ignore()
                self.setWindowState(e.oldState())
                return True

        return super(MainWindow, self).event(e)

    def install_system(self):
        subprocess.Popen(
            [
                "/usr/libexec/helper-scripts/terminal-wrapper",
                "/usr/bin/install-host",
            ]
        )
        timeout_lock(self.install_system_button)

    def check_for_updates(self):
        subprocess.Popen(
            [
                "/usr/libexec/helper-scripts/terminal-wrapper",
                "/usr/bin/sudo",
                "/usr/libexec/helper-scripts/apt-get-update",
            ]
        )
        timeout_lock(self.check_for_updates_button)

    def install_updates(self):
        subprocess.Popen(
            [
                "/usr/libexec/helper-scripts/terminal-wrapper",
                "/usr/bin/upgrade-nonroot",
            ]
        )
        timeout_lock(self.install_updates_button)

    def remove_unused_packages(self):
        subprocess.Popen(
            [
                "/usr/libexec/helper-scripts/terminal-wrapper",
                "/usr/bin/sudo",
                "/usr/bin/apt",
                "autoremove",
            ]
        )
        timeout_lock(self.remove_unused_packages_button)

    def purge_unused_packages(self):
        subprocess.Popen(
            [
                "/usr/libexec/helper-scripts/terminal-wrapper",
                "/usr/bin/sudo",
                "/usr/bin/apt",
                "autopurge",
            ]
        )
        timeout_lock(self.purge_unused_packages_button)

    def lxqt_config(self):
        subprocess.Popen(["/usr/bin/lxqt-config"])
        timeout_lock(self.lxqt_config_button)

    def toggle_panic_on_oops(self):
        subprocess.Popen(
            [
                "/usr/libexec/helper-scripts/terminal-wrapper",
                "/usr/bin/sudo",
                "/usr/libexec/sysmaint-panel/toggle-panic-on-oops",
            ]
        )
        timeout_lock(self.toggle_panic_on_oops_button)

    def enroll_secure_boot_mok(self):
        subprocess.Popen(
            [
                "/usr/libexec/helper-scripts/terminal-wrapper",
                "/usr/bin/sudo",
                "/usr/sbin/shim-manage-mok",
                "--enroll",
            ]
        )
        timeout_lock(self.enroll_secure_boot_mok_button)

    def reset_secure_boot_mok(self):
        subprocess.Popen(
            [
                "/usr/libexec/helper-scripts/terminal-wrapper",
                "/usr/bin/sudo",
                "/usr/sbin/shim-manage-mok",
                "--reset",
            ]
        )
        timeout_lock(self.reset_secure_boot_mok_button)

    @staticmethod
    def manage_software():
        manage_software_window = ManageSoftwareDialog()
        manage_software_window.exec()

    @staticmethod
    def search_logs():
        search_logs_window = SearchLogsDialog()
        search_logs_window.exec()

    @staticmethod
    def configure_displays():
        configure_displays_window = ConfigureDisplaysWindow()
        configure_displays_window.exec()

    def dynamic_resolution(self):
        subprocess.Popen(
            [
                "/usr/libexec/helper-scripts/terminal-wrapper",
                "/usr/bin/sudo",
                "/usr/sbin/configure-dynamic-resolution",
            ]
        )
        timeout_lock(self.dynamic_resolution_button)

    def set_system_keymap(self):
        subprocess.Popen(
            [
                "/usr/libexec/helper-scripts/terminal-wrapper",
                "/usr/bin/sudo",
                "/usr/bin/set-system-keymap",
                "--interactive",
            ]
        )
        timeout_lock(self.set_system_keymap_button)

    def browser_install(self):
        subprocess.Popen(["/usr/bin/browser-choice"])
        timeout_lock(self.browser_install_button)

    def network_conn(self):
        subprocess.Popen(
            [
                "/usr/libexec/helper-scripts/terminal-wrapper",
                "/usr/bin/nmtui",
            ]
        )
        timeout_lock(self.network_conn_button)

    def anon_connection_wizard(self):
        subprocess.Popen(["/usr/bin/anon-connection-wizard"])
        timeout_lock(self.anon_connection_wizard_button)

    def tor_control_panel(self):
        subprocess.Popen(["/usr/bin/tor-control-panel"])
        timeout_lock(self.tor_control_panel_button)

    def onion_circuits(self):
        subprocess.Popen(["/usr/bin/onioncircuits"])
        timeout_lock(self.onion_circuits_button)

    def tor_status_monitor(self):
        subprocess.Popen(
            [
                "/usr/libexec/helper-scripts/terminal-wrapper",
                "/usr/bin/nyx",
            ]
        )
        timeout_lock(self.tor_status_monitor_button)

    @staticmethod
    def manage_passwords():
        manage_passwords_window = ManagePasswordsDialog()
        manage_passwords_window.exec()

    def create_user(self):
        subprocess.Popen(
            [
                "/usr/libexec/helper-scripts/terminal-wrapper",
                "/usr/bin/sudo",
                "/usr/libexec/sysmaint-panel/create-user",
            ]
        )
        timeout_lock(self.create_user_button)

    def remove_user(self):
        subprocess.Popen(
            [
                "/usr/libexec/helper-scripts/terminal-wrapper",
                "/usr/bin/sudo",
                "/usr/libexec/sysmaint-panel/delete-user",
            ]
        )
        timeout_lock(self.remove_user_button)

    def run_repository_wizard(self):
        subprocess.Popen(
            ["/usr/libexec/repository-dist/repository-dist-wizard"]
        )
        timeout_lock(self.run_repository_wizard_button)

    def manage_autologin(self):
        subprocess.Popen(
            [
                "/usr/libexec/helper-scripts/terminal-wrapper",
                "/usr/bin/sudo",
                "/usr/sbin/autologinchange",
            ]
        )
        timeout_lock(self.manage_autologin_button)

    def check_system_status(self):
        subprocess.Popen(["/usr/bin/systemcheck", "--gui"])
        timeout_lock(self.check_system_status_button)

    @staticmethod
    def open_terminal():
        subprocess.Popen(
            ["/usr/libexec/helper-scripts/terminal-wrapper", default_shell]
        )

    def lock_screen(self):
        subprocess.Popen(["/usr/bin/lock-screen"])
        timeout_lock(self.lock_screen_button)

    @staticmethod
    def reboot():
        reboot_window = RebootWindow()
        reboot_window.exec()

    @staticmethod
    def shutdown():
        shutdown_window = ShutdownWindow()
        shutdown_window.exec()

    def toggle_osk(self):
        if Path(f"/run/user/{os.getuid()}/dist-virtual-keyboard.pid").is_file():
            subprocess.Popen(["/usr/bin/dist-virtual-keyboard", "--terminate"])
        else:
            subprocess.Popen(["/usr/bin/dist-virtual-keyboard"])
        timeout_lock(self.toggle_osk_button)


def main():
    app = QApplication(sys.argv)
    app.setDesktopFileName("sysmaint-panel")

    sudo_stat_info = os.stat("/usr/bin/sudo")
    sudo_owning_gid = sudo_stat_info.st_gid
    sudo_owning_group = grp.getgrgid(sudo_owning_gid)[0]
    if sudo_owning_group == "sysmaint":
        if not os.access("/usr/bin/sudo", os.X_OK):
            if "boot-role=sysmaint" in kernel_cmdline:
                wuwin = WrongUserDialog()
                wuwin.show()
            else:
                npwin = NoPrivDialog()
                npwin.show()
            sys.exit(app.exec_())

    if "remove-sysmaint" in kernel_cmdline:
        window = UninstallDialog()
    else:
        window = MainWindow()

    window.show()

    if xdg_current_desktop.startswith("sysmaint-session") and not is_qubes_os():
        if os.getenv("WAYLAND_DISPLAY", "") == "":
            bgrd_list = []
            for screen in app.screens():
                bgrd = BackgroundScreen()
                bgrd.setGeometry(screen.geometry())
                bgrd.setWindowFlags(
                    Qt.WindowStaysOnBottomHint | Qt.WindowDoesNotAcceptFocus
                )
                bgrd.showFullScreen()
                # noinspection PyUnresolvedReferences
                window.closed.connect(bgrd.close)
                bgrd_list.append(bgrd)
        else:
            launch_swaybg()

    sys.exit(app.exec_())


xdg_current_desktop = ""
if "XDG_CURRENT_DESKTOP" in os.environ:
    xdg_current_desktop = os.environ["XDG_CURRENT_DESKTOP"]
default_shell = "/bin/bash"
if "SHELL" in os.environ:
    default_shell = os.environ["SHELL"]

with open("/proc/cmdline", "r") as kernel_cmdline_file:
    kernel_cmdline = kernel_cmdline_file.read()

if __name__ == "__main__":
    main()
