Files
WGSecure/main.py
T
2026-08-31 11:16:04 +02:00

158 lines
5.0 KiB
Python

#!/usr/bin/env python3
"""
WGSecure (WGS) — Interface graphique WireGuard avec MFA
Version 0.4.1
"""
import sys
import argparse
from PyQt6.QtWidgets import QApplication, QMessageBox, QInputDialog, QLineEdit, QSystemTrayIcon
from PyQt6.QtCore import Qt
from app.core.config import Config
from app.core import dns as dns_util
from app.core import log as conn_log
from app.core import shutdown as shutdown_guard
from app.ui.main_window import MainWindow
from app.ui.systray import SystemTray
from app.ui import icons
from app.utils.platform_utils import install_desktop_entry
def _install_taskbar_icon(app_icon) -> None:
"""Exporte l'icône en PNG 256 px et installe l'entrée .desktop."""
try:
import tempfile, os
tmp = os.path.join(tempfile.gettempdir(), "wgsecure_icon.png")
app_icon.pixmap(256, 256).save(tmp, "PNG")
install_desktop_entry(tmp)
except Exception:
pass
def recover_dns_state(config: Config) -> None:
"""Répare la résolution DNS laissée détournée par une session précédente.
Si l'application (ou la machine) s'est arrêtée sans exécuter
`wg-quick down`, la configuration DNS du tunnel reste active alors que le
résolveur correspondant est injoignable : plus aucun nom ne se résout.
Le nettoyage n'a lieu que si un résidu est effectivement constaté.
"""
iface = config.wg.get("interface_name", "wgs0")
try:
repaired, msg = dns_util.repair_if_broken(iface, config.wg.get("dns", ""))
except Exception as e: # noqa: BLE001
repaired, msg = False, f"Vérification DNS impossible : {e}"
if msg:
conn_log.log_event(msg, "warning" if repaired else "error")
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
prog="wgsecure",
description="WGSecure — WireGuard GUI avec MFA",
)
parser.add_argument(
"--admin",
action="store_true",
help="Lancer directement en mode Administrateur",
)
parser.add_argument(
"--no-tray",
action="store_true",
help="Désactiver l'icône systray",
)
return parser.parse_args()
def require_admin_auth(config: Config, app: QApplication) -> bool:
"""Vérifie le mot de passe admin si configuré. Retourne True si autorisé."""
if not config.has_admin_password():
return True
pw, ok = QInputDialog.getText(
None,
"Accès Administrateur",
"Mot de passe administrateur :",
QLineEdit.EchoMode.Password,
)
if not ok:
return False
if config.check_admin_password(pw):
return True
QMessageBox.warning(None, "WGSecure", "Mot de passe incorrect.")
return False
def main():
args = parse_args()
# Support DPI haute résolution
QApplication.setHighDpiScaleFactorRoundingPolicy(
Qt.HighDpiScaleFactorRoundingPolicy.PassThrough
)
app = QApplication(sys.argv)
app.setApplicationName("WGSecure")
app.setApplicationVersion("0.4.1")
app.setOrganizationName("WGS")
# Icône globale multi-tailles — barre des tâches + alt-tab
app_icon = icons.icon_app()
app.setWindowIcon(app_icon)
app.setDesktopFileName("wgsecure")
# Installe .desktop + PNG pour que GNOME/KDE affiche l'icône sur la taskbar
_install_taskbar_icon(app_icon)
# Empêche la fermeture quand toutes les fenêtres sont cachées (tray)
app.setQuitOnLastWindowClosed(False)
config = Config()
# Reste d'une session précédente mal terminée : DNS détourné vers un
# résolveur mort. À faire avant d'ouvrir la fenêtre pour rendre l'accès
# au réseau tout de suite.
recover_dns_state(config)
# Restauration de l'état réseau garantie sur toutes les sorties :
# Quitter, croix, SIGINT/SIGTERM, atexit, exception non rattrapée.
shutdown_guard.install(config, report=conn_log.log_event, app=app)
# Mode Admin direct (--admin)
if args.admin:
if not require_admin_auth(config, app):
sys.exit(0)
from app.ui.admin_window import AdminWindow
win = AdminWindow(config)
win.setWindowIcon(icons.icon_admin())
win.show()
sys.exit(app.exec())
# Mode User (défaut)
window = MainWindow(config)
# Systray
if not args.no_tray and QSystemTrayIcon_available():
tray = SystemTray(config, window)
tray.show()
# Démarrer réduit si déjà configuré
if config.configured:
window.show()
else:
window.show()
tray.showMessage(
"WGSecure",
"Bienvenue ! Ouvrez le panneau Administrateur pour configurer WireGuard.",
QSystemTrayIcon.MessageIcon.Information,
4000,
)
else:
window.show()
sys.exit(app.exec())
def QSystemTrayIcon_available() -> bool:
from PyQt6.QtWidgets import QSystemTrayIcon
return QSystemTrayIcon.isSystemTrayAvailable()
if __name__ == "__main__":
main()