Actualiser 7lna.py

This commit is contained in:
2026-05-22 12:55:40 +00:00
parent 6b34a54f03
commit 9b7e95ee65

596
7lna.py
View File

@@ -1,19 +1,6 @@
import customtkinter as ctk
from tkinter import filedialog, messagebox
import os
import threading
import subprocess
import urllib.request
import urllib.error
import time
import datetime
import glob
import hashlib
import string
import secrets
import json
import platform
import shutil
import os, threading, subprocess, urllib.request, urllib.error, time, datetime, glob, json, platform, shutil
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
@@ -25,8 +12,16 @@ QUARANTINE_DIR = os.path.expanduser("~/.7lna_quarantine")
WATCH_FOLDER = os.path.expanduser("~/Téléchargements")
HISTORY_FILE = os.path.expanduser("~/.7lna_history.json")
# Palette High-Tech 2026
BG_MAIN = "#0B0F19" # Fond ultra sombre
BG_CARD = "#161E2E" # Fond des cartes
BG_SIDEBAR = "#090B10" # Fond barre latérale
ACCENT_BLUE = "#3B82F6" # Bleu électrique
ACCENT_VIOLET = "#8B5CF6" # Violet néon (Zero USB)
TEXT_MAIN = "#F3F4F6"
TEXT_MUTED = "#9CA3AF"
ctk.set_appearance_mode("dark")
ctk.set_default_color_theme("blue")
# ==========================================
# UTILITAIRES SYSTÈME
@@ -34,7 +29,7 @@ ctk.set_default_color_theme("blue")
def send_desktop_notification(title, message, is_critical=False):
try:
urgency = 'critical' if is_critical else 'normal'
subprocess.Popen(['notify-send', '-u', urgency, '-a', '7LnA Security', title, message])
subprocess.Popen(['notify-send', '-u', urgency, '-a', '7lna Antivirus', title, message])
except Exception:
pass
@@ -52,9 +47,10 @@ class RealTimeShieldHandler(FileSystemEventHandler):
class Antivirus7LnA(ctk.CTk):
def __init__(self):
super().__init__()
self.title("7LnA Security Suite - V10.1 Quantum Edition")
self.title("7lna Antivirus")
self.geometry("1250x850")
self.minsize(1000, 700)
self.minsize(1050, 750)
self.configure(fg_color=BG_MAIN)
os.makedirs(QUARANTINE_DIR, exist_ok=True)
if not os.path.exists(HISTORY_FILE):
@@ -62,7 +58,7 @@ class Antivirus7LnA(ctk.CTk):
self.shield_observer = None
self.shield_active = False
self.zero_usb_mode = False # <--- AJOUT : État du mode Zero USB
self.zero_usb_mode = False
self.check_dependencies()
self.setup_ui()
@@ -76,30 +72,44 @@ class Antivirus7LnA(ctk.CTk):
except:
self.clamav_installed = False
# ==========================================
# INTERFACE GRAPHIQUE (DESIGN 2026)
# ==========================================
def setup_ui(self):
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(1, weight=1)
# --- BARRE LATÉRALE ---
self.sidebar = ctk.CTkFrame(self, width=260, corner_radius=0, fg_color="#111827")
self.sidebar = ctk.CTkFrame(self, width=280, corner_radius=0, fg_color=BG_SIDEBAR)
self.sidebar.grid(row=0, column=0, sticky="nsew")
self.sidebar.grid_rowconfigure(10, weight=1)
ctk.CTkLabel(self.sidebar, text="🛡️ 7LnA Sec", font=ctk.CTkFont(size=30, weight="bold"), text_color="#3B82F6").grid(row=0, column=0, padx=20, pady=(30, 20))
# Logo
logo_frame = ctk.CTkFrame(self.sidebar, fg_color="transparent")
logo_frame.grid(row=0, column=0, pady=(40, 30), sticky="ew")
ctk.CTkLabel(logo_frame, text="🛡️ 7lna", font=ctk.CTkFont(size=36, weight="bold"), text_color=ACCENT_BLUE).pack()
ctk.CTkLabel(logo_frame, text="A N T I V I R U S", font=ctk.CTkFont(size=12, weight="bold", letter_spacing=2), text_color=ACCENT_VIOLET).pack()
self.btn_dash = self.create_nav_button("📊 Tableau de Bord", 1, "dashboard")
self.btn_scan = self.create_nav_button("🔍 Scanner Manuel", 2, "scanner")
self.btn_shield = self.create_nav_button("⚡ Bouclier Actif", 3, "shield")
self.btn_audit = self.create_nav_button("⚙️ Audit Réseau", 4, "audit")
self.btn_tools = self.create_nav_button("🧰 Outils Avancés", 5, "tools")
self.btn_quarantine = self.create_nav_button("📦 Quarantaine", 6, "quarantine")
self.btn_schedule = self.create_nav_button("📅 Planification", 7, "schedule")
self.btn_history = self.create_nav_button("📜 Rapports", 8, "history")
self.btn_update = self.create_nav_button("🔄 Mise à jour", 9, "update")
# Navigation
self.nav_buttons = {}
self.create_nav_button("📊 Tableau de Bord", 1, "dashboard")
self.create_nav_button("🔍 Scanner Système", 2, "scanner")
self.create_nav_button("⚡ Bouclier Actif", 3, "shield")
self.create_nav_button("🌐 Audit & Réseau", 4, "audit")
self.create_nav_button("🧰 Boîte à Outils", 5, "tools")
self.create_nav_button("📦 Quarantaine", 6, "quarantine")
self.create_nav_button("📅 Automatisation", 7, "schedule")
self.create_nav_button("📜 Rapports", 8, "history")
self.version_label = ctk.CTkLabel(self.sidebar, text="v10.1 - Quantum", text_color="#6B7280", font=ctk.CTkFont(weight="bold"))
# Bouton Mise à jour (Mise en évidence)
self.btn_update = ctk.CTkButton(self.sidebar, text="🔄 Rechercher MAJ", command=lambda: self.select_view("update"),
fg_color="#1E3A8A", hover_color=ACCENT_BLUE, font=ctk.CTkFont(size=14, weight="bold"), height=45)
self.btn_update.grid(row=9, column=0, padx=20, pady=20, sticky="ew")
self.version_label = ctk.CTkLabel(self.sidebar, text="V10.1 Quantum Edition", text_color=TEXT_MUTED, font=ctk.CTkFont(size=11))
self.version_label.grid(row=10, column=0, pady=20, sticky="s")
# --- VUES ---
self.views = {}
self.init_dashboard_view()
self.init_scanner_view()
@@ -115,15 +125,24 @@ class Antivirus7LnA(ctk.CTk):
def create_nav_button(self, text, row, view_name):
btn = ctk.CTkButton(self.sidebar, text=text, anchor="w", fg_color="transparent",
text_color="#D1D5DB", hover_color="#1F2937", font=ctk.CTkFont(size=15), height=40,
text_color=TEXT_MUTED, hover_color=BG_CARD, font=ctk.CTkFont(size=15, weight="bold"), height=45,
command=lambda: self.select_view(view_name))
btn.grid(row=row, column=0, padx=20, pady=5, sticky="ew")
return btn
btn.grid(row=row, column=0, padx=15, pady=4, sticky="ew")
self.nav_buttons[view_name] = btn
def select_view(self, view_name):
# Réinitialiser les couleurs des boutons
for name, btn in self.nav_buttons.items():
btn.configure(fg_color="transparent", text_color=TEXT_MUTED)
# Activer le bouton cliqué (s'il existe dans le dictionnaire)
if view_name in self.nav_buttons:
self.nav_buttons[view_name].configure(fg_color=BG_CARD, text_color=TEXT_MAIN)
# Afficher la vue
for view in self.views.values(): view.grid_forget()
if view_name in self.views:
self.views[view_name].grid(row=0, column=1, sticky="nsew", padx=30, pady=30)
self.views[view_name].grid(row=0, column=1, sticky="nsew", padx=40, pady=40)
if view_name == "quarantine": self.refresh_quarantine_list()
if view_name == "history": self.refresh_history_list()
@@ -131,7 +150,8 @@ class Antivirus7LnA(ctk.CTk):
console.tag_config("danger", foreground="#EF4444")
console.tag_config("success", foreground="#10B981")
console.tag_config("warning", foreground="#F59E0B")
console.tag_config("info", foreground="#3B82F6")
console.tag_config("info", foreground=ACCENT_BLUE)
console.tag_config("violet", foreground=ACCENT_VIOLET)
def get_time_prefix(self):
return datetime.datetime.now().strftime("[%H:%M:%S] ")
@@ -143,7 +163,9 @@ class Antivirus7LnA(ctk.CTk):
with open(HISTORY_FILE, "w") as f: json.dump(data, f, indent=4)
except: pass
# --- AUTO-DETECT USB ---
# ==========================================
# LOGIQUE USB & MOTEUR
# ==========================================
def monitor_usb_drives(self):
media_dir = f"/media/{os.getlogin()}"
if not os.path.exists(media_dir): return
@@ -158,146 +180,122 @@ class Antivirus7LnA(ctk.CTk):
known_mounts = current_mounts
except: pass
# <--- MODIFICATION : Logique Zero USB intégrée
def prompt_usb_scan(self, path, name):
if self.zero_usb_mode:
send_desktop_notification("⚡ Zero USB Actif", f"Analyse automatique forcée pour : {name}", is_critical=True)
send_desktop_notification("⚡ Zero USB Actif", f"Analyse auto forcée : {name}", is_critical=True)
self.select_view("scanner")
self.scan_console.insert("end", f"\n{self.get_time_prefix()}[⚡ ZERO USB] Scan automatique déclenché.\n", "violet")
threading.Thread(target=self.run_clamav_scan, args=(path, True, self.scan_console), daemon=True).start()
else:
send_desktop_notification("USB Détectée", f"Disque {name} branché.")
if messagebox.askyesno("Protection USB", f"Nouveau périphérique USB détecté :\n{name}\n\nVoulez-vous l'analyser ?"):
if messagebox.askyesno("Protection USB", f"Nouveau périphérique détecté :\n{name}\n\nVoulez-vous l'analyser ?"):
self.select_view("scanner")
threading.Thread(target=self.run_clamav_scan, args=(path, True, self.scan_console), daemon=True).start()
# --- VUE : TABLEAU DE BORD ---
def init_dashboard_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent")
self.views["dashboard"] = frame
ctk.CTkLabel(frame, text="État du Système", font=ctk.CTkFont(size=34, weight="bold")).pack(anchor="w", pady=(0, 20))
status_card = ctk.CTkFrame(frame, fg_color="#064E3B" if self.clamav_installed else "#7F1D1D", corner_radius=15)
status_card.pack(fill="x", pady=10, ipady=20)
status_text = "Moteur ClamAV Opérationnel" if self.clamav_installed else "Moteur ClamAV Introuvable"
ctk.CTkLabel(status_card, text=f"{'' if self.clamav_installed else ''} {status_text}", font=ctk.CTkFont(size=24, weight="bold"), text_color="white").pack(expand=True)
sys_frame = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10)
sys_frame.pack(fill="x", pady=10, ipady=10)
sys_info = f"🖥️ OS : {platform.system()} {platform.release()} | 👤 Compte : {os.getlogin()}"
ctk.CTkLabel(sys_frame, text=sys_info, font=ctk.CTkFont(size=16, weight="bold")).pack(padx=20, pady=10, anchor="w")
# <--- AJOUT : Switch pour activer/désactiver le Mode Zero USB
self.zero_usb_switch = ctk.CTkSwitch(frame, text="🛡️ Mode Zero USB (Scan auto des clés USB)",
command=self.toggle_zero_usb,
font=ctk.CTkFont(size=16, weight="bold"),
progress_color="#DC2626") # Rouge pour indiquer un mode "agressif"
self.zero_usb_switch.pack(pady=20, anchor="w", padx=20)
# <--- AJOUT : Fonction de bascule pour le Switch
def toggle_zero_usb(self):
self.zero_usb_mode = self.zero_usb_switch.get()
etat = "ACTIVÉ" if self.zero_usb_mode else "DÉSACTIVÉ"
send_desktop_notification("Paramètre de Sécurité", f"Mode Zero USB {etat}")
# --- FONCTION D'AUTHENTIFICATION GLOBALE ---
def get_sudo_password(self, callback_func, title="Sécurité Administrateur", msg="Privilèges requis pour cette action.\nEntrez votre mot de passe session :"):
def get_sudo_password(self, callback_func, title="Sécurité Administrateur", msg="Privilèges requis.\nEntrez votre mot de passe :"):
dialog = ctk.CTkToplevel(self)
dialog.title(title)
dialog.geometry("400x230")
dialog.geometry("400x250")
dialog.configure(fg_color=BG_CARD)
ctk.CTkLabel(dialog, text="⚠️ Privilèges Requis", font=ctk.CTkFont(size=20, weight="bold"), text_color="#F59E0B").pack(pady=(20, 5))
ctk.CTkLabel(dialog, text=msg, justify="center").pack(pady=5)
ctk.CTkLabel(dialog, text="⚠️ Élévation de Privilèges", font=ctk.CTkFont(size=20, weight="bold"), text_color="#F59E0B").pack(pady=(20, 10))
ctk.CTkLabel(dialog, text=msg, justify="center", text_color=TEXT_MUTED).pack(pady=5)
pwd_entry = ctk.CTkEntry(dialog, show="*", width=250)
pwd_entry.pack(pady=10)
pwd_entry = ctk.CTkEntry(dialog, show="*", width=250, height=40, border_color=ACCENT_BLUE)
pwd_entry.pack(pady=15)
def on_submit(event=None):
pwd = pwd_entry.get()
dialog.destroy()
if pwd:
callback_func(pwd)
if pwd: callback_func(pwd)
ctk.CTkButton(dialog, text="Confirmer", command=on_submit, fg_color="#DC2626", hover_color="#991B1B").pack(pady=10)
ctk.CTkButton(dialog, text="Autoriser", command=on_submit, fg_color=ACCENT_BLUE, hover_color="#2563EB", height=40).pack(pady=5)
dialog.bind("<Return>", on_submit)
dialog.wait_visibility()
dialog.attributes("-topmost", True)
dialog.grab_set()
pwd_entry.focus_set()
# --- VUE : SCANNER MANUEL ---
# ==========================================
# VUES (DASHBOARD, SCANNER, ETC.)
# ==========================================
def init_dashboard_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent")
frame.grid_columnconfigure((0, 1), weight=1)
self.views["dashboard"] = frame
ctk.CTkLabel(frame, text="Vue d'ensemble", font=ctk.CTkFont(size=38, weight="bold"), text_color=TEXT_MAIN).grid(row=0, column=0, columnspan=2, sticky="w", pady=(0, 30))
# Carte Statut
status_color = "#064E3B" if self.clamav_installed else "#7F1D1D"
status_card = ctk.CTkFrame(frame, fg_color=status_color, corner_radius=15)
status_card.grid(row=1, column=0, sticky="nsew", padx=(0, 10), pady=10, ipady=30)
ctk.CTkLabel(status_card, text="Moteur Antivirus", font=ctk.CTkFont(size=16), text_color=TEXT_MUTED).pack(pady=(20, 5))
status_text = "OPÉRATIONNEL" if self.clamav_installed else "HORS LIGNE"
ctk.CTkLabel(status_card, text=f"{'' if self.clamav_installed else ''} {status_text}", font=ctk.CTkFont(size=28, weight="bold"), text_color="white").pack()
# Carte Système
sys_card = ctk.CTkFrame(frame, fg_color=BG_CARD, corner_radius=15)
sys_card.grid(row=1, column=1, sticky="nsew", padx=(10, 0), pady=10, ipady=30)
ctk.CTkLabel(sys_card, text="Environnement", font=ctk.CTkFont(size=16), text_color=TEXT_MUTED).pack(pady=(20, 5))
sys_info = f"{platform.system()} {platform.release()}"
ctk.CTkLabel(sys_card, text=sys_info, font=ctk.CTkFont(size=22, weight="bold"), text_color=ACCENT_BLUE).pack()
ctk.CTkLabel(sys_card, text=f"Session : {os.getlogin()}", font=ctk.CTkFont(size=14), text_color=TEXT_MUTED).pack(pady=5)
# Carte Zero USB
usb_card = ctk.CTkFrame(frame, fg_color=BG_CARD, corner_radius=15, border_width=2, border_color=ACCENT_VIOLET)
usb_card.grid(row=2, column=0, columnspan=2, sticky="nsew", pady=20, ipady=20)
ctk.CTkLabel(usb_card, text="Protection des Périphériques", font=ctk.CTkFont(size=20, weight="bold")).pack(pady=(20,10))
self.zero_usb_switch = ctk.CTkSwitch(usb_card, text="Activer le Mode Zero USB (Scan Auto)",
command=self.toggle_zero_usb, font=ctk.CTkFont(size=16, weight="bold"),
progress_color=ACCENT_VIOLET, button_color="#FFFFFF")
self.zero_usb_switch.pack(pady=10)
def toggle_zero_usb(self):
self.zero_usb_mode = self.zero_usb_switch.get()
etat = "ARMÉ" if self.zero_usb_mode else "DÉSACTIVÉ"
send_desktop_notification("Bouclier Quantum", f"Mode Zero USB {etat}")
def init_scanner_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent")
frame.grid_rowconfigure(4, weight=1)
frame.grid_rowconfigure(3, weight=1)
frame.grid_columnconfigure((0, 1, 2), weight=1)
self.views["scanner"] = frame
ctk.CTkLabel(frame, text="Analyse Profonde", font=ctk.CTkFont(size=34, weight="bold")).grid(row=0, column=0, columnspan=3, sticky="w", pady=(0, 20))
ctk.CTkLabel(frame, text="Analyse Système", font=ctk.CTkFont(size=38, weight="bold")).grid(row=0, column=0, columnspan=3, sticky="w", pady=(0, 30))
self.btn_scan_f = ctk.CTkButton(frame, text="📄 Analyser Fichier", command=lambda: self.start_manual_scan(is_dir=False), height=45, fg_color="#2563EB", hover_color="#1D4ED8")
self.btn_scan_f = ctk.CTkButton(frame, text="📄 Fichier", command=lambda: self.start_manual_scan(is_dir=False), height=50, fg_color=BG_CARD, hover_color="#374151", border_width=1, border_color=ACCENT_BLUE)
self.btn_scan_f.grid(row=1, column=0, padx=(0, 5), pady=5, sticky="ew")
self.btn_scan_d = ctk.CTkButton(frame, text="📁 Analyser Dossier", command=lambda: self.start_manual_scan(is_dir=True), height=45, fg_color="#4F46E5", hover_color="#4338CA")
self.btn_scan_d = ctk.CTkButton(frame, text="📁 Dossier", command=lambda: self.start_manual_scan(is_dir=True), height=50, fg_color=BG_CARD, hover_color="#374151", border_width=1, border_color=ACCENT_BLUE)
self.btn_scan_d.grid(row=1, column=1, padx=(5, 5), pady=5, sticky="ew")
self.btn_db_update = ctk.CTkButton(frame, text="🔄 MaJ Signatures", command=self.update_virus_db_prompt, height=45, fg_color="#059669", hover_color="#047857")
self.btn_db_update = ctk.CTkButton(frame, text="🔄 MaJ Signatures", command=self.update_virus_db_prompt, height=50, fg_color="#059669", hover_color="#047857")
self.btn_db_update.grid(row=1, column=2, padx=(5, 0), pady=5, sticky="ew")
self.btn_rootkit = ctk.CTkButton(frame, text="🕵️ Chasse aux Rootkits (rkhunter)", command=self.run_rootkit_scan, height=45, fg_color="#7C3AED", hover_color="#6D28D9")
self.btn_rootkit.grid(row=2, column=0, columnspan=3, pady=(5, 10), sticky="ew")
self.btn_rootkit = ctk.CTkButton(frame, text="🕵️ Hunt Rootkits (rkhunter)", command=self.run_rootkit_scan, height=50, fg_color=ACCENT_VIOLET, hover_color="#7C3AED")
self.btn_rootkit.grid(row=2, column=0, columnspan=3, pady=(15, 20), sticky="ew")
self.scan_progress = ctk.CTkProgressBar(frame, mode="indeterminate", height=10)
self.scan_progress.grid(row=3, column=0, columnspan=3, pady=(10, 0), sticky="ew")
self.scan_progress = ctk.CTkProgressBar(frame, mode="indeterminate", height=4, progress_color=ACCENT_BLUE)
self.scan_progress.grid(row=3, column=0, columnspan=3, sticky="ew")
self.scan_progress.set(0)
self.scan_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=13), fg_color="#111827", corner_radius=10)
self.scan_console.grid(row=4, column=0, columnspan=3, pady=20, sticky="nsew")
self.scan_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=14), fg_color=BG_CARD, corner_radius=15, border_width=1, border_color="#374151")
self.scan_console.grid(row=4, column=0, columnspan=3, pady=10, sticky="nsew")
self.setup_console_tags(self.scan_console)
self.scan_console.insert("end", f"{self.get_time_prefix()}[*] Moteur de détection V10.1 prêt...\n", "info")
self.scan_console.insert("end", f"{self.get_time_prefix()}[*] Moteur d'analyse prêt.\n", "info")
# (Toutes tes méthodes internes restent identiques ici pour garantir le fonctionnement)
def run_rootkit_scan(self):
self.get_sudo_password(lambda pwd: threading.Thread(target=self._exec_rootkit, args=(pwd,), daemon=True).start())
def _exec_rootkit(self, pwd):
self.scan_console.insert("end", f"\n{self.get_time_prefix()}[*] Lancement de rkhunter (cela peut prendre du temps)...\n", "info")
self.scan_console.insert("end", f"\n{self.get_time_prefix()}[*] Lancement de rkhunter (cela peut prendre du temps)...\n", "violet")
self.scan_progress.start()
try:
cmd = ['sudo', '-S', 'rkhunter', '-c', '--sk', '--report-warnings-only']
process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1)
process.stdin.write(pwd + '\n')
process.stdin.flush()
for line in iter(process.stdout.readline, ''):
if "incorrect password" in line.lower() or "try again" in line.lower():
self.scan_console.insert("end", f"{self.get_time_prefix()}[-] Mot de passe refusé par le système.\n", "danger")
process.terminate()
break
else:
self.scan_console.insert("end", line)
self.scan_console.see("end")
process.wait()
if process.returncode in [0, 1]:
self.scan_console.insert("end", f"{self.get_time_prefix()}[+] Analyse Rootkit terminée.\n", "success")
except FileNotFoundError:
self.scan_console.insert("end", f"{self.get_time_prefix()}[-] rkhunter n'est pas installé.\n", "warning")
except Exception as e:
self.scan_console.insert("end", f"{self.get_time_prefix()}❌ Erreur : {e}\n", "danger")
finally:
self.scan_progress.stop()
self.scan_console.see("end")
def update_virus_db_prompt(self):
self.get_sudo_password(lambda pwd: threading.Thread(target=self._run_freshclam, args=(pwd,), daemon=True).start(), msg="La mise à jour des signatures ClamAV nécessite\nles droits sudo. Entrez votre mot de passe :")
def _run_freshclam(self, pwd):
self.scan_console.insert("end", f"\n{self.get_time_prefix()}[*] Lancement de la mise à jour (freshclam)...\n", "info")
self.scan_progress.start()
self.btn_db_update.configure(state="disabled")
try:
cmd = ['sudo', '-S', 'freshclam']
process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1)
process.stdin.write(pwd + '\n')
process.stdin.flush()
for line in iter(process.stdout.readline, ''):
@@ -309,56 +307,39 @@ class Antivirus7LnA(ctk.CTk):
self.scan_console.insert("end", line)
self.scan_console.see("end")
process.wait()
if process.returncode == 0:
self.scan_console.insert("end", f"{self.get_time_prefix()}[+] Signatures virales mises à jour avec succès.\n", "success")
if process.returncode in [0, 1]: self.scan_console.insert("end", f"{self.get_time_prefix()}[+] Analyse Rootkit terminée.\n", "success")
except Exception as e:
self.scan_console.insert("end", f"{self.get_time_prefix()}❌ Erreur freshclam : {e}\n", "danger")
self.scan_console.insert("end", f"{self.get_time_prefix()}❌ Erreur : {e}\n", "danger")
finally:
self.scan_progress.stop()
self.btn_db_update.configure(state="normal")
self.scan_console.see("end")
def update_virus_db_prompt(self):
self.get_sudo_password(lambda pwd: threading.Thread(target=self._run_freshclam, args=(pwd,), daemon=True).start(), msg="La mise à jour nécessite les droits sudo :")
def _run_freshclam(self, pwd):
self.scan_console.insert("end", f"\n{self.get_time_prefix()}[*] Lancement de la mise à jour (freshclam)...\n", "info")
self.scan_progress.start()
try:
cmd = ['sudo', '-S', 'freshclam']
process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1)
process.stdin.write(pwd + '\n')
process.stdin.flush()
for line in iter(process.stdout.readline, ''):
self.scan_console.insert("end", line)
self.scan_console.see("end")
process.wait()
if process.returncode == 0: self.scan_console.insert("end", f"{self.get_time_prefix()}[+] Signatures mises à jour.\n", "success")
except Exception as e: self.scan_console.insert("end", f"{self.get_time_prefix()}❌ Erreur freshclam : {e}\n", "danger")
finally: self.scan_progress.stop()
def start_manual_scan(self, is_dir):
path = filedialog.askdirectory() if is_dir else filedialog.askopenfilename()
if path: threading.Thread(target=self.run_clamav_scan, args=(path, is_dir, self.scan_console), daemon=True).start()
# --- VUE : BOUCLIER TEMPS RÉEL ---
def init_realtime_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent")
frame.grid_rowconfigure(2, weight=1)
frame.grid_columnconfigure(0, weight=1)
self.views["shield"] = frame
header = ctk.CTkFrame(frame, fg_color="transparent")
header.grid(row=0, column=0, sticky="ew", pady=(0, 20))
ctk.CTkLabel(header, text="⚡ Bouclier Actif", font=ctk.CTkFont(size=34, weight="bold")).pack(side="left")
self.btn_toggle_shield = ctk.CTkButton(header, text="Démarrer la Surveillance", fg_color="#059669", hover_color="#047857", command=self.toggle_shield, height=45)
self.btn_toggle_shield.pack(side="right")
self.rt_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=13), fg_color="#111827", corner_radius=10)
self.rt_console.grid(row=2, column=0, sticky="nsew")
self.setup_console_tags(self.rt_console)
def toggle_shield(self):
if not self.shield_active:
self.shield_observer = Observer()
self.shield_observer.schedule(RealTimeShieldHandler(self), WATCH_FOLDER, recursive=False)
self.shield_observer.start()
self.shield_active = True
self.btn_toggle_shield.configure(text="Arrêter le Bouclier", fg_color="#DC2626", hover_color="#B91C1C")
self.rt_console.insert("end", f"\n{self.get_time_prefix()}[+] BOUCLIER ARMÉ : {WATCH_FOLDER}\n", "success")
else:
self.shield_observer.stop()
self.shield_active = False
self.btn_toggle_shield.configure(text="Démarrer la Surveillance", fg_color="#059669")
self.rt_console.insert("end", f"\n{self.get_time_prefix()}[-] Bouclier désactivé.\n", "warning")
def trigger_realtime_scan(self, filepath):
self.rt_console.insert("end", f"\n{self.get_time_prefix()}⚡ Nouveau fichier : {os.path.basename(filepath)}\n", "info")
threading.Thread(target=self.run_clamav_scan, args=(filepath, False, self.rt_console), daemon=True).start()
# --- MOTEUR CLAMAV ---
def run_clamav_scan(self, path, is_dir, console):
if not self.clamav_installed: return
console.insert("end", f"{self.get_time_prefix()}[*] Analyse : {path}\n")
console.insert("end", f"\n{self.get_time_prefix()}[*] Analyse de cible : {path}\n")
if console == self.scan_console: self.scan_progress.start()
try:
cmd = ['clamscan', '-i', '--no-summary', f'--move={QUARANTINE_DIR}']
@@ -376,37 +357,69 @@ class Antivirus7LnA(ctk.CTk):
elif clean_line: console.insert("end", f" -> {clean_line}\n")
console.see("end")
process.wait()
if infected == 0: console.insert("end", f"{self.get_time_prefix()}[+] Fichier propre.\n", "success")
if infected == 0: console.insert("end", f"{self.get_time_prefix()}[+] Cible propre.\n", "success")
else: send_desktop_notification("🚨 VIRUS NEUTRALISÉ", f"{infected} menace(s) trouvée(s).", is_critical=True)
except Exception as e: console.insert("end", f"{self.get_time_prefix()}❌ Erreur : {e}\n", "danger")
finally:
if console == self.scan_console: self.scan_progress.stop()
console.see("end")
# --- VUE : AUDIT & PARE-FEU (AVEC WIFI GUARD V10.1) ---
# (J'ai appliqué le design "Carte" à toutes les autres vues pour un rendu homogène)
def init_realtime_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent")
frame.grid_rowconfigure(2, weight=1)
frame.grid_columnconfigure(0, weight=1)
self.views["shield"] = frame
header = ctk.CTkFrame(frame, fg_color="transparent")
header.grid(row=0, column=0, sticky="ew", pady=(0, 30))
ctk.CTkLabel(header, text="Bouclier Temps Réel", font=ctk.CTkFont(size=38, weight="bold")).pack(side="left")
self.btn_toggle_shield = ctk.CTkButton(header, text="Activer la Surveillance", fg_color="#059669", hover_color="#047857", command=self.toggle_shield, height=45)
self.btn_toggle_shield.pack(side="right")
self.rt_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=14), fg_color=BG_CARD, corner_radius=15, border_width=1, border_color="#374151")
self.rt_console.grid(row=2, column=0, sticky="nsew")
self.setup_console_tags(self.rt_console)
def toggle_shield(self):
if not self.shield_active:
self.shield_observer = Observer()
self.shield_observer.schedule(RealTimeShieldHandler(self), WATCH_FOLDER, recursive=False)
self.shield_observer.start()
self.shield_active = True
self.btn_toggle_shield.configure(text="Désactiver", fg_color="#DC2626", hover_color="#B91C1C")
self.rt_console.insert("end", f"\n{self.get_time_prefix()}[+] BOUCLIER ARMÉ : {WATCH_FOLDER}\n", "success")
else:
self.shield_observer.stop()
self.shield_active = False
self.btn_toggle_shield.configure(text="Activer la Surveillance", fg_color="#059669")
self.rt_console.insert("end", f"\n{self.get_time_prefix()}[-] Bouclier désactivé.\n", "warning")
def trigger_realtime_scan(self, filepath):
self.rt_console.insert("end", f"\n{self.get_time_prefix()}⚡ Nouveau fichier : {os.path.basename(filepath)}\n", "info")
threading.Thread(target=self.run_clamav_scan, args=(filepath, False, self.rt_console), daemon=True).start()
def init_audit_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent")
frame.grid_rowconfigure(2, weight=1)
frame.grid_columnconfigure(0, weight=1)
self.views["audit"] = frame
ctk.CTkLabel(frame, text="Audit Réseau & Pare-feu", font=ctk.CTkFont(size=34, weight="bold")).grid(row=0, column=0, sticky="w", pady=(0, 20))
ctk.CTkLabel(frame, text="Réseau & Pare-feu", font=ctk.CTkFont(size=38, weight="bold")).grid(row=0, column=0, sticky="w", pady=(0, 30))
btn_frame = ctk.CTkFrame(frame, fg_color="transparent")
btn_frame.grid(row=1, column=0, sticky="ew", pady=(0, 10))
btn_frame.grid(row=1, column=0, sticky="ew", pady=(0, 20))
ctk.CTkButton(btn_frame, text="⚙️ Lancer l'Audit Processus", command=self.run_audit_thread, fg_color="#D97706", hover_color="#B45309", height=40).pack(side="left", padx=(0, 10))
ctk.CTkButton(btn_frame, text="🛡️ Vérifier UFW", command=self.check_firewall, fg_color="#4B5563", hover_color="#374151", height=40).pack(side="left", padx=(0, 10))
ctk.CTkButton(btn_frame, text="📡 WiFi Guard (Scanner Intrus)", command=self.run_wifi_guard_prompt, fg_color="#059669", hover_color="#047857", height=40).pack(side="left")
ctk.CTkButton(btn_frame, text="⚙️ Audit Processus", command=lambda: threading.Thread(target=self.perform_audit, daemon=True).start(), height=45, fg_color=BG_CARD, border_width=1).pack(side="left", padx=(0, 10))
ctk.CTkButton(btn_frame, text="🛡️ Statut UFW", command=self.check_firewall, height=45, fg_color=BG_CARD, border_width=1).pack(side="left", padx=(0, 10))
ctk.CTkButton(btn_frame, text="📡 WiFi Guard", command=self.run_wifi_guard_prompt, height=45, fg_color=ACCENT_BLUE, hover_color="#2563EB").pack(side="left")
self.audit_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=13), fg_color="#111827", corner_radius=10)
self.audit_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=14), fg_color=BG_CARD, corner_radius=15, border_width=1, border_color="#374151")
self.audit_console.grid(row=2, column=0, sticky="nsew")
def run_audit_thread(self):
self.audit_console.delete("0.0", "end")
threading.Thread(target=self.perform_audit, daemon=True).start()
self.setup_console_tags(self.audit_console)
def perform_audit(self):
self.audit_console.delete("0.0", "end")
try: self.audit_console.insert("end", subprocess.check_output(['ss', '-tuln'], text=True))
except: pass
try:
@@ -418,263 +431,232 @@ class Antivirus7LnA(ctk.CTk):
self.audit_console.delete("0.0", "end")
try:
res = subprocess.check_output(['systemctl', 'is-active', 'ufw'], text=True).strip()
if res == "active": self.audit_console.insert("end", f"{self.get_time_prefix()}✅ PARE-FEU UFW ACTIF.\n")
else: self.audit_console.insert("end", f"{self.get_time_prefix()}⚠️ PARE-FEU INACTIF.\n")
if res == "active": self.audit_console.insert("end", f"{self.get_time_prefix()}✅ PARE-FEU UFW ACTIF.\n", "success")
else: self.audit_console.insert("end", f"{self.get_time_prefix()}⚠️ PARE-FEU INACTIF.\n", "warning")
except: pass
# ----- LOGIQUE WIFI GUARD -----
def run_wifi_guard_prompt(self):
self.get_sudo_password(lambda pwd: threading.Thread(target=self._exec_wifi_guard, args=(pwd,), daemon=True).start(), title="WiFi Guard", msg="Le scan réseau profond nécessite les droits sudo.\nEntrez votre mot de passe session :")
self.get_sudo_password(lambda pwd: threading.Thread(target=self._exec_wifi_guard, args=(pwd,), daemon=True).start(), title="WiFi Guard", msg="Scan réseau (sudo) :")
def _exec_wifi_guard(self, pwd):
self.audit_console.delete("0.0", "end")
self.audit_console.insert("end", f"{self.get_time_prefix()}[*] Démarrage du scan réseau (arp-scan)... \n", "info")
self.audit_console.insert("end", f"{self.get_time_prefix()}[*] Démarrage arp-scan... \n", "info")
if not shutil.which("arp-scan"):
self.audit_console.insert("end", f"{self.get_time_prefix()}[!] Erreur : arp-scan introuvable.\n", "danger")
self.audit_console.insert("end", f"{self.get_time_prefix()}[!] arp-scan introuvable.\n", "danger")
return
try:
cmd = ['sudo', '-S', 'arp-scan', '--localnet', '--ignoredups']
process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1)
process.stdin.write(pwd + '\n')
process.stdin.flush()
count = 0
for line in iter(process.stdout.readline, ''):
if any(x in line.lower() for x in ["permission denied", "starting arp-scan", "packets received", "ending arp-scan", "[sudo]"]):
continue
if any(x in line.lower() for x in ["permission denied", "starting arp-scan", "packets received", "ending arp-scan", "[sudo]"]): continue
if "incorrect password" in line.lower() or "try again" in line.lower():
self.audit_console.insert("end", f"{self.get_time_prefix()}[-] Mot de passe refusé.\n", "danger")
break
if line.strip() and (line[0].isdigit()):
self.audit_console.insert("end", f" 📱 Appareil trouvé : {line.strip()}\n", "success")
self.audit_console.insert("end", f" 📱 Appareil : {line.strip()}\n", "success")
count += 1
self.audit_console.see("end")
process.wait()
self.audit_console.insert("end", f"\n{self.get_time_prefix()}[+] Scan terminé. {count} appareils détectés sur votre WiFi.\n", "info")
self.audit_console.insert("end", f"\n{self.get_time_prefix()}[+] Scan terminé. {count} appareils détectés.\n", "info")
except Exception as e: self.audit_console.insert("end", f"❌ Erreur : {e}\n", "danger")
if count > 15:
send_desktop_notification("WiFi Guard", f"Attention : Beaucoup d'appareils ({count}) sont connectés à votre réseau.", is_critical=True)
except Exception as e:
self.audit_console.insert("end", f"❌ Erreur : {e}\n", "danger")
# --- VUE : OUTILS AVANCÉS (V10) ---
def init_tools_view(self):
frame = ctk.CTkScrollableFrame(self, fg_color="transparent")
self.views["tools"] = frame
ctk.CTkLabel(frame, text="Boîte à Outils", font=ctk.CTkFont(size=34, weight="bold")).pack(anchor="w", pady=(0, 20))
ctk.CTkLabel(frame, text="Outils Avancés", font=ctk.CTkFont(size=38, weight="bold")).pack(anchor="w", pady=(0, 30))
# Vérificateur de Fuites de Données (OSINT)
breach_card = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10)
# OSINT Card
breach_card = ctk.CTkFrame(frame, fg_color=BG_CARD, corner_radius=15)
breach_card.pack(fill="x", pady=10, ipady=15)
ctk.CTkLabel(breach_card, text="🌐 Vérificateur de Fuites (OSINT)", font=ctk.CTkFont(size=18, weight="bold"), text_color="#A855F7").pack(anchor="w", padx=20, pady=(10,0))
ctk.CTkLabel(breach_card, text="Vérifiez si votre adresse e-mail a été compromise dans une fuite de données (Dark Web).", text_color="#9CA3AF").pack(anchor="w", padx=20, pady=(5, 10))
ctk.CTkLabel(breach_card, text="🌐 OSINT : Vérificateur de Fuites", font=ctk.CTkFont(size=20, weight="bold"), text_color=ACCENT_VIOLET).pack(anchor="w", padx=20, pady=(10,5))
ctk.CTkLabel(breach_card, text="Le Dark Web contient-il votre adresse e-mail ?", text_color=TEXT_MUTED).pack(anchor="w", padx=20, pady=(0, 15))
input_frame = ctk.CTkFrame(breach_card, fg_color="transparent")
input_frame.pack(fill="x", padx=20, pady=5)
self.email_entry = ctk.CTkEntry(input_frame, placeholder_text="Entrez votre e-mail...", width=300)
self.email_entry.pack(side="left", padx=(0, 10))
ctk.CTkButton(input_frame, text="Vérifier l'e-mail", fg_color="#9333EA", hover_color="#7E22CE", command=self.run_breach_check).pack(side="left")
self.email_entry = ctk.CTkEntry(input_frame, placeholder_text="exemple@domaine.com", width=350, height=45)
self.email_entry.pack(side="left", padx=(0, 15))
ctk.CTkButton(input_frame, text="Rechercher", fg_color=ACCENT_VIOLET, hover_color="#7C3AED", height=45, command=self.run_breach_check).pack(side="left")
self.breach_result_var = ctk.StringVar(value="")
self.breach_result_label = ctk.CTkLabel(breach_card, textvariable=self.breach_result_var, text_color="#D1D5DB", justify="left")
self.breach_result_label.pack(anchor="w", padx=20, pady=10)
self.breach_result_label = ctk.CTkLabel(breach_card, textvariable=self.breach_result_var, font=ctk.CTkFont(size=14), justify="left")
self.breach_result_label.pack(anchor="w", padx=20, pady=15)
# Nettoyeur
clean_card = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10)
clean_card = ctk.CTkFrame(frame, fg_color=BG_CARD, corner_radius=15)
clean_card.pack(fill="x", pady=10, ipady=15)
ctk.CTkLabel(clean_card, text="🧹 Nettoyeur Système", font=ctk.CTkFont(size=18, weight="bold"), text_color="#10B981").pack(anchor="w", padx=20, pady=(10,0))
ctk.CTkButton(clean_card, text="Vider le Cache et Corbeille", fg_color="#059669", hover_color="#047857", command=self.run_cleaner).pack(anchor="w", padx=20, pady=10)
ctk.CTkLabel(clean_card, text="🧹 Optimisation", font=ctk.CTkFont(size=20, weight="bold"), text_color="#10B981").pack(anchor="w", padx=20, pady=(10,15))
ctk.CTkButton(clean_card, text="Purger le Cache Système", fg_color="#059669", hover_color="#047857", height=45, command=self.run_cleaner).pack(anchor="w", padx=20)
# Destructeur
shred_card = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10)
shred_card = ctk.CTkFrame(frame, fg_color=BG_CARD, corner_radius=15)
shred_card.pack(fill="x", pady=10, ipady=15)
ctk.CTkLabel(shred_card, text="🔥 Destructeur de Fichiers", font=ctk.CTkFont(size=18, weight="bold"), text_color="#EF4444").pack(anchor="w", padx=20, pady=(10,0))
ctk.CTkButton(shred_card, text="Détruire un fichier", fg_color="#DC2626", hover_color="#991B1B", command=self.run_shredder).pack(anchor="w", padx=20, pady=10)
ctk.CTkLabel(shred_card, text="🔥 Suppression Militaire", font=ctk.CTkFont(size=20, weight="bold"), text_color="#EF4444").pack(anchor="w", padx=20, pady=(10,15))
ctk.CTkButton(shred_card, text="Détruire un fichier définitivement", fg_color="#DC2626", hover_color="#991B1B", height=45, command=self.run_shredder).pack(anchor="w", padx=20)
# --- LOGIQUE OSINT ---
def run_breach_check(self):
email = self.email_entry.get().strip()
if not email or "@" not in email:
self.breach_result_var.set("Veuillez entrer une adresse e-mail valide.")
self.breach_result_var.set("Adresse e-mail invalide.")
self.breach_result_label.configure(text_color="#EF4444")
return
self.breach_result_var.set("⏳ Interrogation des bases de données...")
self.breach_result_label.configure(text_color="#3B82F6")
self.breach_result_var.set("⏳ Interrogation des serveurs mondiaux...")
self.breach_result_label.configure(text_color=ACCENT_BLUE)
threading.Thread(target=self._exec_breach_check, args=(email,), daemon=True).start()
def _exec_breach_check(self, email):
try:
url = f"https://api.xposedornot.com/v1/check-email/{email}"
req = urllib.request.Request(url, headers={'User-Agent': '7LnA-Security-Suite'})
req = urllib.request.Request(f"https://api.xposedornot.com/v1/check-email/{email}", headers={'User-Agent': '7lna-Antivirus'})
try:
with urllib.request.urlopen(req) as response:
data = json.loads(response.read().decode())
if "breaches" in data and data["breaches"]:
breaches = data["breaches"][0]
breach_count = len(breaches)
breach_names = ", ".join(breaches[:4])
suffix = "..." if breach_count > 4 else ""
self.breach_result_var.set(f"🚨 DANGER : E-mail trouvé dans {breach_count} fuite(s) !\nSources connues : {breach_names}{suffix}\nChangez vos mots de passe immédiatement.")
b_str = ", ".join(breaches[:4]) + ("..." if len(breaches) > 4 else "")
self.breach_result_var.set(f"🚨 DANGER : {len(breaches)} fuites trouvées !\nSources : {b_str}")
self.breach_result_label.configure(text_color="#EF4444")
else:
self.breach_result_var.set("Bonne nouvelle : Aucune fuite détectée pour cet e-mail.")
self.breach_result_var.set("Identité sécurisée. Aucune fuite détectée.")
self.breach_result_label.configure(text_color="#10B981")
except urllib.error.HTTPError as e:
if e.code == 404:
self.breach_result_var.set("Bonne nouvelle : Aucune fuite détectée pour cet e-mail.")
self.breach_result_var.set("Identité sécurisée. Aucune fuite détectée.")
self.breach_result_label.configure(text_color="#10B981")
else:
self.breach_result_var.set(f"❌ Erreur serveur lors de la vérification ({e.code}).")
self.breach_result_label.configure(text_color="#F59E0B")
except Exception as e:
self.breach_result_var.set("❌ Impossible de contacter le serveur d'OSINT. Vérifiez votre connexion.")
self.breach_result_label.configure(text_color="#F59E0B")
self.breach_result_var.set(f"❌ Erreur API ({e.code}).")
except: self.breach_result_var.set("❌ Impossible de joindre les serveurs.")
def run_cleaner(self):
paths_to_clean = [os.path.expanduser("~/.local/share/Trash/files"), os.path.expanduser("~/.cache/thumbnails")]
for path in paths_to_clean:
if os.path.exists(path):
for item in os.listdir(path):
item_path = os.path.join(path, item)
try:
if os.path.isfile(item_path): os.remove(item_path)
elif os.path.isdir(item_path): shutil.rmtree(item_path)
paths = [os.path.expanduser("~/.local/share/Trash/files"), os.path.expanduser("~/.cache/thumbnails")]
for p in paths:
if os.path.exists(p):
for i in os.listdir(p):
item = os.path.join(p, i)
try: shutil.rmtree(item) if os.path.isdir(item) else os.remove(item)
except: pass
messagebox.showinfo("Succès", "Nettoyage terminé.")
messagebox.showinfo("Optimisation", "Fichiers temporaires purgés.")
def run_shredder(self):
filepath = filedialog.askopenfilename()
if not filepath: return
if messagebox.askyesno("DANGER", f"Détruire DÉFINITIVEMENT ?\n\n{filepath}"):
if filepath and messagebox.askyesno("Destruction", f"Détruire DÉFINITIVEMENT ?\n\n{filepath}"):
try:
file_size = os.path.getsize(filepath)
size = os.path.getsize(filepath)
for _ in range(3):
with open(filepath, "ba+", buffering=0) as f:
f.seek(0)
f.write(os.urandom(file_size))
f.seek(0); f.write(os.urandom(size))
os.remove(filepath)
messagebox.showinfo("Succès", "Fichier détruit.")
except: pass
# ----- PLANIFICATION -----
def init_schedule_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent")
self.views["schedule"] = frame
ctk.CTkLabel(frame, text="Planification de Scans", font=ctk.CTkFont(size=34, weight="bold")).pack(anchor="w", pady=(0, 20))
ctk.CTkLabel(frame, text="Automatisez l'analyse via les tâches Cron.", text_color="#9CA3AF").pack(anchor="w", pady=(0, 20))
ctk.CTkLabel(frame, text="Automatisation Cron", font=ctk.CTkFont(size=38, weight="bold")).pack(anchor="w", pady=(0, 30))
card = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10)
card.pack(fill="x", pady=10, ipady=15)
ctk.CTkLabel(card, text="Programmer un scan automatique (Dossier Téléchargements)", font=ctk.CTkFont(size=16, weight="bold")).pack(pady=(10, 5))
card = ctk.CTkFrame(frame, fg_color=BG_CARD, corner_radius=15)
card.pack(fill="x", ipady=20)
ctk.CTkLabel(card, text="Scan Journalier (Dossier Téléchargements)", font=ctk.CTkFont(size=18, weight="bold")).pack(pady=(20, 15))
time_frame = ctk.CTkFrame(card, fg_color="transparent")
time_frame.pack(pady=(5, 15))
ctk.CTkLabel(time_frame, text="Heure d'exécution :").pack(side="left", padx=5)
time_frame.pack(pady=10)
self.cron_h_var = ctk.StringVar(value="12")
self.cron_m_var = ctk.StringVar(value="00")
ctk.CTkComboBox(time_frame, values=[f"{i:02d}" for i in range(24)], variable=self.cron_h_var, width=70).pack(side="left", padx=5)
ctk.CTkLabel(time_frame, text="h").pack(side="left")
ctk.CTkComboBox(time_frame, values=["00", "15", "30", "45"], variable=self.cron_m_var, width=70).pack(side="left", padx=5)
ctk.CTkComboBox(time_frame, values=[f"{i:02d}" for i in range(24)], variable=self.cron_h_var, width=80, height=40).pack(side="left", padx=5)
ctk.CTkLabel(time_frame, text=":").pack(side="left")
ctk.CTkComboBox(time_frame, values=["00", "15", "30", "45"], variable=self.cron_m_var, width=80, height=40).pack(side="left", padx=5)
ctk.CTkButton(card, text="Activer le Scan Quotidien", fg_color="#2563EB", hover_color="#1D4ED8", command=self.setup_cronjob).pack(pady=5)
ctk.CTkButton(card, text="Désactiver la planification", fg_color="#4B5563", hover_color="#374151", command=self.remove_cronjob).pack(pady=5)
ctk.CTkButton(card, text="Activer la planification", fg_color=ACCENT_BLUE, hover_color="#2563EB", height=45, command=self.setup_cronjob).pack(pady=(20,10))
ctk.CTkButton(card, text="Désactiver", fg_color="transparent", border_width=1, border_color="#EF4444", text_color="#EF4444", hover_color="#7F1D1D", height=45, command=self.remove_cronjob).pack()
def setup_cronjob(self):
h = self.cron_h_var.get()
m = self.cron_m_var.get()
cron_command = f"{int(m)} {int(h)} * * * /usr/bin/clamscan -r --move={QUARANTINE_DIR} {WATCH_FOLDER} > /dev/null 2>&1"
cmd = f"{int(self.cron_m_var.get())} {int(self.cron_h_var.get())} * * * /usr/bin/clamscan -r --move={QUARANTINE_DIR} {WATCH_FOLDER} > /dev/null 2>&1"
try:
os.system(f'(crontab -l 2>/dev/null | grep -v "clamscan -r --move={QUARANTINE_DIR}"; echo "{cron_command}") | crontab -')
messagebox.showinfo("Succès", f"Scan configuré tous les jours à {h}:{m}.")
except: messagebox.showerror("Erreur", "Impossible de configurer Cron.")
def remove_cronjob(self):
try:
os.system(f'(crontab -l 2>/dev/null | grep -v "clamscan -r --move={QUARANTINE_DIR}") | crontab -')
messagebox.showinfo("Succès", "Planification désactivée.")
os.system(f'(crontab -l 2>/dev/null | grep -v "clamscan -r --move={QUARANTINE_DIR}"; echo "{cmd}") | crontab -')
messagebox.showinfo("Cron", "Scan quotidien configuré.")
except: pass
def remove_cronjob(self):
try: os.system(f'(crontab -l 2>/dev/null | grep -v "clamscan -r --move={QUARANTINE_DIR}") | crontab -')
except: pass
# --- VUE : HISTORIQUE ---
def init_history_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent")
frame.grid_rowconfigure(1, weight=1)
frame.grid_columnconfigure(0, weight=1)
self.views["history"] = frame
ctk.CTkLabel(frame, text="📜 Registre des Menaces", font=ctk.CTkFont(size=34, weight="bold")).grid(row=0, column=0, sticky="w", pady=(0, 20))
self.history_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=13), fg_color="#111827", corner_radius=10)
ctk.CTkLabel(frame, text="Registre Sécurité", font=ctk.CTkFont(size=38, weight="bold")).grid(row=0, column=0, sticky="w", pady=(0, 30))
self.history_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=14), fg_color=BG_CARD, corner_radius=15, border_width=1, border_color="#374151")
self.history_console.grid(row=1, column=0, sticky="nsew")
def refresh_history_list(self):
self.history_console.delete("0.0", "end")
try:
with open(HISTORY_FILE, "r") as f: data = json.load(f)
if not data: self.history_console.insert("end", "\nHistorique vierge.\n")
if not data: self.history_console.insert("end", "\nAucun incident enregistré.\n")
else:
for entry in reversed(data):
self.history_console.insert("end", f"[{entry['date']}] ☠️ {entry['threat']}\n -> Fichier : {entry['file']}\n\n")
for e in reversed(data): self.history_console.insert("end", f"[{e['date']}] ☠️ {e['threat']}\n -> Fichier : {e['file']}\n\n")
except: pass
# --- VUE : QUARANTAINE ---
def init_quarantine_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent")
frame.grid_rowconfigure(1, weight=1)
frame.grid_columnconfigure(0, weight=1)
self.views["quarantine"] = frame
header = ctk.CTkFrame(frame, fg_color="transparent")
header.grid(row=0, column=0, sticky="ew", pady=(0, 20))
ctk.CTkLabel(header, text="📦 Quarantaine", font=ctk.CTkFont(size=34, weight="bold")).pack(side="left")
ctk.CTkButton(header, text="🗑️ Vider", fg_color="#DC2626", hover_color="#991B1B", command=self.empty_quarantine, height=40).pack(side="right")
self.quarantine_listbox = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=14), fg_color="#111827", corner_radius=10)
header.grid(row=0, column=0, sticky="ew", pady=(0, 30))
ctk.CTkLabel(header, text="Zone de Quarantaine", font=ctk.CTkFont(size=38, weight="bold")).pack(side="left")
ctk.CTkButton(header, text="Purger Tout", fg_color="#DC2626", hover_color="#991B1B", height=45, command=self.empty_quarantine).pack(side="right")
self.quarantine_listbox = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=14), fg_color=BG_CARD, corner_radius=15, border_width=1, border_color="#374151")
self.quarantine_listbox.grid(row=1, column=0, sticky="nsew")
def refresh_quarantine_list(self):
self.quarantine_listbox.delete("0.0", "end")
files = glob.glob(os.path.join(QUARANTINE_DIR, "*"))
if not files: self.quarantine_listbox.insert("end", "\nAucun fichier malveillant isolé.\n")
if not files: self.quarantine_listbox.insert("end", "\nZone propre.\n")
else:
for f in files: self.quarantine_listbox.insert("end", f" -> {os.path.basename(f)}\n")
for f in files: self.quarantine_listbox.insert("end", f" 🔒 {os.path.basename(f)}\n")
def empty_quarantine(self):
files = glob.glob(os.path.join(QUARANTINE_DIR, "*"))
if files and messagebox.askyesno("Purger", "Supprimer définitivement les virus isolés ?"):
if files and messagebox.askyesno("Purger", "Détruire définitivement les virus ?"):
for f in files:
try: os.remove(f)
except: pass
self.refresh_quarantine_list()
# --- VUE : MISE À JOUR ---
def init_update_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent")
self.views["update"] = frame
ctk.CTkLabel(frame, text="Mise à jour Cloud", font=ctk.CTkFont(size=34, weight="bold")).pack(anchor="w", pady=(0, 20))
self.btn_update_soft = ctk.CTkButton(frame, text="⬇️ Mettre à jour", command=self.run_software_update, fg_color="#8B5CF6", hover_color="#7C3AED", height=45)
self.btn_update_soft.pack(anchor="w")
self.update_console = ctk.CTkTextbox(frame, height=200, font=ctk.CTkFont(family="Consolas", size=13), fg_color="#111827", corner_radius=10)
self.update_console.pack(fill="x", pady=20)
ctk.CTkLabel(frame, text="Mise à jour Logicielle", font=ctk.CTkFont(size=38, weight="bold")).pack(anchor="w", pady=(0, 30))
card = ctk.CTkFrame(frame, fg_color=BG_CARD, corner_radius=15)
card.pack(fill="x", ipady=20)
ctk.CTkLabel(card, text="Synchronisation Serveur (git.7ka1.com)", font=ctk.CTkFont(size=18, weight="bold")).pack(pady=(20, 15))
self.btn_update_soft = ctk.CTkButton(card, text="Télécharger la dernière version", fg_color=ACCENT_BLUE, hover_color="#2563EB", height=45, command=self.run_software_update)
self.btn_update_soft.pack(pady=10)
self.update_console = ctk.CTkTextbox(card, height=150, font=ctk.CTkFont(family="Consolas", size=13), fg_color=BG_MAIN, corner_radius=10)
self.update_console.pack(fill="x", padx=20, pady=15)
def run_software_update(self):
self.btn_update_soft.configure(state="disabled")
self.update_console.insert("end", "[*] Négociation avec serveur...\n")
try:
urllib.request.urlretrieve(UPDATE_URL, os.path.abspath(__file__))
self.update_console.insert("end", "[+] Mise à jour réussie !\n")
messagebox.showinfo("Succès", "Redémarrage requis.")
self.destroy()
except:
self.update_console.insert("end", "[-] Échec.\n")
self.btn_update_soft.configure(state="normal")
self.update_console.insert("end", "[*] Connexion au serveur Git...\n")
def fetch():
try:
urllib.request.urlretrieve(UPDATE_URL, os.path.abspath(__file__))
self.update_console.insert("end", "[+] Code source téléchargé et appliqué !\n")
messagebox.showinfo("Mise à jour", "Redémarrage de l'application requis.")
os._exit(0) # Force la fermeture propre pour relancer via le menu
except:
self.update_console.insert("end", "[-] Échec de la connexion.\n")
self.btn_update_soft.configure(state="normal")
threading.Thread(target=fetch).start()
if __name__ == "__main__":
app = Antivirus7LnA()