Compare commits

5 Commits

Author SHA1 Message Date
be72749b53 update idea 2025-05-23 00:36:08 +02:00
dc2adfbca5 Merge remote-tracking branch 'origin/master' 2025-04-22 02:13:15 +02:00
a693338891 disable encryption inter process 2025-04-22 02:12:58 +02:00
bfa6865109 Merge remote-tracking branch 'origin/master' 2025-04-20 12:00:24 +02:00
3155154083 linux integration 2025-04-20 11:58:46 +02:00
7 changed files with 135 additions and 120 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
.venv .venv
OxApp.dist OxApp.dist
installer installer
OxApp.bin

2
.idea/misc.xml generated
View File

@@ -3,7 +3,7 @@
<component name="Black"> <component name="Black">
<option name="sdkName" value="Python 3.13 (oxapp25)" /> <option name="sdkName" value="Python 3.13 (oxapp25)" />
</component> </component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.13 virtualenv at D:\Dev\oxapp25\.venv" project-jdk-type="Python SDK" /> <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.13 (oxapp25)" project-jdk-type="Python SDK" />
<component name="PythonCompatibilityInspectionAdvertiser"> <component name="PythonCompatibilityInspectionAdvertiser">
<option name="version" value="3" /> <option name="version" value="3" />
</component> </component>

2
.idea/oxapp25.iml generated
View File

@@ -4,7 +4,7 @@
<content url="file://$MODULE_DIR$"> <content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.venv" /> <excludeFolder url="file://$MODULE_DIR$/.venv" />
</content> </content>
<orderEntry type="jdk" jdkName="Python 3.13 virtualenv at D:\Dev\oxapp25\.venv" jdkType="Python SDK" /> <orderEntry type="jdk" jdkName="Python 3.13 (oxapp25)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
</component> </component>
</module> </module>

View File

@@ -16,7 +16,7 @@ resources:
# Déploie l'application avec pyside6-deploy # Déploie l'application avec pyside6-deploy
deploy: deploy:
pyside6-deploy -c pysidedeploy.spec pyside6-deploy -c pysidedeploy_linux.spec
# Crée l'exécutable avec PyInstaller # Crée l'exécutable avec PyInstaller
pyinstaller: pyinstaller:

116
main.py
View File

@@ -1,4 +1,4 @@
from PySide6.QtCore import QStandardPaths, QDataStream, QByteArray, QIODevice, Signal, Qt, QTimer, QCryptographicHash from PySide6.QtCore import QDataStream, QIODevice, Signal, QTimer
from PySide6.QtGui import QPalette, QColor from PySide6.QtGui import QPalette, QColor
from PySide6.QtNetwork import QLocalServer, QLocalSocket from PySide6.QtNetwork import QLocalServer, QLocalSocket
from PySide6.QtWidgets import QApplication from PySide6.QtWidgets import QApplication
@@ -7,14 +7,8 @@ import qasync
import sys import sys
import asyncio import asyncio
import os import os
import platform
import argparse import argparse
import hashlib
import random
import string
import base64
import logging import logging
from pathlib import Path
from src.logs import configure_logging from src.logs import configure_logging
from windows.main_window import MainWindow from windows.main_window import MainWindow
@@ -63,9 +57,6 @@ class SingleApplication(QApplication):
self.app_id = app_id self.app_id = app_id
self.logger.debug(f"ID de l'application: {app_id}") self.logger.debug(f"ID de l'application: {app_id}")
self.shared_key = hashlib.sha256(app_id.encode()).hexdigest()[:16]
self.logger.debug(f"Clé partagée générée: {self.shared_key}")
self.server = None self.server = None
self.is_primary_instance = self.try_connect_to_primary() self.is_primary_instance = self.try_connect_to_primary()
@@ -77,7 +68,8 @@ class SingleApplication(QApplication):
self.logger.debug("Signal newConnection connecté") self.logger.debug("Signal newConnection connecté")
if not self.server.listen(self.app_id): if not self.server.listen(self.app_id):
self.logger.warning(f"Échec de l'écoute sur {self.app_id}, tentative de suppression du serveur existant") self.logger.warning(
f"Échec de l'écoute sur {self.app_id}, tentative de suppression du serveur existant")
# En cas d'erreur (serveur déjà existant mais zombie), on le supprime et on réessaie # En cas d'erreur (serveur déjà existant mais zombie), on le supprime et on réessaie
QLocalServer.removeServer(self.app_id) QLocalServer.removeServer(self.app_id)
if self.server.listen(self.app_id): if self.server.listen(self.app_id):
@@ -90,78 +82,6 @@ class SingleApplication(QApplication):
self.logger.info("Instance secondaire détectée, fermeture de l'application") self.logger.info("Instance secondaire détectée, fermeture de l'application")
QTimer.singleShot(0, self.quit) QTimer.singleShot(0, self.quit)
def encrypt_data(self, data_str):
"""
Méthode simple pour brouiller les données
Args:
data_str (str): Données à chiffrer
Returns:
str: Données chiffrées en base64
"""
self.logger.debug(f"Chiffrement des données (longueur: {len(data_str)})")
# 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))
self.logger.debug(f"Nonce générée: {nonce}")
# Combiner la nonce, la clé et les données
combined = nonce + self.shared_key + data_str
self.logger.debug("Données combinées avec nonce et clé partagée")
# 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]
self.logger.debug(f"Signature générée: {signature}")
# Encoder le tout en base64
encoded = base64.b64encode((nonce + signature + data_str).encode()).decode()
self.logger.debug(f"Données encodées en base64 (longueur: {len(encoded)})")
return encoded
def decrypt_data(self, encoded_str):
"""
Déchiffre les données et vérifie leur intégrité
Args:
encoded_str (str): Données chiffrées en base64
Returns:
str ou None: Données déchiffrées ou None en cas d'erreur
"""
self.logger.debug(f"Déchiffrement des données (longueur: {len(encoded_str)})")
try:
# Décoder de base64
decoded = base64.b64decode(encoded_str.encode()).decode()
self.logger.debug("Données décodées de base64")
# Extraire nonce, signature et données
nonce = decoded[:8]
signature = decoded[8:24]
data_str = decoded[24:]
self.logger.debug(f"Nonce extraite: {nonce}, signature: {signature}")
# 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]
self.logger.debug(f"Signature attendue: {expected_signature}")
if signature != expected_signature:
self.logger.warning("Signature invalide, données potentiellement corrompues ou falsifiées")
return None
self.logger.debug(f"Données déchiffrées avec succès (longueur: {len(data_str)})")
return data_str
except Exception as e:
self.logger.error(f"Erreur lors du déchiffrement: {e}", exc_info=True)
return None
def try_connect_to_primary(self): def try_connect_to_primary(self):
""" """
Essaie de se connecter à l'instance primaire de l'application Essaie de se connecter à l'instance primaire de l'application
@@ -180,12 +100,13 @@ class SingleApplication(QApplication):
args = sys.argv[1:] if len(sys.argv) > 1 else [] args = sys.argv[1:] if len(sys.argv) > 1 else []
self.logger.debug(f"Arguments à transmettre: {args}") self.logger.debug(f"Arguments à transmettre: {args}")
encrypt_args = self.encrypt_data(";".join(args)) # Envoi des arguments sans chiffrement
self.logger.debug("Arguments chiffrés pour transmission") args_str = ";".join(args)
self.logger.debug("Arguments à envoyer: " + args_str)
# Envoyer les arguments à l'instance primaire # Envoyer les arguments à l'instance primaire
stream = QDataStream(socket) stream = QDataStream(socket)
stream.writeQString(encrypt_args) stream.writeQString(args_str)
socket.flush() socket.flush()
self.logger.debug("Données envoyées à l'instance primaire") self.logger.debug("Données envoyées à l'instance primaire")
@@ -216,22 +137,17 @@ class SingleApplication(QApplication):
self.logger.debug("Données disponibles pour lecture") self.logger.debug("Données disponibles pour lecture")
stream = QDataStream(socket) stream = QDataStream(socket)
encrypted_args = stream.readQString() # Lecture des arguments sans déchiffrement
self.logger.debug(f"Arguments chiffrés reçus (longueur: {len(encrypted_args)})") args_str = stream.readQString()
self.logger.debug(f"Arguments reçus: {args_str}")
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 []
self.logger.debug(f"Arguments déchiffrés: {args_str}") if args:
self.logger.info(f"Émission du signal files_received avec {len(args)} arguments")
# Émettre un signal pour informer l'application des fichiers à ouvrir self.files_received.emit(args)
args = args_str.split(";") if args_str else []
if args:
self.logger.info(f"Émission du signal files_received avec {len(args)} arguments")
self.files_received.emit(args)
else:
self.logger.debug("Aucun argument à traiter")
else: else:
self.logger.warning("Échec du déchiffrement des arguments") self.logger.debug("Aucun argument à traiter")
else: else:
self.logger.warning("Délai d'attente dépassé pour la lecture des données") self.logger.warning("Délai d'attente dépassé pour la lecture des données")

View File

@@ -43,7 +43,7 @@ qml_files =
excluded_qml_plugins = excluded_qml_plugins =
# qt modules used. comma separated # qt modules used. comma separated
modules = WebEngineCore,WebEngineWidgets,WebChannel,Core,Gui,Widgets,Network modules = Network,WebEngineCore,Widgets,Gui,WebEngineWidgets,Core,WebChannel
# qt plugins used by the application. only relevant for desktop deployment. for qt plugins used # qt plugins used by the application. only relevant for desktop deployment. for qt plugins used
# in android application see [android][plugins] # in android application see [android][plugins]
@@ -71,7 +71,7 @@ macos.permissions =
mode = standalone mode = standalone
# (str) specify any extra nuitka arguments # (str) specify any extra nuitka arguments
extra_args = --quiet --noinclude-qt-translations --windows-console-mode=disable --output-filename=oxapp extra_args = --quiet --noinclude-qt-translations --windows-console-mode=disable --output-filename=oxapp --company-name=Oxpanel --product-name=OxApp --file-description="Application légitime OxApp" --copyright="Oxpanel (c) 2023" --file-version=1.0.0 --product-version=1.0.0 --unstripped --no-deployment-flag=self-execution --disable-ccache --disable-console
[buildozer] [buildozer]
@@ -99,4 +99,3 @@ local_libs =
# architecture of deployed platform # architecture of deployed platform
# possible values = ["aarch64", "armv7a", "i686", "x86_64"] # possible values = ["aarch64", "armv7a", "i686", "x86_64"]
arch = arch =

99
pysidedeploy_linux.spec Executable file
View File

@@ -0,0 +1,99 @@
[app]
# title of your application
title = OxApp
# project directory. the general assumption is that project_dir is the parent directory
# of input_file
project_dir = /home/nell/dev/oxapp25
# source file path
input_file = /home/nell/dev/oxapp25/main.py
# directory where the executable output is generated
exec_directory = .
# path to .pyproject project file
project_file =
# application icon
icon = /home/nell/dev/oxapp25/oxpanel.ico
[python]
# python path
python_path = /home/nell/dev/oxapp25/.venv/bin/python
# python packages to install
packages = Nuitka==2.5.1
# buildozer = for deploying Android application
android_packages = buildozer==1.5.0,cython==0.29.33
[qt]
# comma separated path to qml files required
# normally all the qml files required by the project are added automatically
qml_files =
# excluded qml plugin binaries
excluded_qml_plugins =
# qt modules used. comma separated
modules = Positioning,WebChannel,Quick,QuickWidgets,QmlMeta,OpenGL,DBus,Core,QmlModels,QmlWorkerScript,Qml,Network,Widgets,Gui,WebEngineWidgets,PrintSupport,WebEngineCore
# qt plugins used by the application. only relevant for desktop deployment. for qt plugins used
# in android application see [android][plugins]
plugins = networkinformation,scenegraph,platforms/darwin,iconengines,tls,xcbglintegrations,qmltooling,position,platformthemes,imageformats,platforms,platforminputcontexts,accessiblebridge,generic,networkaccess,styles,printsupport,egldeviceintegrations
[android]
# path to pyside wheel
wheel_pyside =
# path to shiboken wheel
wheel_shiboken =
# plugins to be copied to libs folder of the packaged application. comma separated
plugins =
[nuitka]
# usage description for permissions requested by the app as found in the info.plist file
# of the app bundle
# eg = extra_args = --show-modules --follow-stdlib
macos.permissions =
# mode of using nuitka. accepts standalone or onefile. default is onefile.
mode = onefile
# (str) specify any extra nuitka arguments
extra_args = --quiet --noinclude-qt-translations --jobs=auto --lto=yes
[buildozer]
# build mode
# possible options = [release, debug]
# release creates an aab, while debug creates an apk
mode = debug
# contrains path to pyside6 and shiboken6 recipe dir
recipe_dir =
# path to extra qt android jars to be loaded by the application
jars_dir =
# if empty uses default ndk path downloaded by buildozer
ndk_path =
# if empty uses default sdk path downloaded by buildozer
sdk_path =
# other libraries to be loaded. comma separated.
# loaded at app startup
local_libs =
# architecture of deployed platform
# possible values = ["aarch64", "armv7a", "i686", "x86_64"]
arch =