From 29da0a838039bef8647d4373cd4dac3f5cb2fd58 Mon Sep 17 00:00:00 2001 From: Jules Date: Wed, 6 May 2026 00:40:36 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20blocage=20si=20licence=20invalide/d?= =?UTF-8?q?=C3=A9sactiv=C3=A9e=20depuis=20Vision,=20cache=2030min?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- web/includes/auth.php | 1 + web/includes/layout.php | 1 + web/includes/vision.php | 78 +++++++++++++++++++++++++++++++++++------ 3 files changed, 70 insertions(+), 10 deletions(-) diff --git a/web/includes/auth.php b/web/includes/auth.php index 790533e..fe7c6b9 100644 --- a/web/includes/auth.php +++ b/web/includes/auth.php @@ -13,6 +13,7 @@ function auth_check(): void { header('Location: /login.php'); exit; } + if (function_exists('vision_enforce')) vision_enforce(); } function admin_check(): void { diff --git a/web/includes/layout.php b/web/includes/layout.php index 332cfc9..33e78dc 100644 --- a/web/includes/layout.php +++ b/web/includes/layout.php @@ -1,5 +1,6 @@ &machine_id=&hostname= + * Vérification toutes les 30 min, résultat mis en cache en BDD. */ define('VISION_LICENSE_URL', 'https://vision.copydev.fr/api/license.php'); define('VISION_PRODUCT', 'copymail'); +define('VISION_CACHE_TTL', 1800); // 30 minutes function vision_machine_id(): string { $id = cfg('vision_machine_id', ''); @@ -22,6 +24,14 @@ function vision_check(): array { return ['valid' => false, 'msg' => 'Licence non configurée.', 'unconfigured' => true]; } + // Cache : si dernier check < 30 min et résultat valide, on ne re-vérifie pas + $lastCheck = (int)cfg('vision_last_check', '0'); + $lastResult = cfg('vision_last_result', ''); + if ($lastResult && (time() - $lastCheck) < VISION_CACHE_TTL) { + $cached = json_decode($lastResult, true); + if ($cached) return $cached; + } + $url = VISION_LICENSE_URL . '?' . http_build_query([ 'key' => $key, 'machine_id' => vision_machine_id(), @@ -37,29 +47,77 @@ function vision_check(): array { $resp = @file_get_contents($url, false, $ctx); if ($resp === false) { - // Vision injoignable — on vérifie le délai offline autorisé - $lastOk = (int)cfg('vision_last_ok', '0'); - $timeout = (int)cfg('vision_offline_timeout', '72'); - $elapsed = (time() - $lastOk) / 3600; + // Vision injoignable — mode dégradé selon offline_timeout + $lastOk = (int)cfg('vision_last_ok', '0'); + $timeout = (int)cfg('vision_offline_timeout', '72'); + $elapsed = (time() - $lastOk) / 3600; if ($lastOk && $elapsed < $timeout) { - return ['valid' => true, 'msg' => "Vision injoignable (mode dégradé, {$timeout}h max)", 'degraded' => true]; + $result = ['valid' => true, 'msg' => "Vision injoignable — mode dégradé ({$timeout}h max)", 'degraded' => true]; + } else { + $result = ['valid' => false, 'msg' => 'Vision injoignable et délai hors-ligne dépassé.']; } - return ['valid' => false, 'msg' => 'Vision injoignable et délai offline dépassé.']; + return $result; } $data = json_decode($resp, true); if (!empty($data['valid'])) { - cfg_set('vision_last_ok', (string)time()); - cfg_set('vision_offline_timeout', (string)($data['offline_timeout_hours'] ?? 72)); - return [ + $result = [ 'valid' => true, 'client' => $data['client'] ?? '', 'product' => $data['product'] ?? '', 'expires' => $data['expires_at'] ?? null, 'devices' => $data['max_devices'] ?? 1, ]; + cfg_set('vision_last_ok', (string)time()); + cfg_set('vision_last_check', (string)time()); + cfg_set('vision_last_result', json_encode($result)); + cfg_set('vision_offline_timeout', (string)($data['offline_timeout_hours'] ?? 72)); + return $result; } - return ['valid' => false, 'msg' => $data['message'] ?? 'Licence invalide.']; + // Licence invalide / désactivée depuis Vision : invalide immédiatement le cache + $result = ['valid' => false, 'msg' => $data['message'] ?? 'Licence invalide ou désactivée.']; + cfg_set('vision_last_check', (string)time()); + cfg_set('vision_last_result', json_encode($result)); + cfg_set('vision_last_ok', '0'); // reset grace period + return $result; +} + +/** + * Vérifie la licence et bloque si invalide. + * À appeler sur toutes les pages. + * Exception : la page de config (/admin/domain.php) reste accessible aux admins. + */ +function vision_enforce(): void { + $result = vision_check(); + if ($result['valid']) return; + + $uri = $_SERVER['REQUEST_URI'] ?? ''; + + // L'admin peut toujours accéder à la config pour saisir la clé + if (is_admin() && strpos($uri, '/admin/domain.php') !== false) return; + + // Bloque tout le reste + $msg = htmlspecialchars($result['msg'] ?? 'Licence invalide.'); + $isAdmin = is_admin(); + $configLink = $isAdmin + ? 'Configurer la licence' + : ''; + http_response_code(403); + echo " + + Licence CopyMail + + + + +
+ +

CopyMail — Accès bloqué

+

{$msg}

+

Contactez CopyDev pour régulariser votre licence.

+ {$configLink} +
"; + exit; }