sp-hydra-veil-gui/gui/v2/ui/pages/install_system_package.py
2026-06-13 16:19:11 -04:00

488 lines
20 KiB
Python
Executable file

import os
import subprocess
from PyQt6.QtWidgets import QApplication, QPushButton, QLabel
from PyQt6.QtGui import QIcon
from PyQt6.QtCore import QSize, QTimer
from PyQt6 import QtCore
from gui.v2.ui.pages.Page import Page
from gui.v2.workers.worker_thread import WorkerThread
class InstallSystemPackage(Page):
def __init__(self, page_stack, main_window=None, parent=None):
super().__init__("InstallPackage", page_stack, main_window, parent)
self.btn_path = main_window.btn_path
self.update_status = main_window
self.package_name = ""
self.install_command = ""
self.manual_install_command = ""
self.is_sync = False
self.ui_elements = []
self.permanent_elements = []
self.current_distro = "debian"
self.distro_buttons = {}
self._setup_permanent_ui()
self._setup_distro_buttons()
self.auto_install_package_commands = {
'all': {
'debian': 'pkexec apt install -y bubblewrap iproute2 microsocks proxychains4 ratpoison tor wireguard xserver-xephyr',
'arch': 'pkexec pacman -S bubblewrap iproute2 microsocks proxychains-ng tor wireguard-tools xorg-server-xephyr',
'fedora': 'pkexec dnf -y install bubblewrap iproute proxychains4 ratpoison tor wireguard-tools xorg-x11-server-Xephyr'
},
'bwrap': {
'debian': 'pkexec apt -y install bubblewrap',
'arch': 'pkexec pacman -S bubblewrap',
'fedora': 'pkexec dnf -y install bubblewrap'
},
'ip': {
'debian': 'pkexec apt -y install iproute2',
'arch': 'pkexec pacman -S iproute2',
'fedora': 'pkexec dnf -y install iproute'
},
'microsocks': {
'debian': 'pkexec apt -y install microsocks',
'arch': 'pkexec pacman -S microsocks',
'fedora': 'zenity --warning --text="An essential dependency (microsocks) is not included in your distribution\'s default repository. Please build it from source or contact support."'
},
'proxychains4': {
'debian': 'pkexec apt -y install proxychains4',
'arch': 'pkexec pacman -S proxychains-ng',
'fedora': 'pkexec dnf -y install proxychains4'
},
'ratpoison': {
'debian': 'pkexec apt -y install ratpoison',
'arch': 'zenity --warning --text="An essential dependency (ratpoison) is not included in your distribution\'s default repository. Please build it from source or contact support."',
'fedora': 'pkexec dnf -y install ratpoison'
},
'tor': {
'debian': 'pkexec apt -y install tor',
'arch': 'pkexec pacman -S tor',
'fedora': 'pkexec dnf -y install tor'
},
'wg-quick': {
'debian': 'pkexec apt -y install wireguard',
'arch': 'pkexec pacman -S wireguard-tools',
'fedora': 'pkexec dnf -y install wireguard-tools'
},
'Xephyr': {
'debian': 'pkexec apt -y install xserver-xephyr',
'arch': 'pkexec pacman -S xorg-server-xephyr',
'fedora': 'pkexec dnf -y install xorg-x11-server-Xephyr'
}
}
self.manual_install_package_commands = {
'all': {
'debian': 'sudo apt install -y bubblewrap iproute2 microsocks proxychains4 ratpoison tor wireguard xserver-xephyr',
'arch': 'sudo pacman -S bubblewrap iproute2 microsocks proxychains-ng tor wireguard-tools xorg-server-xephyr',
'fedora': 'sudo dnf -y install bubblewrap iproute proxychains4 ratpoison tor wireguard-tools xorg-x11-server-Xephyr'
},
'bwrap': {
'debian': 'sudo apt -y install bubblewrap',
'arch': 'sudo pacman -S bubblewrap',
'fedora': 'sudo dnf -y install bubblewrap'
},
'ip': {
'debian': 'sudo apt -y install iproute2',
'arch': 'sudo pacman -S iproute2',
'fedora': 'sudo dnf -y install iproute'
},
'microsocks': {
'debian': 'sudo apt -y install microsocks',
'arch': 'sudo pacman -S microsocks',
'fedora': '# Package not available.'
},
'proxychains4': {
'debian': 'sudo apt -y install proxychains4',
'arch': 'sudo pacman -S proxychains-ng',
'fedora': 'sudo dnf -y install proxychains4'
},
'ratpoison': {
'debian': 'sudo apt -y install ratpoison',
'arch': '# Package not available.',
'fedora': 'sudo dnf -y install ratpoison'
},
'tor': {
'debian': 'sudo apt -y install tor',
'arch': 'sudo pacman -S tor',
'fedora': 'sudo dnf -y install tor'
},
'wg-quick': {
'debian': 'sudo apt -y install wireguard',
'arch': 'sudo pacman -S wireguard-tools',
'fedora': 'sudo dnf -y install wireguard-tools'
},
'Xephyr': {
'debian': 'sudo apt -y install xserver-xephyr',
'arch': 'sudo pacman -S xorg-server-xephyr',
'fedora': 'sudo dnf -y install xorg-x11-server-Xephyr'
}
}
def _setup_permanent_ui(self):
self.title.setGeometry(20, 50, 300, 60)
self.title.setText("Package Installation")
self.title.setStyleSheet("font-size: 22px; font-weight: bold;")
self.permanent_elements.append(self.title)
self.display.setGeometry(QtCore.QRect(100, 160, 580, 435))
self.permanent_elements.append(self.display)
def _setup_distro_buttons(self):
distros = ["debian", "arch", "fedora"]
for i, distro in enumerate(distros):
btn = QPushButton(self)
btn.setGeometry(360 + i*140, 40, 120, 49)
btn.setIcon(QIcon(os.path.join(self.btn_path, f"{distro}.png")))
btn.setIconSize(QSize(120, 49))
btn.setCheckable(True)
btn.clicked.connect(
lambda checked, d=distro: self.switch_distro(d))
self.distro_buttons[distro] = btn
self.permanent_elements.append(btn)
self.distro_buttons[self.current_distro].setChecked(True)
def switch_distro(self, distro):
self.current_distro = distro
for d, btn in self.distro_buttons.items():
btn.setChecked(d == distro)
if self.package_name:
self.update_command()
self.setup_ui()
def update_command(self):
if self.package_name in self.auto_install_package_commands:
if self.is_sync:
self.install_command = self.auto_install_package_commands[
'tor_sync'][self.current_distro]
self.manual_install_command = self.manual_install_package_commands[
'tor_sync'][self.current_distro]
else:
self.install_command = self.auto_install_package_commands[
self.package_name][self.current_distro]
self.manual_install_command = self.manual_install_package_commands[
self.package_name][self.current_distro]
else:
self.install_command = ""
self.manual_install_command = ""
def configure(self, package_name, distro, is_sync=False):
self.package_name = package_name
self.current_distro = distro
self.is_sync = is_sync
self.update_command()
self.setup_ui()
return self
def setup_ui(self):
self.button_back.setVisible(False)
self.button_go.setVisible(True)
if not self.package_name:
return
self.clear_ui()
self._setup_auto_install()
self._setup_manual_install()
self._setup_status_and_nav()
for element in self.ui_elements:
element.setParent(self)
element.setVisible(True)
element.raise_()
def clear_ui(self):
for element in self.ui_elements:
element.setParent(None)
element.deleteLater()
self.ui_elements.clear()
self.command_box = None
self.status_label = None
self.install_button = None
self.check_button = None
def _setup_auto_install(self):
auto_install_label = QLabel(
f"Automatic Installation for {self.current_distro}", self)
auto_install_label.setGeometry(80, 130, 580, 30)
auto_install_label.setStyleSheet("font-size: 18px; font-weight: bold;")
auto_install_label.setVisible(True)
self.ui_elements.append(auto_install_label)
auto_install_desc = QLabel(
"Click the button below to automatically install the missing packages:", self)
auto_install_desc.setStyleSheet("font-size: 14px; font-weight: bold;")
auto_install_desc.setGeometry(80, 170, 650, 30)
auto_install_desc.setVisible(True)
self.ui_elements.append(auto_install_desc)
self.install_button = QPushButton(self)
self.install_button.setGeometry(135, 210, 100, 40)
self.install_button.setIcon(
QIcon(os.path.join(self.btn_path, "install.png")))
self.install_button.setIconSize(self.install_button.size())
self.install_button.clicked.connect(self.install_package)
self.install_button.setVisible(True)
self.ui_elements.append(self.install_button)
if self.current_distro == 'arch':
auto_install_label.setGeometry(50, 110, 580, 30)
auto_install_desc.setGeometry(50, 140, 650, 30)
self.install_button.setGeometry(150, 185, 100, 40)
self.check_button = QPushButton(self)
self.check_button.setGeometry(270, 185, 100, 40)
self.check_button.setIcon(
QIcon(os.path.join(self.btn_path, "check.png")))
self.check_button.setIconSize(self.check_button.size())
self.check_button.clicked.connect(self.check_package)
self.check_button.setVisible(True)
self.ui_elements.append(self.check_button)
def _setup_manual_install(self):
if self.current_distro != 'arch':
manual_install_label = QLabel(
f"Manual Installation for {self.current_distro}", self)
manual_install_label.setGeometry(80, 290, 650, 30)
manual_install_label.setStyleSheet(
"font-size: 18px; font-weight: bold;")
manual_install_label.setVisible(True)
self.ui_elements.append(manual_install_label)
manual_install_desc = QLabel(
"Run this command in your terminal:", self)
manual_install_desc.setStyleSheet(
"font-size: 14px; font-weight: bold;")
manual_install_desc.setGeometry(80, 330, 380, 30)
manual_install_desc.setVisible(True)
self.ui_elements.append(manual_install_desc)
self.command_box = QLabel(self.manual_install_command, self)
self.command_box.setGeometry(80, 370, 380, 40)
if self.current_distro == 'arch':
self.command_box.setGeometry(50, 280, 380, 40)
metrics = self.command_box.fontMetrics()
text_width = metrics.horizontalAdvance(
self.manual_install_command) + 100
available_width = self.command_box.width() - 20
font_size = 18
MIN_FONT_SIZE = 10
while text_width > available_width:
if font_size <= MIN_FONT_SIZE:
break
font_size -= 1
text_width -= 10
self.command_box.setStyleSheet(f"""
background-color: #2b2b2b;
color: #ffffff;
padding: 10px;
border-radius: 5px;
font-family: monospace;
font-size: {font_size}px;
""")
self.command_box.mousePressEvent = lambda _: self.copy_command()
self.command_box.setVisible(True)
self.ui_elements.append(self.command_box)
if self.current_distro == 'arch':
pacman_label = QLabel("Pacman Installation:", self)
pacman_label.setGeometry(50, 240, 650, 30)
pacman_label.setStyleSheet(
"font-size: 18px; font-weight: bold; color: #00ffff;")
pacman_label.setVisible(True)
self.ui_elements.append(pacman_label)
aur_label = QLabel("AUR / Yay Installation:", self)
aur_label.setGeometry(50, 350, 300, 30)
aur_label.setStyleSheet(
"font-size: 18px; font-weight: bold; color: #00ffff;")
self.ui_elements.append(aur_label)
self.yay_command_box = QLabel("sudo yay -S ratpoison", self)
self.yay_command_box.setGeometry(50, 390, 300, 40)
self._setup_command_box_style(self.yay_command_box)
self.yay_command_box.mousePressEvent = lambda _: self.copy_command(
"sudo yay -S ratpoison", self.yay_command_box)
self.ui_elements.append(self.yay_command_box)
aur_command = "git clone https://aur.archlinux.org/ratpoison.git\ncd ratpoison\nmakepkg -si --skippgpcheck"
self.aur_command_box = QLabel(aur_command, self)
self.aur_command_box.setGeometry(370, 390, 380, 60)
self._setup_command_box_style(self.aur_command_box)
self.aur_command_box.setWordWrap(True)
self.aur_command_box.mousePressEvent = lambda _: self.copy_command(
aur_command, self.aur_command_box)
self.ui_elements.append(self.aur_command_box)
else:
self.command_box = QLabel(self.manual_install_command, self)
self.command_box.setGeometry(80, 370, 380, 40)
metrics = self.command_box.fontMetrics()
text_width = metrics.horizontalAdvance(
self.manual_install_command) + 100
available_width = self.command_box.width() - 20
font_size = 18
MIN_FONT_SIZE = 10
while text_width > available_width:
if font_size <= MIN_FONT_SIZE:
break
font_size -= 1
text_width -= 10
self.command_box.setStyleSheet(f"""
background-color: #2b2b2b;
color: #ffffff;
padding: 10px;
border-radius: 5px;
font-family: monospace;
font-size: {font_size}px;
""")
self.command_box.mousePressEvent = lambda _: self.copy_command()
self.command_box.setVisible(True)
self.ui_elements.append(self.command_box)
def _setup_command_box_style(self, command_box):
metrics = command_box.fontMetrics()
text_width = metrics.horizontalAdvance(command_box.text()) + 100
available_width = command_box.width() - 20
font_size = 18
MIN_FONT_SIZE = 10
while text_width > available_width:
if font_size <= MIN_FONT_SIZE:
break
font_size -= 1
text_width -= 10
command_box.setStyleSheet(f"""
background-color: #2b2b2b;
color: #ffffff;
padding: 10px;
border-radius: 5px;
font-family: monospace;
font-size: {font_size}px;
""")
command_box.setVisible(True)
def copy_yay_command(self):
clipboard = QApplication.clipboard()
clipboard.setText("sudo yay -S ratpoison")
original_style = self.yay_command_box.styleSheet()
self.yay_command_box.setStyleSheet(
original_style + "background-color: #27ae60;")
QTimer.singleShot(
200, lambda: self.yay_command_box.setStyleSheet(original_style))
self.update_status.update_status("Yay command copied to clipboard")
def _setup_status_and_nav(self):
if self.current_distro != 'arch':
self.check_button = QPushButton(self)
self.check_button.setGeometry(130, 430, 100, 40)
self.check_button.setIcon(
QIcon(os.path.join(self.btn_path, "check.png")))
self.check_button.setIconSize(self.check_button.size())
self.check_button.clicked.connect(self.check_package)
self.ui_elements.append(self.check_button)
self.button_go.clicked.connect(self.go_next)
self.button_back.clicked.connect(self.go_next)
self.status_label = QLabel("", self)
self.status_label.setGeometry(300, 430, 250, 40)
if self.current_distro == 'arch':
self.status_label.setGeometry(450, 185, 250, 40)
self.ui_elements.append(self.status_label)
def copy_command(self, command=None, command_box=None):
if command_box is None:
command_box = self.command_box
clipboard = QApplication.clipboard()
if command:
clipboard.setText(command)
else:
clipboard.setText(self.manual_install_command)
original_style = command_box.styleSheet()
command_box.setStyleSheet(
original_style + "background-color: #27ae60;")
QTimer.singleShot(
200, lambda: command_box.setStyleSheet(original_style))
self.update_status.update_status("Command copied to clipboard")
def install_package(self):
if not self.install_command:
self.update_status.update_status(
f"Installation not supported for {self.current_distro}")
return
if subprocess.getstatusoutput('pkexec --help')[0] == 127:
self.update_status.update_status("polkit toolkit is not installed")
self.install_button.setDisabled(True)
return
self.worker_thread = WorkerThread('INSTALL_PACKAGE',
package_command=self.install_command,
package_name=self.package_name)
self.worker_thread.text_output.connect(
self.update_status.update_status)
self.worker_thread.finished.connect(self.installation_complete)
self.worker_thread.start()
def installation_complete(self, success):
if success:
self.check_package()
def check_package(self):
try:
if self.package_name.lower() == 'all':
packages = ['bwrap', 'ip', 'microsocks',
'proxychains4', 'ratpoison', 'tor', 'Xephyr', 'wg']
for package in packages:
if subprocess.getstatusoutput(f'{package} --help')[0] == 127:
self.status_label.setText(
f"{package} is not installed")
self.status_label.setStyleSheet("color: red;")
return
self.status_label.setText("All packages installed!")
self.status_label.setStyleSheet("color: green;")
elif self.package_name.lower() == 'wireguard':
self.install_name = 'wg'
elif self.package_name.lower() == 'bubblewrap':
self.install_name = 'bwrap'
elif self.package_name.lower() == 'xserver-xephyr':
self.install_name = 'Xephyr'
else:
self.install_name = self.package_name
if subprocess.getstatusoutput(f'{self.install_name.lower()} --help')[0] == 127:
self.status_label.setText(
f"{self.package_name} is not installed")
self.status_label.setStyleSheet("color: red;")
else:
self.status_label.setText(
f"{self.package_name} is installed!")
self.status_label.setStyleSheet("color: green;")
except Exception:
self.status_label.setText("Check failed")
self.status_label.setStyleSheet("color: red;")
def go_next(self):
self.custom_window.navigator.navigate("menu")