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