<?php

## fonctions pour hasher les documents

if (!defined("_ECRIRE_INC_VERSION")) return;

include_spip('config/hasherfin');


/**
 *  Niveau de profondeur par extension
 *
 *  Définit le niveau de profondeur des répertoires pour cette extension
 *  Les extensions non-listées (return=0) auront leurs fichiers stockés à plat
 * 	pour les nouveaux déposés, et inchangé pour les autres.
 *
 *  A faire évoluer à terme vers un accès à la configuration dans l'espace privé 
 *
 * @param string $ext	extension de fichier
 * @return int			niveau de sous-repertoires pour cette extension
 */
function hasherfin_niveau_extension($ext) {
	$extensions = hasherfin_extensions();
	$ext = strtolower(trim($ext));
	
	if(array_key_exists($ext,$extensions)) {
		return $extensions[$ext];
	} else {
		return 0;
	}
}


/**
 * Utilitaire
 * Calcul du chemin cible en fonction du niveau de sous-repertoire souhaité
 *
 * @param array $r		$r[1]: chemin initial ; $r[2]: nom du fichier sans chemin
 * @param int $level	Niveau de sous-repertoire souhaité
 * @return string		Chemin calculé
 */
function hasherfin_chemin_md5($r,$level) {
	$m = md5($r[2]);
	switch($level) {
	case 0:
		return $r[1].'/'.$r[2];
	case 1:
		return $r[1].'/'.$m[0].'/'.$r[2];
	case 2:
		return $r[1].'/'.$m[0].'/'.$m[1].'/'.$r[2];
	case 3:
		return $r[1].'/'.$m[0].'/'.$m[1].'/'.$m[2].'/'.$r[2];
	}
}


/**
 * Calcul du chemin cible en fonction du chemin initial
 *
 * pour un fichier d'origine situé dans IMG/{$ext}(/.)?(/.)?(/.)?/xxxx.ext,
 * prendre les $level premiers caractères (a, b, c) du md5(xxxx.ext),
 * et déplacer le fichier dans IMG/{$ext}/xxxx.ext, IMG/{$ext}/a/xxxx.ext,
 * IMG/{$ext}/a/b/xxxx.ext, ou IMG/{$ext}/a/b/c/xxxx.ext
 * attention on ignore le IMG/ eventuel dans $doc, et on retourne sans IMG/
 * 
 * @param string $doc		chemin initial
 * @param int $target		(valeurs 0,1,2,3 seulement)
 * @return string|false		chemin cible
 */
function hasherfin_adresser_document($doc, $target) {

	if(preg_match(',^(?:IMG/)?([^/]+)/([^/]+\.\1)$,S', $doc, $r)) { 		// niveau 0
		return ($target == 0) ? false : hasherfin_chemin_md5($r,$target);
	}
	
	if(preg_match(',^(?:IMG/)?([^/]+)/./([^/]+\.\1)$,S', $doc, $r)) { 		// niveau 1
		return ($target == 1) ? false : hasherfin_chemin_md5($r,$target);
	}

	if(preg_match(',^(?:IMG/)?([^/]+)/././([^/]+\.\1)$,S', $doc, $r)) {		// niveau 2
		return ($target == 2) ? false : hasherfin_chemin_md5($r,$target);
	}

	if(preg_match(',^(?:IMG/)?([^/]+)/./././([^/]+\.\1)$,S', $doc, $r)) {	// niveau 3
		return ($target == 3) ? false : hasherfin_chemin_md5($r,$target);
	}	
	
	// autres
	return false;
}


/**
 * Deplacer un fichier et sa reference dans la base de donnees
 * avec tous les controles d'erreur
 * 
 * @param int $id_document	
 * @param bool $corriger	true pour les rectifications de problèmes
 * @param int $level		niveau désiré
 * @return bool				true en cas de succès
 */
function hasherfin_deplacer_document($id_document, $corriger=false, $level=3) {

	// 1. recuperer les donnees du document
	// et verifier qu'on peut le hasher
	if (!$id_document = intval($id_document)){
		spip_log("Erreur hasherfin_deplacer_document intval $id_document", 'hash');
		return false;
	}
	/* === OK en Spip 3.2 - Obsolete en Spip 4.0
	if (!$s = spip_query('SELECT fichier FROM spip_documents WHERE id_document='.$id_document)
	OR !$t = spip_fetch_array($s)) {
		spip_log("Erreur hasherfin_deplacer_document select doc=$id_document ".var_export($s, true), 'hash'._LOG_ERREUR);
		return false;
	}
	*/ /* FIX S42.001 */
	if(!$t = sql_fetsel('fichier','spip_documents',"id_document=$id_document")) {
		spip_log("Erreur hasherfin_deplacer_document select doc=$id_document ", 'hash'._LOG_ERREUR);
		return false;
	}
	$src = $t['fichier'];

	// savoir si on a IMG/ devant (en SPIP 1.9.2) ou pas (SPIP 2)
	$img = preg_match(',^IMG/,', $src)
		? 'IMG/' : '';
	$dir_ref = preg_match(',^IMG/,', $src)
		? _DIR_RACINE : _DIR_IMG;

	// On fabrique le nom du fichier dest
	if (!$dest = hasherfin_adresser_document($src, $level)) {
		spip_log("Erreur hasherfin_adresser_document($src) level : $level", 'hash'._LOG_ERREUR);
		return false;
	}

	// si le src n'existe pas, ciao, enfin presque
	if (!file_exists($dir_ref.$src)) {
		spip_log("Erreur hasherfin_deplacer_document id_document $id_document fichier $dir_ref $src n'existe pas", 'hash'._LOG_INFO_IMPORTANTE);

		// si le src n'existe pas, on verifie qu'il n'a pas deja ete déplace (ie le dest existe),
		// et si oui, on modifie juste le chemin en base... 
		if($corriger) {
			if(file_exists(_DIR_IMG.$dest)){
				// on note la destination finale
				if (!spip_query('UPDATE spip_documents SET fichier="'.$img.$dest.'" WHERE id_document='.$id_document)) {
					spip_log("erreur hasherfin_deplacer_document update correction $img $dest doc $id_document", 'hash'._LOG_ERREUR);
					return false;
				} else {
					spip_log("hasherfin_deplacer_document fichier "._DIR_IMG."$dest existe deja, Table corrigee", 'hash'._LOG_ERREUR);
					return true ;
				}
			} else {
				spip_log("hasherfin_deplacer_document fichier "._DIR_IMG."$dest n'existe pas", 'hash'._LOG_ERREUR);
			}
		}
		return false ;
	}

	// si le dest existe deja, renommer jusqu'a trouver un creneau libre
	$i = 0;
	while (file_exists(_DIR_IMG.$dest)) {
		$i++;
		$dest = preg_replace(',(-\d+)?(\.[^.]+)$,', '-'.$i.'\2', $dest);
	}

	// 2. creer au besoin les sous-repertoires
	if (!is_dir(_DIR_IMG.$dir = dirname($dest))
	AND !mkdir(_DIR_IMG.$dir, _SPIP_CHMOD, /* recursive, php5 */ true)) {
		spip_log("erreur hasherfin_deplacer_document mkdir($dir)", 'hash'._LOG_ERREUR);
		return false;
	}

	// 3. Section critique : il faut modifier dans la base *et* deplacer
	// on note les fichiers en cours de deplacement avec un - devant ; si
	// ca casse on saura reparer
	if (!spip_query('UPDATE spip_documents SET fichier=CONCAT("-", fichier) WHERE id_document='.$id_document)) {
		spip_log("erreur hasherfin_deplacer_document update 1", 'hash'._LOG_ERREUR);
		return false;
	}
	// on deplace
	if (!rename($dir_ref.$src, _DIR_IMG.$dest)) {
		spip_log("erreur hasherfin_deplacer_document rename", 'hash');
		spip_query('UPDATE spip_documents SET fichier="'.$src.'" WHERE id_document='.$id_document);
		return false;
	}
	// on note la destination finale
	if (!spip_query('UPDATE spip_documents SET fichier="'.$img.$dest.'" WHERE id_document='.$id_document)) {
		spip_log("erreur hasherfin_deplacer_document update 2", 'hash');
		return false;
	}

	// 4. Ouf c'est fini et sans erreur
	spip_log("HasherFin - Document $id_document deplace sans erreur",'hash'._LOG_INFO_IMPORTANTE);
	return true;
}

/**
 * Utilitaire
 * Définition de la pattern de recherche des chemins de fichiers à transformer
 * en fonction du niveau cible. On cherche tous les chemins qui ne sont pas au
 * niveau considéré
 *
 * @param int $level	Niveau à atteindre
 * @return string		Le pattern de recherche
 */
function hasherfin_pattern($level) {
	switch($level) {
	case 0:
		return "([^/]+/[^/]+$|[^/]+/[^/]+/[^/]+$|[^/]+/[^/]+/[^/]+/[^/]+$)";
	case 1:
		return "([^/]+$|[^/]+/[^/]+/[^/]+$|[^/]+/[^/]+/[^/]+/[^/]+$)";
	case 2:
		return "([^/]+/[^/]+$|[^/]+$|[^/]+/[^/]+/[^/]+/[^/]+$)";
	case 3:
	default:
		return "([^/]+/[^/]+$|[^/]+/[^/]+/[^/]+$|[^/]+$)";
	}
}

/**
 * Cette fonction prend les n documents non hashés les plus récents,
 * ayant l'extension $ext
 * et appelle hasherfin_deplacer_document() sur chacun d'eux. Elle renvoie
 * un array() contenant les id_document des documents qu'elle a déplacés.
 *
 * @param int $n			Nombre de documents à traiter
 * @param string $ext		Extension de fichier à considérer
 * @param bool $corriger	true pour traiter les problèmes
 * @param int $level		Niveau cible
 * @return array|bool		tableau des docid ou false en cas de problème
 */
function hasherfin_deplacer_n_documents($n, $ext, $corriger=false, $level=3) {
	$docs = array();
	static $dejafait = false;

	// Une seule fois par hit (cas de l'insertion de plusieurs docs venant d'un zip)
	if ($dejafait) return $docs ;
	$dejafait = true;

	/* OBSOLETE SPIP 4.0
	if (!$n = intval($n)
	OR !$s = spip_query($q = "SELECT id_document FROM spip_documents WHERE fichier REGEXP '^(IMG/)?$ext/"
	. hasherfin_pattern($level)
	."' AND distant='non' ORDER BY date DESC LIMIT $n")) {
		spip_log("erreur hasherfin_deplacer_n_documents requete $q", 'hash');
		return false;
	} */
	/* FIX S42.001 */
	if (!$n = intval($n) OR
	    !$s = sql_select(
	    		'id_document', /* select */
	    		'spip_documents',  /* from */
				array( /* where */
					"fichier REGEXP '^(IMG/)?$ext/" . hasherfin_pattern($level)."'",
					"distant='non'"
					),
				"", /* Group by */
				'date DESC', /* Order by */
				"$n" /* Limit */
				)) {
		spip_log("erreur hasherfin_deplacer_n_documents requete $q", 'hash');
		return false;
	}	

	while ($t = sql_fetch($s)) { /* was spip_fetch_array($s) until SPIP 3.2 */
		$id_document = $t['id_document'];
		if (hasherfin_deplacer_document($id_document, $corriger, $level))
			$docs[] = $id_document;
	}
	
	

	return $docs;
}

include_spip('inc/meta');

function hasherfin_ranger_n_documents($n) {
	
	// On cherche les extensions déjà traitées et celles qui restent
	$hash_extensions = hasherfin_extensions();
	$hash_fait = array();
	$reste=array();
	if(isset($GLOBALS['meta']['hasherfin_fait'])) {		
		$hash_fait = unserialize($GLOBALS['meta']['hasherfin_fait']);
		$reste = array_values(array_diff(array_keys($hash_extensions),$hash_fait));
		if(count($reste)==0) {
			$reste = array_keys($hash_extensions);
			$hash_fait = array();
		}		
	} else {
		$reste = array_keys($hash_extensions);
	}
	
	// L'extension à traiter est la première qui reste
	$ext = $reste[0];	
	spip_log("HasherFin - Rangement de $ext ","hash"._LOG_INFO_IMPORTANTE);
	$level = hasherfin_niveau_extension($ext);
	hasherfin_deplacer_n_documents($n,$ext,false,$level);
	$hash_fait[] = $ext;
	ecrire_meta('hasherfin_fait',serialize($hash_fait));
}
	
/**
 * Compte les documents hashes par extension et niveau de sous-repertoire
 * @return array	Une valeur pour chaque extension/niveau
 */
function hasherfin_compter_documents() {

	$compte = array();
	/* OBOLETE FROM SPIP 4.0
	foreach(hasherfin_extensions() as $ext) { 
		for($i=0; $i<4; $i++) {
			$s = spip_query($q = "SELECT COUNT(*) FROM spip_documents WHERE fichier REGEXP '^(IMG/)?$ext/"
				. hasherfin_pattern($level) . "' AND distant='non'");
			$compte[$ext][$i] = array_pop(spip_fetch_array($s));
		}
	}*/
	/* FIX S42.001 */
	foreach(hasherfin_extensions() as $ext) { 
		for($i=0; $i<4; $i++) {
			$s = sql_select("COUNT(*)",'spip_documents',
					array(
						"fichier REGEXP '^(IMG/)?$ext/". hasherfin_pattern($level) . "'",
						"distant='non'"
						));
			$compte[$ext][$i] = array_pop(sql_fetch($s));
		}
	}
	return $compte;
}

/**
 * Pipeline post_edition pour agir apres ajout de nouveaux documents via upload
 * @param array $flux
 * @return array
 */
function hasherfin_post_edition($flux) {
	/* FIX S43.001 - 'operation' > 'action' */
	if ($flux['args']['action'] == 'ajouter_document'
	AND $id = intval($flux['args']['id_objet'])) {
		$fichier = sql_getfetsel('fichier','spip_documents',"id_document=$id");
		if(preg_match(',\.([^\./]+)$,',$fichier,$match) == 1) {
			$ext = $match[1];
			if($level = hasherfin_niveau_extension($ext)) {
				hasherfin_deplacer_document($id, false, $level);  
			}
		}
	}
	return $flux;
}

 /**
 * Taches périodiques
 *
 * Donne la périodicité des tâches en secondes.
 * La $tache['nom'] est décite par la fonction genie_nom_dist() dans le
 * fichier genie/nom.php
 *
 * @pipeline taches_generales_cron
 * @param  array $taches tableau de tâches à exécuter
 * @return array      	 tableau de tâches à exécuter
**/
function hasherfin_taches_generales_cron($taches){
	
	$taches['ranger'] = _HF_FREQUENCE_RANGEMENT; 	// Rangement des fichiers	
	
	spip_log("CRON - taches enregitreees : " . print_r($taches,true), "hash");
	return $taches;
}

/**
 * Utilitaire
 * Détermine si un fichier htaccess est installé
 *
 * @param string $htaccess	Chemin d'accès potentiel
 * @return bool				true si installé
 */
function htaccess_est_installe($htaccess){
	if (!lire_fichier($htaccess, $contenu)
	OR !preg_match(',hash_404,', $contenu)) {
		return false ;
	} else {
		return true ;
	}
}
?>