#!/usr/bin/env php true, CURLOPT_TIMEOUT => $UPDATE_TIMEOUT_TOTAL, CURLOPT_FAILONERROR => true, CURLOPT_FOLLOWLOCATION => true ]); $content = curl_exec($ch); if (curl_errno($ch)) { log_error("manifest_download_failed", "Échec téléchargement manifeste", ["url" => $url, "err" => curl_error($ch)]); return false; } curl_close($ch); $manifest_entries = []; $lines = explode("\n", trim($content)); foreach ($lines as $line) { if (preg_match('/^([0-9a-fA-F]{64})\s+(644|755)\s+((bin|lib|conf)\/[A-Za-z0-9._\/-]+)$/', trim($line), $matches)) { $manifest_entries[] = ['hash' => $matches[1], 'mode' => $matches[2], 'path' => $matches[3]]; } } return $manifest_entries; } /** * Met à jour un fichier */ function update_one_file($entry) { global $MONITORING_BASE_DIR, $UPDATE_BASE_URL, $UPDATE_TMP_DIR, $UPDATE_TIMEOUT_TOTAL; $rel_path = $entry['path']; $target_file = $MONITORING_BASE_DIR . '/' . $rel_path; $remote_url = rtrim($UPDATE_BASE_URL, '/') . '/' . $rel_path; $expected_hash = strtolower($entry['hash']); if (file_exists($target_file) && hash_file('sha256', $target_file) === $expected_hash) { return true; } $tmp_file = $UPDATE_TMP_DIR . '/' . basename($rel_path) . '.' . uniqid(); $ch = curl_init($remote_url); $fp = fopen($tmp_file, 'wb'); curl_setopt_array($ch, [ CURLOPT_FILE => $fp, CURLOPT_TIMEOUT => $UPDATE_TIMEOUT_TOTAL, CURLOPT_FAILONERROR => true, CURLOPT_FOLLOWLOCATION => true ]); $success = curl_exec($ch); curl_close($ch); fclose($fp); if (!$success || hash_file('sha256', $tmp_file) !== $expected_hash) { log_error("update_failed", "Fichier invalide ou corrompu", ["file" => $rel_path]); @unlink($tmp_file); return false; } ensure_parent_dir($target_file); chmod($tmp_file, octdec($entry['mode'])); safe_mv($tmp_file, $target_file); // Utilise la fonction safe_mv de ta lib log_notice("file_updated", "Mise à jour appliquée", ["file" => $rel_path]); return true; } /** * Nettoyage */ function delete_extra_files($remote_files) { global $UPDATE_ALLOW_DELETE, $MONITORING_BASE_DIR, $SCRIPT_PATH; if (!$UPDATE_ALLOW_DELETE) return; foreach (['bin', 'lib', 'conf'] as $dir) { $full_path = $MONITORING_BASE_DIR . '/' . $dir; if (!is_dir($full_path)) continue; $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($full_path, RecursiveDirectoryIterator::SKIP_DOTS)); foreach ($iterator as $file) { $path = $file->getPathname(); $rel_path = substr($path, strlen($MONITORING_BASE_DIR) + 1); // PROTECTIONS if (in_array($rel_path, $remote_files)) continue; if (str_contains($rel_path, '.local.')) continue; // Protection fichiers locaux if ($path === $SCRIPT_PATH) continue; // Ne pas se suicider if (@unlink($path)) { log_notice("file_deleted", "Fichier obsolète supprimé", ["file" => $rel_path]); } } } } // --- Main --- $manifest = fetch_manifest($UPDATE_MANIFEST_URL); if (!$manifest) exit(2); $remote_paths = []; $updated = 0; $failed = 0; foreach ($manifest as $entry) { $remote_paths[] = $entry['path']; update_one_file($entry) ? $updated++ : $failed++; } delete_extra_files($remote_paths); if ($failed > 0) { log_warning("update_partial", "Mise à jour terminée avec erreurs", ["failed" => $failed]); } else { log_info("update_ok", "Mise à jour terminée avec succès"); } exit_with_status();