10 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
9031e1d04d icon png integration 2025-04-20 11:59:39 +02:00
3155154083 linux integration 2025-04-20 11:58:46 +02:00
012ab977a7 some fix 2025-04-20 04:40:14 +02:00
ce8d9d639a wtf ? 2025-04-20 03:36:58 +02:00
a061ee20a8 add http2 2025-04-20 01:46:58 +02:00
ed5a381262 Init 2025-04-19 23:09:40 +02:00
12 changed files with 175 additions and 122 deletions

4
.gitignore vendored
View File

@@ -1,2 +1,4 @@
.venv .venv
OxApp.dist OxApp.dist
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>

32
Makefile Normal file
View File

@@ -0,0 +1,32 @@
# Makefile pour OxApp - équivalent Linux du build.bat
.PHONY: help resources deploy pyinstaller installer
# Commande par défaut - affiche l'aide
help:
@echo "Commandes disponibles:"
@echo " make resources - Compile les ressources"
@echo " make deploy - Déploie l'application"
@echo " make pyinstaller - Crée l'exécutable avec PyInstaller"
@echo " make installer - Crée l'installateur"
# Compile les ressources
resources:
pyside6-rcc resources.qrc -o resources_rc.py
# Déploie l'application avec pyside6-deploy
deploy:
pyside6-deploy -c pysidedeploy_linux.spec
# Crée l'exécutable avec PyInstaller
pyinstaller:
pyinstaller OxApp.spec --noconfirm
# Crée l'installateur
# Note: Inno Setup est spécifique à Windows, vous devrez utiliser
# un équivalent Linux comme makeself, AppImage, ou Flatpak
installer:
@echo "La création d'installateur sur Linux nécessite un outil différent d'Inno Setup."
@echo "Options recommandées: makeself, AppImage, ou Flatpak"
@echo "Exemple avec makeself (si installé):"
@echo " makeself.sh ./dist/OxApp \"OxApp\" \"OxApp Installation\" ./dist/Ox

View File

@@ -5,8 +5,12 @@ if "%1"=="resources" (
pyside6-deploy.exe -c pysidedeploy.spec pyside6-deploy.exe -c pysidedeploy.spec
) else if "%1"=="pyinstaller" ( ) else if "%1"=="pyinstaller" (
pyinstaller OxApp.spec --noconfirm pyinstaller OxApp.spec --noconfirm
) else if "%1"=="installer" (
"C:\Program Files (x86)\Inno Setup 6\ISCC.exe" "setup.iss"
) else ( ) else (
echo Commandes disponibles: echo Commandes disponibles:
echo build.bat resources - Compile les ressources echo build.bat resources - Compile les ressources
echo build.bat deploy - Déploie l'application echo build.bat deploy - Déploie l'application
echo build.bat pyinstaller - Crée l'exécutable avec PyInstaller
echo build.bat installer - Crée l'installateur avec Inno Setup
) )

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

BIN
oxpanel.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

View File

@@ -17,7 +17,7 @@ input_file = D:\Dev\oxapp25\main.py
exec_directory = . exec_directory = .
# path to .pyproject project file # path to .pyproject project file
project_file = project_file =
# application icon # application icon
icon = D:\Dev\oxapp25\oxpanel.ico icon = D:\Dev\oxapp25\oxpanel.ico
@@ -37,13 +37,13 @@ android_packages = buildozer==1.5.0,cython==0.29.33
# comma separated path to qml files required # comma separated path to qml files required
# normally all the qml files required by the project are added automatically # normally all the qml files required by the project are added automatically
qml_files = qml_files =
# excluded qml plugin binaries # excluded qml plugin binaries
excluded_qml_plugins = excluded_qml_plugins =
# qt modules used. comma separated # qt modules used. comma separated
modules = Gui,Network,Core,Widgets,WebChannel,WebEngineWidgets,WebEngineCore 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]
@@ -52,26 +52,26 @@ plugins = networkinformation,styles,networkaccess,tls
[android] [android]
# path to pyside wheel # path to pyside wheel
wheel_pyside = wheel_pyside =
# path to shiboken wheel # path to shiboken wheel
wheel_shiboken = wheel_shiboken =
# plugins to be copied to libs folder of the packaged application. comma separated # plugins to be copied to libs folder of the packaged application. comma separated
plugins = plugins =
[nuitka] [nuitka]
# usage description for permissions requested by the app as found in the info.plist file # usage description for permissions requested by the app as found in the info.plist file
# of the app bundle # of the app bundle
# eg = extra_args = --show-modules --follow-stdlib # eg = extra_args = --show-modules --follow-stdlib
macos.permissions = macos.permissions =
# mode of using nuitka. accepts standalone or onefile. default is onefile. # mode of using nuitka. accepts standalone or onefile. default is onefile.
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]
@@ -81,22 +81,21 @@ extra_args = --quiet --noinclude-qt-translations --windows-console-mode=disable
mode = release mode = release
# contrains path to pyside6 and shiboken6 recipe dir # contrains path to pyside6 and shiboken6 recipe dir
recipe_dir = recipe_dir =
# path to extra qt android jars to be loaded by the application # path to extra qt android jars to be loaded by the application
jars_dir = jars_dir =
# if empty uses default ndk path downloaded by buildozer # if empty uses default ndk path downloaded by buildozer
ndk_path = ndk_path =
# if empty uses default sdk path downloaded by buildozer # if empty uses default sdk path downloaded by buildozer
sdk_path = sdk_path =
# other libraries to be loaded. comma separated. # other libraries to be loaded. comma separated.
# loaded at app startup # loaded at app startup
local_libs = 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 =

View File

@@ -1,4 +1,4 @@
PySide6<6.9,>=6.8.0 PySide6<6.9,>=6.8.0
qasync>=0.27.1 qasync>=0.27.1
httpx httpx[http2]
anyio anyio

View File

@@ -39,11 +39,11 @@ Name: "french"; MessagesFile: "compiler:Languages\French.isl"
[Tasks] [Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
Name: "associatetorrent"; Description: "Associer les fichiers .torrent à {#MyAppName}"; GroupDescription: "Association de fichiers:"; Flags: unchecked Name: "associatetorrent"; Description: "Associer les fichiers .torrent à {#MyAppName}"; GroupDescription: "Association de fichiers:";
[Files] [Files]
; Ajoutez tous les fichiers nécessaires à votre application ; Ajoutez tous les fichiers nécessaires à votre application
Source: "D:\Dev\oxapp25\dist\OxApp.dist\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs Source: "D:\Dev\oxapp25\OxApp.dist\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
[Icons] [Icons]
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}" Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"

View File

@@ -186,6 +186,7 @@ class DownloadManager(QObject):
follow_redirects=True, follow_redirects=True,
verify=False, verify=False,
cookies=self.cookies, cookies=self.cookies,
http2=True,
) as client: ) as client:
# requête pour le téléchargement # requête pour le téléchargement
async with client.stream("GET", file.url, headers=headers) as response: async with client.stream("GET", file.url, headers=headers) as response: