Server : Apache System : Linux webd348.cluster026.gra.hosting.ovh.net 5.15.148-ovh-vps-grsec-zfs-classid #1 SMP Thu Feb 8 09:41:04 UTC 2024 x86_64 User : hednacluml ( 122243) PHP Version : 8.3.9 Disable Function : _dyuweyrj4,_dyuweyrj4r,dl Directory : /home/hednacluml/jobs/plugins-dist/bigup/inc/Bigup/ |
<?php namespace Spip\Bigup; use Spip\Bigup\CacheRepertoire; use Spip\Bigup\Identifier; /** * Gère le cache des fichiers dans tmp/bigupload * * @plugin Bigup * @copyright 2016 * @author marcimat * @licence GNU/GPL * @package SPIP\Bigup\Fonctions */ /** * Gère le cache des fichiers dans tmp/bigupload * **/ class CacheFichiers { use LogTrait; /** * Cache racine du stockage */ private ?CacheRepertoire $cache = null; /** * Identification du formulaire */ private ?Identifier $identifier = null; /** * Nom du champ */ private ?string $champ = null; /** * Constructeur * * @param CacheRepertoire $cache * Cache des fichiers pour ce formulaire * @param string $champ * Nom du champ (valeur de l'attribut name) pour ces fichiers */ public function __construct(CacheRepertoire $cache, $champ) { $this->cache = $cache; $this->identifier = $this->cache->cache->identifier; $this->champ = $champ; } /** * Retourne le chemin du cache pour ce champ du formulaire * @return string */ public function dir_champ() { // le nom du champ n'est pas falsifiable, il vient du token. // On l'utilise directement malgré les [] présents return $this->cache->dir . DIRECTORY_SEPARATOR . $this->champ; } /** * Retourne le chemin du répertoire cache pour cet identifiant de fichier du formulaire * @return string */ public function dir_identifiant($identifiant) { return $this->dir_champ() . DIRECTORY_SEPARATOR . static::hash_identifiant($identifiant); } /** * Retourne le chemin du répertoire cache pour cet identifiant de fichier et nom ce fichier du formulaire * @return string */ public function dir_fichier($identifiant, $fichier) { return $this->dir_identifiant($identifiant) . DIRECTORY_SEPARATOR . GestionRepertoires::nommer_repertoire($fichier); } /** * Retourne le chemin du fichier cache pour cet identifiant de fichier et nom ce fichier du formulaire * @param string $identifiant * @param stiring $fichier * @return string */ public function path_fichier($identifiant, $fichier) { return $this->dir_fichier($identifiant, $fichier) . DIRECTORY_SEPARATOR . 'file'; } /** * Retourne le nom du répertoire / hash relatif à l'identifiant de fichier indiqué. * * Si l'identifiant transmis est déjà un hash, le retourne directement * * @param string $identifiant * @return string */ public static function hash_identifiant($identifiant) { if ( strlen($identifiant) == 10 and $identifiant[0] == '@' and $identifiant[9] == '@' and ctype_xdigit(substr($identifiant, 1, -1)) ) { return $identifiant; } return '@' . substr(md5($identifiant), 0, 8) . '@'; } /** * Retoune le chemin du fichier qui stocke les descriptions d'un fichier dans le cache. * @param string $chemin * @return string */ public static function chemin_description($chemin) { return $chemin . '.bigup.json'; } /** * Indique si ce nom de fichier est un fichier de description * @param string $nom * @return bool true si c'en est un, false sinon. */ public static function est_fichier_description($nom) { return substr($nom, -strlen('.bigup.json')) == '.bigup.json'; } /** * Retourne la description d'un fichier dont le chemin est indiqué * * Cette description est sauvegardée à côté du fichier lors de son * enregistrement dans le cache. * * @uses lire_description_fichier() * @param string $chemin * Chemin du fichier dans le cache de bigup. * @return array|false * Description si retrouvée, sinon false **/ public static function obtenir_description_fichier($chemin) { $description = self::lire_description_fichier($chemin); if ($description) { self::debug('* Description de : ' . $description['name'] . ' (' . $chemin . ')'); } else { self::debug('* Description introuvable pour : ' . $chemin); } return $description; } /** * Décrire un fichier (comme dans `$_FILES`) * * @uses decrire_fichier_chemin() * @param string $identifiant * Identifiant du fichier dans le cache * @param array $infos * Description du fichier tel que $_FILES le fournit. * Tableau [ cle => valeur] avec pour clés : name, type, tmp_name, size, error * @return array|false * Description du fichier, complétée. False si erreur. **/ public function decrire_fichier($identifiant, $infos) { if (!is_array($infos)) { self::error('Infos non transmises pour décrire le fichier : ' . $identifiant); return false; } if (empty($infos['tmp_name'])) { self::error('Chemin du fichier absent pour décrire le fichier : ' . $identifiant); return false; } $chemin = $infos['tmp_name']; if (empty($infos['name'])) { self::error('Nom original du fichier absent pour décrire le fichier : ' . $chemin); return false; } if (empty($this->champ)) { self::error("Valeur de l'attribut 'name' absente pour décrire le fichier : " . $chemin); return false; } if (empty($this->identifier->formulaire_identifiant)) { self::error('Identifiant de formulaire absent pour décrire le fichier : ' . $chemin); return false; } $desc = self::decrire_fichier_description($infos, [ 'formulaire' => $this->identifier->formulaire, 'formulaire_args' => $this->identifier->formulaire_args, 'formulaire_identifiant' => $this->identifier->formulaire_identifiant, 'champ' => $this->champ, 'identifiant' => CacheFichiers::hash_identifiant($identifiant), ]); if ($desc and self::ecrire_description_fichier($chemin, $desc)) { return $desc; } else { return false; } } /** * Décrire un fichier (comme dans `$_FILES`) * * @param array $infos * Description du fichier tel que $_FILES le fournit. * Tableau [ cle => valeur] avec pour clés : name, type, tmp_name, size, error * @return array|false * Description du fichier, complétée. False si erreur. **/ public static function decrire_fichier_description($infos, $bigup) { if (!$infos or empty($infos['name']) or empty($infos['tmp_name'])) { return false; } $chemin = $infos['tmp_name']; if (!file_exists($chemin)) { self::error('Fichier introuvable pour description : ' . $chemin); return false; } $obligatoires = [ 'champ', 'identifiant', 'formulaire', 'formulaire_args', 'formulaire_identifiant', ]; if ($diff = array_diff_key(array_flip($obligatoires), $bigup)) { self::error('Description manquante dans (' . implode(',', $diff) . ') : ' . $chemin); return false; } $error = 0; $size = filesize($chemin); if (!empty($infos['size']) and $size != $infos['size']) { if ($size <= $infos['size']) { $error = UPLOAD_ERR_PARTIAL; // partially uploaded } else { $error = 99; // erreur de bigup ? } } $extension = pathinfo($infos['name'], PATHINFO_EXTENSION); include_spip('action/ajouter_documents'); $finfo = finfo_open(FILEINFO_MIME_TYPE); $type = finfo_file($finfo, $chemin); finfo_close($finfo); $extension = corriger_extension(strtolower($extension)); $desc = [ // présent dans $_FILES 'name' => $infos['name'], 'tmp_name' => $chemin, 'size' => $size, 'type' => $type, 'error' => $error, // informations supplémentaires (pas dans $_FILES habituellement) 'bigup' => $bigup + [ 'extension' => $extension, 'pathname' => $chemin, ] ]; // calcul d'une miniature if ($vignette = self::creer_vignette($desc, 200, 200)) { $desc['bigup']['vignette'] = $vignette; } return $desc; } /** * Sauvegarde la description du fichier * * @param string $chemin * @param array $description * @return bool */ public static function ecrire_description_fichier($chemin, $description) { $cache = self::chemin_description($chemin); $json = json_encode($description, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); if (json_last_error()) { return false; } ecrire_fichier($cache, $json); return true; } /** * Lit une description de fichier sauvegardée * * @param string $chemin * @return array|bool */ public static function lire_description_fichier($chemin) { $json = null; $cache = self::chemin_description($chemin); if (!lire_fichier($cache, $json)) { return false; } $description = json_decode($json, true); if ($error = json_last_error()) { self::error("Erreur de lecture JSON ($error) pour : " . $chemin); return false; } return $description; } /** * Crée une vignette à partir d'un chemin et retourne un base64 de l'image générée. * * @param array|string $desc * Description d'un fichier * @param int $width * @param int $height * @return array|false * False si vignette non calculée, tableau avec clés : 'width', 'height', 'data' */ public static function creer_vignette($desc, $width, $height) { $vignette = null; // description de fichier bigup / files if ( !is_array($desc) or empty($desc['tmp_name']) or empty($desc['type']) or empty($desc['size']) ) { return false; } $source = $desc['tmp_name']; // enlever le charset éventuel [$mime_type] = explode(';', $desc['type']); $size = $desc['size']; if (!str_starts_with($mime_type, 'image/')) { return false; } // 10Mo max. if ($size > 10 * 1024 * 1024) { return false; } // obtenir l'extension. $extension = substr($mime_type, 6); if ($extension === 'svg+xml') { // Les SVG ne perdront pas de poids, on n’envoie que les légers (< 10ko) if ($size > 10 * 1024) { return false; } $extension = 'svg'; } if (!in_array($extension, ['jpeg', 'png', 'gif', 'webp', 'svg'])) { return false; } include_spip('inc/filtres'); include_spip('inc/filtres_images_mini'); // il faut l'extension dans le chemin pour les filtres d'images… pff self::debug('Calcul d\'une vignette pour' . $source); if (!file_exists($source)) { self::debug('Image absente pour vignette : ' . $source); return false; } rename($source, $image = $source . '.' . $extension); $img = image_reduire($image, $width, $height); rename($image, $source); if (!$img or $img == $image) { return false; } $src = extraire_attribut($img, 'src'); $src = supprimer_timestamp($src); $width = extraire_attribut($img, 'width'); $height = extraire_attribut($img, 'height'); lire_fichier($src, $vignette); $vignette = 'data:' . $mime_type . ';base64,' . base64_encode($vignette); supprimer_fichier($src); return [ 'width' => $width, 'height' => $height, 'data' => $vignette ]; } }