import os from pathlib import Path import sqlite3 import logging from PySide6.QtWebEngineCore import QWebEngineSettings, QWebEngineProfile, QWebEnginePage, QWebEngineCookieStore from PySide6.QtWidgets import QWidget from PySide6.QtWebEngineWidgets import QWebEngineView from PySide6.QtWebChannel import QWebChannel from PySide6.QtCore import Slot, QFile, QIODevice, Signal, Qt, QStandardPaths, QTimer, QDateTime from PySide6.QtNetwork import QNetworkCookie import sys from src.conf import ConfManager from src.handler import WebHandler # https://gitea.devpanel.fr/oxpanel/app/src/branch/master/window/site.py class SiteWindow(QWebEngineView): on_cookie_added = Signal(QNetworkCookie) def __init__(self, parent=None): super().__init__(parent) # Configuration du logger self.logger = logging.getLogger(__name__) self.logger.debug("Initialisation de SiteWindow") self.conf = parent.conf self.web_handler = parent.web_handler self.page().profile().cookieStore().deleteAllCookies() self.logger.debug("Cookies supprimés du profil par défaut") storage_path = Path(QStandardPaths.writableLocation(QStandardPaths.StandardLocation.AppDataLocation)) self.logger.debug(f"Chemin de stockage des données: {storage_path}") if not storage_path.exists(): storage_path.mkdir(parents=True) self.logger.debug(f"Création du répertoire de stockage: {storage_path}") self.persistent_profile = QWebEngineProfile("OxAppProfile", parent=self) self.logger.debug("Création du profil web persistant: OxAppProfile") self.cookie_store = self.persistent_profile.cookieStore() self.cookie_store.cookieAdded.connect(self.on_cookie_added.emit) self.logger.debug("Signal cookieAdded connecté") self.persistent_profile.setHttpCacheType(QWebEngineProfile.HttpCacheType.MemoryHttpCache) self.logger.debug("Type de cache HTTP configuré: MemoryHttpCache") cache_path = str(self.conf.app_config_path / "web_cache") self.persistent_profile.setPersistentStoragePath(cache_path) self.logger.debug(f"Chemin de stockage persistant configuré: {cache_path}") self.persistent_profile.setPersistentCookiesPolicy(QWebEngineProfile.PersistentCookiesPolicy.AllowPersistentCookies) self.logger.debug("Politique de cookies persistants configurée: AllowPersistentCookies") self.persistent_profile.setHttpUserAgent("oxapp25") self.logger.debug("User-Agent HTTP configuré: oxapp25") custom_page = QWebEnginePage(self.persistent_profile, parent=self) self.setPage(custom_page) self.logger.debug("Page web personnalisée créée et configurée") # Configuration des attributs de la page web self.settings().setAttribute(QWebEngineSettings.WebAttribute.Accelerated2dCanvasEnabled, True) self.settings().setAttribute(QWebEngineSettings.WebAttribute.ScrollAnimatorEnabled, True) self.settings().setAttribute(QWebEngineSettings.WebAttribute.WebGLEnabled, True) # self.settings().setAttribute(QWebEngineSettings.WebAttribute.LocalStorageEnabled, False) # self.settings().setAttribute(QWebEngineSettings.WebAttribute.AutoLoadImages, True) # self.settings().setAttribute(QWebEngineSettings.WebAttribute.PluginsEnabled, False) self.settings().setAttribute(QWebEngineSettings.WebAttribute.ShowScrollBars, True) self.page().setBackgroundColor(Qt.GlobalColor.white) self.logger.debug("Attributs de la page web configurés") self.web_channel = QWebChannel(self) self.web_channel.registerObject("handler", self.web_handler) self.page().setWebChannel(self.web_channel) self.logger.debug("Canal web configuré avec le gestionnaire") self.loadFinished.connect(self.on_load_finished) self.logger.debug("Signal loadFinished connecté") self.logger.info(f"Chargement de l'URL: {parent.url}") self.load(parent.url) @Slot(bool) def on_load_finished(self, is_success): if is_success: self.logger.info("Page chargée avec succès") # Injection de l'API WebChannel api_file = QFile(":/qtwebchannel/qwebchannel.js") api_file.open(QIODevice.OpenModeFlag.ReadOnly) api_content = api_file.readAll().data().decode("utf-8") api_file.close() self.page().runJavaScript(api_content) self.logger.debug("API WebChannel injectée dans la page") # Correction de la barre de défilement principale css = """ html::-webkit-scrollbar, body::-webkit-scrollbar { display: none; } html, body { scrollbar-width: none; /* Pour Firefox */ -ms-overflow-style: none; /* Pour Internet Explorer et Edge */ overflow: auto; /* Conserver la capacité de défilement */ } """ js_code = f"var style = document.createElement('style'); style.textContent = `{css}`; document.head.appendChild(style);" self.page().runJavaScript(js_code) self.logger.debug("CSS personnalisé injecté pour masquer les barres de défilement") # Recréation du cookie de session self.recreate_sessionid_cookie() else: self.logger.error("Échec du chargement de la page") def sessionid_from_cookie_store(self) -> None|sqlite3.Row: """ Récupère le cookie de session depuis le stockage de cookies. Cette approche est expérimentale et dépend de l'implémentation interne. Returns: sqlite3.Row ou None: Les informations du cookie de session ou None si non trouvé """ profile_path = self.persistent_profile.persistentStoragePath() cookies_db_path = os.path.join(profile_path, "Cookies") self.logger.debug(f"Recherche du cookie de session dans: {cookies_db_path}") if os.path.exists(cookies_db_path): try: conn = sqlite3.connect(cookies_db_path) conn.row_factory = sqlite3.Row cursor = conn.cursor() cursor.execute("SELECT * FROM cookies WHERE name='sessionid'") result = cursor.fetchone() conn.close() if result: self.logger.debug("Cookie de session trouvé dans la base de données") return result # La valeur du cookie else: self.logger.debug("Aucun cookie de session trouvé dans la base de données") except Exception as e: self.logger.error(f"Erreur lors de l'accès au fichier de cookies: {e}", exc_info=True) else: self.logger.debug(f"Fichier de cookies non trouvé: {cookies_db_path}") return None def recreate_sessionid_cookie(self): """ Recrée le cookie de session à partir des informations stockées dans la base de données. """ self.logger.debug("Tentative de recréation du cookie de session") cookie_info = self.sessionid_from_cookie_store() if cookie_info is None: self.logger.debug("Aucune information de cookie de session disponible, abandon") return # Créer un nouveau cookie avec le nom et la valeur cookie = QNetworkCookie(b"sessionid", cookie_info['value'].encode()) self.logger.debug(f"Création d'un nouveau cookie 'sessionid' avec la valeur récupérée") # Configurer toutes les propriétés disponibles cookie.setDomain(cookie_info['host_key']) cookie.setPath(cookie_info['path']) self.logger.debug(f"Configuration du domaine: {cookie_info['host_key']} et du chemin: {cookie_info['path']}") # Gérer les dates d'expiration (conversion du format si nécessaire) if cookie_info['has_expires']: # Convertir depuis le format UNIX en QDateTime # Attention: Chromium stocke les dates en microseconds depuis 1601-01-01 UTC # Vous devrez peut-être ajuster cette conversion selon le format exact expires = QDateTime.fromSecsSinceEpoch(int(cookie_info['expires_utc'] / 1000000) - 11644473600) cookie.setExpirationDate(expires) self.logger.debug(f"Date d'expiration configurée: {expires.toString()}") # Configurer les attributs de sécurité cookie.setSecure(bool(cookie_info['is_secure'])) cookie.setHttpOnly(bool(cookie_info['is_httponly'])) self.logger.debug(f"Attributs de sécurité configurés - Secure: {bool(cookie_info['is_secure'])}, HttpOnly: {bool(cookie_info['is_httponly'])}") # Configurer SameSite si disponible dans votre version de PySide6 # Vérifier si l'attribut est disponible (PySide6 ≥ 6.2.0) if hasattr(cookie, 'setSameSitePolicy'): # Conversion des valeurs numériques en constantes SameSite samesite_value = cookie_info['samesite'] samesite_map = { 0: QNetworkCookie.SameSite.None_, # None 1: QNetworkCookie.SameSite.Lax, # Lax 2: QNetworkCookie.SameSite.Strict # Strict } if samesite_value in samesite_map: cookie.setSameSitePolicy(samesite_map[samesite_value]) self.logger.debug(f"Politique SameSite configurée: {samesite_map[samesite_value]}") # Ajouter le cookie au store - cela va déclencher le signal cookieAdded self.cookie_store.setCookie(cookie) self.logger.info("Cookie de session recréé et ajouté au store") def contextMenuEvent(self, event): # Ignorer l'événement pour empêcher l'affichage du menu contextuel self.logger.debug("Menu contextuel désactivé") event.ignore()