Valider f1d36b5f rédigé par marcimat's avatar marcimat
Parcourir les fichiers

Début d’une gestion du stockage des fichiers postés par Bigup, même s’ils...

Début d’une gestion du stockage des fichiers postés par Bigup, même s’ils proviennent d’un champ de type File qui n’utilise pas je JS (ou si le JS est cassé),
dès lors qu’on demande à récupérer les fichiers du formulaires.

Il faut récupérer le $_FILES posté, l’analyser, déplacer les fichiers qui ne sont pas en erreur dans le cache de bigup, et modifier le tableau $_FILES en conséquence.
Il reste quelques bugs.
parent 6ffe1cdb
Chargement en cours
Chargement en cours
Chargement en cours
Chargement en cours
+107 −3
Numéro de ligne d'origine Numéro de ligne de diff Ligne de diff
@@ -98,8 +98,8 @@ class Bigup {
	 * @return true
	 */
	public function effacer_fichiers($identifiants = []) {
		$this->debug("Suppression des fichiers restants");
		if (!$identifiants) {
			$this->debug("Suppression des fichiers");
			$this->cache->supprimer_repertoire($this->cache->dir_final());
		} else {
			$this->cache->enlever_fichier_depuis_identifiants($identifiants);
@@ -217,11 +217,115 @@ class Bigup {
	/**
	 * Chaque fichier présent dans `$_FILES` n'étant pas en erreur
	 * est géré par Bigup
	 *
	 * @todo
	 */
	public function gerer_fichiers_postes() {
		$liste = $this->extraire_fichiers_valides();
		foreach ($liste as $champ => $description) {
			$this->cache->stocker_fichier($champ, $description);
		}
	}

	/**
	 * Extrait et enlève de `$_FILES` les fichiers reçus sans erreur
	 * et crée un tableau avec pour clé le champ d'origine du fichier
	 *
	 * @return array Tableau (champ => description)
	 */
	public function extraire_fichiers_valides() {
		$liste = [];
		if (!count($_FILES)) {
			return $liste;
		}

		$infos = []; // name, pathname, error …
		foreach ($_FILES as $racine => $descriptions) {
			$infos = array_keys($descriptions);
			break;
		}

		foreach ($_FILES as $racine => $descriptions) {
			$error = $descriptions['error'];

			// cas le plus simple : name="champ", on s'embête pas
			if (!is_array($error)) {
				if ($error == 0) {
					$liste[$racine] = $descriptions;
					unset($_FILES[$racine]);
				}
				continue;
			}

			// cas plus compliqués :
			// name="champ[tons][][sous][la][pluie][]"
			// $_FILES[champ][error][tons][0][sous][la][pluie][0]
			else {
				$chemins = $this->extraire_sous_chemins_fichiers($error);
				foreach ($chemins['phps'] as $k => $chemin) {
					$description = [];
					foreach ($infos as $info) {
						$complet = '$_FILES[\'' . $racine . '\'][\'' . $info . '\']' . $chemin;
						eval("\$x = $complet; unset($complet);");
						$description[$info] = $x;
					}
					$liste[$racine . $chemins['names'][$k]] = $description;
				}
			}
		}
		return $liste;
	}


	/**
	 * Retourne l'écriture plate de l'arborescence d'un tableau
	 *
	 * - Phps a toutes les arborescences en conservant les index numériques autoincrémentés
	 *   et en mettant les autres index entre guillements
	 * - Reels a toutes les arborescences en conservant les index numériques autoincrémentés
	 * - Names a les arborescences sans les index numériques
	 *
	 * @param $tableau
	 * @return array Tableau [ phps => [], reels => [], names => []]
	 */
	public function extraire_sous_chemins_fichiers($tableau) {
		$listes = [
			'phps' => [],   // ['tons'][0]['sous']['la']['pluie'][0]
			'reels' => [],  // [tons][0][sous][la][pluie][0]
			'names' => [],  // [tons][][sous][la][pluie][]
		];

		// si le name était [], PHP ordonnera les entrées dans l'ordre, forcément.
		// si quelqu'un avait mis name="truc[8][]", ça devrait trouver la bonne écriture.
		$i = 0;

		foreach ($tableau as $cle => $valeur) {
			$reel = '[' . $cle . ']';
			$php = is_int($cle) ? $reel : '[\'' . $cle . '\']';

			if ($cle === $i) {
				$name = '[]';
			} else {
				$name = '[' . $cle . ']';
			}

			if (is_array($valeur)) {
				$ls = $this->extraire_sous_chemins_fichiers($valeur);
				foreach ($ls['phps'] as $l) {
					$listes['phps'][] = $php . $l;
				}
				foreach ($ls['reels'] as $l) {
					$listes['reels'][] = $reel . $l;
				}
				foreach ($ls['names'] as $l) {
					$listes['names'][] = $name . $l;
				}
			} else {
				$listes['phps'][] = $php;
				$listes['reels'][] = $reel;
				$listes['names'][] = $name;
			}
			$i++;
		}

		return $listes;
	}
}
+70 −0
Numéro de ligne d'origine Numéro de ligne de diff Ligne de diff
@@ -185,6 +185,32 @@ class Cache {
		return $desc;
	}

	/**
	 * Pour un champ donné (attribut name) et une description
	 * de fichier issue des données de `$_FILES`, déplace le fichier
	 * dans le cache de bigup
	 *
	 * @param string $champ
	 * @param array $description
	 */
	public function stocker_fichier($champ, $description) {
		$nom = $description['name'];
		$chemin = $description['tmp_name'];
		$nouveau_chemin =
			$this->dir_final
			. $champ . DIRECTORY_SEPARATOR
			. substr(md5($description['size'] . $nom), 10) . DIRECTORY_SEPARATOR
			. $nom;

		if (GestionRepertoires::creer_sous_repertoire(dirname($nouveau_chemin))) {
			if ($this->deplacer_fichier_upload($chemin, $nouveau_chemin)) {
				return $this->decrire_fichier($nouveau_chemin);
			}
		}

		return false;
	}

	/**
	 * Enlève un fichier complet
	 *
@@ -274,4 +300,48 @@ class Cache {
		}
		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
	 */
	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 ($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;
	}

}
 No newline at end of file
+20 −105
Numéro de ligne d'origine Numéro de ligne de diff Ligne de diff
@@ -29,58 +29,22 @@ class Flow {
	use LogTrait;

	/**
	 * Chemins des répertoires de travail (dans _DIR_TMP)
	 * Peut contenir la clé :
	 * - 'parts' pour les morceaux de fichiers
	 * - 'final' pour les fichiers complétés
	 * 
	 * @var array */
	private $dir = [];

	/**
	 * ?
	 * @var array */
	private $params = [];
	 * Gestion du cache Bigup
	 * @var Identifier
	 */
	private $cache = null;

	/**
	 * Préfixe utilisé par la librairie JS lors d'une requête
	 * @var string */
	private $prefixe = 'flow';

	/**
	 * Nom du répertoire, dans _DIR_TMP, qui va stocker les fichiers et morceaux de fichiers
	 * @var string */
	private $cache_dir = 'bigupload';

	/**
	 * Nom du formulaire qui utilise flow
	 * @var string */
	private $formulaire = '';

	/**
	 * Identifie un formulaire par rapport à un autre identique sur la même page ayant un appel différent.
	 * @var string */
	private $formulaire_identifiant = '';

	/**
	 * Nom du champ dans le formulaire qui utilise flow
	 * @var string */
	private $champ = '';

	/**
	 * Constructeur
	**/
	public function __construct() {}


	/**
	 * Définir les répertoires de travail
	 *
	 * @param string $type
	 * @param string $dir
	**/
	public function definir_repertoire($type, $chemin) {
		$this->dir[$type] = $chemin;
	public function __construct(Cache $cache) {
		$this->cache = $cache;
	}

	/**
@@ -186,7 +150,10 @@ class Flow {
		$key = key($_FILES);

		if (!$this->isChunkUploaded($identifier, $filename, $chunkNumber)) {
			if (!$this->deplacer_fichier_upload($file['tmp_name'], $this->tmpChunkPathFile($identifier, $filename, $chunkNumber))) {
			if (!$this->cache->deplacer_fichier_upload(
				$file['tmp_name'],
				$this->tmpChunkPathFile($identifier, $filename, $chunkNumber))
			) {
				return $this->send(415);
			}
		}
@@ -227,31 +194,22 @@ class Flow {
	}

	/**
	 * Trouver le chemin d'un répertoire temporaire 
	 * Construire le chemin d'un répertoire temporaire
	 *
	 * @param string $identifier
	 * @param string $subdir Type de répertoire
	 * @param string $racine Chemin racine à compléter
	 * @param bool $nocreate true pour ne pas créer le répertoire s'il manque.
	 * @return string|false
	 *     - string : chemin du répertoire
	 *     - false : échec.
	**/
	public function determine_upload($identifier, $subdir, $nocreate = false) {
		if (empty($this->dir[$subdir])) {
			return false;
		}

		$dir = $this->dir[$subdir] . DIRECTORY_SEPARATOR . $identifier;

		if ($nocreate) {
			return $dir;
		}

		include_spip('bigup_fonctions');
	public function determine_upload($identifier, $racine, $nocreate = false) {
		$dir = $racine . DIRECTORY_SEPARATOR . $identifier;
		if (!$nocreate) {
			if (!GestionRepertoires::creer_sous_repertoire($dir)) {
				return false;
			}

		}
		return $dir;
	}

@@ -264,10 +222,9 @@ class Flow {
	 * @return string chemin du répertoire
	**/
	public function determine_upload_parts($identifier = null, $nocreate = false) {
		return $this->determine_upload($identifier, 'parts', $nocreate);
		return $this->determine_upload($identifier, $this->cache->dir_parts(), $nocreate);
	}


	/**
	 * Trouver le répertoire temporaire pour stocker les fichiers complets reconstitués
	 *
@@ -276,49 +233,7 @@ class Flow {
	 * @return string chemin du répertoire
	**/
	public function determine_upload_final($identifier = null) {
		return $this->determine_upload($identifier, 'final');
	}

	/**
	 * 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
	 */
	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 ($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;
		return $this->determine_upload($identifier, $this->cache->dir_final());
	}

	/**
+4 −0
Numéro de ligne d'origine Numéro de ligne de diff Ligne de diff
@@ -37,6 +37,10 @@ class GestionRepertoires {
	 *     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);
+1 −3
Numéro de ligne d'origine Numéro de ligne de diff Ligne de diff
@@ -134,9 +134,7 @@ class Receptionner {
	 **/
	public function repondre_flow() {

		$flow = new Flow();
		$flow->definir_repertoire('parts', $this->cache->dir_parts());
		$flow->definir_repertoire('final', $this->cache->dir_final());
		$flow = new Flow($this->cache);
		$res = $flow->run();

		// le fichier est complet