498 lines
26 KiB
PHP
498 lines
26 KiB
PHP
<?php
|
|
// ============================================================================
|
|
// cron_archivage.php - Traitement périodique des archives de la flotte
|
|
// (cf synthèse §32-40, §43). Exécuté NATIVEMENT sur le VPS (pas dans le
|
|
// conteneur ship2net-php, cf synthèse §38.1), de façon planifiée :
|
|
// /usr/bin/php /var/www/cron_archivage.php
|
|
//
|
|
// Pour chaque boîtier connu (TB_Cartes avec une adresse NetBird) : liste des
|
|
// archives en attente sur son API, récupération, vérification d'intégrité,
|
|
// déchiffrement, affectation/MMSI (quarantaine si besoin), génération,
|
|
// chargement, enregistrement dans TB_Archives_traitees, confirmation de
|
|
// suppression côté CM5. Chaque boîtier est traité sous verrou exclusif
|
|
// (Verrouiller_boitier(), synthèse §44.6) : un boîtier déjà en cours de
|
|
// traitement par un autre processus est sauté pour ce passage.
|
|
// ============================================================================
|
|
|
|
// Certains fichiers du site (fonctions_mail.php notamment) utilisent des
|
|
// chemins relatifs ("../vendor/autoload.php"), qui se résolvent par rapport
|
|
// au répertoire de travail du processus - pas par rapport à l'emplacement du
|
|
// fichier qui les appelle. Ça fonctionnait par accident tant qu'Apache fixait
|
|
// ce répertoire à /var/www/html (la racine web). Fixé explicitement ici pour
|
|
// ne jamais dépendre de l'endroit d'où ce script est lancé - crucial pour une
|
|
// tâche cron, qui n'a généralement aucun répertoire de travail prévisible.
|
|
chdir("/var/www/html");
|
|
|
|
require_once __DIR__ . "/config/config.php";
|
|
require_once __DIR__ . "/config/fonctions_bdd.php";
|
|
require_once __DIR__ . "/fonctions_archivage.php";
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Port d'écoute de shipheart_api sur chaque CM5 (cf synthèse §33.1).
|
|
// ----------------------------------------------------------------------------
|
|
const PORT_API_CM5 = 8080;
|
|
|
|
// Délais réseau volontairement courts : un bateau injoignable ne doit jamais
|
|
// bloquer longtemps le traitement des autres (cf synthèse §33.1 - le cron
|
|
// retentera au prochain cycle, ce n'est jamais une perte).
|
|
const TIMEOUT_CONNEXION_CM5 = 5;
|
|
const TIMEOUT_REQUETE_CM5 = 15;
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Liste TOUS les boîtiers connus de ship2net (TB_Cartes avec une adresse
|
|
// NetBird renseignée) - pas seulement ceux affectés à un bateau. Nécessaire
|
|
// pour pouvoir mettre en quarantaine les archives d'un boîtier jamais affecté
|
|
// ou retiré, pas seulement celles d'une divergence MMSI (cf synthèse §38).
|
|
// Un boîtier neuf doit donc être déclaré dans TB_Cartes (adresse NetBird
|
|
// renseignée) avant toute affectation, pour entrer dans cette liste.
|
|
// Renvoie un tableau de ['Adresse_mac' => string, 'Adresse_netbird' => string].
|
|
// ----------------------------------------------------------------------------
|
|
function Lister_boitiers_connus($db)
|
|
{
|
|
$requete = "SELECT Adresse_mac, Adresse_netbird FROM TB_Cartes
|
|
WHERE Adresse_netbird IS NOT NULL AND Adresse_netbird <> ''";
|
|
$resultat = $db->query($requete);
|
|
$boitiers = [];
|
|
while ($ligne = $resultat->fetch_assoc()) $boitiers[] = $ligne;
|
|
return $boitiers;
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Interroge GET /archives sur le boîtier d'un bateau. Renvoie :
|
|
// ['ok' => true, 'archives' => [...]] en cas de succès
|
|
// ['ok' => false, 'raison' => "..."] sinon (bateau injoignable, réponse
|
|
// invalide...) - jamais une erreur
|
|
// fatale, le cron doit continuer
|
|
// avec les autres bateaux.
|
|
// Le tri chronologique est déjà garanti côté API par le nom de fichier
|
|
// (cf synthèse §32.4) ; retrié ici aussi par précaution, sans coût réel.
|
|
// ----------------------------------------------------------------------------
|
|
function Lister_archives_en_attente($adresse_netbird)
|
|
{
|
|
if (empty($adresse_netbird))
|
|
{
|
|
return ['ok' => false, 'raison' => "aucune adresse NetBird enregistrée pour ce boîtier"];
|
|
}
|
|
|
|
$url = "http://" . $adresse_netbird . ":" . PORT_API_CM5 . "/archives";
|
|
$ch = curl_init($url);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, TIMEOUT_CONNEXION_CM5);
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, TIMEOUT_REQUETE_CM5);
|
|
$reponse_brute = curl_exec($ch);
|
|
$erreur_curl = curl_error($ch);
|
|
$code_http = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
|
|
curl_close($ch);
|
|
|
|
if ($reponse_brute === false)
|
|
{
|
|
return ['ok' => false, 'raison' => "injoignable (" . $erreur_curl . ")"];
|
|
}
|
|
if ($code_http !== 200)
|
|
{
|
|
return ['ok' => false, 'raison' => "réponse HTTP " . $code_http];
|
|
}
|
|
|
|
$donnees = json_decode($reponse_brute, true);
|
|
if (!is_array($donnees) || ($donnees['status'] ?? '') !== 'ok' || !isset($donnees['archives']))
|
|
{
|
|
return ['ok' => false, 'raison' => "réponse JSON invalide ou inattendue"];
|
|
}
|
|
|
|
$archives = $donnees['archives'];
|
|
usort($archives, fn($a, $b) => strcmp($a['nom'], $b['nom']));
|
|
|
|
return ['ok' => true, 'archives' => $archives];
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Extrait l'empreinte (16 hex) portée par le nom de fichier - même format,
|
|
// exactement, que la validation déjà faite côté shipheart_api (§33.1).
|
|
// Renvoie false si le nom ne correspond pas au format attendu.
|
|
// ----------------------------------------------------------------------------
|
|
function Extraire_empreinte_nom($nom_fichier)
|
|
{
|
|
if (!preg_match('/^[0-9A-Fa-f]{12}_\d{6}_\d{6}_([0-9A-Fa-f]{16})\.sql\.gz\.enc$/', $nom_fichier, $m)) return false;
|
|
return strtolower($m[1]);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// GET /archives/<nom> - récupère le contenu brut d'une archive précise.
|
|
// Même forme de retour que Lister_archives_en_attente() (['ok' => bool, ...]).
|
|
// ----------------------------------------------------------------------------
|
|
function Recuperer_archive($adresse_netbird, $nom_fichier)
|
|
{
|
|
$url = "http://" . $adresse_netbird . ":" . PORT_API_CM5 . "/archives/" . rawurlencode($nom_fichier);
|
|
$ch = curl_init($url);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, TIMEOUT_CONNEXION_CM5);
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, TIMEOUT_REQUETE_CM5);
|
|
$contenu = curl_exec($ch);
|
|
$erreur_curl = curl_error($ch);
|
|
$code_http = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
|
|
curl_close($ch);
|
|
|
|
if ($contenu === false) return ['ok' => false, 'raison' => "injoignable (" . $erreur_curl . ")"];
|
|
if ($code_http !== 200) return ['ok' => false, 'raison' => "réponse HTTP " . $code_http];
|
|
|
|
return ['ok' => true, 'contenu' => $contenu];
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Vérifie que le contenu récupéré correspond bien à l'empreinte portée par
|
|
// son nom - détecte une corruption de transfert (cf synthèse §32.4/33.1).
|
|
// Ne jamais supprimer l'archive côté CM5 si cette vérification échoue - une
|
|
// nouvelle tentative au prochain cycle récupérera à nouveau le même fichier.
|
|
// ----------------------------------------------------------------------------
|
|
function Integrite_confirmee($nom_fichier, $contenu)
|
|
{
|
|
$empreinte_attendue = Extraire_empreinte_nom($nom_fichier);
|
|
if ($empreinte_attendue === false) return false;
|
|
$empreinte_calculee = substr(hash('sha256', $contenu), 0, 16);
|
|
return $empreinte_calculee === $empreinte_attendue;
|
|
}
|
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Copie de sauvegarde des archives mises en quarantaine (MMSI ou affectation absente) - avant de
|
|
// confirmer la suppression côté CM5, pour ne jamais perdre le contenu pendant que la quarantaine est en
|
|
// attente (cf synthèse §38, correction d'un manque identifié en testant le mécanisme).
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Met une archive en quarantaine (divergence MMSI) : sauvegarde son contenu
|
|
// CHIFFRÉ tel que reçu (pour ne rien perdre pendant que la quarantaine est en
|
|
// attente - correction d'un manque identifié en testant le mécanisme, cf
|
|
// synthèse §38), enregistre la ligne dans TB_Archives_traitees, puis confirme
|
|
// la suppression côté CM5 (son intégrité a déjà été vérifiée avant cet appel).
|
|
// ----------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------
|
|
// Met une archive en quarantaine faute d'affectation active (boîtier jamais
|
|
// affecté, ou retiré - cf synthèse §38). Contrairement à la quarantaine MMSI,
|
|
// aucun déchiffrement n'est nécessaire : sans bateau à qui l'attribuer, ni
|
|
// l'en-tête ni le contenu ne servent à rien à ce stade.
|
|
// ----------------------------------------------------------------------------
|
|
function Mettre_en_quarantaine_sans_affectation($db_principale, $adresse_netbird, $nom_fichier, $contenu_chiffre, $adresse_mac)
|
|
{
|
|
if (!is_dir(REPERTOIRE_QUARANTAINE)) @mkdir(REPERTOIRE_QUARANTAINE, 0750, true);
|
|
file_put_contents(REPERTOIRE_QUARANTAINE . "/" . $nom_fichier, $contenu_chiffre);
|
|
|
|
$statut = 'en_quarantaine_sans_affectation';
|
|
$requete = "INSERT INTO TB_Archives_traitees (Nom_fichier, Adresse_mac, Statut) VALUES (?, ?, ?)";
|
|
$stmt = $db_principale->prepare($requete);
|
|
$stmt->bind_param('sss', $nom_fichier, $adresse_mac, $statut);
|
|
$stmt->execute();
|
|
$stmt->close();
|
|
|
|
Confirmer_suppression_archive($adresse_netbird, $nom_fichier);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Rejette une archive suite à une erreur technique NON transitoire (cf
|
|
// synthèse §38) : déchiffrement, version inconnue, fichier SQL introuvable,
|
|
// échec de création de génération, échec de chargement. Sauvegarde le
|
|
// contenu chiffré dans un répertoire dédié (suivi manuel, reprise éventuelle
|
|
// une fois la cause corrigée), enregistre Statut='rejetee' avec le détail de
|
|
// l'erreur, puis confirme la suppression côté CM5 - inutile de laisser le
|
|
// CM5 retransmettre indéfiniment un fichier dont le contenu ne changera
|
|
// jamais.
|
|
// ----------------------------------------------------------------------------
|
|
function Rejeter_archive($db_principale, $adresse_netbird, $nom_fichier, $contenu_chiffre, $adresse_mac, $id_bateau, $detail_erreur, $mmsi_recu = null, $mmsi_attendu = null)
|
|
{
|
|
if (!is_dir(REPERTOIRE_ERREURS)) @mkdir(REPERTOIRE_ERREURS, 0750, true);
|
|
file_put_contents(REPERTOIRE_ERREURS . "/" . $nom_fichier, $contenu_chiffre);
|
|
|
|
$statut = 'rejetee';
|
|
$requete = "INSERT INTO TB_Archives_traitees
|
|
(Nom_fichier, Adresse_mac, ID_Bateau_affecte, ID_Bateau_final, Statut, Detail_erreur, MMSI_recu, MMSI_attendu, Date_resolution)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW())";
|
|
$stmt = $db_principale->prepare($requete);
|
|
$stmt->bind_param('ssiissss', $nom_fichier, $adresse_mac, $id_bateau, $id_bateau, $statut, $detail_erreur, $mmsi_recu, $mmsi_attendu);
|
|
$stmt->execute();
|
|
$stmt->close();
|
|
|
|
Confirmer_suppression_archive($adresse_netbird, $nom_fichier);
|
|
}
|
|
|
|
function Mettre_en_quarantaine_mmsi($db_principale, $adresse_netbird, $nom_fichier, $contenu_chiffre, $adresse_mac, $id_bateau, $mmsi_recu, $mmsi_attendu)
|
|
{
|
|
if (!is_dir(REPERTOIRE_QUARANTAINE)) @mkdir(REPERTOIRE_QUARANTAINE, 0750, true);
|
|
file_put_contents(REPERTOIRE_QUARANTAINE . "/" . $nom_fichier, $contenu_chiffre);
|
|
|
|
$statut = 'en_quarantaine_mmsi';
|
|
$requete = "INSERT INTO TB_Archives_traitees
|
|
(Nom_fichier, Adresse_mac, ID_Bateau_affecte, Statut, MMSI_recu, MMSI_attendu)
|
|
VALUES (?, ?, ?, ?, ?, ?)";
|
|
$stmt = $db_principale->prepare($requete);
|
|
$stmt->bind_param('ssisss', $nom_fichier, $adresse_mac, $id_bateau, $statut, $mmsi_recu, $mmsi_attendu);
|
|
$stmt->execute();
|
|
$stmt->close();
|
|
|
|
Confirmer_suppression_archive($adresse_netbird, $nom_fichier);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Vérifie si cette archive a déjà été traitée (quel que soit le résultat) -
|
|
// garde-fou anti-double-traitement, en complément de l'index UNIQUE déjà
|
|
// présent sur TB_Archives_traitees.Nom_fichier (cf synthèse §32.6/34.3).
|
|
// ----------------------------------------------------------------------------
|
|
function Archive_deja_traitee($db_principale, $nom_fichier)
|
|
{
|
|
$requete = "SELECT 1 FROM TB_Archives_traitees WHERE Nom_fichier=?";
|
|
$stmt = $db_principale->prepare($requete);
|
|
$stmt->bind_param('s', $nom_fichier);
|
|
$stmt->execute();
|
|
$stmt->store_result();
|
|
$trouve = $stmt->num_rows > 0;
|
|
$stmt->close();
|
|
return $trouve;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Enregistre le résultat du traitement d'une archive dans TB_Archives_traitees
|
|
// (cf synthèse §34.3). $id_generation peut être null (non encore résolu, ex :
|
|
// bateau sans aucune affectation).
|
|
// ----------------------------------------------------------------------------
|
|
function Enregistrer_archive_traitee($db_principale, $nom_fichier, $adresse_mac, $id_bateau, $id_generation, $statut, $mmsi_recu = null, $mmsi_attendu = null)
|
|
{
|
|
// MMSI_recu/MMSI_attendu toujours renseignés quand connus, même en cas de concordance parfaite -
|
|
// trace d'historique, et par principe : un NULL ne doit jamais pouvoir signifier à la fois "MMSI
|
|
// inconnu" et "MMSI connu mais non indiqué ici" (cf synthèse §39).
|
|
$requete = "INSERT INTO TB_Archives_traitees
|
|
(Nom_fichier, Adresse_mac, ID_Bateau_affecte, ID_Bateau_final, ID_Generation, Statut, MMSI_recu, MMSI_attendu, Date_resolution)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW())";
|
|
$stmt = $db_principale->prepare($requete);
|
|
$stmt->bind_param('ssiiisss', $nom_fichier, $adresse_mac, $id_bateau, $id_bateau, $id_generation, $statut, $mmsi_recu, $mmsi_attendu);
|
|
$ok = $stmt->execute();
|
|
$stmt->close();
|
|
return $ok;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// DELETE /archives/<nom> - confirme le traitement auprès du CM5, qui peut
|
|
// alors supprimer le fichier local (cf synthèse §33.1).
|
|
// ----------------------------------------------------------------------------
|
|
function Confirmer_suppression_archive($adresse_netbird, $nom_fichier)
|
|
{
|
|
$url = "http://" . $adresse_netbird . ":" . PORT_API_CM5 . "/archives/" . rawurlencode($nom_fichier);
|
|
$ch = curl_init($url);
|
|
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, TIMEOUT_CONNEXION_CM5);
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, TIMEOUT_REQUETE_CM5);
|
|
$reponse = curl_exec($ch);
|
|
$code_http = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
|
|
curl_close($ch);
|
|
return ($reponse !== false && $code_http === 200);
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Bloc principal - pour l'instant, se limite à afficher ce qui serait traité.
|
|
// Les blocs suivants remplaceront cet affichage par le traitement réel.
|
|
// ----------------------------------------------------------------------------
|
|
function Executer_cron_archivage()
|
|
{
|
|
// Ce script tourne nativement sur le VPS (pas dans le conteneur ship2net-php) - contrairement au site
|
|
// web, il ne peut pas résoudre "ship2net-db" (nom DNS interne à Docker). Le port de ship2net-db a été
|
|
// publié sur 127.0.0.1:3307 uniquement (jamais exposé publiquement, ni même via NetBird) - le 3306
|
|
// natif du port est déjà pris par l'instance MariaDB native du VPS, un usage différent (cf synthèse §37).
|
|
Ouverture_bdd("127.0.0.1:3307", BDD_USER, BDD_PASSWORD, BDD_DATABASE);
|
|
$db = $_SESSION['db'];
|
|
|
|
$boitiers = Lister_boitiers_connus($db);
|
|
echo count($boitiers) . " boîtier(s) connu(s) avec une adresse NetBird renseignée.\n\n";
|
|
|
|
foreach ($boitiers as $boitier)
|
|
{
|
|
$adresse_mac = $boitier['Adresse_mac'];
|
|
$verrou = Verrouiller_boitier($adresse_mac);
|
|
if (!$verrou['ok'])
|
|
{
|
|
echo "Boîtier {$adresse_mac} : " . $verrou['raison'] . " - ignoré pour ce cycle.\n";
|
|
continue;
|
|
}
|
|
|
|
try
|
|
{
|
|
$resultat = Lister_archives_en_attente($boitier['Adresse_netbird']);
|
|
|
|
if (!$resultat['ok'])
|
|
{
|
|
echo "Boîtier {$adresse_mac} : " . $resultat['raison'] . " - ignoré pour ce cycle.\n";
|
|
continue;
|
|
}
|
|
|
|
$nb = count($resultat['archives']);
|
|
if ($nb === 0) continue; // rien à signaler pour ce boîtier ce cycle-ci
|
|
|
|
// Résolu UNE SEULE FOIS par boîtier (pas par archive) : toutes les archives d'un même passage
|
|
// partagent la même adresse MAC, et l'affectation ne change jamais en cours d'exécution du
|
|
// script - seul un geste administratif explicite la modifie (cf synthèse §38).
|
|
$affectation = Resoudre_affectation_active($db, $adresse_mac);
|
|
$libelle = $affectation['existe'] ? "Bateau {$affectation['id_bateau']}" : "Boîtier {$adresse_mac} (aucune affectation active)";
|
|
echo "{$libelle} : {$nb} archive(s) en attente.\n";
|
|
|
|
foreach ($resultat['archives'] as $archive)
|
|
{
|
|
$nom = $archive['nom'];
|
|
|
|
if (Archive_deja_traitee($db, $nom))
|
|
{
|
|
echo " - {$nom} : déjà traitée - confirmation de suppression seulement.\n";
|
|
Confirmer_suppression_archive($boitier['Adresse_netbird'], $nom);
|
|
continue;
|
|
}
|
|
|
|
$recuperation = Recuperer_archive($boitier['Adresse_netbird'], $nom);
|
|
if (!$recuperation['ok'])
|
|
{
|
|
echo " - {$nom} : ÉCHEC de récupération (" . $recuperation['raison'] . ") - nouvelle tentative au prochain cycle.\n";
|
|
continue;
|
|
}
|
|
|
|
if (!Integrite_confirmee($nom, $recuperation['contenu']))
|
|
{
|
|
echo " - {$nom} : empreinte invalide, archive potentiellement corrompue - nouvelle tentative au prochain cycle.\n";
|
|
continue;
|
|
}
|
|
|
|
// Aucune affectation active (jamais affecté, ou retiré) : quarantaine directe, sans
|
|
// déchiffrement - ni l'en-tête ni le contenu ne servent à rien sans bateau à qui l'attribuer
|
|
// (cf synthèse §38) :
|
|
if (!$affectation['existe'])
|
|
{
|
|
echo " - {$nom} : aucune affectation active pour ce boîtier - mise en quarantaine.\n";
|
|
Mettre_en_quarantaine_sans_affectation($db, $boitier['Adresse_netbird'], $nom, $recuperation['contenu'], $adresse_mac);
|
|
continue;
|
|
}
|
|
$id_bateau = $affectation['id_bateau'];
|
|
|
|
$dechiffrement = Decrypter_archive($recuperation['contenu']);
|
|
if (!$dechiffrement['ok'])
|
|
{
|
|
echo " - {$nom} : ÉCHEC de déchiffrement (" . $dechiffrement['raison'] . ") - rejetée.\n";
|
|
Rejeter_archive($db, $boitier['Adresse_netbird'], $nom, $recuperation['contenu'], $adresse_mac, $id_bateau, "Échec de déchiffrement : " . $dechiffrement['raison']);
|
|
continue;
|
|
}
|
|
|
|
$origine_bdd = $dechiffrement['entete']['Origine_bdd'] ?? 'normale';
|
|
$mmsi_recu = $dechiffrement['entete']['No_mmsi'] ?? null;
|
|
$mmsi_attendu_connu = null; // reste null si No_mmsi absent de l'en-tête - sinon toujours renseigné (cf synthèse §39)
|
|
if ($mmsi_recu !== null)
|
|
{
|
|
$verification_mmsi = Verifier_mmsi($db, $id_bateau, $mmsi_recu);
|
|
$mmsi_attendu_connu = $verification_mmsi['attendu'];
|
|
if ($verification_mmsi['statut'] === 'quarantaine')
|
|
{
|
|
echo " - {$nom} : MMSI reçu ({$mmsi_recu}) différent de celui attendu ({$verification_mmsi['attendu']}) - mise en quarantaine.\n";
|
|
Mettre_en_quarantaine_mmsi($db, $boitier['Adresse_netbird'], $nom, $recuperation['contenu'], $adresse_mac, $id_bateau, $mmsi_recu, $verification_mmsi['attendu']);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
$version_cible = $dechiffrement['entete']['Version_soft_no_package'] ?? null;
|
|
$generation_active = Resoudre_generation_active($db, $id_bateau);
|
|
$nouvelle_generation = Nouvelle_generation_necessaire($origine_bdd, $generation_active);
|
|
|
|
echo " - {$nom} : récupérée et intègre (" . strlen($recuperation['contenu']) . " octets), origine=" . $origine_bdd . "\n";
|
|
|
|
$fichier_sql = Resoudre_fichier_sql($db, $version_cible);
|
|
if ($fichier_sql === false)
|
|
{
|
|
echo " ÉCHEC : version logicielle '{$version_cible}' inconnue de TB_Versions - rejetée.\n";
|
|
Rejeter_archive($db, $boitier['Adresse_netbird'], $nom, $recuperation['contenu'], $adresse_mac, $id_bateau, "Version logicielle '{$version_cible}' inconnue de TB_Versions", $mmsi_recu, $mmsi_attendu_connu);
|
|
continue;
|
|
}
|
|
$contenu_fichier_sql = @file_get_contents(REPERTOIRE_VERSIONS_SQL . "/" . $fichier_sql);
|
|
if ($contenu_fichier_sql === false)
|
|
{
|
|
echo " ÉCHEC : fichier {$fichier_sql} introuvable sur le VPS - rejetée.\n";
|
|
Rejeter_archive($db, $boitier['Adresse_netbird'], $nom, $recuperation['contenu'], $adresse_mac, $id_bateau, "Fichier {$fichier_sql} introuvable sur le VPS", $mmsi_recu, $mmsi_attendu_connu);
|
|
continue;
|
|
}
|
|
|
|
if ($nouvelle_generation)
|
|
{
|
|
$zone_usine = Extraire_zone_usine($contenu_fichier_sql);
|
|
if ($zone_usine === false)
|
|
{
|
|
echo " ÉCHEC : marqueur Factory_structure introuvable dans {$fichier_sql} - rejetée.\n";
|
|
Rejeter_archive($db, $boitier['Adresse_netbird'], $nom, $recuperation['contenu'], $adresse_mac, $id_bateau, "Marqueur Factory_structure introuvable dans {$fichier_sql}", $mmsi_recu, $mmsi_attendu_connu);
|
|
continue;
|
|
}
|
|
// 'factory'/'restauration' est toujours un fait rapporté honnêtement par le bateau - à
|
|
// conserver tel quel pour le diagnostic, qu'une génération existait déjà côté serveur ou
|
|
// non. Seul 'normale' sans aucune génération existante (un bateau vraiment neuf, qui n'a
|
|
// jamais rien envoyé) devient 'creation_initiale' (cf synthèse §37).
|
|
if ($origine_bdd === 'factory' || $origine_bdd === 'restauration')
|
|
{
|
|
$motif = $origine_bdd;
|
|
}
|
|
else
|
|
{
|
|
$motif = 'creation_initiale';
|
|
}
|
|
$resultat_generation = Creer_generation($db, $id_bateau, $motif, $zone_usine);
|
|
if (!$resultat_generation['ok'])
|
|
{
|
|
echo " ÉCHEC de création de génération : " . $resultat_generation['raison'] . " - rejetée.\n";
|
|
Rejeter_archive($db, $boitier['Adresse_netbird'], $nom, $recuperation['contenu'], $adresse_mac, $id_bateau, "Échec de création de génération : " . $resultat_generation['raison'], $mmsi_recu, $mmsi_attendu_connu);
|
|
continue;
|
|
}
|
|
$nom_base_cible = $resultat_generation['nom_base'];
|
|
echo " Nouvelle génération créée : {$nom_base_cible} (motif={$motif}).\n";
|
|
}
|
|
else
|
|
{
|
|
$nom_base_cible = $generation_active['nom_base'];
|
|
echo " Génération existante : {$nom_base_cible}.\n";
|
|
// Bloc suivant : rejeu des segments incrémentaux si la version a changé,
|
|
// via Extraire_segments_incrementaux() + exécution (même mécanique que Creer_generation).
|
|
}
|
|
$chargement = Charger_contenu_archive($nom_base_cible, $dechiffrement['sql']);
|
|
if (!$chargement['ok'])
|
|
{
|
|
echo " ÉCHEC de chargement dans {$nom_base_cible} : " . $chargement['raison'] . " - rejetée.\n";
|
|
Rejeter_archive($db, $boitier['Adresse_netbird'], $nom, $recuperation['contenu'], $adresse_mac, $id_bateau, "Échec de chargement dans {$nom_base_cible} : " . $chargement['raison'], $mmsi_recu, $mmsi_attendu_connu);
|
|
continue;
|
|
}
|
|
echo " Contenu chargé dans {$nom_base_cible}.\n";
|
|
|
|
// Résolution de la vraie Cle_primaire de la génération (celle juste créée, ou l'existante) :
|
|
$generation_pour_enregistrement = Resoudre_generation_active($db, $id_bateau);
|
|
Enregistrer_archive_traitee($db, $nom, $adresse_mac, $id_bateau, $generation_pour_enregistrement['cle'], 'traitee', $mmsi_recu, $mmsi_attendu_connu);
|
|
|
|
if (Confirmer_suppression_archive($boitier['Adresse_netbird'], $nom))
|
|
{
|
|
echo " Archive confirmée et supprimée côté CM5.\n";
|
|
}
|
|
else
|
|
{
|
|
echo " AVERTISSEMENT : confirmation de suppression échouée (le fichier restera côté CM5, sans conséquence - cf synthèse §32.5).\n";
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
Liberer_boitier($verrou['poignee']);
|
|
}
|
|
}
|
|
|
|
Fermeture_bdd();
|
|
}
|
|
|
|
Executer_cron_archivage();
|