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
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;
|
|
}
|
|
|
|
}
|