Files
oxapp25/main.py
2025-04-17 13:56:26 +02:00

163 lines
5.9 KiB
Python

from PySide6.QtCore import QStandardPaths, QDataStream, QByteArray, QIODevice, Signal, Qt, QTimer, QCryptographicHash
from PySide6.QtNetwork import QLocalServer, QLocalSocket
from PySide6.QtWidgets import QApplication
import qasync
import sys
import asyncio
import os
import platform
import argparse
import hashlib
import random
import string
import base64
from pathlib import Path
from src.logs import configure_logging
from windows.main_window import MainWindow
class SingleApplication(QApplication):
# Signal émis lorsque des fichiers sont reçus d'une instance secondaire
files_received = Signal(list)
def __init__(self, app_id, args):
super().__init__(args)
self.app_id = app_id
self.shared_key = hashlib.sha256(app_id.encode()).hexdigest()[:16]
self.server = None
self.is_primary_instance = self.try_connect_to_primary()
if self.is_primary_instance:
# C'est la première instance, on crée un serveur local
self.server = QLocalServer()
self.server.newConnection.connect(self.handle_new_connection)
if not self.server.listen(self.app_id):
# En cas d'erreur (serveur déjà existant mais zombie), on le supprime et on réessaie
QLocalServer.removeServer(self.app_id)
self.server.listen(self.app_id)
else:
QTimer.singleShot(0, self.quit)
def encrypt_data(self, data_str):
"""Méthode simple pour brouiller les données"""
# Générer une "nonce" aléatoire pour éviter que les mêmes données produisent le même résultat
nonce = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(8))
# Combiner la nonce, la clé et les données
combined = nonce + self.shared_key + data_str
# Utiliser SHA-256 pour obtenir un hash
hash_obj = QCryptographicHash(QCryptographicHash.Algorithm.Sha256)
hash_obj.addData(combined.encode())
signature = hash_obj.result().toHex().data().decode()[:16]
# Encoder le tout en base64
encoded = base64.b64encode((nonce + signature + data_str).encode()).decode()
return encoded
def decrypt_data(self, encoded_str):
"""Déchiffre les données et vérifie leur intégrité"""
try:
# Décoder de base64
decoded = base64.b64decode(encoded_str.encode()).decode()
# Extraire nonce, signature et données
nonce = decoded[:8]
signature = decoded[8:24]
data_str = decoded[24:]
# Vérifier la signature
combined = nonce + self.shared_key + data_str
hash_obj = QCryptographicHash(QCryptographicHash.Algorithm.Sha256)
hash_obj.addData(combined.encode())
expected_signature = hash_obj.result().toHex().data().decode()[:16]
if signature != expected_signature:
print("Signature invalide, données potentiellement corrompues ou falsifiées")
return None
return data_str
except Exception as e:
print(f"Erreur lors du déchiffrement: {e}")
return None
def try_connect_to_primary(self):
"""Essaie de se connecter à l'instance primaire de l'application"""
socket = QLocalSocket()
socket.connectToServer(self.app_id, QIODevice.OpenModeFlag.WriteOnly)
if socket.waitForConnected(500):
# Récupérer les arguments pour les envoyer à l'instance primaire
args = sys.argv[1:] if len(sys.argv) > 1 else []
encrypt_args = self.encrypt_data(";".join(args))
# Envoyer les arguments à l'instance primaire
stream = QDataStream(socket)
stream.writeQString(encrypt_args)
socket.flush()
socket.waitForBytesWritten(1000)
socket.disconnectFromServer()
QTimer.singleShot(0, self.quit)
return False # Ce n'est pas l'instance primaire
return True # C'est l'instance primaire
def handle_new_connection(self):
"""Gère une nouvelle connexion d'une instance secondaire"""
socket = self.server.nextPendingConnection()
if socket.waitForReadyRead(2000):
stream = QDataStream(socket)
encrypted_args = stream.readQString()
args_str = self.decrypt_data(encrypted_args)
# Émettre un signal pour informer l'application des fichiers à ouvrir
if args_str:
args = args_str.split(";") if args_str else []
if args:
self.files_received.emit(args)
socket.disconnectFromServer()
if __name__ == "__main__":
# Analyser les arguments de la ligne de commande
parser = argparse.ArgumentParser(description='Application PySide6', allow_abbrev=False)
parser.add_argument('--dev', action='store_true', help='Active le mode développement avec logs')
parser.add_argument('files', nargs='*', help='Fichiers à ouvrir')
args, unknown = parser.parse_known_args()
# Configurer le logging en fonction du mode
configure_logging(args.dev)
os.environ["QTWEBENGINE_CHROMIUM_FLAGS"] = "--enable-gpu-rasterization --ignore-gpu-blocklist"
if args.dev:
os.environ["QTWEBENGINE_REMOTE_DEBUGGING"] = "4000"
app_id = "OxAPP25"
app = SingleApplication(app_id, sys.argv)
if not app.is_primary_instance:
sys.exit(0)
event_loop = qasync.QEventLoop(app)
asyncio.set_event_loop(event_loop)
app_close_event = asyncio.Event()
app.aboutToQuit.connect(app_close_event.set)
window = MainWindow()
# Connecter le signal de fichiers reçus à une méthode de traitement
app.files_received.connect(window.handle_files)
if args.files:
window.handle_files(args.files)
window.show()
with event_loop:
event_loop.run_until_complete(app_close_event.wait())