You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

339 lines
8.6 KiB

<?php
namespace Spip\Bigup;
/**
* Gère la création ou le nettoyages de répertoires
*
* @plugin Bigup
* @copyright 2016
* @author marcimat
* @licence GNU/GPL
* @package SPIP\Bigup\Fonctions
*/
include_spip('inc/Bigup/LogTrait');
include_spip('inc/flock');
/**
* Gère la création ou le nettoyages de répertoires
*
* @notes
* Certaines fonctions peuvent faire beaucoup d'accès disque.
**/
class GestionRepertoires {
/**
* Pour un nom donné, propose un nom de répertoire valide sur la plupart des systèmes de fichiers
*
* @param string $nom
* Nom d'origine
* @return mixed string
* Nom possible pour un répertoire
*/
public static function nommer_repertoire($nom) {
// éviter les accents
include_spip('inc/charsets');
$nom = translitteration($nom);
// éviter les balises
$nom = preg_replace("/<[^>]*>/", '', $nom);
// éviter * . " / \ [ ] : ; | = , et bien d'autres
$nom = preg_replace('/\W/u', '_', $nom);
return $nom;
}
/**
* Reformater le nom du fichier pour l'écrire sur le serveur
*
* @see copier_document() dans SPIP
* @param string $filename
* @return string Nom du fichier corrigé
*/
public static function nommer_fichier($filename) {
$infos = pathinfo($filename);
include_spip('action/ajouter_documents');
$extension = corriger_extension($infos['extension']);
$nom = self::nommer_repertoire($infos['filename']);
return $nom . '.' . $extension;
}
/**
* Vérifier et préparer l'arborescence jusqu'au répertoire parent
*
* @note
* Code repris de SVP (action/teleporter)
*
* @param string $dest
* @return bool|string
* false en cas d'échec
* Chemin du répertoire sinon
*/
public static function creer_sous_repertoire($dest){
if (!$dest) {
return false;
}
$dest = rtrim($dest, "/");
$final = basename($dest);
$base = dirname($dest);
$create = [];
// on cree tout le chemin jusqu'a dest non inclus
while (!is_dir($base)) {
$create[] = basename($base);
$base = dirname($base);
}
while (count($create)){
if (!is_writable($base)) {
return false;
}
$base = sous_repertoire($base, array_pop($create));
if (!$base) {
return false;
}
}
if (!is_writable($base)) {
return false;
}
return sous_repertoire($base, $final);
}
/**
* Enlever d'une liste des fichiers ce qui est inutile
*
* Enlève les fichiers .. et . ainsi que des fichiers à
* ne pas considérer comme importants pour tester qu'un
* répertoire a du contenu.
* Particulièrement .ok et file.bigup.json
*
* @todo
* Trouver un mécanisme pour transmettre l'info file.bigup.json
* qui ne devrait pas être en dur ici.
*
* @param array $fichiers
* @return array
*/
public static function filtrer_fichiers($fichiers) {
return array_diff($fichiers, ['..', '.', '.ok', 'file.bigup.json']);
}
/**
* Nettoyer un répertoire suivant l'age et le nombre de ses fichiers
*
* Nettoie aussi les sous répertoires.
* Supprime automatiquement les répertoires vides.
*
* @param string $repertoire
* Répertoire à nettoyer
* @param int $age_max
* Age maxium des fichiers en seconde. Par défaut 24*3600
* @return bool
* - false : erreur de lecture du répertoire.
* - true : action réalisée.
**/
public static function nettoyer_repertoire_recursif($repertoire, $age_max = 86400) {
$repertoire = rtrim($repertoire, '/');
if (!is_dir($repertoire)) {
return false;
}
$fichiers = scandir($repertoire);
if ($fichiers === false) {
return false;
}
$fichiers = self::filtrer_fichiers($fichiers);
if (!$fichiers) {
supprimer_repertoire($repertoire);
return true;
}
foreach ($fichiers as $fichier) {
$chemin = $repertoire . DIRECTORY_SEPARATOR . $fichier;
if (is_dir($chemin)) {
self::nettoyer_repertoire_recursif($chemin, $age_max);
}
elseif (is_file($chemin) and !jeune_fichier($chemin, $age_max)) {
supprimer_fichier($chemin);
}
}
// à partir d'ici, on a pu possiblement vider le répertoire…
// on le supprime s'il est devenu vide.
$fichiers = scandir($repertoire);
if ($fichiers === false) {
return false;
}
$fichiers = self::filtrer_fichiers($fichiers);
if (!$fichiers) {
supprimer_repertoire($repertoire);
}
return true;
}
/**
* Supprimer le contenu d'un répertoire et nettoie les répertoires parents s'ils sont vides
*
* @param string $chemin Chemin du répertoire à supprimer
* @return bool
*/
public static function supprimer_repertoire($chemin) {
if (!$chemin) {
return false;
}
supprimer_repertoire($chemin);
GestionRepertoires::supprimer_repertoires_vides_parents($chemin);
return true;
}
/**
* Supprimer les répertoires vides enfants et parents (jusqu'à _DIR_TMP) d'un répertoire.
*
* @param string $chemin Chemin du repertoire à nettoyer, dans _DIR_TMP
* @param bool $parents True pour nettoyer les répertoires vides parents
* @param bool $enfants True pour nettoyer les répertoires vides enfants
* @return bool
*/
public static function supprimer_repertoires_vides($chemin, $parents = true, $enfants = true) {
// Se nettoyer soi et les répertoires enfants vides
if ($enfants) {
self::supprimer_repertoires_vides_enfants($chemin);
}
// Se nettoyer soi et nettoyer les répertoires parents vides
if ($parents) {
self::supprimer_repertoires_vides_parents($chemin);
}
return true;
}
/**
* Supprimer les répertoires enfants vides et moi même si vide.
*
* @param string $chemin
* Chemin du répertoire à nettoyer, dans _DIR_TMP
* @return bool
*/
public static function supprimer_repertoires_vides_enfants($chemin) {
$chemin = rtrim($chemin, DIRECTORY_SEPARATOR);
$chemin = substr($chemin, strlen(_DIR_TMP));
if (is_dir(_DIR_TMP . $chemin)) {
$fichiers = scandir(_DIR_TMP . $chemin);
if ($fichiers !== false) {
$fichiers = self::filtrer_fichiers($fichiers);
if ($fichiers) {
foreach ($fichiers as $fichier) {
$fichier = _DIR_TMP . $chemin . DIRECTORY_SEPARATOR . $fichier;
if (is_dir($fichier)) {
self::supprimer_repertoires_vides_enfants($fichier);
}
}
} else {
supprimer_repertoire(_DIR_TMP . $chemin);
}
}
if ($fichiers and is_dir(_DIR_TMP . $chemin)) {
$fichiers = scandir(_DIR_TMP . $chemin);
if ($fichiers !== false) {
$fichiers = self::filtrer_fichiers($fichiers);
if (!$fichiers) {
supprimer_repertoire(_DIR_TMP . $chemin);
}
}
}
}
return true;
}
/**
* Supprimer ce répertoire si vide et ses parents s'ils deviennent vides
*
* @param string $chemin
* Chemin du répertoire à nettoyer, dans _DIR_TMP
* @return bool
*/
public static function supprimer_repertoires_vides_parents($chemin) {
$chemin = rtrim($chemin, DIRECTORY_SEPARATOR);
$chemin = substr($chemin, strlen(_DIR_TMP));
while ($chemin and ($chemin !== '.')) {
if (!is_dir(_DIR_TMP . $chemin)) {
$chemin = dirname($chemin);
continue;
}
// on utilise un @ ici car si il y a 2 uploads concurrents ils peuvent se retrouver a cleaner en concurrence
// et le repertoire qu'on veut scanner a deja ete supprime
$fichiers = @scandir(_DIR_TMP . $chemin);
if ($fichiers === false) {
$chemin = dirname($chemin);
continue;
}
$fichiers = self::filtrer_fichiers($fichiers);
if (!$fichiers) {
supprimer_repertoire(_DIR_TMP . $chemin);
$chemin = dirname($chemin);
continue;
}
return true;
}
return true;
}
/**
* Déplacer ou copier un fichier
*
* @note
* Proche de inc/documents: deplacer_fichier_upload()
* mais sans l'affichage d'erreur éventuelle.
*
* @uses _DIR_RACINE
* @uses spip_unlink()
*
* @param string $source
* Fichier source à copier
* @param string $dest
* Fichier de destination
* @param bool $move
* - `true` : on déplace le fichier source vers le fichier de destination
* - `false` : valeur par défaut. On ne fait que copier le fichier source vers la destination.
* @return bool|mixed|string
*/
public static function deplacer_fichier_upload($source, $dest, $move=false) {
// Securite
if (substr($dest, 0, strlen(_DIR_RACINE)) == _DIR_RACINE) {
$dest = _DIR_RACINE . preg_replace(',\.\.+,', '.', substr($dest, strlen(_DIR_RACINE)));
} else {
$dest = preg_replace(',\.\.+,', '.', $dest);
}
if (!GestionRepertoires::creer_sous_repertoire(dirname($dest))) {
return false;
}
if ($move) {
$ok = @rename($source, $dest);
} else {
$ok = @copy($source, $dest);
}
if (!$ok) {
$ok = @move_uploaded_file($source, $dest);
}
if ($ok) {
@chmod($dest, _SPIP_CHMOD & ~0111);
}
return $ok ? $dest : false;
}
}