Files
oxapp25/windows/site_window.py
2025-04-19 14:51:11 +02:00

211 lines
9.9 KiB
Python

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()