Files
7LnA_Antivirus_Linux_Free_C…/7lna.py
2026-03-05 10:28:08 +00:00

537 lines
28 KiB
Python

import customtkinter as ctk
from tkinter import filedialog, messagebox
import os
import threading
import subprocess
import urllib.request
import time
import datetime
import glob
import hashlib
import string
import secrets
import json
import platform
import shutil
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
# ==========================================
# CONFIGURATION GLOBALE
# ==========================================
UPDATE_URL = "https://git.7ka1.com/7ka1/7LnA_Antivirus_Linux_Free_ClamAV_Based/raw/branch/main/7lna.py"
QUARANTINE_DIR = os.path.expanduser("~/.7lna_quarantine")
WATCH_FOLDER = os.path.expanduser("~/Téléchargements")
HISTORY_FILE = os.path.expanduser("~/.7lna_history.json")
ctk.set_appearance_mode("dark")
ctk.set_default_color_theme("blue")
# ==========================================
# UTILITAIRES SYSTÈME
# ==========================================
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])
except Exception:
pass
class RealTimeShieldHandler(FileSystemEventHandler):
def __init__(self, app_instance):
self.app = app_instance
def on_created(self, event):
if not event.is_directory:
time.sleep(1.5)
self.app.trigger_realtime_scan(event.src_path)
# ==========================================
# APPLICATION PRINCIPALE
# ==========================================
class Antivirus7LnA(ctk.CTk):
def __init__(self):
super().__init__()
self.title("7LnA Security Suite - V8 Enterprise Edition")
self.geometry("1250x850")
self.minsize(1000, 700)
os.makedirs(QUARANTINE_DIR, exist_ok=True)
if not os.path.exists(HISTORY_FILE):
with open(HISTORY_FILE, "w") as f: json.dump([], f)
self.shield_observer = None
self.shield_active = False
self.check_dependencies()
self.setup_ui()
threading.Thread(target=self.monitor_usb_drives, daemon=True).start()
def check_dependencies(self):
try:
subprocess.run(['clamscan', '--version'], capture_output=True, check=True)
self.clamav_installed = True
except:
self.clamav_installed = False
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.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))
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")
self.version_label = ctk.CTkLabel(self.sidebar, text="v8.1 - Enterprise", text_color="#6B7280", font=ctk.CTkFont(weight="bold"))
self.version_label.grid(row=10, column=0, pady=20, sticky="s")
self.views = {}
self.init_dashboard_view()
self.init_scanner_view()
self.init_realtime_view()
self.init_audit_view()
self.init_tools_view()
self.init_quarantine_view()
self.init_schedule_view()
self.init_history_view()
self.init_update_view()
self.select_view("dashboard")
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,
command=lambda: self.select_view(view_name))
btn.grid(row=row, column=0, padx=20, pady=5, sticky="ew")
return btn
def select_view(self, view_name):
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)
if view_name == "quarantine": self.refresh_quarantine_list()
if view_name == "history": self.refresh_history_list()
def setup_console_tags(self, console):
console.tag_config("danger", foreground="#EF4444")
console.tag_config("success", foreground="#10B981")
console.tag_config("warning", foreground="#F59E0B")
console.tag_config("info", foreground="#3B82F6")
def get_time_prefix(self):
return datetime.datetime.now().strftime("[%H:%M:%S] ")
def log_threat_to_history(self, filepath, threat_name):
try:
with open(HISTORY_FILE, "r") as f: data = json.load(f)
data.append({"date": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "file": filepath, "threat": threat_name})
with open(HISTORY_FILE, "w") as f: json.dump(data, f, indent=4)
except: pass
# --- AUTO-DETECT USB ---
def monitor_usb_drives(self):
media_dir = f"/media/{os.getlogin()}"
if not os.path.exists(media_dir): return
known_mounts = set(os.listdir(media_dir))
while True:
time.sleep(3)
try:
current_mounts = set(os.listdir(media_dir))
new_mounts = current_mounts - known_mounts
for mount in new_mounts:
self.after(0, self.prompt_usb_scan, os.path.join(media_dir, mount), mount)
known_mounts = current_mounts
except: pass
def prompt_usb_scan(self, path, name):
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 ?"):
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")
# --- VUE : SCANNER MANUEL ---
def init_scanner_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent")
frame.grid_rowconfigure(4, 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))
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.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.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, height=45, 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.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.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.setup_console_tags(self.scan_console)
self.scan_console.insert("end", f"{self.get_time_prefix()}[*] Moteur de détection V8 prêt...\n", "info")
# ----- CORRECTION DU ROOTKIT (Compatible X11 & Wayland) -----
def run_rootkit_scan(self):
dialog = ctk.CTkToplevel(self)
dialog.title("Sécurité Administrateur")
dialog.geometry("400x230")
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="L'analyse anti-rootkit nécessite les droits sudo.\nEntrez votre mot de passe session :", justify="center").pack(pady=5)
pwd_entry = ctk.CTkEntry(dialog, show="*", width=250)
pwd_entry.pack(pady=10)
def on_submit(event=None):
pwd = pwd_entry.get()
dialog.destroy()
if pwd:
threading.Thread(target=self._exec_rootkit, args=(pwd,), daemon=True).start()
ctk.CTkButton(dialog, text="Lancer l'analyse", command=on_submit, fg_color="#DC2626", hover_color="#991B1B").pack(pady=10)
dialog.bind("<Return>", on_submit)
# Attendre que la fenêtre soit dessinée à l'écran (Universel pour Linux)
dialog.wait_visibility()
dialog.attributes("-topmost", True)
dialog.grab_set()
pwd_entry.focus_set()
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_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é. (Lancez le nouveau script install.sh)\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(self):
threading.Thread(target=self._run_freshclam, daemon=True).start()
def _run_freshclam(self):
self.scan_progress.start()
try:
process = subprocess.Popen(['freshclam'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
for line in process.stdout:
self.scan_console.insert("end", line)
self.scan_console.see("end")
process.wait()
self.scan_console.insert("end", f"{self.get_time_prefix()}[+] Signatures à jour.\n", "success")
except: pass
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")
if console == self.scan_console: self.scan_progress.start()
try:
cmd = ['clamscan', '-i', '--no-summary', f'--move={QUARANTINE_DIR}']
if is_dir: cmd.append('-r')
cmd.append(path)
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
infected = 0
for line in process.stdout:
clean_line = line.strip()
if "FOUND" in clean_line:
threat_name = clean_line.split(":")[-1].replace("FOUND", "").strip()
console.insert("end", f"{self.get_time_prefix()}☠️ MENACE DÉTECTÉE : {clean_line}\n", "danger")
infected += 1
self.log_threat_to_history(path, threat_name)
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")
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 ---
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))
btn_frame = ctk.CTkFrame(frame, fg_color="transparent")
btn_frame.grid(row=1, column=0, sticky="ew", pady=(0, 10))
ctk.CTkButton(btn_frame, text="Lancer l'Audit", 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")
self.audit_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=13), fg_color="#111827", corner_radius=10)
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()
def perform_audit(self):
try: self.audit_console.insert("end", subprocess.check_output(['ss', '-tuln'], text=True))
except: pass
try:
res = subprocess.check_output(['ps', '-eo', 'pid,user,%cpu,%mem,cmd', '--sort=-%cpu'], text=True)
self.audit_console.insert("end", "\n" + "\n".join(res.split('\n')[:11]) + "\n")
except: pass
def check_firewall(self):
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")
except: pass
# --- VUE : OUTILS AVANCÉS ---
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))
clean_card = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10)
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)
shred_card = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10)
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)
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)
except: pass
messagebox.showinfo("Succès", "Nettoyage terminé.")
def run_shredder(self):
filepath = filedialog.askopenfilename()
if not filepath: return
if messagebox.askyesno("DANGER", f"Détruire DÉFINITIVEMENT ?\n\n{filepath}"):
try:
file_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))
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))
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))
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)
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.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)
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"
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.")
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)
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", "\n ✅ Historique vierge.\n")
else:
for entry in reversed(data):
self.history_console.insert("end", f"[{entry['date']}] ☠️ {entry['threat']}\n -> Fichier : {entry['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)
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", "\n ✅ Aucun fichier malveillant isolé.\n")
else:
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 ?"):
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)
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")
if __name__ == "__main__":
app = Antivirus7LnA()
app.mainloop()