Your IP : 216.73.216.245


Current Path : /home/bourgleram/www/398a1/
Upload File :
Current File : /home/bourgleram/www/398a1/inc.tar

notes.php000066600000015263152522474730006433 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

//
// Notes de bas de page
//
if (!defined('_NOTES_OUVRE_REF')) define('_NOTES_OUVRE_REF','<span class="spip_note_ref">&nbsp;[');
if (!defined('_NOTES_FERME_REF')) define('_NOTES_FERME_REF',']</span>');
if (!defined('_NOTES_OUVRE_NOTE')) define('_NOTES_OUVRE_NOTE','<span class="spip_note_ref">[');
if (!defined('_NOTES_FERME_NOTE')) define('_NOTES_FERME_NOTE',']&nbsp;</span>');
if (!defined('_NOTES_RACCOURCI')) define('_NOTES_RACCOURCI', ',\[\[(\s*(<([^>\'"]*)>)?(.*?))\]\],msS');

// argument = true: empiler l'etat courant, initialiser un nouvel etat
// argument = false: restaurer l'etat precedent, denonce un etat courant perdu
// argument chaine, on y recherche les notes et on les renvoie en tableau
// argument tableau, texte de notes a rajouter dans ce qu'on a deja
// le dernier cas retourne la composition totale
// en particulier, envoyer un tableau vide permet de tout recuperer
// C'est stocke dans la globale $les_notes, mais pas besoin de le savoir

function inc_notes_dist($arg,$operation='traiter', $ignorer_autobr=false)
{
	static $pile = array();
	static $next_marqueur = 1;
	static $marqueur = 1;
	global $les_notes, $compt_note, $notes_vues;
	switch ($operation){
		case 'traiter':
			if (is_array($arg)) return traiter_les_notes($arg,$ignorer_autobr);
			else
				return traiter_raccourci_notes($arg, $marqueur>1?$marqueur:'');
			break;
		case 'empiler':
			if ($compt_note==0)
				// si le marqueur n'a pas encore ete utilise, on le recycle dans la pile courante
				array_push($pile, array(@$les_notes, @$compt_note, $notes_vues,0));
			else {
				// sinon on le stocke au chaud, et on en cree un nouveau
				array_push($pile, array(@$les_notes, @$compt_note, $notes_vues,$marqueur));
				$next_marqueur++; // chaque fois qu'on rempile on incremente le marqueur general
				$marqueur = $next_marqueur; // et on le prend comme marqueur courant
			}
			$les_notes = '';
			$compt_note = 0;
			break;
		case 'depiler':
			#$prev_notes = $les_notes;
			if (strlen($les_notes)) spip_log("notes perdues");
			// si le marqueur n'a pas servi, le liberer
			if (!strlen($les_notes) AND $marqueur==$next_marqueur)
				$next_marqueur--;
			// on redepile tout suite a une fin d'inclusion ou d'un affichage des notes
			list($les_notes, $compt_note, $notes_vues, $marqueur) = array_pop($pile);
			#$les_notes .= $prev_notes;
			// si pas de marqueur attribue, on le fait
			if (!$marqueur){
				$next_marqueur++; // chaque fois qu'on rempile on incremente le marqueur general
				$marqueur = $next_marqueur; // et on le prend comme marqueur courant
			}
			break;
		case 'sauver_etat':
			if ($compt_note OR $marqueur>1 OR $next_marqueur>1)
				return array($les_notes, $compt_note, $notes_vues, $marqueur,$next_marqueur);
			else
				return ''; // rien a sauver
			break;
		case 'restaurer_etat':
			if ($arg AND is_array($arg)) // si qqchose a restaurer
				list($les_notes, $compt_note, $notes_vues, $marqueur,$next_marqueur) = $arg;
			break;
		case 'contexter_cache':
			if ($compt_note OR $marqueur>1 OR $next_marqueur>1)
				return array("$compt_note:$marqueur:$next_marqueur");
			else
				return '';
			break;
		case 'reset_all': // a n'utiliser qu'a fins de test
			if (strlen($les_notes)) spip_log("notes perdues [reset_all]");
			$pile = array();
			$next_marqueur = 1;
			$marqueur = 1;
			$les_notes = '';
			$compt_note = 0;
			$notes_vues = array();
			break;
	}
}


function traiter_raccourci_notes($letexte, $marqueur_notes)
{
	global $compt_note, $notes_vues;

	if (strpos($letexte, '[[') === false
	OR !preg_match_all(_NOTES_RACCOURCI, $letexte, $m, PREG_SET_ORDER))
		return array($letexte, array());

	// quand il y a plusieurs series de notes sur une meme page
	$mn =  !$marqueur_notes ? '' : ($marqueur_notes.'-');
	$mes_notes = array();
	foreach ($m as $r) {
		list($note_source, $note_all, $ref, $nom, $note_texte) = $r;

		// reperer une note nommee, i.e. entre chevrons
		// On leve la Confusion avec une balise en regardant
		// si la balise fermante correspondante existe
		// Cas pathologique:   [[ <a> <a href="x">x</a>]]

		if (!(isset($nom) AND $ref
		AND ((strpos($note_texte, '</' . $nom .'>') === false)
		     OR preg_match(",<$nom\W.*</$nom>,", $note_texte)))) {
			$nom = ++$compt_note;
			$note_texte = $note_all;
		}

		// eliminer '%' pour l'attribut id
		$ancre = $mn . str_replace('%','_', rawurlencode($nom));

		// ne mettre qu'une ancre par appel de note (XHTML)
		$att = ($notes_vues[$ancre]++) ? '' : " id='nh$ancre'";

		// creer le popup 'title' sur l'appel de note
		// propre est couteux => nettoyer_raccourcis_typo
		if ($title = supprimer_tags(nettoyer_raccourcis_typo($note_texte))) {
			$title = " title='" . couper($title,80) . "'";
		}

		// ajouter la note aux notes precedentes
		if ($note_texte) {
			$mes_notes[]= array($ancre, $nom, $note_texte);
		}

		// dans le texte, mettre l'appel de note a la place de la note
		if ($nom) $nom = _NOTES_OUVRE_REF."<a href='#nb$ancre' class='spip_note' rel='footnote'$title$att>$nom</a>"._NOTES_FERME_REF;

		$pos = strpos($letexte, $note_source);

		// supprimer les ' ' ou 'nbsp;' avant un appel de note
		$avant = preg_replace(',( |&nbsp;|'.chr(194).chr(160).')+$,S', '', substr($letexte, 0, $pos));

		$letexte = $avant
		. code_echappement($nom)
		. substr($letexte, $pos + strlen($note_source));

	}

	return array($letexte, $mes_notes);
}


// http://code.spip.net/@traiter_les_notes
function traiter_les_notes($notes,$ignorer_autobr) {
	$mes_notes = '';
	if ($notes) {
		$title =  _T('info_notes');
		foreach ($notes as $r) {
			list($ancre, $nom, $texte) = $r;
			$atts = " href='#nh$ancre' class='spip_note' title='$title $ancre' rev='footnote'";
			$mes_notes .= "\n\n"
			. "<div id='nb$ancre'><p". ($GLOBALS['class_spip'] ? " class='spip_note'" : "") .">"
			. code_echappement($nom
				? _NOTES_OUVRE_NOTE."<a".$atts.">$nom</a>"._NOTES_FERME_NOTE
				: '')
			. trim($texte)
			.'</div>';
		}
		if ($ignorer_autobr)
			$mes_notes = _AUTOBR_IGNORER.$mes_notes;
		$mes_notes = propre($mes_notes);
	}
	return ($GLOBALS['les_notes'] .= $mes_notes);
}

?>
autoliens.php000066600000003422152522474730007300 0ustar00<?php


// Les URLs brutes sont converties en <a href='url'>url</a>
// http://code.spip.net/@traiter_raccourci_liens
function tw_autoliens($t) {

	defined('_EXTRAIRE_LIENS') || define('_EXTRAIRE_LIENS', ',' . '\[[^\[\]]*(?:<-|->).*?\]' . '|<a\b.*?</a\b' . '|<\w.*?>' . '|((?:https?:/|www\.)[^"\'\s\[\]\}\)<>]*)' .',imsS');

	$t = preg_replace_callback(_EXTRAIRE_LIENS, 'tw_traiter_autoliens', $t);

	include_spip('inc/ressource');
	if (defined('_EXTRAIRE_RESSOURCES'))
	$t = preg_replace_callback(_EXTRAIRE_RESSOURCES, 'traiter_ressources', $t);

	// echapper les autoliens eventuellement inseres (en une seule fois)
	if (strpos($t,"<html>")!==false)
		$t = echappe_html($t);
	return $t;
}


// callback pour la fonction autoliens()
// http://code.spip.net/@autoliens_callback
function tw_traiter_autoliens($r) {
	if (count($r)<2) return reset($r);
	list($tout, $l) = $r;
	if (!$l) return $tout;
	// reperer le protocole
	if (preg_match(',^(https?):/*,S', $l, $m)) {
		$l = substr($l, strlen($m[0]));
		$protocol = $m[1];
	} else 	$protocol = 'http';
	// valider le nom de domaine
	if (!preg_match(_EXTRAIRE_DOMAINE, $l)) return $tout;
	// les ponctuations a la fin d'une URL n'en font certainement pas partie
	// en particulier le "|" quand elles sont dans un tableau a la SPIP
	preg_match('/^(.*?)([,.;?|]?)$/', $l, $k);
	$url = $protocol.'://'.$k[1];
	$lien = charger_fonction('lien', 'inc');
	// deux fois <html> car inc_lien echappe un coup et restaure ensuite
	// => un perd 1 <html>
	$r = $lien($url,"<html><html>$url</html></html>",'','','','nofollow') . $k[2];

	// ajouter la class auto
	$r = inserer_attribut($r, 'class', trim(extraire_attribut($r,'class').' auto'));

	// si l'original ne contenait pas le 'http:' on le supprime du clic
	return ($m ? $r : str_replace('>http://', '>', $r));
}



?>memoization-mini.php000066600000000552152522474730010563 0ustar00<?php

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

# memoization minimale (preferer le plugin memoization)
function cache_get($key) {
	return @unserialize(file_get_contents(_DIR_CACHE."wheels/".$key.".txt"));
}
function cache_set($key, $value) {
	$dir = sous_repertoire(_DIR_CACHE,"wheels/");
	return ecrire_fichier($dir.$key.".txt", serialize($value));
}

?>
yaml-mini.php000066600000000534152522474730007172 0ustar00<?php

if (!defined('_ECRIRE_INC_VERSION')) return;
# yaml_decode

function yaml_decode($input) {
	require_once dirname(__FILE__).'/../lib/yaml/sfYaml.php';
	require_once dirname(__FILE__).'/../lib/yaml/sfYamlParser.php';

	$yaml = new sfYamlParser();

	try
	{
	  return $yaml->parse($input);
	  
	}
	catch (Exception $e)
	{
		return null;
	}
}

?>textwheel.php000066600000007522152522474730007313 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

include_spip('engine/textwheel');
// si une regle change et rend son cache non valide
// incrementer ce define au numero de commit concerne
// (inconsistence entre la wheel et l'inclusion php)
if (!defined('_WHEELS_VERSION')) define('_WHEELS_VERSION',68672);

//
// Definition des principales wheels de SPIP
//
if (!isset($GLOBALS['spip_wheels'])) {
	$GLOBALS['spip_wheels'] = array();
}

// Si le tableau des raccourcis existe déjà
if (!isset($GLOBALS['spip_wheels']['raccourcis']) OR !is_array($GLOBALS['spip_wheels']['raccourcis']))
	$GLOBALS['spip_wheels']['raccourcis'] = array(
		'spip/spip.yaml',
		'spip/spip-paragrapher.yaml'
	);
else
	$GLOBALS['spip_wheels']['raccourcis'] = array_merge(
		array(
			'spip/spip.yaml',
			'spip/spip-paragrapher.yaml'
		),
		$GLOBALS['spip_wheels']['raccourcis']
	);

if (test_espace_prive ())
	$GLOBALS['spip_wheels']['raccourcis'][] = 'spip/ecrire.yaml';

$GLOBALS['spip_wheels']['interdire_scripts'] = array(
	'spip/interdire-scripts.yaml'
);

$GLOBALS['spip_wheels']['echappe_js'] = array(
	'spip/echappe-js.yaml'
);

$GLOBALS['spip_wheels']['paragrapher'] = array(
	'spip/spip-paragrapher.yaml'
);

$GLOBALS['spip_wheels']['listes'] = array(
	'spip/spip-listes.yaml'
);

//
// Methode de chargement d'une wheel SPIP
//

class SPIPTextWheelRuleset extends TextWheelRuleSet {
	protected function findFile(&$file, $path=''){
		static $default_path;

		// absolute file path?
		if (file_exists($file))
			return $file;

		// file include with texwheels, relative to calling ruleset
		if ($path AND file_exists($f = $path.$file))
			return $f;

		return find_in_path($file,'wheels/');
	}

	public static function &loader($ruleset, $callback = '', $class = 'SPIPTextWheelRuleset') {

		# memoization
		# attention : le ruleset peut contenir apres loading des chemins relatifs
		# il faut donc que le cache depende du chemin courant vers la racine de SPIP
		$key = 'tw-'.md5(_WHEELS_VERSION."-".serialize($ruleset).$callback.$class._DIR_RACINE);

		# lecture du cache
		if ((!defined('_VAR_MODE') OR _VAR_MODE!='recalcul')
		  AND $cacheruleset = tw_cache_get($key))
			return $cacheruleset;

		# calcul de la wheel
		$ruleset = parent::loader($ruleset, $callback, $class);

		# ecriture du cache
		tw_cache_set($key, $ruleset);

		return $ruleset;
	}
}


function tw_trig_purger($quoi){
	if ($quoi=='cache')
		purger_repertoire(_DIR_CACHE."wheels");
	return $quoi;
}


/**
 * Lire une valeur en cache
 * memoization minimale
 * (utilise le plugin memoization si disponible)
 *
 * @param string $key
 * @return mixed
 */
function tw_cache_get($key) {
	if (function_exists('cache_get')){
		return cache_get($key);
	}
	return @unserialize(file_get_contents(_DIR_CACHE."wheels/".$key.".txt"));
}

/**
 * Ecrire une valeur en cache
 * memoization minimale
 * (utilise le plugin memoization si disponible)
 *
 * @param string $key
 * @param mixed $value
 * @param int|null $ttl
 * @return bool
 */
function tw_cache_set($key, $value, $ttl=null) {
	if (function_exists('cache_set')){
		return cache_set($key, $value, $ttl);
	}
	$dir = sous_repertoire(_DIR_CACHE,"wheels/");
	return ecrire_fichier($dir.$key.".txt", serialize($value));
}
ressource.php000066600000002122152522474730007303 0ustar00<?php

/*
 * transforme un raccourci de ressource en un lien minimaliste
 * 
 *
 */

define('_EXTRAIRE_RESSOURCES', ',' . '<"?(https?://|[^\s][\w -]+\.[\w -]+)[^<]*>'.',UimsS');


function traiter_ressources($r) {
	$html = null;
	if ($ressource = charger_fonction('ressource', 'inc', true)) {
		$html = $ressource($r[0]);
	}

	if (is_null($html)) {
		include_spip('inc/lien');
		$url = explode(' ', trim($r[0], '<>'));
		$url = $url[0];
		# <http://url/absolue>
		if (preg_match(',^https?://,i', $url))
			$html = PtoBR(propre("<span class='ressource spip_out'>&lt;[->".$url."]&gt;</span>"));
		# <url/relative>
		else if (false !== strpos($url, '/'))
			$html = PtoBR(propre("<span class='ressource spip_in'>&lt;[->".$url."]&gt;</span>"));
		# <fichier.rtf>
		else {
			preg_match(',\.([^.]+)$,', $url, $regs);
			if (file_exists($f = _DIR_IMG.$regs[1].'/'.$url)) {
				$html = PtoBR(propre("<span class='ressource spip_in'>&lt;[".$url."->".$f."]&gt;</span>"));
			} else {
				$html = PtoBR(propre("<span class='ressource'>&lt;".$url."&gt;</span>"));
			}
		}
	}

	return '<html>'.$html.'</html>';
}

?>
lien.php000066600000026477152522474730006243 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

include_spip('base/abstract_sql');

//
// Production de la balise A+href a partir des raccourcis [xxx->url] etc.
// Note : complique car c'est ici qu'on applique typo(),
// et en plus on veut pouvoir les passer en pipeline
//

function inc_lien_dist($lien, $texte='', $class='', $title='', $hlang='', $rel='', $connect='', $env=array()) {

	return $lien;
}

// Regexp des raccourcis, aussi utilisee pour la fusion de sauvegarde Spip
// Laisser passer des paires de crochets pour la balise multi
// mais refuser plus d'imbrications ou de mauvaises imbrications
// sinon les crochets ne peuvent plus servir qu'a ce type de raccourci
define('_RACCOURCI_LIEN', "/\[([^][]*?([[]\w*[]][^][]*)*)->(>?)([^]]*)\]/msS");

// http://doc.spip.org/@expanser_liens
function expanser_liens($t, $connect='', $env=array()){

	$t = pipeline('pre_liens', $t);

	// on passe a traiter_modeles la liste des liens reperes pour lui permettre
	// de remettre le texte d'origine dans les parametres du modele
	$t = traiter_modeles($t, false, false, $connect);
	return $t;
}

// Meme analyse mais pour eliminer les liens
// et ne laisser que leur titre, a expliciter si ce n'est fait
// http://doc.spip.org/@nettoyer_raccourcis_typo
function nettoyer_raccourcis_typo($texte, $connect='')
{


	return $texte;
}



// Repere dans la partie texte d'un raccourci [texte->...]
// la langue et la bulle eventuelles
// http://doc.spip.org/@traiter_raccourci_lien_atts
function traiter_raccourci_lien_atts($texte) {
	$bulle='';
	$hlang='';
	return array(trim($texte), $bulle, $hlang);
}


define('_RACCOURCI_CHAPO', '/^(\W*)(\W*)(\w*\d+([?#].*)?)$/');
/**
 * Fonction pour les champs virtuels de redirection qui peut etre:
 * 3. une URL std
 *
 * renvoie l'url reelle de redirection si le $url=true,
 * l'url brute contenue dans le chapo sinon
 *
 * http://doc.spip.org/@chapo_redirige
 *
 * @param string $virtuel
 * @param bool $url
 * @return string
 */
function virtuel_redirige($virtuel, $url=false){
	return $virtuel;
}


// Cherche un lien du type [->raccourci 123]
// associe a une fonction generer_url_raccourci() definie explicitement 
// ou implicitement par le jeu de type_urls courant.
//
// Valeur retournee selon le parametre $pour:
// 'tout' : tableau d'index url,class,titre,lang (vise <a href="U" class='C' hreflang='L'>T</a>)
// 'titre': seulement T ci-dessus (i.e. le TITRE ci-dessus ou dans table SQL)
// 'url':   seulement U  (i.e. generer_url_RACCOURCI)

// http://doc.spip.org/@calculer_url
function calculer_url ($ref, $texte='', $pour='url', $connect='', $echappe_typo = true) {
	$r = traiter_lien_implicite($ref, $texte, $pour, $connect, $echappe_typo);
	return $r ? $r : traiter_lien_explicite($ref, $texte, $pour, $connect, $echappe_typo);
}

define('_EXTRAIRE_LIEN', ',^\s*(?:' . _PROTOCOLES_STD . '):?/?/?\s*$,iS');

// http://doc.spip.org/@traiter_lien_explicite
function traiter_lien_explicite ($ref, $texte='', $pour='url', $connect='', $echappe_typo = true)
{
	if (preg_match(_EXTRAIRE_LIEN, $ref))
		return ($pour != 'tout') ? '' : array('','','','');

	$lien = entites_html(trim($ref));

	// Liens explicites
	if (!$texte) {
		$texte = str_replace('"', '', $lien);
		// evite l'affichage de trops longues urls.
		$lien_court = charger_fonction('lien_court', 'inc');
		$texte = $lien_court($texte);
		if ($echappe_typo){
			$texte = "<html>".quote_amp($texte)."</html>";
		}
	}

	// petites corrections d'URL
	if (preg_match('/^www\.[^@]+$/S',$lien))
		$lien = "http://".$lien;
	else if (strpos($lien, "@") && email_valide($lien)) {
		if (!$texte) $texte = $lien;
		$lien = "mailto:".$lien;
	}
	
	if ($pour == 'url') return $lien;

	if ($pour == 'titre') return $texte;

	return array('url' => $lien, 'titre' => $texte);
}

function liens_implicite_glose_dist($texte,$id,$type,$args,$ancre,$connect=''){
	if (function_exists($f = 'glossaire_' . $ancre))
		$url = $f($texte, $id);
	else
		$url = glossaire_std($texte);
	return $url;
}

// http://doc.spip.org/@traiter_lien_implicite
function traiter_lien_implicite ($ref, $texte='', $pour='url', $connect='')
{
	if (!($match = typer_raccourci($ref))) return false;
	@list($type,,$id,,$args,,$ancre) = $match;
# attention dans le cas des sites le lien doit pointer non pas sur
# la page locale du site, mais directement sur le site lui-meme
	if ($f = charger_fonction("implicite_$type","liens",true))
		$url = $f($texte,$id,$type,$args,$ancre,$connect);
	if (!$url)
		$url = generer_url_entite($id,$type,$args,$ancre,$connect ? $connect : NULL);
	if (!$url) return false;
	if (is_array($url)) {
		@list($type,$id) = $url;
		$url = generer_url_entite($id,$type,$args,$ancre,$connect ? $connect : NULL);
	}
	if ($pour === 'url') return $url;
	$r = traiter_raccourci_titre($id, $type, $connect);
	if ($r) $r['class'] =  ($type == 'site')?'spip_out':'spip_in';
	if ($texte = trim($texte)) $r['titre'] = $texte;
	if (!@$r['titre']) $r['titre'] =  _T($type) . " $id";
	if ($pour=='titre') return $r['titre'];
	$r['url'] = $url;

	// dans le cas d'un lien vers un doc, ajouter le type='mime/type'
	if ($type == 'document'
	AND $mime = sql_getfetsel('mime_type', 'spip_types_documents',
			"extension IN (".sql_get_select("extension","spip_documents","id_document=".sql_quote($id)).")",
			'','','','',$connect)
	)
		$r['mime'] = $mime;

	return $r;
}

// analyse des raccourcis issus de [TITRE->RACCOURCInnn] et connexes

define('_RACCOURCI_URL', '/^\s*(\w*?)\s*(\d+)(\?(.*?))?(#([^\s]*))?\s*$/S');

// http://doc.spip.org/@typer_raccourci
function typer_raccourci ($lien) {
	if (!preg_match(_RACCOURCI_URL, $lien, $match)) return array();
	$f = $match[1];
	// valeur par defaut et alias historiques
	if (!$f) $f = 'article';
	else if ($f == 'art') $f = 'article';
	else if ($f == 'br') $f = 'breve';
	else if ($f == 'rub') $f = 'rubrique';
	else if ($f == 'aut') $f = 'auteur';
	else if ($f == 'doc' OR $f == 'im' OR $f == 'img' OR $f == 'image' OR $f == 'emb')
		$f = 'document';
	else if (preg_match('/^br..?ve$/S', $f)) $f = 'breve'; # accents :(
	$match[0] = $f;
	return $match;
}

// Retourne le champ textuel associe a une cle primaire, et sa langue
function traiter_raccourci_titre($id, $type, $connect=NULL)
{
	$trouver_table = charger_fonction('trouver_table', 'base');
	$desc = $trouver_table(table_objet($type));
	if (!($desc AND $s = $desc['titre'])) return array();
	$_id = $desc['key']['PRIMARY KEY'];
	$r = sql_fetsel($s, $desc['table'], "$_id=$id", '','','','',$connect);
	if (!$r) return array();
	$r['titre'] = supprimer_numero($r['titre']);
	if (!$r['titre']) $r['titre'] = $r['surnom'];
	if (!isset($r['lang'])) $r['lang'] = '';
	return $r;
}

// traite les modeles (dans la fonction typo), en remplacant
// le raccourci <modeleN|parametres> par la page calculee a
// partir du squelette modeles/modele.html
// Le nom du modele doit faire au moins trois caracteres (evite <h2>)
// Si $doublons==true, on repere les documents sans calculer les modeles
// mais on renvoie les params (pour l'indexation par le moteur de recherche)
// http://doc.spip.org/@traiter_modeles

define('_RACCOURCI_MODELE', 
	 '(<([a-z_-]{3,})' # <modele
	.'\s*([0-9]*)\s*' # id
	.'([|](?:<[^<>]*>|[^>])*?)?' # |arguments (y compris des tags <...>)
	.'\s*/?'.'>)' # fin du modele >
	.'\s*(<\/a>)?' # eventuel </a>
       );

define('_RACCOURCI_MODELE_DEBUT', '@^' . _RACCOURCI_MODELE .'@isS');

// http://doc.spip.org/@traiter_modeles
function traiter_modeles($texte, $doublons=false, $echap='', $connect='', $liens = null, $env = array()) {
	// preserver la compatibilite : true = recherche des documents
	if ($doublons===true)
		$doublons = array('documents'=>array('doc','emb','img'));
	// detecter les modeles (rapide)
	if (strpos($texte,"<")!==false AND
	  preg_match_all('/<[a-z_-]{3,}\s*[0-9|]+/iS', $texte, $matches, PREG_SET_ORDER)) {
		include_spip('public/assembler');
		$wrap_embed_html = charger_fonction("wrap_embed_html","inc",true);
		foreach ($matches as $match) {
			// Recuperer l'appel complet (y compris un eventuel lien)

			$a = strpos($texte,$match[0]);
			preg_match(_RACCOURCI_MODELE_DEBUT,
			substr($texte, $a), $regs);
			$regs[]=""; // s'assurer qu'il y a toujours un 5e arg, eventuellement vide
			list(,$mod, $type, $id, $params, $fin) = $regs;
			if ($fin AND
			preg_match('/<a\s[^<>]*>\s*$/i',
					substr($texte, 0, $a), $r)) {
				$lien = array(
					'href' => extraire_attribut($r[0],'href'),
					'class' => extraire_attribut($r[0],'class'),
					'mime' => extraire_attribut($r[0],'type'),
					'title' => extraire_attribut($r[0],'title'),
					'hreflang' => extraire_attribut($r[0],'hreflang')
				);
				$n = strlen($r[0]);
				$a -= $n;
				$cherche = $n + strlen($regs[0]);
			} else {
				$lien = false;
				$cherche = strlen($mod);
			}

			// calculer le modele
			# hack indexation
			if ($doublons)
				$texte .= preg_replace(',[|][^|=]*,s',' ',$params);
			# version normale
			else {
				// si un tableau de liens a ete passe, reinjecter le contenu d'origine
				// dans les parametres, plutot que les liens echappes
				if (!is_null($liens))
					$params = str_replace($liens[0], $liens[1], $params);
				$modele = inclure_modele($type, $id, $params, $lien, $connect, $env);
				// en cas d'echec, 
				// si l'objet demande a une url, 
				// creer un petit encadre vers elle
				if ($modele === false) {
					if (!$lien)
						$lien = traiter_lien_implicite("$type$id", '', 'tout', $connect);
					if ($lien)
						$modele = '<a href="'
						  .$lien['url']
						  .'" class="spip_modele'
						  . '">'
						  .sinon($lien['titre'], _T('ecrire:info_sans_titre'))
						  ."</a>";
					else {
						$modele = "";
						if (test_espace_prive()) {
							$modele = entites_html(substr($texte,$a,$cherche));
							if (!is_null($liens))
								$modele = "<pre>".str_replace($liens[0], $liens[1], $modele)."</pre>";
						}
					}
				}
				// le remplacer dans le texte
				if ($modele !== false) {
					$modele = protege_js_modeles($modele);
					if ($wrap_embed_html){
						$modele = $wrap_embed_html($mod,$modele);
					}
					$rempl = code_echappement($modele, $echap);
					$texte = substr($texte, 0, $a)
						. $rempl
						. substr($texte, $a+$cherche);
				}
			}

			// hack pour tout l'espace prive
			if (((!_DIR_RESTREINT) OR ($doublons)) AND ($id)){
				foreach($doublons?$doublons:array('documents'=>array('doc','emb','img')) as $quoi=>$modeles)
					if (in_array($type,$modeles))
						$GLOBALS["doublons_{$quoi}_inclus"][] = $id;
			}
		}
	}

	return $texte;
}

//
// Raccourcis ancre [#ancre<-]
//
// http://doc.spip.org/@traiter_raccourci_ancre
function traiter_raccourci_ancre($letexte)
{
	return $letexte;
}

// http://doc.spip.org/@traiter_raccourci_glossaire
function traiter_raccourci_glossaire($texte)
{
	return $texte;
}

// http://doc.spip.org/@glossaire_std
function glossaire_std($terme)
{
	return  $terme;
}

?>
texte.php000066600000021773152522474730006437 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

include_spip('inc/texte_mini');
include_spip('inc/lien');

/*************************************************************************************************************************
 * Fonctions inutilisees en dehors de inc/texte
 *
 */

// Raccourcis dependant du sens de la langue
function definir_raccourcis_alineas(){
	return array('','');
}


//
// Tableaux
//
// http://doc.spip.org/@traiter_tableau
function traiter_tableau($bloc) {
	return $bloc;
}


//
// Traitement des listes (merci a Michael Parienti)
//
// http://doc.spip.org/@traiter_listes
function traiter_listes ($texte) {
	return $texte;
}

// Nettoie un texte, traite les raccourcis autre qu'URL, la typo, etc.
// http://doc.spip.org/@traiter_raccourcis
function traiter_raccourcis($letexte) {

	// Appeler les fonctions de pre_traitement
	$letexte = pipeline('pre_propre', $letexte);

	// APPELER ICI UN PIPELINE traiter_raccourcis ?
	// $letexte = pipeline('traiter_raccourcis', $letexte);

	// Appeler les fonctions de post-traitement
	$letexte = pipeline('post_propre', $letexte);

	return $letexte;
}

/*************************************************************************************************************************
 * Fonctions utilisees en dehors de inc/texte
 */

// afficher joliment les <script>
// http://doc.spip.org/@echappe_js
function echappe_js($t,$class=' class="echappe-js"') {
	if (preg_match_all(',<script.*?($|</script.),isS', $t, $r, PREG_SET_ORDER))
	foreach ($r as $regs)
		$t = str_replace($regs[0],
			"<code$class>".nl2br(spip_htmlspecialchars($regs[0])).'</code>',
			$t);
	return $t;
}



/**
 * Empêcher l'exécution de code PHP et JS
 *
 * Sécurité : empêcher l'exécution de code PHP, en le transformant en joli code
 * dans l'espace privé. Cette fonction est aussi appelée par propre et typo.
 * 
 * De la même manière, la fonction empêche l'exécution de JS mais selon le mode
 * de protection déclaré par la globale filtrer_javascript :
 * - -1 : protection dans l'espace privé et public
 * - 0  : protection dans l'espace public
 * - 1  : aucune protection
 * 
 * Il ne faut pas désactiver globalement la fonction dans l'espace privé car elle protège
 * aussi les balises des squelettes qui ne passent pas forcement par propre ou typo après
 * si elles sont appelées en direct
 * 
 * @param string $arg
 *     Code à protéger
 * @return string
 *     Code protégé
**/
function interdire_scripts($arg) {
	// on memorise le resultat sur les arguments non triviaux
	static $dejavu = array();

	// Attention, si ce n'est pas une chaine, laisser intact
	if (!$arg OR !is_string($arg) OR !strstr($arg, '<')) return $arg; 

	if (isset($dejavu[$GLOBALS['filtrer_javascript']][$arg])) return $dejavu[$GLOBALS['filtrer_javascript']][$arg];

	// echapper les tags asp/php
	$t = str_replace('<'.'%', '&lt;%', $arg);

	// echapper le php
	$t = str_replace('<'.'?', '&lt;?', $t);

	// echapper le < script language=php >
	$t = preg_replace(',<(script\b[^>]+\blanguage\b[^\w>]+php\b),UimsS', '&lt;\1', $t);

	// Pour le js, trois modes : parano (-1), prive (0), ok (1)
	switch($GLOBALS['filtrer_javascript']) {
		case 0:
			if (!_DIR_RESTREINT)
				$t = echappe_js($t);
			break;
		case -1:
			$t = echappe_js($t);
			break;
	}

	// pas de <base href /> svp !
	$t = preg_replace(',<(base\b),iS', '&lt;\1', $t);

	// Reinserer les echappements des modeles
	if (defined('_PROTEGE_JS_MODELES'))
		$t = echappe_retour($t,"javascript"._PROTEGE_JS_MODELES);
	if (defined('_PROTEGE_PHP_MODELES'))
		$t = echappe_retour($t,"php"._PROTEGE_PHP_MODELES);

	return $dejavu[$GLOBALS['filtrer_javascript']][$arg] = $t;
}

// Typographie generale
// avec protection prealable des balises HTML et SPIP

// http://doc.spip.org/@typo
function typo($letexte, $echapper=true, $connect=null, $env=array()) {
	// Plus vite !
	if (!$letexte) return $letexte;

	// les appels directs a cette fonction depuis le php de l'espace
	// prive etant historiquement ecrit sans argment $connect
	// on utilise la presence de celui-ci pour distinguer les cas
	// ou il faut passer interdire_script explicitement
	// les appels dans les squelettes (de l'espace prive) fournissant un $connect
	// ne seront pas perturbes
	$interdire_script = false;
	if (is_null($connect)){
		$connect = '';
		$interdire_script = true;
		$env['espace_prive'] = 1;
	}

	// Echapper les codes <html> etc
	if ($echapper)
		$letexte = echappe_html($letexte, 'TYPO');

	//
	// Installer les modeles, notamment images et documents ;
	//
	// NOTE : propre() ne passe pas par ici mais directement par corriger_typo
	// cf. inc/lien

	$letexte = traiter_modeles($mem = $letexte, false, $echapper ? 'TYPO' : '', $connect, null, $env);
	if ($letexte != $mem) $echapper = true;
	unset($mem);

	$letexte = corriger_typo($letexte);
	$letexte = echapper_faux_tags($letexte);

	// reintegrer les echappements
	if ($echapper)
		$letexte = echappe_retour($letexte, 'TYPO');

	// Dans les appels directs hors squelette, securiser ici aussi
	if ($interdire_script)
		$letexte = interdire_scripts($letexte);

	// Dans l'espace prive on se mefie de tout contenu dangereux
	// https://core.spip.net/issues/3371
	if (isset($env['espace_prive']) AND $env['espace_prive']){
		$letexte = echapper_html_suspect($letexte);
	}

	return $letexte;
}

// Correcteur typographique
define('_TYPO_PROTEGER', "!':;?~%-");
define('_TYPO_PROTECTEUR', "\x1\x2\x3\x4\x5\x6\x7\x8");

define('_TYPO_BALISE', ",</?[a-z!][^<>]*[".preg_quote(_TYPO_PROTEGER)."][^<>]*>,imsS");

// http://doc.spip.org/@corriger_typo
function corriger_typo($letexte, $lang='') {

	// Plus vite !
	if (!$letexte) return $letexte;

	$letexte = pipeline('pre_typo', $letexte);

	// Caracteres de controle "illegaux"
	$letexte = corriger_caracteres($letexte);

	// Proteger les caracteres typographiques a l'interieur des tags html
	if (preg_match_all(_TYPO_BALISE, $letexte, $regs, PREG_SET_ORDER)) {
		foreach ($regs as $reg) {
			$insert = $reg[0];
			// hack: on transforme les caracteres a proteger en les remplacant
			// par des caracteres "illegaux". (cf corriger_caracteres())
			$insert = strtr($insert, _TYPO_PROTEGER, _TYPO_PROTECTEUR);
			$letexte = str_replace($reg[0], $insert, $letexte);
		}
	}

	// trouver les blocs multi et les traiter a part
	$letexte = extraire_multi($e = $letexte, $lang, true);
	$e = ($e === $letexte);

	// Charger & appliquer les fonctions de typographie
	$typographie = charger_fonction(lang_typo($lang), 'typographie');
	$letexte = $typographie($letexte);

	// Les citations en une autre langue, s'il y a lieu
	if (!$e) $letexte = echappe_retour($letexte, 'multi');

	// Retablir les caracteres proteges
	$letexte = strtr($letexte, _TYPO_PROTECTEUR, _TYPO_PROTEGER);

	// pipeline
	$letexte = pipeline('post_typo', $letexte);

	# un message pour abs_url - on est passe en mode texte
	$GLOBALS['mode_abs_url'] = 'texte';

	return $letexte;
}




//
// Une fonction pour fermer les paragraphes ; on essaie de preserver
// des paragraphes indiques a la main dans le texte
// (par ex: on ne modifie pas un <p align='center'>)
//
// deuxieme argument : forcer les <p> meme pour un seul paragraphe
//
// http://doc.spip.org/@paragrapher
// /!\ appelee dans inc/filtres et public/composer
function paragrapher($letexte, $forcer=true) {
	return $letexte;
}

// Harmonise les retours chariots et mange les paragraphes html
// http://doc.spip.org/@traiter_retours_chariots
// ne sert plus
function traiter_retours_chariots($letexte) {
	$letexte = preg_replace(",\r\n?,S", "\n", $letexte);
	$letexte = preg_replace(",<p[>[:space:]],iS", "\n\n\\0", $letexte);
	$letexte = preg_replace(",</p[>[:space:]],iS", "\\0\n\n", $letexte);
	return $letexte;
}


// Filtre a appliquer aux champs du type #TEXTE*
// http://doc.spip.org/@propre
function propre($t, $connect=null, $env=array()) {
	// les appels directs a cette fonction depuis le php de l'espace
	// prive etant historiquement ecrits sans argment $connect
	// on utilise la presence de celui-ci pour distinguer les cas
	// ou il faut passer interdire_script explicitement
	// les appels dans les squelettes (de l'espace prive) fournissant un $connect
	// ne seront pas perturbes
	$interdire_script = false;
	if (is_null($connect)){
		$connect = '';
		$interdire_script = true;
	}

	if (!$t) return strval($t);

	$t = echappe_html($t);
	$t = expanser_liens($t,$connect, $env);
	$t = traiter_raccourcis($t);
	$t = echappe_retour_modeles($t, $interdire_script);

	return $t;
}

?>
forum_insert.php000066600000013157152522475200010010 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

if (!defined("_ECRIRE_INC_VERSION")) return;
include_spip('inc/forum');
include_spip('inc/filtres');
include_spip('inc/actions');

// Ce fichier est inclus par dist/formulaires/forum.php

// http://code.spip.net/@mots_du_forum
function mots_du_forum($ajouter_mot, $id_message)
{
	include_spip('action/editer_mot');
	mot_associer($ajouter_mot, array('forum'=>$id_message));
}



// http://code.spip.net/@tracer_erreur_forum
function tracer_erreur_forum($type='') {
	spip_log("erreur forum ($type): ".print_r($_POST, true));

	define('_TRACER_ERREUR_FORUM', false);
	if (_TRACER_ERREUR_FORUM) {
		$envoyer_mail = charger_fonction('envoyer_mail','inc');
		$envoyer_mail($GLOBALS['meta']['email_webmaster'], "erreur forum ($type)",
			"erreur sur le forum ($type) :\n\n".
			'$_POST = '.print_r($_POST, true)."\n\n".
			'$_SERVER = '.print_r($_SERVER, true));
	}
}

/**
 * Un parametre permet de forcer le statut (exemple: plugin antispam)
 *
 * http://code.spip.net/@inc_forum_insert_dist
 *
 * @param $objet
 * @param $id_objet
 * @param $id_forum
 *   en reponse a
 * @param null $force_statut
 * @return bool
 */
function inc_forum_insert_dist($objet, $id_objet, $id_forum, $force_statut = NULL) {

	if (!in_array($force_statut,array('privrac','privadm'))){
		if (!strlen($objet)
		  OR !intval($id_objet)){
			spip_log("Erreur insertion forum sur objet='$objet', id_objet=$id_objet",'forum.'. _LOG_ERREUR);
			return 0;
		}
	}
	spip_log("insertion de forum $force_statut sur $objet $id_objet (+$id_forum)", 'forum');



	include_spip('inc/filtres');
	include_spip('inc/modifier');
	$champs = objet_info('forum','champs_editables');
	$c = collecter_requests($champs, array());
	
	$c['statut'] = 'off';
	$c['objet'] = $objet;
	$c['id_objet'] = $id_objet;
	$c['auteur'] = sinon($GLOBALS['visiteur_session']['nom'],
		$GLOBALS['visiteur_session']['session_nom']);
	$c['email_auteur'] = sinon($GLOBALS['visiteur_session']['email'],
		$GLOBALS['visiteur_session']['session_email']);

	$c = pipeline('pre_edition',array(
		'args'=>array(
				'table' => 'spip_forum',
				'id_objet' => $id_forum,
				'action'=>'instituer'
		),
		'data'=>forum_insert_statut($c, $force_statut)
	));

	$id_reponse = forum_insert_base($c, $id_forum, $objet, $id_objet, $c['statut'], _request('ajouter_mot'));

	if (!$id_reponse)
		spip_log("Echec insertion forum sur $objet $id_objet (+$id_forum)", 'forum.'._LOG_ERREUR);
	else
		spip_log("forum insere' $id_reponse sur $objet $id_objet (+$id_forum)", 'forum');

	return $id_reponse;

}

// http://code.spip.net/@forum_insert_base
function forum_insert_base($c, $id_forum, $objet, $id_objet, $statut, $ajouter_mot = false)
{

	if (!in_array($statut,array('privrac','privadm'))){
		// si le statut est vide, c'est qu'on ne veut pas de ce presume spam !
		if (!$statut OR !$objet OR !$id_objet){
			$args = func_get_args();
			spip_log("Erreur sur forum_insert_base ".var_export($args,1),'forum.'. _LOG_ERREUR);
			return false;
		}
	}

	// Entrer le message dans la base
	$id_reponse = sql_insertq('spip_forum', array(
		'date_heure'=> date('Y-m-d H:i:s'),
		'ip' => $GLOBALS['ip'],
		'id_auteur' => $GLOBALS['visiteur_session']['id_auteur']
	));

	if ($id_reponse){
		if ($id_forum>0) {
			$id_thread = sql_getfetsel("id_thread", "spip_forum", "id_forum =".intval($id_forum));
		}
		else
			$id_thread = $id_reponse; # id_thread oblige INSERT puis UPDATE.

		// Entrer les cles
		sql_updateq('spip_forum', array('id_parent' => $id_forum, 'objet' => $objet, 'id_objet' => $id_objet, 'id_thread' => $id_thread, 'statut' => $statut), "id_forum=".intval($id_reponse));

		// Entrer les mots-cles associes
		if ($ajouter_mot) mots_du_forum($ajouter_mot, $id_reponse);

		//
		// Entree du contenu et invalidation des caches
		//
		include_spip('action/editer_forum');
		revision_forum($id_reponse, $c);

		// Ajouter un document
		if (isset($_FILES['ajouter_document'])
		AND $_FILES['ajouter_document']['tmp_name']) {
			$files[] = array('tmp_name'=>$_FILES['ajouter_document']['tmp_name'],'name'=>$_FILES['ajouter_document']['name']);
			$ajouter_documents = charger_fonction('ajouter_documents','action');
			$ajouter_documents(
				'new',
				$files,
				'forum',
				$id_reponse,
				'document');
			// supprimer le temporaire et ses meta donnees
			spip_unlink($_FILES['ajouter_document']['tmp_name']);
			spip_unlink(preg_replace(',\.bin$,',
				'.txt', $_FILES['ajouter_document']['tmp_name']));
		}

		// Notification
		$quoi = (strncmp($statut,'priv',4)==0?'forumprive':'forumposte');
		if ($notifications = charger_fonction('notifications', 'inc'))
			$notifications($quoi, $id_reponse);
	}

	return $id_reponse;
}


// http://code.spip.net/@forum_insert_statut
function forum_insert_statut($champs, $forcer_statut=NULL)
{
	include_spip('inc/forum');
	$statut = controler_forum($champs['objet'], $champs['id_objet']);

	if ($forcer_statut !== NULL)
		$champs['statut'] = $forcer_statut;
	else
		$champs['statut'] = ($statut == 'non') ? 'off' : (($statut == 'pri') ? 'prop' :	'publie');

	return $champs;
}

?>
article_accepter_forums_publics.php000066600000001113152522475200013666 0ustar00<?php

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

/**
 * Moderation des forums specifique a un article
 * @param int $id_objet identifiant de l'article
 * @return string  : "non", "pos"(teriori), "pri"(ori), "abo"(nnement)
 */
function inc_article_accepter_forums_publics_dist($id_objet) {
	$accepter_forum = $GLOBALS['meta']["forums_publics"];
	$art_accepter_forum = sql_getfetsel('accepter_forum', 'spip_articles', array(
		"id_article = ". intval($id_objet)
	));
	if ($art_accepter_forum) {
		$accepter_forum = $art_accepter_forum;
	}
	return substr($accepter_forum, 0, 3);
}


?>
email_notification_forum.php000066600000010134152522475200012331 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

/**
 * Construitre l'email personalise de notification d'un forum
 *
 * @param array $t
 * @param string $email
 * @param array $contexte
 * @return string
 */
function inc_email_notification_forum_dist ($t, $email, $contexte=array()) {
	static $contextes_store = array();

	if(!isset($contextes_store[$t['id_forum']])){
		$url = '';
		$id_forum = $t['id_forum'];

		if ($t['statut'] == 'prive') # forum prive
		{
			if ($t['id_objet'])
				$url = generer_url_entite($t['id_objet'], $t['objet'], '', 'forum'.$id_forum, false);
		}
		else if ($t['statut'] == 'privrac') # forum general
		{
			$url = generer_url_ecrire('forum').'#forum'.$id_forum;
		}
		else if ($t['statut'] == 'privadm') # forum des admins
		{
			$url = generer_url_ecrire('forum','quoi=admin').'#forum'.$id_forum;
		}
		else if ($t['statut'] == 'publie') # forum publie
		{
			$url = generer_url_entite($id_forum, 'forum', '', 'forum'.$id_forum, true);
		}
		else #  forum modere, spam, poubelle direct ....
		{
			$url = generer_url_ecrire('controler_forum', "debut_id_forum=".$id_forum);
		}

		if (!$url) {
			spip_log("forum $id_forum sans referent",'notifications');
			$url = './';
		}
		if ($t['id_objet']) {
			include_spip('inc/filtres');
			$t['titre_source'] = generer_info_entite($t['id_objet'], $t['objet'], 'titre');
		}

		$t['url'] = $url;

		// detecter les url des liens du forum
		// pour la moderation (permet de reperer les SPAMS avec des liens caches)
		// il faut appliquer le traitement de raccourci car sinon on rate des liens sous forme [->..] utilises par les spammeurs !
		include_spip("public/interfaces");
		$table_objet = "forum";

		$links = array();
		foreach ($t as $champ=>$v){
			$champ = strtoupper($champ);
			$traitement = (isset($GLOBALS['table_des_traitements'][$champ])?$GLOBALS['table_des_traitements'][$champ]:null);
			if (is_array($traitement)
			  AND (isset($traitement[$table_objet]) OR isset($traitement[0]))){
				$traitement = $traitement[isset($traitement[$table_objet]) ? $table_objet : 0];
				$traitement = str_replace('%s', "'".texte_script($v)."'", $traitement);
				eval("\$v = $traitement;");
			}

			$links = $links + extraire_balises($v,'a');
		}
		$links = extraire_attribut($links,'href');
		$links = implode("\n",$links);
		$t['liens'] = $links;

		$contextes_store[$t['id_forum']] = $t;
	}

	$fond = "notifications/forum_poste";
	if (isset($contexte['fond'])){
		$fond = $contexte['fond'];
		unset($contexte['fond']);
	}
	$t = array_merge($contextes_store[$t['id_forum']],$contexte);
		// Rechercher eventuellement la langue du destinataire
	if (NULL !== ($l = sql_getfetsel('lang', 'spip_auteurs', "email=" . sql_quote($email))))
		$l = lang_select($l);

	$parauteur = (strlen($t['auteur']) <= 2) ? '' :
		(" " ._T('forum_par_auteur', array(
			'auteur' => $t['auteur'])
		) .
		 ($t['email_auteur'] ? ' <' . $t['email_auteur'] . '>' : ''));

	$titre = textebrut(typo($t['titre_source']));
	if ($titre){
		$forum_poste_par = _T(
			$t['objet']=='article'?'forum:forum_poste_par':'forum:forum_poste_par_generique',
			array('parauteur' => $parauteur, 'titre' => $titre, 'objet'=>$t['objet'])
		);
	}
	else
		$forum_poste_par = _T('forum:forum_poste_par_court',array('parauteur' => $parauteur));

	$t['par_auteur'] = $forum_poste_par;

	$envoyer_mail = charger_fonction('envoyer_mail','inc'); // pour nettoyer_titre_email
	$corps = recuperer_fond($fond,$t);

	if ($l)
		lang_select();

	return $corps;
}
forum.php000066600000022727152522475200006427 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

if (!defined("_ECRIRE_INC_VERSION")) return;
include_spip('inc/actions');

/**
 * recuperer le critere SQL qui selectionne nos forums
 * http://code.spip.net/@critere_statut_controle_forum
 *
 * @param string $type
 * @param int|array $id_secteur
 * @param string $recherche
 * @return array
 */
function critere_statut_controle_forum($type, $id_secteur=0, $recherche='') {

	if (!$id_secteur) {
		$from = 'spip_forum AS F';
		$where = "";
		$and = "";
	} else {
		if (!is_array($id_secteur)){
			$id_secteur = explode(',',$id_secteur);
		}
		$from = 'spip_forum AS F, spip_articles AS A';
		$where = sql_in("A.id_secteur",$id_secteur)." AND F.objet='article' AND F.id_objet=A.id_article";
		$and = ' AND ';
	}
   
	switch ($type) {
		case 'public':
			$and .= "F.statut IN ('publie', 'off', 'prop', 'spam') AND F.texte!=''";
			break;
		case 'prop':
			$and .= "F.statut='prop'";
			break;
		case 'spam':
			$and .= "F.statut='spam'";
			break;
		case 'interne':
			$and .= "F.statut IN ('prive', 'privrac', 'privoff', 'privadm') AND F.texte!=''";
			break;
		case 'vide':
			$and .= "F.statut IN ('publie', 'off', 'prive', 'privrac', 'privoff', 'privadm') AND F.texte=''";
			break;
		default:
			$where = '0=1';
			$and ='';
			break;
	}

	if ($recherche) {
		# recherche par IP
		if (preg_match(',^\d+\.\d+\.(\*|\d+\.(\*|\d+))$,', $recherche)) {
			$and .= " AND ip LIKE ".sql_quote(str_replace('*', '%', $recherche));
		} else {
			include_spip('inc/rechercher');
			if ($a = recherche_en_base($recherche, 'forum'))
				$and .= " AND ".sql_in('id_forum',
					array_keys(array_pop($a)));
			else
				$and .= " AND 0=1";
		}
	}

	return array($from, "$where$and");
}

// Index d'invalidation des forums
// obsolete, remplace par l'appel systematique a 2 invalideurs :
// - forum/id_forum
// - objet/id_objet
// http://code.spip.net/@calcul_index_forum
function calcul_index_forum($objet,$id_objet) {
	return substr($objet,0,1).$id_objet;
}

//
// Recalculer tous les threads
//
// http://code.spip.net/@calculer_threads
function calculer_threads() {
	// fixer les id_thread des debuts de discussion
	sql_update('spip_forum', array('id_thread'=>'id_forum'), "id_parent=0");
	// reparer les messages qui n'ont pas l'id_secteur de leur parent
	do {
		$discussion = "0";
		$precedent = 0;
		$r = sql_select("fille.id_forum AS id,	maman.id_thread AS thread", 'spip_forum AS fille, spip_forum AS maman', "fille.id_parent = maman.id_forum AND fille.id_thread <> maman.id_thread",'', "thread");
		while ($row = sql_fetch($r)) {
			if ($row['thread'] == $precedent)
				$discussion .= "," . $row['id'];
			else {
				if ($precedent)
					sql_updateq("spip_forum", array("id_thread" => $precedent), "id_forum IN ($discussion)");
				$precedent = $row['thread'];
				$discussion = $row['id'];
			}
		}
		sql_updateq("spip_forum", array("id_thread" => $precedent), "id_forum IN ($discussion)");
	} while ($discussion != "0");
}

// Calculs des URLs des forums (pour l'espace public)
// http://code.spip.net/@racine_forum
function racine_forum($id_forum){
	if (!$id_forum = intval($id_forum)) return false;

	$row = sql_fetsel("id_parent, objet, id_objet, id_thread", "spip_forum", "id_forum=".$id_forum);

	if (!$row) return false;

	if ($row['id_parent']
	AND $row['id_thread'] != $id_forum) // eviter boucle infinie
		return racine_forum($row['id_thread']);

	return array($row['objet'], $row['id_objet'], $id_forum);
} 


// http://code.spip.net/@parent_forum
function parent_forum($id_forum) {
	if (!$id_forum = intval($id_forum)) return;
	$row = sql_fetsel("id_parent, objet, id_objet", "spip_forum", "id_forum=".$id_forum);
	if(!$row) return array();
	if ($row['id_parent'])
		return array('forum', $row['id_parent']);
	else
		return array($row['objet'], $row['id_objet']);
} 


/**
 * Pour compatibilite : remplacer les appels par son contenu
 *
 * @param unknown_type $id_forum
 * @param unknown_type $args
 * @param unknown_type $ancre
 * @return unknown
 */
function generer_url_forum_dist($id_forum, $args='', $ancre='') {
	$generer_url_forum = charger_fonction('generer_url_forum','urls');
	return $generer_url_forum($id_forum, $args, $ancre);
}


// http://code.spip.net/@generer_url_forum_parent
function generer_url_forum_parent($id_forum) {
	if ($id_forum = intval($id_forum)) {
		list($type, $id) = parent_forum($id_forum);
		if ($type)
			return generer_url_entite($id, $type);
	}
	return '';
} 


// Quand on edite un forum, on tient a conserver l'original
// sous forme d'un forum en reponse, de statut 'original'
// http://code.spip.net/@conserver_original
function conserver_original($id_forum) {
	$s = sql_fetsel("id_forum", "spip_forum", "id_parent=".intval($id_forum)." AND statut='original'");

	if ($s)	return ''; // pas d'erreur

	// recopier le forum
	$t = sql_fetsel("*", "spip_forum", "id_forum=".intval($id_forum));

	if ($t) {
		unset($t['id_forum']);
		$id_copie = sql_insertq('spip_forum', $t);
		if ($id_copie) {
			sql_updateq('spip_forum', array('id_parent'=> $id_forum, 'statut'=>'original'), "id_forum=$id_copie");
			return ''; // pas d'erreur
		}
	}

	return '&erreur';
}

// appelle conserver_original(), puis modifie le contenu via l'API inc/modifier
// http://code.spip.net/@enregistre_et_modifie_forum
function enregistre_et_modifie_forum($id_forum, $c=false) {
	if ($err = conserver_original($id_forum)) {
		spip_log("erreur de sauvegarde de l'original, $err");
		return;
	}

	include_spip('action/editer_forum');
	return revision_forum($id_forum, $c);
}


/**
 * Trouver le titre d'un objet publie
 * @param string $objet
 * @param int $id_objet
 * @param int $id_forum
 * @param bool $publie
 * @return bool|string
 */
function forum_recuperer_titre_dist($objet, $id_objet, $id_forum=0, $publie = true) {
	include_spip('inc/filtres');
	$titre = "";

	if ($f = charger_fonction($objet.'_forum_extraire_titre', 'inc', true)){
		$titre = $f($id_objet);
	}
	else {
		include_spip('base/objets');
		if ($publie AND !objet_test_si_publie($objet, $id_objet))
			return false;

		$titre = generer_info_entite($id_objet, $objet,'titre','*');
	}

	if ($titre AND $id_forum){
		$titre_m = sql_getfetsel('titre', 'spip_forum', "id_forum = " . intval($id_forum));
		if (!$titre_m) {
			return false; // URL fabriquee
		}
		$titre = $titre_m;
	}

	$titre = supprimer_numero($titre);
	$titre = str_replace('~', ' ', extraire_multi($titre));

	return $titre;
}


/**
 * Retourne pour un couple objet/id_objet donne
 * de quelle maniere les forums sont acceptes dessus
 * non: pas de forum
 * pos: a posteriori, acceptes et eventuellements moderes ensuite
 * pri: a priori, doivent etre valides par un admin
 * abo: les personnes doivent au prealable etre identifiees
 *
 * http://code.spip.net/@controler_forum
 * 
 * @param string $objet
 *   objet a tester
 * @param int $id_objet
 *   identifiant de l'objet
 * @return string
 *   chaine de 3 caractere parmi 'non','pos','pri','abo'
 */
function controler_forum($objet, $id_objet) {
	// Valeur par defaut
	$accepter_forum = $GLOBALS['meta']["forums_publics"];

	// il y a un cas particulier pour l'acceptation de forum d'article...
	if ($f = charger_fonction($objet . '_accepter_forums_publics', 'inc', true)){
		$accepter_forum = $f($id_objet);
	}

	return substr($accepter_forum, 0, 3);
}


/**
 * Verifier la presence du jeton de secu post previsu
 * http://code.spip.net/@forum_insert_noprevisu
 * @return bool
 */
function forum_insert_noprevisu(){
	// simuler une action venant de l'espace public
	// pour se conformer au cas general.
	set_request('action', 'ajout_forum');
	// Creer une session s'il n'y en a pas (cas du postage sans cookie)
	$securiser_action = charger_fonction('securiser_action', 'inc');
	$arg = $securiser_action();

	$file = _DIR_TMP ."forum_" . preg_replace('/[^0-9]/', '', $arg) .".lck";
	if (!file_exists($file)) {
		# ne pas tracer cette erreur, peut etre due a un double POST
		# tracer_erreur_forum('session absente');
		return true;
	}
	unlink($file);

	// antispam : si le champ au nom aleatoire verif_$hash n'est pas 'ok'
	// on meurt
	if (_request('verif_'._request('hash')) != 'ok') {
			tracer_erreur_forum('champ verif manquant');
			return true;
	}
	return false;
}


/**
 * recuperer tous les objets dont on veut pouvoir obtenir l'identifiant
 * directement dans l'environnement
 *
 * @return array
 */
function forum_get_objets_depuis_env() {
	static $objets = null;
	if ($objets === null) {
		// on met une cle (le type d'objet) pour qu'un appel du pipeline
		// puisse facilement soustraire un objet qu'il ne veut pas avec
		// unset($objets['rubrique']) par exemple.
		$objets = pipeline('forum_objets_depuis_env', array(
			'article' => id_table_objet('article'),
			'rubrique' => id_table_objet('rubrique'),
			'site' => id_table_objet('site'),
			'breve' => id_table_objet('breve')
		));
		asort($objets);
	}

	return $objets;
}


// http://code.spip.net/@reduce_strlen
function reduce_strlen($n, $c)
{
  return $n - (is_string($c) ? strlen($c) : 0);
}
?>syndic.php000066600000040473152522550620006566 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

// ATTENTION
// Cette inclusion charge executer_une_syndication pour compatibilite,
// mais cette fonction ne doit plus etre invoquee directement:
// il faut passer par cron() pour avoir un verrou portable
// Voir un exemple dans action/editer/site
include_spip('genie/syndic');

// prend un fichier backend et retourne un tableau des items lus,
// et une chaine en cas d'erreur
// http://code.spip.net/@analyser_backend
function analyser_backend($rss, $url_syndic='') {
	include_spip('inc/texte'); # pour couper()

	$rss = pipeline('pre_syndication', $rss);

	// si true, les URLs de type feedburner sont dereferencees
	define('_SYNDICATION_DEREFERENCER_URL', false);

	// Echapper les CDATA
	cdata_echappe($rss, $echappe_cdata);

	// supprimer les commentaires
	$rss = preg_replace(',<!--.*-->,Ums', '', $rss);

	// simplifier le backend, en supprimant les espaces de nommage type "dc:"
	$rss = preg_replace(',<(/?)(dc):,i', '<\1', $rss);

	// chercher auteur/lang dans le fil au cas ou les items n'en auraient pas
	list($header) = preg_split(',<(item|entry)\b,', $rss, 2);
	if (preg_match_all(
	',<(author|creator)\b(.*)</\1>,Uims',
	$header, $regs, PREG_SET_ORDER)) {
		$les_auteurs_du_site = array();
		foreach ($regs as $reg) {
			$nom = $reg[2];
			if (preg_match(',<name>(.*)</name>,Uims', $nom, $reg))
				$nom = $reg[1];
			$les_auteurs_du_site[] = trim(textebrut(filtrer_entites($nom)));
		}
		$les_auteurs_du_site = join(', ', array_unique($les_auteurs_du_site));
	} else
		$les_auteurs_du_site = '';
	if ((preg_match(',<([^>]*xml:)?lang(uage)?'.'>([^<>]+)<,i',
	$header, $match) AND $l = $match[3])
	OR ($l = extraire_attribut(extraire_balise($header, 'feed'), 'xml:lang'))
	)
		$langue_du_site = $l;
	// atom
	elseif (preg_match(',<feed\s[^>]*xml:lang=[\'"]([^<>\'"]+)[\'"],i', $header, $match))
		$langue_du_site = $match[1];

	// Recuperer les blocs item et entry
	$items = array_merge(extraire_balises($rss, 'item'), extraire_balises($rss, 'entry'));


	//
	// Analyser chaque <item>...</item> du backend et le transformer en tableau
	//

	if (!count($items)) return _T('sites:avis_echec_syndication_01');

	if (!defined('_SYNDICATION_MAX_ITEMS')) define('_SYNDICATION_MAX_ITEMS',1000);
	$nb_items = 0;
	foreach ($items as $item) {
		$data = array();
		if ($nb_items++>_SYNDICATION_MAX_ITEMS){
			break;
		}

		// URL (semi-obligatoire, sert de cle)

		// guid n'est un URL que si marque de <guid ispermalink="true"> ;
		// attention la valeur par defaut est 'true' ce qui oblige a quelque
		// gymnastique
		if (preg_match(',<guid.*>[[:space:]]*(https?:[^<]*)</guid>,Uims',
		$item, $regs) AND preg_match(',^(true|1)?$,i',
		extraire_attribut($regs[0], 'ispermalink')))
			$data['url'] = $regs[1];
		// contourner les redirections feedburner
		else if (_SYNDICATION_DEREFERENCER_URL
		AND preg_match(',<feedburner:origLink>(.*)<,Uims',
		$item, $regs))
			$data['url'] = $regs[1];
		// <link>, plus classique
		else if (preg_match(
		',<link[^>]*[[:space:]]rel=["\']?alternate[^>]*>(.*)</link>,Uims',
		$item, $regs))
			$data['url'] = $regs[1];
		else if (preg_match(',<link[^>]*[[:space:]]rel=.alternate[^>]*>,Uims',
		$item, $regs))
			$data['url'] = extraire_attribut($regs[0], 'href');
		else if (preg_match(',<link[^>]*>\s*([^\s]+)\s*</link>,Uims', $item, $regs))
			$data['url'] = $regs[1];
		else if (preg_match(',<link[^>]*>,Uims', $item, $regs))
			$data['url'] = extraire_attribut($regs[0], 'href');

		// Aucun link ni guid, mais une enclosure
		else if (preg_match(',<enclosure[^>]*>,ims', $item, $regs)
		AND $url = extraire_attribut($regs[0], 'url'))
			$data['url'] = $url;

		// pas d'url, c'est genre un compteur...
		else
			$data['url'] = '';

		// Titre (semi-obligatoire)
		if (preg_match(",<title[^>]*>(.*?)</title>,ims",$item,$match))
			$data['titre'] = $match[1];
		else if (preg_match(',<link[[:space:]][^>]*>,Uims',$item,$mat)
		AND $title = extraire_attribut($mat[0], 'title'))
			$data['titre'] = $title; 
		if (!strlen($data['titre'] = trim($data['titre'])))
			$data['titre'] = _T('ecrire:info_sans_titre');

		// Date
		$la_date = '';
		if (preg_match(',<(published|modified|issued)>([^<]*)<,Uims',
		$item,$match)) {
			cdata_echappe_retour($match[2], $echappe_cdata);
			$la_date = my_strtotime($match[2], $langue_du_site);
		}
		if (!$la_date AND
		preg_match(',<(pubdate)>([^<]*)<,Uims',$item, $match)) {
			cdata_echappe_retour($match[2], $echappe_cdata);
			$la_date = my_strtotime($match[2], $langue_du_site);
		}
		if (!$la_date AND
		preg_match(',<([a-z]+:date)>([^<]*)<,Uims',$item,$match)) {
			cdata_echappe_retour($match[2], $echappe_cdata);
			$la_date = my_strtotime($match[2], $langue_du_site);
		}
		if (!$la_date AND
		preg_match(',<date>([^<]*)<,Uims',$item,$match)) {
			cdata_echappe_retour($match[1], $echappe_cdata);
			$la_date = my_strtotime($match[1], $langue_du_site);
		}

		// controle de validite de la date
		// pour eviter qu'un backend errone passe toujours devant
		// (note: ca pourrait etre defini site par site, mais ca risque d'etre
		// plus lourd que vraiment utile)
		if ($GLOBALS['controler_dates_rss']) {
			if (!$la_date
				OR $la_date > time() + 48 * 3600)
				$la_date = time();
		}

		if ($la_date)
			$data['date'] = $la_date;

		// Honorer le <lastbuilddate> en forcant la date
		if (preg_match(',<(lastbuilddate|updated|modified)>([^<>]+)</\1>,i',
		$item, $regs)
		AND $lastbuilddate = my_strtotime(trim($regs[2]), $langue_du_site)
		// pas dans le futur
		AND $lastbuilddate < time())
			$data['lastbuilddate'] = $lastbuilddate;

		// Auteur(s)
		if (preg_match_all(
		',<(author|creator)\b[^>]*>(.*)</\1>,Uims',
		$item, $regs, PREG_SET_ORDER)) {
			$auteurs = array();
			foreach ($regs as $reg) {
				$nom = $reg[2];
				if (preg_match(',<name\b[^>]*>(.*)</name>,Uims', $nom, $reg))
					$nom = $reg[1];
				// Cas particulier d'un auteur Flickr
				if (preg_match(',nobody@flickr.com \((.*)\),Uims', $nom, $reg))
					$nom = $reg[1];
				$auteurs[] = trim(textebrut(filtrer_entites($nom)));
			}
			$data['lesauteurs'] = join(', ', array_unique($auteurs));
		}
		else
			$data['lesauteurs'] = $les_auteurs_du_site;

		// Description
		if (preg_match(',<(description|summary)\b.*'
		.'>(.*)</\1\b,Uims',$item,$match)) {
			$data['descriptif'] = trim($match[2]);
		}
		if (preg_match(',<(content)\b.*'
		.'>(.*)</\1\b,Uims',$item,$match)) {
			$data['content'] = trim($match[2]);
		}

		// lang
		if (preg_match(',<([^>]*xml:)?lang(uage)?'.'>([^<>]+)<,i',
			$item, $match))
			$data['lang'] = trim($match[3]);
		else if ($lang = trim(extraire_attribut($item, 'xml:lang')))
			$data['lang'] = $lang;
		else
			$data['lang'] = trim($langue_du_site);

		// source et url_source  (pas trouve d'exemple en ligne !!)
		# <source url="http://www.truc.net/music/uatsap.mp3" length="19917" />
		# <source url="http://www.truc.net/rss">Site source</source>
		if (preg_match(',(<source[^>]*>)(([^<>]+)</source>)?,i',
		$item, $match)) {
			$data['source'] = trim($match[3]);
			$data['url_source'] = str_replace('&amp;', '&',
				trim(extraire_attribut($match[1], 'url')));
		}

		// tags
		# a partir de "<dc:subject>", (del.icio.us)
		# ou <media:category> (flickr)
		# ou <itunes:category> (apple)
		# on cree nos tags microformat <a rel="directory" href="url">titre</a>
		# http://microformats.org/wiki/rel-directory-fr
		$tags = array();
		if (preg_match_all(
		',<(([a-z]+:)?(subject|category|directory|keywords?|tags?|type))[^>]*>'
		.'(.*?)</\1>,ims',
		$item, $matches, PREG_SET_ORDER))
			$tags = ajouter_tags($matches, $item); # array()
		elseif (preg_match_all(
		',<(([a-z]+:)?(subject|category|directory|keywords?|tags?|type))[^>]*/>'
		.',ims',
		$item, $matches, PREG_SET_ORDER))
			$tags = ajouter_tags($matches, $item); # array()
		// Pieces jointes :
		// chercher <enclosure> au format RSS et les passer en microformat
		// ou des microformats relEnclosure,
		// ou encore les media:content
		if (!afficher_enclosures(join(', ', $tags))) {
			// on prend toutes les pièces jointes possibles, et on essaie de les rendre uniques.
			$enclosures = array();
			# rss 2
			if (preg_match_all(',<enclosure[[:space:]][^<>]+>,i',
				$item, $matches, PREG_PATTERN_ORDER)) {
					$enclosures += array_map('enclosure2microformat', $matches[0]);
			}
			# atom
			if (preg_match_all(',<link\b[^<>]+rel=["\']?enclosure["\']?[^<>]+>,i', 
				$item, $matches, PREG_PATTERN_ORDER)) {
					$enclosures += array_map('enclosure2microformat', $matches[0]);
			}
			# media rss
			if (preg_match_all(',<media:content\b[^<>]+>,i', 
				$item, $matches, PREG_PATTERN_ORDER)) {
					$enclosures += array_map('enclosure2microformat', $matches[0]);
			}
			$data['enclosures'] = join(', ', array_unique($enclosures));
			unset($enclosures);
		}
		$data['item'] = $item;

		// Nettoyer les donnees et remettre les CDATA en place
		cdata_echappe_retour($data, $echappe_cdata);
		cdata_echappe_retour($tags, $echappe_cdata);

		// passer l'url en absolue
		$data['url'] = url_absolue(filtrer_entites($data['url']), $url_syndic);

		// si on demande un dereferencement de l'URL, il faut verifier que ce n'est pas une redirection
		if (_SYNDICATION_DEREFERENCER_URL){
			$target = $data['url'];
			include_spip("inc/distant");
			for ($i = 0; $i<10; $i++){
				// on fait un GET et pas un HEAD car les vieux SPIP ne repondent pas la redirection avec un HEAD (honte) sur un article virtuel
				$res = recuperer_lapage($target, false, "GET", 4096);
				if (!$res) break; // c'est pas bon signe car on a pas trouve l'URL...
				if (is_array($res))	break; // on a trouve la page, donc on a l'URL finale
				$target = $res; // c'est une redirection, on la suit pour voir ou elle mene
			}
			// ici $target est l'URL finale de la page
			$data['url'] = $target;
		}

		// Trouver les microformats (ecrase les <category> et <dc:subject>)
		if (preg_match_all(
		',<a[[:space:]]([^>]+[[:space:]])?rel=[^>]+>.*</a>,Uims',
		$data['item'], $regs, PREG_PATTERN_ORDER)) {
			$tags = $regs[0];
		}
		// Cas particulier : tags Connotea sous la forme <a class="postedtag">
		if (preg_match_all(
		',<a[[:space:]][^>]+ class="postedtag"[^>]*>.*</a>,Uims',
		$data['item'], $regs, PREG_PATTERN_ORDER))
			$tags = preg_replace(', class="postedtag",i',
			' rel="tag"', $regs[0]);

		$data['tags'] = $tags;
		// enlever le html des titre pour etre homogene avec les autres objets spip
		$data['titre'] = textebrut($data['titre']);

		$articles[] = $data;
	}

	return $articles;
}


// helas strtotime ne reconnait pas le format W3C
// http://www.w3.org/TR/NOTE-datetime
// http://code.spip.net/@my_strtotime
function my_strtotime($la_date, $lang=null) {
	// format complet
	if (preg_match(
	',^(\d+-\d+-\d+[T ]\d+:\d+(:\d+)?)(\.\d+)?'
	.'(Z|([-+]\d{2}):\d+)?$,',
	$la_date, $match)) {
		$la_date = str_replace("T", " ", $match[1])." GMT";
		return strtotime($la_date) - intval($match[5]) * 3600;
	}

	// YYYY
	if (preg_match(',^\d{4}$,', $la_date, $match))
		return strtotime($match[0]."-01-01");

	// YYYY-MM
	if (preg_match(',^\d{4}-\d{2}$,', $la_date, $match))
		return strtotime($match[0]."-01");

	// YYYY-MM-DD hh:mm:ss
	if (preg_match(',^\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}\b,', $la_date, $match))
		return strtotime($match[0]);

	// utiliser strtotime en dernier ressort
	// en nettoyant le jour qui prefixe parfois la date, suivi d'une virgule
	// et les UT qui sont en fait des UTC
	$la_date_c = preg_replace("/^\w+,\s*/ms","",$la_date);
	$la_date_c = preg_replace("/UT\s*$/ms","UTC",$la_date_c);
	if ($s=strtotime($la_date)
	  OR $s=strtotime($la_date_c))
		return $s;

	// essayons de voir si le nom du mois est dans la langue du flux et remplacons le
	// par la version anglaise avant de faire strtotime
	if ($lang){
		// "fr-fr"
		list($lang) = explode("-", $lang);
		static $months = null;
		if (!isset($months[$lang])){
			$prev_lang = $GLOBALS['spip_lang'];
			changer_langue($lang);
			foreach(range(1,12) as $m){
				$s = _T("date_mois_$m");
				$months[$lang][$s] = date("M",strtotime("2013-$m-01"));
				$s = _T("date_mois_".$m."_abbr");
				$months[$lang][$s] = date("M",strtotime("2013-$m-01"));
				$months[$lang][trim($s,".")] = date("M",strtotime("2013-$m-01"));
			}
			changer_langue($prev_lang);
		}
		spip_log($la_date_c,"dbgs");
		foreach($months[$lang] as $loc=>$en){
			if (stripos($la_date_c,$loc)!==false){
				$s=str_ireplace($loc,$en,$la_date_c);
				if ($s=strtotime($s))
					return $s;
			}
		}
	}

	// erreur
	spip_log("Impossible de lire le format de date '$la_date'");
	return false;
}
// A partir d'un <dc:subject> ou autre essayer de recuperer
// le mot et son url ; on cree <a href="url" rel="tag">mot</a>
// http://code.spip.net/@creer_tag
function creer_tag($mot,$type,$url) {
	if (!strlen($mot = trim($mot))) return '';
	$mot = "<a rel=\"tag\">$mot</a>";
	if ($url)
		$mot = inserer_attribut($mot, 'href', $url);
	if ($type)
		$mot = inserer_attribut($mot, 'rel', $type);
	return $mot;
}


// http://code.spip.net/@ajouter_tags
function ajouter_tags($matches, $item) {
	include_spip('inc/filtres');
	$tags = array();
	foreach ($matches as $match) {
		$type = ($match[3] == 'category' OR $match[3] == 'directory')
			? 'directory':'tag';
		$mot = supprimer_tags($match[0]);
		if (!strlen($mot)
		AND !strlen($mot = extraire_attribut($match[0], 'label')))
			break;
		// rechercher un url
		if ($url = extraire_attribut($match[0], 'domain')) {
			// category@domain est la racine d'une url qui se prolonge
			// avec le contenu text du tag <category> ; mais dans SPIP < 2.0
			// on donnait category@domain = #URL_RUBRIQUE, et
			// text = #TITRE_RUBRIQUE ; d'ou l'heuristique suivante sur le slash
			if (substr($url, -1) == '/')
				$url .= rawurlencode($mot);
		}
		else if ($url = extraire_attribut($match[0], 'resource')
		OR $url = extraire_attribut($match[0], 'url')
		)
			{}

		## cas particuliers
		else if (extraire_attribut($match[0], 'scheme') == 'urn:flickr:tags') {
			foreach(explode(' ', $mot) as $petit)
				if ($t = creer_tag($petit, $type,
				'http://www.flickr.com/photos/tags/'.rawurlencode($petit).'/'))
					$tags[] = $t;
			$mot = '';
		}
		else if (
			// cas atom1, a faire apres flickr
			$term = extraire_attribut($match[0], 'term')
		) {
			if ($scheme = extraire_attribut($match[0], 'scheme'))
				$url = suivre_lien($scheme,$term);
			else
				$url = $term;
		}
		else {
			# type delicious.com
			foreach(explode(' ', $mot) as $petit)
				if (preg_match(',<rdf\b[^>]*\bresource=["\']([^>]*/'
				.preg_quote(rawurlencode($petit),',').')["\'],i',
				$item, $m)) {
					$mot = '';
					if ($t = creer_tag($petit, $type, $m[1]))
						$tags[] = $t;
				}
		}

		if ($t = creer_tag($mot, $type, $url))
			$tags[] = $t;
	}
	return $tags;
}


// Lit contenu des blocs [[CDATA]] dans un flux
// http://code.spip.net/@cdata_echappe_retour
function cdata_echappe(&$rss, &$echappe_cdata) {
	$echappe_cdata = array();
	if (preg_match_all(',<!\[CDATA\[(.*)]]>,Uims', $rss,
	$regs, PREG_SET_ORDER)) {
		foreach ($regs as $n => $reg) {
			if (strpos($reg[1],'<')!==false
			  or strpos($reg[1],'>')!==false) {
				// verifier que la chaine est encore dans le flux, car on peut avoir X fois la meme
				// inutile de (sur)peupler le tableau avec des substitutions identiques
				if (strpos($rss,$reg[0])!==false){
					$echappe_cdata["@@@SPIP_CDATA$n@@@"] = $reg[1];
					$rss = str_replace($reg[0], "@@@SPIP_CDATA$n@@@", $rss);
				}
			} else
				$rss = str_replace($reg[0], $reg[1], $rss);
		}
	}
}

// Retablit le contenu des blocs [[CDATA]] dans une chaine ou un tableau
// http://code.spip.net/@cdata_echappe_retour
function cdata_echappe_retour(&$x, &$echappe_cdata) {
	if (is_string($x)) {
		if (strpos($x, '&lt;') !== false){
			$x = filtrer_entites($x);
		}
		if (strpos($x, '@@@SPIP_CDATA') !== false){
			$x = str_replace( array_keys($echappe_cdata), array_values($echappe_cdata), $x);
		}
	}

	else if (is_array($x)) {
		foreach($x as $k => &$v)
			cdata_echappe_retour($v, $echappe_cdata);
	}
}
?>
feedfinder.php000066600000015410152522550620007361 0ustar00<?php
/**
 * adaptation en php de feedfinder.py :
 *
 * """Ultra-liberal feed finder, de Mark Pilgrim
 * <http://diveintomark.org/projects/feed_finder/>
 * Par: courcy.michael@wanadoo.fr
 *
 * adaptation en php, je ne reprends qu'une partie de cette algorithme
 *
 * 0) A chaque etape on verifie si les feed indiques sont reellement des feeds
 * 1) Si l'uri passe est un feed on retourne le resultat tout simplement
 * 2) Si le header de la page contient des balises LINK qui renvoient vers des feed on les retourne
 * 3) on cherche les liens <a> qui se termine par  ".rss", ".rdf", ".xml", ou ".atom"
 * 4) on cherche les liens <a> contenant "rss", "rdf", "xml", ou "atom"
 *
 * j'integre pas l'interrogation  avec xml_rpc de syndic8, mais on peut le faire assez facilement
 * dans la phase de test sur differentes url je n'ai constate aucune diffrerence entre les reponses 
 * donnees par feedfinder.py et les miennes donc je ne suis pas sur de voir l'interet
 *
 * Je ne me preoccupe pas comme l'auteur de savoir si mes liens de feed sont sur le meme serveur ou pas
 *
 * exemple d'utilisation
 * 
 * print_r (get_feed_from_url("http://willy.boerland.com/myblog/"));
 * 
 * on obtient
 * 
 * Array
 * (
 *   [0] => http://willy.boerland.com/myblog/atom/feed
 *   [1] => http://willy.boerland.com/myblog/blogapi/rsd
 *   [2] => http://willy.boerland.com/myblog/rss.xml
 *   [3] => http://willy.boerland.com/myblog/node/feed
 * )
 */
if (!defined('_ECRIRE_INC_VERSION')) return;

$verif_complete = 0; //mettez le a 1 si vous voulez controler la validite des feed trouves mais le temps d'execution
                     //est alors plus long

/**
 * une fonction qui permet de si un lien est un feed ou nom, 
 * si c'est un feed elle retourne son type, si c'est pas un feed elle retourne 0, 
 * cette verification est évidemment très très légère
 * 
 * @param string $url
 * 		URL à analyser
 * @return string|0 
 * 		Retourne son type (rss|atom|rdf) ou 0 si pas feed
 */
function is_feed($url){

	/**
	 * méthode SPIP
	 */
	if (function_exists('recuperer_page')) {
		$buffer = recuperer_page($url);
		if (preg_match("/<(\w*) .*/", $buffer, $matches)){
			//ici on detecte la premiere balise
			$type_feed = $matches[1];
			switch ($type_feed) {
				case "rss": return "rss";
				case "feed": return "atom";
				case "rdf": return "rdf";
			}
		}
		return '';
	}

	$fp = @fopen($url, "r");
	if (!$fp )
		return 0;
	//verifion la nature de ce fichier
	while (!feof($fp)) {
		$buffer = fgets($fp, 4096);
		if (preg_match("/<(\w*) .*/", $buffer, $matches)){
			//ici on detecte la premiere balise
			$type_feed = $matches[1];
			switch ($type_feed) {
				case "rss": fclose($fp); return "rss";
				case "feed": fclose($fp); return "atom";
				case "rdf": fclose($fp); return "rdf";
				default : fclose($fp); return 0;
			}
		}
	}
}

/*****************test is_feed******************************
echo is_feed("http://contrib.spip.net/spip.php?page=backend" _EXTENSIO_PHP") . "<br />"; //retourne rss
echo is_feed("http://liberation.fr/rss.php") . "<br />"; //retourne rss
echo is_feed("http://liberation.fr/rss.php") . "<br />"; //retourne rss
echo is_feed("http://willy.boerland.com/myblog/atom/feed") //retourne atom
echo is_feed("http://spip.net/") . "<br />"; //retoune 0
************************************************************/

/**
 * fonction sans finesse mais efficace
 * on parcourt ligne par ligne a la recherche de balise <a> ou <link>
 * si dans le corps de celle-ci on trouve les mots rss, xml, atom ou rdf
 * alors on recupere la valeur href='<url>', on adapte celle-ci si elle
 * est relative et on verifie que c'est bien un feed si oui on l'ajoute
 * au tableau des feed si on ne trouve rien ou si aucun feed est trouve on retourne 
 * un tableau vide
 * 
 * @param string $url
 * 		L'URL à analyser
 * @param $buffer
 * @return array $feed_list
 * 		Le tableau des feed trouvés dans la page
 */
function get_feed_from_url($url, $buffer=false){
	global $verif_complete;
	//j'ai prevenu ce sera pas fin
	if (!preg_match("/^http:\/\/.*/", $url)) $url = "http://www." . $url;
	if (!$buffer) $buffer = @file_get_contents($url);

	include_spip("inc/filtres");

	$feed_list = array();
	//extraction des <link>
	if ($links = extraire_balises($buffer,"link")){
		//y a t-y rss atom rdf ou xml dans ces balises
		foreach($links as $link){
			if (
				  (strpos($link, "rss")
				  || strpos($link, "rdf")
				  || strpos($link, "atom")
				  || strpos($link, "xml"))
				&&
				  (!strpos($link,'opensearch') && !strpos($link,'oembed'))
			  ){
				//voila un candidat on va extraire sa partie href et la placer dans notre tableau
				if ($href = extraire_attribut($link,"href")){
 					//on aura pris soin de verifier si ce lien est relatif d'en faire un absolu
					$href = suivre_lien($url, $href);
					if(!$verif_complete OR is_feed($href)){
						$feed_list[] = $href;
          }
				}
			}
		}
	}
	//extraction des <a>
	if ($links = extraire_balises($buffer,"a")){
		//y a t-y rss atom rdf ou xml dans ces balises
		foreach($links as $link){
			if (
				  (strpos($link, "rss")
				  || strpos($link, "rdf")
				  || strpos($link, "atom")
				  || strpos($link, "xml"))
				&&
				  (!strpos($link,'opensearch') && !strpos($link,'oembed'))
			  ){
				//voila un candidat on va extraire sa partie href et la placer dans notre tableau
				if ($href = extraire_attribut($link,"href")){
 					//on aura pris soin de verifier si ce lien est relatif d'en faire un absolu
					$href = suivre_lien($url, $href);
					if(!$verif_complete OR is_feed($href)){
						$feed_list[] = $href;
          }
				}
			}
		}
	}

	// si c'est un site SPIP, tentons l'url connue
	if (!count($feed_list)
	  AND (
		strpos($url,"spip") OR stripos($buffer,"spip")
		)){
		$href = suivre_lien($url,"spip.php?page=backend");
		if (is_feed($href))
			$feed_list[] = $href;
	}
	return $feed_list;
}
/************************************ getFeed ****************************
print_r (get_feed_from_url("contrib.spip.net"));
print_r (get_feed_from_url("http://liberation.fr/"));
print_r (get_feed_from_url("cnn.com"));
print_r (get_feed_from_url("http://willy.boerland.com/myblog/"));
*****************************    Resultat *****************************************
Array
(
    [0] => http://contrib.spip.net/backend.php
)
Array
(
    [0] => http://www.liberation.fr/rss.php
)
Array
(
    [0] => http://rss.cnn.com/rss/cnn_topstories.rss
    [1] => http://rss.cnn.com/rss/cnn_latest.rss
    [2] => http://www.cnn.com/services/rss/
    [3] => http://www.cnn.com/services/rss/
    [4] => http://www.cnn.com/services/rss/
)
Array
(
    [0] => http://willy.boerland.com/myblog/atom/feed
    [1] => http://willy.boerland.com/myblog/blogapi/rsd
    [2] => http://willy.boerland.com/myblog/rss.xml
    [3] => http://willy.boerland.com/myblog/node/feed
)
************************************************************************/

?>site.php000066600000010500152522550620006225 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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


// http://code.spip.net/@analyser_site
function analyser_site($url) {
	include_spip('inc/filtres');
	include_spip('inc/distant');

	// Accepter les URLs au format feed:// ou qui ont oublie le http://
	$url = preg_replace(',^feed://,i', 'http://', $url);
	if (!preg_match(',^[a-z]+://,i', $url)) $url = 'http://'.$url;

	$texte = recuperer_page($url, true);
	if (!$texte) return false;

	include_spip('inc/syndic');
	cdata_echappe($texte, $echappe_cdata);

	if (preg_match(',<(channel|feed)([\:[:space:]][^>]*)?'
	.'>(.*)</\1>,ims', $texte, $regs)) {
		$result['syndication'] = 'oui';
		$result['url_syndic'] = $url;
		$channel = $regs[3];

		// Pour recuperer l'entete, on supprime tous les items
		$b = array_merge(
			extraire_balises($channel, 'item'),
			extraire_balises($channel, 'entry')
		);
		$header = str_replace($b,array(),$channel);

		if ($t = extraire_balise($header, 'title')) {
			cdata_echappe_retour($t, $echappe_cdata);
			$result['nom_site'] = filtrer_entites(supprimer_tags($t));
		}
		if ($t = extraire_balises($header, 'link')) {
			cdata_echappe_retour($t, $echappe_cdata);
			foreach ($t as $link) {
				$u = supprimer_tags(filtrer_entites($link));
				if (!strlen($u))
					$u = extraire_attribut($link, 'href');
				if (strlen($u)) {
					// on installe l'url comme url du site
					// si c'est non vide, en donnant la priorite a rel=alternate
					if (preg_match(',\balternate\b,', extraire_attribut($link, 'rel'))
					OR !isset($result['url_site']))
						$result['url_site'] = filtrer_entites($u);
				}
			}
		}
		$result['url_site'] = url_absolue($result['url_site'], $url);

		if ($a = extraire_balise($header, 'description')
		OR $a = extraire_balise($header, 'tagline')) {
			cdata_echappe_retour($a, $echappe_cdata);
			$result['descriptif'] = filtrer_entites(supprimer_tags($a));
		}

		if (preg_match(',<image.*<url.*>(.*)</url>.*</image>,Uims',
		$header, $r)
		AND preg_match(',(https?://.*/.*(gif|png|jpg)),ims', $r[1], $r)
		AND $image = recuperer_infos_distantes($r[1])) {
			if (in_array($image['extension'], array('gif', 'jpg', 'png'))) {
				$result['format_logo'] = $image['extension'];
				$result['logo'] = $r[1];
			}
			else if ($image['fichier']) {
				spip_unlink($image['fichier']);
			}
		}
	}
	else {
		$result['syndication'] = 'non';
		$result['url_site'] = $url;
		if (preg_match(',<head>(.*(description|title).*)</head>,Uims', $texte, $regs)) {
			$head = filtrer_entites($regs[1]);
		} else
			$head = $texte;

		if (preg_match(',<title[^>]*>(.*),ims', $head, $regs)){
			$titre = trim($regs[1]);
			if (!strlen($titre)){
				$titre = substr($head,strpos($head,$regs[0]));
			}
			$result['nom_site'] = filtrer_entites(supprimer_tags(preg_replace(',</title>.*$,ims', '', $titre)));
		}

		if ($a = array_merge(
			extraire_balises($head, 'meta'),
			extraire_balises($head, 'http-equiv')
		)) {
			foreach($a as $meta) {
				if (extraire_attribut($meta, 'name') == 'description') {
					$desc = trim(extraire_attribut($meta, 'content'));
					if (!strlen($desc))
						$desc = trim(extraire_attribut($meta, 'value'));
					$result['descriptif'] = $desc;
				}
			}
		}

		// Cherchons quand meme un backend
		include_spip('inc/distant');
		include_spip('inc/feedfinder');
		$feeds = get_feed_from_url($url, $texte);
		// si on a a trouve un (ou plusieurs) on le note avec select:
		// ce qui constitue un signal pour exec=sites qui proposera de choisir
		// si on syndique, et quelle url.
		if (count($feeds)>=1) {
			spip_log("feedfinder.php :\n".join("\n", $feeds));
			$result['url_syndic'] = "select: ".join(' ',$feeds);
		}
	}

	cdata_echappe_retour($result, $echappe_cdata);
	return $result;
}

?>
quete_calendrier.php000066600000021733152522552200010602 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

include_spip('inc/filtres');
// http://code.spip.net/@calendrier_categories
function calendrier_categories($table, $num, $objet)
{
  if (function_exists('generer_calendrier_class'))
    return generer_calendrier_class($table, $num, $objet);
  else {
    // cf agenda.css
    $num= sql_getfetsel((($objet != 'id_breve') ? 'id_secteur' : 'id_rubrique') . " AS id", $table, "$objet=$num");

    return 'calendrier-couleur' . (($num%14)+1);
  }
}

// ici on prend en fait le jour, la veille et le lendemain

// http://code.spip.net/@quete_calendrier_jour
function quete_calendrier_jour($annee,$mois,$jour) {
	$avant = "'" . date("Y-m-d", mktime(0,0,0,$mois,$jour-1,$annee)) . "'";
	$apres = "'" . date("Y-m-d", mktime(1,1,1,$mois,$jour+1,$annee)) .
	" 23:59:59'";
	return array($avant, $apres);
}

// retourne un tableau de 2 tableaux indexes par des dates
// - le premier indique les evenements du jour, sans indication de duree
// - le deuxime indique les evenements commencant ce jour, avec indication de duree

// http://code.spip.net/@quete_calendrier_interval
function quete_calendrier_interval($limites) {
	include_spip('inc/urls');
	list($avant, $apres) = $limites;
	$evt = array();
	quete_calendrier_interval_articles($avant, $apres, $evt);
	quete_calendrier_interval_breves($avant, $apres, $evt);
	quete_calendrier_interval_rubriques($avant, $apres, $evt);
	return array($evt, quete_calendrier_interval_rv($avant, $apres));
}

// http://code.spip.net/@quete_calendrier_interval_forums
function  quete_calendrier_interval_forums($limites, &$evenements) {
	list($avant, $apres) = $limites;
	$result=sql_select("DISTINCT titre, date_heure, id_forum",	"spip_forum", "date_heure >= $avant AND date_heure < $apres", '',  "date_heure");
	while($row=sql_fetch($result)){
		$amj = date_anneemoisjour($row['date_heure']);
		$id = $row['id_forum'];
		if (autoriser('voir','forum',$id))
			$evenements[$amj][]=
			array(
				'URL' => generer_url_entite($id, 'forum'),
				'CATEGORIES' => 'calendrier-couleur7',
				'SUMMARY' => $row['titre'],
				'DTSTART' => date_ical($row['date_heure']));
	}
}

# 3 fonctions retournant les evenements d'une periode
# le tableau retourne est indexe par les balises du format ics
# afin qu'il soit facile de produire de tels documents.
# L'URL de chacun de ces evenements est celle de l'espace prive
# pour faciliter la navigation, ce qu'on obtient utilisant
# le 4e argument des fonctions generer_url_ecrire_$table

// http://code.spip.net/@quete_calendrier_interval_articles
function quete_calendrier_interval_articles($avant, $apres, &$evenements) {

  $result=sql_select('id_article, titre, date, descriptif, chapo,  lang', 'spip_articles', "statut='publie' AND date >= $avant AND date < $apres", '', "date");

	// tables traduites
	$objets = explode(',', $GLOBALS['meta']['multi_objets']);

	if (in_array('spip_articles', $objets)) {
	  include_spip('inc/lang_liste');
	  $langues = $GLOBALS['codes_langues'];
	} else $langues = array();
	while($row=sql_fetch($result)){
		$amj = date_anneemoisjour($row['date']);
		$id = $row['id_article'];
		if (autoriser('voir','article',$id))
			$evenements[$amj][]=
			    array(
				'CATEGORIES' => calendrier_categories('spip_articles', $id, 'id_article'),
				'DESCRIPTION' => $row['descriptif'] ? $row['descriptif'] : $langues[$row['lang']],
				'SUMMARY' => $row['titre'],
				'URL' => generer_url_ecrire_objet('article',$id, '','','prop'));
	}
}

// http://code.spip.net/@quete_calendrier_interval_rubriques
function quete_calendrier_interval_rubriques($avant, $apres, &$evenements) {

  $result=sql_select('DISTINCT R.id_rubrique, titre, descriptif, date', 'spip_rubriques AS R, spip_documents_liens AS L', "statut='publie' AND	date >= $avant AND	date < $apres AND	R.id_rubrique = L.id_objet AND L.objet='rubrique'",'', "date");
	while($row=sql_fetch($result)){
		$amj = date_anneemoisjour($row['date']);
		$id = $row['id_rubrique'];
		if (autoriser('voir','rubrique',$id))
			$evenements[$amj][]=
			    array(
				'CATEGORIES' => calendrier_categories('spip_rubriques', $id, 'id_rubrique'),
				'DESCRIPTION' => $row['descriptif'],
				'SUMMARY' => $row['titre'],
				'URL' => generer_url_ecrire_objet('rubrique',$id, '','', 'prop'));
	}
}

// http://code.spip.net/@quete_calendrier_interval_breves
function quete_calendrier_interval_breves($avant, $apres, &$evenements) {
  $result=sql_select("id_breve, titre, date_heure, id_rubrique", 'spip_breves',	"statut='publie' AND date_heure >= $avant AND date_heure < $apres", '', "date_heure");
	while($row=sql_fetch($result)){
		$amj = date_anneemoisjour($row['date_heure']);
		$id = $row['id_breve'];
		$ir = $row['id_rubrique'];
		if (autoriser('voir','breve',$id))
			$evenements[$amj][]=
			array(
			      'URL' => generer_url_ecrire_objet('breve',$id, '','', 'prop'),
			      'CATEGORIES' => calendrier_categories('spip_breves', $ir, 'id_breve'),
			      'SUMMARY' => $row['titre']);
	}
}

// http://code.spip.net/@quete_calendrier_interval_rv
function quete_calendrier_interval_rv($avant, $apres) {
	include_spip('inc/session');
	$connect_id_auteur = session_get('id_auteur');

	$evenements= array();
	if (!$connect_id_auteur) return $evenements;
	$result=sql_select("M.id_message, M.titre, M.texte, M.date_heure, M.date_fin, M.type",
		"spip_messages AS M LEFT JOIN spip_auteurs_liens AS L ON (L.id_objet=M.id_message)",
		"((L.objet='message' AND (L.id_auteur=$connect_id_auteur OR M.type='affich')) OR (L.objet IS NULL AND M.id_auteur=$connect_id_auteur AND ".sql_in('M.type',array('pb','affich'))."))"
		." AND M.rv='oui' AND ((M.date_fin >= $avant OR M.date_heure >= $avant) AND M.date_heure <= $apres) AND M.statut='publie'",
		"M.id_message", "M.date_heure");
	while($row=sql_fetch($result)){
		$date_heure=$row["date_heure"];
		$date_fin=$row["date_fin"];
		$type=$row["type"];
		$id_message=$row['id_message'];

		if ($type=="pb")
		  $cat = 'calendrier-couleur2';
		else {
		  if ($type=="affich")
		  $cat = 'calendrier-couleur4';
		  else {
		    if ($type!="normal")
		      $cat = 'calendrier-couleur12';
		    else {
		      $cat = 'calendrier-couleur9';
		      $auteurs = array_map('array_shift', sql_allfetsel("nom", "spip_auteurs AS A LEFT JOIN spip_auteurs_liens AS L ON L.id_auteur=A.id_auteur", "(L.objet='message' AND L.id_objet=$id_message AND (A.id_auteur!=$connect_id_auteur))"));
		    }
		  }
		}

		$jour_avant = substr($avant, 9,2);
		$mois_avant = substr($avant, 6,2);
		$annee_avant = substr($avant, 1,4);
		$jour_apres = substr($apres, 9,2);
		$mois_apres = substr($apres, 6,2);
		$annee_apres = substr($apres, 1,4);
		$ical_apres = date_anneemoisjour("$annee_apres-$mois_apres-".sprintf("%02d",$jour_apres));

		// Calcul pour les semaines a cheval sur deux mois
 		$j = 0;
		$amj = date_anneemoisjour("$annee_avant-$mois_avant-".sprintf("%02d", $j+($jour_avant)));

		while ($amj <= $ical_apres) {
		if (!($amj == date_anneemoisjour($date_fin) AND preg_match(",00:00:00,", $date_fin)))  // Ne pas prendre la fin a minuit sur jour precedent
			$evenements[$amj][$id_message]=
			  array(
				'URL' => generer_url_ecrire("message","id_message=$id_message"),
				'DTSTART' => date_ical($date_heure),
				'DTEND' => date_ical($date_fin),
				'DESCRIPTION' => $row['texte'],
				'SUMMARY' => $row['titre'],
				'CATEGORIES' => $cat,
				'ATTENDEE' => (count($auteurs) == 0) ? '' : join($auteurs,", "));

			$j ++;
			$ladate = date("Y-m-d",mktime (1,1,1,$mois_avant, ($j + $jour_avant), $annee_avant));

			$amj = date_anneemoisjour($ladate);

		}

	}
  return $evenements;
}

// http://code.spip.net/@quete_calendrier_agenda
function quete_calendrier_agenda ($annee, $mois) {
	include_spip('inc/session');
	$connect_id_auteur = session_get('id_auteur');

	$rv = array();
	if (!$connect_id_auteur) return $rv;
	$date = date("Y-m-d", mktime(0,0,0,$mois, 1, $annee));
	$mois = mois($date);
	$annee = annee($date);

	// rendez-vous personnels dans le mois
	$result_messages = sql_select("M.titre AS summary, M.texte AS description, M.id_message AS uid, M.date_heure", "spip_messages AS M, spip_auteurs_liens AS L", "((L.id_auteur=$connect_id_auteur AND L.id_objet=M.id_message AND L.objet='message') OR M.type='affich') AND M.rv='oui' AND M.date_heure >='$annee-$mois-1' AND date_heure < DATE_ADD('$annee-$mois-1', INTERVAL 1 MONTH) AND M.statut='publie'");
	while($row=sql_fetch($result_messages)) {
		$rv[journum($row['date_heure'])] = $row;
	}
	return $rv;
}

?>
messages.php000066600000014062152522552200007073 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

include_spip('inc/filtres');
include_spip('base/abstract_sql');

if (!defined('_EMAIL_GENERAL'))
	define('_EMAIL_GENERAL','general'); // permet aux admin d'envoyer un email a tout le monde

/**
 * Lister les statuts des auteurs pouvant recevoir un message
 * c'est tous les auteurs au moins redacteur
 * 
 * @return array
 */
function messagerie_statuts_destinataires_possibles(){
	include_spip('inc/filtres_ecrire');
	return pipeline('messagerie_statuts_destinataires_possibles', auteurs_lister_statuts('redacteurs',false));
}

/**
 * Nettoyer une liste de destinataires
 * @param $destinataires
 * @return array
 */
function messagerie_nettoyer_destinataires($destinataires){
	foreach ($destinataires as $k=>$id){
		// il se peut que l'id recupere l'ancre qui suit avec certains ie ... :(
		if (preg_match(',^[0-9]+#[a-z_0-9]+,',$id))
			$destinataires[$k] = intval($id);
	}
	return $destinataires;
}

/**
 * Fonction generique de verification des destinataires
 * lors de l'envoi d'un message ou de recommander
 * un destinataire peut etre un id_auteur numerique
 * ou une adresse mail valide, si l'options accepter_email est true
 *
 * @param array $destinataires
 * @param array $options
 * @return array
 */
function messagerie_verifier_destinataires($destinataires,$options=array('accepter_email'=>true)){
	$erreurs = array();

	$destinataires = messagerie_nettoyer_destinataires($destinataires);
	foreach ($destinataires as $id){
		if (is_numeric($id)){
			if (!$id)
				$erreurs[] = _T('organiseur:erreur_destinataire_invalide',array('dest'=>$id));
		}
		else {
			if (!$options['accepter_email']
				OR !email_valide($id))
				$erreurs[] = _T('organiseur:erreur_destinataire_invalide',array('dest'=>$id));
		}
	}

	return $erreurs;
}

/**
 * Selectionner les destinataires en distinguant emails et id_auteur
 *
 * @param array $dests
 * @return array
 */
function messagerie_destiner($dests){
	// separer les destinataires auteur des destinataires email
	$auteurs_dest = array();
	$email_dests = array();

	$dests = messagerie_nettoyer_destinataires($dests);
	foreach ($dests as $id){
		if (is_numeric($id))
			$auteurs_dest[] = $id;
		elseif (defined('_MESSAGERIE_EMAIL_GENERAL') AND $id!=_MESSAGERIE_EMAIL_GENERAL)
			$email_dests[] = $id;
	}
	if (count($email_dests)) {
		// retrouver les id des emails pour ceux qui sont en base
		$res = sql_select('id_auteur,email','spip_auteurs',sql_in('email', $email_dests));
		$auteurs_dest_found = array();
		while ($row = sql_fetch($res)){
			$auteurs_dest_found[] = $row['id_auteur'];
		}
		$auteurs_dest = array_merge($auteurs_dest,$auteurs_dest_found);
	}
	return array($auteurs_dest,$email_dests);
}

/**
 * Diffuser un message par la messagerie interne
 *
 * @param int $id_message
 * @param array $auteurs_dest
 * @return bool|int
 */
function messagerie_diffuser_message($id_message, $auteurs_dest=array()){
	$out = false;
	if ($id_message=intval($id_message)
	  AND count($auteurs_dest)){
		include_spip('action/editer_liens');
		$out = objet_associer(array('auteur'=>$auteurs_dest),array('message'=>$id_message),array('vu'=>'non'));
	}
	return $out;
}

/**
 * Envoyer un message par mail pour les destinataires externes
 *
 * @param int $id_message
 * @param array $emails_dest
 * @return bool
 */
function messagerie_mailer_message($id_message, $emails_dest=array()){
	if ($id_message=intval($id_message)
	  AND count($emails_dest)) {
		if ($row = sql_fetsel('titre,texte,id_auteur','spip_messages','id_message='.intval($id_message))){
			$from = sql_getfetsel('email','spip_auteurs','id_auteur='.$row['id_auteur']);
			foreach($emails_dest as $email)
				job_queue_add(
					'envoyer_mail',
					'messagerie mail',
				  array($email,$row['titre'],array('texte'=>$row['texte'],'from'=>$from)),
					'inc/'
				);
			return true;
		}
	}
	return false;
}

/**
 * Marquer un message dans l'etat indique par $vu
 *
 * @param int $id_auteur
 * @param array $liste
 * @param string $vu
 * @return void
 */
function messagerie_marquer_message($id_auteur,$liste,$vu){
	include_spip('action/editer_liens');
	if (!is_array($liste))
		$liste = array($liste);
	// completer les liens qui n'existent pas encore
	// ex : pour marquer lue une annonce, on ajoute le lien d'abord (n'existe pas)
	// puis on le marque 'oui'
	$liens = objet_trouver_liens(array('auteur'=>$id_auteur),array('message'=>$liste));
	$l = array();
	foreach($liens as $lien)
		$l[] = $lien['message'];
	objet_associer(array('auteur'=>$id_auteur),array('message'=>array_diff($liste,$l)),array('vu'=>$vu));
	// puis les marquer tous lus
	objet_qualifier_liens(array('auteur'=>$id_auteur),array('message'=>$liste),array('vu'=>$vu));
	include_spip('inc/invalideur');
	suivre_invalideur("message/".implode(',',$liste));
}

/**
 * Marquer un message comme lu
 *
 * @param int $id_auteur
 * @param array $liste_id_message
 */
function messagerie_marquer_lus($id_auteur,$liste_id_message){messagerie_marquer_message($id_auteur,$liste_id_message,'oui');}

/**
 * Marquer un message comme non lu
 *
 * @param int $id_auteur
 * @param array $liste_id_message
 */
function messagerie_marquer_non_lus($id_auteur,$liste_id_message){messagerie_marquer_message($id_auteur,$liste_id_message,'non');}

/**
 * Effacer un message recu
 *
 * @param int $id_auteur
 * @param array $liste_id_message
 */
function messagerie_effacer_message_recu($id_auteur,$liste_id_message){messagerie_marquer_message($id_auteur,$liste_id_message,'poub');}

?>
date_gestion.php000066600000003472152522552210007735 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

/**
 * Recuperer les champs date_xx et heure_xx, verifier leur coherence et les reformater
 *
 * @param string $suffixe
 * @param bool $horaire
 * @param array $erreurs
 * @return int
 */
function verifier_corriger_date_saisie($suffixe,$horaire,&$erreurs){
	include_spip('inc/filtres');
	$date = _request("date_$suffixe").($horaire?' '.trim(_request("heure_$suffixe")).':00':'');
	$date = recup_date($date);
	if (!$date)
		return '';
	$ret = null;
	if (!$ret=mktime(0,0,0,$date[1],$date[2],$date[0]))
		$erreurs["date_$suffixe"] = _T('organiseur:erreur_date');
	elseif (!$ret=mktime($date[3],$date[4],$date[5],$date[1],$date[2],$date[0]))
		$erreurs["date_$suffixe"] = _T('organiseur:erreur_heure');
	if ($ret){
		if (trim(_request("date_$suffixe")!==($d=date('d/m/Y',$ret)))){
			$erreurs["date_$suffixe"] = _T('organiseur:erreur_date_corrigee');
			set_request("date_$suffixe",$d);
		}
		if ($horaire AND trim(_request("heure_$suffixe")!==($h=date('H:i',$ret)))){
			$erreurs["heure_$suffixe"] = _T('organiseur:erreur_heure_corrigee');
			set_request("heure_$suffixe",$h);
		}
	}
	return $ret;
}

?>documenter_objet.php000066600000002725152525404020010616 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

// http://code.spip.net/@naviguer_doc
function inc_documenter_objet_dist($id, $type) {
	$serveur = '';
	// avant de documenter un objet, on verifie que ses documents vus sont bien lies !
	$spip_table_objet = table_objet_sql($type);
	$table_objet = table_objet($type);
	$id_table_objet = id_table_objet($type,$serveur);
	$champs = sql_fetsel('*',$spip_table_objet,addslashes($id_table_objet)."=".intval($id));

	$marquer_doublons_doc = charger_fonction('marquer_doublons_doc','inc');
	$marquer_doublons_doc($champs,$id,$type,$id_table_objet,$table_objet,$spip_table_objet, '', $serveur);

	$contexte = array('objet'=>$type,'id_objet'=>$id);
	return recuperer_fond('prive/objets/contenu/portfolio_document',array_merge($_GET,$contexte));


}


?>getdocument.php000066600000002636152525404020007605 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/
#
# Fichier obsolete, a supprimer
#
#
#
#
#

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

// inclure les fonctions bases du core
include_once _DIR_RESTREINT . "inc/documents.php";

include_spip('inc/minipres');

// Erreur appelee depuis public.php (la precedente ne fonctionne plus
// depuis qu'on est sortis de spip_image.php, apparemment).
// http://code.spip.net/@erreur_upload_trop_gros
function erreur_upload_trop_gros() {
	include_spip('inc/filtres');
	
	$msg = 		"<p>"
		.taille_en_octets($_SERVER["CONTENT_LENGTH"])
		.'<br />'
		._T('medias:upload_limit',
		array('max' => ini_get('upload_max_filesize')))
		."</p>";
	
  echo minipres(_T('pass_erreur'),"<div class='upload_answer upload_error'>".$msg."</div>");
	exit;
}


?>
documents.php000066600000015220152525404020007261 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

/**
 * donne le chemin du fichier relatif a _DIR_IMG
 * pour stockage 'tel quel' dans la base de donnees
 *
 * http://doc.spip.org/@set_spip_doc
 *
 * @param string $fichier
 * @return string
 */
function set_spip_doc($fichier) {
	if (strpos($fichier, _DIR_IMG) === 0)
		return substr($fichier, strlen(_DIR_IMG));
	else
		return $fichier; // ex: fichier distant
}

/**
 * donne le chemin complet du fichier
 *
 * http://doc.spip.org/@get_spip_doc
 *
 * @param string $fichier
 * @return bool|string
 */
function get_spip_doc($fichier) {
	// fichier distant
	if (preg_match(',^\w+://,', $fichier))
		return $fichier;

	// gestion d'erreurs, fichier=''
	if (!strlen($fichier))
		return false;

	$fichier = (
		    strncmp($fichier,_DIR_IMG, strlen(_DIR_IMG))!=0
		)
		? _DIR_IMG . $fichier
		: $fichier ;

	// fichier normal
	return $fichier;
}

/**
 * Creer IMG/pdf/
 *
 * http://doc.spip.org/@creer_repertoire_documents
 *
 * @param  $ext
 * @return string
 */
function creer_repertoire_documents($ext) {
	$rep = sous_repertoire(_DIR_IMG, $ext);

	if (!$ext OR !$rep) {
		spip_log("creer_repertoire_documents '$rep' interdit");
		exit;
	}

	// Cette variable de configuration peut etre posee par un plugin
	// par exemple acces_restreint
	if (isset($GLOBALS['meta']["creer_htaccess"]) AND $GLOBALS['meta']["creer_htaccess"] == 'oui') {
		include_spip('inc/acces');
		verifier_htaccess($rep);
	}

	return $rep;
}

/**
 * Efface le repertoire de maniere recursive !
 *
 * http://doc.spip.org/@effacer_repertoire_temporaire
 *
 * @param string $nom
 */
function effacer_repertoire_temporaire($nom) {
	$d = opendir($nom);
	while (($f = readdir($d)) !== false) {
		if (is_file("$nom/$f"))
			spip_unlink("$nom/$f");
		else if ($f <> '.' AND $f <> '..'
		AND is_dir("$nom/$f"))
			effacer_repertoire_temporaire("$nom/$f");
	}
	closedir($d);
	@rmdir($nom);
}

//
/**
 * Copier un document $source un dossier IMG/$ext/$orig.$ext
 * en numerotant eventuellement si un du meme nom existe deja
 *
 * http://doc.spip.org/@copier_document
 *
 * @param string $ext
 * @param string $orig
 * @param string $source
 * @return bool|mixed|string
 */
function copier_document($ext, $orig, $source) {

	$orig = preg_replace(',\.\.+,', '.', $orig); // pas de .. dans le nom du doc
	$dir = creer_repertoire_documents($ext);
	$dest = preg_replace("/[^.=\w-]+/", "_",
			translitteration(preg_replace("/\.([^.]+)$/", "",
						      preg_replace("/<[^>]*>/", '', basename($orig)))));

	// ne pas accepter de noms de la forme -r90.jpg qui sont reserves
	// pour les images transformees par rotation (action/documenter)
	$dest = preg_replace(',-r(90|180|270)$,', '', $dest);

	// Si le document "source" est deja au bon endroit, ne rien faire
	if ($source == ($dir . $dest . '.' . $ext))
		return $source;

	// sinon tourner jusqu'a trouver un numero correct
	$n = 0;
	while (@file_exists($newFile = $dir . $dest .($n++ ? ('-'.$n) : '').'.'.$ext));

	return deplacer_fichier_upload($source, $newFile);
}

/**
 * Trouver le dossier utilise pour upload un fichier
 *
 * http://doc.spip.org/@determine_upload
 *
 * @param string $type
 * @return bool|string
 */
function determine_upload($type='') {
	if(!function_exists('autoriser'))
		include_spip('inc/autoriser');
	
	if (!autoriser('chargerftp')
	OR $type == 'logos') # on ne le permet pas pour les logos
		return false;

	$repertoire = _DIR_TRANSFERT;
	if (!@is_dir($repertoire)) {
		$repertoire = str_replace(_DIR_TMP, '', $repertoire);
		$repertoire = sous_repertoire(_DIR_TMP, $repertoire);
	}

	if (!$GLOBALS['visiteur_session']['restreint'])
		return $repertoire;
	else
		return sous_repertoire($repertoire, $GLOBALS['visiteur_session']['login']);
}

/**
 * Deplacer ou copier un fichier
 *
 * http://doc.spip.org/@deplacer_fichier_upload
 *
 * @param string $source
 * @param string $dest
 * @param bool $move
 * @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);
	else {
		$f = @fopen($dest,'w');
		if ($f) {
			fclose ($f);
		} else {
			include_spip('inc/flock');
			raler_fichier($dest);
		}
		spip_unlink($dest);
	}
	return $ok ? $dest : false;
}


// Erreurs d'upload
// renvoie false si pas d'erreur
// et true si erreur = pas de fichier
// pour les autres erreurs affiche le message d'erreur et meurt
// http://doc.spip.org/@check_upload_error
function check_upload_error($error, $msg='', $return=false) {
	global $spip_lang_right;

	if (!$error) return false;

	spip_log("Erreur upload $error -- cf. http://php.net/manual/fr/features.file-upload.errors.php");

	switch ($error) {

		case 4: /* UPLOAD_ERR_NO_FILE */
			return true;

		# on peut affiner les differents messages d'erreur
		case 1: /* UPLOAD_ERR_INI_SIZE */
			$msg = _T('upload_limit',
			array('max' => ini_get('upload_max_filesize')));
			break;
		case 2: /* UPLOAD_ERR_FORM_SIZE */
			$msg = _T('upload_limit',
			array('max' => ini_get('upload_max_filesize')));
			break;
		case 3: /* UPLOAD_ERR_PARTIAL  */
			$msg = _T('upload_limit',
			array('max' => ini_get('upload_max_filesize')));
			break;

		default: /* autre */
			if (!$msg)
			$msg = _T('pass_erreur').' '. $error
			. '<br />' . propre("[->http://php.net/manual/fr/features.file-upload.errors.php]");
			break;
	}

	spip_log ("erreur upload $error");
	if ($return)
		return $msg;

  if(_request("iframe")=="iframe") {
	  echo "<div class='upload_answer upload_error'>$msg</div>";
	  exit;
	}

	include_spip('inc/minipres');
	echo minipres($msg,
		      "<div style='text-align: $spip_lang_right'><a href='"  . rawurldecode($GLOBALS['redirect']) . "'><button type='button'>" . _T('ecrire:bouton_suivant') . "</button></a></div>");
	exit;
}
?>
verifier_document_mode_vignette.php000066600000002273152525404020013706 0ustar00<?php
/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

function inc_verifier_document_mode_vignette_dist($infos){

	// Si on veut uploader une vignette, il faut qu'elle ait ete bien lue
	if ($infos['inclus'] != 'image')
		return _T('medias:erreur_format_fichier_image',array('nom'=> $infos['fichier'])); #SVG

	if (isset($infos['largeur']) AND isset($infos['hauteur'])){
		if (!($infos['largeur'] OR $infos['hauteur']))
			return _T('medias:erreur_upload_vignette',array('nom'=>$infos['fichier']));
	}

	return true;
}renseigner_document.php000066600000007406152525404020011326 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

/**
 * recuperer les infos distantes d'une url,
 * et renseigner pour une insertion en base
 * utilise une variable static car appellee plusieurs fois au cours du meme hit
 * (verification puis traitement)
 *
 * @param string $source
 * @return string
 */
function renseigner_source_distante($source){
	static $infos = array();
	if (isset($infos[$source]))
		return $infos[$source];
	
	include_spip('inc/distant');
	// on passe la source dans le pipeline, le premier plugin
	// qui est capable de renseigner complete
	// fichier et mode + tous les autres champs a son gout
	// ex : oembed
	$a = pipeline('renseigner_document_distant',array('source'=>$source));

	// si la source est encore la, en revenir a la
	// methode traditionnelle : chargement de l'url puis analyse
	if (!isset($a['fichier']) OR !isset($a['mode'])){
		if (!$a = recuperer_infos_distantes($a['source'])) {
			return _T('medias:erreur_chemin_distant',array('nom'=>$source));
		}
		# NB: dans les bonnes conditions (fichier autorise et pas trop gros)
		# $a['fichier'] est une copie locale du fichier
		unset($a['body']);
		$a['distant'] = 'oui';
		$a['mode'] = 'document';
		$a['fichier'] = set_spip_doc($source);
	}

	// stocker pour la seconde demande
	return $infos[$source] = $a;
}

/**
 * Renseigner les informations de taille et dimension d'un document
 * 
 * Récupère les informations de taille (largeur / hauteur / type_image / taille) d'un document
 * Utilise pour cela les fonctions du répertoire metadatas/*
 * 
 * Ces fonctions de récupérations peuvent retourner d'autres champs si ces champs sont définis
 * comme editable dans la déclaration de la table spip_documents
 * 
 * TODO Renommer cette fonction sans "_image"
 *
 * @param string $fichier 
 * 		Le fichier à examiner 
 * @param string $ext
 * 		L'extension du fichier à examiner
 * @return array|string $infos
 * 		Si c'est une chaine, c'est une erreur
 * 		Si c'est un tableau, l'ensemble des informations récupérées du fichier
 */
function renseigner_taille_dimension_image($fichier,$ext){

	$infos = array(
		'largeur'=>0,
		'hauteur'=>0,
		'type_image'=>'',
		'taille'=>0
	);
	
	// Quelques infos sur le fichier
	if (
		!$fichier
		OR !@file_exists($fichier)
		OR !$infos['taille'] = @intval(filesize($fichier))) {
			spip_log ("Echec copie du fichier $fichier");
			return _T('medias:erreur_copie_fichier',array('nom'=> $fichier));
	}

	// chercher une fonction de description
	$meta = array();
	if ($metadata = charger_fonction($ext,"metadata",true)){
		$meta = $metadata($fichier);
	}
	else {
		$media = sql_getfetsel('media_defaut','spip_types_documents','extension='.sql_quote($ext));
		if ($metadata = charger_fonction($media,"metadata",true)){
			$meta = $metadata($fichier);
		}
	}

	$meta = pipeline('renseigner_document',array('args'=>array('extension'=>$ext,'fichier'=>$fichier),'data' => $meta));

	include_spip('inc/filtres'); # pour objet_info()
	$editables = objet_info('document','champs_editables');
	foreach($meta as $m=>$v)
		if (isset($infos[$m]) OR in_array($m,$editables))
			$infos[$m] = $v;

	return $infos;
}

?>choisir_mode_document.php000066600000003563152525404020011631 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

/**
 * Choisir le mode du document : image/document
 * fonction surchargeable
 *
 * @param unknown_type $fichier
 * @param unknown_type $type_image
 * @param unknown_type $largeur
 * @param unknown_type $hauteur
 */
function inc_choisir_mode_document($infos, $type_inclus_image, $objet){
	
	// si ce n'est pas une image, c'est forcement un document
	if (!$infos['type_image'] OR !$type_inclus_image)
		return 'document';

	// si on a pas le droit d'ajouter de document a l'objet, c'est donc un mode image
	if ($objet AND isset($GLOBALS['meta']["documents_$objet"]) AND ($GLOBALS['meta']["documents_$objet"]=='non'))
		return 'image';
	

	// _INTERFACE_DOCUMENTS
	// en fonction de la taille de l'image
	// par defaut l'affectation en fonction de la largeur de l'image
	// est desactivee car pas comprehensible par le novice
	// il suffit de faire dans mes_options
	// define('_LARGEUR_MODE_IMAGE', 450);
	// pour beneficier de cette detection auto
	@define('_LARGEUR_MODE_IMAGE', 0);
	
	if (!_LARGEUR_MODE_IMAGE)
		return 'image';
	
	if ($infos['largeur'] > 0
	  AND $infos['largeur'] < _LARGEUR_MODE_IMAGE)
		return 'image';
	else
		return 'document';
}

?>marquer_doublons_doc.php000066600000006563152525404020011500 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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


// On liste tous les champs susceptibles de contenir des documents ou images si on veut que ces derniers soient lies a l objet lorsqu on y fait reference par imgXX docXX ou embXX
// la dist ne regarde que chapo et texte, on laisse comme ca, mais ca permet d etendre a descriptif ou toto depuis d autre plugin comme agenda ou grappe
$GLOBALS['medias_liste_champs'][] = 'texte';
$GLOBALS['medias_liste_champs'][] = 'chapo';
 
// http://code.spip.net/@marquer_doublons_documents
function inc_marquer_doublons_doc_dist($champs,$id,$type,$id_table_objet,$table_objet,$spip_table_objet, $desc=array(), $serveur=''){
	$champs_selection=array();

	foreach ($GLOBALS['medias_liste_champs'] as $champs_choisis) {
		if ( isset($champs[$champs_choisis]) )
			array_push($champs_selection,$champs_choisis);
	}
	if (count($champs_selection) == 0)
		return;
	if (!$desc){
		$trouver_table = charger_fonction('trouver_table', 'base');
		$desc = $trouver_table($table_objet, $serveur);
	}
	$load = "";
	// charger le champ manquant en cas de modif partielle de l	'objet
	// seulement si le champ existe dans la table demande

	$champs_a_traiter = "";
	foreach ($champs_selection as $champs_a_parcourir) {
		if (isset($desc['field'][$champs_a_parcourir])) {
			$load = $champs_a_parcourir;
			$champs_a_traiter .= $champs[$champs_a_parcourir];
		}
	}

	if ($load){
		$champs[$load] = "";
		$row = sql_fetsel($load, $spip_table_objet, "$id_table_objet=".sql_quote($id));
		if ($row AND isset($row[$load]))
			$champs[$load] = $row[$load];
	}
	include_spip('inc/texte');
	include_spip('base/abstract_sql');
	include_spip('action/editer_liens');
	include_spip('base/objets');
	$modeles = lister_tables_objets_sql('spip_documents');
	$modeles = $modeles['modeles'];
	$GLOBALS['doublons_documents_inclus'] = array();
	$env = array(
		'objet' => $type,
		'id_objet' => $id,
		$id_table_objet => $id
	);
	traiter_modeles($champs_a_traiter,array('documents'=>$modeles),'','',null,$env); // detecter les doublons
	objet_qualifier_liens(array('document'=>'*'),array($type=>$id),array('vu'=>'non'));
	if (count($GLOBALS['doublons_documents_inclus'])){
		// on repasse par une requete sur spip_documents pour verifier que les documents existent bien !
		$in_liste = sql_in('id_document',$GLOBALS['doublons_documents_inclus']);
		$res = sql_allfetsel("id_document", "spip_documents", $in_liste);
		$res = array_map('reset',$res);
		// Creer le lien s'il n'existe pas deja
		objet_associer(array('document'=>$res),array($type=>$id),array('vu'=>'oui'));
		objet_qualifier_liens(array('document'=>$res),array($type=>$id),array('vu'=>'oui'));
	}
}

?>joindre_document.php000066600000025637152525404020010625 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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


/**
 * Recuperer le nom du fichier selon le mode d'upload choisi
 * et mettre cela au format $_FILES
 * 
 * Renvoie une liste de fichier ou un message en cas d'erreur
 *
 * @return string/array
 */
function joindre_trouver_fichier_envoye(){
	static $files = array();
	// on est appele deux fois dans un hit, resservir ce qu'on a trouve a la verif
	// lorsqu'on est appelle au traitement
	
	if (count($files))
		return $files;
	
	if (_request('joindre_upload')){
		$post = isset($_FILES) ? $_FILES : $GLOBALS['HTTP_POST_FILES'];
		$files = array();
		if (is_array($post)){
			include_spip('action/ajouter_documents');
		  foreach ($post as $file) {
		  	if (is_array($file['name'])){
		  		while (count($file['name'])){
						$test=array(
							'error'=>array_shift($file['error']),
							'name'=>array_shift($file['name']),
							'tmp_name'=>array_shift($file['tmp_name']),
							'type'=>array_shift($file['type']),
							);
						if (!($test['error'] == 4)){
							if (is_string($err = joindre_upload_error($test['error'])))
								return $err; // un erreur upload
							if (!is_array(verifier_upload_autorise($test['name'])))
								return _T('medias:erreur_upload_type_interdit',array('nom'=>$test['name']));
							$files[]=$test;
						}
		  		}
		  	}
		  	else {
			  	//UPLOAD_ERR_NO_FILE
					if (!($file['error'] == 4)){
						if (is_string($err = joindre_upload_error($file['error'])))
							return $err; // un erreur upload
						if (!is_array(verifier_upload_autorise($file['name'])))
							return _T('medias:erreur_upload_type_interdit',array('nom'=>$file['name']));
						$files[]=$file;
					}
		  	}
			}
			if (!count($files))
				return _T('medias:erreur_indiquez_un_fichier');
		}
		return $files;
	}
	elseif (_request('joindre_distant')){
		$path = _request('url');
		if (!strlen($path) OR $path=='http://')
			return _T('medias:erreur_indiquez_un_fichier');
		include_spip('action/ajouter_documents');
		$infos = renseigner_source_distante($path);
		if (!is_array($infos))
			return $infos; // message d'erreur
		else
			return array(
				array(
					'name' => basename($path),
					'tmp_name' => $path,
					'distant' => true,
				)
			);
	}
	elseif (_request('joindre_ftp')){
		$path = _request('cheminftp');
		if (!$path || strstr($path, '..')) return _T('medias:erreur_indiquez_un_fichier');
		
		include_spip('inc/documents');	
		include_spip('inc/actions');
		$upload = determine_upload();
		if ($path != '/' AND $path != './') $upload .= $path;
	
		if (!is_dir($upload))
		  // seul un fichier est demande
		  return array(
		  	array (
		  		'name' => basename($upload),
					'tmp_name' => $upload
				)
			);
		else {
		  // on upload tout un repertoire
		  $files = array();
		  foreach (preg_files($upload) as $fichier) {
				$files[]= array (
					'name' => basename($fichier),
					'tmp_name' => $fichier
				);
		  }
		  return $files;
		}
	}
	elseif (_request('joindre_zip') and $token_zip = _request('chemin_zip')) {

		$zip_to_clean = (isset($GLOBALS['visiteur_session']['zip_to_clean']) ? unserialize($GLOBALS['visiteur_session']['zip_to_clean']) : array());
		if (!$zip_to_clean or !isset($zip_to_clean[$token_zip]) or !$path = $zip_to_clean[$token_zip]){
			return _T('avis_operation_impossible');
		}

		include_spip('inc/documents'); //pour creer_repertoire_documents
		define('_tmp_zip', $path);
		define('_tmp_dir', creer_repertoire_documents(md5($path.$GLOBALS['visiteur_session']['id_auteur'])));
		if (_tmp_dir == _DIR_IMG)
			return _T('avis_operation_impossible');
		
		$files = array();
		if (_request('options_upload_zip')=='deballe')
			$files = joindre_deballer_lister_zip($path,_tmp_dir);
	  
		// si le zip doit aussi etre conserve, l'ajouter
		if (_request('options_upload_zip')=='upload' OR _request('options_deballe_zip_conserver')){
	  	$files[] = array(
				'name' => basename($path),
				'tmp_name' => $path,
	  	);
	  }

	  return $files;
		
	}

	return array();
}


// Erreurs d'upload
// renvoie false si pas d'erreur
// et true si erreur = pas de fichier
// pour les autres erreurs renvoie le message d'erreur
function joindre_upload_error($error) {

	if (!$error) return false;
	spip_log("Erreur upload $error -- cf. http://php.net/manual/fr/features.file-upload.errors.php");
	switch ($error) {
			
		case 4: /* UPLOAD_ERR_NO_FILE */
			return true;

		# on peut affiner les differents messages d'erreur
		case 1: /* UPLOAD_ERR_INI_SIZE */
			$msg = _T('medias:upload_limit',
			array('max' => ini_get('upload_max_filesize')));
			break;
		case 2: /* UPLOAD_ERR_FORM_SIZE */
			$msg = _T('medias:upload_limit',
			array('max' => ini_get('upload_max_filesize')));
			break;
		case 3: /* UPLOAD_ERR_PARTIAL  */
			$msg = _T('medias:upload_limit',
			array('max' => ini_get('upload_max_filesize')));
			break;
		case 6: /* UPLOAD_ERR_NO_TMP_DIR  */
			$msg = _T('medias:erreur_dossier_tmp_manquant');
			break;
		case 7: /* UPLOAD_ERR_CANT_WRITE */
			$msg = _T('medias:erreur_ecriture_fichier');

		default: /* autre */
			if (!$msg)
			$msg = _T('pass_erreur').' '. $error
			. '<br />' . propre("[->http://php.net/manual/fr/features.file-upload.errors.php]");
			break;
	}

	spip_log ("erreur upload $error");
	return $msg;
	
}

/**
 * Verifier si le fichier poste est un zip
 * Si on sait le deballer, proposer les options necessaires
 *
 * @param array $files
 * @return string
 */
function joindre_verifier_zip($files){
	if (function_exists('gzopen')
	 AND (count($files) == 1)
	 AND !isset($files[0]['distant'])
	 AND 
	  (preg_match('/\.zip$/i', $files[0]['name']) 
	   OR (isset($files[0]['type']) AND $files[0]['type'] == 'application/zip'))
	  ){
	
	  // on pose le fichier dans le repertoire zip 
	  // (nota : copier_document n'ecrase pas un fichier avec lui-meme
	  // ca autorise a boucler)
	  include_spip('inc/getdocument');
		$desc = $files[0];
		$zip = copier_document("zip",
					$desc['name'],
					$desc['tmp_name']
				);
		
		// Est-ce qu'on sait le lire ?
		include_spip('inc/pclzip');
		if ($zip
			AND $archive = new PclZip($zip)
		  AND $contenu = joindre_decrire_contenu_zip($archive)
			AND $tmp = sous_repertoire(_DIR_TMP,"zip")
		  AND rename($zip, $tmp = $tmp.basename($zip))
		  ){
			$zip_to_clean = (isset($GLOBALS['visiteur_session']['zip_to_clean'])?unserialize($GLOBALS['visiteur_session']['zip_to_clean']):array());
			$zip_to_clean[md5($tmp)] = $tmp;
			session_set('zip_to_clean',serialize($zip_to_clean));
		 	$contenu[] = $tmp;
		 	return $contenu;
		 }
	}
	
	// ce n'est pas un zip sur lequel il faut demander plus de precisions
	return false;
}

/**
 * Verifier et decrire les fichiers de l'archive, en deux listes :
 * - une liste des noms de fichiers ajoutables
 * - une liste des erreurs (fichiers refuses)
 *
 * @param object $zip
 * @return array
 */
function joindre_decrire_contenu_zip($zip) {
	include_spip('action/ajouter_documents');
	// si pas possible de decompacter: installer comme fichier zip joint
	if (!$list = $zip->listContent()) return false;

	// Verifier si le contenu peut etre uploade (verif extension)
	$fichiers = array();
	$erreurs = array();
	foreach ($list as $file) {
		if (accepte_fichier_upload($f = $file['stored_filename']))
			$fichiers[$f] = $file;
		else
			// pas de message pour les dossiers et fichiers caches
			if (substr($f,-1)!=='/' AND substr(basename($f),0,1)!=='.')
				$erreurs[] = _T('medias:erreur_upload_type_interdit',array('nom'=>$f));
	}

	// si aucun fichier uploadable : installer comme fichier zip joint
	if (!count($fichiers))
		return false;

	ksort($fichiers);
	return array($fichiers,$erreurs);
}



// http://code.spip.net/@joindre_deballes
function joindre_deballer_lister_zip($path,$tmp_dir) {
  include_spip('inc/pclzip');
	$archive = new PclZip($path);
	$archive->extract(
		PCLZIP_OPT_PATH, _tmp_dir,
		PCLZIP_CB_PRE_EXTRACT, 'callback_deballe_fichier'
	);
	if ($contenu = joindre_decrire_contenu_zip($archive)){
		$files = array();
		$fichiers = reset($contenu);		
		foreach($fichiers as $fichier){
			$f = basename($fichier['filename']);
			$files[] = array('tmp_name'=>$tmp_dir. $f,'name'=>$f,'titrer'=>_request('options_deballe_zip_titrer'),'mode'=>_request('options_deballe_zip_mode_document')?'document':null);
		}
		return $files;
	}
 	return _T('avis_operation_impossible');
}

if (!function_exists('fixer_extension_document')){
/**
 * Cherche dans la base le type-mime du tableau representant le document
 * et corrige le nom du fichier ; retourne array(extension, nom corrige)
 * s'il ne trouve pas, retourne '' et le nom inchange
 *
 * @param unknown_type $doc
 * @return unknown
 */
// http://code.spip.net/@fixer_extension_document
function fixer_extension_document($doc) {
	$extension = '';
	$name = $doc['name'];
	if (preg_match(',\.([^.]+)$,', $name, $r)
	 AND $t = sql_fetsel("extension", "spip_types_documents",	"extension=" . sql_quote(corriger_extension($r[1])))
	 ) {
		$extension = $t['extension'];
		$name = preg_replace(',\.[^.]*$,', '', $doc['name']).'.'.$extension;
	}
	else if ($t = sql_fetsel("extension", "spip_types_documents",	"mime_type=" . sql_quote($doc['type']))) {
		$extension = $t['extension'];
		$name = preg_replace(',\.[^.]*$,', '', $doc['name']).'.'.$extension;
	}

	return array($extension,$name);
}
}

//
// Gestion des fichiers ZIP
//
// http://code.spip.net/@accepte_fichier_upload

function accepte_fichier_upload ($f) {
	if (!preg_match(",.*__MACOSX/,", $f)
	AND !preg_match(",^\.,", basename($f))) {
	include_spip('action/ajouter_documents');
		$ext = corriger_extension((strtolower(substr(strrchr($f, "."), 1))));
		return sql_countsel('spip_types_documents', "extension=" . sql_quote($ext) . " AND upload='oui'");
	}
}

# callback pour le deballage d'un zip telecharge
# http://www.phpconcept.net/pclzip/man/en/?options-pclzip_cb_pre_extractfunction
// http://code.spip.net/@callback_deballe_fichier

function callback_deballe_fichier($p_event, &$p_header) {
	if (accepte_fichier_upload($p_header['filename'])) {
		$p_header['filename'] = _tmp_dir . basename($p_header['filename']);
		return 1;
	} else {
		return 0;
	}
}

?>
vignette.php000066600000003277152525404020007116 0ustar00<?php
/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

/**
 * Vignette pour les documents lies
 * rechercher les fichiers d'icone au format png pour l'extension demandee
 *
 * on cherche prive/vignettes/ext.png dans le path
 *
 * @param string $ext
 * @param bool $size
 * @param bool $loop
 * @return array|bool|int|string
 */
function inc_vignette_dist($ext, $size=true, $loop = true) {

	if (!$ext)
		$ext = 'txt';

	// Chercher la vignette correspondant a ce type de document
	// dans les vignettes persos, ou dans les vignettes standard
	if (
	# installation dans un dossier /vignettes personnel, par exemple /squelettes/vignettes
	!$v = find_in_path("prive/vignettes/".$ext.".png")
	)
		if ($loop){
			$f = charger_fonction('vignette','inc');
			$v = $f('defaut', false, $loop=false);
		}
		else
			$v = false; # pas trouve l'icone de base

	if (!$size) return $v;

	$largeur = $hauteur = 0;
	if ($v AND $size = @getimagesize($v)) {
		$largeur = $size[0];
		$hauteur = $size[1];
	}

	return array($v, $largeur, $hauteur);
}

?>
verifier_document_mode_image.php000066600000002265152525404020013144 0ustar00<?php
/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

function inc_verifier_document_mode_image_dist($infos){

	// Si on veut uploader une image, il faut qu'elle ait ete bien lue
	if ($infos['inclus'] != 'image')
		return _T('medias:erreur_format_fichier_image',array('nom'=> $infos['fichier'])); #SVG

	if (isset($infos['largeur']) AND isset($infos['hauteur'])){
		if (!($infos['largeur'] OR $infos['hauteur']))
			return _T('medias:erreur_upload_vignette',array('nom'=>$infos['fichier']));
	}

	return true;
}autoriser.php000066600000133017152525475610007317 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

/**
 * Gestion de l'API autoriser et fonctions d'autorisations de SPIP
 *
 * @package SPIP\Autorisations
**/
if (!defined('_ECRIRE_INC_VERSION')) return;

include_spip('base/abstract_sql');

/**
 * Tracer les autorisations dans tmp/spip.log pour débug ?
 */
defined('_DEBUG_AUTORISER') || define('_DEBUG_AUTORISER', false);

// Constantes surchargeables, cf. plugin autorite

/**
 * Gérer les admins restreints ?
 * 
 * @todo une option à activer
 */
defined('_ADMINS_RESTREINTS') || define('_ADMINS_RESTREINTS', true);

/** Statut par defaut à la creation */
defined('_STATUT_AUTEUR_CREATION') || define('_STATUT_AUTEUR_CREATION', '1comite');

/** statuts associables a des rubriques (separes par des virgules) */
defined('_STATUT_AUTEUR_RUBRIQUE') || define('_STATUT_AUTEUR_RUBRIQUE', _ADMINS_RESTREINTS ? '0minirezo' : '');

// mes_fonctions peut aussi declarer des autorisations, donc il faut donc le charger
if ($f = find_in_path('mes_fonctions.php')) {
	global $dossier_squelettes;
	include_once(_ROOT_CWD . $f);
}


if (!function_exists('autoriser')) {
/**
 * Autoriser une action
 * 
 * Teste si une personne (par défaut le visiteur en cours) peut effectuer
 * une certaine action. Cette fonction est le point d'entrée de toutes
 * les autorisations.
 *
 * La fonction se charge d'appeler des fonctions d'autorisations spécifiques
 * aux actions demandées si elles existent. Elle cherche donc les fonctions
 * dans cet ordre :
 * - autoriser_{type}_{faire}, sinon avec _dist
 * - autoriser_{type}, sinon avec _dist
 * - autoriser_{faire}, sinon avec _dist
 * - autoriser_{defaut}, sinon avec _dist
 * 
 * Seul le premier argument est obligatoire
 *
 * @api
 * @see autoriser_dist()
 * 
 * @param string $faire
 *   une action ('modifier', 'publier'...)
 * @param string $type
 *   type d'objet ou nom de table ('article')
 * @param int $id
 *   id de l'objet sur lequel on veut agir
 * @param null|int|array $qui
 *   - si null on prend alors visiteur_session
 *   - un id_auteur (on regarde dans la base)
 *   - un tableau auteur complet, y compris [restreint]
 * @param null|array $opt
 *   options sous forme de tableau associatif
 * @return bool
 *   true si la personne peut effectuer l'action
 */
	function autoriser($faire, $type='', $id=0, $qui = NULL, $opt = NULL) {
		// Charger les fonctions d'autorisation supplementaires
		static $pipe;
		if (!isset($pipe)) { $pipe = 1; pipeline('autoriser'); }

		$args = func_get_args();
		return call_user_func_array('autoriser_dist', $args);
	}
}


/**
 * Autoriser une action
 * 
 * Voir autoriser() pour une description complète
 * 
 * @see autoriser()
 * 
 * @param string $faire
 *   une action ('modifier', 'publier'...)
 * @param string $type
 *   type d'objet ou nom de table ('article')
 * @param int $id
 *   id de l'objet sur lequel on veut agir
 * @param null|int|array $qui
 *   si null on prend alors visiteur_session
 *   un id_auteur (on regarde dans la base)
 *   un tableau auteur complet, y compris [restreint]
 * @param null|array $opt
 *   options sous forme de tableau associatif
 * @return bool
 *   true si la personne peut effectuer l'action
 */
function autoriser_dist($faire, $type='', $id=0, $qui = NULL, $opt = NULL) {

	// Qui ? visiteur_session ?
	// si null ou '' (appel depuis #AUTORISER) on prend l'auteur loge
	if ($qui === NULL OR $qui==='')
	  $qui = $GLOBALS['visiteur_session'] ? $GLOBALS['visiteur_session'] : array('statut' => '', 'id_auteur' =>0, 'webmestre' => 'non');
	elseif (is_numeric($qui)) {
		$qui = sql_fetsel("*", "spip_auteurs", "id_auteur=".$qui);
	}

	// Admins restreints, on construit ici (pas generique mais...)
	// le tableau de toutes leurs rubriques (y compris les sous-rubriques)
	if (_ADMINS_RESTREINTS AND is_array($qui)) {
		$qui['restreint'] = isset($qui['id_auteur']) ? liste_rubriques_auteur($qui['id_auteur']) : array();
	}

	if (_DEBUG_AUTORISER) spip_log("autoriser $faire $type $id (" . (isset($qui['nom']) ? $qui['nom'] : '') . ") ?");

	// passer par objet_type pour avoir les alias
	// et supprimer les _
	$type = str_replace('_','',  strncmp($type,"_",1)==0?$type:objet_type($type,false));

	// Si une exception a ete decretee plus haut dans le code, l'appliquer
	if (isset($GLOBALS['autoriser_exception'][$faire][$type][$id])
	AND autoriser_exception($faire,$type,$id,'verifier'))
		return true;

	// Chercher une fonction d'autorisation
	// Dans l'ordre on va chercher autoriser_type_faire[_dist], autoriser_type[_dist],
	// autoriser_faire[_dist], autoriser_defaut[_dist]
	$fonctions = $type
		? array (
			'autoriser_'.$type.'_'.$faire,
			'autoriser_'.$type.'_'.$faire.'_dist',
			'autoriser_'.$type,
			'autoriser_'.$type.'_dist',
			'autoriser_'.$faire,
			'autoriser_'.$faire.'_dist',
			'autoriser_defaut',
			'autoriser_defaut_dist'
		)
		: array (
			'autoriser_'.$faire,
			'autoriser_'.$faire.'_dist',
			'autoriser_defaut',
			'autoriser_defaut_dist'
		);

	foreach ($fonctions as $f) {
		if (function_exists($f)) {
			$a = $f($faire,$type,$id,$qui,$opt);
			break;
		}
	}

	if (_DEBUG_AUTORISER) spip_log("$f($faire,$type,$id," . (isset($qui['nom']) ? $qui['nom'] : '') . "): ".($a?'OK':'niet'));

	return $a;
}

// une globale pour aller au plus vite dans la fonction generique ci dessus
$GLOBALS['autoriser_exception']=array();

/**
 * Accorder une autorisation exceptionnel pour le hit en cours, ou la revoquer
 *
 * http://doc.spip.org/@autoriser_exception
 *
 * @param string $faire   Action demandée
 * @param string $type    Type d'objet sur lequel appliquer l'action
 * @param int    $id      Identifiant de l'objet
 * @param bool $autoriser accorder (true) ou revoquer (false)
 * @return bool
 */
function autoriser_exception($faire,$type,$id,$autoriser=true){
	// une static innaccessible par url pour verifier que la globale est positionnee a bon escient
	static $autorisation;
	if ($autoriser==='verifier')
			return isset($autorisation[$faire][$type][$id]);
	if ($autoriser===true)
		$GLOBALS['autoriser_exception'][$faire][$type][$id] = $autorisation[$faire][$type][$id] = true;
	if ($autoriser===false) {
		unset($GLOBALS['autoriser_exception'][$faire][$type][$id]);
		unset($autorisation[$faire][$type][$id]);
	}
	return false;
}

 
/**
 * Autorisation par defaut
 *
 * Les admins complets OK, les autres non
 *
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_defaut_dist($faire, $type, $id, $qui, $opt) {
	return
		$qui['statut'] == '0minirezo'
		AND !$qui['restreint'];
}


/**
 * Autorisation d'accès è l'espace privé ?
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_ecrire_dist($faire, $type, $id, $qui, $opt) {
	return in_array($qui['statut'], array('0minirezo', '1comite'));
}

/**
 * Autorisation de créer un contenu
 *
 * Accordée par defaut ceux qui accèdent à l'espace privé,
 * peut-être surchargée au cas par cas
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_creer_dist($faire, $type, $id, $qui, $opt) {
	return in_array($qui['statut'], array('0minirezo', '1comite'));
}

/**
 * Autorisation de prévisualiser un contenu
 *
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_previsualiser_dist($faire, $type, $id, $qui, $opt) {
	// si auteur pas autorise, NIET
	if (strpos($GLOBALS['meta']['preview'], ",". $qui['statut'] .",")===false)
		return false;
	// si pas de type et statut fourni, c'est une autorisation generale => OK
	if (!$type)
		return true;

	include_spip('base/objets');
	$infos = lister_tables_objets_sql(table_objet_sql($type));
	if (isset($infos['statut']))
		foreach($infos['statut'] as $c){
			if (isset($c['publie'])){
				if (!isset($c['previsu'])) return false; // pas de previsu definie => NIET
				$champ = $c['champ'];
				if (!isset($opt[$champ])) return false; // pas de champ passe a la demande => NIET
				$previsu = explode(',',$c['previsu']);
				// regarder si ce statut est autorise pour l'auteur
				if (in_array($opt[$champ]."/auteur",$previsu)){
					if (!sql_countsel("spip_auteurs_liens","id_auteur=".intval($qui['id_auteur'])." AND objet=".sql_quote($type)." AND id_objet=".intval($id)))
						return false;  // pas auteur de cet objet => NIET
				}
				elseif (!in_array($opt[$champ],$previsu)) // le statut n'est pas dans ceux definis par la previsu => NIET
					return false;
			}
		}
	return true;
}

/**
 * Autorisation de changer de langue un contenu
 *
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_changerlangue_dist($faire, $type, $id, $qui, $opt) {
	return autoriser('modifier',$type,$id,$qui,$opt);
}

/**
 * Autorisation de changer la date d'un contenu
 *
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_dater_dist($faire, $type, $id, $qui, $opt) {
	if (!isset($opt['statut'])){
		$table = table_objet($type);
		$trouver_table = charger_fonction('trouver_table','base');
		$desc = $trouver_table($table);
		if (!$desc)
			return false;
		if (isset($desc['field']['statut'])){
			$statut = sql_getfetsel("statut", $desc['table'], id_table_objet($type)."=".intval($id));
		}
		else
			$statut = 'publie'; // pas de statut => publie
	}
	else
		$statut = $opt['statut'];

	if ($statut == 'publie'
	 OR ($statut == 'prop' AND $type=='article' AND $GLOBALS['meta']["post_dates"] == "non"))
		return autoriser('modifier', $type, $id);
	return false;
}

/**
 * Autorisation d'instituer un contenu
 *
 * C'est à dire de changer son statut ou son parent.
 * Par défaut, il faut l'autorisation de modifier le contenu
 *
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_instituer_dist($faire, $type, $id, $qui, $opt) {
	return autoriser('modifier',$type,$id,$qui,$opt);
}

/**
 * Autorisation de publier dans une rubrique $id
 *
 * Il faut être administrateur ou administrateur restreint de la rubrique
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_rubrique_publierdans_dist($faire, $type, $id, $qui, $opt) {
	return
		($qui['statut'] == '0minirezo')
		AND (
			!$qui['restreint'] OR !$id
			OR in_array($id, $qui['restreint'])
		);
}

 /**
 * Autorisation de créer une rubrique
 *
 * Il faut être administrateur pour pouvoir publier à la racine
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_rubrique_creer_dist($faire, $type, $id, $qui, $opt) {
	return 
		((!$id AND autoriser('defaut',null,null,$qui, $opt))
		OR $id AND autoriser('creerrubriquedans','rubrique', $id, $qui, $opt)
		);
}

/**
 * Autorisation de créer une sous rubrique dans une rubrique $id
 *
 * Il faut être administrateur et pouvoir publier dans la rubrique
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_rubrique_creerrubriquedans_dist($faire, $type, $id, $qui, $opt) {
	return
		($id OR ($qui['statut'] == '0minirezo' AND !$qui['restreint']))
		AND autoriser('voir','rubrique',$id)
		AND autoriser('publierdans','rubrique',$id);
}

/**
 * Autorisation de créer un article dans une rubrique $id
 *
 * Il faut pouvoir voir la rubrique
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_rubrique_creerarticledans_dist($faire, $type, $id, $qui, $opt) {
	return
		$id
		AND autoriser('voir','rubrique',$id)
		AND autoriser('creer', 'article');
}


/**
 * Autorisation de modifier une rubrique $id
 *
 * Il faut pouvoir publier dans cette rubrique
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_rubrique_modifier_dist($faire, $type, $id, $qui, $opt) {
	return
		autoriser('publierdans', 'rubrique', $id, $qui, $opt);
}

/**
 * Autorisation de supprimer une rubrique $id
 *
 * Il faut quelle soit vide (pas d'enfant) et qu'on ait le droit de la modifier
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_rubrique_supprimer_dist($faire, $type, $id, $qui, $opt) {
	if (!$id = intval($id))
		return false;

	if (sql_countsel('spip_rubriques', "id_parent=".intval($id)))
		return false;

	if (sql_countsel('spip_articles', "id_rubrique=".intval($id)." AND (statut<>'poubelle')"))
		return false;

	$compte = pipeline('objet_compte_enfants',array('args'=>array('objet'=>'rubrique','id_objet'=>$id),'data'=>array()));
	foreach($compte as $objet => $n)
		if ($n)
			return false;

	return autoriser('modifier','rubrique',$id);
}


/**
 * Autorisation de modifier un article $id
 *
 * Il faut pouvoir publier dans le parent
 * ou, si on change le statut en proposé ou préparation être auteur de l'article
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_article_modifier_dist($faire, $type, $id, $qui, $opt) {
	$r = sql_fetsel("id_rubrique,statut", "spip_articles", "id_article=".sql_quote($id));

	if (!function_exists('auteurs_article'))
		include_spip('inc/auth'); // pour auteurs_article si espace public

	return
		$r
		AND
		(
			autoriser('publierdans', 'rubrique', $r['id_rubrique'], $qui, $opt)
			OR (
				(!isset($opt['statut']) OR $opt['statut']!=='publie')
				AND in_array($qui['statut'], array('0minirezo', '1comite'))
				AND in_array($r['statut'], array('prop','prepa', 'poubelle'))
				AND auteurs_article($id, "id_auteur=".$qui['id_auteur'])
			)
		);
}

 /**
 * Autorisation de créer un article
 *
 * Il faut qu'une rubrique existe et être au moins rédacteur
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_article_creer_dist($faire, $type, $id, $qui, $opt) {
	return (sql_countsel('spip_rubriques')>0 AND in_array($qui['statut'], array('0minirezo', '1comite')));
}

/**
 * Autorisation de voir un article
 *
 * Il faut être admin ou auteur de l'article, sinon il faut que l'article
 * soit publié ou proposé.
 * 
 * Peut-être appelée sans $id, mais avec un $opt['statut'] pour tester
 * la liste des status autorisés en fonction de $qui['statut']
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
 */
function autoriser_article_voir_dist($faire, $type, $id, $qui, $opt){
	if ($qui['statut'] == '0minirezo') return true;
	// cas des articles : depend du statut de l'article et de l'auteur
	if (isset($opt['statut']))
		$statut = $opt['statut'];
	else {
		if (!$id) return false;
		$statut = sql_getfetsel("statut", "spip_articles", "id_article=".intval($id));
	}

	return
		// si on est pas auteur de l'article,
		// seuls les propose et publies sont visibles
		in_array($statut, array('prop', 'publie'))
		// sinon si on est auteur, on a le droit de le voir, evidemment !
		OR
		($id AND $qui['id_auteur']
		     AND (function_exists('auteurs_article') OR include_spip('inc/auth'))
		     AND auteurs_article($id, "id_auteur=".$qui['id_auteur']));
}


/**
 * Autorisation de voir un objet
 *
 * Tout est visible par défaut, sauf les auteurs où il faut au moins être rédacteur.
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_voir_dist($faire, $type, $id, $qui, $opt) {
	# securite, mais on aurait pas du arriver ici !
	if (function_exists($f='autoriser_'.$type.'_voir') OR function_exists($f='autoriser_'.$type.'_voir_dist')){
		return $f($faire, $type, $id, $qui, $opt);
	}

	if ($qui['statut'] == '0minirezo') return true;
	// admins et redacteurs peuvent voir un auteur
	if ($type == 'auteur')
		return in_array($qui['statut'], array('0minirezo', '1comite'));
	// sinon par defaut tout est visible
	// sauf cas particuliers traites separemment (ie article)
	return true;
}


/**
 * Autorisation de webmestre
 *
 * Est-on webmestre ? Signifie qu'on n'a même pas besoin de passer par ftp
 * pour modifier les fichiers, cf. notamment inc/admin
 * 
 * Soit la liste des webmestres est définie via une constante _ID_WEBMESTRES,
 * soit on regarde l'état "webmestre" de l'auteur
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_webmestre_dist($faire, $type, $id, $qui, $opt) {
	return
		(defined('_ID_WEBMESTRES')?
			in_array($qui['id_auteur'], explode(':', _ID_WEBMESTRES))
			:$qui['webmestre']=='oui')
		AND $qui['statut'] == '0minirezo'
		AND !$qui['restreint']
		;
}

/**
 * Autorisation Configurer le site
 *
 * Il faut être administrateur complet
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_configurer_dist($faire, $type, $id, $qui, $opt) {
	return
		$qui['statut'] == '0minirezo'
		AND !$qui['restreint']
		;
}

/**
 * Autorisation de sauvegarder la base de données
 *
 * Il faut être administrateur (y compris restreint)
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_sauvegarder_dist($faire, $type, $id, $qui, $opt) {
	return
		$qui['statut'] == '0minirezo'
		;
}

/**
 * Autorisation d'effacer la base de données
 *
 * Il faut être webmestre
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_detruire_dist($faire, $type, $id, $qui, $opt) {
	return
		autoriser('webmestre', null, null, $qui, $opt);
}

/**
 * Autorisation de prévisialiser un auteur
 *
 * Il faut être administrateur ou que l'auteur à prévisualiser
 * ait au moins publié un article
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_auteur_previsualiser_dist($faire, $type, $id, $qui, $opt) {
	// les admins peuvent "previsualiser" une page auteur
	if ($qui['statut'] == '0minirezo'
		AND !$qui['restreint']) return true;
	// "Voir en ligne" si l'auteur a un article publie
	$n = sql_fetsel('A.id_article', 'spip_auteurs_liens AS L LEFT JOIN spip_articles AS A ON (L.objet=\'article\' AND L.id_objet=A.id_article)', "A.statut='publie' AND L.id_auteur=".sql_quote($id));
	return $n ? true : false;
}

/**
 * Autorisation de créer un auteur
 *
 * Il faut être administrateur (restreint compris).
 *
 * @note
 *     Seuls les administrateurs complets ont accès à tous les
 *     champs du formulaire d'édition d'un auteur. À la création
 *     d'un auteur, son statut est 'poubelle'. C'est l'autorisation
 *     de modifier qui permet de changer les informations sensibles
 *     (statut, login, pass, etc.) à l'institution.
 * 
 * @see auteur_inserer()
 * @see auteur_instituer()
 * @see autoriser_auteur_modifier_dist() 
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_auteur_creer_dist($faire, $type, $id, $qui, $opt) {
	return ($qui['statut'] == '0minirezo');
}


/**
 * Autorisation de modifier un auteur
 *
 * Attention tout depend de ce qu'on veut modifier. Il faut être au moins
 * rédacteur, mais on ne peut pas promouvoir (changer le statut) un auteur
 * avec des droits supérieurs au sien.
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_auteur_modifier_dist($faire, $type, $id, $qui, $opt) {

	// Ni admin ni redacteur => non
	if (!in_array($qui['statut'], array('0minirezo', '1comite')))
		return false;

	// Un redacteur peut modifier ses propres donnees mais ni son login/email
	// ni son statut (qui sont le cas echeant passes comme option)
	if ($qui['statut'] == '1comite') {
		if ($opt['webmestre'])
			return false;
		elseif ($opt['statut'] OR $opt['restreintes'] OR $opt['email'])
			return false;
		elseif ($id == $qui['id_auteur'])
			return true;
		else
			return false;
	}

	// Un admin restreint peut modifier/creer un auteur non-admin mais il
	// n'a le droit ni de le promouvoir admin, ni de changer les rubriques
	if ($qui['restreint']) {
		if ($opt['webmestre'])
			return false;
		elseif ($opt['statut'] == '0minirezo' OR $opt['restreintes'])
			return false;
		else {
			if ($id == $qui['id_auteur']) {
				if ($opt['statut'])
					return false;
				else
					return true;
			}
			else if ($id_auteur = intval($id)) {
				$t = sql_fetsel("statut", "spip_auteurs", "id_auteur=$id_auteur");
				if ($t AND $t['statut'] != '0minirezo')
					return true;
				else
					return false;
			}
			// id = 0 => creation
			else
				return true;
		}
	}

	// Un admin complet fait ce qu'elle veut
	// sauf se degrader
	if ($id == $qui['id_auteur'] && $opt['statut'])
		return false;
	// et toucher au statut webmestre si il ne l'est pas lui meme
	// ou si les webmestres sont fixes par constante (securite)
	elseif (isset($opt['webmestre']) AND $opt['webmestre'] AND (defined('_ID_WEBMESTRES') OR !autoriser('webmestre')))
		return false;
	// et modifier un webmestre si il ne l'est pas lui meme
	elseif (intval($id) AND autoriser('webmestre','',0,$id) AND !autoriser('webmestre'))
		return false;
	else
		return true;
}


/**
 * Autorisation d'associer un auteur sur un objet
 *
 * Il faut pouvoir modifier l'objet en question
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_associerauteurs_dist($faire, $type, $id, $qui, $opt){
	return autoriser('modifier', $type, $id, $qui, $opt);
}


/**
 * Autorisation d'upload FTP
 *
 * Il faut être administrateur.
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_chargerftp_dist($faire, $type, $id, $qui, $opt) {
	return $qui['statut'] == '0minirezo';
}

/**
 * Autorisation d'activer le mode debug
 *
 * Il faut être administrateur.
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_debug_dist($faire, $type, $id, $qui, $opt) {
	return $qui['statut'] == '0minirezo';
}

/**
 * Liste les rubriques d'un auteur
 *
 * Renvoie la liste des rubriques liées à cet auteur, independamment de son
 * statut (pour les admins restreints, il faut donc aussi vérifier statut)
 *
 * Mémorise le resultat dans un tableau statique indéxé par les id_auteur.
 * On peut reinitialiser un élément en passant un 2e argument non vide
 * 
 * @param int  $id_auteur Identifiant de l'auteur
 * @param bool $raz       Recalculer le résultat connu pour cet auteur
 * @return array          Liste des rubriques
**/
function liste_rubriques_auteur($id_auteur, $raz=false) {
	static $restreint = array();

	if (!$id_auteur = intval($id_auteur)) return array();
	if ($raz) unset($restreint[$id_auteur]);
	elseif (isset($restreint[$id_auteur])) return $restreint[$id_auteur];

	$rubriques = array();
	if (
		(!isset($GLOBALS['meta']['version_installee']) OR $GLOBALS['meta']['version_installee']>16428)
	  AND $r = sql_allfetsel('id_objet', 'spip_auteurs_liens', "id_auteur=".intval($id_auteur)." AND objet='rubrique' AND id_objet!=0")
	  AND count($r)) {
		$r = array_map('reset',$r);

		// recuperer toute la branche, au format chaine enumeration
		include_spip('inc/rubriques');
		$r = calcul_branche_in($r);
		$r = explode(',',$r);

		// passer les rubriques en index, elimine les doublons
		$r = array_flip($r);
		// recuperer les index seuls
		$r = array_keys($r);
		// combiner pour avoir un tableau id_rubrique=>id_rubrique
		// est-ce vraiment utile ? (on preserve la forme donnee par le code precedent)
		$rubriques = array_combine($r,$r);
	}

	// Affecter l'auteur session le cas echeant
	if (isset($GLOBALS['visiteur_session']['id_auteur'])
	  AND $GLOBALS['visiteur_session']['id_auteur'] == $id_auteur)
		$GLOBALS['visiteur_session']['restreint'] = $rubriques;


	return $restreint[$id_auteur] = $rubriques;
}

/**
 * Autorisation de modifier l'URL d'un objet
 *
 * Il faut pouvoir modifier l'objet.
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_modifierurl_dist($faire, $type, $id, $qui, $opt) {
	return autoriser('modifier', $type, $id, $qui, $opt);
}

/**
 * Autorisation de prévisualiser une rubrique
 *
 * Il faut pouvoir prévisualiser.
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_rubrique_previsualiser_dist($faire,$type,$id,$qui,$opt){
	return autoriser('previsualiser');
}

/**
 * Autorisation d'iconifier une rubrique (mettre un logo)
 *
 * Il faut pouvoir publier dans la rubrique.
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_rubrique_iconifier_dist($faire,$type,$id,$qui,$opt){
	return autoriser('publierdans', 'rubrique', $id, $qui, $opt);
}

/**
 * Autorisation d'iconifier un auteur (mettre un logo)
 *
 * Il faut un administrateur ou que l'auteur soit celui qui demande l'autorisation
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_auteur_iconifier_dist($faire,$type,$id,$qui,$opt){
 return (($id == $qui['id_auteur']) OR
 		(($qui['statut'] == '0minirezo') AND !$qui['restreint']));
}

/**
 * Autorisation d'iconifier un objet (mettre un logo)
 *
 * Il faut pouvoir modifier l'objet
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_iconifier_dist($faire,$type,$id,$qui,$opt){
	// par defaut, on a le droit d'iconifier si on a le droit de modifier
	return autoriser('modifier', $type, $id, $qui, $opt);
}


/**
 * Autorisation OK
 *
 * Autorise toujours !
 * Fonction sans surprise pour permettre les tests.
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true
**/
function autoriser_ok_dist($faire, $type, $id, $qui, $opt) { return true; }

/**
 * Autorisation NIET
 *
 * Refuse toujours !
 * Fonction sans surprise pour permettre les tests.
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          false
**/
function autoriser_niet_dist($faire, $type, $id, $qui, $opt) { return false; }

/**
 * Autorisation de réparer la base de données
 *
 * Il faut pouvoir la détruire (et ne pas être en cours de réinstallation)
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          false
**/
function autoriser_base_reparer_dist($faire, $type, $id, $qui, $opt) {
	if (!autoriser('detruire') OR _request('reinstall'))
		return false;

	return true;
}

/**
 * Autorisation de voir l'onglet infosperso
 *
 * Toujours OK
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_infosperso_dist($faire,$type,$id,$qui,$opt) {
	return true;
}

/**
 * Autorisation de voir le formulaire configurer_langage
 *
 * Toujours OK
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_langage_configurer_dist($faire,$type,$id,$qui,$opt) {
	return true;
}

/**
 * Autorisation de voir l'onglet configurerlangage
 *
 * Calquée sur l'autorisation de voir le formulaire configurer_langage
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_configurerlangage_dist($faire,$type,$id,$qui,$opt) {
	return autoriser('configurer', '_langage', $id, $qui, $opt);
}

/**
 * Autorisation de voir le formulaire configurer_preferences
 *
 * Toujours OK
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_preferences_configurer_dist($faire,$type,$id,$qui,$opt) {
	return true;
}

/**
 * Autorisation de voir l'onglet configurerpreferences
 *
 * Calquée sur l'autorisation de voir le formulaire configurer_preferences
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_configurerpreferences_dist($faire,$type,$id,$qui,$opt) {
	return autoriser('configurer', '_preferences', $id, $qui, $opt);
}

/**
 * Autorisation de voir le menu auteurs
 *
 * Toujours OK
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_auteurs_menu_dist($faire, $type, $id, $qui, $opt){return true;}

/**
 * Autorisation de voir le menu articles
 *
 * Toujours OK
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_articles_menu_dist($faire, $type, $id, $qui, $opt){return true;}

/**
 * Autorisation de voir le menu rubriques
 *
 * Toujours OK
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_rubriques_menu_dist($faire, $type, $id, $qui, $opt){return true;}

/**
 * Autorisation de voir le menu articlecreer 
 *
 * Il faut au moins une rubrique présente.
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_articlecreer_menu_dist($faire, $type, $id, $qui, $opt){
	return verifier_table_non_vide();
}

/**
 * Autorisation de voir le menu auteurcreer
 *
 * Il faut pouvoir créer un auteur !
 * 
 * @see autoriser_auteur_creer_dist()
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_auteurcreer_menu_dist($faire, $type, $id, $qui, $opt) {
	return autoriser('creer', 'auteur', $id, $qui, $opt);
}

/**
 * Autorisation de voir le menu suiviedito
 *
 * Il faut être administrateur (y compris restreint).
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_suiviedito_menu_dist($faire, $type, $id, $qui, $opt){
	return $qui['statut']=='0minirezo';
}

/**
 * Autorisation de voir le menu synchro
 *
 * Il faut être administrateur (y compris restreint).
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_synchro_menu_dist($faire, $type, $id, $qui, $opt){
	return $qui['statut']=='0minirezo';
}

/**
 * Autorisation de purger la queue de travaux
 *
 * Il faut être webmestre.
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_queue_purger_dist($faire, $type, $id, $qui, $opt){
	return autoriser('webmestre');
}


/**
 * Autorisation l'échafaudage de squelettes en Z
 *
 * Il faut être dans l'espace privé (et authentifié),
 * sinon il faut être webmestre (pas de fuite d'informations publiées)
 * 
 * @param  string $faire Action demandée
 * @param  string $type  Type d'objet sur lequel appliquer l'action
 * @param  int    $id    Identifiant de l'objet
 * @param  array  $qui   Description de l'auteur demandant l'autorisation
 * @param  array  $opt   Options de cette autorisation
 * @return bool          true s'il a le droit, false sinon
**/
function autoriser_echafauder_dist($faire, $type, $id, $qui, $opt){
	if (test_espace_prive())
		return intval($qui['id_auteur'])?true:false;
	else
		return autoriser('webmestre','',$id,$qui,$opt);
}


/**
 * Lister les auteurs d'un article
 * 
 * Fonction générique utilisée par plusieurs autorisations
 *
 * @param int $id_article Identifiant de l'article
 * @param string $cond    Condition en plus dans le where de la requête
 * @return array|bool
 *     - array : liste des id_auteur trouvés
 *     - false : serveur SQL indisponible
 */
function auteurs_article($id_article, $cond='')
{
	return sql_allfetsel("id_auteur", "spip_auteurs_liens", "objet='article' AND id_objet=$id_article". ($cond ? " AND $cond" : ''));
}


/**
 * Tester si on est admin restreint sur une rubrique donnée
 * 
 * Fonction générique utilisee dans des autorisations ou assimilée
 *
 * @param int $id_rubrique  Identifiant de la rubrique
 * @return bool             true si administrateur de cette rubrique, false sinon.
 */
function acces_restreint_rubrique($id_rubrique) {
	global $connect_id_rubrique;

	return (isset($connect_id_rubrique[$id_rubrique]));
}


/**
 * Verifier qu'il existe au moins un parent
 * 
 * Fonction utilisee dans des autorisations des boutons / menus du prive des objets enfants (articles, breves, sites)
 *
 * @param string $table  	la table a verifier
 * @return bool             true si un parent existe
 */
function verifier_table_non_vide($table='spip_rubriques') {
	static $done = array();
	if (!isset($done[$table]))
		 $done[$table] = sql_countsel($table)>0;
	return $done[$table];
}

/**
 * Une autorisation determiner la possibilite de s'inscire pour un statut et un id_rubrique,
 * a l'aide de la liste globale des statuts (tableau mode => nom du mode)
 * Utile pour le formulaire d'inscription.
 * Par defaut, seuls 6forum et 1comite possibles, les autres sont en false
 * pour un nouveau mode il suffit de definir l'autorisation specifique
 *
 * @param $faire
 * @param $quoi
 *   statut auteur demande
 * @param $id
 *   id_rubrique eventuel (pas utilise ici, utilise dans des usages persos)
 * @param $qui
 * @param $opt
 * @return bool
 */
function autoriser_inscrireauteur_dist($faire, $quoi, $id, $qui, $opt){

	$s = array_search($quoi, $GLOBALS['liste_des_statuts']);
	switch ($s) {

		case 'info_redacteurs' :
		  return ($GLOBALS['meta']['accepter_inscriptions'] == 'oui');

		case 'info_visiteurs' :
		  return ($GLOBALS['meta']['accepter_visiteurs'] == 'oui' OR $GLOBALS['meta']['forums_publics'] == 'abo');

	}

	return false;
}
?>
traduire.php000066600000015230152525475610007115 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

/**
 * Rechercher tous les lang/file dans le path
 * qui seront ensuite charges dans l'ordre du path
 * version dediee et optimisee pour cet usage de find_in_path
 *
 * @staticvar <type> $dirs
 * @param <type> $file
 * @param <type> $dirname
 * @return <type>
 */
function find_langs_in_path ($file, $dirname='lang') {
	static $dirs=array();
	$liste = array();
	foreach(creer_chemin() as $dir) {
		if (!isset($dirs[$a = $dir . $dirname]))
			$dirs[$a] = (is_dir($a) || !$a) ;
		if ($dirs[$a]) {
			if (is_readable($a .= $file)) {
				$liste[] = $a;
			}
		}
	}
	return array_reverse($liste);
}
//
// Charger un fichier langue
//
// http://doc.spip.org/@chercher_module_lang
function chercher_module_lang($module, $lang = '') {
	if ($lang)
		$lang = '_'.$lang;

	// 1) dans un repertoire nomme lang/ se trouvant sur le chemin
	if ($f = ($module == 'local'
		? find_in_path($module.$lang.'.php', 'lang/')
		: find_langs_in_path($module.$lang.'.php', 'lang/')))
		return is_array($f)?$f:array($f);

	// 2) directement dans le chemin (old style, uniquement pour local)
	return (($module == 'local') OR strpos($module, '/'))
		? (($f = find_in_path($module.$lang. '.php')) ? array($f):false)
		: false;
}

// http://doc.spip.org/@charger_langue
function charger_langue($lang, $module = 'spip') {
	if ($lang AND $fichiers_lang = chercher_module_lang($module, $lang)) {
		$GLOBALS['idx_lang']='i18n_'.$module.'_'.$lang;
		include(array_shift($fichiers_lang));
		surcharger_langue($fichiers_lang);
	} else {
		// si le fichier de langue du module n'existe pas, on se rabat sur
		// la langue par defaut du site -- et au pire sur le francais, qui
		// *par definition* doit exister, et on copie le tableau dans la
		// var liee a la langue
		$l = $GLOBALS['meta']['langue_site'];
		if (!$fichiers_lang = chercher_module_lang($module, $l))
			$fichiers_lang = chercher_module_lang($module, _LANGUE_PAR_DEFAUT);

		if ($fichiers_lang) {
			$GLOBALS['idx_lang']='i18n_'.$module.'_' .$l;
			include(array_shift($fichiers_lang));
			surcharger_langue($fichiers_lang);
			$GLOBALS['i18n_'.$module.'_'.$lang]
				= &$GLOBALS['i18n_'.$module.'_'.$l];
			#spip_log("module de langue : ${module}_$l.php");
		}
	}
}

//
// Surcharger le fichier de langue courant avec un ou plusieurs autre (tordu, hein...)
//
// http://doc.spip.org/@surcharger_langue
function surcharger_langue($fichiers) {
	static $surcharges = array();
	if (!isset($GLOBALS['idx_lang'])) return;

	if (!is_array($fichiers)) $fichiers = array($fichiers);
	if (!count($fichiers)) return;
	foreach($fichiers as $fichier){
		if (!isset($surcharges[$fichier])) {
			$idx_lang_normal = $GLOBALS['idx_lang'];
			$GLOBALS['idx_lang'] = $GLOBALS['idx_lang'].'@temporaire';
			include($fichier);
			$surcharges[$fichier] = $GLOBALS[$GLOBALS['idx_lang']];
			unset ($GLOBALS[$GLOBALS['idx_lang']]);
			$GLOBALS['idx_lang'] = $idx_lang_normal;
		}
		if (is_array($surcharges[$fichier])) {
			$GLOBALS[$GLOBALS['idx_lang']] = array_merge(
				(array)$GLOBALS[$GLOBALS['idx_lang']],
				$surcharges[$fichier]
			);
		}
	}
}

//
// Traduire une chaine internationalisee
//
// http://doc.spip.org/@inc_traduire_dist
function inc_traduire_dist($ori, $lang) {
	static $deja_vu = array();
	static $local = array();
  
	if (isset($deja_vu[$lang][$ori]) AND (_request('var_mode') != 'traduction'))
		return $deja_vu[$lang][$ori];

	// modules demandes explicitement <xxx|yyy|zzz:code> cf MODULES_IDIOMES
	if (strpos($ori,':')) {
		list($modules,$code) = explode(':',$ori,2);
		$modules = explode('|', $modules);
		$ori_complet = $ori;
	} else {
		$modules = array('spip', 'ecrire');
		$code = $ori;
		$ori_complet = implode('|', $modules) . ':' . $ori;
	}

	$text = '';
	// parcourir tous les modules jusqu'a ce qu'on trouve
	foreach ($modules as $module) {
		$var = "i18n_".$module."_".$lang;

		if (empty($GLOBALS[$var])) {
			charger_langue($lang, $module);

			// surcharge perso
			// -- on cherche (lang/)local_xx.php ...
			if (!isset($local['local_'.$lang])) {
				// redéfinir la langue en cours pour les surcharges (chercher_langue a pu le changer)
				$GLOBALS['idx_lang']= $var;
				$local['local_'.$lang] = chercher_module_lang('local', $lang);
			}
			if ($local['local_'.$lang])
				surcharger_langue($local['local_'.$lang]);
			// ... puis (lang/)local.php
			if (!isset($local['local']))
				$local['local'] = chercher_module_lang('local');
			if ($local['local'])
				surcharger_langue($local['local']);
		}

		if (isset($GLOBALS[$var][$code])) {
			$module_retenu = $module;
			$text = $GLOBALS[$var][$code];
			break;
		}
	}

	// Retour aux sources si la chaine est absente dans la langue cible ;
	// on essaie d'abord la langue du site, puis a defaut la langue fr
	$langue_retenue = $lang;
	if (!strlen($text)
	AND $lang !== _LANGUE_PAR_DEFAUT) {
		if ($lang !== $GLOBALS['meta']['langue_site']) {
			$text = inc_traduire_dist($ori, $GLOBALS['meta']['langue_site']);
			$langue_retenue = (!strlen($text) ? $GLOBALS['meta']['langue_site'] : '');
		}
		else {
			$text = inc_traduire_dist($ori, _LANGUE_PAR_DEFAUT);
			$langue_retenue = (!strlen($text) ? _LANGUE_PAR_DEFAUT : '');
		}
	}

	// Supprimer la mention <NEW> ou <MODIF>
	if (substr($text,0,1) === '<')
		$text = str_replace(array('<NEW>', '<MODIF>'), array(), $text);

	// Si on n'est pas en utf-8, la chaine peut l'etre...
	// le cas echeant on la convertit en entites html &#xxx;
	if ($GLOBALS['meta']['charset'] !== 'utf-8'
	AND preg_match(',[\x7f-\xff],S', $text)) {
		include_spip('inc/charsets');
		$text = charset2unicode($text,'utf-8');
	}

	if (_request('var_mode') == 'traduction') {
		if ($text)  {
			$classe = 'debug-traduction' . ($module_retenu == 'ecrire' ? '-prive' : '');
			$text = '<span lang=' . $langue_retenue . ' class=' . $classe . ' title=' . $ori_complet . '(' . $langue_retenue . ')>' . $text . '</span>';
			$text = str_replace(
						array("$module_retenu:", "$module_retenu|"),
						array("*$module_retenu*:", "*$module_retenu*|"),
						$text);
		}
	}
	else {
		$deja_vu[$lang][$ori] = $text;
	}

	return $text;
}
?>cvt_multietapes.php000066600000020213152525475610010503 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

/*
 * CVT Multi etapes
 * Module facilitant l'ecriture de formulaires CVT
 * en plusieurs etapes
 *
 * #FORMULAIRE_TRUC
 *
 * Squelette :
 * Chaque etape est representee par un squelette independant qui doit
 * implementer un formulaire autonome pour les saisies de l'etape n
 * formulaires/truc.html pour l'etape 1
 * formulaires/truc_2.html pour l'etape 2
 * formulaires/truc_n.html pour l'etape n
 *
 * Si un squelette formulaires/truc_n.html manque pour l'etape n
 * c'est formulaires/truc.html qui sera utilise
 * (charge a lui de gerer le cas de cette etape)
 *
 * Charger :
 * formulaires_truc_charger_dist() :
 *	passer '_etapes' => nombre total d'etapes de saisies (>1 !)
 *  indiquer toutes les valeurs a saisir sur toutes les pages
 *  comme si il s'agissait d'un formulaire unique
 *
 * Verifier :
 * le numero d'etape courante est disponible dans $x=_request('_etape'), si necessaire
 * _request() permet d'acceder aux saisies effectuees depuis l'etape 1,
 * comme si les etapes 1 a $x avaient ete saisies en une seule fois
 *
 * formulaires_truc_verifier_1_dist() : verifier les saisies de l'etape 1 uniquement
 * formulaires_truc_verifier_2_dist() : verifier les saisies de l'etape 2
 * formulaires_truc_verifier_n_dist() : verifier les saisies de l'etape n
 *
 * Il est possible d'implementer toutes les verifications dans une fonction unique qui sera alors appelee
 * avec en premier argument le numero de l'etape a verifier
 * formulaires_truc_verifier_etape_dist($etape,...) : verifier les saisies de l'etape $etape uniquement
 *
 * A chaque etape x, les etapes 1 a x sont appelees en verification
 * pour verifier l'absence de regression dans la validation (erreur, tentative de reinjection ...)
 * en cas d'erreur, la saisie retourne a la premiere etape en erreur.
 * en cas de succes, l'etape est incrementee, sauf si c'est la derniere.
 * Dans ce dernier cas on declenche traiter()
 *
 * Traiter
 * formulaires_truc_traiter_dist() : ne sera appele que lorsque *toutes*
 * les etapes auront ete saisies sans erreur.
 * La fonction traiter peut donc traiter l'ensemble des saisies comme si il s'agissait d'un formulaire unique
 * dans lequel toutes les donnees auraient ete saisies en une fois
 *
 *
 */

/**
 * Reinjecter dans _request() les valeurs postees
 * dans les etapes precedentes
 *
 * @param string $form
 * @return array
 */
function cvtmulti_recuperer_post_precedents($form){
	include_spip('inc/filtres');
	if ($form
	  AND $c = _request('cvtm_prev_post')
		AND $c = decoder_contexte_ajax($c, $form)){
		#var_dump($c);
		
		# reinjecter dans la bonne variable pour permettre de retrouver
		# toutes les saisies dans un seul tableau
		if ($_SERVER['REQUEST_METHOD']=='POST')
			$store = &$_POST;
		else
			$store = &$_GET;

		foreach($c as $k=>$v)
			// on ecrase pas si saisi a nouveau !
			if (!isset($store[$k]))
				$_REQUEST[$k] = $store[$k] = $v;
			// mais si tableau des deux cotes, on merge avec priorite a la derniere saisie
			elseif(is_array($store[$k])
			  AND is_array($v)
				AND $z = array_keys($v)
				AND !is_numeric(reset($z))
				AND $z = array_keys($store[$k])
				AND !is_numeric(reset($z))
				)
				$_REQUEST[$k] = $store[$k] = array_merge($v,$store[$k]);

		// vider pour eviter un second appel a verifier_n
		// en cas de double implementation (unipotence)
		set_request('cvtm_prev_post');
		return array($c['_etape'],$c['_etapes']);
	}
	return false;
}

/**
 * Sauvegarder les valeurs postees dans une variable encodee
 * pour les recuperer a la prochaine etape
 *  
 * @param string $form
 * @param bool $je_suis_poste
 * @param array $valeurs
 * @return array
 */
function cvtmulti_sauver_post($form, $je_suis_poste, &$valeurs){
	if (!isset($valeurs['_cvtm_prev_post'])){
		$post = array('_etape'=>$valeurs['_etape'],'_etapes'=>$valeurs['_etapes']);
		foreach(array_keys($valeurs) as $champ){
			if (substr($champ,0,1)!=='_'){
				if ($je_suis_poste || (isset($valeurs['_forcer_request']) && $valeurs['_forcer_request'])) {
					if (($v = _request($champ))!==NULL)
						$post[$champ] = $v;
				}
			}
		}
		include_spip('inc/filtres');
		$c = encoder_contexte_ajax($post,$form);
		if (!isset($valeurs['_hidden']))
			$valeurs['_hidden'] = '';
		$valeurs['_hidden'] .= "<input type='hidden' name='cvtm_prev_post' value='$c' />";
		// marquer comme fait, pour eviter double encodage (unipotence)
		$valeurs['_cvtm_prev_post'] = true;
	}
	return $valeurs;
}


/**
 * Reperer une demande de formulaire CVT multi page
 * et la reformater
 * 
 * @param <type> $flux
 * @return <type> 
 */
function cvtmulti_formulaire_charger($flux){
	#var_dump($flux['data']['_etapes']);
	if (is_array($flux['data'])
	  AND isset($flux['data']['_etapes'])){
		$form = $flux['args']['form'];
		$je_suis_poste = $flux['args']['je_suis_poste'];
		$nb_etapes = $flux['data']['_etapes'];
		$etape = _request('_etape');
		$etape = min(max($etape,1),$nb_etapes);
		set_request('_etape',$etape);
		$flux['data']['_etape'] = $etape;

		// sauver les posts de cette etape pour les avoir a la prochaine etape
		$flux['data'] = cvtmulti_sauver_post($form, $je_suis_poste, $flux['data']);
		#var_dump($flux['data']);
	}
	return $flux;
}


/**
 * Verifier les etapes de saisie
 *
 * @param array $flux
 * @return array
 */
function cvtmulti_formulaire_verifier($flux){
	#var_dump('Pipe verifier');
	
	if ($form = $flux['args']['form']
	  AND ($e = cvtmulti_recuperer_post_precedents($form))!==false){
		// recuperer l'etape saisie et le nombre d'etapes total
		list($etape,$etapes) = $e;
		$etape_demandee = _request('aller_a_etape'); // possibilite de poster en entier dans aller_a_etape

		// lancer les verifs pour chaque etape deja saisie de 1 a $etape
		$erreurs = array();
		$derniere_etape_ok = 0;
		$e = 0;
		while ($e<$etape AND $e<$etapes){
			$e++;
			$erreurs[$e] = array();
			if ($verifier = charger_fonction("verifier_$e","formulaires/$form/",true))
				$erreurs[$e] = call_user_func_array($verifier, $flux['args']['args']);
			elseif ($verifier = charger_fonction("verifier_etape","formulaires/$form/",true)){
				$args = $flux['args']['args'];
				array_unshift($args, $e);
				$erreurs[$e] = call_user_func_array($verifier, $args);
			}
			if ($derniere_etape_ok==$e-1 AND !count($erreurs[$e]))
				$derniere_etape_ok = $e;
			// possibilite de poster dans _retour_etape_x
			if (!is_null(_request("_retour_etape_$e")))
				$etape_demandee = $e;
		}

		// si la derniere etape OK etait la derniere
		// on renvoie le flux inchange et ca declenche traiter
		if ($derniere_etape_ok==$etapes AND !$etape_demandee){
			return $flux;
		}
		else {
			$etape = $derniere_etape_ok+1;
			if ($etape_demandee>0 AND $etape_demandee<$etape)
				$etape = $etape_demandee;
			$etape = min($etape,$etapes);
			#var_dump("prochaine etape $etape");
			// retourner les erreurs de l'etape ciblee
			$flux['data'] = isset($erreurs[$etape]) ? $erreurs[$etape] : array() ;
			$flux['data']['_etapes'] = "etape suivante $etape";
			set_request('_etape',$etape);
		}
	}
	return $flux;
}

/**
 * Selectionner le bon fond en fonction de l'etape
 * L'etape 1 est sur le fond sans suffixe
 * Les autres etapes x sont sur le fond _x
 * 
 * @param array $flux
 * @return array
 */
function cvtmulti_styliser($flux){
	if (strncmp($flux['args']['fond'],'formulaires/',12)==0
	  AND isset($flux['args']['contexte']['_etapes'])
	  AND isset($flux['args']['contexte']['_etape'])
	  AND ($e=$flux['args']['contexte']['_etape'])>1
		AND $ext = $flux['args']['ext']
		AND $f=$flux['data']
		AND file_exists($f."_$e.$ext"))
		$flux['data'] = $f."_$e";
	return $flux;
}


?>
plonger.php000066600000007720152525475610006751 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

include_spip('inc/texte');

// http://doc.spip.org/@inc_plonger_dist
function inc_plonger_dist($id_rubrique, $idom="", $list=array(), $col = 1, $exclu=0, $do='aff') {
	global  $spip_lang_left;
	
	if ($list) $id_rubrique = $list[$col-1];
	
	$ret = '';

	# recherche les filles et petites-filles de la rubrique donnee
	# en excluant une eventuelle rubrique interdite (par exemple, lorsqu'on
	# deplace une rubrique, on peut la deplacer partout a partir de la
	# racine... sauf vers elle-meme ou sa propre branche)
	$ordre = array();
	$rub = array();

	$res = sql_select("rub1.id_rubrique, rub1.titre, rub1.id_parent, rub1.lang, rub1.langue_choisie, rub2.id_rubrique AS id_enfant",
			  "spip_rubriques AS rub1 LEFT JOIN spip_rubriques AS rub2 ON (rub1.id_rubrique = rub2.id_parent)",
			  "rub1.id_parent = ".sql_quote($id_rubrique)."
			AND rub1.id_rubrique!=".sql_quote($exclu)."
			AND (rub2.id_rubrique IS NULL OR rub2.id_rubrique!=".sql_quote($exclu).")",  "", "0+rub1.titre,rub1.titre");

	while ($row = sql_fetch($res)) {
		if (autoriser('voir','rubrique',$row['id_rubrique'])){
			$rub[$row['id_rubrique']]['enfants'] = $row['id_enfant'];
			if ($row['id_parent'] == $id_rubrique) {
				$t = trim(typo(supprimer_numero($row['titre'])));
				if ($row['langue_choisie'] != 'oui')
					$t .= ' <small title="'
						.traduire_nom_langue($row['lang'])
						.'">[' . $row['lang'] . ']</small>';
					$ordre[$row['id_rubrique']] = $t;
			}
		}
	}
	$next = isset($list[$col]) ? $list[$col] : 0;
	if ($ordre) {
		$rec = generer_url_ecrire('plonger',"rac=$idom&exclus=$exclu&do=$do&col=".($col+1));
		$info = generer_url_ecrire('informer', "type=rubrique&rac=$idom&do=$do&id=");
		$args = "'$idom',this,$col,'$spip_lang_left','$info',event";
		while (list($id, $titrebrut) = each($ordre)) {

			$titre = supprimer_numero($titrebrut);

			$classe1 = $id_rubrique ? 'petite-rubrique' : "petit-secteur";
			if (isset($rub[$id]["enfants"])) {
				$classe2 = " class='rub-ouverte'";
				$url = "\nhref='$rec&amp;id=$id'" ;
			} else {  $url = $classe2 = '' ; }

			$js_func = $do . '_selection_titre';
			$click = "\nonclick=\"changerhighlight(this.parentNode.parentNode.parentNode);\nreturn "
			. (!is_array($list) ? ' false' 
			   : "aff_selection_provisoire($id,$args)")
# ce lien provoque la selection (directe) de la rubrique cliquee
# et l'affichage de son titre dans le bandeau
			. "\"\nondblclick=\""
			. "$js_func(this."
			. "firstChild.nodeValue,"
			. $id
			. ",'selection_rubrique','id_parent');"
			. "\nreturn aff_selection_provisoire($id,$args);"
			. "\"";

			$ret .= "<div class='"
			. (($id == $next) ? "item on" : "item")
			. "'><div class='"
			. $classe1
			. "'><div$classe2><a"
			. $url
			. $click
			. ">"
			. $titre
			. "</a></div></div></div>";
		}
	}

	$idom2 = $idom . "_col_".($col+1);
	$left = ($col*150);

	return http_img_pack("searching.gif", "*", "style='visibility: hidden; position: absolute; $spip_lang_left: "
	. ($left-30)
	. "px; top: 2px; z-index: 2;' id='img_$idom2'")
	. "<div style='width: 150px; height: 100%; overflow: auto; position: absolute; top: 0px; $spip_lang_left: "
	.($left-150)
	."px;'>"
	. $ret
	. "\n</div>\n<div id='$idom2'>"
	. ($next
	   ? inc_plonger_dist($id_rubrique, $idom, $list, $col+1, $exclu)
	   : "")
	. "\n</div>";
}

?>
lien_court.php000066600000002354152525475610007444 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

/*
 * Cette fonction prend une URL et la raccourcit si elle est trop longue
 * de cette maniere au lieu d'afficher
 * "http://zoumzamzouilam/truc/chose/machin/qui/fait/peur/a/tout/le/monde.mp3"
 * on affiche
 * http://zoumzamzouilam/truc/chose/machin..."
 */
function inc_lien_court($url) {
	$long_url = defined('_MAX_LONG_URL') ? _MAX_LONG_URL : 40;
	$coupe_url = defined('_MAX_COUPE_URL') ? _MAX_COUPE_URL : 35;

	if (strlen($url)>$long_url) {
		$url = substr($url,0,$coupe_url).'...';
	}

	return $url;
}

?>meta.php000066600000015365152525475610006235 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

// Les parametres generaux du site sont dans une table SQL;
// Recopie dans le tableau PHP global meta, car on en a souvent besoin

// duree maximale du cache. Le double pour l'antidater
define('_META_CACHE_TIME', 1<<24);

// http://doc.spip.org/@inc_meta_dist
function inc_meta_dist($table='meta')
{
	// Lire les meta, en cache si present, valide et lisible
	// en cas d'install ne pas faire confiance au meta_cache eventuel
	$cache = cache_meta($table);

	if ((_request('exec')!=='install' OR !test_espace_prive())
	AND $new = jeune_fichier($cache, _META_CACHE_TIME)
	AND lire_fichier_securise($cache, $meta)
	AND $meta = @unserialize($meta))
		$GLOBALS[$table] = $meta;

	if (isset($GLOBALS[$table]['touch']) 
	AND ($GLOBALS[$table]['touch']<time()-_META_CACHE_TIME))
		$GLOBALS[$table] = array();
	// sinon lire en base
	if (!$GLOBALS[$table]) $new = !lire_metas($table);

	// renouveller l'alea general si trop vieux ou sur demande explicite
	if ((test_espace_prive() || isset($_GET['renouvelle_alea']))
	AND $GLOBALS[$table]
	AND (time() > _RENOUVELLE_ALEA + $GLOBALS['meta']['alea_ephemere_date'])) {
		// si on n'a pas l'acces en ecriture sur le cache,
		// ne pas renouveller l'alea sinon le cache devient faux
		if (supprimer_fichier($cache)) {
			include_spip('inc/acces');
			renouvelle_alea();
			$new = false; 
		} else spip_log("impossible d'ecrire dans " . $cache);
	}
	// et refaire le cache si on a du lire en base
	if (!$new) touch_meta(false, $table);
}

// fonctions aussi appelees a l'install ==> spip_query en premiere requete 
// pour eviter l'erreur fatale (serveur non encore configure)

// http://doc.spip.org/@lire_metas
function lire_metas($table='meta') {

	if ($result = spip_query("SELECT nom,valeur FROM spip_$table")) {
		include_spip('base/abstract_sql');
		$GLOBALS[$table] = array();
		while ($row = sql_fetch($result))
			$GLOBALS[$table][$row['nom']] = $row['valeur'];
        sql_free($result);

		if (!$GLOBALS[$table]['charset']
		  OR $GLOBALS[$table]['charset']=='_DEFAULT_CHARSET' // hum, correction d'un bug ayant abime quelques install
		)
			ecrire_meta('charset', _DEFAULT_CHARSET, NULL, $table);

		// noter cette table de configuration dans les meta de SPIP
		if ($table!=='meta') {
			$liste = unserialize($GLOBALS['meta']['tables_config']);
			if (!$liste)
				$liste = array();
			if (!in_array($table, $liste)) {
				$liste[] = $table;
				ecrire_meta('tables_config', serialize($liste));
			}
		}
	}
	return $GLOBALS[$table];
}

// Mettre en cache la liste des meta, sauf les valeurs sensibles 
// pour qu'elles ne soient pas visibiles dans un fichier.souvent en 777
// http://doc.spip.org/@touch_meta
function touch_meta($antidate= false, $table='meta'){
	$file = cache_meta($table);
	if (!$antidate OR !@touch($file, $antidate)) {
		$r = $GLOBALS[$table];
		unset($r['alea_ephemere']);
		unset($r['alea_ephemere_ancien']);
		// le secret du site est utilise pour encoder les contextes ajax que l'on considere fiables
		// mais le sortir deu cache meta implique une requete sql des qu'on a un form dynamique
		// meme si son squelette est en cache
		//unset($r['secret_du_site']);
		if ($antidate) $r['touch']= $antidate;
		ecrire_fichier_securise($file, serialize($r));
	}
}

// http://doc.spip.org/@effacer_meta
function effacer_meta($nom, $table='meta') {
	// section critique sur le cache:
	// l'invalider avant et apres la MAJ de la BD
	// c'est un peu moins bien qu'un vrai verrou mais ca suffira
	// et utiliser une statique pour eviter des acces disques a repetition
	static $touch = array();
	$antidate = time() - (_META_CACHE_TIME<<4);
	if (!isset($touch[$table])) {touch_meta($antidate, $table);}
	sql_delete('spip_' . $table, "nom='$nom'");
	unset($GLOBALS[$table][$nom]);
	if (!isset($touch[$table])) {touch_meta($antidate, $table); $touch[$table] = false;}
}

// http://doc.spip.org/@ecrire_meta
function ecrire_meta($nom, $valeur, $importable = NULL, $table='meta') {

	static $touch = array();
	if (!$nom) return;
	include_spip('base/abstract_sql');
	$res = sql_select("*",'spip_' . $table,"nom=" . sql_quote($nom),'','','','','','continue');
	// table pas encore installee, travailler en php seulement
	if (!$res) {
		$GLOBALS[$table][$nom] = $valeur;
		return;
	}
	$row = sql_fetch($res);
    sql_free($res);

	// ne pas invalider le cache si affectation a l'identique
	// (tant pis si impt aurait du changer)
	if ($row AND $valeur == $row['valeur'] AND $GLOBALS[$table][$nom] == $valeur) return;

	$GLOBALS[$table][$nom] = $valeur;
	// cf effacer pour comprendre le double touch
	$antidate = time() - (_META_CACHE_TIME<<1);
	if (!isset($touch[$table])) {touch_meta($antidate, $table);}
	$r = array('nom' => $nom, 'valeur' => $valeur);
	// Gaffe aux tables sans impt (vieilles versions de SPIP notamment)
	if ($importable AND isset($row['impt'])) $r['impt'] = $importable;
	if ($row) {
		sql_updateq('spip_' . $table, $r,"nom=" . sql_quote($nom));
	} else {
		sql_insertq('spip_' . $table, $r);
	}
	if (!isset($touch[$table])) {touch_meta($antidate, $table); $touch[$table] = false;}
}

function cache_meta($table='meta')
{
	return ($table=='meta') ? _FILE_META : (_DIR_CACHE . $table . '.php');
}

/**
 * Une fonction pour installer une table de configuration supplementaire
 * @param string $table
 */
function installer_table_meta($table) {
	$trouver_table = charger_fonction('trouver_table','base');
	if (!$trouver_table("spip_$table")) {
		include_spip('base/auxiliaires');
		include_spip('base/create');
		creer_ou_upgrader_table("spip_$table", $GLOBALS['tables_auxiliaires']['spip_meta'], false, false);
		$trouver_table('');
	}
	lire_metas($table);
}

/**
 * Une fonction pour supprimer une table de configuration supplementaire
 * si $force=true, on ne verifie pas qu'elle est bien vide
 * 
 * @param string $table
 * @param bool $force
 */
function supprimer_table_meta($table, $force=false) {
	if ($table=='meta') return; // interdit !

	if ($force OR !sql_countsel("spip_$table")) {
		unset($GLOBALS[$table]);
		sql_drop_table("spip_$table");
		// vider le cache des tables
		$trouver_table = charger_fonction('trouver_table','base');
		$trouver_table('');
	}
}
?>
cvt_configurer.php000066600000017233152525475610010322 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

include_spip('inc/config');

/**
 * Proposer un chargement par defaut pour les #FORMULAIRE_CONFIGURER_XXX
 *
 * @param array $flux
 * @return array
 */
function cvtconf_formulaire_charger($flux){
	if (
		$form = $flux['args']['form']
		and strncmp($form,'configurer_',11)==0 // un #FORMULAIRE_CONFIGURER_XXX
	) {
		// Pour tous les formulaires CONFIGURER, ayant une fonction charger ou pas, on teste si autorisé
		include_spip('inc/autoriser');
		if (!autoriser('configurer', '_'.substr($form,11))) {
			return false;
		}
		
		// S'il n'y a pas de fonction charger(), on génère un contexte automatiquement
		if (!charger_fonction("charger","formulaires/$form/",true)) {
			$flux['data'] = cvtconf_formulaires_configurer_recense($form);
			$flux['data']['editable'] = true;
			if (_request('var_mode')=='configurer' AND autoriser('webmestre')){
				if (!_AJAX) var_dump($flux['data']);
				// reinjecter pour la trace au traitement
				$flux['data']['_hidden'] = "<input type='hidden' name='var_mode' value='configurer' />";
			}
		}
	}
	return $flux;
}

/**
 * Proposer un traitement par defaut pour les #FORMULAIRE_CONFIGURER_XXX
 *
 * @param array $flux
 * @return array
 */
function cvtconf_formulaire_traiter($flux){
	if ($form = $flux['args']['form']
	  AND strncmp($form,'configurer_',11)==0 // un #FORMULAIRE_CONFIGURER_XXX
		AND !charger_fonction("traiter","formulaires/$form/",true) // sans fonction traiter()
		) {
		$trace = cvtconf_formulaires_configurer_enregistre($form,$flux['args']['args']);
		$flux['data'] = array('message_ok'=>_T('config_info_enregistree').$trace,'editable'=>true);
	}
	return $flux;
}

/**
 * Enregistrer les donnees d'un formulaire $form appele avec les arguments $args
 * Cette fonction peut etre appellee manuellement et explicitement depuis la fonction traiter()
 * d'un formulaire configurer_xxx dont on veut personaliser le traitement
 * sans reecrire le stockage des donnees
 *
 * @param string $form
 *   nom du formulaire "configurer_xxx"
 * @param array $args
 *   arguments de l'appel de la fonction traiter ($args = func_get_args();)
 * @return string
 */
function cvtconf_formulaires_configurer_enregistre($form,$args){
		$valeurs = array();
		// charger les valeurs
		// ce qui permet de prendre en charge une fonction charger() existante
		// qui prend alors la main sur l'auto detection
		if ($charger_valeurs = charger_fonction("charger","formulaires/$form/",true))
			$valeurs = call_user_func_array($charger_valeurs,$args);
		$valeurs = pipeline(
			'formulaire_charger',
			array(
				'args'=>array('form'=>$form,'args'=>$args,'je_suis_poste'=>false),
				'data'=>$valeurs)
		);
		// ne pas stocker editable !
		unset($valeurs['editable']);

		// recuperer les valeurs postees
		$store = array();
		foreach($valeurs as $k=>$v){
			if (substr($k,0,1)!=='_')
				$store[$k] = _request($k);
		}

		return cvtconf_configurer_stocker($form,$valeurs,$store);
}

/**
 * Definir la regle de conteneur, en fonction de la presence
 * des
 * _meta_table : nom de la table meta ou stocker (par defaut 'meta')
 * _meta_casier : nom du casier dans lequel serializer (par defaut xx de formulaire_configurer_xx)
 * _meta_prefixe : prefixer les meta (alternative au casier) dans la table des meta (par defaur rien)
 * _meta_stockage : Methode externe de stockage. Aucune n'est fournie par le core.
 *
 * @param string $form
 * @param array $valeurs
 * @return array
 */
function cvtconf_definir_configurer_conteneur($form,$valeurs) {
		// stocker en base
		// par defaut, dans un casier serialize dans spip_meta (idem CFG)
		$casier = substr($form,11);
		$table = 'meta';
		$prefixe = '';
		$stockage = '';

		if (isset($valeurs['_meta_casier']))   $casier   = $valeurs['_meta_casier'];
		if (isset($valeurs['_meta_prefixe']))  $prefixe  = $valeurs['_meta_prefixe'];
		if (isset($valeurs['_meta_stockage'])) $stockage = $valeurs['_meta_stockage'] . '::';
			
		// si on indique juste une table, il faut vider les autres proprietes
		// car par defaut on utilise ni casier ni prefixe dans ce cas
		if (isset($valeurs['_meta_table'])) {
			$table = $valeurs['_meta_table'];
			$casier = (isset($valeurs['_meta_casier'])?$valeurs['_meta_casier']:'');
		}
	
		return array($table,$casier,$prefixe,$stockage);
}

/**
 * Retrouver les champs d'un formulaire en parcourant son squelette
 * et en extrayant les balises input, textarea, select
 *
 * @param string $form
 * @return array
 */
function cvtconf_formulaires_configurer_recense($form){
	$valeurs = array('editable'=>' ');

	// sinon cas analyse du squelette
	if ($f = find_in_path($form.'.' . _EXTENSION_SQUELETTES, 'formulaires/')
		AND lire_fichier($f, $contenu)) {

		for ($i=0;$i<2;$i++) {
			// a la seconde iteration, evaluer le fond avec les valeurs deja trouvees
			// permet de trouver aussi les name="#GET{truc}"
			if ($i==1) $contenu = recuperer_fond("formulaires/$form",$valeurs);

			$balises = array_merge(extraire_balises($contenu,'input'),
				extraire_balises($contenu,'textarea'),
				extraire_balises($contenu,'select'));

			foreach($balises as $b) {
				if ($n = extraire_attribut($b, 'name')
					AND preg_match(",^([\w\-]+)(\[\w*\])?$,",$n,$r)
					AND !in_array($n,array('formulaire_action','formulaire_action_args'))
					AND extraire_attribut($b,'type')!=='submit') {
						$valeurs[$r[1]] = '';
						// recuperer les valeurs _meta_xx qui peuvent etre fournies
						// en input hidden dans le squelette
						if (strncmp($r[1],'_meta_',6)==0)
							$valeurs[$r[1]] = extraire_attribut($b,'value');
					}
			}
		}
	}


	cvtconf_configurer_lire_meta($form,$valeurs);
	return $valeurs;
}

/**
 * Stocker les metas
 * @param string $form
 * @param array $valeurs
 * @param array $store
 * @return string
 */
function cvtconf_configurer_stocker($form,$valeurs,$store) {
	$trace = '';
	list($table,$casier,$prefixe,$stockage) = cvtconf_definir_configurer_conteneur($form,$valeurs);
	// stocker en base
	// par defaut, dans un casier serialize dans spip_meta (idem CFG)
	if (!isset($GLOBALS[$table]))
		lire_metas($table);

	$prefixe = ($prefixe?$prefixe.'_':'');
	$table = ($table) ? "/$table/" : ""; 
	$casier = ($casier) ? rtrim($casier,'/').'/' : ""; // slash final, sinon rien
	
	foreach($store as $k=>$v){
		ecrire_config("$stockage$table$prefixe$casier$k", $v);
		if (_request('var_mode')=='configurer' AND autoriser('webmestre')){
			$trace .= "<br />table $table : ".$prefixe.$k." = $v;";
		}
	}
	return $trace;
}

/**
 * Lecture en base des metas d'un form
 * @param string $form
 * @param array $valeurs
 */
function cvtconf_configurer_lire_meta($form,&$valeurs) {
	list($table,$casier,$prefixe,$stockage) = cvtconf_definir_configurer_conteneur($form,$valeurs);

	$table = ($table) ? "/$table/" : ""; 
	$prefixe = ($prefixe?$prefixe.'_':'');
	if ($casier) {
		$meta = lire_config("$stockage$table$prefixe$casier");
		$prefixe = '';
	}
	else {
		$table = rtrim($table, '/');
		$meta = lire_config("$stockage$table");
	}

	foreach($valeurs as $k=>$v){
		if (substr($k,0,1)!=='_')
		$valeurs[$k] = (isset($meta[$prefixe.$k])?$meta[$prefixe.$k]:'');
	}
}


?>
recherche_to_array.php000066600000017061152525475610011132 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/


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


// methodes sql
function inc_recherche_to_array_dist($recherche, $options = array()) {

	// options par defaut
	$options = array_merge(
		array(
		'score' => true,
		'champs' => false,
		'toutvoir' => false,
		'matches' => false,
		'jointures' => false
		),
		$options
	);

	include_spip('inc/rechercher');
	include_spip('inc/autoriser');

	$requete = array(
		"SELECT"=>array(),
		"FROM"=>array(),
		"WHERE"=>array(),
		"GROUPBY"=>array(),
		"ORDERBY"=>array(),
		"LIMIT"=>"",
		"HAVING"=>array()
	);

	$table = sinon($options['table'], 'article');
	if ($options['champs'])
		$champs = $options['champs'];
	else {
		$l = liste_des_champs();
		$champs = $l['article'];
	}
	$serveur = $options['serveur'];

	list($methode, $q, $preg) = expression_recherche($recherche, $options);

	$jointures = $options['jointures']
		? liste_des_jointures()
		: array();

	$_id_table = id_table_objet($table);

	// c'est un pis-aller : ca a peu de chance de marcher, mais mieux quand meme que en conservant la ','
	// (aka ca marche au moins dans certains cas comme avec spip_formulaires_reponses_champs)
	if (strpos($_id_table,",")!==false){
		$_id_table = explode(',',$_id_table);
		$_id_table = reset($_id_table);
	}

	$requete['SELECT'][] = "t.".$_id_table;
	$a = array();
	// Recherche fulltext
	foreach ($champs as $champ => $poids) {
		if (is_array($champ)){
			spip_log("requetes imbriquees interdites");
		} else {
			if (strpos($champ,".")===FALSE)
				$champ = "t.$champ";
			$requete['SELECT'][] = $champ;
			$a[] = $champ.' '.$methode.' '.$q;
		}
	}
	if ($a) $requete['WHERE'][] = join(" OR ", $a);
	$requete['FROM'][] = table_objet_sql($table).' AS t';

	$results = array();

	$s = sql_select(
		$requete['SELECT'], $requete['FROM'], $requete['WHERE'],
		implode(" ",$requete['GROUPBY']),
		$requete['ORDERBY'], $requete['LIMIT'],
		$requete['HAVING'], $serveur
	);

	while ($t = sql_fetch($s,$serveur)
	AND (!isset($t['score']) OR $t['score']>0)) {
		$id = intval($t[$_id_table]);

		if ($options['toutvoir']
		OR autoriser('voir', $table, $id)) {
			// indiquer les champs concernes
			$champs_vus = array();
			$score = 0;
			$matches = array();

			$vu = false;
			foreach ($champs as $champ => $poids) {
				$champ = explode('.',$champ);
				$champ = end($champ);
				// translitteration_rapide uniquement si on est deja en utf-8
				$value = ($GLOBALS['meta']['charset']=='utf-8' ? translitteration_rapide($t[$champ]) : translitteration($t[$champ]));
				if ($n =
					($options['score'] || $options['matches'])
					? preg_match_all($preg, $value, $regs, PREG_SET_ORDER)
					: preg_match($preg, $value)
				) {
					$vu = true;

					if ($options['champs'])
						$champs_vus[$champ] = $t[$champ];
					if ($options['score'])
						$score += $n * $poids;
					if ($options['matches'])
						$matches[$champ] = $regs;

					if (!$options['champs']
					AND !$options['score']
					AND !$options['matches'])
						break;
				}
			}

			if ($vu) {
				if (!isset($results))
					$results = array();
				$results[$id] = array();
				if ($champs_vus)
					$results[$id]['champs'] = $champs_vus;
				if ($score)
					$results[$id]['score'] = $score;
				if ($matches)
					$results[$id]['matches'] = $matches;
			}
		}
	}


	// Gerer les donnees associees
	// ici on est un peu naze : pas capables de reconstruire une jointure complexe
	// on ne sait passer que par table de laison en 1 coup
	if (isset($jointures[$table])
	AND $joints = recherche_en_base(
			$recherche,
			$jointures[$table],
			array_merge($options, array('jointures' => false))
		)
	) {
		include_spip('action/editer_liens');
		$trouver_table = charger_fonction('trouver_table','base');
		$cle_depart = id_table_objet($table);
		$table_depart = table_objet($table,$serveur);
		$desc_depart = $trouver_table($table_depart,$serveur);
		$depart_associable = objet_associable($table);
		foreach ($joints as $table_liee => $ids_trouves) {
			// on peut definir une fonction de recherche jointe pour regler les cas particuliers
			if (
				!(
					$rechercher_joints = charger_fonction("rechercher_joints_${table}_${table_liee}","inc",true)
					or $rechercher_joints = charger_fonction("rechercher_joints_objet_${table_liee}","inc",true)
					or $rechercher_joints = charger_fonction("rechercher_joints_${table}_objet_lie","inc",true)
				)
			){
				$cle_arrivee =  id_table_objet($table_liee);
				$table_arrivee = table_objet($table_liee,$serveur);
				$desc_arrivee = $trouver_table($table_arrivee,$serveur);
				// cas simple : $cle_depart dans la table_liee
				if (isset($desc_arrivee['field'][$cle_depart])){
					$s = sql_select("$cle_depart, $cle_arrivee", $desc_arrivee['table_sql'], sql_in($cle_arrivee, array_keys($ids_trouves)), '','','','',$serveur);
				}
				// cas simple : $cle_arrivee dans la table
				elseif (isset($desc_depart['field'][$cle_arrivee])){
					$s = sql_select("$cle_depart, $cle_arrivee", $desc_depart['table_sql'], sql_in($cle_arrivee, array_keys($ids_trouves)), '','','','',$serveur);
				}
				// sinon cherchons une table de liaison
				// cas recherche principale article, objet lie document : passer par spip_documents_liens
				elseif ($l = objet_associable($table_liee)){
					list($primary, $table_liens) = $l;
					$s = sql_select("id_objet as $cle_depart, $primary as $cle_arrivee", $table_liens, array("objet='$table'",sql_in($primary, array_keys($ids_trouves))), '','','','',$serveur);
				}
				// cas recherche principale auteur, objet lie article: passer par spip_auteurs_liens
				elseif ($l = $depart_associable){
					list($primary, $table_liens) = $l;
					$s = sql_select("$primary as $cle_depart, id_objet as $cle_arrivee", $table_liens, array("objet='$table_liee'",sql_in('id_objet', array_keys($ids_trouves))), '','','','',$serveur);
				}
				// cas table de liaison generique spip_xxx_yyy
				elseif($t=$trouver_table($table_arrivee."_".$table_depart,$serveur)
				  OR $t=$trouver_table($table_depart."_".$table_arrivee,$serveur)){
					$s = sql_select("$cle_depart,$cle_arrivee", $t["table_sql"], sql_in($cle_arrivee, array_keys($ids_trouves)), '','','','',$serveur);
				}
			}
			else
				list($cle_depart,$cle_arrivee,$s) = $rechercher_joints($table,$table_liee,array_keys($ids_trouves), $serveur);

			while ($t = is_array($s)?array_shift($s):sql_fetch($s)) {
				$id = $t[$cle_depart];
				$joint = $ids_trouves[$t[$cle_arrivee]];
				if (!isset($results))
					$results = array();
				if (!isset($results[$id]))
					$results[$id] = array();
				if (isset($joint['score']) and $joint['score']) {
					$results[$id]['score'] += $joint['score'];
				}
				if (isset($joint['champs']) and $joint['champs']) {
					foreach($joint['champs'] as $c => $val) {
						$results[$id]['champs'][$table_liee.'.'.$c] = $val;
					}
				}
				if (isset($joint['matches']) and $joint['matches']) {
						foreach($joint['matches'] as $c => $val) {
						$results[$id]['matches'][$table_liee.'.'.$c] = $val;
					}
				}
			}
		}
	}

	return $results;
}


?>
simplexml_to_array.php000066600000005600152525475610011210 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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




/**
 * Transforme un texte XML en tableau PHP
 * @param string|object $u
 * @param bool $utiliser_namespace
 * @return array
 */
function inc_simplexml_to_array_dist($u, $utiliser_namespace=false){
	// decoder la chaine en SimpleXML si pas deja fait
	if (is_string($u))
		$u = simplexml_load_string($u);
	return array('root'=>@xmlObjToArr($u, $utiliser_namespace));
}


/**
 * Transforme un objet SimpleXML en tableau PHP
 * http://www.php.net/manual/pt_BR/book.simplexml.php#108688
 * xaviered at gmail dot com 17-May-2012 07:00
 *
 * @param object $obj
 * @param bool $utiliser_namespace
 * @return array
**/
function xmlObjToArr($obj, $utiliser_namespace=false) {

	$tableau = array();

	// Cette fonction getDocNamespaces() est longue sur de gros xml. On permet donc
	// de l'activer ou pas suivant le contenu supposé du XML
	if (is_object($obj)) {
		if (is_array($utiliser_namespace)){
			$namespace = $utiliser_namespace;
		}
		else {
			if ($utiliser_namespace)
				$namespace = $obj->getDocNamespaces(true);
			$namespace[NULL] = NULL;
		}

		$name = strtolower((string)$obj->getName());
		$text = trim((string)$obj);
		if (strlen($text) <= 0) {
			$text = NULL;
		}

		$children = array();
		$attributes = array();

		// get info for all namespaces
		foreach( $namespace as $ns=>$nsUrl ) {
			// attributes
			$objAttributes = $obj->attributes($ns, true);
			foreach( $objAttributes as $attributeName => $attributeValue ) {
				$attribName = strtolower(trim((string)$attributeName));
				$attribVal = trim((string)$attributeValue);
				if (!empty($ns)) {
					$attribName = $ns . ':' . $attribName;
				}
				$attributes[$attribName] = $attribVal;
			}

			// children
			$objChildren = $obj->children($ns, true);
			foreach( $objChildren as $childName=>$child ) {
				$childName = strtolower((string)$childName);
				if( !empty($ns) ) {
					$childName = $ns.':'.$childName;
				}
				$children[$childName][] = xmlObjToArr($child, $namespace);
			}
		}

		$tableau = array(
			'name'=>$name,
		);
		if ($text)
			$tableau['text'] = $text;
		if ($attributes)
			$tableau['attributes'] = $attributes;
		if ($children)
			$tableau['children'] = $children;
	}

	return $tableau;
}

?>
charger_php_extension.php000066600000004042152525475610011653 0ustar00<?php

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

// cette fonction (adaptee de phpMyAdmin)
// permet de charger un module php
// dont le nom est donne en argument (ex: 'mysql')
// retourne true en cas de succes
//
// 3 etapes :
// - 1) si le module est deja charge, on sort vainqueur
// - 2) on teste si l'on a la possibilite de charger un module
// via la meta 'dl_allowed'. Si elle n'est pas renseignee, 
// elle sera cree en fonction des parametres de php
// - 3) si l'on peut, on charge le module par la fonction dl()
//
// http://doc.spip.org/@inc_charger_php_extension_dist
function inc_charger_php_extension_dist($module){
	if (extension_loaded($module)) {
		return true;
	}

	// A-t-on le droit de faire un dl() ; si on peut, on memorise la reponse,
	// lourde a calculer, dans les meta
	if (!isset($GLOBALS['meta']['dl_allowed'])) {
		if (!@ini_get('safe_mode') 
		  && @ini_get('enable_dl')
		  && @function_exists('dl')) {
			ob_start();
			phpinfo(INFO_GENERAL); /* Only general info */
			$a = strip_tags(ob_get_contents());
			ob_end_clean();
			if (preg_match('@Thread Safety[[:space:]]*enabled@', $a)) {
				if (preg_match('@Server API[[:space:]]*\(CGI\|CLI\)@', $a)) {
					$GLOBALS['meta']['dl_allowed'] = true;
				} else {
					$GLOBALS['meta']['dl_allowed'] = false;
				}
			} else {
				$GLOBALS['meta']['dl_allowed'] = true;
			}
		} else {
			$GLOBALS['meta']['dl_allowed'] = false;
		}

		// Attention, le ecrire_meta() echouera si on le tente ici ;
		// donc on ne fait rien, et on attend qu'un prochain ecrire_meta()
		// se produisant apres cette sequence enregistre sa valeur.
		#include_spip('inc/meta');
		#ecrire_meta('dl_allowed', $GLOBALS['meta']['dl_allowed'], 'non');
	}

	if (!$GLOBALS['meta']['dl_allowed']) {
		return false;
	}

	/* Once we require PHP >= 4.3, we might use PHP_SHLIB_SUFFIX here */
	if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
		$module_file = 'php_' . $module . '.dll';
	} elseif (PHP_OS=='HP-UX') {
		$module_file = $module . '.sl';
	} else {
		$module_file = $module . '.so';
	}

	return @dl($module_file);
}
?>
cvt_autosave.php000066600000006660152525475610010010 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

/**
 * Nettoyer les echappements
 * @param $val
 * @return string
 */
function autosave_clean_value($val){
	return stripslashes(urldecode($val));
}

/**
 * Reperer une demande de formulaire autosave
 * et la conditionner
 *
 * @param array $flux
 * @return array
 */
function cvtautosave_formulaire_charger($flux){
	if (is_array($flux['data'])
	  AND isset($flux['data']['_autosave_id'])
	  AND $cle_autosave = $flux['data']['_autosave_id']){

		$form = $flux['args']['form'];
		$je_suis_poste = $flux['args']['je_suis_poste'];

		$cle_autosave = serialize($cle_autosave);
		$cle_autosave = $form."_".md5($cle_autosave);

		// si on a un backup en session et qu'on est au premier chargement, non poste
		// on restitue les donnees
		if (isset($GLOBALS['visiteur_session']['session_autosave_'.$cle_autosave])
		  AND !$je_suis_poste) {
			parse_str($GLOBALS['visiteur_session']['session_autosave_'.$cle_autosave], $vars);
			foreach ($vars as $key=>$val) {
				if (isset($flux['data'][$key]))
					$flux['data'][$key] = (is_string($val)?autosave_clean_value($val):array_map('autosave_clean_value',$val));
			}
		}

		/**
		 * Envoyer le input hidden et le bout de js qui l'utilisera
		 */
		$flux['data']['_hidden'] .= "<input type='hidden' name='autosave' class='autosaveactive' value='$cle_autosave' />"
		  .'<script type="text/javascript">/*<![CDATA[*/if (window.jQuery) jQuery(function(){
		  $("input.autosaveactive").closest("form:not(.autosaveon)").autosave({url:"'.$GLOBALS['meta']['adresse_site'].'/"}).addClass("autosaveon");
			});/*]]>*/</script>';

	}
	return $flux;
}

/**
 * Quand on poste definitivement un formulaire autosave,
 * on peut vider la session autosave
 * et on vide aussi toutes les autosave de plus de 72H (delai par defaut) ou sans __timestamp (vieilles sessions)
 * @param $flux
 * @return
 */
function cvtautosave_formulaire_traiter($flux){
	// si on poste 'autosave' c'est qu'on n'a plus besoin de sauvegarder :
	// on elimine les donnees de la session
	if ($cle_autosave = _request('autosave')){
		include_spip('inc/session');
		session_set('session_autosave_'.$cle_autosave, null);
	}

	if (isset($GLOBALS['visiteur_session']) AND $GLOBALS['visiteur_session']){
		// delai par defaut avant purge d'un backup de form : 72H
		if (!defined('_AUTOSAVE_GB_DELAY')) define('_AUTOSAVE_GB_DELAY',72*3600);
		$time_too_old = time() - _AUTOSAVE_GB_DELAY;
		// purger aussi toutes les vieilles autosave
		$session = $GLOBALS['visiteur_session'];
		foreach($session as $k=>$v){
			if (strncmp($k,'session_autosave_',17)==0){
				$timestamp = 0;
				if (preg_match(",&__timestamp=(\d+)$,",$v,$m)){
					$timestamp = intval($m[1]);
				}
				if ($timestamp<$time_too_old)
					session_set($k, null);
			}
		}
	}
	
	return $flux;
}


distant.php000066600000063416152525475610006755 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

/**
 * Ce fichier gère l'obtention de données distantes
 *
 * @package SPIP\Core\Distant
**/
if (!defined('_ECRIRE_INC_VERSION')) return;

if (!defined('_INC_DISTANT_VERSION_HTTP')) define('_INC_DISTANT_VERSION_HTTP', "HTTP/1.0");
if (!defined('_INC_DISTANT_CONTENT_ENCODING')) define('_INC_DISTANT_CONTENT_ENCODING', "gzip");
if (!defined('_INC_DISTANT_USER_AGENT')) define('_INC_DISTANT_USER_AGENT', 'SPIP-' . $GLOBALS['spip_version_affichee'] . " (" . $GLOBALS['home_server'] . ")");
if (!defined('_INC_DISTANT_MAX_SIZE')) define('_INC_DISTANT_MAX_SIZE',2097152);
if (!defined('_INC_DISTANT_CONNECT_TIMEOUT')) define('_INC_DISTANT_CONNECT_TIMEOUT',10);

define('_REGEXP_COPIE_LOCALE', ',' .
       preg_replace('@^https?:@', 'https?:', $GLOBALS['meta']['adresse_site'])
       . "/?spip.php[?]action=acceder_document.*file=(.*)$,");

//@define('_COPIE_LOCALE_MAX_SIZE',2097152); // poids (inc/utils l'a fait)

/**
 * Crée au besoin la copie locale d'un fichier distant
 *
 * Prend en argument un chemin relatif au rep racine, ou une URL
 * Renvoie un chemin relatif au rep racine, ou false
 *
 * @link http://www.spip.net/4155
 *
 * @param $source
 * @param string $mode
 *   'test' - ne faire que tester
 *   'auto' - charger au besoin
 *   'modif' - Si deja present, ne charger que si If-Modified-Since
 *   'force' - charger toujours (mettre a jour)
 * @param string $local
 *   permet de specifier le nom du fichier local (stockage d'un cache par exemple, et non document IMG)
 * @param int $taille_max
 *   taille maxi de la copie local, par defaut _COPIE_LOCALE_MAX_SIZE
 * @return bool|string
 */
function copie_locale($source, $mode='auto', $local=null, $taille_max=null){

	// si c'est la protection de soi-meme, retourner le path
	if ($mode !== 'force' AND preg_match(_REGEXP_COPIE_LOCALE, $source, $match)) {
		$source = substr(_DIR_IMG,strlen(_DIR_RACINE)) . urldecode($match[1]);
		return @file_exists($source) ? $source : false;
	}

	if (is_null($local))
		$local = fichier_copie_locale($source);
	else {
		if (_DIR_RACINE AND strncmp(_DIR_RACINE, $local, strlen(_DIR_RACINE))==0) {
			$local = substr($local, strlen(_DIR_RACINE));
		}
	}

	// si $local = '' c'est un fichier refuse par fichier_copie_locale(),
	// par exemple un fichier qui ne figure pas dans nos documents ;
	// dans ce cas on n'essaie pas de le telecharger pour ensuite echouer
	if (!$local) return false;

	$localrac = _DIR_RACINE.$local;
	$t = ($mode=='force') ? false  : @file_exists($localrac);

	// test d'existence du fichier
	if ($mode=='test') return $t ? $local : '';

	// sinon voir si on doit/peut le telecharger
	if ($local==$source OR !preg_match(',^\w+://,', $source))
		return $local;

	if ($mode=='modif' OR !$t){
		// passer par un fichier temporaire unique pour gerer les echecs en cours de recuperation
		// et des eventuelles recuperations concurantes
		include_spip("inc/acces");
		if (!$taille_max) $taille_max = _COPIE_LOCALE_MAX_SIZE;
		$res = recuperer_page($source, $localrac, false, $taille_max, '', '', false, $t ? filemtime($localrac) : '');
		if (!$res) {
			if (!$t) // si $t c'est sans doute juste un not-modified-since qui fait renvoyer false
				spip_log("copie_locale : Echec recuperation $source sur $localrac",_LOG_INFO_IMPORTANTE);
			return $t ? $local : false;
		}
		spip_log("copie_locale : recuperation $source sur $localrac taille $res OK");

		// pour une eventuelle indexation
		pipeline('post_edition',
			array(
				'args' => array(
					'operation' => 'copie_locale',
					'source' => $source,
					'fichier' => $local
				),
				'data' => null
			)
		);
	}

	return $local;
}

// http://doc.spip.org/@prepare_donnees_post
function prepare_donnees_post($donnees, $boundary = '') {

	// permettre a la fonction qui a demande le post de formater elle meme ses donnees
	// pour un appel soap par exemple
	// l'entete est separe des donnees par un double retour a la ligne
	// on s'occupe ici de passer tous les retours lignes (\r\n, \r ou \n) en \r\n
	if (is_string($donnees) && strlen($donnees)){
		$entete = "";
		// on repasse tous les \r\n et \r en simples \n
		$donnees = str_replace("\r\n","\n",$donnees);
		$donnees = str_replace("\r","\n",$donnees);
		// un double retour a la ligne signifie la fin de l'entete et le debut des donnees
		$p = strpos($donnees, "\n\n");
		if ($p!==FALSE){
			$entete = str_replace("\n", "\r\n", substr($donnees, 0, $p+1));
			$donnees = substr($donnees, $p+2);
		}
		$chaine = str_replace("\n", "\r\n", $donnees);
	}
	else {
		/* boundary automatique */
		// Si on a plus de 500 octects de donnees, on "boundarise"
		if ($boundary===''){
			$taille = 0;
			foreach ($donnees as $cle => $valeur){
				if (is_array($valeur)){
					foreach ($valeur as $val2){
						$taille += strlen($val2);
					}
				} else {
					// faut-il utiliser spip_strlen() dans inc/charsets ?
					$taille += strlen($valeur);
				}
			}
			if ($taille>500){
				$boundary = substr(md5(rand() . 'spip'), 0, 8);
			}
		}

		if (is_string($boundary) and strlen($boundary)){
			// fabrique une chaine HTTP pour un POST avec boundary
			$entete = "Content-Type: multipart/form-data; boundary=$boundary\r\n";
			$chaine = '';
			if (is_array($donnees)) {
				foreach ($donnees as $cle => $valeur) {
					if (is_array($valeur)) {
						foreach ($valeur as $val2) {
							$chaine .= "\r\n--$boundary\r\n";
							$chaine .= "Content-Disposition: form-data; name=\"{$cle}[]\"\r\n";
							$chaine .= "\r\n";
							$chaine .= $val2;
						}
					} else {
						$chaine .= "\r\n--$boundary\r\n";
						$chaine .= "Content-Disposition: form-data; name=\"$cle\"\r\n";
						$chaine .= "\r\n";
						$chaine .= $valeur;
					}
				}
				$chaine .= "\r\n--$boundary\r\n";
			}
		} else {
			// fabrique une chaine HTTP simple pour un POST
			$entete = 'Content-Type: application/x-www-form-urlencoded'."\r\n";
			$chaine = array();
			if (is_array($donnees)) {
				foreach ($donnees as $cle => $valeur) {
					if (is_array($valeur)) {
						foreach ($valeur as $val2) {
							$chaine[] = rawurlencode($cle).'[]='.rawurlencode($val2);
						}
					} else {
						$chaine[] = rawurlencode($cle).'='.rawurlencode($valeur);
					}
				}
				$chaine = implode('&', $chaine);
			} else {
				$chaine = $donnees;
			}
		}
	}
	return array($entete, $chaine);
}

//
// Recupere une page sur le net
// et au besoin l'encode dans le charset local
//
// options : get_headers si on veut recuperer les entetes
// taille_max : arreter le contenu au-dela (0 = seulement les entetes ==>HEAD)
// Par defaut taille_max = 1Mo.
// datas, une chaine ou un tableau pour faire un POST de donnees
// boundary, pour forcer l'envoi par cette methode
// et refuser_gz pour forcer le refus de la compression (cas des serveurs orthographiques)
// date_verif, un timestamp unix pour arreter la recuperation si la page distante n'a pas ete modifiee depuis une date donnee
// uri_referer, preciser un referer different
// Le second argument ($trans) :
// * si c'est une chaine longue, alors c'est un nom de fichier
//   dans lequel on ecrit directement la page
// * si c'est true/null ca correspond a une demande d'encodage/charset
// http://doc.spip.org/@recuperer_page
function recuperer_page($url, $trans = false, $get_headers = false,
                        $taille_max = null, $datas = '', $boundary = '', $refuser_gz = false,
                        $date_verif = '', $uri_referer = ''){
	$gz = false;

	// $copy = copier le fichier ?
	$copy = (is_string($trans) AND strlen($trans)>5); // eviter "false" :-)

	if (is_null($taille_max))
		$taille_max = $copy ? _COPIE_LOCALE_MAX_SIZE : _INC_DISTANT_MAX_SIZE;

	// Accepter les URLs au format feed:// ou qui ont oublie le http://
	$url = preg_replace(',^feed://,i', 'http://', $url);
	if (!preg_match(',^[a-z]+://,i', $url)) $url = 'http://' . $url;

	if ($taille_max==0)
		$get = 'HEAD';
	else
		$get = 'GET';

	if (!empty($datas)) {
		$get = 'POST';
		list($type, $postdata) = prepare_donnees_post($datas, $boundary);
		$datas = $type . 'Content-Length: ' . strlen($postdata) . "\r\n\r\n" . $postdata;
	}

	// dix tentatives maximum en cas d'entetes 301...
	for ($i = 0; $i<10; $i++){
		$url = recuperer_lapage($url, $trans, $get, $taille_max, $datas, $refuser_gz, $date_verif, $uri_referer);
		if (!$url) return false;
		if (is_array($url)){
			list($headers, $result) = $url;
			return ($get_headers ? $headers . "\n" : '') . $result;
		} else spip_log("recuperer page recommence sur $url");
	}
}

// args comme ci-dessus (presque)
// retourne l'URL en cas de 301, un tableau (entete, corps) si ok, false sinon
// si $trans est null -> on ne veut que les headers
// si $trans est une chaine, c'est un nom de fichier pour ecrire directement dedans
// http://doc.spip.org/@recuperer_lapage
function recuperer_lapage($url, $trans = false, $get = 'GET', $taille_max = _INC_DISTANT_MAX_SIZE, $datas = '', $refuser_gz = false, $date_verif = '', $uri_referer = ''){
	// $copy = copier le fichier ?
	$copy = (is_string($trans) AND strlen($trans)>5); // eviter "false" :-)

	// si on ecrit directement dans un fichier, pour ne pas manipuler
	// en memoire refuser gz
	if ($copy)
		$refuser_gz = true;

	// ouvrir la connexion et envoyer la requete et ses en-tetes
	list($f, $fopen) = init_http($get, $url, $refuser_gz, $uri_referer, $datas, _INC_DISTANT_VERSION_HTTP, $date_verif);
	if (!$f){
		spip_log("ECHEC init_http $url");
		return false;
	}

	$result = '';
	// Sauf en fopen, envoyer le flux d'entree
	// et recuperer les en-tetes de reponses
	if ($fopen)
		$headers = '';
	else {
		$headers = recuperer_entetes($f, $date_verif);
		if (is_numeric($headers)){
			fclose($f);
			// Chinoisierie inexplicable pour contrer
			// les actions liberticides de l'empire du milieu
			if ($headers){
				spip_log("HTTP status $headers pour $url");
				return false;
			}
			$t = @parse_url($url);
			$host = $t['host'];
			if (!need_proxy($host)
				AND $result = @file_get_contents($url)){
				// on peuple les headers de vide et on continue
				$headers = array('');
			}
			else
				return false;
		}
		if (!is_array($headers)){ // cas Location
			fclose($f);
			include_spip('inc/filtres');
			return suivre_lien($url, $headers);
		}
		$headers = join('', $headers);
	}

	if ($trans===NULL) return array($headers, '');

	// s'il faut deballer, le faire via un fichier temporaire
	// sinon la memoire explose pour les gros flux

	$gz = preg_match(",\bContent-Encoding: .*gzip,is", $headers) ?
		(_DIR_TMP . md5(uniqid(mt_rand())) . '.tmp.gz') : '';

#	spip_log("entete ($trans $copy $gz)\n$headers");
	if (!$result){
		$result = recuperer_body($f, $taille_max, $gz ? $gz : ($copy ? $trans : ''));
		fclose($f);
	}
	if (!$result)
		return array($headers, $result);

	// Decompresser au besoin
	if ($gz){
		$result = join('', gzfile($gz));
		supprimer_fichier($gz);
	}
	// Faut-il l'importer dans notre charset local ?
	if ($trans===true){
		include_spip('inc/charsets');
		$result = transcoder_page($result, $headers);
	}

	return array($headers, $result);
}

// http://doc.spip.org/@recuperer_body
function recuperer_body($f, $taille_max = _INC_DISTANT_MAX_SIZE, $fichier = ''){
	$taille = 0;
	$result = '';
	$fp = false;
	if ($fichier){
		include_spip("inc/acces");
		$tmpfile = "$fichier.".creer_uniqid().".tmp";
		$fp = spip_fopen_lock($tmpfile, 'w', LOCK_EX);
		if (!$fp AND file_exists($fichier)){
			return filesize($fichier);
		}
		if (!$fp)
			return false;
		$result = 0; // on renvoie la taille du fichier
	}
	while (!feof($f) AND $taille<$taille_max){
		$res = fread($f, 16384);
		$taille += strlen($res);
		if ($fp){
			fwrite($fp, $res);
			$result = $taille;
		}
		else
			$result .= $res;
	}
	if ($fp){
		spip_fclose_unlock($fp);
		spip_unlink($fichier);
		@rename($tmpfile, $fichier);
		if (!file_exists($fichier))
			return false;
	}
	return $result;
}

// Lit les entetes de reponse HTTP sur la socket $f et retourne:
// la valeur (chaine) de l'en-tete Location si on l'a trouvee
// la valeur (numerique) du statut si different de 200, notamment Not-Modified
// le tableau des entetes dans tous les autres cas

// http://doc.spip.org/@recuperer_entetes
function recuperer_entetes($f, $date_verif = ''){
	$s = @trim(fgets($f, 16384));

	if (!preg_match(',^HTTP/[0-9]+\.[0-9]+ ([0-9]+),', $s, $r)){
		return 0;
	}
	$status = intval($r[1]);
	$headers = array();
	$not_modif = $location = false;
	while ($s = trim(fgets($f, 16384))){
		$headers[] = $s . "\n";
		preg_match(',^([^:]*): *(.*)$,i', $s, $r);
		list(, $d, $v) = $r;
		if (strtolower(trim($d))=='location' AND $status>=300 AND $status<400){
			$location = $v;
		}
		elseif ($date_verif AND ($d=='Last-Modified')) {
			if ($date_verif>=strtotime($v)){
				//Cas ou la page distante n'a pas bouge depuis
				//la derniere visite
				$not_modif = true;
			}
		}
	}

	if ($location)
		return $location;
	if ($status!=200 or $not_modif)
		return $status;
	return $headers;
}

// Si on doit conserver une copie locale des fichiers distants, autant que ca
// soit a un endroit canonique -- si ca peut etre bijectif c'est encore mieux,
// mais la tout de suite je ne trouve pas l'idee, etant donne les limitations
// des filesystems
// http://doc.spip.org/@nom_fichier_copie_locale
function nom_fichier_copie_locale($source, $extension){
	include_spip('inc/documents');

	$d = creer_repertoire_documents('distant'); # IMG/distant/
	$d = sous_repertoire($d, $extension); # IMG/distant/pdf/

	// on se place tout le temps comme si on etait a la racine
	if (_DIR_RACINE)
		$d = preg_replace(',^' . preg_quote(_DIR_RACINE) . ',', '', $d);

	$m = md5($source);

	return $d
		. substr(preg_replace(',[^\w-],', '', basename($source)) . '-' . $m, 0, 12)
		. substr($m, 0, 4)
		. ".$extension";
}

//
// Donne le nom de la copie locale de la source
//
// http://doc.spip.org/@fichier_copie_locale
function fichier_copie_locale($source){
	// Si c'est deja local pas de souci
	if (!preg_match(',^\w+://,', $source)){
		if (_DIR_RACINE)
			$source = preg_replace(',^' . preg_quote(_DIR_RACINE) . ',', '', $source);
		return $source;
	}

	// optimisation : on regarde si on peut deviner l'extension dans l'url et si le fichier
	// a deja ete copie en local avec cette extension
	// dans ce cas elle est fiable, pas la peine de requeter en base
	$path_parts = pathinfo($source);
	$ext = $path_parts ? $path_parts['extension'] : '';
	if ($ext
	AND preg_match(',^\w+$,', $ext) // pas de php?truc=1&...
	AND $f = nom_fichier_copie_locale($source, $ext)
	AND file_exists(_DIR_RACINE . $f)
	)
		return $f;


	// Si c'est deja dans la table des documents,
	// ramener le nom de sa copie potentielle

	$ext = sql_getfetsel("extension", "spip_documents", "fichier=" . sql_quote($source) . " AND distant='oui' AND extension <> ''");


	if ($ext) return nom_fichier_copie_locale($source, $ext);

	// voir si l'extension indiquee dans le nom du fichier est ok
	// et si il n'aurait pas deja ete rapatrie

	$ext = $path_parts ? $path_parts['extension'] : '';

	if ($ext AND sql_getfetsel("extension", "spip_types_documents", "extension=" . sql_quote($ext))){
		$f = nom_fichier_copie_locale($source, $ext);
		if (file_exists(_DIR_RACINE . $f))
			return $f;
	}

	// Ping  pour voir si son extension est connue et autorisee
	// avec mise en cache du resultat du ping

	$cache = sous_repertoire(_DIR_CACHE, 'rid') . md5($source);
	if (!@file_exists($cache)
		OR !$path_parts = @unserialize(spip_file_get_contents($cache))
		OR _request('var_mode')=='recalcul'
	){
		$path_parts = recuperer_infos_distantes($source, 0, false);
		ecrire_fichier($cache, serialize($path_parts));
	}
	$ext = $path_parts ? $path_parts['extension'] : '';
	if ($ext AND sql_getfetsel("extension", "spip_types_documents", "extension=" . sql_quote($ext))){
		return nom_fichier_copie_locale($source, $ext);
	}
	spip_log("pas de copie locale pour $source");
}


// Recuperer les infos d'un document distant, sans trop le telecharger
#$a['body'] = chaine
#$a['type_image'] = booleen
#$a['titre'] = chaine
#$a['largeur'] = intval
#$a['hauteur'] = intval
#$a['taille'] = intval
#$a['extension'] = chaine
#$a['fichier'] = chaine
#$a['mime_type'] = chaine

// http://doc.spip.org/@recuperer_infos_distantes
function recuperer_infos_distantes($source, $max = 0, $charger_si_petite_image = true){

	// pas la peine de perdre son temps
	if (!tester_url_absolue($source)) {
		return false;
	}
	
	# charger les alias des types mime
	include_spip('base/typedoc');
	global $mime_alias;

	$a = array();
	$mime_type = '';
	// On va directement charger le debut des images et des fichiers html,
	// de maniere a attrapper le maximum d'infos (titre, taille, etc). Si
	// ca echoue l'utilisateur devra les entrer...
	if ($headers = recuperer_page($source, false, true, $max, '', '', true)){
		list($headers, $a['body']) = preg_split(',\n\n,', $headers, 2);

		if (preg_match(",\nContent-Type: *([^[:space:];]*),i", "\n$headers", $regs))
			$mime_type = (trim($regs[1]));
		else
			$mime_type = ''; // inconnu

		// Appliquer les alias
		while (isset($mime_alias[$mime_type]))
			$mime_type = $mime_alias[$mime_type];

		// Si on a un mime-type insignifiant
		// text/plain,application/octet-stream ou vide
		// c'est peut-etre que le serveur ne sait pas
		// ce qu'il sert ; on va tenter de detecter via l'extension de l'url
		// ou le Content-Disposition: attachment; filename=...
		$t = null;
		if (in_array($mime_type, array('text/plain', '', 'application/octet-stream'))){
			if (!$t
				AND preg_match(',\.([a-z0-9]+)(\?.*)?$,i', $source, $rext)
			){
				$t = sql_fetsel("extension", "spip_types_documents", "extension=" . sql_quote($rext[1],'','text'));
			}
			if (!$t
				AND preg_match(",^Content-Disposition:\s*attachment;\s*filename=(.*)$,Uims", $headers, $m)
				AND preg_match(',\.([a-z0-9]+)(\?.*)?$,i', $m[1], $rext)
			){
				$t = sql_fetsel("extension", "spip_types_documents", "extension=" . sql_quote($rext[1],'','text'));
			}
		}

		// Autre mime/type (ou text/plain avec fichier d'extension inconnue)
		if (!$t)
			$t = sql_fetsel("extension", "spip_types_documents", "mime_type=" . sql_quote($mime_type));

		// Toujours rien ? (ex: audio/x-ogg au lieu de application/ogg)
		// On essaie de nouveau avec l'extension
		if (!$t
			AND $mime_type!='text/plain'
			AND preg_match(',\.([a-z0-9]+)(\?.*)?$,i', $source, $rext)
		){
			$t = sql_fetsel("extension", "spip_types_documents", "extension=" . sql_quote($rext[1],'','text')); # eviter xxx.3 => 3gp (> SPIP 3)
		}


		if ($t){
			spip_log("mime-type $mime_type ok, extension " . $t['extension']);
			$a['extension'] = $t['extension'];
		} else {
			# par defaut on retombe sur '.bin' si c'est autorise
			spip_log("mime-type $mime_type inconnu");
			$t = sql_fetsel("extension", "spip_types_documents", "extension='bin'");
			if (!$t) return false;
			$a['extension'] = $t['extension'];
		}

		if (preg_match(",\nContent-Length: *([^[:space:]]*),i",
			"\n$headers", $regs)
		)
			$a['taille'] = intval($regs[1]);
	}

	// Echec avec HEAD, on tente avec GET
	if (!$a AND !$max){
		spip_log("tenter GET $source");
		$a = recuperer_infos_distantes($source, _INC_DISTANT_MAX_SIZE);
	}

	// si on a rien trouve pas la peine d'insister
	if (!$a) {
		return false;
	}

	// S'il s'agit d'une image pas trop grosse ou d'un fichier html, on va aller
	// recharger le document en GET et recuperer des donnees supplementaires...
	if (preg_match(',^image/(jpeg|gif|png|swf),', $mime_type)){
		if ($max==0
			AND $a['taille']<_INC_DISTANT_MAX_SIZE
				AND (strpos($GLOBALS['meta']['formats_graphiques'], $a['extension'])!==false)
					AND $charger_si_petite_image
		){
			$a = recuperer_infos_distantes($source, _INC_DISTANT_MAX_SIZE);
		}
		else if ($a['body']){
			$a['fichier'] = _DIR_RACINE . nom_fichier_copie_locale($source, $a['extension']);
			ecrire_fichier($a['fichier'], $a['body']);
			$size_image = @getimagesize($a['fichier']);
			$a['largeur'] = intval($size_image[0]);
			$a['hauteur'] = intval($size_image[1]);
			$a['type_image'] = true;
		}
	}

	// Fichier swf, si on n'a pas la taille, on va mettre 425x350 par defaut
	// ce sera mieux que 0x0
	if ($a AND $a['extension']=='swf'
		AND !$a['largeur']
	){
		$a['largeur'] = 425;
		$a['hauteur'] = 350;
	}

	if ($mime_type=='text/html'){
		include_spip('inc/filtres');
		$page = recuperer_page($source, true, false, _INC_DISTANT_MAX_SIZE);
		if (preg_match(',<title>(.*?)</title>,ims', $page, $regs))
			$a['titre'] = corriger_caracteres(trim($regs[1]));
		if (!$a['taille']) $a['taille'] = strlen($page); # a peu pres
	}
	$a['mime_type']=$mime_type;

	return $a;
}


/**
 * Tester si un host peut etre recuperer directement ou doit passer par un proxy
 * on peut passer en parametre le proxy et la liste des host exclus,
 * pour les besoins des tests, lors de la configuration
 *
 * @param string $host
 * @param string $http_proxy
 * @param string $http_noproxy
 * @return string
 */
function need_proxy($host, $http_proxy = null, $http_noproxy = null){
	if (is_null($http_proxy))
		$http_proxy = @$GLOBALS['meta']["http_proxy"];
	if (is_null($http_noproxy))
		$http_noproxy = @$GLOBALS['meta']["http_noproxy"];

	$domain = substr($host, strpos($host, '.'));

	return ($http_proxy
		AND (strpos(" $http_noproxy ", " $host ")===false
		AND (strpos(" $http_noproxy ", " $domain ")===false)))
		? $http_proxy : '';
}

//
// Lance une requete HTTP avec entetes
// retourne le descripteur sur lequel lire la reponse
//
// http://doc.spip.org/@init_http
function init_http($method, $url, $refuse_gz = false, $referer = '', $datas = "", $vers = "HTTP/1.0", $date = ''){
	$user = $via_proxy = $proxy_user = '';
	$fopen = false;

	$t = @parse_url($url);
	$host = $t['host'];
	if ($t['scheme']=='http'){
		$scheme = 'http';
		$noproxy = '';
	} elseif ($t['scheme']=='https') {
		$scheme = 'tls';
		$noproxy = 'tls://';
		if (!isset($t['port']) || !($port = $t['port'])) $t['port'] = 443;
	}
	else {
		$scheme = $t['scheme'];
		$noproxy = $scheme . '://';
	}
	if (isset($t['user']))
		$user = array($t['user'], $t['pass']);

	if (!isset($t['port']) || !($port = $t['port'])) $port = 80;
	if (!isset($t['path']) || !($path = $t['path'])) $path = "/";
	if (@$t['query']) $path .= "?" . $t['query'];

	$f = lance_requete($method, $scheme, $user, $host, $path, $port, $noproxy, $refuse_gz, $referer, $datas, $vers, $date);
	if (!$f){
		// fallback : fopen
		if (!need_proxy($host)
		  AND !_request('tester_proxy')
		  AND (!isset($GLOBALS['inc_distant_allow_fopen']) OR $GLOBALS['inc_distant_allow_fopen'])){
			$f = @fopen($url, "rb");
			spip_log("connexion vers $url par simple fopen");
			$fopen = true;
		}
		else
			$f = false;
		// echec total
	}

	return array($f, $fopen);
}

// http://doc.spip.org/@lance_requete
function lance_requete($method, $scheme, $user, $host, $path, $port, $noproxy, $refuse_gz = false, $referer = '', $datas = "", $vers = "HTTP/1.0", $date = ''){

	$proxy_user = '';
	$http_proxy = need_proxy($host);
	if ($user) $user = urlencode($user[0]) . ":" . urlencode($user[1]);

	$connect = "";
	if ($http_proxy){
		if (defined('_PROXY_HTTPS_VIA_CONNECT') AND $scheme=="tls"){
			$path_host = (!$user ? '' : "$user@") . $host . (($port!=80) ? ":$port" : "");
			$connect = "CONNECT " .$path_host." $vers\r\n"
				."Host: $path_host\r\n"
				."Proxy-Connection: Keep-Alive\r\n";
		}
		else {
			$path = (($scheme=='tls') ? 'https://' : "$scheme://")
				. (!$user ? '' : "$user@")
				. "$host" . (($port!=80) ? ":$port" : "") . $path;
		}
		$t2 = @parse_url($http_proxy);
		$first_host = $t2['host'];
		if (!($port = $t2['port'])) $port = 80;
		if ($t2['user'])
			$proxy_user = base64_encode($t2['user'] . ":" . $t2['pass']);
	}
	else
		$first_host = $noproxy . $host;

	if ($connect){
		$streamContext = stream_context_create(array('ssl' => array('verify_peer' => false, 'allow_self_signed' => true)));
		$f = @stream_socket_client("tcp://$first_host:$port", $nError, $sError, _INC_DISTANT_CONNECT_TIMEOUT, STREAM_CLIENT_CONNECT, $streamContext);
		spip_log("Recuperer $path sur $first_host:$port par $f (via CONNECT)","connect");
		if (!$f) return false;
		stream_set_timeout($f, _INC_DISTANT_CONNECT_TIMEOUT);

		fputs($f, $connect);
		fputs($f, "\r\n");
		$res = fread($f, 1024);
		if (!$res
		  OR !count($res = explode(' ',$res))
		  OR $res[1]!=='200'){
			spip_log("Echec CONNECT sur $first_host:$port","connect"._LOG_INFO_IMPORTANTE);
			fclose($f);
			return false;
		}
		// important, car sinon on lit trop vite et les donnees ne sont pas encore dispo
		stream_set_blocking($f, true);
		// envoyer le handshake
		stream_socket_enable_crypto($f, true,	STREAM_CRYPTO_METHOD_SSLv23_CLIENT);
		spip_log("OK CONNECT sur $first_host:$port","connect");
	}
	else {
		$f = @fsockopen($first_host, $port, $errno, $errstr, _INC_DISTANT_CONNECT_TIMEOUT);
		spip_log("Recuperer $path sur $first_host:$port par $f");
		if (!$f) {
			spip_log("Erreur connexion $errno $errstr",_LOG_ERREUR);
			return false;
		}
		stream_set_timeout($f, _INC_DISTANT_CONNECT_TIMEOUT);
	}

	$site = $GLOBALS['meta']["adresse_site"];

	$req = "$method $path $vers\r\n"
		. "Host: $host\r\n"
		. "User-Agent: " . _INC_DISTANT_USER_AGENT . "\r\n"
		. ($refuse_gz ? '' : ("Accept-Encoding: " . _INC_DISTANT_CONTENT_ENCODING . "\r\n"))
		. (!$site ? '' : "Referer: $site/$referer\r\n")
		. (!$date ? '' : "If-Modified-Since: " . (gmdate("D, d M Y H:i:s", $date) . " GMT\r\n"))
		. (!$user ? '' : ("Authorization: Basic " . base64_encode($user) . "\r\n"))
		. (!$proxy_user ? '' : "Proxy-Authorization: Basic $proxy_user\r\n")
		. (!strpos($vers, '1.1') ? '' : "Keep-Alive: 300\r\nConnection: keep-alive\r\n");

#	spip_log("Requete\n$req");
	fputs($f, $req);
	fputs($f, $datas ? $datas : "\r\n");
	return $f;
}

genie.php000066600000015772152525475610006400 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

// --------------------------
// Gestion des taches de fond
// --------------------------

// Deux difficultes:
// - la plupart des hebergeurs ne fournissent pas le Cron d'Unix
// - les scripts usuels standard sont limites a 30 secondes

// Solution:
// Toute connexion a SPIP s'acheve par un appel a la fonction cron()
// qui appelle la fonction surchargeable genie dans inc/
// Sa definition standard ci-dessous prend dans une liste de taches
// la plus prioritaire, leurs dates etant donnees par leur fichier-verrou.
// Une fonction executant une tache doit retourner un nombre:
// - nul, si la tache n'a pas a etre effecutee
// - positif, si la tache a ete effectuee
// - negatif, si la tache doit etre poursuivie ou recommencee
// Elle recoit en argument la date de la derniere execution de la tache.

// On peut appeler cette fonction avec d'autres taches (pour etendre Spip)
// specifiee par des fonctions respectant le protocole ci-dessus
// On peut modifier la frequence de chaque tache et leur ordre d'analyse
// en modifiant les variables ci-dessous.

//----------

// Les taches sont dans un tableau ('nom de la tache' => periodicite)
// Cette fonction execute la tache la plus urgente
// (celle dont la date de derniere execution + la periodicite est minimale)
// La date de la derniere intervention est donnee par un fichier homonyme,
// de suffixe ".lock", modifie a chaque intervention et des le debut
// de celle-ci afin qu'un processus concurrent ne la demarre pas aussi.
// Les taches les plus longues sont tronconnees, ce qui impose d'antidater
// le fichier de verrouillage (avec la valeur absolue du code de retour).
// La fonction executant la tache est un homonyme de prefixe "genie_".
// Le fichier homonyme du repertoire "genie/" est automatiquement lu
// et il est suppose definir cette fonction.

// http://doc.spip.org/@inc_genie_dist
function inc_genie_dist($taches = array()) {
	include_spip('inc/queue');

	if (_request('exec')=='job_queue')
		return false;

	$force_jobs = array();
	// l'ancienne facon de lancer une tache cron immediatement
	// etait de la passer en parametre a ing_genie_dist
	// on reroute en ajoutant simplement le job a la queue, ASAP
	foreach($taches as $function=>$period)
		$force_jobs[] = queue_add_job($function, _T('tache_cron_asap', array('function'=>$function)), array(time()-abs($period)), "genie/");
	
	// et on passe la main a la gestion de la queue !
	// en forcant eventuellement les jobs ajoute a l'instant
	return queue_schedule(count($force_jobs)?$force_jobs:null);
}

//
// Construction de la liste des taches.
// la cle est la tache, 
// la valeur le temps minimal, en secondes, entre deux memes taches
// NE PAS METTRE UNE VALEUR INFERIEURE A 30 
// les serveurs Http n'accordant en general pas plus de 30 secondes
// a leur sous-processus
//
// http://doc.spip.org/@taches_generales
function taches_generales($taches_generales = array()) {

	// verifier que toutes les taches cron sont planifiees
	// c'est une tache cron !
	$taches_generales['queue_watch'] = 3600*24;

	// MAJ des rubriques publiques (cas de la publication post-datee)
	// est fait au coup par coup a present
	//	$taches_generales['rubriques'] = 3600;

	// Optimisation de la base
	$taches_generales['optimiser'] = 3600*48;

	// cache (chaque 10 minutes => 1/16eme du repertoire cache,
	// soit toutes les 2h40 sur le meme rep)
	$taches_generales['invalideur'] = 600;

	// nouveautes
	if ($GLOBALS['meta']['adresse_neuf'] AND $GLOBALS['meta']['jours_neuf']
	AND ($GLOBALS['meta']['quoi_de_neuf'] == 'oui'))
		$taches_generales['mail']= 3600 * 24 * $GLOBALS['meta']['jours_neuf'];

	// maintenance (ajax, verifications diverses)
	$taches_generales['maintenance'] = 3600 * 2;

	// verifier si une mise a jour de spip est disponible (2 fois par semaine suffit largement)
	$taches_generales['mise_a_jour'] = 3*24*3600;

	return pipeline('taches_generales_cron',$taches_generales);
}

// Pas de fichier a part pour une fonction aussi petite:
// - elle peut retirer les fichiers perimes
// - elle fait appliquer le quota
// En cas de quota sur le CACHE/, nettoyer les fichiers les plus vieux
// http://doc.spip.org/@genie_invalideur_dist
function genie_invalideur_dist($t) {

	include_spip('inc/invalideur');
	$encore = appliquer_quota_cache();

	// si le cache est trop gonfle, redemander la main pour poursuivre
	if ($encore)
		return (0 - $t);
	return 1;
}

/**
 * Une tache periodique pour surveiller les taches crons et les relancer si besoin
 * quand ce cron s'execute, il n'est plus dans la queue, donc il se replanifie
 * lui meme, avec last=time()
 * avec une dose d'aleatoire pour ne pas planifier toutes les taches au meme moment
 *
 * @return int
 */
function genie_queue_watch_dist(){
	static $deja_la = false;
	if ($deja_la) return; // re-entrance si l'insertion des jobs echoue (pas de table spip_jobs a l'upgrade par exemple)
	$deja_la = true;
	$taches = taches_generales();
	$programmees = sql_allfetsel('fonction','spip_jobs',sql_in('fonction',array_keys($taches)));
	$programmees = array_map('reset',$programmees);
	foreach($taches as $tache=>$periode){
		if (!in_array($tache,$programmees))
			queue_genie_replan_job($tache,$periode,time()-round(rand(1,$periode)),0);
	}
	$deja_la = false;
	return 1;
}

/**
 * Replanifier une tache periodique
 *
 * @param string $function
 *   nom de la fonction a appeler
 * @param int $period
 *   periodicite en secondes
 * @param int $last
 *   date du dernier appel (timestamp)
 * @param int $time
 *   date de replanification
 *   si null calculee automaitquement a partir de $last et $period
 *   si 0  = asap mais on n'insere pas le job si deja en cours d'execution
 * @param int $priority
 *   priorite
 * @return void
 */
function queue_genie_replan_job($function,$period,$last=0,$time=null, $priority=0){
	static $done = array();
	if (isset($done[$function])) return;
	$done[$function] = true;
	if (is_null($time)){
		$time=time();
		if ($last)
			$time = max($last+$period,$time);
	}
	if (!$last)
		$last = $time-$period;
	spip_log("replan_job $function $period $last $time $priority",'queue');
	include_spip('inc/queue');
	// on replanifie un job cron
	// uniquement si il n'y en a pas deja un avec le meme nom
	// independament de l'argument
	queue_add_job($function, _T('tache_cron_secondes', array('function'=>$function, 'nb'=>$period)), array($last), "genie/", 'function_only', $time, $priority);
}


?>
informer.php000066600000005353152525475610007124 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

# Les information d'une rubrique selectionnee dans le mini navigateur

// http://doc.spip.org/@inc_informer_dist
function inc_informer_dist($id, $col, $exclus, $rac, $type, $do='aff')
{
	include_spip('inc/texte');
	if ($type == "rubrique") {
		$row = sql_fetsel("titre, descriptif", "spip_rubriques", "id_rubrique = $id");
		if ($row) {
			$titre = typo($row["titre"]);
			$descriptif = propre($row["descriptif"]);
		} else {
			$titre = _T('info_racine_site');
		}
	} else
		$titre = '';

	$res = '';
	if ($type == "rubrique" AND $GLOBALS['spip_display'] != 1 AND isset($GLOBALS['meta']['image_process']))
	  if ($GLOBALS['meta']['image_process'] != "non") {
		$chercher_logo = charger_fonction('chercher_logo', 'inc');
		if ($res = $chercher_logo($id, 'id_rubrique', 'on'))  {
			list($fid, $dir, $nom, $format) = $res;
			include_spip('inc/filtres_images_mini');
			$res = image_reduire("<img src='$fid' alt='' />", 100, 48);
			if ($res)
				$res =  "<div style='float: ".$GLOBALS['spip_lang_right']."; margin-".$GLOBALS['spip_lang_right'].": -5px; margin-top: -5px;'>$res</div>";
		}
	}

	$rac = spip_htmlentities($rac);

# ce lien provoque la selection (directe) de la rubrique cliquee
# et l'affichage de son titre dans le bandeau
	$titre = strtr(str_replace("'", "&#8217;",
			str_replace('"', "&#34;", textebrut($titre))),
		       "\n\r", "  ");

	$js_func = $do . '_selection_titre';
	return "<div style='display: none;'>"
	. "<input type='text' id='".$rac."_sel' value='$id' />"
	. "<input type='text' id='".$rac."_sel2' value=\""
	. entites_html($titre)
	. "\" />"
	. "</div>"
	. "<div class='informer' style='padding: 5px; border-top: 0px;'>"
	. (!$res ? '' : $res)
	. "<p><b>".safehtml($titre)."</b></p>"
	. (!$descriptif ? '' : "<div>".safehtml($descriptif)."</div>")
	. "<div style='text-align: ".$GLOBALS['spip_lang_right'].";'>"
	. "<input type='submit' class='fondo' value='"
	. _T('bouton_choisir')
	. "'\nonclick=\"$js_func('$titre',$id,'selection_rubrique','id_parent'); return false;\" />"
	.  "</div>"
	.  "</div>";
}
?>
layer.php000066600000011720152525475610006412 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

// http://doc.spip.org/@cadre_depliable
function cadre_depliable($icone,$titre,$deplie,$contenu,$ids='',$style_cadre='r'){
	$bouton = bouton_block_depliable($titre,$deplie,$ids);
	return 
		debut_cadre($style_cadre,$icone,'',$bouton, '', '', false)
		. debut_block_depliable($deplie,$ids)
		. "<div class='cadre_padding'>\n"
		. $contenu
		. "</div>\n"
		. fin_block()
		. fin_cadre();
}

// http://doc.spip.org/@block_parfois_visible
function block_parfois_visible($nom, $invite, $masque, $style='', $visible=false){
	return "\n"
	. bouton_block_depliable($invite,$visible,$nom)
	. debut_block_depliable($visible,$nom)
	. $masque
	. fin_block();
}

// http://doc.spip.org/@debut_block_depliable
function debut_block_depliable($deplie,$id=""){
	$class=' blocdeplie';
	// si on n'accepte pas js, ne pas fermer
	if (!$deplie)
		$class=" blocreplie";
	return "<div ".($id?"id='$id' ":"")."class='bloc_depliable$class'>";	
}
// http://doc.spip.org/@fin_block
function fin_block() {
	return "<div class='nettoyeur'></div>\n</div>";
}
// $texte : texte du bouton
// $deplie : true (deplie) ou false (plie) ou -1 (inactif) ou 'incertain' pour que le bouton s'auto init au chargement de la page 
// $ids : id des div lies au bouton (facultatif, par defaut c'est le div.bloc_depliable qui suit)
// http://doc.spip.org/@bouton_block_depliable
function bouton_block_depliable($texte,$deplie,$ids=""){
	$bouton_id = 'b'.substr(md5($texte.microtime()),0,8);

	$class = ($deplie===true)?" deplie":(($deplie==-1)?" impliable":" replie");
	if (strlen($ids)){
		$cible = explode(',',$ids);
		$cible = '#'.implode(",#",$cible);
	}
	else{
		$cible = "#$bouton_id + div.bloc_depliable";
	}

	$b = (strpos($texte,"<h")===false?'h3':'div');
	return "<$b "
	  .($bouton_id?"id='$bouton_id' ":"")
	  ."class='titrem$class'"
	  . (($deplie===-1)
	  	?""
	  	:" onmouseover=\"jQuery(this).depliant('$cible');\""
	  )
	  .">"
	  // une ancre pour rendre accessible au clavier le depliage du sous bloc
	  . "<a href='#' onclick=\"return jQuery(this).depliant_clicancre('$cible');\" class='titremancre'></a>"
	  . "$texte</$b>"
	  . http_script( ($deplie==='incertain')
			? "jQuery(document).ready(function(){if (jQuery('$cible').is(':visible')) $('#$bouton_id').addClass('deplie').removeClass('replie');});"
			: '');
}

//
// Tests sur le nom du butineur
//
// http://doc.spip.org/@verif_butineur
function verif_butineur() {

	global $browser_name, $browser_version;
	global $browser_description, $browser_rev, $browser_barre;
	preg_match(",^([A-Za-z]+)/([0-9]+\.[0-9]+) (.*)$,", $_SERVER['HTTP_USER_AGENT'], $match);
	$browser_name = $match[1];
	$browser_version = $match[2];
	$browser_description = $match[3];
	$GLOBALS['browser_layer'] = ' '; // compat avec vieux scripts qui testent la valeur
	$browser_barre = '';

	if (!preg_match(",opera,i", $browser_description)&&preg_match(",opera,i", $browser_name)) {
		$browser_name = "Opera";
		$browser_version = $match[2];
		$browser_barre = ($browser_version >= 8.5);
	}
	else if (preg_match(",opera,i", $browser_description)) {
		preg_match(",Opera ([^\ ]*),i", $browser_description, $match);
		$browser_name = "Opera";
		$browser_version = $match[1];
		$browser_barre = ($browser_version >= 8.5);
	}
	else if (preg_match(",msie,i", $browser_description)) {
		preg_match(",MSIE ([^;]*),i", $browser_description, $match);
		$browser_name = "MSIE";
		$browser_version = $match[1];
		$browser_barre = ($browser_version >= 5.5);
	}
	else if (preg_match(",KHTML,i", $browser_description) &&
		preg_match(",Safari/([^;]*),", $browser_description, $match)) {
		$browser_name = "Safari";
		$browser_version = $match[1];
		$browser_barre = ($browser_version >= 5.0);
	}
	else if (preg_match(",mozilla,i", $browser_name) AND $browser_version >= 5) {
		// Numero de version pour Mozilla "authentique"
		if (preg_match(",rv:([0-9]+\.[0-9]+),", $browser_description, $match))
			$browser_rev = doubleval($match[1]);
		// Autres Gecko => equivalents 1.4 par defaut (Galeon, etc.)
		else if (strpos($browser_description, "Gecko") and !strpos($browser_description, "KHTML"))
			$browser_rev = 1.4;
		// Machins quelconques => equivalents 1.0 par defaut (Konqueror, etc.)
		else $browser_rev = 1.0;
		$browser_barre = $browser_rev >= 1.3;
	}

	if (!$browser_name) $browser_name = "Mozilla";
}

verif_butineur();
?>
xml.php000066600000013162152525475610006100 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

// http://doc.spip.org/@spip_xml_load
function spip_xml_load($fichier, $strict=true, $clean=true, $taille_max = 1048576, $datas='', $profondeur = -1){
	$contenu = "";
	if (preg_match(",^(http|ftp)://,",$fichier)){
		include_spip('inc/distant');
		$contenu = recuperer_page($fichier,false,false,$taille_max, $datas);
	}
	else lire_fichier ($fichier, $contenu);
	$arbre = array();
	if ($contenu)
		$arbre = spip_xml_parse($contenu, $strict, $clean, $profondeur);
		
	return count($arbre)?$arbre:false;
}

if (!defined('_SPIP_XML_TAG_SPLIT')) define('_SPIP_XML_TAG_SPLIT', "{<([^:>][^>]*?)>}sS");
// http://doc.spip.org/@spip_xml_parse
function spip_xml_parse(&$texte, $strict=true, $clean=true, $profondeur = -1){
	$out = array();
  // enlever les commentaires
  $charset = 'AUTO';
  if ($clean===true){
  	if (preg_match(",<\?xml\s(.*?)encoding=['\"]?(.*?)['\"]?(\s(.*))?\?>,im",$texte,$regs))
  		$charset = $regs[2];
	  $texte = preg_replace(',<!--(.*?)-->,is','',$texte);
	  $texte = preg_replace(',<\?(.*?)\?>,is','',$texte);
		include_spip('inc/charsets');
		$clean = $charset;
		//$texte = importer_charset($texte,$charset);
  }
  if (is_string($clean)) $charset = $clean;
  $txt = $texte;

	// tant qu'il y a des tags
	$chars = preg_split(_SPIP_XML_TAG_SPLIT,$txt,2,PREG_SPLIT_DELIM_CAPTURE);
	while(count($chars)>=2){
		// tag ouvrant
		//$chars = preg_split("{<([^>]*?)>}s",$txt,2,PREG_SPLIT_DELIM_CAPTURE);
	
		// $before doit etre vide ou des espaces uniquements!
		$before = trim($chars[0]);

		if (strlen($before)>0)
			return importer_charset($texte,$charset);//$texte; // before non vide, donc on est dans du texte
	
		$tag = rtrim($chars[1]);
		$txt = $chars[2];
		
		if (strncmp($tag,'![CDATA[',8)==0) return importer_charset($texte,$charset);//$texte;
		if(substr($tag,-1)=='/'){ // self closing tag
			$tag = rtrim(substr($tag,0,strlen($tag)-1));
			$out[$tag][]="";
		}
		else{
			$closing_tag = preg_split(",\s|\t|\n|\r,",trim($tag));
			$closing_tag=reset($closing_tag);
			// tag fermant
			$ncclos = strlen("</$closing_tag>");
			$p = strpos($txt,"</$closing_tag>");
			if ($p!==FALSE  AND (strpos($txt,"<")<$p)){
				$nclose =0; $nopen = 0;
				$d = 0;
				while (
					$p!==FALSE
					AND ($morceau = substr($txt,$d,$p-$d))
					AND (($nopen+=preg_match_all("{<".preg_quote($closing_tag)."(\s*>|\s[^>]*[^/>]>)}is",$morceau,$matches,PREG_SET_ORDER))>$nclose)
					){
					$nclose++;
					$d=$p+$ncclos;
					$p = strpos($txt,"</$closing_tag>",$d);
				}
			}
			if ($p===FALSE){
				if ($strict){
					$out[$tag][]="erreur : tag fermant $tag manquant::$txt"; 
					return $out;
				}
				else return importer_charset($texte,$charset);//$texte // un tag qui constitue du texte a reporter dans $before
			}
			$content = substr($txt,0,$p);
			$txt = substr($txt,$p+$ncclos);
			if ($profondeur==0 OR strpos($content,"<")===FALSE) // eviter une recursion si pas utile
				$out[$tag][] = importer_charset($content,$charset);//$content;
			else
				$out[$tag][]=spip_xml_parse($content, $strict, $clean, $profondeur-1);
		}
		$chars = preg_split(_SPIP_XML_TAG_SPLIT,$txt,2,PREG_SPLIT_DELIM_CAPTURE);
	}
	if (count($out)&&(strlen(trim($txt))==0))
		return $out;
	else
		return importer_charset($texte,$charset);//$texte;
}

// http://doc.spip.org/@spip_xml_aplatit
function spip_xml_aplatit($arbre,$separateur = " "){
	$s = "";
	if (is_array($arbre))
		foreach($arbre as $tag=>$feuille){
			if (is_array($feuille)){
				if ($tag!==intval($tag)){
					$f = spip_xml_aplatit($feuille, $separateur);
					if (strlen($f)) {
						$tagf = explode(" ",$tag);
						$tagf = $tagf[0];
						$s.="<$tag>$f</$tagf>";
					}
					else $s.="<$tag />";
				}
				else
					$s.=spip_xml_aplatit($feuille);
				$s .= $separateur;
			}
			else
				$s.="$feuille$separateur";
		}
	return strlen($separateur) ? substr($s, 0, -strlen($separateur)) : $s;
}

// http://doc.spip.org/@spip_xml_tagname
function spip_xml_tagname($tag){
	if (preg_match(',^([a-z][\w:]*),i',$tag,$reg))
		return $reg[1];
	return "";
}
// http://doc.spip.org/@spip_xml_decompose_tag
function spip_xml_decompose_tag($tag){
	$tagname = spip_xml_tagname($tag);
	$liste = array();
	$p=strpos($tag,' ');
	$tag = substr($tag,$p);
	$p=strpos($tag,'=');
	while($p!==false){
		$attr = trim(substr($tag,0,$p));
		$tag = ltrim(substr($tag,$p+1));
		$quote = $tag{0};
		$p=strpos($tag,$quote,1);
		$cont = substr($tag,1,$p-1);
		$liste[$attr] = $cont;
		$tag = substr($tag,$p+1);
		$p=strpos($tag,'=');
	}
	return array($tagname,$liste);
}

// http://doc.spip.org/@spip_xml_match_nodes
function spip_xml_match_nodes($regexp,&$arbre,&$matches,$init=true){
	if ($init)
		$matches = array();
	if(is_array($arbre) && count($arbre))
		foreach(array_keys($arbre) as $tag){
			if (preg_match($regexp,$tag))
				$matches[$tag] = &$arbre[$tag];
			if (is_array($arbre[$tag]))
				foreach(array_keys($arbre[$tag]) as $occurences)
					spip_xml_match_nodes($regexp,$arbre[$tag][$occurences],$matches,false);
		}
	return (count($matches));
}


?>
commencer_page.php000066600000007514152525475610010250 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

//
// Presentation de l'interface privee, debut du HTML
//

// http://doc.spip.org/@inc_commencer_page_dist
function inc_commencer_page_dist($titre = "", $rubrique = "accueil", $sous_rubrique = "accueil", $id_rubrique = "",$menu=true,$minipres=false, $alertes = true) {
	global $connect_id_auteur;

	include_spip('inc/headers');

	http_no_cache();

	return init_entete($titre, $id_rubrique, $minipres)
	. init_body($rubrique, $sous_rubrique, $id_rubrique,$menu)
	. "<div id='page'>"
	. auteurs_recemment_connectes($connect_id_auteur)
	. ($alertes?alertes_auteur($connect_id_auteur):'')
	. '<div class="largeur">';
}

// envoi du doctype et du <head><title>...</head>
// http://doc.spip.org/@init_entete
function init_entete($titre='', $dummy=0, $minipres=false) {
	include_spip('inc/texte');
	if (!$nom_site_spip = textebrut(typo($GLOBALS['meta']["nom_site"])))
		$nom_site_spip=  _T('info_mon_site_spip');

	$titre = "["
		. $nom_site_spip
		. "]" 
	  . ($titre ? " ".textebrut(typo($titre)):"");

	return _DOCTYPE_ECRIRE
	. html_lang_attributes()
	. "<head>\n"
	. init_head($titre, $dummy, $minipres)
	. "</head>\n";
}

function init_head($titre='', $dummy=0, $minipres=false) {
	return recuperer_fond("prive/squelettes/head/dist",array('titre'=>$titre,'minipres'=>$minipres?' ':''));
}

// fonction envoyant la double serie d'icones de redac
// http://doc.spip.org/@init_body
function init_body($rubrique='accueil', $sous_rubrique='accueil', $id_rubrique='',$menu=true) {
	global $connect_id_auteur, $auth_can_disconnect;

	$res = pipeline('body_prive',"<body class='"
			. init_body_class()." "._request('exec')."'"
			. ($GLOBALS['spip_lang_rtl'] ? " dir='rtl'" : "")
			.'>');

	if (!$menu) return $res;


	$bandeau = charger_fonction('bandeau', 'inc');

	return $res
	 . $bandeau();
}

function init_body_class() {
	$GLOBALS['spip_display'] = isset($GLOBALS['visiteur_session']['prefs']['display'])
		? $GLOBALS['visiteur_session']['prefs']['display']
		: 2;
	$spip_display_navigation = isset($GLOBALS['visiteur_session']['prefs']['display_navigation'])
		? $GLOBALS['visiteur_session']['prefs']['display_navigation']
		: 'navigation_avec_icones';
	$spip_display_outils = isset($GLOBALS['visiteur_session']['prefs']['display_outils'])
		? ($GLOBALS['visiteur_session']['prefs']['display_outils']?'navigation_avec_outils':'navigation_sans_outils')
		: 'navigation_avec_outils';
	$GLOBALS['spip_ecran'] = isset($_COOKIE['spip_ecran']) ? $_COOKIE['spip_ecran'] : "etroit";

	$display_class=array(0=>'icones_img_texte'/*init*/,1=>'icones_texte',2=>'icones_img_texte',3=>'icones_img');
	return $GLOBALS['spip_ecran'] . " $spip_display_navigation $spip_display_outils ".$display_class[$GLOBALS['spip_display']];
}


// http://doc.spip.org/@lien_change_var
function lien_change_var($lien, $set, $couleur, $coords, $titre, $mouseOver="") {
	$lien = parametre_url($lien, $set, $couleur);
	return "\n<area shape='rect' href='$lien' coords='$coords' title=\"$titre\" alt=\"$titre\" $mouseOver />";
}

// http://doc.spip.org/@auteurs_recemment_connectes
function auteurs_recemment_connectes($id_auteur){
	return recuperer_fond('prive/objets/liste/auteurs_enligne');
}

?>
urls.php000066600000017643152525475610006275 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

if (!defined('_ECRIRE_INC_VERSION')) return;
include_spip('base/objets');

/**
 * Decoder une url en utilisant les fonctions inverse
 * gestion des URLs transformee par le htaccess
 * $renommer = 'urls_propres_dist';
 * renvoie array($contexte, $type, $url_redirect, $nfond)
 * $nfond n'est retourne que si l'url est definie apres le ?
 * et risque d'etre effacee par un form en get
 * elle est utilisee par form_hidden exclusivement
 * Compat ascendante si le retour est null en gerant une sauvegarde/restauration
 * des globales modifiees par les anciennes fonctions
 *
 * @param string $url
 *  url a decoder
 * @param string $fond
 *  fond initial par defaut
 * @param array $contexte
 *  contexte initial a prendre en compte
 * @param bool $assembler
 *	true si l'url correspond a l'url principale de la page qu'on est en train d'assembler
 *  dans ce cas la fonction redirigera automatiquement si besoin
 *  et utilisera les eventuelles globales $_SERVER['REDIRECT_url_propre'] et $_ENV['url_propre']
 *  provenant du htaccess
 * @return array
 *  ($fond,$contexte,$url_redirect)
 *  si l'url n'est pas valide, $fond restera a la valeur initiale passee
 *  il suffit d'appeler la fonction sans $fond et de verifier qu'a son retour celui-ci
 *  est non vide pour verifier une url
 *
 */
function urls_decoder_url($url, $fond='', $contexte=array(), $assembler=false){
	static $current_base = null;
	// les anciennes fonctions modifient directement les globales
	// on les sauve avant l'appel, et on les retablit apres !
	$save = array(@$GLOBALS['fond'],@$GLOBALS['contexte'],@$_SERVER['REDIRECT_url_propre'],@$_ENV['url_propre'],$GLOBALS['profondeur_url']);
	if (is_null($current_base)){
		include_spip('inc/filtres_mini');
		// le decodage des urls se fait toujours par rapport au site public
		$current_base = url_absolue(_DIR_RACINE?_DIR_RACINE:'./');
	}
	if (strncmp($url,$current_base,strlen($current_base))==0)
		$url = substr($url,strlen($current_base));

	// si on est pas en train d'assembler la page principale,
	// vider les globales url propres qui ne doivent pas etre utilisees en cas
	// d'inversion url => objet
	if (!$assembler) {
		unset($_SERVER['REDIRECT_url_propre']);
		unset($_ENV['url_propre']);
		include_spip('inc/filtres_mini');
		if (strpos($url,"://")===false){
            $GLOBALS['profondeur_url'] = substr_count(ltrim(resolve_path("/$url"),'/'),'/');
    }
    else {
            $GLOBALS['profondeur_url'] = max(0,substr_count($url,"/")-substr_count($current_base,"/"));
    }
	}

	
	$url_redirect = "";
	$renommer = generer_url_entite('','','','',true);
	if (!$renommer AND !function_exists('recuperer_parametres_url'))
		$renommer = charger_fonction('page','urls'); // fallback pour decoder l'url
	if ($renommer) {
		$a = $renommer($url, $fond, $contexte);
		if (is_array($a)) {
			list($ncontexte, $type, $url_redirect, $nfond) = array_pad($a, 4, null);
			if ($url_redirect == $url)
				$url_redirect = ""; // securite pour eviter une redirection infinie
			if ($assembler AND strlen($url_redirect)) {
				spip_log("Redirige $url vers $url_redirect");
				include_spip('inc/headers');
				redirige_par_entete($url_redirect, '', 301);
			}
			if (isset($nfond))
				$fond = $nfond;
			else if ($fond == ''
			OR $fond == 'type_urls' /* compat avec htaccess 2.0.0 */
			)
				$fond = $type;
			if (isset($ncontexte))
				$contexte = $ncontexte;
			if (defined('_DEFINIR_CONTEXTE_TYPE') AND _DEFINIR_CONTEXTE_TYPE)
				$contexte['type'] = $type;
			if (defined('_DEFINIR_CONTEXTE_TYPE_PAGE') AND _DEFINIR_CONTEXTE_TYPE_PAGE)
				$contexte['type-page'] = $type;
		}
	}
	// compatibilite <= 1.9.2
	elseif (function_exists('recuperer_parametres_url')) {
		$GLOBALS['fond'] = $fond;
		$GLOBALS['contexte'] = $contexte;
		recuperer_parametres_url($fond, nettoyer_uri());
		// fond est en principe modifiee directement
		$contexte = $GLOBALS['contexte'];
	}

	// retablir les globales
	list($GLOBALS['fond'],$GLOBALS['contexte'],$_SERVER['REDIRECT_url_propre'],$_ENV['url_propre'],$GLOBALS['profondeur_url']) = $save;
	
	// vider les globales url propres qui ne doivent plus etre utilisees en cas
	// d'inversion url => objet
	// maintenir pour compat ?
	#if ($assembler) {
	#	unset($_SERVER['REDIRECT_url_propre']);
	#	unset($_ENV['url_propre']);
	#}

	return array($fond,$contexte,$url_redirect);
}


/**
 * Lister les objets pris en compte dans les urls
 * c'est a dire suceptibles d'avoir une url propre
 *
 * @param bool $preg
 *  permet de definir si la fonction retourne une chaine avec | comme separateur
 *  pour utiliser en preg, ou un array()
 * @return string/array
 */
function urls_liste_objets($preg = true){
	static $url_objets = null;
	if (is_null($url_objets)){
		$url_objets = array();
		// recuperer les tables_objets_sql declarees
		$tables_objets = lister_tables_objets_sql();
		foreach($tables_objets as $t=>$infos){
			if ($infos['page']) {
				$url_objets[] = $infos['type'];
				$url_objets = array_merge($url_objets,$infos['type_surnoms']);
			}
		}
		$url_objets = pipeline('declarer_url_objets',$url_objets);
	}
	if (!$preg) return $url_objets;
	return implode('|',array_map('preg_quote',$url_objets));
}

/**
 * Nettoyer une url, en reperant notamment les raccourcis d'entites
 * comme ?article13, ?rubrique21 ...
 * et en les traduisant pour completer le contexte fourni en entree
 *
 * @param string $url
 * @param array $contexte
 * @return array
 */
function nettoyer_url_page($url, $contexte=array())
{
	$url_objets = urls_liste_objets();
	$raccourci_url_page_html = ',^(?:[^?]*/)?('. $url_objets . ')([0-9]+)(?:\.html)?([?&].*)?$,';
	$raccourci_url_page_id = ',^(?:[^?]*/)?('. $url_objets .')\.php3?[?]id_\1=([0-9]+)([?&].*)?$,';
	$raccourci_url_page_spip = ',^(?:[^?]*/)?(?:spip[.]php)?[?]('. $url_objets .')([0-9]+)=?(&.*)?$,';

	if (preg_match($raccourci_url_page_html, $url, $regs)
	OR preg_match($raccourci_url_page_id, $url, $regs)
	OR preg_match($raccourci_url_page_spip, $url, $regs)) {
		$type = objet_type($regs[1]);
		$_id = id_table_objet($type);
		$contexte[$_id] = $regs[2];
		$suite = $regs[3];
		return array($contexte, $type, null, $type, $suite);
	}
	return array();
}

/**
 * Generer l'url d'un objet dans l'espace prive,
 * fonction de son etat publie ou non
 * calcule a partir de la declaration de statut
 *
 * @param int $id
 * @param string $args
 * @param string $ancre
 * @param string $statut
 * @param string $connect
 * @return string
 *
 */
function generer_url_ecrire_objet($objet,$id, $args='', $ancre='', $public=null, $connect=''){
	static $furls = array();
	if (!isset($furls[$objet])){
		if (function_exists($f = 'generer_url_ecrire_' . $objet)
			// ou definie par un plugin
			OR $f = charger_fonction($f,'urls',true))
			$furls[$objet] = $f;
		else
			$furls[$objet] = '';
	}
	if ($furls[$objet])
		return $furls[$objet]($id, $args, $ancre, $public, $connect);
	// si pas de flag public fourni
	// le calculer en fonction de la declaration de statut
	if (is_null($public) AND !$connect)
		$public = objet_test_si_publie($objet, $id, $connect);
	if ($public OR $connect){
		return generer_url_entite_absolue($id, $objet, $args, $ancre, $connect);
	}
	$a = id_table_objet($objet) . "=" . intval($id);
	if (!function_exists('objet_info'))
		include_spip('inc/filtres');
	return generer_url_ecrire(objet_info($objet,'url_voir'), $a . ($args ? "&$args" : '')). ($ancre ? "#$ancre" : '');
}

?>
config.php000066600000035617152525475610006556 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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


/**
 * Appliquer les valeurs par defaut pour les options non initialisees
 * (pour les langues c'est fait)
 *
 * @return null
 */
// http://doc.spip.org/@inc_config_dist
function inc_config_dist() {
	actualise_metas(liste_metas());
}

/**
 * Expliquer une configuration :
 * analyser le cfg pour determiner la table, le casier et le sous-casier eventuel
 *
 * @param string $cfg
 * @return array
 */
function expliquer_config($cfg){
	// par defaut, sur la table des meta
	$table = 'meta';
	$casier = null;
	$sous_casier = array();
	if (strlen($cfg)){
		$cfg = explode('/',$cfg);
		// si le premier argument est vide, c'est une syntaxe /table/ ou un appel vide ''
		if (!reset($cfg) AND count($cfg)>1) {
			array_shift($cfg);
			$table = array_shift($cfg);
			if (!isset($GLOBALS[$table]))
				lire_metas($table);
		}
		// si on a demande #CONFIG{/meta,'',0}
		if (count($cfg))
			$casier = array_shift($cfg);

		if (count($cfg))
			$sous_casier = $cfg;
	}
	return array($table,$casier,$sous_casier);
}

/**
 * Lecture de la configuration
 *
 * lire_config() permet de recuperer une config depuis le php<br>
 * memes arguments que la balise (forcement)<br>
 * $cfg: la config, lire_config('montruc') est un tableau<br>
 * lire_config('/table/champ') lit le valeur de champ dans la table des meta 'spip_table'<br>
 * lire_config('montruc/sub') est l'element "sub" de cette config equivalent a lire_config('/meta/montruc/sub')<br>
 *
 * lire_config('methode::montruc/sub') delegue la lecture a methode_lire_config_dist via un charger_fonction
 * permet de brancher CFG ou autre outil externe qui etend les methodes de stockage de config
 *
 * $unserialize est mis par l'histoire
 *
 * @param  string  $cfg          la config
 * @param  mixed   $def          un défaut optionnel
 * @param  boolean $unserialize  n'affecte que le dépôt 'meta'
 * @return string
 */
function lire_config($cfg='', $def=null, $unserialize=true) {
	// lire le stockage sous la forme /table/valeur
	// ou valeur qui est en fait implicitement /meta/valeur
	// ou casier/valeur qui est en fait implicitement /meta/casier/valeur

	// traiter en priorite le cas simple et frequent
	// de lecture direct $GLOBALS['meta']['truc'], si $cfg ne contient ni / ni :
	if ($cfg AND strpbrk($cfg,'/:')===false){
		$r = isset($GLOBALS['meta'][$cfg])?
		  ((!$unserialize
			// ne pas essayer de deserialiser autre chose qu'une chaine
			OR !is_string($GLOBALS['meta'][$cfg])
			// ne pas essayer de deserialiser si ce n'est visiblement pas une chaine serializee
			OR strpos($GLOBALS['meta'][$cfg],':')===false
			OR ($t=unserialize($GLOBALS['meta'][$cfg]))===false)?$GLOBALS['meta'][$cfg]:$t)
		  :$def;
		return $r;
	}

	// Brancher sur methodes externes si besoin
	if ($cfg AND $p=strpos($cfg,'::')){
		$methode = substr($cfg,0,$p);
		$lire_config = charger_fonction($methode, 'lire_config');
		return $lire_config(substr($cfg,$p+2),$def,$unserialize);
	}

	list($table,$casier,$sous_casier) = expliquer_config($cfg);

	if (!isset($GLOBALS[$table]))
			return $def;

	$r = $GLOBALS[$table];

	// si on a demande #CONFIG{/meta,'',0}
	if (!$casier)
		return $unserialize ? $r : serialize($r);

	// casier principal :
	// le deserializer si demande
	// ou si on a besoin
	// d'un sous casier
	$r = isset($r[$casier])?$r[$casier]:null;
	if (($unserialize OR count($sous_casier)) AND $r AND is_string($r))
		$r = (($t=unserialize($r))===false?$r:$t);

	// aller chercher le sous_casier
	while(!is_null($r) AND $casier = array_shift($sous_casier))
		$r = isset($r[$casier])?$r[$casier]:null;

	if (is_null($r)) return $def;
	return $r;
}

/**
 * metapack est inclue dans lire_config, mais on traite le cas ou il est explicite
 * metapack:: dans le $cfg de lire_config.
 * On renvoie simplement sur lire_config
 * 
 * @param string $cfg
 * @param mixed $def
 * @param bool $unserialize
 * @return mixed
 */
function lire_config_metapack_dist($cfg='', $def=null, $unserialize=true) {
	return lire_config($cfg, $def, $unserialize);
}


/**
 * Ecrire une configuration
 *
 * @param string $cfg
 * @param mixed $store
 * @return bool
 */
function ecrire_config($cfg,$store) {
	// Brancher sur methodes externes si besoin
	if ($cfg AND $p=strpos($cfg,'::')){
		$methode = substr($cfg,0,$p);
		$ecrire_config = charger_fonction($methode, 'ecrire_config');
		return $ecrire_config(substr($cfg,$p+2),$store);
	}
	
	list($table,$casier,$sous_casier) = expliquer_config($cfg);
	// il faut au moins un casier pour ecrire
	if (!$casier) return false;

	// trouvons ou creons le pointeur sur le casier
	$st = isset($GLOBALS[$table][$casier])?$GLOBALS[$table][$casier]:null;
	if (!is_array($st) AND ($sous_casier OR is_array($store))) {
		$st = unserialize($st);
		if ($st===false) {
			// ne rien creer si c'est une demande d'effacement
			if (is_null($store))
				return false;
			$st=array();
		}
	}

	// si on a affaire a un sous caiser
	// il faut ecrire au bon endroit sans perdre les autres sous casier freres
	if ($c = $sous_casier) {
		$sc = &$st;
		$pointeurs = array();
		while (count($c) AND $cc=array_shift($c)) {
			// creer l'entree si elle n'existe pas
			if (!isset($sc[$cc])) {
				// si on essaye d'effacer une config qui n'existe pas
				// ne rien creer mais sortir
				if (is_null($store))
					return false;
				$sc[$cc] = array();
			}
			$pointeurs[$cc] = &$sc;
			$sc = &$sc[$cc];
		}

		// si c'est une demande d'effacement
		if (is_null($store)){
			$c = $sous_casier;
			$sous = array_pop($c);
			// effacer, et remonter pour effacer les parents vides
			do {
				unset($pointeurs[$sous][$sous]);
			} while ($sous = array_pop($c) AND !count($pointeurs[$sous][$sous]));
			
			// si on a vide tous les sous casiers,
			// et que le casier est vide
			// vider aussi la meta
			if (!$sous AND !count($st))
				$st = null;
		}
		// dans tous les autres cas, on ecrase
		else
			$sc = $store;

		// Maintenant que $st est modifiee
		// reprenons la comme valeur a stocker dans le casier principal
		$store = $st;
	}

	if (is_null($store)) {
		if (is_null($st) AND !$sous_casier)
			return false; // la config n'existait deja pas !
		effacer_meta ($casier, $table);
		if (!count($GLOBALS[$table])
			OR count($GLOBALS[$table])==1 AND isset($GLOBALS[$table]['charset'])) {
			effacer_meta('charset', $table); // enlevons cette meta
			supprimer_table_meta($table); // supprimons la table (si elle est bien vide)
		}
	}
	// les meta ne peuvent etre que des chaines : il faut serializer le reste
	else {
		if (!isset($GLOBALS[$table]))
			installer_table_meta($table);
		// si ce n'est pas une chaine
		// il faut serializer
		if (!is_string($store))
			$store=serialize($store);
		ecrire_meta($casier, $store, null, $table);
	}
	// verifier que lire_config($cfg)==$store ?
	return true;
}


/**
 * metapack est inclue dans ecrire_config, mais on traite le cas ou il est explicite
 * metapack:: dans le $cfg de ecrire_config.
 * On renvoie simplement sur ecrire_config
 *
 * @param string $cfg
 * @param mixed $store
 * @return bool
 */
function ecrire_config_metapack_dist($cfg,$store) {
	// cas particulier en metapack::
	// si on ecrit une chaine deja serializee, il faut la reserializer pour la rendre
	// intacte en sortie ...
	if (is_string($store) AND strpos($store,':') AND unserialize($store))
		$store = serialize($store);
	return ecrire_config($cfg, $store);
}

/**
 * Effacer une configuration : revient a ecrire une valeur null
 * @param string $cfg
 * @return bool
 */
function effacer_config($cfg){
	ecrire_config($cfg, null);
	return true;
}


function lister_configurer($exclure = array()){
	return array();
	
	// lister les pages de config deja dans les menus
	$deja = array();
	foreach($exclure as $id=>$b) {
		$url = ($b['url'] ? $b['url'] : $id);
		if (!$b['url'] or !isset($exclure[$url])) {
			if (strncmp($url,'configurer_',11)==0) {
				$deja[$url] = $b;
			} elseif($b['url']=='configurer' AND preg_match(',cfg=([a-z0-9_]+),i',$b['args'],$match)) {
				$deja["configurer_".$match[1]] = $b;
			}
		}
		
	}
	$exclure = $exclure + $deja;

	$icone_defaut = "images/configuration-16.png";
	$liste = array();
	$skels = array();
	$forms = array();

	// trouver toutes les pages configurer_xxx de l'espace prive
	// et construire un tableau des entrees qui ne sont pas dans $deja
	$pages = find_all_in_path("prive/squelettes/contenu/", "configurer_.*[.]"._EXTENSION_SQUELETTES.'$');

	foreach($pages as $page) {
		$configurer = basename($page,"."._EXTENSION_SQUELETTES);
		if (!isset($exclure[$configurer])) {
			$liste[$configurer] = array(
					'parent' => 'bando_configuration',
					'url' => $configurer,
					'titre' => _T("configurer:{$configurer}_titre"),
					'icone' => find_in_theme($i="images/{$configurer}-16.png")?$i:$icone_defaut,
			);
		}
		$skels[$configurer] = $page;
	}

	// analyser la liste des $skels pour voir les #FORMULAIRE_CONFIGURER_ inclus
	foreach($skels as $file) {
		$forms = array_merge($forms, lister_formulaires_configurer($file));
	}
	$forms = array_flip($forms);

	// trouver tous les formulaires/configurer_
	// et construire un tableau des entrees
	$pages = find_all_in_path("formulaires/", "configurer_.*[.]"._EXTENSION_SQUELETTES.'$');
	foreach($pages as $page) {
		$configurer = basename($page,"."._EXTENSION_SQUELETTES);
		if (!isset($forms[$configurer])
		  AND !isset($liste[$configurer])
			AND !isset($exclure[$configurer]))
			$liste[$configurer] = array(
					'parent' => 'bando_configuration',
					'url' => 'configurer',
					'args' => 'cfg='.substr($configurer,11),
					'titre' => _T("configurer:{$configurer}_titre"),
					'icone' => find_in_theme($i="images/{$configurer}-16.png")?$i:$icone_defaut,
			);
	}

	return $liste;
}


/**
 * Retourne la liste des formulaires de configuration
 * presents dans le fichier dont l'adresse est donnee 
 *
 * @param string $file adresse du fichier
 * @return array liste des formulaires trouves
**/
function lister_formulaires_configurer($file) {
	$forms = array();
	
	lire_fichier($file, $skel);
	if (preg_match_all(",#FORMULAIRE_(CONFIGURER_[A-Z0-9_]*),", $skel, $matches,PREG_SET_ORDER)) {
		$matches = array_map('end',$matches);
		$matches = array_map('strtolower',$matches);
		$forms = array_merge($forms,$matches);
	}

	// evaluer le fond en lui passant un exec coherent pour que les pipelines le reconnaissent
	// et reperer les formulaires CVT configurer_xx insereres par les plugins via pipeline
	$config = basename(substr($file,0,-strlen("."._EXTENSION_SQUELETTES)));
	spip_log('Calcul de '."prive/squelettes/contenu/$config");
	$fond = recuperer_fond("prive/squelettes/contenu/$config", array("exec" => $config));
	
	// passer dans le pipeline affiche_milieu pour que les plugins puissent ajouter leur formulaires...
	// et donc que l'on puisse les referencer aussi !
	$fond = pipeline('affiche_milieu', array('args'=>array("exec" => $config),'data'=>$fond));

	// recuperer les noms des formulaires presents.
	if (is_array($inputs = extraire_balises($fond,"input"))) {
		foreach($inputs as $i) {
			if (extraire_attribut($i,'name')=='formulaire_action') {
				$forms[] = ($c=extraire_attribut($i,'value'));
			}
		}
	}
	return $forms;
}


// http://doc.spip.org/@liste_metas
function liste_metas()
{
	return pipeline('configurer_liste_metas', array(
		'nom_site' => _T('info_mon_site_spip'),
		'slogan_site' => '',
		'adresse_site' => preg_replace(",/$,", "", url_de_base()),
		'descriptif_site' => '',
		'activer_logos' => 'oui',
		'activer_logos_survol' => 'non',
		'articles_surtitre' => 'non',
		'articles_soustitre' => 'non',
		'articles_descriptif' => 'non',
		'articles_chapeau' => 'non',
		'articles_texte' => 'oui',
		'articles_ps' => 'non',
		'articles_redac' => 'non',
		'post_dates' => 'non',
		'articles_urlref' => 'non',
		'articles_redirection' => 'non',
		'creer_preview' => 'non',
		'taille_preview' => 150,
		'articles_modif' => 'non',

		'rubriques_descriptif' => 'non',
		'rubriques_texte' => 'oui',

		'accepter_inscriptions' => 'non',
		'accepter_visiteurs' => 'non',
		'prevenir_auteurs' => 'non',
		'suivi_edito' => 'non',
		'adresse_suivi' =>'',
		'adresse_suivi_inscription' =>'',
		'adresse_neuf' => '',
		'jours_neuf' => '',
		'quoi_de_neuf' => 'non',
		'preview' => ',0minirezo,1comite,',

		'syndication_integrale' => 'oui',
		'charset' => _DEFAULT_CHARSET,
		'dir_img' => substr(_DIR_IMG,strlen(_DIR_RACINE)),

		'multi_rubriques' => 'non',
		'multi_secteurs' => 'non',
		'gerer_trad' => 'non',
		'langues_multilingue' => '',

		'version_html_max' => 'html4',

		'type_urls' => 'page',

		'email_envoi' => '',
		'email_webmaster' => '',
		'auto_compress_http'=>'non',
	));
}

// mets les meta a des valeurs conventionnelles quand elles sont vides
// et recalcule les langues

// http://doc.spip.org/@actualise_metas
function actualise_metas($liste_meta)
{
	$meta_serveur =
		array('version_installee','adresse_site','alea_ephemere_ancien','alea_ephemere','alea_ephemere_date','langue_site','langues_proposees','date_calcul_rubriques','derniere_modif','optimiser_table','drapeau_edition','creer_preview','taille_preview','creer_htpasswd','creer_htaccess','gd_formats_read','gd_formats',
	'netpbm_formats','formats_graphiques','image_process','plugin_header','plugin');
	// verifier le impt=non
	sql_updateq('spip_meta',array('impt'=>'non'),sql_in('nom',$meta_serveur));

	while (list($nom, $valeur) = each($liste_meta)) {
		if (!$GLOBALS['meta'][$nom]) {
			ecrire_meta($nom, $valeur);
		}
	}

	include_spip('inc/rubriques');
	$langues = calculer_langues_utilisees();
	ecrire_meta('langues_utilisees', $langues);
}



//
// Gestion des modifs
//

// http://doc.spip.org/@appliquer_modifs_config
function appliquer_modifs_config($purger_skel=false) {

	foreach(liste_metas() as $i => $v) {
		if (($x =_request($i))!==NULL)
			ecrire_meta($i, $x);
		elseif  (!isset($GLOBALS['meta'][$i]))
			ecrire_meta($i, $v);
	}

	if ($purger_skel) {
		include_spip('inc/invalideur');
		purger_repertoire(_DIR_SKELS);
	}
}

/**
 * Mettre a jour l'adresse du site a partir d'une valeur saisie
 * (ou auto detection si vide)
 * 
 * @param  $adresse_site
 * @return void
 */
function appliquer_adresse_site($adresse_site){
	if ($adresse_site!==NULL){
		if (!strlen($adresse_site)) {$GLOBALS['profondeur_url']=_DIR_RESTREINT?0:1;$adresse_site = url_de_base();}
		$adresse_site = preg_replace(",/?\s*$,", "", $adresse_site);

		if (!preg_match(",^[\w]+://,Uims",$adresse_site))
			$adresse_site = "http://$adresse_site";

		ecrire_meta('adresse_site',$adresse_site);
	}
	return $adresse_site;
}

?>
index.php000066600000000002152525475610006374 0ustar00X
headers.php000066600000014267152525475610006722 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/


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

// envoyer le navigateur sur une nouvelle adresse
// en evitant les attaques par la redirection (souvent indique par 1 $_GET)

// http://doc.spip.org/@redirige_par_entete
function redirige_par_entete($url, $equiv='', $status = 302) {
	if (!in_array($status,array(301,302)))
		$status = 302;

	$url = trim(strtr($url, "\n\r", "  "));
	# en theorie on devrait faire ca tout le temps, mais quand la chaine
	# commence par ? c'est imperatif, sinon l'url finale n'est pas la bonne
	if ($url[0]=='?')
		$url = url_de_base().$url;
	if ($url[0]=='#')
		$url = self('&').$url;
	# si profondeur non nulle et url relative, il faut la passer en absolue
	if ($GLOBALS['profondeur_url']>(_DIR_RESTREINT?1:2)
		AND !preg_match(",^(\w+:)?//,",$url)){
		include_spip("inc/filtres_mini");
		$url = url_absolue($url);
	}

	if ($x = _request('transformer_xml'))
		$url = parametre_url($url, 'transformer_xml', $x, '&');

	if (defined('_AJAX') AND _AJAX)
		$url = parametre_url($url, 'var_ajax_redir', 1, '&');
		
	// ne pas laisser passer n'importe quoi dans l'url
	$url = str_replace(array('<','"'),array('&lt;','&quot;'),$url);
	// interdire les url inline avec des pseudo-protocoles :
	if (
		(preg_match(",data:,i",$url) AND preg_match("/base64\s*,/i",$url))
		OR preg_match(",(javascript|mailto):,i",$url)
		)
		$url ="./";

	// Il n'y a que sous Apache que setcookie puis redirection fonctionne
  include_spip('inc/cookie');
	if ((!$equiv AND !spip_cookie_envoye()) OR ((strncmp("Apache", $_SERVER['SERVER_SOFTWARE'],6)==0) OR defined('_SERVER_APACHE'))) {
		@header("Location: " . $url);
		$equiv="";
	} else {
		@header("Refresh: 0; url=" . $url);
		$equiv = "<meta http-equiv='Refresh' content='0; url=$url'>";
	}
	include_spip('inc/lang');
	if ($status!=302)
		http_status($status);
	echo '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">',"\n",
	  html_lang_attributes(),'
<head>',
	  $equiv,'
<title>HTTP '.$status.'</title>
</head>
<body>
<h1>HTTP '.$status.'</h1>
<a href="',
	  quote_amp($url),
	  '">',
	  _T('navigateur_pas_redirige'),
	  '</a></body></html>';

	spip_log("redirige $status: $url");

	exit;
}

// http://doc.spip.org/@redirige_formulaire
function redirige_formulaire($url, $equiv = '', $format='message') {
	if (!_AJAX
	AND !headers_sent()
	AND !_request('var_ajax')) {
		redirige_par_entete(str_replace('&amp;','&',$url), $equiv);
	}
	// si c'est une ancre, fixer simplement le window.location.hash
	elseif($format=='ajaxform' AND preg_match(',^#[0-9a-z\-_]+$,i',$url)) {
		return array(
		// on renvoie un lien masque qui sera traite par ajaxCallback.js
		"<a href='$url' name='ajax_ancre' style='display:none;'>anchor</a>",
		// et rien dans le message ok
		'');
	}
	else {
		// ne pas laisser passer n'importe quoi dans l'url
		$url = str_replace(array('<','"'),array('&lt;','&quot;'),$url);

		$url = strtr($url, "\n\r", "  ");
		# en theorie on devrait faire ca tout le temps, mais quand la chaine
		# commence par ? c'est imperatif, sinon l'url finale n'est pas la bonne
		if ($url[0]=='?')
			$url = url_de_base().$url;
		$url = str_replace('&amp;','&',$url);
		spip_log("redirige formulaire ajax: $url");
		include_spip('inc/filtres');
		if ($format=='ajaxform')
			return array(
			// on renvoie un lien masque qui sera traite par ajaxCallback.js
			'<a href="'.quote_amp($url).'" name="ajax_redirect"  style="display:none;">'._T('navigateur_pas_redirige').'</a>',
			// et un message au cas ou
			'<br /><a href="'.quote_amp($url).'">'._T('navigateur_pas_redirige').'</a>'
			);
		else // format message texte, tout en js inline
			return
		// ie poste les formulaires dans une iframe, il faut donc rediriger son parent
		"<script type='text/javascript'>if (parent.window){parent.window.document.location.replace(\"$url\");} else {document.location.replace(\"$url\");}</script>"
		. http_img_pack('searching.gif','')
		. '<br />'
		. '<a href="'.quote_amp($url).'">'._T('navigateur_pas_redirige').'</a>';
	}
}

// http://doc.spip.org/@redirige_url_ecrire
function redirige_url_ecrire($script='', $args='', $equiv='') {
	return redirige_par_entete(generer_url_ecrire($script, $args, true), $equiv);
}

// http://doc.spip.org/@http_status
function http_status($status) {
	global $REDIRECT_STATUS, $flag_sapi_name;
	static $status_string = array(
		200 => '200 OK',
		204 => '204 No Content',
		301 => '301 Moved Permanently',
		302 => '302 Found',
		304 => '304 Not Modified',
		401 => '401 Unauthorized',
		403 => '403 Forbidden',
		404 => '404 Not Found',
		503 => '503 Service Unavailable'
	);

	if ($REDIRECT_STATUS && $REDIRECT_STATUS == $status) return;

	$php_cgi = ($flag_sapi_name AND preg_match(",cgi,i", @php_sapi_name()));
	if ($php_cgi)
		header("Status: ".$status_string[$status]);
	else
		header("HTTP/1.0 ".$status_string[$status]);
}

// Retourne ce qui va bien pour que le navigateur ne mette pas la page en cache
// http://doc.spip.org/@http_no_cache
function http_no_cache() {
	if (headers_sent())
		{ spip_log("http_no_cache arrive trop tard"); return;}
	$charset = empty($GLOBALS['meta']['charset']) ? 'utf-8' : $GLOBALS['meta']['charset'];

	// selon http://developer.apple.com/internet/safari/faq.html#anchor5
	// il faudrait aussi pour Safari
	// header("Cache-Control: post-check=0, pre-check=0", false)
	// mais ca ne respecte pas
	// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9

	header("Content-Type: text/html; charset=$charset");
	header("Expires: 0");
	header("Last-Modified: " .gmdate("D, d M Y H:i:s"). " GMT");
	header("Cache-Control: no-cache, must-revalidate");
	header("Pragma: no-cache");
}

?>
texte_mini.php000066600000035533152525475610007453 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

if (!defined('_ECRIRE_INC_VERSION')) return;
include_spip('inc/filtres');
include_spip('inc/lang');

// On initialise la puce pour eviter find_in_path() a chaque rencontre de \n-
// Mais attention elle depend de la direction et de X_fonctions.php, ainsi que
// de l'espace choisi (public/prive)
// http://doc.spip.org/@definir_puce
function definir_puce() {

	// Attention au sens, qui n'est pas defini de la meme facon dans
	// l'espace prive (spip_lang est la langue de l'interface, lang_dir
	// celle du texte) et public (spip_lang est la langue du texte)
	$dir = _DIR_RESTREINT ? lang_dir() : lang_dir($GLOBALS['spip_lang']);

	$p = 'puce' . (test_espace_prive() ? '_prive' : '');
	if ($dir == 'rtl') $p .= '_rtl';

	if (!isset($GLOBALS[$p])) {
		$img = find_in_path($p.'.gif');
		list(,,,$size) = @getimagesize($img);
		$GLOBALS[$p] = '<img src="'.$img.'" '.$size.' class="puce" alt="-" />';
	}
	return $GLOBALS[$p];
}


// XHTML - Preserver les balises-bloc : on liste ici tous les elements
// dont on souhaite qu'ils provoquent un saut de paragraphe

if (!defined('_BALISES_BLOCS')) define('_BALISES_BLOCS',
	'p|div|pre|ul|ol|li|blockquote|h[1-6r]|'
	.'t(able|[rdh]|head|body|foot|extarea)|'
	.'form|object|center|marquee|address|'
	.'applet|iframe|'
	.'d[ltd]|script|noscript|map|button|fieldset|style');

if (!defined('_BALISES_BLOCS_REGEXP'))
	define('_BALISES_BLOCS_REGEXP',',</?('._BALISES_BLOCS.')[>[:space:]],iS');

//
// Echapper les elements perilleux en les passant en base64
//

// Creer un bloc base64 correspondant a $rempl ; au besoin en marquant
// une $source differente ; le script detecte automagiquement si ce qu'on
// echappe est un div ou un span
// http://doc.spip.org/@code_echappement
function code_echappement($rempl, $source='', $no_transform=false, $mode=NULL) {
	if (!strlen($rempl)) return '';

	// Tester si on echappe en span ou en div
	if (is_null($mode) OR !in_array($mode,array('div','span')))
		$mode = preg_match(',</?('._BALISES_BLOCS.')[>[:space:]],iS', $rempl) ? 'div' : 'span';

	// Decouper en morceaux, base64 a des probleme selon la taille de la pile
	$taille = 30000;
	$return = "";
	for($i = 0; $i < strlen($rempl); $i += $taille) {
		// Convertir en base64 et cacher dans un attribut
		// utiliser les " pour eviter le re-encodage de ' et &#8217
		$base64 = base64_encode(substr($rempl, $i, $taille));
		$return .= "<$mode class=\"base64$source\" title=\"$base64\"></$mode>";
	}

	return $return;

}


// Echapper les <html>...</ html>
// http://doc.spip.org/@traiter_echap_html_dist
function traiter_echap_html_dist($regs) {
	return $regs[3];
}

// Echapper les <code>...</ code>
// http://doc.spip.org/@traiter_echap_code_dist
function traiter_echap_code_dist($regs) {
	list(,,$att,$corps) = $regs;
	$echap = spip_htmlspecialchars($corps); // il ne faut pas passer dans entites_html, ne pas transformer les &#xxx; du code !

	// ne pas mettre le <div...> s'il n'y a qu'une ligne
	if (is_int(strpos($echap,"\n"))) {
		// supprimer les sauts de ligne debut/fin
		// (mais pas les espaces => ascii art).
		$echap = preg_replace("/^[\n\r]+|[\n\r]+$/s", "", $echap);
		$echap = nl2br($echap);
		$echap = "<div style='text-align: left;' "
		. "class='spip_code' dir='ltr'><code$att>"
		.$echap."</code></div>";
	} else {
		$echap = "<code$att class='spip_code' dir='ltr'>".$echap."</code>";
	}

	$echap = str_replace("\t", "&nbsp; &nbsp; &nbsp; &nbsp; ", $echap);
	$echap = str_replace("  ", " &nbsp;", $echap);
	return $echap;
}

// Echapper les <cadre>...</ cadre> aka <frame>...</ frame>
// http://doc.spip.org/@traiter_echap_cadre_dist
function traiter_echap_cadre_dist($regs) {
	$echap = trim(entites_html($regs[3]));
	// compter les lignes un peu plus finement qu'avec les \n
	$lignes = explode("\n",trim($echap));
	$n = 0;
	foreach($lignes as $l)
		$n+=floor(strlen($l)/60)+1;
	$n = max($n,2);
	$echap = "\n<textarea readonly='readonly' cols='40' rows='$n' class='spip_cadre' dir='ltr'>$echap</textarea>";
	return $echap;
}
// http://doc.spip.org/@traiter_echap_frame_dist
function traiter_echap_frame_dist($regs) {
	return traiter_echap_cadre_dist($regs);
}

// http://doc.spip.org/@traiter_echap_script_dist
function traiter_echap_script_dist($regs) {
	// rendre joli (et inactif) si c'est un script language=php
	if (preg_match(',<script\b[^>]+php,ims', $regs[0]))
		return highlight_string($regs[0],true);

	// Cas normal : le script passe tel quel
	return $regs[0];
}

/**
 * Traiter les blocs <math></math> echappes par echappe_html
 * @param $regs
 * @return string
 */
function traiter_echap_math_dist($regs) {
	// Gestion du TeX
	if (!function_exists('traiter_math'))
		include_spip('inc/math');

	$t = traiter_math($regs[0], '');
	return $t;
}

define('_PROTEGE_BLOCS', ',<(html|code|cadre|frame|script)(\s[^>]*)?>(.*)</\1>,UimsS');

// - pour $source voir commentaire infra (echappe_retour)
// - pour $no_transform voir le filtre post_autobr dans inc/filtres
// http://doc.spip.org/@echappe_html
function echappe_html($letexte, $source='', $no_transform=false,
$preg='') {
	if (!is_string($letexte) or !strlen($letexte))
		return $letexte;

	// si le texte recu est long PCRE risque d'exploser, on
	// fait donc un mic-mac pour augmenter pcre.backtrack_limit
	if (($len = strlen($letexte)) > 100000) {
		if (!$old = @ini_get('pcre.backtrack_limit')) $old = 100000;
		if ($len > $old) {
			$a = @ini_set('pcre.backtrack_limit', $len);
			spip_log("ini_set pcre.backtrack_limit=$len ($old)");
		}
	}

	if (($preg OR strpos($letexte,"<")!==false)
	  AND preg_match_all($preg ? $preg : _PROTEGE_BLOCS, $letexte, $matches, PREG_SET_ORDER)) {
		foreach ($matches as $regs) {
			// echappements tels quels ?
			if ($no_transform) {
				$echap = $regs[0];
			}

			// sinon les traiter selon le cas
			else if (function_exists($f = 'traiter_echap_'.strtolower($regs[1])))
				$echap = $f($regs);
			else if (function_exists($f = $f.'_dist'))
				$echap = $f($regs);

			$p = strpos($letexte,$regs[0]);
			$letexte = substr_replace($letexte,code_echappement($echap, $source, $no_transform),$p,strlen($regs[0]));
		}
	}

	if ($no_transform)
		return $letexte;

	// Gestion du TeX
	// code mort sauf si on a personalise _PROTEGE_BLOCS sans y mettre <math>
	// eviter la rupture de compat en branche 3.0
	// a supprimer en branche 3.1
	if (strpos($preg ? $preg : _PROTEGE_BLOCS,'code')!==false){
		if (strpos($letexte, "<math>") !== false) {
			include_spip('inc/math');
			$letexte = traiter_math($letexte, $source);
		}
	}

	// Echapper le php pour faire joli (ici, c'est pas pour la securite)
	// seulement si on a echappe les <script>
	// (derogatoire car on ne peut pas faire passer < ? ... ? >
	// dans une callback autonommee
	if (strpos($preg ? $preg : _PROTEGE_BLOCS,'script')!==false){
		if (strpos($letexte,"<"."?")!==false AND preg_match_all(',<[?].*($|[?]>),UisS',
		$letexte, $matches, PREG_SET_ORDER))
		foreach ($matches as $regs) {
			$letexte = str_replace($regs[0],
				code_echappement(highlight_string($regs[0],true), $source),
				$letexte);
		}
	}

	return $letexte;
}

//
// Traitement final des echappements
// Rq: $source sert a faire des echappements "a soi" qui ne sont pas nettoyes
// par propre() : exemple dans multi et dans typo()
// http://doc.spip.org/@echappe_retour
function echappe_retour($letexte, $source='', $filtre = "") {
	if (strpos($letexte,"base64$source")) {
		# spip_log(spip_htmlspecialchars($letexte));  ## pour les curieux
		$max_prof = 5;
		while (strpos($letexte,"<")!==false
			AND
		  preg_match_all(',<(span|div)\sclass=[\'"]base64'.$source.'[\'"]\s(.*)>\s*</\1>,UmsS',
		$letexte, $regs, PREG_SET_ORDER)
		  AND $max_prof--) {
			foreach ($regs as $reg) {
				$rempl = base64_decode(extraire_attribut($reg[0], 'title'));
				// recherche d'attributs supplementaires
				$at = array();
				foreach(array('lang', 'dir') as $attr) {
					if ($a = extraire_attribut($reg[0], $attr))
						$at[$attr] = $a;
				}
				if ($at) {
					$rempl = '<'.$reg[1].'>'.$rempl.'</'.$reg[1].'>';
					foreach($at as $attr => $a)
						$rempl = inserer_attribut($rempl, $attr, $a);
				}
				if ($filtre) $rempl = $filtre($rempl);
				$letexte = str_replace($reg[0], $rempl, $letexte);
			}
		}
	}
	return $letexte;
}

// Reinserer le javascript de confiance (venant des modeles)

// http://doc.spip.org/@echappe_retour_modeles
function echappe_retour_modeles($letexte, $interdire_scripts=false)
{
	$letexte = echappe_retour($letexte);

	// Dans les appels directs hors squelette, securiser aussi ici
	if ($interdire_scripts)
		$letexte = interdire_scripts($letexte);

	return trim($letexte);
}


// http://doc.spip.org/@couper
function couper($texte, $taille=50, $suite = '&nbsp;(...)') {
	if (!($length=strlen($texte)) OR $taille <= 0) return '';
	$offset = 400 + 2*$taille;
	while ($offset<$length
		AND strlen(preg_replace(",<[^>]+>,Uims","",substr($texte,0,$offset)))<$taille)
		$offset = 2*$offset;
	if (	$offset<$length
			&& ($p_tag_ouvrant = strpos($texte,'<',$offset))!==NULL){
		$p_tag_fermant = strpos($texte,'>',$offset);
		if ($p_tag_fermant && ($p_tag_fermant<$p_tag_ouvrant))
			$offset = $p_tag_fermant+1; // prolonger la coupe jusqu'au tag fermant suivant eventuel
	}
	$texte = substr($texte, 0, $offset); /* eviter de travailler sur 10ko pour extraire 150 caracteres */

	// on utilise les \r pour passer entre les gouttes
	$texte = str_replace("\r\n", "\n", $texte);
	$texte = str_replace("\r", "\n", $texte);

	// sauts de ligne et paragraphes
	$texte = preg_replace("/\n\n+/", "\r", $texte);
	$texte = preg_replace("/<(p|br)( [^>]*)?".">/", "\r", $texte);

	// supprimer les traits, lignes etc
	$texte = preg_replace("/(^|\r|\n)(-[-#\*]*|_ )/", "\r", $texte);

	// supprimer les tags
	$texte = supprimer_tags($texte);
	$texte = trim(str_replace("\n"," ", $texte));
	$texte .= "\n";	// marquer la fin

	// travailler en accents charset
	$texte = unicode2charset(html2unicode($texte, /* secure */ true));
	if (!function_exists('nettoyer_raccourcis_typo'))
		include_spip('inc/lien');
	$texte = nettoyer_raccourcis_typo($texte);

	// corriger la longueur de coupe
	// en fonction de la presence de caracteres utf
	if ($GLOBALS['meta']['charset']=='utf-8'){
		$long = charset2unicode($texte);
		$long = spip_substr($long, 0, max($taille,1));
		$nbcharutf = preg_match_all('/(&#[0-9]{3,5};)/S', $long, $matches);
		$taille += $nbcharutf;
	}


	// couper au mot precedent
	$long = spip_substr($texte, 0, max($taille-4,1));
	$u = $GLOBALS['meta']['pcre_u'];
	$court = preg_replace("/([^\s][\s]+)[^\s]*\n?$/".$u, "\\1", $long);
	$points = $suite;

	// trop court ? ne pas faire de (...)
	if (spip_strlen($court) < max(0.75 * $taille,2)) {
		$points = '';
		$long = spip_substr($texte, 0, $taille);
		$texte = preg_replace("/([^\s][\s]+)[^\s]*\n?$/".$u, "\\1", $long);
		// encore trop court ? couper au caractere
		if (spip_strlen($texte) < 0.75 * $taille)
			$texte = $long;
	} else
		$texte = $court;

	if (strpos($texte, "\n"))	// la fin est encore la : c'est qu'on n'a pas de texte de suite
		$points = '';

	// remettre les paragraphes
	$texte = preg_replace("/\r+/", "\n\n", $texte);

	// supprimer l'eventuelle entite finale mal coupee
	$texte = preg_replace('/&#?[a-z0-9]*$/S', '', $texte);

	return quote_amp(trim($texte)).$points;
}


// http://doc.spip.org/@protege_js_modeles
function protege_js_modeles($t) {
	if (isset($GLOBALS['visiteur_session'])){
		if (preg_match_all(',<script.*?($|</script.),isS', $t, $r, PREG_SET_ORDER)){
			if (!defined('_PROTEGE_JS_MODELES')){
				include_spip('inc/acces');
				define('_PROTEGE_JS_MODELES',creer_uniqid());
			}
			foreach ($r as $regs)
				$t = str_replace($regs[0],code_echappement($regs[0],'javascript'._PROTEGE_JS_MODELES),$t);
		}
		if (preg_match_all(',<\?php.*?($|\?'.'>),isS', $t, $r, PREG_SET_ORDER)){
			if (!defined('_PROTEGE_PHP_MODELES')){
				include_spip('inc/acces');
				define('_PROTEGE_PHP_MODELES',creer_uniqid());
			}
			foreach ($r as $regs)
				$t = str_replace($regs[0],code_echappement($regs[0],'php'._PROTEGE_PHP_MODELES),$t);
		}
	}
	return $t;
}


function echapper_faux_tags($letexte){
	if (strpos($letexte,'<')===false)
		return $letexte;
  $textMatches = preg_split (',(</?[a-z!][^<>]*>),', $letexte, null, PREG_SPLIT_DELIM_CAPTURE);

  $letexte = "";
  while (count($textMatches)) {
  	// un texte a echapper
  	$letexte .= str_replace("<",'&lt;',array_shift($textMatches));
  	// un tag html qui a servit a faite le split
 		$letexte .= array_shift($textMatches);
  }
  return $letexte;
}

/**
 * Si le html contenu dans un texte ne passe pas sans transformation a travers safehtml
 * on l'echappe
 * si safehtml ne renvoie pas la meme chose on echappe les < en &lt; pour montrer le contenu brut
 *
 * @param string $texte
 * @return string
 */
function echapper_html_suspect($texte){
	if (strpos($texte,'<')===false OR strpos($texte,'=')===false)
		return $texte;

	// on teste sur strlen car safehtml supprime le contenu dangereux
	// mais il peut aussi changer des ' en " sur les attributs html,
	// donc un test d'egalite est trop strict
	if (strlen(safehtml($texte))!==strlen($texte)){
		$texte = str_replace("<","&lt;",$texte);
	}

	return $texte;
}


/**
 * Sécurise un texte HTML 
 *
 * Échappe le code PHP et JS.
 * Applique en plus safehtml si un plugin le définit dans inc/safehtml.php
 *
 * Permet de protéger les textes issus d'une origine douteuse (forums, syndications...)
 *
 * @filtre
 * @link http://www.spip.net/4310
 * 
 * @param string $t
 *      Texte à sécuriser
 * @return string
 *      Texte sécurisé
**/
function safehtml($t) {
	static $safehtml;

	if (!$t OR !is_string($t))
		return $t;
	# attention safehtml nettoie deux ou trois caracteres de plus. A voir
	if (strpos($t,'<')===false)
		return str_replace("\x00", '', $t);

	$t = interdire_scripts($t); // jolifier le php
	$t = echappe_js($t);

	if (!isset($safehtml))
		$safehtml = charger_fonction('safehtml', 'inc', true);
	if ($safehtml)
		$t = $safehtml($t);

	return interdire_scripts($t); // interdire le php (2 precautions)
}


// fonction en cas de texte extrait d'un serveur distant:
// on ne sait pas (encore) rapatrier les documents joints
// Sert aussi a nettoyer un texte qu'on veut mettre dans un <a> etc.
// TODO: gerer les modeles ?
// http://doc.spip.org/@supprime_img
function supprime_img($letexte, $message=NULL) {
	if ($message===NULL) $message = '(' . _T('img_indisponible') . ')';
	return preg_replace(',<(img|doc|emb)([0-9]+)(\|([^>]*))?'.'\s*/?'.'>,i',
		$message, $letexte);
}
pipelines.php000066600000016151152525475610007271 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

/**
 * Fonctions déclarées dans des pipelines (espace public)
 *
 * @package SPIP\Pipelines
**/
if (!defined('_ECRIRE_INC_VERSION')) return;
if (test_espace_prive())
	include_spip('inc/pipelines_ecrire');



/**
 * Inserer jQuery et ses plugins
 *
 * La fonction ajoute les balises scripts dans le texte qui appelent
 * les scripts jQuery ainsi que certains de ses plugins. La liste
 * des js chargée peut être complété par le pipeline 'jquery_plugins'
 *
 * Cette fonction est appelée par le pipeline insert_head
 * 
 * @internal
 *     Ne pas vérifier ici qu'on ne doublonne pas #INSERT_HEAD
 *     car cela empêche un double appel (multi calcul en cache cool,
 *     ou erreur de l'espace privé)
 *
 * @see f_jQuery_prive()
 * @link http://doc.spip.org/@f_jQuery
 * 
 * @param string $texte    Contenu qui sera inséré dans le head HTML
 * @return string          Contenu qui sera inséré dans le head HTML
**/
function f_jQuery ($texte) {
	$x = '';
	$jquery_plugins = pipeline('jquery_plugins',
		array(
		'javascript/jquery.js',
		'javascript/jquery.form.js',
		'javascript/jquery.autosave.js',
		'javascript/jquery.placeholder-label.js',
		'javascript/ajaxCallback.js',
		'javascript/jquery.cookie.js'
		));
	foreach (array_unique($jquery_plugins) as $script)
		if ($script = find_in_path($script))
			$x .= "\n<script src=\"$script\" type=\"text/javascript\"></script>\n";

	$texte = $x.$texte;
	return $texte;
}


/**
 * Traiter var_recherche ou le referrer pour surligner les mots
 *
 * Surligne les mots de la recherche (si var_recherche est présent)
 * ou des réferers (si la constante _SURLIGNE_RECHERCHE_REFERERS est
 * définie à true) dans un texte HTML
 *
 * Cette fonction est appelée par le pipeline affichage_final
 * 
 * @param string $texte   Contenu de la page envoyée au navigateur
 * @return string         Contenu de la page envoyée au navigateur
**/
function f_surligne ($texte) {
	if (!$GLOBALS['html']) return $texte;
	$rech = _request('var_recherche');
	if (!$rech
	  AND (!defined('_SURLIGNE_RECHERCHE_REFERERS')
	       OR !_SURLIGNE_RECHERCHE_REFERERS
	       OR !isset($_SERVER['HTTP_REFERER'])))
		return $texte;
	include_spip('inc/surligne');
	if (isset($_SERVER['HTTP_REFERER'])) {
		$_SERVER['HTTP_REFERER'] = preg_replace(',[^\w\,/#&;-]+,', ' ', $_SERVER['HTTP_REFERER']);
	}
	if ($rech){
		$rech = preg_replace(',[^\w\,/#&;-]+,', ' ', $rech);
	}

	return surligner_mots($texte, $rech);
}

/**
 * Indente un code HTML
 *
 * Indente et valide un code HTML si la globale 'xhtml' est
 * définie à true.
 *
 * Cette fonction est appelée par le pipeline affichage_final
 * 
 * @param string $texte   Contenu de la page envoyée au navigateur
 * @return string         Contenu de la page envoyée au navigateur
 **/
function f_tidy ($texte) {
	/**
	 * Indentation à faire ?
	 *
	 * - true : actif.
	 * - false par défaut.
	 */
	global $xhtml;

	if ($xhtml # tidy demande
	AND $GLOBALS['html'] # verifie que la page avait l'entete text/html
	AND strlen($texte)
	AND !headers_sent()) {
		# Compatibilite ascendante
		if (!is_string($xhtml)) $xhtml ='tidy';

		if (!$f = charger_fonction($xhtml, 'inc', true)) {
			spip_log("tidy absent, l'indenteur SPIP le remplace");
			$f = charger_fonction('sax', 'xml');
		}
		return $f($texte);
	}

	return $texte;
}


/**
 * Offre #INSERT_HEAD sur tous les squelettes (bourrin)
 *
 * À activer dans mes_options via :
 * $GLOBALS['spip_pipeline']['affichage_final'] .= '|f_insert_head';
 *
 * Ajoute le contenu du pipeline insert head dans la page HTML
 * si cela n'a pas été fait.
 *
 * @param string $texte   Contenu de la page envoyée au navigateur
 * @return string         Contenu de la page envoyée au navigateur
**/
function f_insert_head($texte) {
	if (!$GLOBALS['html']) return $texte;
	include_spip('public/admin'); // pour strripos

	($pos = stripos($texte, '</head>'))
	    || ($pos = stripos($texte, '<body>'))
	    || ($pos = 0);

	if (false === strpos(substr($texte, 0,$pos), '<!-- insert_head -->')) {
		$insert = "\n".pipeline('insert_head','<!-- f_insert_head -->')."\n";
		$texte = substr_replace($texte, $insert, $pos, 0);
	}

	return $texte;
}


/**
 * Insérer au besoin les boutons admins
 * 
 * Cette fonction est appelée par le pipeline affichage_final
 *
 * @param string $texte   Contenu de la page envoyée au navigateur
 * @return string         Contenu de la page envoyée au navigateur
**/
function f_admin ($texte) {
	if (defined('_VAR_PREVIEW') AND _VAR_PREVIEW AND $GLOBALS['html']) {
		include_spip('inc/filtres'); // pour http_img_pack
		$x = "<div class='spip-previsu' "
		     . http_style_background('preview-32.png')
		     . ">"
		     . _T('previsualisation')
		     . "</div>";
		if (!$pos = stripos($texte, '</body>'))
			$pos = strlen($texte);
		$texte = substr_replace($texte, $x, $pos, 0);
	}

	if (isset($GLOBALS['affiche_boutons_admin']) AND $GLOBALS['affiche_boutons_admin']) {
		include_spip('public/admin');
		$texte = affiche_boutons_admin($texte);
	}
	if (_request('var_mode')=='noajax'){
		$texte = preg_replace(',(class=[\'"][^\'"]*)ajax([^\'"]*[\'"]),Uims',"\\1\\2",$texte);
	}
	return $texte;
}

/**
 * Actions sur chaque inclusion
 *
 * Appelle f_afficher_blocs_ecrire() sur les inclusions dans l'espace privé.
 * Ne change rien dans l'espace public.
 * 
 * Cette fonction est appelée par le pipeline recuperer_fond 
 *
 * @see f_afficher_blocs_ecrire()
 * 
 * @param  array $flux  Description et contenu de l'inclusion
 * @return array $flux  Description et contenu de l'inclusion
**/
function f_recuperer_fond($flux) {
	if (!test_espace_prive()) return $flux;
	return f_afficher_blocs_ecrire($flux);
}

/**
 * Gérer le lancement du cron si des tâches sont en attente
 * 
 * Cette fonction est appelée par le pipeline affichage_final
 *
 * @param string $texte   Contenu de la page envoyée au navigateur
 * @return string         Contenu de la page envoyée au navigateur
 */
function f_queue(&$texte){

	// eviter une inclusion si rien a faire
	if (_request('action')=='cron'
		OR queue_sleep_time_to_next_job()>0
	  OR defined('_DEBUG_BLOCK_QUEUE')){
		return $texte;
	}

	include_spip('inc/queue');
	$code = queue_affichage_cron();

	// si rien a afficher
	// ou si on est pas dans une page html, on ne sait rien faire de mieux
	if (!$code OR !$GLOBALS['html'])
		return $texte;

	// inserer avant le </body> fermant si on peut, a la fin de la page sinon
	if (($p=strpos($texte,'</body>'))!==FALSE)
		$texte = substr($texte,0,$p).$code.substr($texte,$p);
	else
		$texte .= $code;

	return $texte;
}

?>
log.php000066600000006433152525475610006064 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

function inc_log_dist($message, $logname=NULL, $logdir=NULL, $logsuf=NULL) {
	static $test_repertoire = array();
	static $compteur = array();
	static $debugverb = ""; // pour ne pas le recalculer au reappel
	global $nombre_de_logs, $taille_des_logs;

	if (is_null($logname) OR !is_string($logname))
		$logname = defined('_FILE_LOG') ? _FILE_LOG : 'spip';
	if (!isset($compteur[$logname])) $compteur[$logname] = 0;
	if ($logname != 'maj'
	AND defined('_MAX_LOG')
	AND (
		$compteur[$logname]++ > _MAX_LOG
		OR !$nombre_de_logs
		OR !$taille_des_logs
	))
		return;

	$logfile = ($logdir===NULL ? _DIR_LOG : $logdir)
	  . ($logname)
	  . ($logsuf===NULL ? _FILE_LOG_SUFFIX : $logsuf);

	if (!isset($test_repertoire[$d = dirname($logfile)])) {
		$test_repertoire[$d] = false; // eviter une recursivite en cas d'erreur de sous_repertoire
		$test_repertoire[$d] = (@is_dir($d)?true:(function_exists('sous_repertoire')?sous_repertoire($d, '', false, true):false));
	}

	// si spip_log() dans mes_options, ou repertoire log/ non present, poser dans tmp/
	if (!defined('_DIR_LOG') OR !$test_repertoire[$d])
		$logfile = _DIR_RACINE._NOM_TEMPORAIRES_INACCESSIBLES.$logname.'.log';

	$rotate = 0;
	$pid = '(pid '.@getmypid().')';

	// accepter spip_log( Array )
	if (!is_string($message)) $message = var_export($message, true);

	if (!$debugverb AND defined('_LOG_FILELINE') AND _LOG_FILELINE){
		$debug = debug_backtrace();
		$l = $debug[1]['line'];
		$fi = $debug[1]['file'];
		if (strncmp($fi,_ROOT_RACINE,strlen(_ROOT_RACINE))==0)
			$fi = substr($fi,strlen(_ROOT_RACINE));
		$fu = isset($debug[2]['function']) ? $debug[2]['function'] : '';
		$debugverb = "$fi:L$l:$fu"."():";
	}

	$m = date("Y-m-d H:i:s").' '.$GLOBALS['ip'].' '.$pid.' '
	  //distinguer les logs prives et publics dans les grep
		. $debugverb
		. (test_espace_prive()?':Pri:':':Pub:')
		.preg_replace("/\n*$/", "\n", $message);


	if (@is_readable($logfile)
	AND (!$s = @filesize($logfile) OR $s > $taille_des_logs * 1024)) {
		$rotate = $nombre_de_logs;
		$m .= "[-- rotate --]\n";
	}

	$f = @fopen($logfile, "ab");
	if ($f) {
		fputs($f, (defined('_LOG_BRUT') AND _LOG_BRUT) ? $m : str_replace('<','&lt;',$m));
		fclose($f);
	}

	if ($rotate-- > 0
	AND function_exists('spip_unlink')) {
		spip_unlink($logfile . '.' . $rotate);
		while ($rotate--) {
			@rename($logfile . ($rotate ? '.' . $rotate : ''), $logfile . '.' . ($rotate + 1));
		}
	}

	// Dupliquer les erreurs specifiques dans le log general
	if ($logname !== _FILE_LOG
	  AND defined('_FILE_LOG'))
		inc_log_dist($logname=='maj' ? 'cf maj.log' : $message);
	$debugverb = "";
}

?>
rechercher.php000066600000022226152525475610007413 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/


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


// Donne la liste des champs/tables ou l'on sait chercher/remplacer
// avec un poids pour le score
// http://doc.spip.org/@liste_des_champs
function liste_des_champs() {
	static $liste=null;
	if (is_null($liste)) {
		$liste = array();
		// recuperer les tables_objets_sql declarees
		include_spip('base/objets');
		$tables_objets = lister_tables_objets_sql();
		foreach($tables_objets as $t=>$infos){
			if ($infos['rechercher_champs']){
				$liste[$infos['type']] = $infos['rechercher_champs'];
			}
		}
		// puis passer dans le pipeline
		$liste = pipeline('rechercher_liste_des_champs', $liste);
	}
	return $liste;
}


// Recherche des auteurs et mots-cles associes
// en ne regardant que le titre ou le nom
// http://doc.spip.org/@liste_des_jointures
function liste_des_jointures() {
	static $liste=null;
	if (is_null($liste)) {
		$liste = array();
		// recuperer les tables_objets_sql declarees
		include_spip('base/objets');
		$tables_objets = lister_tables_objets_sql();
		foreach($tables_objets as $t=>$infos){
			if ($infos['rechercher_jointures']){
				$liste[$infos['type']] = $infos['rechercher_jointures'];
			}
		}
		// puis passer dans le pipeline
		$liste = pipeline('rechercher_liste_des_jointures', $liste);
	}
	return $liste;
}

function expression_recherche($recherche, $options) {
	// ne calculer qu'une seule fois l'expression par hit
	// (meme si utilisee dans plusieurs boucles)
	static $expression = array();
	$key = serialize(array($recherche, $options['preg_flags']));
	if (isset($expression[$key]))
		return $expression[$key];

	$u = $GLOBALS['meta']['pcre_u'];
	if ($u AND strpos($options['preg_flags'],$u)===false)
		$options['preg_flags'] .= $u;
	include_spip('inc/charsets');
	$recherche = trim($recherche);

	$is_preg = false;
	if (substr($recherche, 0, 1) == '/' and substr($recherche, -1, 1) == '/' and strlen($recherche) > 2) {
		// c'est une preg
		$recherche_trans = translitteration($recherche);
		$preg = $recherche_trans.$options['preg_flags'];
		$is_preg = true;
	}
	else{
		// s'il y a plusieurs mots il faut les chercher tous : oblige REGEXP
		// sauf ceux de moins de 4 lettres (on supprime ainsi 'le', 'les', 'un',
		// 'une', 'des' ...)
		if (preg_match(",\s+,".$u, $recherche)){
			$is_preg = true;
			$recherche_inter = '|';
			$recherche_mots = explode(' ', $recherche);
			$min_long = defined('_RECHERCHE_MIN_CAR') ? _RECHERCHE_MIN_CAR : 4;
			foreach ($recherche_mots as $mot) {
				if (strlen($mot) >= $min_long) {
					$recherche_inter .= $mot.' ';
				}
			}
			// mais on cherche quand même l'expression complète, même si elle
			// comporte des mots de moins de quatre lettres
			$recherche = rtrim($recherche.preg_replace(',\s+,'.$u, '|', $recherche_inter), '|');
			$recherche_trans = translitteration($recherche);
		}

		$preg = '/'.str_replace('/', '\\/', $recherche_trans).'/' . $options['preg_flags'];
	}

	// Si la chaine est inactive, on va utiliser LIKE pour aller plus vite
	// ou si l'expression reguliere est invalide
	if (!$is_preg
	  OR (@preg_match($preg,'')===FALSE) ) {
		$methode = 'LIKE';
		$u = $GLOBALS['meta']['pcre_u'];
		// eviter les parentheses et autres caractères qui interferent avec pcre par la suite (dans le preg_match_all) s'il y a des reponses
		$recherche = str_replace(
			array('(',')','?','[', ']', '+', '*', '/'),
			array('\(','\)','[?]', '\[', '\]', '\+', '\*', '\/'),
			$recherche);
		$recherche_trans = translitteration($recherche);
		$recherche_mod = $recherche_trans;

		// echapper les % et _
		$q = str_replace(array('%','_'), array('\%', '\_'), trim($recherche));
		// les expressions entre " " sont un mot a chercher tel quel
		// -> on remplace les espaces par un _ et on enleve les guillemets
		if (preg_match(',["][^"]+["],Uims',$q,$matches)){
			foreach($matches as $match){
				// corriger le like dans le $q
				$word = preg_replace(",\s+,Uims","_",$match);
				$word = trim($word,'"');
				$q = str_replace($match,$word,$q);
				// corriger la regexp
				$word = preg_replace(",\s+,Uims","[\s]",$match);
				$word = trim($word,'"');
				$recherche_mod = str_replace($match,$word,$recherche_mod);
			}
		}
		$q = sql_quote(
			"%"
			. preg_replace(",\s+,".$u, "%", $q)
			. "%"
		);

		$preg = '/'.preg_replace(",\s+,".$u, ".+", trim($recherche_mod)).'/' . $options['preg_flags'];

	} else {
		$methode = 'REGEXP';
		$q = sql_quote(trim($recherche, '/'));
	}

	// tous les caracteres transliterables de $q sont remplaces par un joker
	// permet de matcher en SQL meme si on est sensible aux accents (SQLite)
	$q_t = $q;
	for($i = 0;$i<spip_strlen($q);$i++){
		$char = spip_substr($q,$i,1);
		if (!is_ascii($char)
		  AND $char_t = translitteration($char)
		  AND $char_t !== $char){
			$q_t = str_replace($char,$is_preg?".":"_", $q_t);
		}
	}

	$q = $q_t;

	// fix : SQLite 3 est sensible aux accents, on jokerise les caracteres
	// les plus frequents qui peuvent etre accentues
	// (oui c'est tres dicustable...)
	if (isset($GLOBALS['connexions'][$options['serveur']?$options['serveur']:0]['type'])
	  AND strncmp($GLOBALS['connexions'][$options['serveur']?$options['serveur']:0]['type'],'sqlite',6)==0){
		$q_t = strtr($q,"aeuioc",$is_preg?"......":"______");
		// si il reste au moins un char significatif...
		if (preg_match(",[^'%_.],",$q_t))
			$q = $q_t;
	}

	return $expression[$key] = array($methode, $q, $preg);
}


// Effectue une recherche sur toutes les tables de la base de donnees
// options :
// - toutvoir pour eviter autoriser(voir)
// - flags pour eviter les flags regexp par defaut (UimsS)
// - champs pour retourner les champs concernes
// - score pour retourner un score
// On peut passer les tables, ou une chaine listant les tables souhaitees
// http://doc.spip.org/@recherche_en_base
function recherche_en_base($recherche='', $tables=NULL, $options=array(), $serveur='') {
	include_spip('base/abstract_sql');

	if (!is_array($tables)) {
		$liste = liste_des_champs();

		if (is_string($tables)
		AND $tables != '') {
			$toutes = array();
			foreach(explode(',', $tables) as $t)
				if (isset($liste[$t]))
					$toutes[$t] = $liste[$t];
			$tables = $toutes;
			unset($toutes);
		} else
			$tables = $liste;
	}

	if (!strlen($recherche) OR !count($tables))
		return array();

	include_spip('inc/autoriser');

	// options par defaut
	$options = array_merge(array(
		'preg_flags' => 'UimsS',
		'toutvoir' => false,
		'champs' => false,
		'score' => false,
		'matches' => false,
		'jointures' => false,
		'serveur' => $serveur
		),
		$options
	);

	$results = array();

	// Utiliser l'iterateur (DATA:recherche)
	// pour recuperer les couples (id_objet, score)
	// Le resultat est au format { 
	//      id1 = { 'score' => x, attrs => { } },
	//      id2 = { 'score' => x, attrs => { } },
	// }
	include_spip('inc/memoization');
	foreach ($tables as $table => $champs) {
		# lock via memoization, si dispo
		if (function_exists('cache_lock'))
			cache_lock($lock = 'recherche '.$table.' '.$recherche);

		spip_timer('rech');

		// TODO: ici plutot charger un iterateur via l'API iterateurs
		include_spip('inc/recherche_to_array');
		$to_array = charger_fonction('recherche_to_array', 'inc');
		$results[$table] = $to_array($recherche,
			array_merge($options, array('table' => $table, 'champs' => $champs))
		);
		##var_dump($results[$table]);


		spip_log("recherche $table ($recherche) : ".count($results[$table])." resultats ".spip_timer('rech'),'recherche');

		if (isset($lock))
			cache_unlock($lock);
	}

	return $results;
}


// Effectue une recherche sur toutes les tables de la base de donnees
// http://doc.spip.org/@remplace_en_base
function remplace_en_base($recherche='', $remplace=NULL, $tables=NULL, $options=array()) {
	include_spip('inc/modifier');

	// options par defaut
	$options = array_merge(array(
		'preg_flags' => 'UimsS',
		'toutmodifier' => false
		),
		$options
	);
	$options['champs'] = true;


	if (!is_array($tables))
		$tables = liste_des_champs();

	$results = recherche_en_base($recherche, $tables, $options);

	$preg = '/'.str_replace('/', '\\/', $recherche).'/' . $options['preg_flags'];

	foreach ($results as $table => $r) {
		$_id_table = id_table_objet($table);
		foreach ($r as $id => $x) {
			if ($options['toutmodifier']
			OR autoriser('modifier', $table, $id)) {
				$modifs = array();
				foreach ($x['champs'] as $key => $val) {
					if ($key == $_id_table) next;
					$repl = preg_replace($preg, $remplace, $val);
					if ($repl <> $val)
						$modifs[$key] = $repl;
				}
				if ($modifs)
					objet_modifier_champs($table, $id,
						array(
							'champs' => array_keys($modifs),
						),
						$modifs);
			}
		}
	}
}

?>
presentation.php000066600000014717152525475610010022 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

include_spip('inc/presentation_mini');
include_spip('inc/layer');
include_spip('inc/texte');
include_spip('inc/filtres');
include_spip('inc/boutons');
include_spip('inc/actions');
include_spip('inc/puce_statut');
include_spip('inc/filtres_ecrire');
include_spip('inc/filtres_boites');

// http://doc.spip.org/@debut_cadre
function debut_cadre($style, $icone = "", $fonction = "", $titre = "", $id="", $class="", $padding=true) {
	$style_mapping=array('r'=>'simple','e'=>'raccourcis','couleur'=>'basic highlight','couleur-foncee'=>'basic highlight','trait-couleur'=>'important','alerte'=>'notice','info'=>'info','sous_rub'=>'simple sous-rub');
	$style_titre_mapping=array('couleur'=>'topper','trait-couleur'=>'section');
	$c = isset($style_mapping[$style])?$style_mapping[$style]:'simple';
	$class = $c . ($class?" $class":"");
	if (!$padding)
		$class .= ($class?" ":"")."no-padding";

	//($id?"id='$id' ":"")
	if (strlen($icone) > 1) {
		if ($icone_renommer = charger_fonction('icone_renommer','inc',true))
			list($fond,$fonction) = $icone_renommer($icone,$fonction);
		$size = 24;
		if (preg_match("/-([0-9]{1,3})[.](gif|png)$/i",$fond,$match))
			$size = $match[1];
		if ($fonction){
			// 2 images pour composer l'icone : le fond (article) en background,
			// la fonction (new) en image
			$icone = http_img_pack($fonction, "", "class='cadre-icone' width='$size' height='$size'\n" .
						http_style_background($fond, "no-repeat center center"));
		}
		else {
			$icone = http_img_pack($fond, "", "class='cadre-icone' width='$size' height='$size'");
		}
		$titre = $icone . $titre;
	}
	return boite_ouvrir($titre, $class,isset($style_titre_mapping[$style])?$style_titre_mapping[$style]:'',$id);
}

// http://doc.spip.org/@fin_cadre
function fin_cadre() {return boite_fermer();}


function debut_cadre_relief($icone='', $dummy='', $fonction='', $titre = '', $id="", $class=""){return debut_cadre('r', $icone, $fonction, $titre, $id, $class);}
function fin_cadre_relief(){return fin_cadre('r');}
function debut_cadre_enfonce($icone='', $dummy='', $fonction='', $titre = '', $id="", $class=""){return debut_cadre('e', $icone, $fonction, $titre, $id, $class);}
function fin_cadre_enfonce(){return fin_cadre('e');}
function debut_cadre_sous_rub($icone='', $dummy='', $fonction='', $titre = '', $id="", $class=""){return debut_cadre('sous_rub', $icone, $fonction, $titre, $id, $class);}
function fin_cadre_sous_rub(){return fin_cadre('sous_rub');}
function debut_cadre_couleur($icone='', $dummy='', $fonction='', $titre='', $id="", $class=""){return debut_cadre('couleur', $icone, $fonction, $titre, $id, $class);}
function fin_cadre_couleur(){return fin_cadre('couleur');}
function debut_cadre_couleur_foncee($icone='', $dummy='', $fonction='', $titre='', $id="", $class=""){return debut_cadre('couleur-foncee', $icone, $fonction, $titre, $id, $class);}
function fin_cadre_couleur_foncee(){return fin_cadre('couleur-foncee');}
function debut_cadre_trait_couleur($icone='', $dummy='', $fonction='', $titre='', $id="", $class=""){return debut_cadre('trait-couleur', $icone, $fonction, $titre, $id, $class);}
function fin_cadre_trait_couleur(){return fin_cadre('trait-couleur');}
function debut_boite_alerte() {return debut_cadre('alerte', '', '', '', '', '');}
function fin_boite_alerte() {return fin_cadre('alerte');}
function debut_boite_info() {return debut_cadre('info', '', '', '', '', '');}
function fin_boite_info() {return fin_cadre('info');}

// http://doc.spip.org/@gros_titre
function gros_titre($titre, $ze_logo=''){return "<h1 class='grostitre'>" . $ze_logo.' ' . typo($titre)."</h1>\n";}

// La boite des raccourcis
// Se place a droite si l'ecran est en mode panoramique.
// http://doc.spip.org/@bloc_des_raccourcis
function bloc_des_raccourcis($bloc) {
	return creer_colonne_droite()
	  . boite_ouvrir(_T('titre_cadre_raccourcis'),'raccourcis') . $bloc . boite_fermer();
}

// Compatibilite
// http://doc.spip.org/@afficher_plus
function afficher_plus($lien) {include_spip('inc/filtres_ecrire');afficher_plus_info($lien);}



//
// Fonctions d'affichage
//

// http://doc.spip.org/@afficher_objets
function afficher_objets($type, $titre_table,$requete,$formater='',$force=false){
	$afficher_objets = charger_fonction('afficher_objets','inc');
	return $afficher_objets($type, $titre_table,$requete,$formater,$force);
}

// Fonctions onglets
// http://doc.spip.org/@debut_onglet
// @param string $sous_classe	prend la valeur second pour definir les onglet de deuxieme niveau
function debut_onglet($classe="barre_onglet"){return "<div class='$classe clearfix'><ul>\n";}
// http://doc.spip.org/@fin_onglet
function fin_onglet(){return "</ul></div>\n";}
// http://doc.spip.org/@onglet
function onglet($texte, $lien, $onglet_ref, $onglet, $icone=""){
	return "<li>"
 	 . ($icone?http_img_pack($icone, '', " class='cadre-icone'"):'')
	 . lien_ou_expose($lien,$texte,$onglet == $onglet_ref)
	 . "</li>";
}

// http://doc.spip.org/@icone_inline
function icone_verticale($texte, $lien, $fond, $fonction="", $align="", $javascript=""){
	// cas d'ajax_action_auteur: faut defaire le boulot
	// (il faudrait fusionner avec le cas $javascript)
	if (preg_match(",^<a\shref='([^']*)'([^>]*)>(.*)</a>$,i",$lien,$r)) {
		list($x,$lien,$atts,$texte)= $r;
		$javascript .= $atts;
	}

	return icone_base($lien, $texte, $fond, $fonction,"verticale $align",$javascript);
}

// http://doc.spip.org/@icone_horizontale
function icone_horizontale($texte, $lien, $fond, $fonction="", $dummy="", $javascript="") {
	$retour = '';
	// cas d'ajax_action_auteur: faut defaire le boulot
	// (il faudrait fusionner avec le cas $javascript)
	if (preg_match(",^<a\shref='([^']*)'([^>]*)>(.*)</a>$,i",$lien,$r)) {
		list($x,$lien,$atts,$texte)= $r;
		$javascript .= $atts;
	}

	$retour = icone_base($lien, $texte, $fond, $fonction,"horizontale",$javascript);
	return $retour;
}

?>install.php000066600000040650152525475610006750 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

/**
 * Gestion de l'installation de SPIP
 *
 * @package SPIP\Installation
**/

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


/**
 * Écrit un fichier PHP nécessitant SPIP
 *
 * Écrit le texte transmis dans un fichier PHP. Cette fonction
 * ajoute les entêtes PHP et le test de sécurité vérifiant que SPIP
 * est chargé.
 *
 * @example
 *     install_fichier_connexion(_FILE_CONNECT_TMP, $contenu);
 * 
 * @todo
 *     Renommer cette fonction qui peut servir à d'autres utilisations ?
 * 
 * @param string $nom
 *     Chemin du fichier à créer
 * @param string $texte
 *     Code source du fichier (sans l'ouverture/fermeture PHP)
 * @return void
**/
function install_fichier_connexion($nom, $texte)
{
	$texte = "<"."?php\n"
	. "if (!defined(\"_ECRIRE_INC_VERSION\")) return;\n"
	. $texte
	. "?".">";

	ecrire_fichier($nom, $texte);
}


/**
 * Retourne le code source d'un fichier de connexion à une base de données
 *
 * Le code est un appel à la fonction spip_connect_db()
 *
 * @see spip_connect_db()
 *
 * @internal
 *     Attention etape_ldap4 suppose qu'il n'y aura qu'un seul appel de fonction
 *     dans le fichier produit.
 * 
 * @param string $adr       Adresse de la base de données {@example 'localhost'}
 * @param string $port      Numéro de port
 * @param string $login     Login de connexion
 * @param string $pass      Mot de passe de connexion
 * @param string $base      Nom de la base de données
 * @param string $type      Moteur SQL {@example 'sqlite3', 'mysql'}
 * @param string $pref      Préfixe des tables {@example 'spip'}
 * @param string $ldap      ?
 * @return string
 *     Texte du fichier de connexion
 * 
**/
function install_connexion($adr, $port, $login, $pass, $base, $type, $pref, $ldap='')
{
	$adr = addcslashes($adr,"'\\");
	$port = addcslashes($port,"'\\");
	$login = addcslashes($login,"'\\");
	$pass = addcslashes($pass,"'\\");
	$base = addcslashes($base,"'\\");
	$type = addcslashes($type,"'\\");
	$pref = addcslashes($pref,"'\\");
	$ldap = addcslashes($ldap,"'\\");
	return "\$GLOBALS['spip_connect_version'] = 0.7;\n"
	. "spip_connect_db("
	. "'$adr','$port','$login','$pass','$base'"
	. ",'$type', '$pref','$ldap');\n";
}


/**
 * Analyse un fichier de connexion à une base de données
 * 
 * Le fichier contient normalement le resultat de la fonction install_connexion().
 * L'analyse tient également compte des syntaxes des versions precedentes.
 * 
 * @param $string $file
 *     Chemin du fichier de connexion à analyser
 * @return array
 *     Tableau des informations sur la connexion
**/
function analyse_fichier_connection($file)
{
	$s = @join('', file($file));
	if (preg_match("#mysql_connect\([\"'](.*)[\"'],[\"'](.*)[\"'],[\"'](.*)[\"']\)#", $s, $regs)) {
		array_shift($regs);
		return $regs;
	} else {
		$ar = '\s*\'([^\']*)\'';
		$r = '\s*,' . $ar;
		$r = "#spip_connect_db[(]$ar$r$r$r$r(?:$r(?:$r(?:$r)?)?)?#";
		if (preg_match($r, $s, $regs)) {
			$regs[2] = $regs[1] . (!$regs[2] ? '' : ":$port_db;");
			array_shift($regs);
			array_shift($regs);
			return $regs;
		}
	}
	spip_log("$file n'est pas un fichier de connexion");
	return array();
}

/**
 * Liste les connecteurs aux bases SQL disponibles
 *
 * Dans le code SPIP ces connecteurs sont souvent appelés $connect ou $serveur
 * 
 * @example
 *     $bases = bases_referencees(_FILE_CONNECT_TMP);
 * 
 * @param $string $exclu
 *     Exclure un connecteur particulier (nom du fichier)
 * @return array
 *     Liste des noms de connecteurs
**/
function bases_referencees($exclu='')
{
	$tables = array();
	foreach(preg_files(_DIR_CONNECT, '.php$') as $f) {
		if ($f != $exclu AND analyse_fichier_connection($f))
			$tables[]= basename($f, '.php');
	}
	return $tables;
}


function install_mode_appel($server_db, $tout=true)
{
	return ($server_db != 'mysql') ? ''
	: (($tout  ? test_rappel_nom_base_mysql($server_db) : '')
		. test_sql_mode_mysql($server_db)	);
}

//
// Verifier que l'hebergement est compatible SPIP ... ou l'inverse :-)
// (sert a l'etape 1 de l'installation)
// http://doc.spip.org/@tester_compatibilite_hebergement
function tester_compatibilite_hebergement() {
	$err = array();

	$p = phpversion();
	if (preg_match(',^([0-9]+)\.([0-9]+)\.([0-9]+),', $p, $regs)) {
		$php = array($regs[1], $regs[2], $regs[3]);
		$m = '5.1.0';
		$min = explode('.', $m);
		if ($php[0]<$min[0]
		OR ($php[0]==$min[0] AND $php[1]<$min[1])
		OR ($php[0]==$min[0] AND $php[1]==$min[1] AND $php[2]<$min[2]))
			$err[] = _T('install_php_version', array('version' => $p,  'minimum' => $m));
	}

	// Si on n'a pas la bonne version de PHP, c'est la fin
	if ($err) 
		die("<div class='error'>" 
		. "<h3>"._T('avis_attention').'</h3><p>'._T('install_echec_annonce')."</p><ul class='spip'>"
		. "<li><strong>{$err[0]}</strong></li>\n</ul></div>");

	// Il faut une base de donnees tout de meme ...
	$serveurs = install_select_serveur();
	if (!$serveurs)
		$err[] = _T('install_extension_php_obligatoire')
		. " <a href='http://www.php.net/mysql'>MYSQL</a>"
		. "| <a href='http://www.php.net/pgsql'>PostgreSQL</a>"
		. "| <a href='http://www.php.net/sqlite'>SQLite</a>";

	// et il faut preg
	if (!function_exists('preg_match_all'))
		$err[] = _T('install_extension_php_obligatoire')
		. " <a href='http://se.php.net/pcre'>PCRE</a>";

	// et surtout pas ce mbstring.overload
	if ($a = @ini_get('mbstring.func_overload'))
		$err[] = _T('install_extension_mbstring')
		. "mbstring.func_overload=$a - <a href='http://www.php.net/mb_string'>mb_string</a>.<br /><small>";

	if ($err) {
			echo "<div class='error'>"
				."<h3>"._T('avis_attention').'</h3><p>'._T('install_echec_annonce')."</p><ul class='spip'>";
			foreach($err as $e)
				echo "<li><strong>$e</strong></li>\n";

		# a priori ici on pourrait die(), mais il faut laisser la possibilite
		# de forcer malgre tout (pour tester, ou si bug de detection)
		echo "</ul></div>\n";
	}
}


// Une fonction pour faciliter la recherche du login (superflu ?)
// http://doc.spip.org/@login_hebergeur
function login_hebergeur() {
	global $HTTP_X_HOST, $REQUEST_URI, $SERVER_NAME, $HTTP_HOST;

	$base_hebergeur = 'localhost'; # par defaut

	// Lycos (ex-Multimachin)
	if ($HTTP_X_HOST == 'membres.lycos.fr') {
		preg_match(',^/([^/]*),', $REQUEST_URI, $regs);
		$login_hebergeur = $regs[1];
	}
	// Altern
	else if (preg_match(',altern\.com$,', $SERVER_NAME)) {
		preg_match(',([^.]*\.[^.]*)$,', $HTTP_HOST, $regs);
		$login_hebergeur = preg_replace('[^\w\d]', '_', $regs[1]);
	}
	// Free
	else if (preg_match(',(.*)\.free\.fr$,', $SERVER_NAME, $regs)) {
		$base_hebergeur = 'sql.free.fr';
		$login_hebergeur = $regs[1];
	} else $login_hebergeur = '';

	return array($base_hebergeur, $login_hebergeur);
}


// http://doc.spip.org/@info_etape
function info_etape($titre, $complement = ''){
	return "<h2>".$titre."</h2>\n" .
	($complement ? "".$complement."\n":'');
}

// http://doc.spip.org/@bouton_suivant
function bouton_suivant($code = '') {
	if($code=='') $code = _T('bouton_suivant');
	static $suivant = 0;
	$id = 'suivant'.(($suivant>0)?strval($suivant):'');
	$suivant +=1;
	return "\n<p class='boutons suivant'><input id='".$id."' type='submit'\nvalue=\"" .
		$code .
		" >>\" /></p>\n";
}

// http://doc.spip.org/@info_progression_etape
function info_progression_etape($en_cours,$phase,$dir, $erreur = false){
	//$en_cours = _request('etape')?_request('etape'):"";
	$liste = find_all_in_path($dir,$phase.'(([0-9])+|fin)[.]php$');
	$debut = 1; $etat = "ok";
	$last = count($liste);
//	$texte_etat = array('ok'=>'OK','encours'=>_T('en_cours'),'todo'=>_T('todo'));

	$intitule_etat["etape_"][1] = typo(_T('info_connexion_base_donnee'));
	$intitule_etat["etape_"][2] = typo(_T('menu_aide_installation_choix_base'));
	$intitule_etat["etape_"][3] = typo(_T('info_informations_personnelles'));
	$intitule_etat["etape_"][4] = typo(_T('info_derniere_etape'));

	$intitule_etat["etape_ldap"][1] = typo(_T('titre_connexion_ldap'));
	$intitule_etat["etape_ldap"][2] = typo(_T('titre_connexion_ldap'));
	$intitule_etat["etape_ldap"][3] = typo(_T('info_chemin_acces_1'));
	$intitule_etat["etape_ldap"][4] = typo(_T('info_reglage_ldap'));
	$intitule_etat["etape_ldap"][5] = typo(_T('info_ldap_ok'));

//	$aff_etapes = "<span id='etapes'>";

	$aff_etapes = "<ul id='infos_etapes' class='infos_$phase$en_cours'>";

	foreach($liste as $etape=>$fichier){
		if ($debut < $last) {
			if ($debut == $en_cours && $erreur) $class = "on erreur";
			else if ($debut == $en_cours) $class = "on";
			else if ($debut > $en_cours) $class = "prochains";
			else $class = "valides";

			$aff_etapes .= "<li class='$class'><div class='fond'>";
			$aff_etapes .= ($debut == $en_cours)?"<strong>":'';
			$aff_etapes .= "<em>"._T('etape')." </em><span class='numero_etape'>$debut</span><em>&nbsp;: </em>";
			$aff_etapes .= $intitule_etat["$phase"][$debut];
			$aff_etapes .= ($debut == $en_cours)?"</strong>":'';
			$aff_etapes .= "</div></li>";
		}
		$debut++;
	}
	$aff_etapes .= "</ul>";
	$aff_etapes .= "<br class='nettoyeur' />\n";
	return $aff_etapes;
}


// http://doc.spip.org/@fieldset
function fieldset($legend,  $champs = array(), $apres='', $avant='') {
	return "<fieldset>\n" .
	  $avant .
	  ($legend ? "<legend>".$legend."</legend>\n" : '') .
	  fieldset_champs($champs) .
	  $apres .
	  "</fieldset>\n";
}

function fieldset_champs($champs = array())
  {
	$fieldset = '';
	foreach ($champs as $nom => $contenu) {
		$type = isset($contenu['hidden']) ? 'hidden' : (preg_match(',^pass,', $nom) ? 'password' : 'text');
		$class = isset($contenu['hidden']) ? '' : "class='formo' size='40' ";
		if(isset($contenu['alternatives'])) {
			$fieldset .= $contenu['label'] ."\n";
			foreach($contenu['alternatives'] as $valeur => $label) {
				$fieldset .= "<input type='radio' name='".$nom .
				"' id='$nom-$valeur' value='$valeur'"
				  .(($valeur==$contenu['valeur'])?"\nchecked='checked'":'')
				  ."/>\n";
				$fieldset .= "<label for='$nom-$valeur'>".$label."</label>\n";
			}
			$fieldset .= "<br />\n";
		}
		else {
			$fieldset .= "<label for='".$nom."'>".$contenu['label']."</label>\n";
			$fieldset .= "<input ".$class."type='".$type."' id='" . $nom . "' name='".$nom."'\nvalue='".$contenu['valeur']."'"
							  .(preg_match(',^(pass|login),', $nom)?" autocomplete='off'":'')
			          . ((isset($contenu['required']) AND $contenu['required'])?" required='required'":"")
			          .	" />\n";
		}
	}
	return $fieldset;
}

function install_select_serveur()
{
	$options = array();
	$dir = _DIR_RESTREINT . 'req/';
	$d = @opendir($dir);
	if (!$d) return array();
	while ($f = readdir($d)) {
		if ((preg_match('/^(.*)[.]php$/', $f, $s))
		AND is_readable($f = $dir . $f)) {
			require_once($f);
			$s = $s[1];
			$v = 'spip_versions_' . $s;
			if (function_exists($v) AND $v()) {
			  $titre = _T("install_select_type_$s");
				// proposer sqlite3 par defaut si dispo
				$selected = ($s=='sqlite3'?" selected='selected'":"");
			  $options[$s] =  "<option value='$s'$selected>"
			    . ($titre ? $titre : $s)
			    ."</option>";
			} else spip_log("$s: portage indisponible");
		}
	}
	sort($options);
	return $options;
}

// http://doc.spip.org/@install_connexion_form
function install_connexion_form($db, $login, $pass, $predef, $hidden, $etape, $jquery=true)
{
	$server_db = (is_string($predef[0])) ? $predef[0] : '';

	return generer_form_ecrire('install', (
	  "\n<input type='hidden' name='etape' value='$etape' />"
	. $hidden
	. (_request('echec')?
			("<p><b>"._T('avis_connexion_echec_1').
			"</b></p><p>"._T('avis_connexion_echec_2')."</p><p style='font-size: small;'>"._T('avis_connexion_echec_3')."</p>")
			:"")

	. ($jquery?http_script('',  'jquery.js'):'')
	. http_script('
		$(document).ready(function() {
			$("input[type=hidden][name=server_db]").each(function(){
				if ($(this).attr("value").match("sqlite*")){
					$("#install_adresse_base_hebergeur,#install_login_base_hebergeur,#install_pass_base_hebergeur").hide();
				}
			});
			if ($("#sql_serveur_db").length) {
				if ($("#sql_serveur_db").attr("value").match("sqlite*"))
						$("#install_adresse_base_hebergeur,#install_login_base_hebergeur,#install_pass_base_hebergeur").hide();
					else
						$("#install_adresse_base_hebergeur,#install_login_base_hebergeur,#install_pass_base_hebergeur").show();
				$("#sql_serveur_db").change(function(){
					if ($(this).find("option:selected").attr("value").match("sqlite*"))
						$("#install_adresse_base_hebergeur,#install_login_base_hebergeur,#install_pass_base_hebergeur").hide();
					else
						$("#install_adresse_base_hebergeur,#install_login_base_hebergeur,#install_pass_base_hebergeur").show();
				});
			}
		});')

	. ($server_db
		? '<input type="hidden" name="server_db" value="'.$server_db.'" />'
			. (($predef[0])
			   ?('<h3>'._T('install_serveur_hebergeur').'</h3>')
				:'')
		: ('<fieldset><legend>'
		   ._T('install_select_type_db')
		. "</legend>"
			.'<label for="sql_serveur_db" class="p">'
			. _T('install_types_db_connus')
			// Passer l'avertissement SQLIte en  commentaire, on pourra facilement le supprimer par la suite sans changer les traductions.
			. "<br /><small>(". _T('install_types_db_connus_avertissement') .')</small>'
			.'</label>'
		. "\n<div class='p center'><select name='server_db' id='sql_serveur_db' >\n"
		.   join("\n", install_select_serveur())
		. "\n</select></div></fieldset>")
	)
	. '<div id="install_adresse_base_hebergeur">'
	. '<p>'. _T('texte_connexion_mysql').'</p>'
	. ($predef[1]
	? '<h3>'._T('install_adresse_base_hebergeur').'</h3>'
	: fieldset(_T('entree_base_donnee_1'),
		array(
			'adresse_db' => array(
				'label' => $db[1],
				'valeur' => $db[0]
			),
		)
	)
	)
	. '</div>'

	. '<div id="install_login_base_hebergeur">'
	. ($predef[2]
	? '<h3>'._T('install_login_base_hebergeur').'</h3>'
	: fieldset(_T('entree_login_connexion_1'),
		array(
			'login_db' => array(
					'label' => $login[1],
					'valeur' => $login[0]
			),
		)
	)
	)
	. '</div>'

	. '<div id="install_pass_base_hebergeur">'
	. ($predef[3]
	? '<h3>'._T('install_pass_base_hebergeur').'</h3>'
	: fieldset(_T('entree_mot_passe_1'),
		array(
			'pass_db' => array(
				'label' => $pass[1],
				'valeur' => $pass[0]
			),
		)
	)
	)
	. '</div>'

	. bouton_suivant()));

}

// 4 valeurs qu'on reconduit d'un script a l'autre
// sauf s'ils sont predefinis.

// http://doc.spip.org/@predef_ou_cache
function predef_ou_cache($adresse_db, $login_db, $pass_db, $server_db)
{
	return ((defined('_INSTALL_HOST_DB'))
		? ''
		: "\n<input type='hidden' name='adresse_db'  value=\"".spip_htmlspecialchars($adresse_db)."\" />"
	)
	. ((defined('_INSTALL_USER_DB'))
		? ''
		: "\n<input type='hidden' name='login_db' value=\"".spip_htmlspecialchars($login_db)."\" />"
	)
	. ((defined('_INSTALL_PASS_DB'))
		? ''
		: "\n<input type='hidden' name='pass_db' value=\"".spip_htmlspecialchars($pass_db)."\" />"
	)

	. ((defined('_INSTALL_SERVER_DB'))
		? ''
		: "\n<input type='hidden' name='server_db' value=\"".spip_htmlspecialchars($server_db)."\" />"
	   );
}

// presentation des bases existantes

// http://doc.spip.org/@install_etape_liste_bases
function install_etape_liste_bases($server_db, $login_db, $disabled=array())
{
	$bases = $checked = array();
	$noms = sql_listdbs($server_db);
	if (!$noms) return '';

	foreach ($noms as $nom){
		$id = spip_htmlspecialchars($nom);
		$dis = in_array($nom, $disabled) ? " disabled='disabled'" : '';
		$base = " name=\"choix_db\" value=\""
		  . $nom
		  . '"'
		  . $dis
		  . " type='radio' id='$id'";
		$label = "<label for='$id'>"
		. ($dis ? "<i>$nom</i>" : $nom)
		. "</label>";

		if (!$checked AND !$dis AND
		    (($nom == $login_db) OR
			($GLOBALS['table_prefix'] == $nom))) {
			$checked = "<input$base checked='checked' />\n$label";
		} else {
			$bases[]= "<input$base />\n$label";
		}
	}

	if (!$bases && !$checked) return false;

	if ($checked) {array_unshift($bases, $checked); $checked = true;}

	return array($checked, $bases);
}

function install_propager($hidden)
{
	$res = '';
	foreach($hidden as $k) {
		$v = spip_htmlentities(_request($k));
		$res .= "<input type='hidden' name='$k' value='$v' />";
	}
	return $res;
}
?>
auth.php000066600000047255152525475610006253 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

include_spip('base/abstract_sql');




/**
 * Fonction d'authentification. Retourne:
 *  - URL de connexion  si on ne sait rien (pas de cookie, pas Auth_user);
 *  - URL de connexion  si on ne sait rien (pas de cookie, pas Auth_user);
 *  - un tableau si visiteur sans droit (tableau = sa ligne SQL)
 *  - code numerique d'erreur SQL
 *  - une chaine vide si autorisation a penetrer dans l'espace prive.
 *
 * 
 * http://doc.spip.org/@inc_auth_dist
 *
 * @return array|int|string
 */
function inc_auth_dist() {

	global $connect_login ;

	$row = auth_mode();

	if ($row) return auth_init_droits($row);

	if (!$connect_login) return auth_a_loger();

	// Cas ou l'auteur a ete identifie mais on n'a pas d'info sur lui
	// C'est soit parce que la base est inutilisable,
	// soit parce que la table des auteurs a changee (restauration etc)
	// Pas la peine d'insister.
	// Renvoyer le nom fautif et une URL de remise a zero

	if (spip_connect())
		return array('login' => $connect_login,
			'site' => generer_url_public('', "action=logout&amp;logout=prive"));

	$n = intval(sql_errno());
	spip_log("Erreur base de donnees $n " . sql_error());
	return $n ? $n : 1;
}

/**
 * fonction appliquee par ecrire/index sur le resultat de la precedente
 * en cas de refus de connexion.
 * Retourne un message a afficher ou redirige illico.
 *
 * @param  $raison
 * @return array|string
 */
function auth_echec($raison)
{
	include_spip('inc/minipres');
	include_spip('inc/headers');
	// pas authentifie. Pourquoi ?
	if (is_string($raison)) {
		// redirection vers une page d'authentification
		// on ne revient pas de cette fonction
		// sauf si pb de header
		$raison = redirige_formulaire($raison);
	} elseif (is_int($raison)) {
		// erreur SQL a afficher
		$raison = minipres(_T('info_travaux_titre'), _T('titre_probleme_technique'). "<p><tt>".sql_errno()." ".sql_error()."</tt></p>");
	} elseif (@$raison['statut']) {
		// un simple visiteur n'a pas acces a l'espace prive
		spip_log("connexion refusee a " . @$raison['id_auteur']);
		$raison = minipres(_T('avis_erreur_connexion'),_T('avis_erreur_visiteur'));
	} else {
		// auteur en fin de droits ...
		$h = $raison['site'];
		$raison = minipres(_T('avis_erreur_connexion'),
				"<br /><br /><p>"
				. _T('texte_inc_auth_1',
				array('auth_login' => $raison['login']))
				. " <a href='$h'>"
				.  _T('texte_inc_auth_2')
				. "</a>"
				. _T('texte_inc_auth_3'));
	}
	return $raison;
}

/**
 * Retourne la description d'un authentifie par cookie ou http_auth
 * Et affecte la globale $connect_login
 *
 * @return array|bool|string
 */
function auth_mode()
{
	global $auth_can_disconnect, $ignore_auth_http, $ignore_remote_user;
	global $connect_login ;

	//
	// Initialiser variables (eviter hacks par URL)
	//

	$connect_login = '';
	$id_auteur = NULL;
	$auth_can_disconnect = false;

	//
	// Recuperer les donnees d'identification
	//

	// Session valide en cours ?
	if (isset($_COOKIE['spip_session'])) {
		$session = charger_fonction('session', 'inc');
		if ($id_auteur = $session()
		OR $id_auteur===0 // reprise sur restauration
		) {
			$auth_can_disconnect = true;
			$connect_login = $GLOBALS['visiteur_session']['login'];
		} else unset($_COOKIE['spip_session']);
	}

	// Essayer auth http si significatif
	// (ignorer les login d'intranet independants de spip)
	if (!$ignore_auth_http) {
		if (
			(isset($_SERVER['PHP_AUTH_USER']) AND isset($_SERVER['PHP_AUTH_PW'])
						AND $r = lire_php_auth($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']))
			OR
			// Si auth http differtente de basic, PHP_AUTH_PW
			// est indisponible mais tentons quand meme pour
			// autocreation via LDAP
			(isset($_SERVER['REMOTE_USER'])
						AND $r = lire_php_auth($_SERVER['PHP_AUTH_USER'] = $_SERVER['REMOTE_USER'], ''))
			) {
				if (!$id_auteur) {
					$_SERVER['PHP_AUTH_PW'] = '';
					$auth_can_disconnect = true;
					$GLOBALS['visiteur_session'] = $r;
					$connect_login = $GLOBALS['visiteur_session']['login'];
					$id_auteur = $r['id_auteur'];
				} else {
				  // cas de la session en plus de PHP_AUTH
				  /*				  if ($id_auteur != $r['id_auteur']){
				    spip_log("vol de session $id_auteur" . join(', ', $r));
					unset($_COOKIE['spip_session']);
					$id_auteur = '';
					} */
				}
		}
		// Authentification .htaccess old style, car .htaccess semble
		// souvent definir *aussi* PHP_AUTH_USER et PHP_AUTH_PW
		else if (isset($_SERVER['REMOTE_USER']))
			$connect_login = $_SERVER['REMOTE_USER'];
	}

	$where = (is_numeric($id_auteur)
	/*AND $id_auteur>0*/ // reprise lors des restaurations
	) ?
	  "id_auteur=$id_auteur" :
	  (!strlen($connect_login) ? '' : "login=" . sql_quote($connect_login,'','text'));

	if (!$where) return '';

	// Trouver les autres infos dans la table auteurs.
	// le champ 'quand' est utilise par l'agenda

	return sql_fetsel("*, en_ligne AS quand", "spip_auteurs", "$where AND statut!='5poubelle'");
}

/**
 * Init des globales pour tout l'espace prive si visiteur connu
 * Le tableau global visiteur_session contient toutes les infos pertinentes et
 * a jour (tandis que $visiteur_session peut avoir des valeurs un peu datees
 * s'il est pris dans le fichier de session)
 * Les plus utiles sont aussi dans les variables simples ci-dessus
 * si la globale est vide ce n'est pas un tableau, on la force pour empecher un warning
 *
 * @param array $row
 * @return array|string
 */
function auth_init_droits($row)
{
	global $connect_statut, $connect_toutes_rubriques, $connect_id_rubrique, $connect_login, $connect_id_auteur;

	if ($row['statut']=='nouveau'){
		include_spip('action/inscrire_auteur');
		$row = confirmer_statut_inscription($row);
	}

	$connect_id_auteur = $row['id_auteur'];
	$connect_login = $row['login'];
	$connect_statut = $row['statut'];

	$GLOBALS['visiteur_session'] = array_merge((array)$GLOBALS['visiteur_session'], $row);

	// au cas ou : ne pas memoriser les champs sensibles
	unset($GLOBALS['visiteur_session']['pass']);
	unset($GLOBALS['visiteur_session']['htpass']);
	unset($GLOBALS['visiteur_session']['alea_actuel']);
	unset($GLOBALS['visiteur_session']['alea_futur']);

	// creer la session au besoin
	if (!isset($_COOKIE['spip_session'])) {
		$session = charger_fonction('session', 'inc');
		$spip_session = $session($row);
	}

	// reinjecter les preferences_auteur apres le reset de spip_session
	// car utilisees au retour par auth_loger()
	$r = @unserialize($row['prefs']);
	$GLOBALS['visiteur_session']['prefs'] = ($r ? $r : array());
	// si prefs pas definies, les definir par defaut
	if (!isset($GLOBALS['visiteur_session']['prefs']['couleur'])){
		$GLOBALS['visiteur_session']['prefs']['couleur'] = 1;
		$GLOBALS['visiteur_session']['prefs']['display'] = 2;
		$GLOBALS['visiteur_session']['prefs']["display_navigation"] = "navigation_avec_icones";
		$GLOBALS['visiteur_session']['prefs']["display_outils"] = "oui";
	}

	$GLOBALS['visiteur_session'] = pipeline('preparer_visiteur_session',array('args'=>array('row'=>$row),'data'=>$GLOBALS['visiteur_session']));

	// Etablir les droits selon le codage attendu
	// dans ecrire/index.php ecrire/prive.php

	// Pas autorise a acceder a ecrire ? renvoyer le tableau
	// A noter : le premier appel a autoriser() a le bon gout
	// d'initialiser $GLOBALS['visiteur_session']['restreint'],
	// qui ne figure pas dans le fichier de session
	include_spip('inc/autoriser');

	if (!autoriser('ecrire'))
		return $row;

	// autoriser('ecrire') ne laisse passer que les Admin et les Redac

	auth_trace($row);

	// Administrateurs
	if (in_array($connect_statut, explode(',', _STATUT_AUTEUR_RUBRIQUE))) {
		if (is_array($GLOBALS['visiteur_session']['restreint'])) {
			$connect_id_rubrique = $GLOBALS['visiteur_session']['restreint'];
		}
		if ($connect_statut == '0minirezo') { 
			$connect_toutes_rubriques = !$connect_id_rubrique;
		}
	}
	// Pour les redacteurs, inc_version a fait l'initialisation minimale

	return ''; // i.e. pas de pb.
}

/**
 * Retourne l'url de connexion
 *
 * @return string
 */
function auth_a_loger()
{
	$redirect = generer_url_public('login',
	"url=" . rawurlencode(self('&',true)), '&');

	// un echec au "bonjour" (login initial) quand le statut est
	// inconnu signale sans doute un probleme de cookies
	if (isset($_GET['bonjour']))
		$redirect = parametre_url($redirect,
			'var_erreur',
			(!isset($GLOBALS['visiteur_session']['statut'])
					? 'cookie'
					: 'statut'
			 ),
					  '&'
					  );
	return $redirect;
}

/**
 * Tracer en base la date de derniere connexion de l'auteur
 * http://doc.spip.org/@auth_trace
 *
 * @param array $row
 * @param null|string $date
 */
function auth_trace($row, $date=null)
{
	// Indiquer la connexion. A la minute pres ca suffit.
	if (!is_numeric($connect_quand = $row['quand']))
		$connect_quand = strtotime($connect_quand);

	if (is_null($date))
		$date = date('Y-m-d H:i:s');

	if (abs(strtotime($date) - $connect_quand)  >= 60) {
		sql_updateq("spip_auteurs", array("en_ligne" => $date), "id_auteur=" .$row['id_auteur']);
		$row['en_ligne'] = $date;
	}

	pipeline('trig_auth_trace',array('args'=>array('row'=>$row,'date'=>$date)));
}


/** ----------------------------------------------------------------------------
 * API Authentification, gestion des identites centralisees
 */

/**
 * Fonction aiguillage, privee
 * @param string $fonction
 * @param array $args
 * @param mixed $defaut
 * @return mixed
 */
function auth_administrer($fonction,$args,$defaut=false){
	$auth_methode = array_shift($args);
	$auth_methode = $auth_methode ? $auth_methode : 'spip'; // valeur par defaut au cas ou
	if ($auth = charger_fonction($auth_methode,'auth',true)
		AND function_exists($f="auth_{$auth_methode}_$fonction")
	)
		$res = call_user_func_array($f, $args);
	else
		$res = $defaut;
	$res = pipeline('auth_administrer',array(
		'args' => array(
			'fonction' => $fonction,
			'methode' => $auth_methode,
			'args' => $args
		),
		'data' => $res
	));
	return $res;
}

/**
 * Pipeline pour inserer du contenu dans le formulaire de login
 *
 * @param array $flux
 * @return array
 */
function auth_formulaire_login($flux){
	foreach ($GLOBALS['liste_des_authentifications'] as $methode)
		$flux = auth_administrer('formulaire_login',array($methode,$flux),$flux);
	return $flux;
}



/**
 * Retrouver le login interne lie a une info login saisie
 * la saisie peut correspondre a un login delegue
 * qui sera alors converti en login interne apres verification
 *
 * @param string $login
 * @param string $serveur
 * @return string/bool
 */
function auth_retrouver_login($login, $serveur=''){
	if (!spip_connect($serveur)) {
		include_spip('inc/minipres');
		echo minipres(_T('info_travaux_titre'),
			      _T('titre_probleme_technique'));
		exit;
	}

	foreach ($GLOBALS['liste_des_authentifications'] as $methode) {
		if ($auteur = auth_administrer('retrouver_login',array($methode, $login, $serveur))) {
			return $auteur;
		}
	}
	return false;
}


/**
 * informer sur un login
 * Ce dernier transmet le tableau ci-dessous a la fonction JS informer_auteur
 * Il est invoque par la fonction JS actualise_auteur via la globale JS
 * page_auteur=#URL_PAGE{informer_auteur} dans le squelette login
 * N'y aurait-il pas plus simple ?
 *
 * @param string $login
 * @param string $serveur
 * @return array
 */
function auth_informer_login($login, $serveur=''){
	if (!$login
		OR !$login = auth_retrouver_login($login, $serveur)
		OR !$row = sql_fetsel('*','spip_auteurs','login='.sql_quote($login,$serveur,'text'),'','','','',$serveur)
		)
		return array();

	$prefs = unserialize($row['prefs']);
	$infos = array(
		'id_auteur'=>$row['id_auteur'],
		'login'=>$row['login'],
		'cnx' => ($prefs['cnx'] == 'perma') ? '1' : '0',
		'logo' => recuperer_fond('formulaires/inc-logo_auteur', $row),
	);

	// desactiver le hash md5 si pas auteur spip ?
	if ($row['source']!=='spip'){
		$row['alea_actuel']= '';
		$row['alea_futur']= '';
	}
	verifier_visiteur();

	return auth_administrer('informer_login',array($row['source'],$infos, $row, $serveur),$infos);
}


/**
 * Essayer les differentes sources d'authenfication dans l'ordre specifie.
 * S'en souvenir dans visiteur_session['auth']
 *
 * @param string $login
 * @param string $password
 * @param string $serveur
 * @return mixed
 */
function auth_identifier_login($login, $password, $serveur=''){
	$erreur = "";
	foreach ($GLOBALS['liste_des_authentifications'] as $methode) {
		if ($auth = charger_fonction($methode, 'auth',true)){
			$auteur = $auth($login, $password, $serveur);
			if (is_array($auteur) AND count($auteur)) {
				spip_log("connexion de $login par methode $methode");
				$auteur['auth'] = $methode;
				return $auteur;
			}
			elseif (is_string($auteur))
				$erreur .= "$auteur ";
		}
	}
	return $erreur;
}

/**
 * Fournir une url de retour apres login par un SSO
 * pour finir l'authentification
 *
 * @param string $auth_methode
 * @param string $login
 * @param string $redirect
 * @param string $serveur
 * @return string
 */
function auth_url_retour_login($auth_methode, $login, $redirect='', $serveur=''){
	$securiser_action = charger_fonction('securiser_action','inc');
	return $securiser_action('auth', "$auth_methode/$login", $redirect, true);
}

function auth_terminer_identifier_login($auth_methode, $login, $serveur=''){
	$args = func_get_args();
	$auteur = auth_administrer('terminer_identifier_login',$args);
	return $auteur;
}

/**
 * Loger un auteur suite a son identification
 *
 * @param array $auteur
 * @return bool
 */
function auth_loger($auteur){
	if (!is_array($auteur) OR !count($auteur))
		return false;

	// initialiser et poser le cookie de session
	unset($_COOKIE['spip_session']);
	auth_init_droits($auteur);

	// initialiser les prefs
	$p = $GLOBALS['visiteur_session']['prefs'];
	$p['cnx'] = ($auteur['cookie'] == 'oui') ? 'perma' : '';

	sql_updateq('spip_auteurs',
	            array('prefs' => serialize($p)),
	            "id_auteur=" . $auteur['id_auteur']);

	// Si on est admin, poser le cookie de correspondance
	include_spip('inc/cookie');
	if ($auteur['statut'] == '0minirezo') {
		spip_setcookie('spip_admin', '@'.$auteur['login'],
		time() + 7 * 24 * 3600);
	}
	// sinon le supprimer ...
	else {
		spip_setcookie('spip_admin', '',1);
	}

	//  bloquer ici le visiteur qui tente d'abuser de ses droits
	verifier_visiteur();
	return true;
}


function auth_deloger(){
	$logout = charger_fonction('logout','action');
	$logout();
}

/**
 * Tester la possibilite de modifier le login d'authentification
 * pour la methode donnee
 *
 * @param string $auth_methode
 * @param string $serveur
 * @return bool
 */
function auth_autoriser_modifier_login($auth_methode, $serveur=''){
	$args = func_get_args();
	return auth_administrer('autoriser_modifier_login',$args);
}

/**
 * Verifier la validite d'un nouveau login pour modification
 * pour la methode donnee
 *
 * @param string $auth_methode
 * @param string $new_login
 * @param int $id_auteur
 * @param string $serveur
 * @return string
 *  message d'erreur ou chaine vide si pas d'erreur
 */
function auth_verifier_login($auth_methode, $new_login, $id_auteur=0, $serveur=''){
	$args = func_get_args();
	return auth_administrer('verifier_login',$args,'');
}

/**
 * Modifier le login d'un auteur pour la methode donnee
 *
 * @param string $auth_methode
 * @param string $new_login
 * @param int $id_auteur
 * @param string $serveur
 * @return bool
 */
function auth_modifier_login($auth_methode, $new_login, $id_auteur, $serveur=''){
	$args = func_get_args();
	return auth_administrer('modifier_login',$args);
}

/**
 * Tester la possibilite de modifier le pass
 * pour la methode donnee
 *
 * @param string $auth_methode
 * @param string $serveur
 * @return bool
 *	succes ou echec
 */
function auth_autoriser_modifier_pass($auth_methode, $serveur=''){
	$args = func_get_args();
	return auth_administrer('autoriser_modifier_pass',$args);
}

/**
 * Verifier la validite d'un pass propose pour modification
 * pour la methode donnee
 *
 * @param string $auth_methode
 * @param string $login
 * @param string $new_pass
 * @param int $id_auteur
 * @param string $serveur
 * @return string
 *	message d'erreur ou chaine vide si pas d'erreur
 */
function auth_verifier_pass($auth_methode, $login, $new_pass, $id_auteur=0, $serveur=''){
	$args = func_get_args();
	return auth_administrer('verifier_pass',$args,'');
}

/**
 * Modifier le mot de passe d'un auteur
 * pour la methode donnee
 *
 * @param string $auth_methode
 * @param string $login
 * @param string $new_pass
 * @param int $id_auteur
 * @param string $serveur
 * @return bool
 *	succes ou echec
 */
function auth_modifier_pass($auth_methode, $login, $new_pass, $id_auteur, $serveur=''){
	$args = func_get_args();
	return auth_administrer('modifier_pass',$args);
}

/**
 * Synchroniser un compte sur une base distante pour la methode
 * donnee lorsque des modifications sont faites dans la base auteur
 *
 * @param string|bool $auth_methode
 *   ici true permet de forcer la synchronisation de tous les acces pour toutes les methodes
 * @param int $id_auteur
 * @param array $champs
 * @param array $options
 * @param string $serveur
 * @return void
 */
function auth_synchroniser_distant($auth_methode=true, $id_auteur=0, $champs=array(), $options = array(), $serveur=''){
	$args = func_get_args();
	if ($auth_methode===true OR (isset($options['all']) AND $options['all']==true)){
		$options['all'] = true; // ajouter une option all=>true pour chaque auth
		$args = array(true, $id_auteur, $champs, $options, $serveur);
		foreach ($GLOBALS['liste_des_authentifications'] as $methode) {
			array_shift($args);
			array_unshift($args,$methode);
			auth_administrer('synchroniser_distant',$args);
		}
	}
	else
		auth_administrer('synchroniser_distant',$args);
}


/**
 *
 * @param string $login
 * @param string $pw
 * @param string $serveur
 * @return array
 */
function lire_php_auth($login, $pw, $serveur=''){

	$row = sql_fetsel('*', 'spip_auteurs', 'login=' . sql_quote($login,$serveur,'text'),'','','','',$serveur);

	if (!$row) {
		if (spip_connect_ldap($serveur)
		AND $auth_ldap = charger_fonction('ldap', 'auth', true))
			return $auth_ldap($login, $pw, $serveur, true);
		return false;
	}
	// su pas de source definie
	// ou auth/xxx introuvable, utiliser 'spip'
	if (!$auth_methode = $row['source']
		OR !$auth = charger_fonction($auth_methode, 'auth', true))
		$auth = charger_fonction('spip', 'auth', true);

	$auteur='';
	if ($auth)
		$auteur = $auth($login, $pw, $serveur, true);
	// verifier que ce n'est pas un message d'erreur
	if (is_array($auteur) AND count($auteur))
		return $auteur;
	return false;
}

/**
 * entete php_auth (est-encore utilise ?)
 *
 * @param string $pb
 * @param string $raison
 * @param string $retour
 * @param string $url
 * @param string $re
 * @param string $lien
 */
function ask_php_auth($pb, $raison, $retour='', $url='', $re='', $lien='') {
	@Header("WWW-Authenticate: Basic realm=\"espace prive\"");
	@Header("HTTP/1.0 401 Unauthorized");
	$corps = "";
	$public = generer_url_public();
	$ecrire = generer_url_ecrire();
	$retour = $retour?$retour:_T('icone_retour');
	$corps .= "<p>$raison</p>[<a href='$public'>$retour</a>] ";
	if ($url) {
		$corps .= "[<a href='" . generer_url_action('cookie',"essai_auth_http=oui&$url") . "'>$re</a>]";
	}

	if ($lien)
		$corps .= " [<a href='$ecrire'>"._T('login_espace_prive')."</a>]";
	include_spip('inc/minipres');
	echo minipres($pb,$corps);
	exit;
}
?>
couleurs.php000066600000007150152525475610007141 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

// Appelee sans argument, cette fonction retourne un menu de couleurs
// Avec un argument numerique, elle retourne les parametres d'URL 
// pour les feuilles de style calculees (cf commencer_page et svg)
// Avec un argument de type tableau, soit elle remplace le tableau par defaut
// par celui donne en argument, soit, avec le second parametre
// a true, elle complete le tableau par celui donne en premier argument.

// http://doc.spip.org/@inc_couleurs_dist
function inc_couleurs_dist($choix=NULL, $ajouter=false)
{
	static $couleurs_spip = array(
// Vert
1 => array (
		"couleur_foncee" => "#9DBA00",
		"couleur_claire" => "#C5E41C",
		"couleur_lien" => "#657701",
		"couleur_lien_off" => "#A6C113"
		),
// Violet clair
2 => array (
		"couleur_foncee" => "#eb68b3",
		"couleur_claire" => "#ffa9e6",
		"couleur_lien" => "#8F004D",
		"couleur_lien_off" => "#BE6B97"
		),
// Orange
3 => array (
		"couleur_foncee" => "#fa9a00",
		"couleur_claire" => "#ffc000",
		"couleur_lien" => "#FF5B00",
		"couleur_lien_off" => "#B49280"
		),
// Saumon
4 => array (
		"couleur_foncee" => "#CDA261",
		"couleur_claire" => "#FFDDAA",
		"couleur_lien" => "#AA6A09",
		"couleur_lien_off" => "#B79562"
		),
//  Bleu pastel
5 => array (
		"couleur_foncee" => "#5da7c5",
		"couleur_claire" => "#97d2e1",
		"couleur_lien" => "#116587",
		"couleur_lien_off" => "#81B7CD"
		),
//  Gris
6 => array (
		"couleur_foncee" => "#85909A",
		"couleur_claire" => "#C0CAD4",
		"couleur_lien" => "#3B5063",
		"couleur_lien_off" => "#6D8499"
		),
// Vert de gris
7 => array (
		"couleur_foncee" => "#999966",
		"couleur_claire" => "#CCCC99",
		"couleur_lien" => "#666633",
		"couleur_lien_off" => "#999966"
		),
// Rose vieux
8 => array (
		"couleur_foncee" => "#EB68B3",
		"couleur_claire" => "#E4A7C5",
		"couleur_lien" => "#8F004D",
		"couleur_lien_off" => "#BE6B97"
		),
// Violet
9 => array (
		"couleur_foncee" => "#8F8FBD",
		"couleur_claire" => "#C4C4DD",
		"couleur_lien" => "#6071A5",
		"couleur_lien_off" => "#5C5C8C"
		),
//  Gris
10 => array (
		"couleur_foncee" => "#909090",
		"couleur_claire" => "#D3D3D3",
		"couleur_lien" => "#808080",
		"couleur_lien_off" => "#909090"
		),
);

	if (is_numeric($choix)) {
		// Compatibilite ascendante (plug-ins notamment)
		$GLOBALS["couleur_claire"] = $couleurs_spip[$choix]['couleur_claire'];
		$GLOBALS["couleur_foncee"] = $couleurs_spip[$choix]['couleur_foncee'];
		$GLOBALS["couleur_lien"] = $couleurs_spip[$choix]['couleur_lien'];
		$GLOBALS["couleur_lien_off"] = $couleurs_spip[$choix]['couleur_lien_off'];
		
	  return
	    "couleur_claire=" .
	    substr($couleurs_spip[$choix]['couleur_claire'],1).
	    '&couleur_foncee=' .
	    substr($couleurs_spip[$choix]['couleur_foncee'],1);
	} else {
		if (is_array($choix)) {
			if ($ajouter) {
				foreach($choix as $c)
					$couleurs_spip[] = $c;
				return $couleurs_spip;
			} else {
				return $couleurs_spip = $choix;
			}
		}

	}
	return $couleurs_spip;
}

?>
preselectionner_parent_nouvel_objet.php000066600000004032152525475610014621 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/


if (!defined('_ECRIRE_INC_VERSION')) return;
if (!defined('_AUTO_SELECTION_RUBRIQUE'))
	define('_AUTO_SELECTION_RUBRIQUE',false);


/**
 * Preselectionner la rubrique lors de la creation
 * desactive par defaut suite a remontee utilisateur mais activable par define
 * ou surchargeable
 *
 * @param string $objet
 * @param array $row
 * @return string
 */
function inc_preselectionner_parent_nouvel_objet_dist($objet, $row){
	if (!_AUTO_SELECTION_RUBRIQUE)
		return '';

	if (!isset($row['id_rubrique']))
		return '';

	$id_rubrique = '';
	if ($GLOBALS['connect_id_rubrique']){
		// si admin restreint : sa rubrique
		$id_rubrique = $GLOBALS['connect_id_rubrique'][0];
	}
	else {
		// sinon la derniere rubrique cree
		$row_rub = sql_fetsel("id_rubrique", "spip_rubriques", "", "", "id_rubrique DESC", "0,1");
		$id_rubrique = $row_rub['id_rubrique'];
	}
	// si le choix ne convient pas, on cherche dans un secteur
	if (!autoriser('creer'.$objet.'dans','rubrique',$id_rubrique)){
		$id_rubrique = '';
		// manque de chance, la rubrique n'est pas autorisee, on cherche un des secteurs autorises
		$res = sql_select("id_rubrique", "spip_rubriques", "id_parent=0");
		while (!$id_rubrique AND $row_rub = sql_fetch($res)){
			if (autoriser('creer'.$objet.'dans','rubrique',$row_rub['id_rubrique']))
				$id_rubrique = $row_rub['id_rubrique'];
		}
	}
	return $id_rubrique;

}aider.php000066600000017332152525475610006367 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

include_spip('inc/filtres');

// L'aide en ligne de SPIP est disponible sous forme d'articles de www.spip.net
// qui ont des reperes nommes arrtitre, artdesc etc.
// La fonction inc_aider(_dist) recoit soit ces reperes, 
// soit le nom du champ de saisie, le nom du squelette le contenant et enfin
// l'environnement d'execution du squelette (inutilise pour le moment).
// Le tableau ci-dessous donne le repere correspondant a ces informations.

$GLOBALS['aider_index'] = array(
	'editer_article.html' => array (
		'surtitre' => 'arttitre',
		'titre' => 'arttitre',
		'soustitre' => 'arttitre',
		'id_parent' => 'artrub',
		'descriptif' => 'artdesc',
		'virtuel' => 'artvirt',
		'chapo' => 'arttitre',
		'text_area' => 'arttexte'),

	'editer_breve.html' => array(
		'id_parent' => 'brevesrub',
		'lien_titre' => 'breveslien',
		'statut' => 'brevesstatut'),

	'editer_groupe_mot.html' => array(
		'titre' => 'motsgroupes'),

	'editer_mot.html' => array(
		'titre' => 'mots',
		'id_groupe' => 'motsgroupes'),

	'editer_rubrique.html' => array(
		'titre' => 'arttitre',
		'id_parent' => 'rubrub',
		'text_area' => 'raccourcis')

				);



/**
 * Generer un lien d'aide (icone + lien)
 *
 * @param string $aide
 * 		cle d'identification de l'aide souhaitee
 * @param strink $skel
 * 		Nom du squelette qui appelle ce bouton d'aide
 * @param array $env
 * 		Environnement du squelette
 * @param bool $aide_spip_directe
 * 		false : Le lien genere est relatif a notre site (par defaut)
 * 		true : Le lien est realise sur spip.net/aide/ directement ...
 * @return 
**/
function inc_aider_dist($aide='', $skel='', $env=array(), $aide_spip_directe = false) {
	global $spip_lang, $aider_index;

	if (($skel = basename($skel))
	AND isset($aider_index[$skel])
	AND isset($aider_index[$skel][$aide]))
		$aide = $aider_index[$skel][$aide];

	if ($aide_spip_directe) {
		// on suppose que spip.net est le premier present
		// dans la liste des serveurs. C'est forcement le cas
		// a l'installation tout du moins
		$help_server = $GLOBALS['help_server'];
		$url = array_shift($help_server) . '/';
		$url = parametre_url($url, 'exec', 'aide');
		$url = parametre_url($url, 'aide', $aide);
		$url = parametre_url($url, 'var_lang', $spip_lang);
	} else {
		$args = "aide=$aide&var_lang=$spip_lang";
		$url = generer_url_ecrire("aide", $args);
	}
	
	return aider_icone($url);
}

function aider_icone($url, $clic= '')
{
	global $spip_lang, $spip_lang_rtl;

	if (!$clic) {
		$t = _T('titre_image_aide');
		$clic = http_img_pack("aide".aide_lang_dir($spip_lang,$spip_lang_rtl)."-16.png",
			_T('info_image_aide'),
			" title=\"$t\" class='aide'");
	}
	return "\n&nbsp;&nbsp;<a class='aide popin'\nhref='"
	.  $url
	. "' target='_blank'>"
	. $clic
	. "</a>";
}

// en hebreu le ? ne doit pas etre inverse
// http://doc.spip.org/@aide_lang_dir
function aide_lang_dir($spip_lang,$spip_lang_rtl) {
	return ($spip_lang<>'he') ? $spip_lang_rtl : '';
}

// Les sections d'un fichier aide sont reperees ainsi:
define('_SECTIONS_AIDE', ',<h([12])(?:\s+class="spip")?'. '>([^/]+?)(?:/(.+?))?</h\1>,ism');

function aide_fichier($path, $help_server) {

	$md5 = md5(serialize($help_server));
	$fichier_aide = _DIR_AIDE . substr($md5,0,16) . "-" . $path;
	$lastm = @filemtime($fichier_aide);
	$lastversion = @filemtime(_DIR_RESTREINT . 'inc_version.php');
	$here = @(is_readable($fichier_aide) AND ($lastm >= $lastversion));
	$contenu = '';

	if ($here) {
		lire_fichier($fichier_aide, $contenu);
		return array($contenu, $lastm);
	}

	// mettre en cache (tant pis si echec)
	sous_repertoire(_DIR_AIDE,'','',true);
	$contenu = array();
	include_spip('inc/distant');
	foreach ($help_server as $k => $server) {
		// Remplacer les liens aux images par leur gestionnaire de cache
		$url = "$server/$path";
		$local = _DIR_AIDE.substr(md5($url),0,8)."-".preg_replace(",[^\w.]+,i","_",$url);
		$local = _DIR_RACINE . copie_locale($url, 'modif', $local);

		lire_fichier($local,$page);
		$page = aide_fixe_img($page,$server);
		// les liens internes ne doivent pas etre deguises en externes
		$url = parse_url($url);
		$re = '@(<a\b[^>]*\s+href=["\'])' .
		  '(?:' . $url['scheme'] . '://' . $url['host'] . ')?' .
		  $url['path'] . '([^"\']*)@ims';
		$page = preg_replace($re,'\\1\\2', $page);

		preg_match_all(_SECTIONS_AIDE, $page, $sections, PREG_SET_ORDER);
		// Fusionner les aides ayant meme nom de section
		$vus = array();
		foreach ($sections as $section) {
			list($tout,$prof, $sujet,) = $section;
			if (in_array($sujet, $vus)) continue;
			$corps = aide_section($sujet, $page, $prof);
			foreach ($contenu as $k => $s) {
			  if ($sujet == $k) {
			    // Section deja vue qu'il faut completer
			    // Si le complement a des sous-sections,
			    // ne pas en tenir compte quand on les rencontrera
			    // lors des prochains passages dans la boucle
			    preg_match_all(_SECTIONS_AIDE, $corps, $s, PREG_PATTERN_ORDER);
			    if ($s) {$vus = array_merge($vus, $s[2]);}
			    $contenu[$k] .= $corps;
			    $corps = '';
			    break;
			  }
			}
			// Si totalement nouveau, inserer le titre
			// mais pas le corps s'il contient des sous-sections:
			// elles vont venir dans les passages suivants
			if ($corps) {
			  $corps = aide_section($sujet, $page);
			  $contenu[$sujet] = $tout . "\n" . $corps;
			}
		}
	}

	$contenu = '<div>' . join('',$contenu) . '</div>';

	// Renvoyer les liens vraiment externes dans une autre fenetre
	$contenu = preg_replace('@<a href="(http://[^"]+)"([^>]*)>@',
				'<a href="\\1"\\2 target="_blank">',
				$contenu);

	// Correction typo dans la langue demandee
	#changer_typo($lang_aide);
	$contenu = '<body>' . $contenu . '</body>';

	if (strlen($contenu) <= 100) return array(false, false);
	ecrire_fichier ($fichier_aide, $contenu);
	return array($contenu, time());
}

function generer_url_aide_img($args){
	return generer_url_action('aide_img', $args, false, true);
}


// Les aides non mises a jour ont un vieux Path a remplacer
// (mais ce serait bien de le faire en SQL une bonne fois)
define('_REPLACE_IMG_PACK', "@(<img([^<>]* +)?\s*src=['\"])img_pack\/@ims");

// Remplacer les URL des images par l'URL du gestionnaire de cache local
function aide_fixe_img($contenu, $server){
	$html = "";
	$re = "@(<img([^<>]* +)?\s*src=['\"])((AIDE|IMG|local)/([-_a-zA-Z0-9]*/?)([^'\"<>]*))@imsS";
	while (preg_match($re, $contenu, $r)) {
		$p = strpos($contenu, $r[0]);
		$i = $server . '/' . $r[3];
		$html .= substr($contenu, 0, $p) .  $r[1] . $i;
		$contenu = substr($contenu, $p + strlen($r[0]));
	}
	$html .= $contenu;

	// traiter les vieilles doc
	return  preg_replace(_REPLACE_IMG_PACK,"\\1"._DIR_IMG_PACK, $html);
}


// Extraire la seule section demandee,
// qui commence par son nom entouree d'une balise h2
// et se termine par la prochaine balise h2 ou h1 ou le /body final.

function aide_section($aide, $contenu, $prof=2){
	$maxprof = ($prof >=2) ? "12" : "1";
	$r = "@<h$prof" . '(?: class="spip")?' . '>\s*' . $aide
	  ."\s*(?:/.+?)?</h$prof>(.*?)<(?:(?:h[$maxprof])|/body)@ism";

	if (preg_match($r, $contenu, $m))
	  return $m[1];
#	spip_log("aide inconnue $r dans " . substr($contenu, 0, 150));
	return '';
}


?>
acces.php000066600000016720152525475610006361 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

// http://doc.spip.org/@creer_pass_aleatoire
function creer_pass_aleatoire($longueur = 8, $sel = "") {
	$seed = (double) (microtime() + 1) * time();
	mt_srand($seed);
	srand($seed);
	$s = '';
	$pass = '';
	for ($i = 0; $i < $longueur; $i++) {
		if (!$s) {
			$s = mt_rand();
			if (!$s) $s = rand();
			$s = substr(md5(uniqid($s).$sel), 0, 16);
		}
		$r = unpack("Cr", pack("H2", $s.$s));
		$x = $r['r'] & 63;
		if ($x < 10) $x = chr($x + 48);
		else if ($x < 36) $x = chr($x + 55);
		else if ($x < 62) $x = chr($x + 61);
		else if ($x == 63) $x = '/';
		else $x = '.';
		$pass .= $x;
		$s = substr($s, 2);
	}
	$pass = preg_replace("@[./]@", "a", $pass);
	$pass = preg_replace("@[I1l]@", "L", $pass);
	$pass = preg_replace("@[0O]@", "o", $pass);
	return $pass;
}

/**
 * Creer un identifiant aleatoire
 *
 * http://doc.spip.org/@creer_uniqid
 *
 * @return string
 */
function creer_uniqid() {
	static $seeded;

	if (!$seeded) {
		$seed = (double) (microtime() + 1) * time();
		mt_srand($seed);
		srand($seed);
		$seeded = true;
	}

	$s = mt_rand();
	if (!$s) $s = rand();
	return uniqid($s, 1);
}

//
// Renouvellement de l'alea utilise pour securiser les scripts dans action/
//

// http://doc.spip.org/@renouvelle_alea
function renouvelle_alea() {
	if (!isset($GLOBALS['meta']['alea_ephemere'])){
		include_spip('base/abstract_sql');
		$GLOBALS['meta']['alea_ephemere'] = sql_getfetsel('valeur', 'spip_meta', "nom='alea_ephemere'");
	}
	ecrire_meta('alea_ephemere_ancien', @$GLOBALS['meta']['alea_ephemere'], 'non');
	$GLOBALS['meta']['alea_ephemere'] = md5(creer_uniqid());
	ecrire_meta('alea_ephemere', $GLOBALS['meta']['alea_ephemere'], 'non');
	ecrire_meta('alea_ephemere_date', time(), 'non');
	spip_log("renouvellement de l'alea_ephemere");
}

//
// low-security : un ensemble de fonctions pour gerer de l'identification
// faible via les URLs (suivi RSS, iCal...)
//
// http://doc.spip.org/@low_sec
function low_sec($id_auteur) {
	// Pas d'id_auteur : low_sec
	if (!$id_auteur = intval($id_auteur)) {
		if (!$low_sec = $GLOBALS['meta']['low_sec']) {
			ecrire_meta('low_sec', $low_sec = creer_pass_aleatoire());
		}
	}
	else {
		$low_sec = sql_getfetsel("low_sec", "spip_auteurs", "id_auteur = $id_auteur");
		if (!$low_sec) {
			$low_sec = creer_pass_aleatoire();
			sql_updateq("spip_auteurs", array("low_sec" => $low_sec), "id_auteur = $id_auteur");
		}
	}
	return $low_sec;
}

// Inclure les arguments significatifs pour le hachage
// cas particulier du statut pour compatibilite ancien rss/suivi_revisions

function param_low_sec($op, $args=array(), $lang='', $mime='rss')
{
	$a = $b = '';
	foreach ($args as $val => $var)
		if ($var) {
			if ($val<>'statut') $a .= ':' . $val.'-'.$var;
			$b .= $val.'='.$var . '&';
		}
	$a = substr($a,1);
	$id = intval(@$GLOBALS['connect_id_auteur']);
	return $b
	  . "op="
	  . $op
	  . "&id="
	  . $id
	  . "&cle="
	  . afficher_low_sec($id, "$mime $op $a")
	  . (!$a ? '' : "&args=$a")
	  . (!$lang ? '' : "&lang=$lang");
}

// http://doc.spip.org/@afficher_low_sec
function afficher_low_sec ($id_auteur, $action='') {
	return substr(md5($action.low_sec($id_auteur)),0,8);
}

// http://doc.spip.org/@verifier_low_sec
function verifier_low_sec ($id_auteur, $cle, $action='') {
	return ($cle == afficher_low_sec($id_auteur, $action));
}

// http://doc.spip.org/@effacer_low_sec
function effacer_low_sec($id_auteur) {
	if (!$id_auteur = intval($id_auteur)) return; // jamais trop prudent ;)
	sql_updateq("spip_auteurs", array("low_sec" => ''), "id_auteur = $id_auteur");
}

// http://doc.spip.org/@initialiser_sel
function initialiser_sel() {
	global $htsalt;
	if (CRYPT_MD5) $htsalt = '$1$'.creer_pass_aleatoire();
	else return "";
}

// Cette fonction ne sert qu'a la connexion en mode http_auth.non LDAP
// Son role est de creer le fichier htpasswd
// Voir le plugin "acces restreint"
// http://doc.spip.org/@ecrire_acces
function ecrire_acces() {
	$htaccess = _DIR_RESTREINT . _ACCESS_FILE_NAME;
	$htpasswd = _DIR_TMP . _AUTH_USER_FILE;

	// Cette variable de configuration peut etre posee par un plugin
	// par exemple acces_restreint ;
	// si .htaccess existe, outrepasser spip_meta
	if (($GLOBALS['meta']['creer_htpasswd'] != 'oui')
	AND !@file_exists($htaccess)) {
		spip_unlink($htpasswd);
		spip_unlink($htpasswd."-admin");
		return;
	}

	# remarque : ici on laisse passer les "nouveau" de maniere a leur permettre
	# de devenir redacteur le cas echeant (auth http)... a nettoyer
	// attention, il faut au prealable se connecter a la base (necessaire car utilise par install)
	// TODO: factoriser avec auth/spip qui fait deja ce job et generaliser le test spip_connect_ldap()

	if (spip_connect_ldap()) return;
	$p1 = ''; // login:htpass pour tous
	$p2 = ''; // login:htpass pour les admins
	$s = sql_select("login, htpass, statut", "spip_auteurs", sql_in("statut",  array('1comite','0minirezo','nouveau')));
	while ($t = sql_fetch($s)) {
		if (strlen($t['login']) AND strlen($t['htpass'])) {
			$p1 .= $t['login'].':'.$t['htpass']."\n";
			if ($t['statut'] == '0minirezo')
				$p2 .= $t['login'].':'.$t['htpass']."\n";
		}
	}
	if ($p1) {
	  ecrire_fichier($htpasswd, $p1);
	  ecrire_fichier($htpasswd.'-admin', $p2);
	  spip_log("Ecriture de $htpasswd et $htpasswd-admin");
	}
}


// http://doc.spip.org/@generer_htpass
function generer_htpass($pass) {
	global $htsalt;
	if (function_exists('crypt'))
		return crypt($pass, $htsalt);
}

//
// Installe ou verifie un .htaccess, y compris sa prise en compte par Apache
//
// http://doc.spip.org/@verifier_htaccess
function verifier_htaccess($rep, $force=false) {
	$htaccess = rtrim($rep,"/") . "/" . _ACCESS_FILE_NAME;
	if (((@file_exists($htaccess)) OR defined('_TEST_DIRS')) AND !$force)
		return true;
	if ($ht = @fopen($htaccess, "w")) {
		fputs($ht, "deny from all\n");
		fclose($ht);
		@chmod($htaccess, _SPIP_CHMOD & 0666);
		$t = rtrim($rep,"/") . "/.ok";
		if ($ht = @fopen($t, "w")) {
			@fclose($ht);
			include_spip('inc/distant');
			$t = substr($t,strlen(_DIR_RACINE));
			$t = url_de_base() . $t;
			$ht = recuperer_lapage($t, false, 'HEAD', 0);
			// htaccess inoperant si on a recupere des entetes HTTP
			// (ignorer la reussite si connexion par fopen)
			$ht = !(isset($ht[0]) AND $ht[0]);
		}
	}
	spip_log("Creation de $htaccess " . ($ht ? " reussie" : " manquee"));
	return $ht;
}	



// http://doc.spip.org/@gerer_htaccess
function gerer_htaccess() {
	// Cette variable de configuration peut etre posee par un plugin
	// par exemple acces_restreint
	$f = ($GLOBALS['meta']['creer_htaccess'] === 'oui');
	$dirs = sql_allfetsel('extension', 'spip_types_documents');
	$dirs[] = array('extension' => 'distant');
	foreach($dirs as $e) {
		if (is_dir($dir = _DIR_IMG . $e['extension'])) {
			if ($f)
				verifier_htaccess($dir);
			else spip_unlink($dir . '/' . _ACCESS_FILE_NAME);
		}
	}
	return $GLOBALS['meta']['creer_htaccess'];
}

initialiser_sel();

?>
invalideur.php000066600000020372152525475610007443 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/


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

include_spip('base/serial');

# estime la taille moyenne d'un fichier cache, pour ne pas les regarder (10ko)
define('_TAILLE_MOYENNE_FICHIER_CACHE', 1024 * 10);
# si un fichier n'a pas servi (fileatime) depuis plus d'une heure, on se sent
# en droit de l'eliminer
define('_AGE_CACHE_ATIME', 3600);

// Donne le nombre de fichiers dans un repertoire (plat, pour aller vite)
// false si erreur
// http://doc.spip.org/@nombre_de_fichiers_repertoire
function nombre_de_fichiers_repertoire($dir,$nb_estim_taille = 20) {
	$taille = 0; // mesurer la taille de N fichiers au hasard dans le repertoire
	$nb = $nb_estim_taille;
	if (!$h = @opendir($dir)) return false;
	$total = 0;
	while (($fichier = @readdir($h)) !== false)
		if ($fichier[0]!='.' AND !is_dir("$dir/$fichier")){
			$total++;
			if ($nb AND rand(1,10)==1){
				$taille += filesize("$dir/$fichier");
				$nb--;
			}
		}
	closedir($h);
	return array($total,$taille?$taille/($nb_estim_taille-$nb):_TAILLE_MOYENNE_FICHIER_CACHE);
}

// Indique la taille du repertoire cache ; pour de gros volumes,
// impossible d'ouvrir chaque fichier, on y va donc a l'estime
// http://doc.spip.org/@taille_du_cache
function taille_du_cache() {
	$total = 0;
	$taille = 0;
	for ($i=0;$i<16;$i++) {
		$l = dechex($i);
		$dir = sous_repertoire(_DIR_CACHE, $l);
		list($n,$s) = nombre_de_fichiers_repertoire($dir);
		$total += $n;
		$taille += $s;
	}
	return $total * $taille / 16;
}

// Invalider les caches lies a telle condition
// les invalideurs sont de la forme 'objet/id_objet'
// la condition est generalement "id='objet/id_objet'"
// ici on se contente de noter la date de mise a jour dans les metas
// http://doc.spip.org/@suivre_invalideur
function suivre_invalideur($cond, $modif=true) {
	if (!$modif)
		return;

	// determiner l'objet modifie : forum, article, etc
	if (preg_match(',["\']([a-z_]+)[/"\'],', $cond, $r))
		$objet = objet_type($r[1]);

	// stocker la date_modif_$objet (ne sert a rien pour le moment)
	if (isset($objet))
		ecrire_meta('derniere_modif_'.$objet, time());

	// si $derniere_modif_invalide est un array('article', 'rubrique')
	// n'affecter la meta que si un de ces objets est modifie
	if (is_array($GLOBALS['derniere_modif_invalide'])) {
		if (in_array($objet, $GLOBALS['derniere_modif_invalide']))
			ecrire_meta('derniere_modif', time());
	}
	// sinon, cas standard, toujours affecter la meta
	else
		ecrire_meta('derniere_modif', time());

}



/**
 * Purge un répertoire de ses fichiers
 *
 * Utilisée entre autres pour vider le cache depuis l'espace privé
 *
 * @uses supprimer_fichier()
 * 
 * @param string $dir
 *     Chemin du répertoire à purger
 * @param array $options
 *     Tableau des options. Peut être :
 *
 *     - atime : timestamp pour ne supprimer que les fichiers antérieurs
 *       à cette date (via fileatime)
 *     - mtime : timestamp pour ne supprimer que les fichiers antérieurs
 *       à cette date (via filemtime)
 *     - limit : nombre maximum de suppressions
 * @return int
 *     Nombre de fichiers supprimés
**/
function purger_repertoire($dir, $options=array()) {
	$handle = @opendir($dir);
	if (!$handle) return;

	$total = 0;

	while (($fichier = @readdir($handle)) !== false) {
		// Eviter ".", "..", ".htaccess", ".svn" etc.
		if ($fichier[0] == '.') continue;
		$chemin = "$dir/$fichier";
		if (is_file($chemin)) {
			if (  (!isset($options['atime']) OR (@fileatime($chemin) < $options['atime']))
				AND (!isset($options['mtime']) OR (@filemtime($chemin) < $options['mtime']))
			  ) {
				supprimer_fichier($chemin);
				$total ++;
			}
		}
		else if (is_dir($chemin)){
			$opts = $options;
			if (isset($otpions['limit']))
				$otps['limit'] = $otpions['limit'] - $total;
			$total += purger_repertoire($chemin, $opts);
			if (isset($options['subdir']) && $options['subdir'])
				spip_unlink($chemin);
		}

		if (isset($options['limit']) AND $total>=$options['limit'])
			break;
	}
	closedir($handle);

	return $total;
}


//
// Methode : on prend un des sous-repertoires de CACHE/
// on considere qu'il fait 1/16e de la taille du cache
// et on le ratiboise en supprimant les fichiers qui n'ont pas
// ete sollicites dans l'heure qui vient de s'ecouler
//
// http://doc.spip.org/@appliquer_quota_cache
function appliquer_quota_cache() {
	global $quota_cache;
	$encore = false;

	$tour_quota_cache = intval(1+$GLOBALS['meta']['tour_quota_cache'])%16;
	ecrire_meta('tour_quota_cache', $tour_quota_cache);

	$l = dechex($tour_quota_cache);
	$dir = sous_repertoire(_DIR_CACHE, $l);
	list($nombre,$taille) = nombre_de_fichiers_repertoire($dir);
	$total_cache = $taille * $nombre;
	spip_log("Taille du CACHE estimee ($l): "
		.(intval(16*$total_cache/(1024*1024/10))/10)." Mo","invalideur");

	// Nombre max de fichiers a supprimer
	if ($quota_cache > 0
	AND $taille > 0) {
		$trop = $total_cache - ($quota_cache/16)*1024*1024;
		$trop = 3 * intval($trop / $taille);
		if ($trop > 0) {
			$n = purger_repertoire($dir,
				array(
					'atime' => time() - _AGE_CACHE_ATIME,
					'limit' => $trop,
					'subdir' => true // supprimer les vieux sous repertoire de session (avant [15851])
				)
			);
			spip_log("$dir : $n/$trop caches supprimes [taille moyenne $taille]","invalideur");
			$total_cache = intval(max(0,(16*$total_cache) - $n*$taille)/(1024*1024)*10)/10;
			spip_log("cache restant estime : $total_cache Mo, ratio ".$total_cache/$quota_cache,"invalideur");

			// redemander la main pour eviter que le cache ne gonfle trop
			// mais pas si on ne peut pas purger car les fichiers sont trops recents
			if (
			  $total_cache/$quota_cache>1.5
			  AND $n*50>$trop) {
				$encore = true;
				spip_log("Il faut encore purger","invalideur");
			}
		}
	}
	return $encore;
}


//
// Destruction des fichiers caches invalides
//

// Securite : est sur que c'est un cache
// http://doc.spip.org/@retire_cache
function retire_cache($cache) {

	if (preg_match(
	"|^([0-9a-f]/)?([0-9]+/)?[^.][\-_\%0-9a-z]+--[0-9a-f]+(\.gz)?$|i",
	$cache)) {
		// supprimer le fichier (de facon propre)
		supprimer_fichier(_DIR_CACHE . $cache);
	} else
		spip_log("Nom de fichier cache incorrect : $cache");
}

#######################################################################
##
## Ci-dessous les fonctions qui restent appellees dans le core
## pour pouvoir brancher le plugin invalideur ;
## mais ici elles ne font plus rien
##

// Supprimer les caches marques "x"
// A priori dans cette version la fonction ne sera pas appelee, car
// la meta est toujours false ; mais evitons un bug si elle est appellee
// http://doc.spip.org/@retire_caches
function retire_caches($chemin = '') {
	if (isset($GLOBALS['meta']['invalider_caches']))
		effacer_meta('invalider_caches'); # concurrence
}


// Fonction permettant au compilo de calculer les invalideurs d'une page
// (note: si absente, n'est pas appellee)
/*
// http://doc.spip.org/@calcul_invalideurs
function calcul_invalideurs($corps, $primary, &$boucles, $id_boucle) {
	return $corps;
}
*/

// Cette fonction permet de supprimer tous les invalideurs
// Elle ne touche pas aux fichiers cache eux memes ; elle est
// invoquee quand on vide tout le cache en bloc (action/purger)
//
// http://doc.spip.org/@supprime_invalideurs
function supprime_invalideurs() { }


// Calcul des pages : noter dans la base les liens d'invalidation
// http://doc.spip.org/@maj_invalideurs
function maj_invalideurs ($fichier, &$page) { }

// les invalideurs sont de la forme "objet/id_objet"
// http://doc.spip.org/@insere_invalideur
function insere_invalideur($inval, $fichier) { }


//
// Marquer les fichiers caches invalides comme etant a supprimer
//
// http://doc.spip.org/@applique_invalideur
function applique_invalideur($depart) { }

?>
puce_statut.php000066600000024136152525475610007643 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

if (!defined('_ECRIRE_INC_VERSION')) return;
if (!defined('_ACTIVER_PUCE_RAPIDE'))
	define('_ACTIVER_PUCE_RAPIDE', true);

/**
 * Afficher la puce statut d'un objet
 *
 *
 * http://doc.spip.org/@inc_puce_statut_dist
 *
 * @param int $id_objet
 * @param string $statut
 * @param int $id_parent
 * @param string $type
 * @param bool $ajax
 *   indique qu'il ne faut renvoyer que le coeur du menu car on est dans la requete ajax post changement rapide
 * @param bool $menu_rapide
 * @return string
 */
function inc_puce_statut_dist($id_objet, $statut, $id_parent, $type, $ajax=false, $menu_rapide=_ACTIVER_PUCE_RAPIDE) {
	static $f_puce_statut = array();
	$type = objet_type($type);
	// cas prioritaire : fonction perso, qui permet aussi de gerer les cas historiques
	if (!isset($f_puce_statut[$type]) OR is_null($f_puce_statut[$type]))
		$f_puce_statut[$type] = charger_fonction($type,'puce_statut',true);
	if ($f_puce_statut[$type])
		return $f_puce_statut[$type]($id_objet, $statut, $id_parent, $type, $ajax, $menu_rapide);

	// si statut_image trouve quelque chose (et '' est quelque chose)
	// composer une puce, avec si possible changement rapide
	elseif(!is_null($puce=puce_statut_changement_rapide($id_objet,$statut,$id_parent,$type,$ajax,$menu_rapide))){
		return $puce;
	}

	// sinon fausse puce avec le type de l'image
	else
		return http_img_pack("$type-16.png",'');
}

/**
 * Recuperer l'image correspondant au statut, telle que declaree dans
 * declarer_tables_objets_sql
 * sous la forme
 * array('imagepardefaut.png','statut1'=>'imagestatut1.png','statut2'=>imagestatut2.png' ...)
 * mettre une chaine vide pour ne pas avoir d'image pour un statut particulier
 *
 * si rien de declare et que le statut est dans les cas connus (prepa, prop, publie, refuse, poubelle)
 * renvoyer l'image par defaut
 *
 * @param string $objet
 * @param string $statut
 * @return string
 *   null si pas capable de determiner l'image
 */
function statut_image($objet,$statut){
	$src = null;
	$table = table_objet_sql($objet);
	$desc = lister_tables_objets_sql($table);
	if (isset($desc['statut_images'])){
		// si une declaration statut_images
		// mais rien pour le statut demande, ne rien afficher
		$src = '';
		if (isset($desc['statut_images'][$statut]))
			$src = $desc['statut_images'][$statut];
		// sinon image par defaut ?
		elseif (isset($desc['statut_images'][0]))
			$src = $desc['statut_images'][0];
	}
	else {
		switch ($statut) {
			case 'prepa':
				$src = 'puce-preparer-8.png';
				break;
			case 'prop':
				$src = 'puce-proposer-8.png';
				break;
			case 'publie':
				$src = 'puce-publier-8.png';
				break;
			case 'refuse':
				$src = 'puce-refuser-8.png';
				break;
			case 'poubelle':
			case 'poub':
				$src = 'puce-supprimer-8.png';
				break;
		}
	}
	return $src;
}

/**
 * Recuperer le titre correspondant au statut, tel que declaree dans
 * declarer_tables_objets_sql
 * sous la forme
 * array('titre par defaut','statut1'=>'titre statut 1','statut2'=>'titre statut 2' ...)
 * mettre une chaine vide pour ne pas avoir de titre pour un statut particulier
 *
 * si rien de declare et que le statut est dans les cas connus (prepa, prop, publie, refuse, poubelle)
 * renvoyer le texte par defaut
 *
 * @param string $objet
 * @param string $statut
 * @return string
 */
function statut_titre($objet,$statut){
	$titre = '';
	$table = table_objet_sql($objet);
	$desc = lister_tables_objets_sql($table);
	if (isset($desc['statut_titres'])){
		// si une declaration statut_titres
		// mais rien pour le statut demande, ne rien afficher
		if (isset($desc['statut_titres'][$statut]))
			$titre = $desc['statut_titres'][$statut];
		// sinon image par defaut ?
		elseif (isset($desc['statut_titres'][0]))
			$titre = $desc['statut_titres'][0];
	}
	else {
		switch ($statut) {
			case 'prepa':
				$titre = 'texte_statut_en_cours_redaction';
				break;
			case 'prop':
				$titre = 'texte_statut_propose_evaluation';
				break;
			case 'publie':
				$titre = 'texte_statut_publie';
				break;
			case 'refuse':
				$titre = 'texte_statut_refuse';
				break;
			case 'poubelle':
			case 'poub':
				$titre = 'texte_statut_poubelle';
				break;
		}
	}
	return $titre?_T($titre):'';
}


/**
 * Recuperer le texte correspondant au choix de statut, tel que declare dans
 * declarer_tables_objets_sql
 * sous la forme
 * array('statut1'=>'texte statut 1','statut2'=>'texte statut 2' ...)
 * mettre une chaine vide pour ne pas proposer un statut
 * les statuts seront proposes dans le meme ordre que dans la declaration
 *
 * si rien de declare et que le statut est dans les cas connus (prepa, prop, publie, refuse, poubelle)
 * renvoyer le texte par defaut
 *
 * @param string $objet
 * @param string $statut
 * @return string
 */
function statut_texte_instituer($objet,$statut){
	$texte = '';
	$table = table_objet_sql($objet);
	$desc = lister_tables_objets_sql($table);
	if (isset($desc['statut_textes_instituer'])){
		// si une declaration statut_titres
		// mais rien pour le statut demande, ne rien afficher
		if (isset($desc['statut_textes_instituer'][$statut]))
			$texte = $desc['statut_textes_instituer'][$statut];
	}
	else {
		switch ($statut) {
			case 'prepa':
				$texte = 'texte_statut_en_cours_redaction';
				break;
			case 'prop':
				$texte = 'texte_statut_propose_evaluation';
				break;
			case 'publie':
				$texte = 'texte_statut_publie';
				break;
			case 'refuse':
				$texte = 'texte_statut_refuse';
				break;
			case 'poubelle':
			case 'poub':
				$texte = 'texte_statut_poubelle';
				break;
		}
	}
	return $texte?_T($texte):'';
}


/**
 * Afficher la puce statut d'un auteur
 *
 * Ne semble plus servir : desactive
 * Hack de compatibilite: les appels directs ont un  $type != 'auteur'
 * si l'auteur ne peut pas se connecter
 *
 * http://doc.spip.org/@puce_statut_auteur_dist
 *
 * @param int $id
 * @param string $statut
 * @param int $id_parent
 * @param string $type
 * @param string $ajax
 * @param bool $menu_rapide
 * @return string
 */
function puce_statut_auteur_dist($id, $statut, $id_parent, $type, $ajax='', $menu_rapide=_ACTIVER_PUCE_RAPIDE) {
	$img = statut_image('auteur',$statut);
	if (!$img) return '';
	$alt = statut_titre('auteur',$statut);

	$fond = ''; $titre = '';
	/*
	if ($type != 'auteur') {
	  $img2 = chemin_image('del-16.png');
	  $titre = _T('titre_image_redacteur');
	  $fond = http_style_background($img2, 'top left no-repeat;');
	}
	else {
	}
	*/
	return http_img_pack($img, $alt, $fond, $alt);
}


// http://doc.spip.org/@puce_statut_rubrique_dist
function puce_statut_rubrique_dist($id, $statut, $id_rubrique, $type, $ajax='',$menu_rapide=_ACTIVER_PUCE_RAPIDE) {
	return http_img_pack('rubrique-16.png', '');
}

// http://doc.spip.org/@puce_statut_article_dist
function puce_statut_changement_rapide($id, $statut, $id_rubrique, $type='article', $ajax = false, $menu_rapide=_ACTIVER_PUCE_RAPIDE) {
	$src = statut_image($type, $statut);
	if (!$src)
		return $src;

	if (!$id
	  OR !_SPIP_AJAX
	  OR !$menu_rapide) {
	  $ajax_node ='';
	}
	else
		$ajax_node = " class='imgstatut$type$id'";


	$t = statut_titre($type, $statut);
	$inser_puce = http_img_pack($src,$t,$ajax_node,$t);

	if (!$ajax_node)
		return $inser_puce;

	$table = table_objet_sql($type);
	$desc = lister_tables_objets_sql($table);
	if (!isset($desc['statut_textes_instituer']))
		return $inser_puce;
	
	include_spip('inc/autoriser');
	// cas ou l'on a un parent connu (devrait disparaitre au profit du second cas plus generique)
	if ($id_rubrique){
		if (!autoriser('publierdans', 'rubrique', $id_rubrique))
			return $inser_puce;
	}
	// si pas d'id_rubrique fourni, tester directement instituer type avec le statut publie
	else {
		if (!autoriser('instituer', $type, $id, null, array('statut'=>'publie')))
			return $inser_puce;
	}

	$coord = array_flip(array_keys($desc['statut_textes_instituer']));
	if (!isset($coord[$statut]))
		return $inser_puce;

	$unit = 8/*widh de img*/+4/*padding*/;
	$margin = 4; /* marge a gauche + droite */
	$zero = 1 /*border*/ + $margin/2 + 2 /*padding*/;
	$clip = $zero+ ($unit*$coord[$statut]);

	if ($ajax){
		$width = $unit*count($desc['statut_textes_instituer'])+$margin;
		$out = "<span class='puce_objet_fixe $type'>"
		. $inser_puce
		. "</span>"
		. "<span class='puce_objet_popup $type statutdecal$type$id' style='width:{$width}px;margin-left:-{$clip}px;'>";
		$i=0;
		foreach($desc['statut_textes_instituer'] as $s=>$t){
			$out .= afficher_script_statut($id, $type, -$zero-$i++*$unit, statut_image($type,$s), $s, _T($t));
		}
		$out .= "</span>";
		return $out;
	}
	else {

		$nom = "puce_statut_";
	  $action = generer_url_ecrire('puce_statut',"",true);
	  $action = "if (!this.puce_loaded) { this.puce_loaded = true; prepare_selec_statut(this, '$nom', '$type', '$id', '$action'); }";
	  $over = " onmouseover=\"$action\"";

		$lang_dir = lang_dir(lang_typo());
		return 	"<span class='puce_objet $type' id='$nom$type$id' dir='$lang_dir'$over>"
		. $inser_puce
		. '</span>';
	}
}

// http://doc.spip.org/@afficher_script_statut
function afficher_script_statut($id, $type, $n, $img, $statut, $titre, $act='') {
	$h = generer_action_auteur("instituer_objet","$type-$id-$statut");
	$h = "selec_statut('$id', '$type', $n, jQuery('img',this).attr('src'), '$h');return false;";
	$t = supprimer_tags($titre);
	return "<a href=\"#\" onclick=\"$h\" title=\"$t\"$act>".http_img_pack($img,$t)."</a>";
}

// compat
// La couleur du statut
// http://doc.spip.org/@puce_statut

function puce_statut($statut, $atts='') {
	$src = statut_image('article',$statut);
	if (!$src) return '';
	return http_img_pack($src, statut_titre('article',$statut), $atts);
}

?>
session.php000066600000034005152525475610006762 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

/*
 * Gestion de l'authentification par sessions
 * a utiliser pour valider l'acces (bloquant)
 * ou pour reconnaitre un utilisateur (non bloquant)
 *
 */

$GLOBALS['visiteur_session'] = array(); # globale decrivant l'auteur

/**
 * 3 actions sur les sessions, selon le type de l'argument:
 * - numerique: efface toutes les sessions de l'auteur (retour quelconque)
 * - tableau: cree une session pour l'auteur decrit et retourne l'identifiant
 * - bool: predicat de validite de la session indiquee par le cookie
 *
 * http://doc.spip.org/@inc_session_dist
 *
 * @param int|array|bool $auteur
 * @return bool|null|void
 */
function inc_session_dist($auteur=false)
{
	if (is_numeric($auteur))
		return supprimer_sessions($auteur, $auteur > 0);
	else if (is_array($auteur))
		return ajouter_session($auteur);
	else
		return verifier_session($auteur);
}


/**
 * Supprimer toutes les vieilles sessions d'un auteur
 *
 * Cette fonction efface toutes les sessions appartenant a l'auteur
 * On en profite pour effacer toutes les sessions
 * creees il y a plus de 4*_RENOUVELLE_ALEA
 * Tenir compte de l'ancien format ou les noms commencaient par "session_"
 * et du meme coup des repertoires plats
 * Attention : id_auteur peut etre negatif (cas des auteurs temporaires pendant le dump)
 *
 * http://doc.spip.org/@supprimer_sessions
 *
 * @param int $id_auteur
 * 		Identifiant d'auteur dont on veut supprimer les sessions
 * @param bool $toutes
 * 		Supprimer aussi les vieilles sessions des autres auteurs ?
 * @param bool $actives
 * 		false pour ne pas supprimer les sessions valides de $id_auteur.
 * 		false revient donc a uniquement supprimer les vieilles sessions !
 */
function supprimer_sessions($id_auteur, $toutes=true, $actives=true) {

	$nb_files = 0;
	$nb_max_files = (defined('_MAX_NB_SESSIONS_OUVERTES')?_MAX_NB_SESSIONS_OUVERTES:1000);
	spip_log("supprimer sessions auteur $id_auteur");
	if ($toutes OR $id_auteur!==$GLOBALS['visiteur_session']['id_auteur']) {
		if ($dir = opendir(_DIR_SESSIONS)){
			$t = $_SERVER['REQUEST_TIME']  - (4*_RENOUVELLE_ALEA); // 48h par defaut
			$t_short = $_SERVER['REQUEST_TIME']  - max(_RENOUVELLE_ALEA/4,3*3600); // 3h par defaut
			while(($f = readdir($dir)) !== false) {
				$nb_files++;
				if (preg_match(",^[^\d-]*(-?\d+)_\w{32}\.php[3]?$,", $f, $regs)){
					$f = _DIR_SESSIONS . $f;
					if (($actives AND $regs[1] == $id_auteur) OR ($t > filemtime($f))){
						spip_unlink($f);
					}
					// si il y a trop de sessions ouvertes, on purge les sessions anonymes de plus de 3H
					// cd http://core.spip.org/issues/3276
					elseif($nb_files>$nb_max_files AND !intval($regs[1]) AND ($t_short > filemtime($f))){
						spip_unlink($f);
					}
				}
			}
		}
	}
	else {
		verifier_session();
		spip_unlink(fichier_session('alea_ephemere', true));
	}

	// forcer le recalcul de la session courante
	spip_session(true);
}

/**
 * Ajoute une session pour l'auteur decrit par un tableau issu d'un SELECT-SQL
 *
 * http://doc.spip.org/@ajouter_session
 *
 * @param array $auteur
 * @return bool|string
 */
function ajouter_session($auteur) {
	// Si le client a deja une session valide pour son id_auteur
	// on conserve le meme fichier

	// Attention un visiteur peut avoir une session et un id=0,
	// => ne pas melanger les sessions des differents visiteurs
	$id_auteur = intval($auteur['id_auteur']);

	// Si ce n'est pas un inscrit (les inscrits ont toujours des choses en session)
	// on va vérifier s'il y a vraiment des choses à écrire
	if (!$id_auteur){
		// On supprime les données de base pour voir le contenu réel de la session
		$auteur_verif = $auteur;
		if (isset($auteur_verif['id_auteur'])) unset($auteur_verif['id_auteur']);
		if (isset($auteur_verif['hash_env'])) unset($auteur_verif['hash_env']);
		if (isset($auteur_verif['ip_change'])) unset($auteur_verif['ip_change']);
		if (isset($auteur_verif['date_session'])) unset($auteur_verif['date_session']);
		
		// Les variables vraiment nulle ne sont pas à prendre en compte non plus
		foreach($auteur_verif as $variable=>$valeur){
			if ($valeur === null){
				unset($auteur_verif[$variable]);
			}
		}
		// Si après ça la session est vide et qu'on a pas de cookie session, on arrete
		if (!$auteur_verif AND !isset($_COOKIE['spip_session'])){
			return false;
		}
	}

	if (!isset($_COOKIE['spip_session'])
	  OR !preg_match(',^'.$id_auteur.'_,', $_COOKIE['spip_session'])){
		$_COOKIE['spip_session'] = $id_auteur.'_'.md5(uniqid(rand(),true));
	}

	$fichier_session = fichier_session('alea_ephemere');

	// Si la session est vide alors on supprime l'éventuel fichier et on arrête là
	if (!$id_auteur AND !$auteur_verif){
		if (@file_exists($fichier_session)) spip_unlink($fichier_session);
		// unset le COOKIE de session
		// car il est pris en compte dans spip_session() qui va croire a tort qu'on est pas un visiteur anonyme
		unset($_COOKIE['spip_session']);
		return false;
	}

	// Maintenant on sait qu'on a des choses à écrire
	// On s'assure d'avoir au moins ces valeurs
	$auteur['id_auteur'] = $id_auteur;
	if (!isset($auteur['hash_env'])) $auteur['hash_env'] = hash_env();
	if (!isset($auteur['ip_change'])) $auteur['ip_change'] = false;

	if (!isset($auteur['date_session'])) $auteur['date_session'] = time();
	if (is_string($auteur['prefs']))
		$auteur['prefs'] = unserialize($auteur['prefs']);

	if (!ecrire_fichier_session($fichier_session, $auteur)) {
		spip_log('Echec ecriture fichier session '.$fichier_session,_LOG_HS);
		include_spip('inc/minipres');
		echo minipres();
		exit;
	} else {
		include_spip('inc/cookie');
		$duree = _RENOUVELLE_ALEA *
		  (!isset($auteur['cookie'])
		  	? 2 : (is_numeric($auteur['cookie'])
				? $auteur['cookie'] : 20));
		spip_setcookie(
			'spip_session',
			$_COOKIE['spip_session'],
			time() + $duree
			);
		spip_log("ajoute session $fichier_session cookie $duree");

		# on en profite pour purger les vieilles sessions abandonnees
		supprimer_sessions(0, true, false);

		return $_COOKIE['spip_session'];
	}
}


/**
 * Verifie si le cookie spip_session indique une session valide.
 * Si oui, la decrit dans le tableau $visiteur_session et retourne id_auteur
 * La rejoue si IP change puis accepte le changement si $change=true
 *
 * Retourne false en cas d'echec, l'id_auteur de la session si defini, null sinon
 *
 * http://doc.spip.org/@verifier_session
 *
 * @param bool $change
 * @return bool|int|null
 */
function verifier_session($change=false) {
	// si pas de cookie, c'est fichu

	if (!isset($_COOKIE['spip_session']))
		return false;

	// Tester avec alea courant
	$fichier_session = fichier_session('alea_ephemere', true);

	if ($fichier_session AND @file_exists($fichier_session)) {
		include($fichier_session);
	} else {
		// Sinon, tester avec alea precedent
		$fichier_session = fichier_session('alea_ephemere_ancien', true);
		if (!$fichier_session OR !@file_exists($fichier_session)) return false;

		// Renouveler la session avec l'alea courant
		include($fichier_session);
		spip_log('renouvelle session '.$GLOBALS['visiteur_session']['id_auteur']);
		spip_unlink($fichier_session);
		ajouter_session($GLOBALS['visiteur_session']);
	}

	// Compatibilite ascendante : auteur_session est visiteur_session si
	// c'est un auteur SPIP authentifie (tandis qu'un visiteur_session peut
	// n'etre qu'identifie, sans aucune authentification).

	if ($GLOBALS['visiteur_session']['id_auteur'])
		$GLOBALS['auteur_session'] = &$GLOBALS['visiteur_session'];


	// Si l'adresse IP change, inc/presentation mettra une balise image
	// avec un URL de rappel demandant a changer le nom de la session.
	// Seul celui qui a l'IP d'origine est rejoue
	// ainsi un eventuel voleur de cookie ne pourrait pas deconnecter
	// sa victime, mais se ferait deconnecter par elle.
	if (hash_env() != $GLOBALS['visiteur_session']['hash_env']) {
		if (!$GLOBALS['visiteur_session']['ip_change']) {
			define('_SESSION_REJOUER',rejouer_session());
			$GLOBALS['visiteur_session']['ip_change'] = true;
			ajouter_session($GLOBALS['visiteur_session']);
		} else if ($change) {
			spip_log("session non rejouee, vol de cookie ?");
		}
	} else if ($change) {
		spip_log("rejoue session $fichier_session ".$_COOKIE['spip_session']);
		spip_unlink($fichier_session);
		$GLOBALS['visiteur_session']['ip_change'] = false;
		unset($_COOKIE['spip_session']);
		ajouter_session($GLOBALS['visiteur_session']);
	}

	// Si la session a ete initiee il y a trop longtemps, elle est annulee
	if (isset($GLOBALS['visiteur_session'])
	AND defined('_AGE_SESSION_MAX')
	AND _AGE_SESSION_MAX > 0
	AND time() - @$GLOBALS['visiteur_session']['date_session'] > _AGE_SESSION_MAX) {
		unset($GLOBALS['visiteur_session']);
		return false;
	}

	return is_numeric($GLOBALS['visiteur_session']['id_auteur'])
		? $GLOBALS['visiteur_session']['id_auteur']
		: null;
}

/**
 * Lire une valeur dans la session SPIP
 *
 * http://doc.spip.org/@session_get
 *
 * @param string $nom
 * @return mixed
 */
function session_get($nom) {
	return isset($GLOBALS['visiteur_session'][$nom]) ? $GLOBALS['visiteur_session'][$nom] : null;
}


/**
 * Ajouter une donnee dans la session SPIP
 * http://doc.spip.org/@session_set
 *
 * @param string $nom
 * @param null $val
 * @return void
 */
function session_set($nom, $val=null) {

	if (is_null($val)){
		// rien a faire
		if (!isset($GLOBALS['visiteur_session'][$nom])) return;
		unset($GLOBALS['visiteur_session'][$nom]);
	}
	else {
		// On ajoute la valeur dans la globale
		$GLOBALS['visiteur_session'][$nom] = $val;
	}

	ajouter_session($GLOBALS['visiteur_session']);
	actualiser_sessions($GLOBALS['visiteur_session']);
}

/**
 * Mettre a jour les sessions existantes pour un auteur
 * Quand on modifie une fiche auteur on appelle cette fonction qui va
 * mettre a jour les fichiers de session de l'auteur en question.
 * (auteurs identifies seulement)
 *
 * http://doc.spip.org/@actualiser_sessions
 *
 * @param array $auteur
 */
function actualiser_sessions($auteur) {
	if (!$id_auteur = intval($auteur['id_auteur']))
		return;

	// memoriser l'auteur courant (celui qui modifie la fiche)
	$sauve = $GLOBALS['visiteur_session'];

	// .. mettre a jour les sessions de l'auteur cible
	// attention au $ final pour ne pas risquer d'embarquer un .php.jeton temporaire
	// cree par une ecriture concurente d'une session (fichier atomique temporaire)
	$sessions = preg_files(_DIR_SESSIONS, '/'.$id_auteur.'_.*\.php$');
	foreach($sessions as $session) {
		$GLOBALS['visiteur_session'] = array();
		// a pu etre supprime entre le preg initial et le moment ou l'on arrive la (concurrence)
		if (@file_exists($session)){
			include $session; # $GLOBALS['visiteur_session'] est alors l'auteur cible

			$auteur = array_merge($GLOBALS['visiteur_session'], $auteur);
			ecrire_fichier_session($session, $auteur);
		}
	}

	// restaurer l'auteur courant
	$GLOBALS['visiteur_session'] = $sauve;

	// si c'est le meme, rafraichir les valeurs
	if (isset($sauve['id_auteur']) and $auteur['id_auteur'] == $sauve['id_auteur'])
		verifier_session();
}

/**
 * Ecrire le fichier d'une session
 *
 * http://doc.spip.org/@ecrire_fichier_session
 *
 * @param string $fichier
 * @param array $auteur
 * @return bool
 */
function ecrire_fichier_session($fichier, $auteur) {

	$row = $auteur;

	// ne pas enregistrer ces elements de securite
	// dans le fichier de session
	unset($auteur['pass']);
	unset($auteur['htpass']);
	unset($auteur['low_sec']);
	unset($auteur['alea_actuel']);
	unset($auteur['alea_futur']);

	$auteur = pipeline('preparer_fichier_session',array('args'=>array('row'=>$row),'data'=>$auteur));

	// ne pas enregistrer les valeurs vraiment nulle dans le fichier
	foreach($auteur as $variable=>$valeur){
		if ($valeur === null){
			unset($auteur[$variable]);
		}
	}
	
	// enregistrer les autres donnees du visiteur
	$texte = "<"."?php\n";
	foreach ($auteur as $var => $val)
		$texte .= '$GLOBALS[\'visiteur_session\'][\''.$var.'\'] = '
			. var_export($val,true).";\n";
	$texte .= "?".">\n";

	return ecrire_fichier($fichier, $texte);
}


/**
 * Calculer le nom du fichier session
 *
 * http://doc.spip.org/@fichier_session
 *
 * @param string $alea
 * @param bool $tantpis
 * @return string
 */
function fichier_session($alea, $tantpis=false) {

	if (!isset($GLOBALS['meta'][$alea])) {
		include_spip('base/abstract_sql');
		$GLOBALS['meta'][$alea]  = sql_getfetsel('valeur', 'spip_meta', "nom=" . sql_quote($alea), '','', '', '', '', 'continue');
	}

	if (!$GLOBALS['meta'][$alea]) {
		if (!$tantpis) {
			spip_log("fichier session ($tantpis): $alea indisponible");
			include_spip('inc/minipres');
			echo minipres();
		}
		return ''; // echec mais $tanpis
	}
	else {
		$repertoire = sous_repertoire(_DIR_SESSIONS,'',false,$tantpis);
		$c = $_COOKIE['spip_session'];
		return $repertoire . intval($c) .'_' . md5($c.' '.$GLOBALS['meta'][$alea]). '.php';
	}
}


/**
 * Code a inserer par inc/presentation pour rejouer la session
 * Voir action/cookie qui sera appele.
 * Pourquoi insere-t-on le src par js et non directement en statique dans le HTML ?
 * Historiquement, insere par une balise <script> en r424
 * puis modifie par <img> statique + js en r427
 *
 * http://doc.spip.org/@rejouer_session
 *
 * @return string
 */
function rejouer_session()
{
	return '<img src="'.generer_url_action('cookie','change_session=oui', true).'" width="0" height="0" alt="" />';
}


/**
 * On verifie l'IP et le nom du navigateur
 *
 * http://doc.spip.org/@hash_env
 *
 * @return string
 */
function hash_env() {
  static $res ='';
  if ($res) return $res;
  return $res = md5($GLOBALS['ip'] . $_SERVER['HTTP_USER_AGENT']);
}

?>
json.php000066600000004400152525475610006244 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

// Les fonctions de toggg pour faire du JSON

/**
 * Transform a variable into its javascript equivalent (recursive)
 * @access private
 * @param mixed the variable
 * @return string js script | boolean false if error
 */

// http://doc.spip.org/@var2js
function var2js($var) {
	$asso = false;
	switch (true) {
		case is_null($var) :
			return 'null';
		case is_string($var) :
			return '"' .addcslashes($var, "\"\\\n\r/") . '"';
		case is_bool($var) :
			return $var ? 'true' : 'false';
		case is_scalar($var) :
			return (string)$var;
		case is_object( $var) :
			$var = get_object_vars($var);
			$asso = true;
		case is_array($var) :
			$keys = array_keys($var);
			$ikey = count($keys);
			while (!$asso && $ikey--) {
				$asso = $ikey !== $keys[$ikey];
			}
			$sep = '';
			if ($asso) {
				$ret = '{';
				foreach ($var as $key => $elt) {
					$ret .= $sep . '"' . $key . '":' . var2js($elt);
					$sep = ',';
				}
				return $ret ."}";
			} else {
				$ret = '[';
				foreach ($var as $elt) {
					$ret .= $sep . var2js($elt);
					$sep = ',';
				}
				return $ret ."]";
			}
	}
	return false;
}

if(!function_exists('json_encode')) {
	function json_encode($v) { return var2js($v); }
}

// http://doc.spip.org/@json_export
function json_export($var) {
	$var = json_encode($var);

	// flag indiquant qu'on est en iframe et qu'il faut proteger nos
	// donnees dans un <textarea> ; attention $_FILES a ete vide par array_pop
	if (defined('FILE_UPLOAD'))
		return "<textarea>".spip_htmlspecialchars($var)."</textarea>";
	else
		return $var;
}



?>
cookie.php000066600000006075152525475610006556 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

//
// Appliquer le prefixe cookie
//
// http://doc.spip.org/@spip_setcookie
function spip_setcookie ($name='', $value='', $expire=0, $path='AUTO', $domain='', $secure='') {
	// liste des cookies en httponly (a passer en define si besoin)
	$httponly = in_array($name, explode(' ', 'spip_session'));

	$name = preg_replace ('/^spip_/', $GLOBALS['cookie_prefix'].'_', $name);
	if ($path == 'AUTO')
		$path = defined('_COOKIE_PATH')?_COOKIE_PATH:preg_replace(',^\w+://[^/]*,', '', url_de_base());
	if (!$domain AND defined('_COOKIE_DOMAIN'))
		$domain = _COOKIE_DOMAIN;

	#spip_log("cookie('$name', '$value', '$expire', '$path', '$domain', '$secure', '$httponly'");

	$a =
	($httponly AND strnatcmp(phpversion(),'5.2.0') >= 0) ?
	@setcookie ($name, $value, $expire, $path, $domain, $secure, $httponly)
	: ($secure ?
	@setcookie ($name, $value, $expire, $path, $domain, $secure)
	: ($domain ?
	@setcookie ($name, $value, $expire, $path, $domain)
	: ($path ?
	@setcookie ($name, $value, $expire, $path)
	: ($expire ?
	@setcookie ($name, $value, $expire)
	:
	@setcookie ($name, $value)
	))));

	spip_cookie_envoye(true);

	return $a;
}

function spip_cookie_envoye($set = '') {
  static $envoye = false;
  if($set)
    $envoye = true;
  return $envoye;
}

// http://doc.spip.org/@recuperer_cookies_spip
function recuperer_cookies_spip($cookie_prefix) {
	$prefix_long = strlen($cookie_prefix);

	foreach ($_COOKIE as $name => $value) {
		if (substr($name,0,5)=='spip_' && substr($name,0,$prefix_long)!=$cookie_prefix) {
			unset($_COOKIE[$name]);
			unset($GLOBALS[$name]);
		}
	}
	foreach ($_COOKIE as $name => $value) {
		if (substr($name,0,$prefix_long)==$cookie_prefix) {
			$spipname = preg_replace ('/^'.$cookie_prefix.'_/', 'spip_', $name);
			$_COOKIE[$spipname] = $value;
			$GLOBALS[$spipname] = $value;
		}
	}

}

// Idem faudrait creer exec/test_ajax, mais c'est si court.
// Tester si Ajax fonctionne pour ce brouteur
// (si on arrive la c'est que c'est bon, donc poser le cookie)

// http://doc.spip.org/@exec_test_ajax_dist
function exec_test_ajax_dist() {
	switch (_request('js')) {
		// on est appele par <noscript>
		case -1:
			spip_setcookie('spip_accepte_ajax', -1);
			include_spip('inc/headers');
			redirige_par_entete(chemin_image('puce-orange-anim.gif'));
			break;

		// ou par ajax
		case 1:
		default:
			spip_setcookie('spip_accepte_ajax', 1);
			break;
	}
}
?>
mail.php000066600000002155152525475610006222 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

##
## Module de compatibilite ascendante : desormais inc/envoyer_mail
##

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

if (!function_exists('envoyer_mail')) {
	define('_FUNCTION_ENVOYER_MAIL', charger_fonction('envoyer_mail', 'inc'));
// http://doc.spip.org/@envoyer_mail
	function envoyer_mail() {
		$args = func_get_args();
		if (_FUNCTION_ENVOYER_MAIL)
			return call_user_func_array(_FUNCTION_ENVOYER_MAIL, $args);
	}
}


?>
presentation_mini.php000066600000016065152525475610011034 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

/**
 * Fonctions d'affichage pour l'espace privé (hors squelettes)
 *
 * @package SPIP\Core\Affichage
**/
if (!defined('_ECRIRE_INC_VERSION')) return;

/**
 * Affiche un code html (echo) et log l'affichage car cet echo est anormal !
 * 
 * Signale une fonction qui devrait retourner un contenu mais effectue
 * un echo à la place pour compatibilité ascendante
 *
 * @deprecated
 *     Utiliser des squelettes pour l'affichage !
 * 
 * @param string $f
 *     Nom de la fonction
 * @param string $ret
 *     Code HTML à afficher
 * @return void
**/
function echo_log($f, $ret) {
	spip_log("Page " . self() . " function $f: echo ".substr($ret,0,50)."...",'echo');
	echo
	(_SIGNALER_ECHOS?"#Echo par $f#" :"")
		. $ret;
}

/**
 * Retourne le code HTML d'un début de cadre pour le centre de page (haut de page)
 * @return string Code HTML
 */
function debut_grand_cadre(){ return "\n<div class='table_page'>\n";}

/**
 * Retourne le code HTML d'une fin de cadre pour le centre de page (haut de page)
 * @return string Code HTML
 */
function fin_grand_cadre(){ return "\n</div>";}

// Debut de la colonne de gauche
// div navigation fermee par creer_colonne_droite qui ouvre
// div extra lui-meme ferme par debut_droite qui ouvre
// div contenu lui-meme ferme par fin_gauche() ainsi que
// div conteneur
// http://doc.spip.org/@debut_gauche
/**
 * Retourne le code HTML du début de la colonne gauche
 * @return string Code HTML
 */
function debut_gauche() {	return "<div id='conteneur' class=''>\n<div id='navigation' class='lat' role='contentinfo'>\n";}

/**
 * Retourne le code HTML de la fin de la colonne
 * @return string Code HTML
 */
function fin_gauche(){return "</div></div><br class='nettoyeur' />";}

/**
 * Retourne le code HTML du changement de colonne (passer de la gauche à la droite)
 * @return string Code HTML
 */
function creer_colonne_droite(){
	static $deja_colonne_droite;
	if ($GLOBALS['spip_ecran']!='large' OR $deja_colonne_droite) return '';
	$deja_colonne_droite = true;
	return "\n</div><div id='extra' class='lat' role='complementary'>";
}

/**
 * Retourne le code HTML de la colonne droite et du centre de page
 * @return string Code HTML
 */
function debut_droite() {
	return liste_objets_bloques(_request('exec'))
	  . creer_colonne_droite()
	  . "</div>"
	  . "\n<div id='contenu'>";
}

// http://doc.spip.org/@liste_articles_bloques
function liste_objets_bloques($exec,$contexte=array(),$auteur=null){
	$res = '';
	if ($GLOBALS['meta']["articles_modif"] != "non") {
		include_spip('inc/drapeau_edition');
		if (is_null($auteur))
			$auteur = $GLOBALS['visiteur_session'];
		if ($en_cours=trouver_objet_exec($exec)
			AND $en_cours['edition']
			AND $type = $en_cours['type']
		  AND ($id = $contexte[$en_cours['id_table_objet']] OR $id = _request($en_cours['id_table_objet']))) {
			// marquer le fait que l'objet est ouvert en edition par toto
			// a telle date ; une alerte sera donnee aux autres redacteurs
			signale_edition ($id,  $auteur, $type);
		}

		$objets_ouverts = liste_drapeau_edition($auteur['id_auteur']);
		if (count($objets_ouverts)) {
			$res .= recuperer_fond('prive/objets/liste/objets-en-edition',array(),array('ajax'=>true));
		}
	}
	return $res;
}

// Fin de page de l'interface privee.
// Elle comporte une image invisible declenchant une tache de fond
// http://doc.spip.org/@fin_page
function fin_page(){
	include_spip('inc/pipelines');
	// avec &var_profile=1 on a le tableau de mesures SQL
	$debug = ((_request('exec') !== 'valider_xml')
		AND ((_request('var_mode') == 'debug')
			OR (isset($GLOBALS['tableau_des_temps']) AND $GLOBALS['tableau_des_temps'])
			AND isset($_COOKIE['spip_admin'])));
	$t = '</div><div id="pied"><div class="largeur">'
	. recuperer_fond('prive/squelettes/inclure/pied')
	. "</div>"
	. "</div></div>" // cf. div#page et div.largeur ouvertes dans conmmencer_page()
	. ($debug?erreur_squelette():'')
	. "</body></html>\n";
	return f_queue($t);
}

function html_tests_js(){
	if (_SPIP_AJAX AND !defined('_TESTER_NOSCRIPT')) {
	  // pour le pied de page (deja defini si on est validation XML)
		define('_TESTER_NOSCRIPT',
			"<noscript>\n<div style='display:none;'><img src='"
		        . generer_url_ecrire('test_ajax', 'js=-1')
		        . "' width='1' height='1' alt='' /></div></noscript>\n");
	}
	return
		(defined('_SESSION_REJOUER') ? _SESSION_REJOUER : '')
	. (defined('_TESTER_NOSCRIPT') ? _TESTER_NOSCRIPT : '');
}

function info_maj_spip(){

	$maj = $GLOBALS['meta']['info_maj_spip'];
	if (!$maj)
		return "";

	$maj = explode('|',$maj);
	// c'est une ancienne notif, on a fait la maj depuis !
	if ($GLOBALS['spip_version_branche']!==reset($maj))
		return "";

	if (!autoriser('webmestre'))
		return "";

	array_shift($maj);
	$maj = implode('|',$maj);

	return "$maj<br />";
}

// http://doc.spip.org/@info_copyright
function info_copyright() {
	global $spip_version_affichee, $spip_lang;

	$version = $spip_version_affichee;

	//
	// Mention, le cas echeant, de la revision SVN courante
	//
	if ($svn_revision = version_svn_courante(_DIR_RACINE)) {
		$version .= ' ' . (($svn_revision < 0) ? 'SVN ':'')
		. "[<a href='http://core.spip.net/projects/spip/repository/revisions/"
		. abs($svn_revision) . "' onclick=\"window.open(this.href); return false;\">"
		. abs($svn_revision) . "</a>]";
	}

	// et la version de l'ecran de securite
	$secu = defined('_ECRAN_SECURITE')
		? "<br />"._T('ecran_securite',array('version'=>_ECRAN_SECURITE))
		: '';

	return _T('info_copyright',
		   array('spip' => "<b>SPIP $version</b> ",
			 'lien_gpl' =>
			 "<a href='". generer_url_ecrire("aide", "aide=licence&var_lang=$spip_lang") . "' onclick=\"window.open(this.href, 'spip_aide', 'scrollbars=yes,resizable=yes,width=740,height=580'); return false;\">" . _T('info_copyright_gpl')."</a>"))
		. $secu;

}

// http://doc.spip.org/@formulaire_recherche
function formulaire_recherche($page, $complement=""){
	$recherche = _request('recherche');
	$recherche_aff = entites_html($recherche);
	if (!strlen($recherche)) {
		$recherche_aff = _T('info_rechercher');
		$onfocus = " onfocus=\"this.value='';\"";
	} else $onfocus = '';

	$form = '<input type="text" size="10" value="'.$recherche_aff.'" name="recherche" class="recherche" accesskey="r"' . $onfocus . ' />';
	$form .= "<input type='image' src='" . chemin_image('rechercher-20.png') . "' name='submit' class='submit' alt='"._T('info_rechercher')."' />";
	return "<div class='spip_recherche'>".generer_form_ecrire($page, $form . $complement, " method='get'")."</div>";
}

?>
rubriques.php000066600000062555152525475610007333 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

/**
 * Fichier gérant l'actualisation et le suivi des rubriques, et de leurs branches
 *
 * @package SPIP\Rubriques
 */
 
if (!defined('_ECRIRE_INC_VERSION')) return;


/**
 * Recalcule les statuts d'une rubrique
 * 
 * Fonction à appeler lorsque le statut d'un objet change dans une rubrique
 * ou que la rubrique est deplacée.
 * 
 * Si le statut passe a "publie", la rubrique et ses parents y passent aussi
 * et les langues utilisees sont recalculées.
 * Conséquences symétriques s'il est depublié.
 * 
 * S'il est deplacé alors qu'il était publiée, double conséquence.
 * 
 * Tout cela devrait passer en SQL, sous forme de Cascade SQL.
 *
 * @param int $id_rubrique
 *     Identifiant de la rubrique
 * @param array $modifs
 *     Tableau de description des modifications.
 *     Peut avoir 2 index, 'statut' étant obligatoire :
 *     - statut : indique le nouveau statut de la rubrique
 *     - id_rubrique : indiquer la rubrique dans laquelle on déplace la rubrique (son nouveau parent donc)
 * @param string $statut_ancien
 *     Ancien statut de la rubrique
 * @param bool $postdate
 *     true pour recalculer aussi la date du prochain article post-daté
 * @return bool
 *     true si le statut change effectivement
**/
function calculer_rubriques_if ($id_rubrique, $modifs, $statut_ancien='', $postdate = false)
{
	$neuf = false;
	if ($statut_ancien == 'publie') {
		if (isset($modifs['statut'])
			OR isset($modifs['id_rubrique'])
			OR ($postdate AND strtotime($postdate)>time()))
			$neuf |= depublier_branche_rubrique_if($id_rubrique);
		// ne publier que si c'est pas un postdate, ou si la date n'est pas dans le futur
		if ($postdate){
			calculer_prochain_postdate(true);
			$neuf |= (strtotime($postdate)<=time()); // par securite
		}
		elseif (isset($modifs['id_rubrique']))
			$neuf |= publier_branche_rubrique($modifs['id_rubrique']);
	}
	elseif ($modifs['statut']=='publie'){
		if ($postdate){
			calculer_prochain_postdate(true);
			$neuf |= (strtotime($postdate)<=time()); // par securite
		}
		else
			$neuf |= publier_branche_rubrique($id_rubrique);
	}

	if ($neuf)
	// Sauver la date de la derniere mise a jour (pour menu_rubriques)
	  ecrire_meta("date_calcul_rubriques", date("U"));

	$langues = calculer_langues_utilisees();
	ecrire_meta('langues_utilisees', $langues);
}


/**
 * Publie une rubrique et sa hiérarchie de rubriques
 * 
 * Fonction à appeler lorsqu'on dépublie ou supprime quelque chose
 * dans une rubrique. 
 *
 * @todo Le nom de la fonction est trompeur, vu que la fonction remonte dans la hierarchie !
 * 
 * @param int $id_rubrique
 *     Identifiant de la rubrique
 * @return bool
 *     true si le statut change effectivement
 */
function publier_branche_rubrique($id_rubrique)
{
	$id_pred = $id_rubrique;
	while (true) {
		sql_updateq('spip_rubriques', array('statut'=>'publie', 'date'=>date('Y-m-d H:i:s')), "id_rubrique=$id_rubrique");
		$id_parent = sql_getfetsel('id_parent', 'spip_rubriques AS R', "R.id_rubrique=$id_rubrique");
		if (!$id_parent) break;
		$id_rubrique = $id_parent;
	} 

#	spip_log(" publier_branche_rubrique($id_rubrique $id_pred");
	return $id_pred != $id_rubrique;
}

/**
 * Dépublie si nécessaire des éléments d'une hiérarchie de rubriques
 * 
 * Fonction à appeler lorsqu'on dépublie ou supprime quelque chose
 * dans une rubrique. 
 *
 * @todo Le nom de la fonction est trompeur, vu que la fonction remonte dans la hierarchie !
 * 
 * @param int $id_rubrique
 *     Identifiant de la rubrique
 * @return bool
 *     true si le statut change effectivement
 */
function depublier_branche_rubrique_if($id_rubrique){
	$date = date('Y-m-d H:i:s'); // figer la date

	#	spip_log("depublier_branche_rubrique($id_rubrique ?");
	$id_pred = $id_rubrique;
	while ($id_pred) {

		if (!depublier_rubrique_if($id_pred,$date))
			return $id_pred != $id_rubrique;
		// passer au parent si on a depublie
		$r = sql_fetsel("id_parent", "spip_rubriques", "id_rubrique=$id_pred");
		$id_pred = $r['id_parent'];
	}

	return $id_pred != $id_rubrique;
}

/**
 * Dépublier une rubrique si aucun contenu publié connu n'est trouvé dedans
 * 
 * @param int $id_rubrique
 *     Identifiant de la rubrique à tester
 * @param string|null $date
 *     Date pour le calcul des éléments post-datés.
 *     null = date actuelle.
 * @return bool
 *    true si la rubrique a été dépubliée
 */
function depublier_rubrique_if($id_rubrique,$date=null){
	if (is_null($date)) {
		$date = date('Y-m-d H:i:s');
	}
	$postdates = ($GLOBALS['meta']["post_dates"] == "non") ?
		" AND date <= ".sql_quote($date) : '';

	if (!$id_rubrique=intval($id_rubrique))
		return false;

	// verifier qu'elle existe et est bien publiee
	$r = sql_fetsel('id_rubrique,statut','spip_rubriques',"id_rubrique=$id_rubrique");
	if (!$r OR $r['statut']!=='publie')
		return false;

	// On met le nombre de chaque type d'enfants dans un tableau
	// Le type de l'objet est au pluriel
	$compte = array(
		'articles' => sql_countsel("spip_articles",  "id_rubrique=$id_rubrique AND statut='publie'$postdates"),
		'rubriques' => sql_countsel("spip_rubriques",  "id_parent=$id_rubrique AND statut='publie'"),
		'documents' => sql_countsel("spip_documents_liens",  "id_objet=$id_rubrique AND objet='rubrique'")
	);
	
	// On passe le tableau des comptes dans un pipeline pour que les plugins puissent ajouter (ou retirer) des enfants
	$compte = pipeline('objet_compte_enfants',
		array(
			'args' => array(
				'objet' => 'rubrique',
				'id_objet' => $id_rubrique,
				'statut' => 'publie',
				'date' => $date
			),
			'data' => $compte
		)
	);
	
	// S'il y a au moins un enfant de n'importe quoi, on ne dépublie pas
	foreach($compte as $objet => $n)
		if ($n)
			return false;

	sql_updateq("spip_rubriques", array("statut" => '0'), "id_rubrique=$id_rubrique");
#		spip_log("depublier_rubrique $id_pred");
	return true;
}


/**
 * Recalcule des héritages de rubriques
 *
 * Recalcule le statut des rubriques, les langues héritées et la date
 * du prochain article post-daté
 * 
 * Cette fonction est appelée après importation: elle calcule les meta-donnes
 * resultantes et remet de la coherence au cas où la base importée en manquait
 * 
 * Cette fonction doit etre invoquée sans processus concurrent potentiel.
 * 
 * @return void
**/
function calculer_rubriques() {

	calculer_rubriques_publiees();

	// Apres chaque (de)publication 
	// recalculer les langues utilisees sur le site
	$langues = calculer_langues_utilisees();
	ecrire_meta('langues_utilisees', $langues);

	// Sauver la date de la derniere mise a jour (pour menu_rubriques)
	ecrire_meta("date_calcul_rubriques", date("U"));

	// on calcule la date du prochain article post-date
	calculer_prochain_postdate();
}


/**
 *  
 * Recalcule l'ensemble des donnees associees a l'arborescence des rubriques
 * 
 * Attention, faute de SQL transactionnel on travaille sur
 * des champs temporaires afin de ne pas casser la base
 * pendant la demi seconde de recalculs
 * 
 * @return void
**/
function calculer_rubriques_publiees() {

	// Mettre les compteurs a zero
	sql_updateq('spip_rubriques', array('date_tmp' => '0000-00-00 00:00:00', 'statut_tmp' => 'prive'));

	//
	// Publier et dater les rubriques qui ont un article publie
	//

	// Afficher les articles post-dates ?
	$postdates = ($GLOBALS['meta']["post_dates"] == "non") ?
		"AND A.date <= ".sql_quote(date('Y-m-d H:i:s')) : '';

	$r = sql_select("R.id_rubrique AS id, max(A.date) AS date_h", "spip_rubriques AS R, spip_articles AS A", "R.id_rubrique = A.id_rubrique AND A.statut='publie' $postdates ", "R.id_rubrique");
	while ($row = sql_fetch($r))
		sql_updateq("spip_rubriques", array("statut_tmp" => 'publie', "date_tmp" => $row['date_h']), "id_rubrique=".$row['id']);

	// point d'entree pour permettre a des plugins de gerer le statut
	// autrement (par ex: toute rubrique est publiee des sa creation)
	// Ce pipeline fait ce qu'il veut, mais s'il touche aux statuts/dates
	// c'est statut_tmp/date_tmp qu'il doit modifier
	// [C'est un trigger... a renommer en trig_calculer_rubriques ?]
	pipeline('calculer_rubriques', null);
	

	// Les rubriques qui ont une rubrique fille plus recente
	// on tourne tant que les donnees remontent vers la racine.
	do {
		$continuer = false;
		$r = sql_select("R.id_rubrique AS id, max(A.date_tmp) AS date_h", "spip_rubriques AS R, spip_rubriques AS A", "R.id_rubrique = A.id_parent AND (R.date_tmp < A.date_tmp OR R.statut_tmp<>'publie') AND A.statut_tmp='publie' ", "R.id_rubrique");
		while ($row = sql_fetch($r)) {
		  sql_updateq('spip_rubriques', array('statut_tmp'=>'publie', 'date_tmp'=>$row['date_h']),"id_rubrique=".$row['id']);
			$continuer = true;
		}
	} while ($continuer);

	// Enregistrement des modifs
	sql_update('spip_rubriques', array('date'=>'date_tmp', 'statut'=>'statut_tmp'));
}

/**
 * Recalcule les secteurs et les profondeurs des rubriques (et articles)
 *
 * Cherche les rubriques ayant des id_secteur ou profondeurs ne correspondant pas
 * avec leur parent, et les met à jour. De même avec les articles et leur id_secteur
 * On procede en iterant la profondeur de 1 en 1 pour ne pas risquer une boucle infinie sur reference circulaire
 * 
 * @return void
**/
function propager_les_secteurs()
{
	// Profondeur 0
	// Toutes les rubriques racines sont de profondeur 0
	// et fixer les id_secteur des rubriques racines
	sql_update('spip_rubriques', array('id_secteur'=>'id_rubrique','profondeur'=>0), "id_parent=0");
	// Toute rubrique non racine est de profondeur >0
	sql_updateq('spip_rubriques', array('profondeur'=>1), "id_parent<>0 AND profondeur=0");

	// securite : pas plus d'iteration que de rubriques dans la base
	$maxiter = sql_countsel("spip_rubriques");

	// reparer les rubriques qui n'ont pas l'id_secteur de leur parent
	// on fait profondeur par profondeur

	$prof = 0;
	do {
		$continuer = false;

		// Par recursivite : si toutes les rubriques de profondeur $prof sont bonnes
		// on fixe le profondeur $prof+1

		// Toutes les rubriques dont le parent est de profondeur $prof ont une profondeur $prof+1
		// on teste A.profondeur > $prof+1 car :
		// - toutes les rubriques de profondeur 0 à $prof sont bonnes
		// - si A.profondeur = $prof+1 c'est bon
		// - cela nous protege de la boucle infinie en cas de reference circulaire dans les rubriques
		$maxiter2 = $maxiter;
		while ($maxiter2--
			AND $rows = sql_allfetsel(
			"A.id_rubrique AS id, R.id_secteur AS id_secteur, R.profondeur+1 as profondeur",
			"spip_rubriques AS A JOIN spip_rubriques AS R ON A.id_parent = R.id_rubrique",
			"R.profondeur=".intval($prof)." AND (A.id_secteur <> R.id_secteur OR A.profondeur > R.profondeur+1)",
		  "","R.id_secteur","0,100")){

			$id_secteur = null;
			$ids = array();
			while ($row = array_shift($rows)) {
				if ($row['id_secteur']!==$id_secteur){
					if (count($ids))
						sql_updateq("spip_rubriques", array("id_secteur" => $id_secteur,'profondeur' => $prof+1), sql_in('id_rubrique',$ids));
					$id_secteur = $row['id_secteur'];
					$ids = array();
				}
				$ids[] = $row['id'];
			}
			if (count($ids))
				sql_updateq("spip_rubriques", array("id_secteur" => $id_secteur,'profondeur' => $prof+1), sql_in('id_rubrique',$ids));
		}


		// Toutes les rubriques de profondeur $prof+1 qui n'ont pas un parent de profondeur $prof sont decalees
		$maxiter2 = $maxiter;
		while ($maxiter2--
			AND $rows = sql_allfetsel(
			"id_rubrique as id",
			"spip_rubriques",
			"profondeur=".intval($prof+1)." AND id_parent NOT IN (".sql_get_select("zzz.id_rubrique","spip_rubriques AS zzz","zzz.profondeur=".intval($prof)).")",'','','0,100')){
			$rows = array_map('reset',$rows);
			sql_updateq("spip_rubriques", array('profondeur' => $prof+2), sql_in("id_rubrique",$rows));
		}

		// ici on a fini de valider $prof+1, toutes les rubriques de prondeur 0 a $prof+1 sont OK
		// si pas de rubrique a profondeur $prof+1 pas la peine de continuer
		// si il reste des rubriques non vues, c'est une branche morte ou reference circulaire (base foireuse)
		// on arrete les frais
		if (sql_countsel("spip_rubriques","profondeur=".intval($prof+1))){
			$prof++;
			$continuer = true;
		}
	}
	while ($continuer AND $maxiter--);

	// loger si la table des rubriques semble foireuse
	// et mettre un id_secteur=0 sur ces rubriques pour eviter toute selection par les boucles
	if (sql_countsel("spip_rubriques","profondeur>".intval($prof+1))){
		spip_log("Les rubriques de profondeur>".($prof+1)." semblent suspectes (branches morte ou reference circulaire dans les parents)",_LOG_CRITIQUE);
		sql_update("spip_rubriques",array('id_secteur'=>0),"profondeur>".intval($prof+1));
	}

	// reparer les articles
	$r = sql_select("A.id_article AS id, R.id_secteur AS secteur", "spip_articles AS A, spip_rubriques AS R", "A.id_rubrique = R.id_rubrique AND A.id_secteur <> R.id_secteur");

	while ($row = sql_fetch($r)) {
		sql_update("spip_articles", array("id_secteur" => $row['secteur']), "id_article=".$row['id']);
	}

	// avertir les plugins qui peuvent faire leur mises a jour egalement
	pipeline('trig_propager_les_secteurs','');
}


/**
 * Recalcule les langues héritées des sous-rubriques
 *
 * Cherche les langues incorrectes de sous rubriques, qui doivent hériter
 * de la rubrique parente lorsque langue_choisie est différent de oui,
 * et les corrige.
 * 
 * @return bool
 *     true si un changement a eu lieu
**/
function calculer_langues_rubriques_etape() {
	$s = sql_select("A.id_rubrique AS id_rubrique, R.lang AS lang", "spip_rubriques AS A, spip_rubriques AS R", "A.id_parent = R.id_rubrique AND A.langue_choisie != 'oui' AND R.lang<>'' AND R.lang<>A.lang");

	$t = false;
	while ($row = sql_fetch($s)) {
		$id_rubrique = $row['id_rubrique'];
		$t = sql_updateq('spip_rubriques', array('lang' => $row['lang'], 'langue_choisie'=>'non'), "id_rubrique=$id_rubrique");
	}

	return $t;
}

/**
 * Recalcule les langues des rubriques et articles
 *
 * Redéfinit la langue du site sur les rubriques sans langue spécifiée
 * (langue_choisie différent de 'oui')
 * 
 * Redéfinit les langues des articles sans langue spécifiée
 * (langue_choisie différent de 'oui') en les rebasant sur la langue
 * de la rubrique parente lorsque ce n'est pas le cas.
 *
 * @return void 
**/
function calculer_langues_rubriques() {

	// rubriques (recursivite)
	sql_updateq("spip_rubriques", array("lang" => $GLOBALS['meta']['langue_site'], "langue_choisie" => 'non'), "id_parent=0 AND langue_choisie != 'oui'");
	while (calculer_langues_rubriques_etape());

	// articles
	$s = sql_select("A.id_article AS id_article, R.lang AS lang", "spip_articles AS A, spip_rubriques AS R", "A.id_rubrique = R.id_rubrique AND A.langue_choisie != 'oui' AND (length(A.lang)=0 OR length(R.lang)>0) AND R.lang<>A.lang");
	while ($row = sql_fetch($s)) {
		$id_article = $row['id_article'];
		sql_updateq('spip_articles', array("lang"=> $row['lang'], 'langue_choisie'=>'non'), "id_article=$id_article");
	}

	if ($GLOBALS['meta']['multi_rubriques'] == 'oui') {

		$langues = calculer_langues_utilisees();
		ecrire_meta('langues_utilisees', $langues);
	}
	
	// avertir les plugins qui peuvent faire leur mises a jour egalement
	pipeline('trig_calculer_langues_rubriques','');
}


/**
 * Calcule la liste des langues reellement utilisees dans le site public
 *
 * La recherche de langue est effectuée en recréant une boucle pour chaque
 * objet éditorial gérant des langues de sorte que les éléments non publiés
 * ne sont pas pris en compte.
 * 
 * @param string $serveur
 *    Nom du connecteur à la base de données
 * @return string
 *    Liste des langues utilisées séparées par des virgules
**/
function calculer_langues_utilisees ($serveur='') {
	include_spip('public/interfaces');
	include_spip('public/compiler');
	include_spip('public/composer');
	$langues = array();

	$langues[$GLOBALS['meta']['langue_site']] = 1;

	include_spip('base/objets');
	$tables = lister_tables_objets_sql();
	$trouver_table = charger_fonction('trouver_table','base');

	foreach(array_keys($tables) as $t){
		$desc = $trouver_table($t,$serveur);
		// c'est une table avec des langues
		if ($desc['exist']
		  AND isset($desc['field']['lang'])
			AND isset($desc['field']['langue_choisie'])){

			$boucle = new Boucle();
			$boucle->show = $desc;
			$boucle->nom = 'calculer_langues_utilisees';
			$boucle->id_boucle = $desc['table_objet'];
			$boucle->id_table = $desc['table_objet'];
			$boucle->sql_serveur = $serveur;
			$boucle->select[] = "DISTINCT lang";
			$boucle->from[$desc['table_objet']] = $t;
			$boucle = pipeline('pre_boucle', $boucle);
			
			if (isset($desc['statut'])
		    AND $desc['statut']){
				instituer_boucle($boucle, false);
				$res = calculer_select($boucle->select,$boucle->from,$boucle->from_type,$boucle->where,$boucle->join,$boucle->group,$boucle->order,$boucle->limit,$boucle->having,$desc['table_objet'],$desc['table_objet'],$serveur);
			}
			else
				$res = sql_select(implode(',',$boucle->select),$boucle->from);
			while ($row = sql_fetch($res)) {
				$langues[$row['lang']] = 1;
			}
		}
	}

	$langues = array_filter(array_keys($langues));
	sort($langues);
	$langues = join(',',$langues);
	spip_log("langues utilisees: $langues");
	return $langues;
}

/**
 * Calcule une branche de rubriques
 * 
 * Dépréciée, pour compatibilité
 *
 * @deprecated
 * @see calcul_branche_in()
 * 
 * @param string|int|array $generation
 * @return string
 */
function calcul_branche ($generation) {return calcul_branche_in($generation);}

/**
 * Calcul d'une branche de rubrique
 * 
 * Liste des id_rubrique contenues dans une rubrique donnée
 *
 * @see inc_calcul_branche_in_dist()
 * 
 * @param string|int|array $id
 *     Identifiant de la, ou des rubriques noeuds
 * @return string
 *     Liste des identifiants séparés par des virgules,
 *     incluant les rubriques noeuds et toutes leurs descendances
 */
function calcul_branche_in($id) {
	$calcul_branche_in = charger_fonction('calcul_branche_in','inc');
	return $calcul_branche_in($id);
}

/**
 * Calcul d'une hiérarchie
 * 
 * Liste des id_rubrique contenant une rubrique donnée
 *
 * @see inc_calcul_hierarchie_in_dist()
 * @param string|int|array $id
 *     Identifiant de la, ou des rubriques dont on veut obtenir les hierarchies
 * @param bool $tout
 *     inclure la rubrique de depart dans la hierarchie ou non
 * @return string
 *     Liste des identifiants séparés par des virgules,
 *     incluant les rubriques transmises et toutes leurs parentées
 */
function calcul_hierarchie_in($id, $tout=true) {
	$calcul_hierarchie_in = charger_fonction('calcul_hierarchie_in','inc');
	return $calcul_hierarchie_in($id, $tout);
}


/**
 * Calcul d'une branche de rubriques
 * 
 * Liste des id_rubrique contenues dans une rubrique donnée
 * pour le critere {branche}
 * 
 * Fonction surchargeable pour optimisation
 *
 * @param string|int|array $id
 *     Identifiant de la, ou des rubriques noeuds
 * @return string
 *     Liste des identifiants séparés par des virgules,
 *     incluant les rubriques noeuds et toutes leurs descendances
 */
function inc_calcul_branche_in_dist($id) {
	static $b = array();

	// normaliser $id qui a pu arriver comme un array, comme un entier, ou comme une chaine NN,NN,NN
	if (!is_array($id)) $id = explode(',',$id);
	$id = join(',', array_map('intval', $id));
	if (isset($b[$id]))
		return $b[$id];

	// Notre branche commence par la rubrique de depart
	$branche = $r = $id;

	// On ajoute une generation (les filles de la generation precedente)
	// jusqu'a epuisement, en se protegeant des references circulaires
	$maxiter = 10000;
	while ($maxiter-- AND $filles = sql_allfetsel(
					'id_rubrique',
					'spip_rubriques',
					sql_in('id_parent', $r) ." AND ". sql_in('id_rubrique', $r, 'NOT')
					)) {
		$r = join(',', array_map('reset', $filles));
		$branche .= ',' . $r;
	}

	# securite pour ne pas plomber la conso memoire sur les sites prolifiques
	if (strlen($branche)<10000)
		$b[$id] = $branche;
	return $branche;
}


/**
 * Calcul d'une hiérarchie
 * 
 * Liste des id_rubrique contenant une rubrique donnée,
 * contrairement à la fonction calcul_branche_in() qui calcule les
 * rubriques contenues
 *
 * @param string|int|array $id
 *     Identifiant de la, ou des rubriques dont on veut obtenir les hierarchies
 * @param bool $tout
 *     inclure la rubrique de depart dans la hierarchie ou non
 * @return string
 *     Liste des identifiants séparés par des virgules,
 *     incluant les rubriques transmises et toutes leurs parentées
 */
function inc_calcul_hierarchie_in_dist($id, $tout=true) {
	static $b = array();

	// normaliser $id qui a pu arriver comme un array, comme un entier, ou comme une chaine NN,NN,NN
	if (!is_array($id)) $id = explode(',',$id);
	$id = join(',', array_map('intval', $id));

	if (isset($b[$id])) {
		// Notre branche commence par la rubrique de depart si $tout=true
		return $tout ? (strlen($b[$id]) ? $b[$id] . ",$id" : $id) : $b[$id];
	}

	$hier = "";

	// On ajoute une generation (les filles de la generation precedente)
	// jusqu'a epuisement, en se protegeant des references circulaires
	$ids_nouveaux_parents = $id;
	$maxiter = 10000;
	while ($maxiter-- AND $parents = sql_allfetsel(
			'id_parent',
			'spip_rubriques',
			sql_in('id_rubrique', $ids_nouveaux_parents) ." AND ". sql_in('id_parent',$hier,'NOT')
		  )) {
		$ids_nouveaux_parents = join(',', array_map('reset', $parents));
		$hier = $ids_nouveaux_parents.(strlen($hier)?','.$hier:'');
	}

	# securite pour ne pas plomber la conso memoire sur les sites prolifiques
	if (strlen($hier)<10000) {
		$b[$id] = $hier;
	}

	// Notre branche commence par la rubrique de depart si $tout=true
	$hier = $tout ? (strlen($hier) ? "$hier,$id" : $id) : $hier;

	return $hier;
}



/**
 * Calcule la date du prochain article post-daté 
 *
 * Appelée lorsqu'un (ou plusieurs) article post-daté arrive à terme
 * ou est redaté
 * 
 * @param bool $check
 *     true pour affecter le statut des rubriques concernées.
 * @return void
**/
function calculer_prochain_postdate($check= false) {
	include_spip('base/abstract_sql');
	if ($check) {
		$postdates = ($GLOBALS['meta']["post_dates"] == "non") ?
			"AND A.date <= ".sql_quote(date('Y-m-d H:i:s')) : '';

		$r = sql_select("DISTINCT A.id_rubrique AS id",
			"spip_articles AS A LEFT JOIN spip_rubriques AS R ON A.id_rubrique=R.id_rubrique", "R.statut != 'publie' AND A.statut='publie'$postdates");
		while ($row = sql_fetch($r))
			publier_branche_rubrique($row['id']);

		pipeline('trig_calculer_prochain_postdate','');
	}

	$t = sql_fetsel("date", "spip_articles", "statut='publie' AND date > ".sql_quote(date('Y-m-d H:i:s')), "", "date", "1");
	
	if ($t) {
		$t =  $t['date'];
		if (!isset($GLOBALS['meta']['date_prochain_postdate'])
			OR $t<>$GLOBALS['meta']['date_prochain_postdate']){
			ecrire_meta('date_prochain_postdate', strtotime($t));
			ecrire_meta('derniere_modif', time());
		}
	}
	else {
		effacer_meta('date_prochain_postdate');
		ecrire_meta('derniere_modif', time());
	}

	spip_log("prochain postdate: $t");
}

/**
 * Crée une arborescence de rubrique
 * 
 * creer_rubrique_nommee('truc/machin/chose') va créer
 * une rubrique truc, une sous-rubrique machin, et une sous-sous-rubrique
 * chose, sans créer de rubrique si elle existe déjà
 * à partir de $id_parent (par défaut, à partir de la racine)
 * 
 * NB: cette fonction est très pratique, mais pas utilisée dans le core
 * pour rester légère elle n'appelle pas calculer_rubriques()
 *
 * @param string $titre
 *     Titre des rubriques, séparés par des /
 * @param int $id_parent
 *     Identifiant de la rubrique parente
 * @param string $serveur
 *     Nom du connecteur à la base de données
 * @return int
 *     Identifiant de la rubrique la plus profonde.
 */
function creer_rubrique_nommee($titre, $id_parent=0, $serveur='') {

	// eclater l'arborescence demandee
	// echapper les </multi> et autres balises fermantes html
	$titre = preg_replace(",</([a-z][^>]*)>,ims","<@\\1>",$titre);
	$arbo = explode('/', preg_replace(',^/,', '', $titre));
	include_spip('base/abstract_sql');
	foreach ($arbo as $titre) {
		// retablir les </multi> et autres balises fermantes html
		$titre = preg_replace(",<@([a-z][^>]*)>,ims","</\\1>",$titre);
		$r = sql_getfetsel("id_rubrique", "spip_rubriques", "titre = ".sql_quote($titre)." AND id_parent=".intval($id_parent),
		$groupby = array(), $orderby = array(), $limit = '', $having = array(), $serveur);
		if ($r !== NULL) {
			$id_parent = $r;
		} else {
			$id_rubrique = sql_insertq('spip_rubriques', array(
				'titre' => $titre,
				'id_parent' => $id_parent,
				'statut' => 'prive')
				,$desc=array(), $serveur);
			if ($id_parent > 0) {
				$data = sql_fetsel("id_secteur,lang", "spip_rubriques", "id_rubrique=$id_parent",
				$groupby = array(), $orderby = array(), $limit = '', $having = array(), $serveur);
				$id_secteur = $data['id_secteur'];
				$lang = $data['lang'];
			} else {
				$id_secteur = $id_rubrique;
				$lang = $GLOBALS['meta']['langue_site'];
			}

			sql_updateq('spip_rubriques', array('id_secteur'=>$id_secteur, "lang"=>$lang), "id_rubrique=$id_rubrique", $desc='', $serveur);

			// pour la recursion
			$id_parent = $id_rubrique;
		}
	}

	return intval($id_parent);
}

?>
icone_renommer.php000066600000004443152525475610010303 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

include_spip('inc/boutons');
include_spip('base/objets');

function inc_icone_renommer_dist($fond,$fonction){
	$size = 24;
	if (preg_match("/(?:-([0-9]{1,3}))?([.](gif|png))?$/i",$fond,$match)
		AND ((isset($match[0]) AND $match[0]) OR (isset($match[1]) AND $match[1]))) {
		if (isset($match[1]) AND $match[1]) {
			$size = $match[1];
		}
		$type = substr($fond,0,-strlen($match[0]));
		if (!isset($match[2]) OR !$match[2]) {
			$fond .= ".png";
		}
	}
	else {
		$type = $fond;
		$fond .= ".png";
	}

	$rtl = false;
	if (preg_match(',[-_]rtl$,i',$type,$match)){
		$rtl = true;
		$type = substr($type,0,-strlen($match[0]));
	}

	// objet_type garde invariant tout ce qui ne commence par par id_, spip_
	// et ne finit pas par un s, sauf si c'est une exception declaree
	$type = objet_type($type, false);

	$dir = "images/";
	$f = "$type-$size.png";
	if ($icone = find_in_theme($dir.$f)){
		$dir = dirname($icone);
		$fond = $icone;

		if ($rtl
			AND $fr = "$type-rtl-$size.png"
			AND file_exists($dir.'/'.$fr))
			$type = "$type-rtl";

		$action = $fonction;
		if ($action=="supprimer.gif"){
			$action = "del";
		}
		elseif ($action=="creer.gif"){
			$action = "new";
		}
		elseif ($action=="edit.gif"){
			$action = "edit";
		}
		if (!in_array($action,array('del','new','edit')))
			$action = "";
		if ($action){
			if ($fa = "$type-$action-$size.png"
			AND file_exists($dir.'/'.$fa)){
				$fond = $dir .'/'. $fa;
				$fonction = "";
			}
			else {
				$fonction = "$action-$size.png";
			}
		}
		// c'est bon !
		return array($fond,$fonction);
	}

	return array($fond,$fonction);
}
?>
journal.php000066600000002255152525475610006753 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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


/*
 * Consigner une phrase dans le journal de bord du site
 * Cette API travaille a minima, mais un plugin pourra stocker
 * ces journaux en base et fournir des outils d'affichage, de selection etc
 *
 * @param string $journal
 * @param array $opt
 */
function inc_journal_dist($phrase, $opt = array()) {
	if (!strlen($phrase))
		return;
	if ($opt)
		$phrase .= " :: ".str_replace("\n", ' ', join(', ',$opt));
	spip_log($phrase, 'journal');
}

?>
pclzip.php000066600000601227152525475610006606 0ustar00<?php
// --------------------------------------------------------------------------------
// PhpConcept Library - Zip Module 2.8.2
// --------------------------------------------------------------------------------
// License GNU/LGPL - Vincent Blavet - August 2009
// http://www.phpconcept.net
// --------------------------------------------------------------------------------
//
// Presentation :
//   PclZip is a PHP library that manage ZIP archives.
//   So far tests show that archives generated by PclZip are readable by
//   WinZip application and other tools.
//
// Description :
//   See readme.txt and http://www.phpconcept.net
//
// Warning :
//   This library and the associated files are non commercial, non professional
//   work.
//   It should not have unexpected results. However if any damage is caused by
//   this software the author can not be responsible.
//   The use of this software is at the risk of the user.
//

  // personnalisations spip
  if (!defined('PCLZIP_TEMPORARY_DIR')) {
    define('PCLZIP_TEMPORARY_DIR', _DIR_TMP);
  }
  if (!function_exists('gzopen') && function_exists('gzopen64')) {
    function gzopen($filename, $mode, $use_include_path = 0) {
      return gzopen64($filename, $mode, $use_include_path);
    }
  }
  // fin personnalisations spip

  // ----- Constants
  if (!defined('PCLZIP_READ_BLOCK_SIZE')) {
    define( 'PCLZIP_READ_BLOCK_SIZE', 2048 );
  }
  
  // ----- File list separator
  // In version 1.x of PclZip, the separator for file list is a space
  // (which is not a very smart choice, specifically for windows paths !).
  // A better separator should be a comma (,). This constant gives you the
  // abilty to change that.
  // However notice that changing this value, may have impact on existing
  // scripts, using space separated filenames.
  // Recommanded values for compatibility with older versions :
  //define( 'PCLZIP_SEPARATOR', ' ' );
  // Recommanded values for smart separation of filenames.
  if (!defined('PCLZIP_SEPARATOR')) {
    define( 'PCLZIP_SEPARATOR', ',' );
  }

  // ----- Error configuration
  // 0 : PclZip Class integrated error handling
  // 1 : PclError external library error handling. By enabling this
  //     you must ensure that you have included PclError library.
  // [2,...] : reserved for futur use
  if (!defined('PCLZIP_ERROR_EXTERNAL')) {
    define( 'PCLZIP_ERROR_EXTERNAL', 0 );
  }

  // ----- Optional static temporary directory
  //       By default temporary files are generated in the script current
  //       path.
  //       If defined :
  //       - MUST BE terminated by a '/'.
  //       - MUST be a valid, already created directory
  //       Samples :
  // define( 'PCLZIP_TEMPORARY_DIR', '/temp/' );
  // define( 'PCLZIP_TEMPORARY_DIR', 'C:/Temp/' );
  if (!defined('PCLZIP_TEMPORARY_DIR')) {
    define( 'PCLZIP_TEMPORARY_DIR', '' );
  }

  // ----- Optional threshold ratio for use of temporary files
  //       Pclzip sense the size of the file to add/extract and decide to
  //       use or not temporary file. The algorythm is looking for 
  //       memory_limit of PHP and apply a ratio.
  //       threshold = memory_limit * ratio.
  //       Recommended values are under 0.5. Default 0.47.
  //       Samples :
  // define( 'PCLZIP_TEMPORARY_FILE_RATIO', 0.5 );
  if (!defined('PCLZIP_TEMPORARY_FILE_RATIO')) {
    define( 'PCLZIP_TEMPORARY_FILE_RATIO', 0.47 );
  }

// --------------------------------------------------------------------------------
// ***** UNDER THIS LINE NOTHING NEEDS TO BE MODIFIED *****
// --------------------------------------------------------------------------------

  // ----- Global variables
  $g_pclzip_version = "2.8.2";

  // ----- Error codes
  //   -1 : Unable to open file in binary write mode
  //   -2 : Unable to open file in binary read mode
  //   -3 : Invalid parameters
  //   -4 : File does not exist
  //   -5 : Filename is too long (max. 255)
  //   -6 : Not a valid zip file
  //   -7 : Invalid extracted file size
  //   -8 : Unable to create directory
  //   -9 : Invalid archive extension
  //  -10 : Invalid archive format
  //  -11 : Unable to delete file (unlink)
  //  -12 : Unable to rename file (rename)
  //  -13 : Invalid header checksum
  //  -14 : Invalid archive size
  define( 'PCLZIP_ERR_USER_ABORTED', 2 );
  define( 'PCLZIP_ERR_NO_ERROR', 0 );
  define( 'PCLZIP_ERR_WRITE_OPEN_FAIL', -1 );
  define( 'PCLZIP_ERR_READ_OPEN_FAIL', -2 );
  define( 'PCLZIP_ERR_INVALID_PARAMETER', -3 );
  define( 'PCLZIP_ERR_MISSING_FILE', -4 );
  define( 'PCLZIP_ERR_FILENAME_TOO_LONG', -5 );
  define( 'PCLZIP_ERR_INVALID_ZIP', -6 );
  define( 'PCLZIP_ERR_BAD_EXTRACTED_FILE', -7 );
  define( 'PCLZIP_ERR_DIR_CREATE_FAIL', -8 );
  define( 'PCLZIP_ERR_BAD_EXTENSION', -9 );
  define( 'PCLZIP_ERR_BAD_FORMAT', -10 );
  define( 'PCLZIP_ERR_DELETE_FILE_FAIL', -11 );
  define( 'PCLZIP_ERR_RENAME_FILE_FAIL', -12 );
  define( 'PCLZIP_ERR_BAD_CHECKSUM', -13 );
  define( 'PCLZIP_ERR_INVALID_ARCHIVE_ZIP', -14 );
  define( 'PCLZIP_ERR_MISSING_OPTION_VALUE', -15 );
  define( 'PCLZIP_ERR_INVALID_OPTION_VALUE', -16 );
  define( 'PCLZIP_ERR_ALREADY_A_DIRECTORY', -17 );
  define( 'PCLZIP_ERR_UNSUPPORTED_COMPRESSION', -18 );
  define( 'PCLZIP_ERR_UNSUPPORTED_ENCRYPTION', -19 );
  define( 'PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE', -20 );
  define( 'PCLZIP_ERR_DIRECTORY_RESTRICTION', -21 );

  // ----- Options values
  define( 'PCLZIP_OPT_PATH', 77001 );
  define( 'PCLZIP_OPT_ADD_PATH', 77002 );
  define( 'PCLZIP_OPT_REMOVE_PATH', 77003 );
  define( 'PCLZIP_OPT_REMOVE_ALL_PATH', 77004 );
  define( 'PCLZIP_OPT_SET_CHMOD', 77005 );
  define( 'PCLZIP_OPT_EXTRACT_AS_STRING', 77006 );
  define( 'PCLZIP_OPT_NO_COMPRESSION', 77007 );
  define( 'PCLZIP_OPT_BY_NAME', 77008 );
  define( 'PCLZIP_OPT_BY_INDEX', 77009 );
  define( 'PCLZIP_OPT_BY_EREG', 77010 );
  define( 'PCLZIP_OPT_BY_PREG', 77011 );
  define( 'PCLZIP_OPT_COMMENT', 77012 );
  define( 'PCLZIP_OPT_ADD_COMMENT', 77013 );
  define( 'PCLZIP_OPT_PREPEND_COMMENT', 77014 );
  define( 'PCLZIP_OPT_EXTRACT_IN_OUTPUT', 77015 );
  define( 'PCLZIP_OPT_REPLACE_NEWER', 77016 );
  define( 'PCLZIP_OPT_STOP_ON_ERROR', 77017 );
  // Having big trouble with crypt. Need to multiply 2 long int
  // which is not correctly supported by PHP ...
  //define( 'PCLZIP_OPT_CRYPT', 77018 );
  define( 'PCLZIP_OPT_EXTRACT_DIR_RESTRICTION', 77019 );
  define( 'PCLZIP_OPT_TEMP_FILE_THRESHOLD', 77020 );
  define( 'PCLZIP_OPT_ADD_TEMP_FILE_THRESHOLD', 77020 ); // alias
  define( 'PCLZIP_OPT_TEMP_FILE_ON', 77021 );
  define( 'PCLZIP_OPT_ADD_TEMP_FILE_ON', 77021 ); // alias
  define( 'PCLZIP_OPT_TEMP_FILE_OFF', 77022 );
  define( 'PCLZIP_OPT_ADD_TEMP_FILE_OFF', 77022 ); // alias
  
  // ----- File description attributes
  define( 'PCLZIP_ATT_FILE_NAME', 79001 );
  define( 'PCLZIP_ATT_FILE_NEW_SHORT_NAME', 79002 );
  define( 'PCLZIP_ATT_FILE_NEW_FULL_NAME', 79003 );
  define( 'PCLZIP_ATT_FILE_MTIME', 79004 );
  define( 'PCLZIP_ATT_FILE_CONTENT', 79005 );
  define( 'PCLZIP_ATT_FILE_COMMENT', 79006 );

  // ----- Call backs values
  define( 'PCLZIP_CB_PRE_EXTRACT', 78001 );
  define( 'PCLZIP_CB_POST_EXTRACT', 78002 );
  define( 'PCLZIP_CB_PRE_ADD', 78003 );
  define( 'PCLZIP_CB_POST_ADD', 78004 );
  /* For futur use
  define( 'PCLZIP_CB_PRE_LIST', 78005 );
  define( 'PCLZIP_CB_POST_LIST', 78006 );
  define( 'PCLZIP_CB_PRE_DELETE', 78007 );
  define( 'PCLZIP_CB_POST_DELETE', 78008 );
  */

  // --------------------------------------------------------------------------------
  // Class : PclZip
  // Description :
  //   PclZip is the class that represent a Zip archive.
  //   The public methods allow the manipulation of the archive.
  // Attributes :
  //   Attributes must not be accessed directly.
  // Methods :
  //   PclZip() : Object creator
  //   create() : Creates the Zip archive
  //   listContent() : List the content of the Zip archive
  //   extract() : Extract the content of the archive
  //   properties() : List the properties of the archive
  // --------------------------------------------------------------------------------
  class PclZip
  {
    // ----- Filename of the zip file
    var $zipname = '';

    // ----- File descriptor of the zip file
    var $zip_fd = 0;

    // ----- Internal error handling
    var $error_code = 1;
    var $error_string = '';
    
    // ----- Current status of the magic_quotes_runtime
    // This value store the php configuration for magic_quotes
    // The class can then disable the magic_quotes and reset it after
    var $magic_quotes_status;

  // --------------------------------------------------------------------------------
  // Function : PclZip()
  // Description :
  //   Creates a PclZip object and set the name of the associated Zip archive
  //   filename.
  //   Note that no real action is taken, if the archive does not exist it is not
  //   created. Use create() for that.
  // --------------------------------------------------------------------------------
  function PclZip($p_zipname)
  {

    // ----- Tests the zlib
    if (!function_exists('gzopen'))
    {
      die('Abort '.basename(__FILE__).' : Missing zlib extensions');
    }

    // ----- Set the attributes
    $this->zipname = $p_zipname;
    $this->zip_fd = 0;
    $this->magic_quotes_status = -1;

    // ----- Return
    return;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function :
  //   create($p_filelist, $p_add_dir="", $p_remove_dir="")
  //   create($p_filelist, $p_option, $p_option_value, ...)
  // Description :
  //   This method supports two different synopsis. The first one is historical.
  //   This method creates a Zip Archive. The Zip file is created in the
  //   filesystem. The files and directories indicated in $p_filelist
  //   are added in the archive. See the parameters description for the
  //   supported format of $p_filelist.
  //   When a directory is in the list, the directory and its content is added
  //   in the archive.
  //   In this synopsis, the function takes an optional variable list of
  //   options. See bellow the supported options.
  // Parameters :
  //   $p_filelist : An array containing file or directory names, or
  //                 a string containing one filename or one directory name, or
  //                 a string containing a list of filenames and/or directory
  //                 names separated by spaces.
  //   $p_add_dir : A path to add before the real path of the archived file,
  //                in order to have it memorized in the archive.
  //   $p_remove_dir : A path to remove from the real path of the file to archive,
  //                   in order to have a shorter path memorized in the archive.
  //                   When $p_add_dir and $p_remove_dir are set, $p_remove_dir
  //                   is removed first, before $p_add_dir is added.
  // Options :
  //   PCLZIP_OPT_ADD_PATH :
  //   PCLZIP_OPT_REMOVE_PATH :
  //   PCLZIP_OPT_REMOVE_ALL_PATH :
  //   PCLZIP_OPT_COMMENT :
  //   PCLZIP_CB_PRE_ADD :
  //   PCLZIP_CB_POST_ADD :
  // Return Values :
  //   0 on failure,
  //   The list of the added files, with a status of the add action.
  //   (see PclZip::listContent() for list entry format)
  // --------------------------------------------------------------------------------
  function create($p_filelist)
  {
    $v_result=1;

    // ----- Reset the error handler
    $this->privErrorReset();

    // ----- Set default values
    $v_options = array();
    $v_options[PCLZIP_OPT_NO_COMPRESSION] = FALSE;

    // ----- Look for variable options arguments
    $v_size = func_num_args();

    // ----- Look for arguments
    if ($v_size > 1) {
      // ----- Get the arguments
      $v_arg_list = func_get_args();

      // ----- Remove from the options list the first argument
      array_shift($v_arg_list);
      $v_size--;

      // ----- Look for first arg
      if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) {

        // ----- Parse the options
        $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options,
                                            array (PCLZIP_OPT_REMOVE_PATH => 'optional',
                                                   PCLZIP_OPT_REMOVE_ALL_PATH => 'optional',
                                                   PCLZIP_OPT_ADD_PATH => 'optional',
                                                   PCLZIP_CB_PRE_ADD => 'optional',
                                                   PCLZIP_CB_POST_ADD => 'optional',
                                                   PCLZIP_OPT_NO_COMPRESSION => 'optional',
                                                   PCLZIP_OPT_COMMENT => 'optional',
                                                   PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional',
                                                   PCLZIP_OPT_TEMP_FILE_ON => 'optional',
                                                   PCLZIP_OPT_TEMP_FILE_OFF => 'optional'
                                                   //, PCLZIP_OPT_CRYPT => 'optional'
                                             ));
        if ($v_result != 1) {
          return 0;
        }
      }

      // ----- Look for 2 args
      // Here we need to support the first historic synopsis of the
      // method.
      else {

        // ----- Get the first argument
        $v_options[PCLZIP_OPT_ADD_PATH] = $v_arg_list[0];

        // ----- Look for the optional second argument
        if ($v_size == 2) {
          $v_options[PCLZIP_OPT_REMOVE_PATH] = $v_arg_list[1];
        }
        else if ($v_size > 2) {
          PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER,
		                       "Invalid number / type of arguments");
          return 0;
        }
      }
    }
    
    // ----- Look for default option values
    $this->privOptionDefaultThreshold($v_options);

    // ----- Init
    $v_string_list = array();
    $v_att_list = array();
    $v_filedescr_list = array();
    $p_result_list = array();
    
    // ----- Look if the $p_filelist is really an array
    if (is_array($p_filelist)) {
    
      // ----- Look if the first element is also an array
      //       This will mean that this is a file description entry
      if (isset($p_filelist[0]) && is_array($p_filelist[0])) {
        $v_att_list = $p_filelist;
      }
      
      // ----- The list is a list of string names
      else {
        $v_string_list = $p_filelist;
      }
    }

    // ----- Look if the $p_filelist is a string
    else if (is_string($p_filelist)) {
      // ----- Create a list from the string
      $v_string_list = explode(PCLZIP_SEPARATOR, $p_filelist);
    }

    // ----- Invalid variable type for $p_filelist
    else {
      PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_filelist");
      return 0;
    }
    
    // ----- Reformat the string list
    if (sizeof($v_string_list) != 0) {
      foreach ($v_string_list as $v_string) {
        if ($v_string != '') {
          $v_att_list[][PCLZIP_ATT_FILE_NAME] = $v_string;
        }
        else {
        }
      }
    }
    
    // ----- For each file in the list check the attributes
    $v_supported_attributes
    = array ( PCLZIP_ATT_FILE_NAME => 'mandatory'
             ,PCLZIP_ATT_FILE_NEW_SHORT_NAME => 'optional'
             ,PCLZIP_ATT_FILE_NEW_FULL_NAME => 'optional'
             ,PCLZIP_ATT_FILE_MTIME => 'optional'
             ,PCLZIP_ATT_FILE_CONTENT => 'optional'
             ,PCLZIP_ATT_FILE_COMMENT => 'optional'
						);
    foreach ($v_att_list as $v_entry) {
      $v_result = $this->privFileDescrParseAtt($v_entry,
                                               $v_filedescr_list[],
                                               $v_options,
                                               $v_supported_attributes);
      if ($v_result != 1) {
        return 0;
      }
    }

    // ----- Expand the filelist (expand directories)
    $v_result = $this->privFileDescrExpand($v_filedescr_list, $v_options);
    if ($v_result != 1) {
      return 0;
    }

    // ----- Call the create fct
    $v_result = $this->privCreate($v_filedescr_list, $p_result_list, $v_options);
    if ($v_result != 1) {
      return 0;
    }

    // ----- Return
    return $p_result_list;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function :
  //   add($p_filelist, $p_add_dir="", $p_remove_dir="")
  //   add($p_filelist, $p_option, $p_option_value, ...)
  // Description :
  //   This method supports two synopsis. The first one is historical.
  //   This methods add the list of files in an existing archive.
  //   If a file with the same name already exists, it is added at the end of the
  //   archive, the first one is still present.
  //   If the archive does not exist, it is created.
  // Parameters :
  //   $p_filelist : An array containing file or directory names, or
  //                 a string containing one filename or one directory name, or
  //                 a string containing a list of filenames and/or directory
  //                 names separated by spaces.
  //   $p_add_dir : A path to add before the real path of the archived file,
  //                in order to have it memorized in the archive.
  //   $p_remove_dir : A path to remove from the real path of the file to archive,
  //                   in order to have a shorter path memorized in the archive.
  //                   When $p_add_dir and $p_remove_dir are set, $p_remove_dir
  //                   is removed first, before $p_add_dir is added.
  // Options :
  //   PCLZIP_OPT_ADD_PATH :
  //   PCLZIP_OPT_REMOVE_PATH :
  //   PCLZIP_OPT_REMOVE_ALL_PATH :
  //   PCLZIP_OPT_COMMENT :
  //   PCLZIP_OPT_ADD_COMMENT :
  //   PCLZIP_OPT_PREPEND_COMMENT :
  //   PCLZIP_CB_PRE_ADD :
  //   PCLZIP_CB_POST_ADD :
  // Return Values :
  //   0 on failure,
  //   The list of the added files, with a status of the add action.
  //   (see PclZip::listContent() for list entry format)
  // --------------------------------------------------------------------------------
  function add($p_filelist)
  {
    $v_result=1;

    // ----- Reset the error handler
    $this->privErrorReset();

    // ----- Set default values
    $v_options = array();
    $v_options[PCLZIP_OPT_NO_COMPRESSION] = FALSE;

    // ----- Look for variable options arguments
    $v_size = func_num_args();

    // ----- Look for arguments
    if ($v_size > 1) {
      // ----- Get the arguments
      $v_arg_list = func_get_args();

      // ----- Remove form the options list the first argument
      array_shift($v_arg_list);
      $v_size--;

      // ----- Look for first arg
      if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) {

        // ----- Parse the options
        $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options,
                                            array (PCLZIP_OPT_REMOVE_PATH => 'optional',
                                                   PCLZIP_OPT_REMOVE_ALL_PATH => 'optional',
                                                   PCLZIP_OPT_ADD_PATH => 'optional',
                                                   PCLZIP_CB_PRE_ADD => 'optional',
                                                   PCLZIP_CB_POST_ADD => 'optional',
                                                   PCLZIP_OPT_NO_COMPRESSION => 'optional',
                                                   PCLZIP_OPT_COMMENT => 'optional',
                                                   PCLZIP_OPT_ADD_COMMENT => 'optional',
                                                   PCLZIP_OPT_PREPEND_COMMENT => 'optional',
                                                   PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional',
                                                   PCLZIP_OPT_TEMP_FILE_ON => 'optional',
                                                   PCLZIP_OPT_TEMP_FILE_OFF => 'optional'
                                                   //, PCLZIP_OPT_CRYPT => 'optional'
												   ));
        if ($v_result != 1) {
          return 0;
        }
      }

      // ----- Look for 2 args
      // Here we need to support the first historic synopsis of the
      // method.
      else {

        // ----- Get the first argument
        $v_options[PCLZIP_OPT_ADD_PATH] = $v_add_path = $v_arg_list[0];

        // ----- Look for the optional second argument
        if ($v_size == 2) {
          $v_options[PCLZIP_OPT_REMOVE_PATH] = $v_arg_list[1];
        }
        else if ($v_size > 2) {
          // ----- Error log
          PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments");

          // ----- Return
          return 0;
        }
      }
    }

    // ----- Look for default option values
    $this->privOptionDefaultThreshold($v_options);

    // ----- Init
    $v_string_list = array();
    $v_att_list = array();
    $v_filedescr_list = array();
    $p_result_list = array();
    
    // ----- Look if the $p_filelist is really an array
    if (is_array($p_filelist)) {
    
      // ----- Look if the first element is also an array
      //       This will mean that this is a file description entry
      if (isset($p_filelist[0]) && is_array($p_filelist[0])) {
        $v_att_list = $p_filelist;
      }
      
      // ----- The list is a list of string names
      else {
        $v_string_list = $p_filelist;
      }
    }

    // ----- Look if the $p_filelist is a string
    else if (is_string($p_filelist)) {
      // ----- Create a list from the string
      $v_string_list = explode(PCLZIP_SEPARATOR, $p_filelist);
    }

    // ----- Invalid variable type for $p_filelist
    else {
      PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type '".gettype($p_filelist)."' for p_filelist");
      return 0;
    }
    
    // ----- Reformat the string list
    if (sizeof($v_string_list) != 0) {
      foreach ($v_string_list as $v_string) {
        $v_att_list[][PCLZIP_ATT_FILE_NAME] = $v_string;
      }
    }
    
    // ----- For each file in the list check the attributes
    $v_supported_attributes
    = array ( PCLZIP_ATT_FILE_NAME => 'mandatory'
             ,PCLZIP_ATT_FILE_NEW_SHORT_NAME => 'optional'
             ,PCLZIP_ATT_FILE_NEW_FULL_NAME => 'optional'
             ,PCLZIP_ATT_FILE_MTIME => 'optional'
             ,PCLZIP_ATT_FILE_CONTENT => 'optional'
             ,PCLZIP_ATT_FILE_COMMENT => 'optional'
						);
    foreach ($v_att_list as $v_entry) {
      $v_result = $this->privFileDescrParseAtt($v_entry,
                                               $v_filedescr_list[],
                                               $v_options,
                                               $v_supported_attributes);
      if ($v_result != 1) {
        return 0;
      }
    }

    // ----- Expand the filelist (expand directories)
    $v_result = $this->privFileDescrExpand($v_filedescr_list, $v_options);
    if ($v_result != 1) {
      return 0;
    }

    // ----- Call the create fct
    $v_result = $this->privAdd($v_filedescr_list, $p_result_list, $v_options);
    if ($v_result != 1) {
      return 0;
    }

    // ----- Return
    return $p_result_list;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : listContent()
  // Description :
  //   This public method, gives the list of the files and directories, with their
  //   properties.
  //   The properties of each entries in the list are (used also in other functions) :
  //     filename : Name of the file. For a create or add action it is the filename
  //                given by the user. For an extract function it is the filename
  //                of the extracted file.
  //     stored_filename : Name of the file / directory stored in the archive.
  //     size : Size of the stored file.
  //     compressed_size : Size of the file's data compressed in the archive
  //                       (without the headers overhead)
  //     mtime : Last known modification date of the file (UNIX timestamp)
  //     comment : Comment associated with the file
  //     folder : true | false
  //     index : index of the file in the archive
  //     status : status of the action (depending of the action) :
  //              Values are :
  //                ok : OK !
  //                filtered : the file / dir is not extracted (filtered by user)
  //                already_a_directory : the file can not be extracted because a
  //                                      directory with the same name already exists
  //                write_protected : the file can not be extracted because a file
  //                                  with the same name already exists and is
  //                                  write protected
  //                newer_exist : the file was not extracted because a newer file exists
  //                path_creation_fail : the file is not extracted because the folder
  //                                     does not exist and can not be created
  //                write_error : the file was not extracted because there was a
  //                              error while writing the file
  //                read_error : the file was not extracted because there was a error
  //                             while reading the file
  //                invalid_header : the file was not extracted because of an archive
  //                                 format error (bad file header)
  //   Note that each time a method can continue operating when there
  //   is an action error on a file, the error is only logged in the file status.
  // Return Values :
  //   0 on an unrecoverable failure,
  //   The list of the files in the archive.
  // --------------------------------------------------------------------------------
  function listContent()
  {
    $v_result=1;

    // ----- Reset the error handler
    $this->privErrorReset();

    // ----- Check archive
    if (!$this->privCheckFormat()) {
      return(0);
    }

    // ----- Call the extracting fct
    $p_list = array();
    if (($v_result = $this->privList($p_list)) != 1)
    {
      unset($p_list);
      return(0);
    }

    // ----- Return
    return $p_list;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function :
  //   extract($p_path="./", $p_remove_path="")
  //   extract([$p_option, $p_option_value, ...])
  // Description :
  //   This method supports two synopsis. The first one is historical.
  //   This method extract all the files / directories from the archive to the
  //   folder indicated in $p_path.
  //   If you want to ignore the 'root' part of path of the memorized files
  //   you can indicate this in the optional $p_remove_path parameter.
  //   By default, if a newer file with the same name already exists, the
  //   file is not extracted.
  //
  //   If both PCLZIP_OPT_PATH and PCLZIP_OPT_ADD_PATH aoptions
  //   are used, the path indicated in PCLZIP_OPT_ADD_PATH is append
  //   at the end of the path value of PCLZIP_OPT_PATH.
  // Parameters :
  //   $p_path : Path where the files and directories are to be extracted
  //   $p_remove_path : First part ('root' part) of the memorized path
  //                    (if any similar) to remove while extracting.
  // Options :
  //   PCLZIP_OPT_PATH :
  //   PCLZIP_OPT_ADD_PATH :
  //   PCLZIP_OPT_REMOVE_PATH :
  //   PCLZIP_OPT_REMOVE_ALL_PATH :
  //   PCLZIP_CB_PRE_EXTRACT :
  //   PCLZIP_CB_POST_EXTRACT :
  // Return Values :
  //   0 or a negative value on failure,
  //   The list of the extracted files, with a status of the action.
  //   (see PclZip::listContent() for list entry format)
  // --------------------------------------------------------------------------------
  function extract()
  {
    $v_result=1;

    // ----- Reset the error handler
    $this->privErrorReset();

    // ----- Check archive
    if (!$this->privCheckFormat()) {
      return(0);
    }

    // ----- Set default values
    $v_options = array();
//    $v_path = "./";
    $v_path = '';
    $v_remove_path = "";
    $v_remove_all_path = false;

    // ----- Look for variable options arguments
    $v_size = func_num_args();

    // ----- Default values for option
    $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE;

    // ----- Look for arguments
    if ($v_size > 0) {
      // ----- Get the arguments
      $v_arg_list = func_get_args();

      // ----- Look for first arg
      if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) {

        // ----- Parse the options
        $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options,
                                            array (PCLZIP_OPT_PATH => 'optional',
                                                   PCLZIP_OPT_REMOVE_PATH => 'optional',
                                                   PCLZIP_OPT_REMOVE_ALL_PATH => 'optional',
                                                   PCLZIP_OPT_ADD_PATH => 'optional',
                                                   PCLZIP_CB_PRE_EXTRACT => 'optional',
                                                   PCLZIP_CB_POST_EXTRACT => 'optional',
                                                   PCLZIP_OPT_SET_CHMOD => 'optional',
                                                   PCLZIP_OPT_BY_NAME => 'optional',
                                                   PCLZIP_OPT_BY_EREG => 'optional',
                                                   PCLZIP_OPT_BY_PREG => 'optional',
                                                   PCLZIP_OPT_BY_INDEX => 'optional',
                                                   PCLZIP_OPT_EXTRACT_AS_STRING => 'optional',
                                                   PCLZIP_OPT_EXTRACT_IN_OUTPUT => 'optional',
                                                   PCLZIP_OPT_REPLACE_NEWER => 'optional'
                                                   ,PCLZIP_OPT_STOP_ON_ERROR => 'optional'
                                                   ,PCLZIP_OPT_EXTRACT_DIR_RESTRICTION => 'optional',
                                                   PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional',
                                                   PCLZIP_OPT_TEMP_FILE_ON => 'optional',
                                                   PCLZIP_OPT_TEMP_FILE_OFF => 'optional'
												    ));
        if ($v_result != 1) {
          return 0;
        }

        // ----- Set the arguments
        if (isset($v_options[PCLZIP_OPT_PATH])) {
          $v_path = $v_options[PCLZIP_OPT_PATH];
        }
        if (isset($v_options[PCLZIP_OPT_REMOVE_PATH])) {
          $v_remove_path = $v_options[PCLZIP_OPT_REMOVE_PATH];
        }
        if (isset($v_options[PCLZIP_OPT_REMOVE_ALL_PATH])) {
          $v_remove_all_path = $v_options[PCLZIP_OPT_REMOVE_ALL_PATH];
        }
        if (isset($v_options[PCLZIP_OPT_ADD_PATH])) {
          // ----- Check for '/' in last path char
          if ((strlen($v_path) > 0) && (substr($v_path, -1) != '/')) {
            $v_path .= '/';
          }
          $v_path .= $v_options[PCLZIP_OPT_ADD_PATH];
        }
      }

      // ----- Look for 2 args
      // Here we need to support the first historic synopsis of the
      // method.
      else {

        // ----- Get the first argument
        $v_path = $v_arg_list[0];

        // ----- Look for the optional second argument
        if ($v_size == 2) {
          $v_remove_path = $v_arg_list[1];
        }
        else if ($v_size > 2) {
          // ----- Error log
          PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments");

          // ----- Return
          return 0;
        }
      }
    }

    // ----- Look for default option values
    $this->privOptionDefaultThreshold($v_options);

    // ----- Trace

    // ----- Call the extracting fct
    $p_list = array();
    $v_result = $this->privExtractByRule($p_list, $v_path, $v_remove_path,
	                                     $v_remove_all_path, $v_options);
    if ($v_result < 1) {
      unset($p_list);
      return(0);
    }

    // ----- Return
    return $p_list;
  }
  // --------------------------------------------------------------------------------


  // --------------------------------------------------------------------------------
  // Function :
  //   extractByIndex($p_index, $p_path="./", $p_remove_path="")
  //   extractByIndex($p_index, [$p_option, $p_option_value, ...])
  // Description :
  //   This method supports two synopsis. The first one is historical.
  //   This method is doing a partial extract of the archive.
  //   The extracted files or folders are identified by their index in the
  //   archive (from 0 to n).
  //   Note that if the index identify a folder, only the folder entry is
  //   extracted, not all the files included in the archive.
  // Parameters :
  //   $p_index : A single index (integer) or a string of indexes of files to
  //              extract. The form of the string is "0,4-6,8-12" with only numbers
  //              and '-' for range or ',' to separate ranges. No spaces or ';'
  //              are allowed.
  //   $p_path : Path where the files and directories are to be extracted
  //   $p_remove_path : First part ('root' part) of the memorized path
  //                    (if any similar) to remove while extracting.
  // Options :
  //   PCLZIP_OPT_PATH :
  //   PCLZIP_OPT_ADD_PATH :
  //   PCLZIP_OPT_REMOVE_PATH :
  //   PCLZIP_OPT_REMOVE_ALL_PATH :
  //   PCLZIP_OPT_EXTRACT_AS_STRING : The files are extracted as strings and
  //     not as files.
  //     The resulting content is in a new field 'content' in the file
  //     structure.
  //     This option must be used alone (any other options are ignored).
  //   PCLZIP_CB_PRE_EXTRACT :
  //   PCLZIP_CB_POST_EXTRACT :
  // Return Values :
  //   0 on failure,
  //   The list of the extracted files, with a status of the action.
  //   (see PclZip::listContent() for list entry format)
  // --------------------------------------------------------------------------------
  //function extractByIndex($p_index, options...)
  function extractByIndex($p_index)
  {
    $v_result=1;

    // ----- Reset the error handler
    $this->privErrorReset();

    // ----- Check archive
    if (!$this->privCheckFormat()) {
      return(0);
    }

    // ----- Set default values
    $v_options = array();
//    $v_path = "./";
    $v_path = '';
    $v_remove_path = "";
    $v_remove_all_path = false;

    // ----- Look for variable options arguments
    $v_size = func_num_args();

    // ----- Default values for option
    $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE;

    // ----- Look for arguments
    if ($v_size > 1) {
      // ----- Get the arguments
      $v_arg_list = func_get_args();

      // ----- Remove form the options list the first argument
      array_shift($v_arg_list);
      $v_size--;

      // ----- Look for first arg
      if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) {

        // ----- Parse the options
        $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options,
                                            array (PCLZIP_OPT_PATH => 'optional',
                                                   PCLZIP_OPT_REMOVE_PATH => 'optional',
                                                   PCLZIP_OPT_REMOVE_ALL_PATH => 'optional',
                                                   PCLZIP_OPT_EXTRACT_AS_STRING => 'optional',
                                                   PCLZIP_OPT_ADD_PATH => 'optional',
                                                   PCLZIP_CB_PRE_EXTRACT => 'optional',
                                                   PCLZIP_CB_POST_EXTRACT => 'optional',
                                                   PCLZIP_OPT_SET_CHMOD => 'optional',
                                                   PCLZIP_OPT_REPLACE_NEWER => 'optional'
                                                   ,PCLZIP_OPT_STOP_ON_ERROR => 'optional'
                                                   ,PCLZIP_OPT_EXTRACT_DIR_RESTRICTION => 'optional',
                                                   PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional',
                                                   PCLZIP_OPT_TEMP_FILE_ON => 'optional',
                                                   PCLZIP_OPT_TEMP_FILE_OFF => 'optional'
												   ));
        if ($v_result != 1) {
          return 0;
        }

        // ----- Set the arguments
        if (isset($v_options[PCLZIP_OPT_PATH])) {
          $v_path = $v_options[PCLZIP_OPT_PATH];
        }
        if (isset($v_options[PCLZIP_OPT_REMOVE_PATH])) {
          $v_remove_path = $v_options[PCLZIP_OPT_REMOVE_PATH];
        }
        if (isset($v_options[PCLZIP_OPT_REMOVE_ALL_PATH])) {
          $v_remove_all_path = $v_options[PCLZIP_OPT_REMOVE_ALL_PATH];
        }
        if (isset($v_options[PCLZIP_OPT_ADD_PATH])) {
          // ----- Check for '/' in last path char
          if ((strlen($v_path) > 0) && (substr($v_path, -1) != '/')) {
            $v_path .= '/';
          }
          $v_path .= $v_options[PCLZIP_OPT_ADD_PATH];
        }
        if (!isset($v_options[PCLZIP_OPT_EXTRACT_AS_STRING])) {
          $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE;
        }
        else {
        }
      }

      // ----- Look for 2 args
      // Here we need to support the first historic synopsis of the
      // method.
      else {

        // ----- Get the first argument
        $v_path = $v_arg_list[0];

        // ----- Look for the optional second argument
        if ($v_size == 2) {
          $v_remove_path = $v_arg_list[1];
        }
        else if ($v_size > 2) {
          // ----- Error log
          PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments");

          // ----- Return
          return 0;
        }
      }
    }

    // ----- Trace

    // ----- Trick
    // Here I want to reuse extractByRule(), so I need to parse the $p_index
    // with privParseOptions()
    $v_arg_trick = array (PCLZIP_OPT_BY_INDEX, $p_index);
    $v_options_trick = array();
    $v_result = $this->privParseOptions($v_arg_trick, sizeof($v_arg_trick), $v_options_trick,
                                        array (PCLZIP_OPT_BY_INDEX => 'optional' ));
    if ($v_result != 1) {
        return 0;
    }
    $v_options[PCLZIP_OPT_BY_INDEX] = $v_options_trick[PCLZIP_OPT_BY_INDEX];

    // ----- Look for default option values
    $this->privOptionDefaultThreshold($v_options);

    // ----- Call the extracting fct
    if (($v_result = $this->privExtractByRule($p_list, $v_path, $v_remove_path, $v_remove_all_path, $v_options)) < 1) {
        return(0);
    }

    // ----- Return
    return $p_list;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function :
  //   delete([$p_option, $p_option_value, ...])
  // Description :
  //   This method removes files from the archive.
  //   If no parameters are given, then all the archive is emptied.
  // Parameters :
  //   None or optional arguments.
  // Options :
  //   PCLZIP_OPT_BY_INDEX :
  //   PCLZIP_OPT_BY_NAME :
  //   PCLZIP_OPT_BY_EREG : 
  //   PCLZIP_OPT_BY_PREG :
  // Return Values :
  //   0 on failure,
  //   The list of the files which are still present in the archive.
  //   (see PclZip::listContent() for list entry format)
  // --------------------------------------------------------------------------------
  function delete()
  {
    $v_result=1;

    // ----- Reset the error handler
    $this->privErrorReset();

    // ----- Check archive
    if (!$this->privCheckFormat()) {
      return(0);
    }

    // ----- Set default values
    $v_options = array();

    // ----- Look for variable options arguments
    $v_size = func_num_args();

    // ----- Look for arguments
    if ($v_size > 0) {
      // ----- Get the arguments
      $v_arg_list = func_get_args();

      // ----- Parse the options
      $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options,
                                        array (PCLZIP_OPT_BY_NAME => 'optional',
                                               PCLZIP_OPT_BY_EREG => 'optional',
                                               PCLZIP_OPT_BY_PREG => 'optional',
                                               PCLZIP_OPT_BY_INDEX => 'optional' ));
      if ($v_result != 1) {
          return 0;
      }
    }

    // ----- Magic quotes trick
    $this->privDisableMagicQuotes();

    // ----- Call the delete fct
    $v_list = array();
    if (($v_result = $this->privDeleteByRule($v_list, $v_options)) != 1) {
      $this->privSwapBackMagicQuotes();
      unset($v_list);
      return(0);
    }

    // ----- Magic quotes trick
    $this->privSwapBackMagicQuotes();

    // ----- Return
    return $v_list;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : deleteByIndex()
  // Description :
  //   ***** Deprecated *****
  //   delete(PCLZIP_OPT_BY_INDEX, $p_index) should be prefered.
  // --------------------------------------------------------------------------------
  function deleteByIndex($p_index)
  {
    
    $p_list = $this->delete(PCLZIP_OPT_BY_INDEX, $p_index);

    // ----- Return
    return $p_list;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : properties()
  // Description :
  //   This method gives the properties of the archive.
  //   The properties are :
  //     nb : Number of files in the archive
  //     comment : Comment associated with the archive file
  //     status : not_exist, ok
  // Parameters :
  //   None
  // Return Values :
  //   0 on failure,
  //   An array with the archive properties.
  // --------------------------------------------------------------------------------
  function properties()
  {

    // ----- Reset the error handler
    $this->privErrorReset();

    // ----- Magic quotes trick
    $this->privDisableMagicQuotes();

    // ----- Check archive
    if (!$this->privCheckFormat()) {
      $this->privSwapBackMagicQuotes();
      return(0);
    }

    // ----- Default properties
    $v_prop = array();
    $v_prop['comment'] = '';
    $v_prop['nb'] = 0;
    $v_prop['status'] = 'not_exist';

    // ----- Look if file exists
    if (@is_file($this->zipname))
    {
      // ----- Open the zip file
      if (($this->zip_fd = @fopen($this->zipname, 'rb')) == 0)
      {
        $this->privSwapBackMagicQuotes();
        
        // ----- Error log
        PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in binary read mode');

        // ----- Return
        return 0;
      }

      // ----- Read the central directory informations
      $v_central_dir = array();
      if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1)
      {
        $this->privSwapBackMagicQuotes();
        return 0;
      }

      // ----- Close the zip file
      $this->privCloseFd();

      // ----- Set the user attributes
      $v_prop['comment'] = $v_central_dir['comment'];
      $v_prop['nb'] = $v_central_dir['entries'];
      $v_prop['status'] = 'ok';
    }

    // ----- Magic quotes trick
    $this->privSwapBackMagicQuotes();

    // ----- Return
    return $v_prop;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : duplicate()
  // Description :
  //   This method creates an archive by copying the content of an other one. If
  //   the archive already exist, it is replaced by the new one without any warning.
  // Parameters :
  //   $p_archive : The filename of a valid archive, or
  //                a valid PclZip object.
  // Return Values :
  //   1 on success.
  //   0 or a negative value on error (error code).
  // --------------------------------------------------------------------------------
  function duplicate($p_archive)
  {
    $v_result = 1;

    // ----- Reset the error handler
    $this->privErrorReset();

    // ----- Look if the $p_archive is a PclZip object
    if ((is_object($p_archive)) && (get_class($p_archive) == 'pclzip'))
    {

      // ----- Duplicate the archive
      $v_result = $this->privDuplicate($p_archive->zipname);
    }

    // ----- Look if the $p_archive is a string (so a filename)
    else if (is_string($p_archive))
    {

      // ----- Check that $p_archive is a valid zip file
      // TBC : Should also check the archive format
      if (!is_file($p_archive)) {
        // ----- Error log
        PclZip::privErrorLog(PCLZIP_ERR_MISSING_FILE, "No file with filename '".$p_archive."'");
        $v_result = PCLZIP_ERR_MISSING_FILE;
      }
      else {
        // ----- Duplicate the archive
        $v_result = $this->privDuplicate($p_archive);
      }
    }

    // ----- Invalid variable
    else
    {
      // ----- Error log
      PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_archive_to_add");
      $v_result = PCLZIP_ERR_INVALID_PARAMETER;
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : merge()
  // Description :
  //   This method merge the $p_archive_to_add archive at the end of the current
  //   one ($this).
  //   If the archive ($this) does not exist, the merge becomes a duplicate.
  //   If the $p_archive_to_add archive does not exist, the merge is a success.
  // Parameters :
  //   $p_archive_to_add : It can be directly the filename of a valid zip archive,
  //                       or a PclZip object archive.
  // Return Values :
  //   1 on success,
  //   0 or negative values on error (see below).
  // --------------------------------------------------------------------------------
  function merge($p_archive_to_add)
  {
    $v_result = 1;

    // ----- Reset the error handler
    $this->privErrorReset();

    // ----- Check archive
    if (!$this->privCheckFormat()) {
      return(0);
    }

    // ----- Look if the $p_archive_to_add is a PclZip object
    if ((is_object($p_archive_to_add)) && (get_class($p_archive_to_add) == 'pclzip'))
    {

      // ----- Merge the archive
      $v_result = $this->privMerge($p_archive_to_add);
    }

    // ----- Look if the $p_archive_to_add is a string (so a filename)
    else if (is_string($p_archive_to_add))
    {

      // ----- Create a temporary archive
      $v_object_archive = new PclZip($p_archive_to_add);

      // ----- Merge the archive
      $v_result = $this->privMerge($v_object_archive);
    }

    // ----- Invalid variable
    else
    {
      // ----- Error log
      PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_archive_to_add");
      $v_result = PCLZIP_ERR_INVALID_PARAMETER;
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------



  // --------------------------------------------------------------------------------
  // Function : errorCode()
  // Description :
  // Parameters :
  // --------------------------------------------------------------------------------
  function errorCode()
  {
    if (PCLZIP_ERROR_EXTERNAL == 1) {
      return(PclErrorCode());
    }
    else {
      return($this->error_code);
    }
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : errorName()
  // Description :
  // Parameters :
  // --------------------------------------------------------------------------------
  function errorName($p_with_code=false)
  {
    $v_name = array ( PCLZIP_ERR_NO_ERROR => 'PCLZIP_ERR_NO_ERROR',
                      PCLZIP_ERR_WRITE_OPEN_FAIL => 'PCLZIP_ERR_WRITE_OPEN_FAIL',
                      PCLZIP_ERR_READ_OPEN_FAIL => 'PCLZIP_ERR_READ_OPEN_FAIL',
                      PCLZIP_ERR_INVALID_PARAMETER => 'PCLZIP_ERR_INVALID_PARAMETER',
                      PCLZIP_ERR_MISSING_FILE => 'PCLZIP_ERR_MISSING_FILE',
                      PCLZIP_ERR_FILENAME_TOO_LONG => 'PCLZIP_ERR_FILENAME_TOO_LONG',
                      PCLZIP_ERR_INVALID_ZIP => 'PCLZIP_ERR_INVALID_ZIP',
                      PCLZIP_ERR_BAD_EXTRACTED_FILE => 'PCLZIP_ERR_BAD_EXTRACTED_FILE',
                      PCLZIP_ERR_DIR_CREATE_FAIL => 'PCLZIP_ERR_DIR_CREATE_FAIL',
                      PCLZIP_ERR_BAD_EXTENSION => 'PCLZIP_ERR_BAD_EXTENSION',
                      PCLZIP_ERR_BAD_FORMAT => 'PCLZIP_ERR_BAD_FORMAT',
                      PCLZIP_ERR_DELETE_FILE_FAIL => 'PCLZIP_ERR_DELETE_FILE_FAIL',
                      PCLZIP_ERR_RENAME_FILE_FAIL => 'PCLZIP_ERR_RENAME_FILE_FAIL',
                      PCLZIP_ERR_BAD_CHECKSUM => 'PCLZIP_ERR_BAD_CHECKSUM',
                      PCLZIP_ERR_INVALID_ARCHIVE_ZIP => 'PCLZIP_ERR_INVALID_ARCHIVE_ZIP',
                      PCLZIP_ERR_MISSING_OPTION_VALUE => 'PCLZIP_ERR_MISSING_OPTION_VALUE',
                      PCLZIP_ERR_INVALID_OPTION_VALUE => 'PCLZIP_ERR_INVALID_OPTION_VALUE',
                      PCLZIP_ERR_UNSUPPORTED_COMPRESSION => 'PCLZIP_ERR_UNSUPPORTED_COMPRESSION',
                      PCLZIP_ERR_UNSUPPORTED_ENCRYPTION => 'PCLZIP_ERR_UNSUPPORTED_ENCRYPTION'
                      ,PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE => 'PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE'
                      ,PCLZIP_ERR_DIRECTORY_RESTRICTION => 'PCLZIP_ERR_DIRECTORY_RESTRICTION'
                    );

    if (isset($v_name[$this->error_code])) {
      $v_value = $v_name[$this->error_code];
    }
    else {
      $v_value = 'NoName';
    }

    if ($p_with_code) {
      return($v_value.' ('.$this->error_code.')');
    }
    else {
      return($v_value);
    }
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : errorInfo()
  // Description :
  // Parameters :
  // --------------------------------------------------------------------------------
  function errorInfo($p_full=false)
  {
    if (PCLZIP_ERROR_EXTERNAL == 1) {
      return(PclErrorString());
    }
    else {
      if ($p_full) {
        return($this->errorName(true)." : ".$this->error_string);
      }
      else {
        return($this->error_string." [code ".$this->error_code."]");
      }
    }
  }
  // --------------------------------------------------------------------------------


// --------------------------------------------------------------------------------
// ***** UNDER THIS LINE ARE DEFINED PRIVATE INTERNAL FUNCTIONS *****
// *****                                                        *****
// *****       THESES FUNCTIONS MUST NOT BE USED DIRECTLY       *****
// --------------------------------------------------------------------------------



  // --------------------------------------------------------------------------------
  // Function : privCheckFormat()
  // Description :
  //   This method check that the archive exists and is a valid zip archive.
  //   Several level of check exists. (futur)
  // Parameters :
  //   $p_level : Level of check. Default 0.
  //              0 : Check the first bytes (magic codes) (default value))
  //              1 : 0 + Check the central directory (futur)
  //              2 : 1 + Check each file header (futur)
  // Return Values :
  //   true on success,
  //   false on error, the error code is set.
  // --------------------------------------------------------------------------------
  function privCheckFormat($p_level=0)
  {
    $v_result = true;

	// ----- Reset the file system cache
    clearstatcache();

    // ----- Reset the error handler
    $this->privErrorReset();

    // ----- Look if the file exits
    if (!is_file($this->zipname)) {
      // ----- Error log
      PclZip::privErrorLog(PCLZIP_ERR_MISSING_FILE, "Missing archive file '".$this->zipname."'");
      return(false);
    }

    // ----- Check that the file is readeable
    if (!is_readable($this->zipname)) {
      // ----- Error log
      PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to read archive '".$this->zipname."'");
      return(false);
    }

    // ----- Check the magic code
    // TBC

    // ----- Check the central header
    // TBC

    // ----- Check each file header
    // TBC

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privParseOptions()
  // Description :
  //   This internal methods reads the variable list of arguments ($p_options_list,
  //   $p_size) and generate an array with the options and values ($v_result_list).
  //   $v_requested_options contains the options that can be present and those that
  //   must be present.
  //   $v_requested_options is an array, with the option value as key, and 'optional',
  //   or 'mandatory' as value.
  // Parameters :
  //   See above.
  // Return Values :
  //   1 on success.
  //   0 on failure.
  // --------------------------------------------------------------------------------
  function privParseOptions(&$p_options_list, $p_size, &$v_result_list, $v_requested_options=false)
  {
    $v_result=1;
    
    // ----- Read the options
    $i=0;
    while ($i<$p_size) {

      // ----- Check if the option is supported
      if (!isset($v_requested_options[$p_options_list[$i]])) {
        // ----- Error log
        PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid optional parameter '".$p_options_list[$i]."' for this method");

        // ----- Return
        return PclZip::errorCode();
      }

      // ----- Look for next option
      switch ($p_options_list[$i]) {
        // ----- Look for options that request a path value
        case PCLZIP_OPT_PATH :
        case PCLZIP_OPT_REMOVE_PATH :
        case PCLZIP_OPT_ADD_PATH :
          // ----- Check the number of parameters
          if (($i+1) >= $p_size) {
            // ----- Error log
            PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'");

            // ----- Return
            return PclZip::errorCode();
          }

          // ----- Get the value
          $v_result_list[$p_options_list[$i]] = PclZipUtilTranslateWinPath($p_options_list[$i+1], FALSE);
          $i++;
        break;

        case PCLZIP_OPT_TEMP_FILE_THRESHOLD :
          // ----- Check the number of parameters
          if (($i+1) >= $p_size) {
            PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'");
            return PclZip::errorCode();
          }
          
          // ----- Check for incompatible options
          if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_OFF])) {
            PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_OFF'");
            return PclZip::errorCode();
          }
          
          // ----- Check the value
          $v_value = $p_options_list[$i+1];
          if ((!is_integer($v_value)) || ($v_value<0)) {
            PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Integer expected for option '".PclZipUtilOptionText($p_options_list[$i])."'");
            return PclZip::errorCode();
          }

          // ----- Get the value (and convert it in bytes)
          $v_result_list[$p_options_list[$i]] = $v_value*1048576;
          $i++;
        break;

        case PCLZIP_OPT_TEMP_FILE_ON :
          // ----- Check for incompatible options
          if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_OFF])) {
            PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_OFF'");
            return PclZip::errorCode();
          }
          
          $v_result_list[$p_options_list[$i]] = true;
        break;

        case PCLZIP_OPT_TEMP_FILE_OFF :
          // ----- Check for incompatible options
          if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_ON])) {
            PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_ON'");
            return PclZip::errorCode();
          }
          // ----- Check for incompatible options
          if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_THRESHOLD])) {
            PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_THRESHOLD'");
            return PclZip::errorCode();
          }
          
          $v_result_list[$p_options_list[$i]] = true;
        break;

        case PCLZIP_OPT_EXTRACT_DIR_RESTRICTION :
          // ----- Check the number of parameters
          if (($i+1) >= $p_size) {
            // ----- Error log
            PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'");

            // ----- Return
            return PclZip::errorCode();
          }

          // ----- Get the value
          if (   is_string($p_options_list[$i+1])
              && ($p_options_list[$i+1] != '')) {
            $v_result_list[$p_options_list[$i]] = PclZipUtilTranslateWinPath($p_options_list[$i+1], FALSE);
            $i++;
          }
          else {
          }
        break;

        // ----- Look for options that request an array of string for value
        case PCLZIP_OPT_BY_NAME :
          // ----- Check the number of parameters
          if (($i+1) >= $p_size) {
            // ----- Error log
            PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'");

            // ----- Return
            return PclZip::errorCode();
          }

          // ----- Get the value
          if (is_string($p_options_list[$i+1])) {
              $v_result_list[$p_options_list[$i]][0] = $p_options_list[$i+1];
          }
          else if (is_array($p_options_list[$i+1])) {
              $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1];
          }
          else {
            // ----- Error log
            PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Wrong parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'");

            // ----- Return
            return PclZip::errorCode();
          }
          $i++;
        break;

        // ----- Look for options that request an EREG or PREG expression
        case PCLZIP_OPT_BY_EREG :
          // ereg() is deprecated starting with PHP 5.3. Move PCLZIP_OPT_BY_EREG
          // to PCLZIP_OPT_BY_PREG
          $p_options_list[$i] = PCLZIP_OPT_BY_PREG;
        case PCLZIP_OPT_BY_PREG :
        //case PCLZIP_OPT_CRYPT :
          // ----- Check the number of parameters
          if (($i+1) >= $p_size) {
            // ----- Error log
            PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'");

            // ----- Return
            return PclZip::errorCode();
          }

          // ----- Get the value
          if (is_string($p_options_list[$i+1])) {
              $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1];
          }
          else {
            // ----- Error log
            PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Wrong parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'");

            // ----- Return
            return PclZip::errorCode();
          }
          $i++;
        break;

        // ----- Look for options that takes a string
        case PCLZIP_OPT_COMMENT :
        case PCLZIP_OPT_ADD_COMMENT :
        case PCLZIP_OPT_PREPEND_COMMENT :
          // ----- Check the number of parameters
          if (($i+1) >= $p_size) {
            // ----- Error log
            PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE,
			                     "Missing parameter value for option '"
								 .PclZipUtilOptionText($p_options_list[$i])
								 ."'");

            // ----- Return
            return PclZip::errorCode();
          }

          // ----- Get the value
          if (is_string($p_options_list[$i+1])) {
              $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1];
          }
          else {
            // ----- Error log
            PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE,
			                     "Wrong parameter value for option '"
								 .PclZipUtilOptionText($p_options_list[$i])
								 ."'");

            // ----- Return
            return PclZip::errorCode();
          }
          $i++;
        break;

        // ----- Look for options that request an array of index
        case PCLZIP_OPT_BY_INDEX :
          // ----- Check the number of parameters
          if (($i+1) >= $p_size) {
            // ----- Error log
            PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'");

            // ----- Return
            return PclZip::errorCode();
          }

          // ----- Get the value
          $v_work_list = array();
          if (is_string($p_options_list[$i+1])) {

              // ----- Remove spaces
              $p_options_list[$i+1] = strtr($p_options_list[$i+1], ' ', '');

              // ----- Parse items
              $v_work_list = explode(",", $p_options_list[$i+1]);
          }
          else if (is_integer($p_options_list[$i+1])) {
              $v_work_list[0] = $p_options_list[$i+1].'-'.$p_options_list[$i+1];
          }
          else if (is_array($p_options_list[$i+1])) {
              $v_work_list = $p_options_list[$i+1];
          }
          else {
            // ----- Error log
            PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Value must be integer, string or array for option '".PclZipUtilOptionText($p_options_list[$i])."'");

            // ----- Return
            return PclZip::errorCode();
          }
          
          // ----- Reduce the index list
          // each index item in the list must be a couple with a start and
          // an end value : [0,3], [5-5], [8-10], ...
          // ----- Check the format of each item
          $v_sort_flag=false;
          $v_sort_value=0;
          for ($j=0; $j<sizeof($v_work_list); $j++) {
              // ----- Explode the item
              $v_item_list = explode("-", $v_work_list[$j]);
              $v_size_item_list = sizeof($v_item_list);
              
              // ----- TBC : Here we might check that each item is a
              // real integer ...
              
              // ----- Look for single value
              if ($v_size_item_list == 1) {
                  // ----- Set the option value
                  $v_result_list[$p_options_list[$i]][$j]['start'] = $v_item_list[0];
                  $v_result_list[$p_options_list[$i]][$j]['end'] = $v_item_list[0];
              }
              elseif ($v_size_item_list == 2) {
                  // ----- Set the option value
                  $v_result_list[$p_options_list[$i]][$j]['start'] = $v_item_list[0];
                  $v_result_list[$p_options_list[$i]][$j]['end'] = $v_item_list[1];
              }
              else {
                  // ----- Error log
                  PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Too many values in index range for option '".PclZipUtilOptionText($p_options_list[$i])."'");

                  // ----- Return
                  return PclZip::errorCode();
              }


              // ----- Look for list sort
              if ($v_result_list[$p_options_list[$i]][$j]['start'] < $v_sort_value) {
                  $v_sort_flag=true;

                  // ----- TBC : An automatic sort should be writen ...
                  // ----- Error log
                  PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Invalid order of index range for option '".PclZipUtilOptionText($p_options_list[$i])."'");

                  // ----- Return
                  return PclZip::errorCode();
              }
              $v_sort_value = $v_result_list[$p_options_list[$i]][$j]['start'];
          }
          
          // ----- Sort the items
          if ($v_sort_flag) {
              // TBC : To Be Completed
          }

          // ----- Next option
          $i++;
        break;

        // ----- Look for options that request no value
        case PCLZIP_OPT_REMOVE_ALL_PATH :
        case PCLZIP_OPT_EXTRACT_AS_STRING :
        case PCLZIP_OPT_NO_COMPRESSION :
        case PCLZIP_OPT_EXTRACT_IN_OUTPUT :
        case PCLZIP_OPT_REPLACE_NEWER :
        case PCLZIP_OPT_STOP_ON_ERROR :
          $v_result_list[$p_options_list[$i]] = true;
        break;

        // ----- Look for options that request an octal value
        case PCLZIP_OPT_SET_CHMOD :
          // ----- Check the number of parameters
          if (($i+1) >= $p_size) {
            // ----- Error log
            PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'");

            // ----- Return
            return PclZip::errorCode();
          }

          // ----- Get the value
          $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1];
          $i++;
        break;

        // ----- Look for options that request a call-back
        case PCLZIP_CB_PRE_EXTRACT :
        case PCLZIP_CB_POST_EXTRACT :
        case PCLZIP_CB_PRE_ADD :
        case PCLZIP_CB_POST_ADD :
        /* for futur use
        case PCLZIP_CB_PRE_DELETE :
        case PCLZIP_CB_POST_DELETE :
        case PCLZIP_CB_PRE_LIST :
        case PCLZIP_CB_POST_LIST :
        */
          // ----- Check the number of parameters
          if (($i+1) >= $p_size) {
            // ----- Error log
            PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'");

            // ----- Return
            return PclZip::errorCode();
          }

          // ----- Get the value
          $v_function_name = $p_options_list[$i+1];

          // ----- Check that the value is a valid existing function
          if (!function_exists($v_function_name)) {
            // ----- Error log
            PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Function '".$v_function_name."()' is not an existing function for option '".PclZipUtilOptionText($p_options_list[$i])."'");

            // ----- Return
            return PclZip::errorCode();
          }

          // ----- Set the attribute
          $v_result_list[$p_options_list[$i]] = $v_function_name;
          $i++;
        break;

        default :
          // ----- Error log
          PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER,
		                       "Unknown parameter '"
							   .$p_options_list[$i]."'");

          // ----- Return
          return PclZip::errorCode();
      }

      // ----- Next options
      $i++;
    }

    // ----- Look for mandatory options
    if ($v_requested_options !== false) {
      for ($key=reset($v_requested_options); $key=key($v_requested_options); $key=next($v_requested_options)) {
        // ----- Look for mandatory option
        if ($v_requested_options[$key] == 'mandatory') {
          // ----- Look if present
          if (!isset($v_result_list[$key])) {
            // ----- Error log
            PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Missing mandatory parameter ".PclZipUtilOptionText($key)."(".$key.")");

            // ----- Return
            return PclZip::errorCode();
          }
        }
      }
    }
    
    // ----- Look for default values
    if (!isset($v_result_list[PCLZIP_OPT_TEMP_FILE_THRESHOLD])) {
      
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privOptionDefaultThreshold()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privOptionDefaultThreshold(&$p_options)
  {
    $v_result=1;
    
    if (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD])
        || isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) {
      return $v_result;
    }
    
    // ----- Get 'memory_limit' configuration value
    $v_memory_limit = ini_get('memory_limit');
    $v_memory_limit = trim($v_memory_limit);
    $last = strtolower(substr($v_memory_limit, -1));
 
    if($last == 'g')
        //$v_memory_limit = $v_memory_limit*1024*1024*1024;
        $v_memory_limit = $v_memory_limit*1073741824;
    if($last == 'm')
        //$v_memory_limit = $v_memory_limit*1024*1024;
        $v_memory_limit = $v_memory_limit*1048576;
    if($last == 'k')
        $v_memory_limit = $v_memory_limit*1024;
            
    $p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] = floor($v_memory_limit*PCLZIP_TEMPORARY_FILE_RATIO);
    

    // ----- Sanity check : No threshold if value lower than 1M
    if ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] < 1048576) {
      unset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]);
    }
          
    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privFileDescrParseAtt()
  // Description :
  // Parameters :
  // Return Values :
  //   1 on success.
  //   0 on failure.
  // --------------------------------------------------------------------------------
  function privFileDescrParseAtt(&$p_file_list, &$p_filedescr, $v_options, $v_requested_options=false)
  {
    $v_result=1;
    
    // ----- For each file in the list check the attributes
    foreach ($p_file_list as $v_key => $v_value) {
    
      // ----- Check if the option is supported
      if (!isset($v_requested_options[$v_key])) {
        // ----- Error log
        PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid file attribute '".$v_key."' for this file");

        // ----- Return
        return PclZip::errorCode();
      }

      // ----- Look for attribute
      switch ($v_key) {
        case PCLZIP_ATT_FILE_NAME :
          if (!is_string($v_value)) {
            PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'");
            return PclZip::errorCode();
          }

          $p_filedescr['filename'] = PclZipUtilPathReduction($v_value);
          
          if ($p_filedescr['filename'] == '') {
            PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty filename for attribute '".PclZipUtilOptionText($v_key)."'");
            return PclZip::errorCode();
          }

        break;

        case PCLZIP_ATT_FILE_NEW_SHORT_NAME :
          if (!is_string($v_value)) {
            PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'");
            return PclZip::errorCode();
          }

          $p_filedescr['new_short_name'] = PclZipUtilPathReduction($v_value);

          if ($p_filedescr['new_short_name'] == '') {
            PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty short filename for attribute '".PclZipUtilOptionText($v_key)."'");
            return PclZip::errorCode();
          }
        break;

        case PCLZIP_ATT_FILE_NEW_FULL_NAME :
          if (!is_string($v_value)) {
            PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'");
            return PclZip::errorCode();
          }

          $p_filedescr['new_full_name'] = PclZipUtilPathReduction($v_value);

          if ($p_filedescr['new_full_name'] == '') {
            PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty full filename for attribute '".PclZipUtilOptionText($v_key)."'");
            return PclZip::errorCode();
          }
        break;

        // ----- Look for options that takes a string
        case PCLZIP_ATT_FILE_COMMENT :
          if (!is_string($v_value)) {
            PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'");
            return PclZip::errorCode();
          }

          $p_filedescr['comment'] = $v_value;
        break;

        case PCLZIP_ATT_FILE_MTIME :
          if (!is_integer($v_value)) {
            PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". Integer expected for attribute '".PclZipUtilOptionText($v_key)."'");
            return PclZip::errorCode();
          }

          $p_filedescr['mtime'] = $v_value;
        break;

        case PCLZIP_ATT_FILE_CONTENT :
          $p_filedescr['content'] = $v_value;
        break;

        default :
          // ----- Error log
          PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER,
		                           "Unknown parameter '".$v_key."'");

          // ----- Return
          return PclZip::errorCode();
      }

      // ----- Look for mandatory options
      if ($v_requested_options !== false) {
        for ($key=reset($v_requested_options); $key=key($v_requested_options); $key=next($v_requested_options)) {
          // ----- Look for mandatory option
          if ($v_requested_options[$key] == 'mandatory') {
            // ----- Look if present
            if (!isset($p_file_list[$key])) {
              PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Missing mandatory parameter ".PclZipUtilOptionText($key)."(".$key.")");
              return PclZip::errorCode();
            }
          }
        }
      }
    
    // end foreach
    }
    
    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privFileDescrExpand()
  // Description :
  //   This method look for each item of the list to see if its a file, a folder
  //   or a string to be added as file. For any other type of files (link, other)
  //   just ignore the item.
  //   Then prepare the information that will be stored for that file.
  //   When its a folder, expand the folder with all the files that are in that 
  //   folder (recursively).
  // Parameters :
  // Return Values :
  //   1 on success.
  //   0 on failure.
  // --------------------------------------------------------------------------------
  function privFileDescrExpand(&$p_filedescr_list, &$p_options)
  {
    $v_result=1;
    
    // ----- Create a result list
    $v_result_list = array();
    
    // ----- Look each entry
    for ($i=0; $i<sizeof($p_filedescr_list); $i++) {
      
      // ----- Get filedescr
      $v_descr = $p_filedescr_list[$i];
      
      // ----- Reduce the filename
      $v_descr['filename'] = PclZipUtilTranslateWinPath($v_descr['filename'], false);
      $v_descr['filename'] = PclZipUtilPathReduction($v_descr['filename']);
      
      // ----- Look for real file or folder
      if (file_exists($v_descr['filename'])) {
        if (@is_file($v_descr['filename'])) {
          $v_descr['type'] = 'file';
        }
        else if (@is_dir($v_descr['filename'])) {
          $v_descr['type'] = 'folder';
        }
        else if (@is_link($v_descr['filename'])) {
          // skip
          continue;
        }
        else {
          // skip
          continue;
        }
      }
      
      // ----- Look for string added as file
      else if (isset($v_descr['content'])) {
        $v_descr['type'] = 'virtual_file';
      }
      
      // ----- Missing file
      else {
        // ----- Error log
        PclZip::privErrorLog(PCLZIP_ERR_MISSING_FILE, "File '".$v_descr['filename']."' does not exist");

        // ----- Return
        return PclZip::errorCode();
      }
      
      // ----- Calculate the stored filename
      $this->privCalculateStoredFilename($v_descr, $p_options);
      
      // ----- Add the descriptor in result list
      $v_result_list[sizeof($v_result_list)] = $v_descr;
      
      // ----- Look for folder
      if ($v_descr['type'] == 'folder') {
        // ----- List of items in folder
        $v_dirlist_descr = array();
        $v_dirlist_nb = 0;
        if ($v_folder_handler = @opendir($v_descr['filename'])) {
          while (($v_item_handler = @readdir($v_folder_handler)) !== false) {

            // ----- Skip '.' and '..'
            if (($v_item_handler == '.') || ($v_item_handler == '..')) {
                continue;
            }
            
            // ----- Compose the full filename
            $v_dirlist_descr[$v_dirlist_nb]['filename'] = $v_descr['filename'].'/'.$v_item_handler;
            
            // ----- Look for different stored filename
            // Because the name of the folder was changed, the name of the
            // files/sub-folders also change
            if (($v_descr['stored_filename'] != $v_descr['filename'])
                 && (!isset($p_options[PCLZIP_OPT_REMOVE_ALL_PATH]))) {
              if ($v_descr['stored_filename'] != '') {
                $v_dirlist_descr[$v_dirlist_nb]['new_full_name'] = $v_descr['stored_filename'].'/'.$v_item_handler;
              }
              else {
                $v_dirlist_descr[$v_dirlist_nb]['new_full_name'] = $v_item_handler;
              }
            }
      
            $v_dirlist_nb++;
          }
          
          @closedir($v_folder_handler);
        }
        else {
          // TBC : unable to open folder in read mode
        }
        
        // ----- Expand each element of the list
        if ($v_dirlist_nb != 0) {
          // ----- Expand
          if (($v_result = $this->privFileDescrExpand($v_dirlist_descr, $p_options)) != 1) {
            return $v_result;
          }
          
          // ----- Concat the resulting list
          $v_result_list = array_merge($v_result_list, $v_dirlist_descr);
        }
        else {
        }
          
        // ----- Free local array
        unset($v_dirlist_descr);
      }
    }
    
    // ----- Get the result list
    $p_filedescr_list = $v_result_list;

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privCreate()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privCreate($p_filedescr_list, &$p_result_list, &$p_options)
  {
    $v_result=1;
    $v_list_detail = array();
    
    // ----- Magic quotes trick
    $this->privDisableMagicQuotes();

    // ----- Open the file in write mode
    if (($v_result = $this->privOpenFd('wb')) != 1)
    {
      // ----- Return
      return $v_result;
    }

    // ----- Add the list of files
    $v_result = $this->privAddList($p_filedescr_list, $p_result_list, $p_options);

    // ----- Close
    $this->privCloseFd();

    // ----- Magic quotes trick
    $this->privSwapBackMagicQuotes();

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privAdd()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privAdd($p_filedescr_list, &$p_result_list, &$p_options)
  {
    $v_result=1;
    $v_list_detail = array();

    // ----- Look if the archive exists or is empty
    if ((!is_file($this->zipname)) || (filesize($this->zipname) == 0))
    {

      // ----- Do a create
      $v_result = $this->privCreate($p_filedescr_list, $p_result_list, $p_options);

      // ----- Return
      return $v_result;
    }
    // ----- Magic quotes trick
    $this->privDisableMagicQuotes();

    // ----- Open the zip file
    if (($v_result=$this->privOpenFd('rb')) != 1)
    {
      // ----- Magic quotes trick
      $this->privSwapBackMagicQuotes();

      // ----- Return
      return $v_result;
    }

    // ----- Read the central directory informations
    $v_central_dir = array();
    if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1)
    {
      $this->privCloseFd();
      $this->privSwapBackMagicQuotes();
      return $v_result;
    }

    // ----- Go to beginning of File
    @rewind($this->zip_fd);

    // ----- Creates a temporay file
    $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp';

    // ----- Open the temporary file in write mode
    if (($v_zip_temp_fd = @fopen($v_zip_temp_name, 'wb')) == 0)
    {
      $this->privCloseFd();
      $this->privSwapBackMagicQuotes();

      PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_zip_temp_name.'\' in binary write mode');

      // ----- Return
      return PclZip::errorCode();
    }

    // ----- Copy the files from the archive to the temporary file
    // TBC : Here I should better append the file and go back to erase the central dir
    $v_size = $v_central_dir['offset'];
    while ($v_size != 0)
    {
      $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
      $v_buffer = fread($this->zip_fd, $v_read_size);
      @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size);
      $v_size -= $v_read_size;
    }

    // ----- Swap the file descriptor
    // Here is a trick : I swap the temporary fd with the zip fd, in order to use
    // the following methods on the temporary fil and not the real archive
    $v_swap = $this->zip_fd;
    $this->zip_fd = $v_zip_temp_fd;
    $v_zip_temp_fd = $v_swap;

    // ----- Add the files
    $v_header_list = array();
    if (($v_result = $this->privAddFileList($p_filedescr_list, $v_header_list, $p_options)) != 1)
    {
      fclose($v_zip_temp_fd);
      $this->privCloseFd();
      @unlink($v_zip_temp_name);
      $this->privSwapBackMagicQuotes();

      // ----- Return
      return $v_result;
    }

    // ----- Store the offset of the central dir
    $v_offset = @ftell($this->zip_fd);

    // ----- Copy the block of file headers from the old archive
    $v_size = $v_central_dir['size'];
    while ($v_size != 0)
    {
      $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
      $v_buffer = @fread($v_zip_temp_fd, $v_read_size);
      @fwrite($this->zip_fd, $v_buffer, $v_read_size);
      $v_size -= $v_read_size;
    }

    // ----- Create the Central Dir files header
    for ($i=0, $v_count=0; $i<sizeof($v_header_list); $i++)
    {
      // ----- Create the file header
      if ($v_header_list[$i]['status'] == 'ok') {
        if (($v_result = $this->privWriteCentralFileHeader($v_header_list[$i])) != 1) {
          fclose($v_zip_temp_fd);
          $this->privCloseFd();
          @unlink($v_zip_temp_name);
          $this->privSwapBackMagicQuotes();

          // ----- Return
          return $v_result;
        }
        $v_count++;
      }

      // ----- Transform the header to a 'usable' info
      $this->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]);
    }

    // ----- Zip file comment
    $v_comment = $v_central_dir['comment'];
    if (isset($p_options[PCLZIP_OPT_COMMENT])) {
      $v_comment = $p_options[PCLZIP_OPT_COMMENT];
    }
    if (isset($p_options[PCLZIP_OPT_ADD_COMMENT])) {
      $v_comment = $v_comment.$p_options[PCLZIP_OPT_ADD_COMMENT];
    }
    if (isset($p_options[PCLZIP_OPT_PREPEND_COMMENT])) {
      $v_comment = $p_options[PCLZIP_OPT_PREPEND_COMMENT].$v_comment;
    }

    // ----- Calculate the size of the central header
    $v_size = @ftell($this->zip_fd)-$v_offset;

    // ----- Create the central dir footer
    if (($v_result = $this->privWriteCentralHeader($v_count+$v_central_dir['entries'], $v_size, $v_offset, $v_comment)) != 1)
    {
      // ----- Reset the file list
      unset($v_header_list);
      $this->privSwapBackMagicQuotes();

      // ----- Return
      return $v_result;
    }

    // ----- Swap back the file descriptor
    $v_swap = $this->zip_fd;
    $this->zip_fd = $v_zip_temp_fd;
    $v_zip_temp_fd = $v_swap;

    // ----- Close
    $this->privCloseFd();

    // ----- Close the temporary file
    @fclose($v_zip_temp_fd);

    // ----- Magic quotes trick
    $this->privSwapBackMagicQuotes();

    // ----- Delete the zip file
    // TBC : I should test the result ...
    @unlink($this->zipname);

    // ----- Rename the temporary file
    // TBC : I should test the result ...
    //@rename($v_zip_temp_name, $this->zipname);
    PclZipUtilRename($v_zip_temp_name, $this->zipname);

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privOpenFd()
  // Description :
  // Parameters :
  // --------------------------------------------------------------------------------
  function privOpenFd($p_mode)
  {
    $v_result=1;

    // ----- Look if already open
    if ($this->zip_fd != 0)
    {
      // ----- Error log
      PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Zip file \''.$this->zipname.'\' already open');

      // ----- Return
      return PclZip::errorCode();
    }

    // ----- Open the zip file
    if (($this->zip_fd = @fopen($this->zipname, $p_mode)) == 0)
    {
      // ----- Error log
      PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in '.$p_mode.' mode');

      // ----- Return
      return PclZip::errorCode();
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privCloseFd()
  // Description :
  // Parameters :
  // --------------------------------------------------------------------------------
  function privCloseFd()
  {
    $v_result=1;

    if ($this->zip_fd != 0)
      @fclose($this->zip_fd);
    $this->zip_fd = 0;

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privAddList()
  // Description :
  //   $p_add_dir and $p_remove_dir will give the ability to memorize a path which is
  //   different from the real path of the file. This is usefull if you want to have PclTar
  //   running in any directory, and memorize relative path from an other directory.
  // Parameters :
  //   $p_list : An array containing the file or directory names to add in the tar
  //   $p_result_list : list of added files with their properties (specially the status field)
  //   $p_add_dir : Path to add in the filename path archived
  //   $p_remove_dir : Path to remove in the filename path archived
  // Return Values :
  // --------------------------------------------------------------------------------
//  function privAddList($p_list, &$p_result_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, &$p_options)
  function privAddList($p_filedescr_list, &$p_result_list, &$p_options)
  {
    $v_result=1;

    // ----- Add the files
    $v_header_list = array();
    if (($v_result = $this->privAddFileList($p_filedescr_list, $v_header_list, $p_options)) != 1)
    {
      // ----- Return
      return $v_result;
    }

    // ----- Store the offset of the central dir
    $v_offset = @ftell($this->zip_fd);

    // ----- Create the Central Dir files header
    for ($i=0,$v_count=0; $i<sizeof($v_header_list); $i++)
    {
      // ----- Create the file header
      if ($v_header_list[$i]['status'] == 'ok') {
        if (($v_result = $this->privWriteCentralFileHeader($v_header_list[$i])) != 1) {
          // ----- Return
          return $v_result;
        }
        $v_count++;
      }

      // ----- Transform the header to a 'usable' info
      $this->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]);
    }

    // ----- Zip file comment
    $v_comment = '';
    if (isset($p_options[PCLZIP_OPT_COMMENT])) {
      $v_comment = $p_options[PCLZIP_OPT_COMMENT];
    }

    // ----- Calculate the size of the central header
    $v_size = @ftell($this->zip_fd)-$v_offset;

    // ----- Create the central dir footer
    if (($v_result = $this->privWriteCentralHeader($v_count, $v_size, $v_offset, $v_comment)) != 1)
    {
      // ----- Reset the file list
      unset($v_header_list);

      // ----- Return
      return $v_result;
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privAddFileList()
  // Description :
  // Parameters :
  //   $p_filedescr_list : An array containing the file description 
  //                      or directory names to add in the zip
  //   $p_result_list : list of added files with their properties (specially the status field)
  // Return Values :
  // --------------------------------------------------------------------------------
  function privAddFileList($p_filedescr_list, &$p_result_list, &$p_options)
  {
    $v_result=1;
    $v_header = array();

    // ----- Recuperate the current number of elt in list
    $v_nb = sizeof($p_result_list);

    // ----- Loop on the files
    for ($j=0; ($j<sizeof($p_filedescr_list)) && ($v_result==1); $j++) {
      // ----- Format the filename
      $p_filedescr_list[$j]['filename']
      = PclZipUtilTranslateWinPath($p_filedescr_list[$j]['filename'], false);
      

      // ----- Skip empty file names
      // TBC : Can this be possible ? not checked in DescrParseAtt ?
      if ($p_filedescr_list[$j]['filename'] == "") {
        continue;
      }

      // ----- Check the filename
      if (   ($p_filedescr_list[$j]['type'] != 'virtual_file')
          && (!file_exists($p_filedescr_list[$j]['filename']))) {
        PclZip::privErrorLog(PCLZIP_ERR_MISSING_FILE, "File '".$p_filedescr_list[$j]['filename']."' does not exist");
        return PclZip::errorCode();
      }

      // ----- Look if it is a file or a dir with no all path remove option
      // or a dir with all its path removed
//      if (   (is_file($p_filedescr_list[$j]['filename']))
//          || (   is_dir($p_filedescr_list[$j]['filename'])
      if (   ($p_filedescr_list[$j]['type'] == 'file')
          || ($p_filedescr_list[$j]['type'] == 'virtual_file')
          || (   ($p_filedescr_list[$j]['type'] == 'folder')
              && (   !isset($p_options[PCLZIP_OPT_REMOVE_ALL_PATH])
                  || !$p_options[PCLZIP_OPT_REMOVE_ALL_PATH]))
          ) {

        // ----- Add the file
        $v_result = $this->privAddFile($p_filedescr_list[$j], $v_header,
                                       $p_options);
        if ($v_result != 1) {
          return $v_result;
        }

        // ----- Store the file infos
        $p_result_list[$v_nb++] = $v_header;
      }
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privAddFile()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privAddFile($p_filedescr, &$p_header, &$p_options)
  {
    $v_result=1;
    
    // ----- Working variable
    $p_filename = $p_filedescr['filename'];

    // TBC : Already done in the fileAtt check ... ?
    if ($p_filename == "") {
      // ----- Error log
      PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid file list parameter (invalid or empty list)");

      // ----- Return
      return PclZip::errorCode();
    }
  
    // ----- Look for a stored different filename 
    /* TBC : Removed
    if (isset($p_filedescr['stored_filename'])) {
      $v_stored_filename = $p_filedescr['stored_filename'];
    }
    else {
      $v_stored_filename = $p_filedescr['stored_filename'];
    }
    */

    // ----- Set the file properties
    clearstatcache();
    $p_header['version'] = 20;
    $p_header['version_extracted'] = 10;
    $p_header['flag'] = 0;
    $p_header['compression'] = 0;
    $p_header['crc'] = 0;
    $p_header['compressed_size'] = 0;
    $p_header['filename_len'] = strlen($p_filename);
    $p_header['extra_len'] = 0;
    $p_header['disk'] = 0;
    $p_header['internal'] = 0;
    $p_header['offset'] = 0;
    $p_header['filename'] = $p_filename;
// TBC : Removed    $p_header['stored_filename'] = $v_stored_filename;
    $p_header['stored_filename'] = $p_filedescr['stored_filename'];
    $p_header['extra'] = '';
    $p_header['status'] = 'ok';
    $p_header['index'] = -1;

    // ----- Look for regular file
    if ($p_filedescr['type']=='file') {
      $p_header['external'] = 0x00000000;
      $p_header['size'] = filesize($p_filename);
    }
    
    // ----- Look for regular folder
    else if ($p_filedescr['type']=='folder') {
      $p_header['external'] = 0x00000010;
      $p_header['mtime'] = filemtime($p_filename);
      $p_header['size'] = filesize($p_filename);
    }
    
    // ----- Look for virtual file
    else if ($p_filedescr['type'] == 'virtual_file') {
      $p_header['external'] = 0x00000000;
      $p_header['size'] = strlen($p_filedescr['content']);
    }
    

    // ----- Look for filetime
    if (isset($p_filedescr['mtime'])) {
      $p_header['mtime'] = $p_filedescr['mtime'];
    }
    else if ($p_filedescr['type'] == 'virtual_file') {
      $p_header['mtime'] = time();
    }
    else {
      $p_header['mtime'] = filemtime($p_filename);
    }

    // ------ Look for file comment
    if (isset($p_filedescr['comment'])) {
      $p_header['comment_len'] = strlen($p_filedescr['comment']);
      $p_header['comment'] = $p_filedescr['comment'];
    }
    else {
      $p_header['comment_len'] = 0;
      $p_header['comment'] = '';
    }

    // ----- Look for pre-add callback
    if (isset($p_options[PCLZIP_CB_PRE_ADD])) {

      // ----- Generate a local information
      $v_local_header = array();
      $this->privConvertHeader2FileInfo($p_header, $v_local_header);

      // ----- Call the callback
      // Here I do not use call_user_func() because I need to send a reference to the
      // header.
//      eval('$v_result = '.$p_options[PCLZIP_CB_PRE_ADD].'(PCLZIP_CB_PRE_ADD, $v_local_header);');
      $v_result = $p_options[PCLZIP_CB_PRE_ADD](PCLZIP_CB_PRE_ADD, $v_local_header);
      if ($v_result == 0) {
        // ----- Change the file status
        $p_header['status'] = "skipped";
        $v_result = 1;
      }

      // ----- Update the informations
      // Only some fields can be modified
      if ($p_header['stored_filename'] != $v_local_header['stored_filename']) {
        $p_header['stored_filename'] = PclZipUtilPathReduction($v_local_header['stored_filename']);
      }
    }

    // ----- Look for empty stored filename
    if ($p_header['stored_filename'] == "") {
      $p_header['status'] = "filtered";
    }
    
    // ----- Check the path length
    if (strlen($p_header['stored_filename']) > 0xFF) {
      $p_header['status'] = 'filename_too_long';
    }

    // ----- Look if no error, or file not skipped
    if ($p_header['status'] == 'ok') {

      // ----- Look for a file
      if ($p_filedescr['type'] == 'file') {
        // ----- Look for using temporary file to zip
        if ( (!isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) 
            && (isset($p_options[PCLZIP_OPT_TEMP_FILE_ON])
                || (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD])
                    && ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] <= $p_header['size'])) ) ) {
          $v_result = $this->privAddFileUsingTempFile($p_filedescr, $p_header, $p_options);
          if ($v_result < PCLZIP_ERR_NO_ERROR) {
            return $v_result;
          }
        }
        
        // ----- Use "in memory" zip algo
        else {

        // ----- Open the source file
        if (($v_file = @fopen($p_filename, "rb")) == 0) {
          PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to open file '$p_filename' in binary read mode");
          return PclZip::errorCode();
        }

        // ----- Read the file content
        $v_content = @fread($v_file, $p_header['size']);

        // ----- Close the file
        @fclose($v_file);

        // ----- Calculate the CRC
        $p_header['crc'] = @crc32($v_content);
        
        // ----- Look for no compression
        if ($p_options[PCLZIP_OPT_NO_COMPRESSION]) {
          // ----- Set header parameters
          $p_header['compressed_size'] = $p_header['size'];
          $p_header['compression'] = 0;
        }
        
        // ----- Look for normal compression
        else {
          // ----- Compress the content
          $v_content = @gzdeflate($v_content);

          // ----- Set header parameters
          $p_header['compressed_size'] = strlen($v_content);
          $p_header['compression'] = 8;
        }
        
        // ----- Call the header generation
        if (($v_result = $this->privWriteFileHeader($p_header)) != 1) {
          @fclose($v_file);
          return $v_result;
        }

        // ----- Write the compressed (or not) content
        @fwrite($this->zip_fd, $v_content, $p_header['compressed_size']);

        }

      }

      // ----- Look for a virtual file (a file from string)
      else if ($p_filedescr['type'] == 'virtual_file') {
          
        $v_content = $p_filedescr['content'];

        // ----- Calculate the CRC
        $p_header['crc'] = @crc32($v_content);
        
        // ----- Look for no compression
        if ($p_options[PCLZIP_OPT_NO_COMPRESSION]) {
          // ----- Set header parameters
          $p_header['compressed_size'] = $p_header['size'];
          $p_header['compression'] = 0;
        }
        
        // ----- Look for normal compression
        else {
          // ----- Compress the content
          $v_content = @gzdeflate($v_content);

          // ----- Set header parameters
          $p_header['compressed_size'] = strlen($v_content);
          $p_header['compression'] = 8;
        }
        
        // ----- Call the header generation
        if (($v_result = $this->privWriteFileHeader($p_header)) != 1) {
          @fclose($v_file);
          return $v_result;
        }

        // ----- Write the compressed (or not) content
        @fwrite($this->zip_fd, $v_content, $p_header['compressed_size']);
      }

      // ----- Look for a directory
      else if ($p_filedescr['type'] == 'folder') {
        // ----- Look for directory last '/'
        if (@substr($p_header['stored_filename'], -1) != '/') {
          $p_header['stored_filename'] .= '/';
        }

        // ----- Set the file properties
        $p_header['size'] = 0;
        //$p_header['external'] = 0x41FF0010;   // Value for a folder : to be checked
        $p_header['external'] = 0x00000010;   // Value for a folder : to be checked

        // ----- Call the header generation
        if (($v_result = $this->privWriteFileHeader($p_header)) != 1)
        {
          return $v_result;
        }
      }
    }

    // ----- Look for post-add callback
    if (isset($p_options[PCLZIP_CB_POST_ADD])) {

      // ----- Generate a local information
      $v_local_header = array();
      $this->privConvertHeader2FileInfo($p_header, $v_local_header);

      // ----- Call the callback
      // Here I do not use call_user_func() because I need to send a reference to the
      // header.
//      eval('$v_result = '.$p_options[PCLZIP_CB_POST_ADD].'(PCLZIP_CB_POST_ADD, $v_local_header);');
      $v_result = $p_options[PCLZIP_CB_POST_ADD](PCLZIP_CB_POST_ADD, $v_local_header);
      if ($v_result == 0) {
        // ----- Ignored
        $v_result = 1;
      }

      // ----- Update the informations
      // Nothing can be modified
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privAddFileUsingTempFile()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privAddFileUsingTempFile($p_filedescr, &$p_header, &$p_options)
  {
    $v_result=PCLZIP_ERR_NO_ERROR;
    
    // ----- Working variable
    $p_filename = $p_filedescr['filename'];


    // ----- Open the source file
    if (($v_file = @fopen($p_filename, "rb")) == 0) {
      PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to open file '$p_filename' in binary read mode");
      return PclZip::errorCode();
    }

    // ----- Creates a compressed temporary file
    $v_gzip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.gz';
    if (($v_file_compressed = @gzopen($v_gzip_temp_name, "wb")) == 0) {
      fclose($v_file);
      PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary write mode');
      return PclZip::errorCode();
    }

    // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks
    $v_size = filesize($p_filename);
    while ($v_size != 0) {
      $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
      $v_buffer = @fread($v_file, $v_read_size);
      //$v_binary_data = pack('a'.$v_read_size, $v_buffer);
      @gzputs($v_file_compressed, $v_buffer, $v_read_size);
      $v_size -= $v_read_size;
    }

    // ----- Close the file
    @fclose($v_file);
    @gzclose($v_file_compressed);

    // ----- Check the minimum file size
    if (filesize($v_gzip_temp_name) < 18) {
      PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'gzip temporary file \''.$v_gzip_temp_name.'\' has invalid filesize - should be minimum 18 bytes');
      return PclZip::errorCode();
    }

    // ----- Extract the compressed attributes
    if (($v_file_compressed = @fopen($v_gzip_temp_name, "rb")) == 0) {
      PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary read mode');
      return PclZip::errorCode();
    }

    // ----- Read the gzip file header
    $v_binary_data = @fread($v_file_compressed, 10);
    $v_data_header = unpack('a1id1/a1id2/a1cm/a1flag/Vmtime/a1xfl/a1os', $v_binary_data);

    // ----- Check some parameters
    $v_data_header['os'] = bin2hex($v_data_header['os']);

    // ----- Read the gzip file footer
    @fseek($v_file_compressed, filesize($v_gzip_temp_name)-8);
    $v_binary_data = @fread($v_file_compressed, 8);
    $v_data_footer = unpack('Vcrc/Vcompressed_size', $v_binary_data);

    // ----- Set the attributes
    $p_header['compression'] = ord($v_data_header['cm']);
    //$p_header['mtime'] = $v_data_header['mtime'];
    $p_header['crc'] = $v_data_footer['crc'];
    $p_header['compressed_size'] = filesize($v_gzip_temp_name)-18;

    // ----- Close the file
    @fclose($v_file_compressed);

    // ----- Call the header generation
    if (($v_result = $this->privWriteFileHeader($p_header)) != 1) {
      return $v_result;
    }

    // ----- Add the compressed data
    if (($v_file_compressed = @fopen($v_gzip_temp_name, "rb")) == 0)
    {
      PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary read mode');
      return PclZip::errorCode();
    }

    // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks
    fseek($v_file_compressed, 10);
    $v_size = $p_header['compressed_size'];
    while ($v_size != 0)
    {
      $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
      $v_buffer = @fread($v_file_compressed, $v_read_size);
      //$v_binary_data = pack('a'.$v_read_size, $v_buffer);
      @fwrite($this->zip_fd, $v_buffer, $v_read_size);
      $v_size -= $v_read_size;
    }

    // ----- Close the file
    @fclose($v_file_compressed);

    // ----- Unlink the temporary file
    @unlink($v_gzip_temp_name);
    
    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privCalculateStoredFilename()
  // Description :
  //   Based on file descriptor properties and global options, this method
  //   calculate the filename that will be stored in the archive.
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privCalculateStoredFilename(&$p_filedescr, &$p_options)
  {
    $v_result=1;
    
    // ----- Working variables
    $p_filename = $p_filedescr['filename'];
    if (isset($p_options[PCLZIP_OPT_ADD_PATH])) {
      $p_add_dir = $p_options[PCLZIP_OPT_ADD_PATH];
    }
    else {
      $p_add_dir = '';
    }
    if (isset($p_options[PCLZIP_OPT_REMOVE_PATH])) {
      $p_remove_dir = $p_options[PCLZIP_OPT_REMOVE_PATH];
    }
    else {
      $p_remove_dir = '';
    }
    if (isset($p_options[PCLZIP_OPT_REMOVE_ALL_PATH])) {
      $p_remove_all_dir = $p_options[PCLZIP_OPT_REMOVE_ALL_PATH];
    }
    else {
      $p_remove_all_dir = 0;
    }


    // ----- Look for full name change
    if (isset($p_filedescr['new_full_name'])) {
      // ----- Remove drive letter if any
      $v_stored_filename = PclZipUtilTranslateWinPath($p_filedescr['new_full_name']);
    }
    
    // ----- Look for path and/or short name change
    else {

      // ----- Look for short name change
      // Its when we cahnge just the filename but not the path
      if (isset($p_filedescr['new_short_name'])) {
        $v_path_info = pathinfo($p_filename);
        $v_dir = '';
        if ($v_path_info['dirname'] != '') {
          $v_dir = $v_path_info['dirname'].'/';
        }
        $v_stored_filename = $v_dir.$p_filedescr['new_short_name'];
      }
      else {
        // ----- Calculate the stored filename
        $v_stored_filename = $p_filename;
      }

      // ----- Look for all path to remove
      if ($p_remove_all_dir) {
        $v_stored_filename = basename($p_filename);
      }
      // ----- Look for partial path remove
      else if ($p_remove_dir != "") {
        if (substr($p_remove_dir, -1) != '/')
          $p_remove_dir .= "/";

        if (   (substr($p_filename, 0, 2) == "./")
            || (substr($p_remove_dir, 0, 2) == "./")) {
            
          if (   (substr($p_filename, 0, 2) == "./")
              && (substr($p_remove_dir, 0, 2) != "./")) {
            $p_remove_dir = "./".$p_remove_dir;
          }
          if (   (substr($p_filename, 0, 2) != "./")
              && (substr($p_remove_dir, 0, 2) == "./")) {
            $p_remove_dir = substr($p_remove_dir, 2);
          }
        }

        $v_compare = PclZipUtilPathInclusion($p_remove_dir,
                                             $v_stored_filename);
        if ($v_compare > 0) {
          if ($v_compare == 2) {
            $v_stored_filename = "";
          }
          else {
            $v_stored_filename = substr($v_stored_filename,
                                        strlen($p_remove_dir));
          }
        }
      }
      
      // ----- Remove drive letter if any
      $v_stored_filename = PclZipUtilTranslateWinPath($v_stored_filename);
      
      // ----- Look for path to add
      if ($p_add_dir != "") {
        if (substr($p_add_dir, -1) == "/")
          $v_stored_filename = $p_add_dir.$v_stored_filename;
        else
          $v_stored_filename = $p_add_dir."/".$v_stored_filename;
      }
    }

    // ----- Filename (reduce the path of stored name)
    $v_stored_filename = PclZipUtilPathReduction($v_stored_filename);
    $p_filedescr['stored_filename'] = $v_stored_filename;
    
    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privWriteFileHeader()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privWriteFileHeader(&$p_header)
  {
    $v_result=1;

    // ----- Store the offset position of the file
    $p_header['offset'] = ftell($this->zip_fd);

    // ----- Transform UNIX mtime to DOS format mdate/mtime
    $v_date = getdate($p_header['mtime']);
    $v_mtime = ($v_date['hours']<<11) + ($v_date['minutes']<<5) + $v_date['seconds']/2;
    $v_mdate = (($v_date['year']-1980)<<9) + ($v_date['mon']<<5) + $v_date['mday'];

    // ----- Packed data
    $v_binary_data = pack("VvvvvvVVVvv", 0x04034b50,
	                      $p_header['version_extracted'], $p_header['flag'],
                          $p_header['compression'], $v_mtime, $v_mdate,
                          $p_header['crc'], $p_header['compressed_size'],
						  $p_header['size'],
                          strlen($p_header['stored_filename']),
						  $p_header['extra_len']);

    // ----- Write the first 148 bytes of the header in the archive
    fputs($this->zip_fd, $v_binary_data, 30);

    // ----- Write the variable fields
    if (strlen($p_header['stored_filename']) != 0)
    {
      fputs($this->zip_fd, $p_header['stored_filename'], strlen($p_header['stored_filename']));
    }
    if ($p_header['extra_len'] != 0)
    {
      fputs($this->zip_fd, $p_header['extra'], $p_header['extra_len']);
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privWriteCentralFileHeader()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privWriteCentralFileHeader(&$p_header)
  {
    $v_result=1;

    // TBC
    //for(reset($p_header); $key = key($p_header); next($p_header)) {
    //}

    // ----- Transform UNIX mtime to DOS format mdate/mtime
    $v_date = getdate($p_header['mtime']);
    $v_mtime = ($v_date['hours']<<11) + ($v_date['minutes']<<5) + $v_date['seconds']/2;
    $v_mdate = (($v_date['year']-1980)<<9) + ($v_date['mon']<<5) + $v_date['mday'];


    // ----- Packed data
    $v_binary_data = pack("VvvvvvvVVVvvvvvVV", 0x02014b50,
	                      $p_header['version'], $p_header['version_extracted'],
                          $p_header['flag'], $p_header['compression'],
						  $v_mtime, $v_mdate, $p_header['crc'],
                          $p_header['compressed_size'], $p_header['size'],
                          strlen($p_header['stored_filename']),
						  $p_header['extra_len'], $p_header['comment_len'],
                          $p_header['disk'], $p_header['internal'],
						  $p_header['external'], $p_header['offset']);

    // ----- Write the 42 bytes of the header in the zip file
    fputs($this->zip_fd, $v_binary_data, 46);

    // ----- Write the variable fields
    if (strlen($p_header['stored_filename']) != 0)
    {
      fputs($this->zip_fd, $p_header['stored_filename'], strlen($p_header['stored_filename']));
    }
    if ($p_header['extra_len'] != 0)
    {
      fputs($this->zip_fd, $p_header['extra'], $p_header['extra_len']);
    }
    if ($p_header['comment_len'] != 0)
    {
      fputs($this->zip_fd, $p_header['comment'], $p_header['comment_len']);
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privWriteCentralHeader()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privWriteCentralHeader($p_nb_entries, $p_size, $p_offset, $p_comment)
  {
    $v_result=1;

    // ----- Packed data
    $v_binary_data = pack("VvvvvVVv", 0x06054b50, 0, 0, $p_nb_entries,
	                      $p_nb_entries, $p_size,
						  $p_offset, strlen($p_comment));

    // ----- Write the 22 bytes of the header in the zip file
    fputs($this->zip_fd, $v_binary_data, 22);

    // ----- Write the variable fields
    if (strlen($p_comment) != 0)
    {
      fputs($this->zip_fd, $p_comment, strlen($p_comment));
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privList()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privList(&$p_list)
  {
    $v_result=1;

    // ----- Magic quotes trick
    $this->privDisableMagicQuotes();

    // ----- Open the zip file
    if (($this->zip_fd = @fopen($this->zipname, 'rb')) == 0)
    {
      // ----- Magic quotes trick
      $this->privSwapBackMagicQuotes();
      
      // ----- Error log
      PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in binary read mode');

      // ----- Return
      return PclZip::errorCode();
    }

    // ----- Read the central directory informations
    $v_central_dir = array();
    if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1)
    {
      $this->privSwapBackMagicQuotes();
      return $v_result;
    }

    // ----- Go to beginning of Central Dir
    @rewind($this->zip_fd);
    if (@fseek($this->zip_fd, $v_central_dir['offset']))
    {
      $this->privSwapBackMagicQuotes();

      // ----- Error log
      PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size');

      // ----- Return
      return PclZip::errorCode();
    }

    // ----- Read each entry
    for ($i=0; $i<$v_central_dir['entries']; $i++)
    {
      // ----- Read the file header
      if (($v_result = $this->privReadCentralFileHeader($v_header)) != 1)
      {
        $this->privSwapBackMagicQuotes();
        return $v_result;
      }
      $v_header['index'] = $i;

      // ----- Get the only interesting attributes
      $this->privConvertHeader2FileInfo($v_header, $p_list[$i]);
      unset($v_header);
    }

    // ----- Close the zip file
    $this->privCloseFd();

    // ----- Magic quotes trick
    $this->privSwapBackMagicQuotes();

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privConvertHeader2FileInfo()
  // Description :
  //   This function takes the file informations from the central directory
  //   entries and extract the interesting parameters that will be given back.
  //   The resulting file infos are set in the array $p_info
  //     $p_info['filename'] : Filename with full path. Given by user (add),
  //                           extracted in the filesystem (extract).
  //     $p_info['stored_filename'] : Stored filename in the archive.
  //     $p_info['size'] = Size of the file.
  //     $p_info['compressed_size'] = Compressed size of the file.
  //     $p_info['mtime'] = Last modification date of the file.
  //     $p_info['comment'] = Comment associated with the file.
  //     $p_info['folder'] = true/false : indicates if the entry is a folder or not.
  //     $p_info['status'] = status of the action on the file.
  //     $p_info['crc'] = CRC of the file content.
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privConvertHeader2FileInfo($p_header, &$p_info)
  {
    $v_result=1;

    // ----- Get the interesting attributes
    $v_temp_path = PclZipUtilPathReduction($p_header['filename']);
    $p_info['filename'] = $v_temp_path;
    $v_temp_path = PclZipUtilPathReduction($p_header['stored_filename']);
    $p_info['stored_filename'] = $v_temp_path;
    $p_info['size'] = $p_header['size'];
    $p_info['compressed_size'] = $p_header['compressed_size'];
    $p_info['mtime'] = $p_header['mtime'];
    $p_info['comment'] = $p_header['comment'];
    $p_info['folder'] = (($p_header['external']&0x00000010)==0x00000010);
    $p_info['index'] = $p_header['index'];
    $p_info['status'] = $p_header['status'];
    $p_info['crc'] = $p_header['crc'];

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privExtractByRule()
  // Description :
  //   Extract a file or directory depending of rules (by index, by name, ...)
  // Parameters :
  //   $p_file_list : An array where will be placed the properties of each
  //                  extracted file
  //   $p_path : Path to add while writing the extracted files
  //   $p_remove_path : Path to remove (from the file memorized path) while writing the
  //                    extracted files. If the path does not match the file path,
  //                    the file is extracted with its memorized path.
  //                    $p_remove_path does not apply to 'list' mode.
  //                    $p_path and $p_remove_path are commulative.
  // Return Values :
  //   1 on success,0 or less on error (see error code list)
  // --------------------------------------------------------------------------------
  function privExtractByRule(&$p_file_list, $p_path, $p_remove_path, $p_remove_all_path, &$p_options)
  {
    $v_result=1;

    // ----- Magic quotes trick
    $this->privDisableMagicQuotes();

    // ----- Check the path
    if (   ($p_path == "")
	    || (   (substr($p_path, 0, 1) != "/")
		    && (substr($p_path, 0, 3) != "../")
			&& (substr($p_path,1,2)!=":/")))
      $p_path = "./".$p_path;

    // ----- Reduce the path last (and duplicated) '/'
    if (($p_path != "./") && ($p_path != "/"))
    {
      // ----- Look for the path end '/'
      while (substr($p_path, -1) == "/")
      {
        $p_path = substr($p_path, 0, strlen($p_path)-1);
      }
    }

    // ----- Look for path to remove format (should end by /)
    if (($p_remove_path != "") && (substr($p_remove_path, -1) != '/'))
    {
      $p_remove_path .= '/';
    }
    $p_remove_path_size = strlen($p_remove_path);

    // ----- Open the zip file
    if (($v_result = $this->privOpenFd('rb')) != 1)
    {
      $this->privSwapBackMagicQuotes();
      return $v_result;
    }

    // ----- Read the central directory informations
    $v_central_dir = array();
    if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1)
    {
      // ----- Close the zip file
      $this->privCloseFd();
      $this->privSwapBackMagicQuotes();

      return $v_result;
    }

    // ----- Start at beginning of Central Dir
    $v_pos_entry = $v_central_dir['offset'];

    // ----- Read each entry
    $j_start = 0;
    for ($i=0, $v_nb_extracted=0; $i<$v_central_dir['entries']; $i++)
    {

      // ----- Read next Central dir entry
      @rewind($this->zip_fd);
      if (@fseek($this->zip_fd, $v_pos_entry))
      {
        // ----- Close the zip file
        $this->privCloseFd();
        $this->privSwapBackMagicQuotes();

        // ----- Error log
        PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size');

        // ----- Return
        return PclZip::errorCode();
      }

      // ----- Read the file header
      $v_header = array();
      if (($v_result = $this->privReadCentralFileHeader($v_header)) != 1)
      {
        // ----- Close the zip file
        $this->privCloseFd();
        $this->privSwapBackMagicQuotes();

        return $v_result;
      }

      // ----- Store the index
      $v_header['index'] = $i;

      // ----- Store the file position
      $v_pos_entry = ftell($this->zip_fd);

      // ----- Look for the specific extract rules
      $v_extract = false;

      // ----- Look for extract by name rule
      if (   (isset($p_options[PCLZIP_OPT_BY_NAME]))
          && ($p_options[PCLZIP_OPT_BY_NAME] != 0)) {

          // ----- Look if the filename is in the list
          for ($j=0; ($j<sizeof($p_options[PCLZIP_OPT_BY_NAME])) && (!$v_extract); $j++) {

              // ----- Look for a directory
              if (substr($p_options[PCLZIP_OPT_BY_NAME][$j], -1) == "/") {

                  // ----- Look if the directory is in the filename path
                  if (   (strlen($v_header['stored_filename']) > strlen($p_options[PCLZIP_OPT_BY_NAME][$j]))
                      && (substr($v_header['stored_filename'], 0, strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) == $p_options[PCLZIP_OPT_BY_NAME][$j])) {
                      $v_extract = true;
                  }
              }
              // ----- Look for a filename
              elseif ($v_header['stored_filename'] == $p_options[PCLZIP_OPT_BY_NAME][$j]) {
                  $v_extract = true;
              }
          }
      }

      // ----- Look for extract by ereg rule
      // ereg() is deprecated with PHP 5.3
      /* 
      else if (   (isset($p_options[PCLZIP_OPT_BY_EREG]))
               && ($p_options[PCLZIP_OPT_BY_EREG] != "")) {

          if (ereg($p_options[PCLZIP_OPT_BY_EREG], $v_header['stored_filename'])) {
              $v_extract = true;
          }
      }
      */

      // ----- Look for extract by preg rule
      else if (   (isset($p_options[PCLZIP_OPT_BY_PREG]))
               && ($p_options[PCLZIP_OPT_BY_PREG] != "")) {

          if (preg_match($p_options[PCLZIP_OPT_BY_PREG], $v_header['stored_filename'])) {
              $v_extract = true;
          }
      }

      // ----- Look for extract by index rule
      else if (   (isset($p_options[PCLZIP_OPT_BY_INDEX]))
               && ($p_options[PCLZIP_OPT_BY_INDEX] != 0)) {
          
          // ----- Look if the index is in the list
          for ($j=$j_start; ($j<sizeof($p_options[PCLZIP_OPT_BY_INDEX])) && (!$v_extract); $j++) {

              if (($i>=$p_options[PCLZIP_OPT_BY_INDEX][$j]['start']) && ($i<=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end'])) {
                  $v_extract = true;
              }
              if ($i>=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end']) {
                  $j_start = $j+1;
              }

              if ($p_options[PCLZIP_OPT_BY_INDEX][$j]['start']>$i) {
                  break;
              }
          }
      }

      // ----- Look for no rule, which means extract all the archive
      else {
          $v_extract = true;
      }

	  // ----- Check compression method
	  if (   ($v_extract)
	      && (   ($v_header['compression'] != 8)
		      && ($v_header['compression'] != 0))) {
          $v_header['status'] = 'unsupported_compression';

          // ----- Look for PCLZIP_OPT_STOP_ON_ERROR
          if (   (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR]))
		      && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) {

              $this->privSwapBackMagicQuotes();
              
              PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_COMPRESSION,
			                       "Filename '".$v_header['stored_filename']."' is "
				  	    	  	   ."compressed by an unsupported compression "
				  	    	  	   ."method (".$v_header['compression'].") ");

              return PclZip::errorCode();
		  }
	  }
	  
	  // ----- Check encrypted files
	  if (($v_extract) && (($v_header['flag'] & 1) == 1)) {
          $v_header['status'] = 'unsupported_encryption';

          // ----- Look for PCLZIP_OPT_STOP_ON_ERROR
          if (   (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR]))
		      && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) {

              $this->privSwapBackMagicQuotes();

              PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_ENCRYPTION,
			                       "Unsupported encryption for "
				  	    	  	   ." filename '".$v_header['stored_filename']
								   ."'");

              return PclZip::errorCode();
		  }
    }

      // ----- Look for real extraction
      if (($v_extract) && ($v_header['status'] != 'ok')) {
          $v_result = $this->privConvertHeader2FileInfo($v_header,
		                                        $p_file_list[$v_nb_extracted++]);
          if ($v_result != 1) {
              $this->privCloseFd();
              $this->privSwapBackMagicQuotes();
              return $v_result;
          }

          $v_extract = false;
      }
      
      // ----- Look for real extraction
      if ($v_extract)
      {

        // ----- Go to the file position
        @rewind($this->zip_fd);
        if (@fseek($this->zip_fd, $v_header['offset']))
        {
          // ----- Close the zip file
          $this->privCloseFd();

          $this->privSwapBackMagicQuotes();

          // ----- Error log
          PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size');

          // ----- Return
          return PclZip::errorCode();
        }

        // ----- Look for extraction as string
        if ($p_options[PCLZIP_OPT_EXTRACT_AS_STRING]) {

          $v_string = '';

          // ----- Extracting the file
          $v_result1 = $this->privExtractFileAsString($v_header, $v_string, $p_options);
          if ($v_result1 < 1) {
            $this->privCloseFd();
            $this->privSwapBackMagicQuotes();
            return $v_result1;
          }

          // ----- Get the only interesting attributes
          if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted])) != 1)
          {
            // ----- Close the zip file
            $this->privCloseFd();
            $this->privSwapBackMagicQuotes();

            return $v_result;
          }

          // ----- Set the file content
          $p_file_list[$v_nb_extracted]['content'] = $v_string;

          // ----- Next extracted file
          $v_nb_extracted++;
          
          // ----- Look for user callback abort
          if ($v_result1 == 2) {
          	break;
          }
        }
        // ----- Look for extraction in standard output
        elseif (   (isset($p_options[PCLZIP_OPT_EXTRACT_IN_OUTPUT]))
		        && ($p_options[PCLZIP_OPT_EXTRACT_IN_OUTPUT])) {
          // ----- Extracting the file in standard output
          $v_result1 = $this->privExtractFileInOutput($v_header, $p_options);
          if ($v_result1 < 1) {
            $this->privCloseFd();
            $this->privSwapBackMagicQuotes();
            return $v_result1;
          }

          // ----- Get the only interesting attributes
          if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++])) != 1) {
            $this->privCloseFd();
            $this->privSwapBackMagicQuotes();
            return $v_result;
          }

          // ----- Look for user callback abort
          if ($v_result1 == 2) {
          	break;
          }
        }
        // ----- Look for normal extraction
        else {
          // ----- Extracting the file
          $v_result1 = $this->privExtractFile($v_header,
		                                      $p_path, $p_remove_path,
											  $p_remove_all_path,
											  $p_options);
          if ($v_result1 < 1) {
            $this->privCloseFd();
            $this->privSwapBackMagicQuotes();
            return $v_result1;
          }

          // ----- Get the only interesting attributes
          if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++])) != 1)
          {
            // ----- Close the zip file
            $this->privCloseFd();
            $this->privSwapBackMagicQuotes();

            return $v_result;
          }

          // ----- Look for user callback abort
          if ($v_result1 == 2) {
          	break;
          }
        }
      }
    }

    // ----- Close the zip file
    $this->privCloseFd();
    $this->privSwapBackMagicQuotes();

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privExtractFile()
  // Description :
  // Parameters :
  // Return Values :
  //
  // 1 : ... ?
  // PCLZIP_ERR_USER_ABORTED(2) : User ask for extraction stop in callback
  // --------------------------------------------------------------------------------
  function privExtractFile(&$p_entry, $p_path, $p_remove_path, $p_remove_all_path, &$p_options)
  {
    $v_result=1;

    // ----- Read the file header
    if (($v_result = $this->privReadFileHeader($v_header)) != 1)
    {
      // ----- Return
      return $v_result;
    }


    // ----- Check that the file header is coherent with $p_entry info
    if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) {
        // TBC
    }

    // ----- Look for all path to remove
    if ($p_remove_all_path == true) {
        // ----- Look for folder entry that not need to be extracted
        if (($p_entry['external']&0x00000010)==0x00000010) {

            $p_entry['status'] = "filtered";

            return $v_result;
        }

        // ----- Get the basename of the path
        $p_entry['filename'] = basename($p_entry['filename']);
    }

    // ----- Look for path to remove
    else if ($p_remove_path != "")
    {
      if (PclZipUtilPathInclusion($p_remove_path, $p_entry['filename']) == 2)
      {

        // ----- Change the file status
        $p_entry['status'] = "filtered";

        // ----- Return
        return $v_result;
      }

      $p_remove_path_size = strlen($p_remove_path);
      if (substr($p_entry['filename'], 0, $p_remove_path_size) == $p_remove_path)
      {

        // ----- Remove the path
        $p_entry['filename'] = substr($p_entry['filename'], $p_remove_path_size);

      }
    }

    // ----- Add the path
    if ($p_path != '') {
      $p_entry['filename'] = $p_path."/".$p_entry['filename'];
    }
    
    // ----- Check a base_dir_restriction
    if (isset($p_options[PCLZIP_OPT_EXTRACT_DIR_RESTRICTION])) {
      $v_inclusion
      = PclZipUtilPathInclusion($p_options[PCLZIP_OPT_EXTRACT_DIR_RESTRICTION],
                                $p_entry['filename']); 
      if ($v_inclusion == 0) {

        PclZip::privErrorLog(PCLZIP_ERR_DIRECTORY_RESTRICTION,
			                     "Filename '".$p_entry['filename']."' is "
								 ."outside PCLZIP_OPT_EXTRACT_DIR_RESTRICTION");

        return PclZip::errorCode();
      }
    }

    // ----- Look for pre-extract callback
    if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) {

      // ----- Generate a local information
      $v_local_header = array();
      $this->privConvertHeader2FileInfo($p_entry, $v_local_header);

      // ----- Call the callback
      // Here I do not use call_user_func() because I need to send a reference to the
      // header.
//      eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);');
      $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header);
      if ($v_result == 0) {
        // ----- Change the file status
        $p_entry['status'] = "skipped";
        $v_result = 1;
      }
      
      // ----- Look for abort result
      if ($v_result == 2) {
        // ----- This status is internal and will be changed in 'skipped'
        $p_entry['status'] = "aborted";
      	$v_result = PCLZIP_ERR_USER_ABORTED;
      }

      // ----- Update the informations
      // Only some fields can be modified
      $p_entry['filename'] = $v_local_header['filename'];
    }


    // ----- Look if extraction should be done
    if ($p_entry['status'] == 'ok') {

    // ----- Look for specific actions while the file exist
    if (file_exists($p_entry['filename']))
    {

      // ----- Look if file is a directory
      if (is_dir($p_entry['filename']))
      {

        // ----- Change the file status
        $p_entry['status'] = "already_a_directory";
        
        // ----- Look for PCLZIP_OPT_STOP_ON_ERROR
        // For historical reason first PclZip implementation does not stop
        // when this kind of error occurs.
        if (   (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR]))
		    && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) {

            PclZip::privErrorLog(PCLZIP_ERR_ALREADY_A_DIRECTORY,
			                     "Filename '".$p_entry['filename']."' is "
								 ."already used by an existing directory");

            return PclZip::errorCode();
		    }
      }
      // ----- Look if file is write protected
      else if (!is_writeable($p_entry['filename']))
      {

        // ----- Change the file status
        $p_entry['status'] = "write_protected";

        // ----- Look for PCLZIP_OPT_STOP_ON_ERROR
        // For historical reason first PclZip implementation does not stop
        // when this kind of error occurs.
        if (   (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR]))
		    && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) {

            PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL,
			                     "Filename '".$p_entry['filename']."' exists "
								 ."and is write protected");

            return PclZip::errorCode();
		    }
      }

      // ----- Look if the extracted file is older
      else if (filemtime($p_entry['filename']) > $p_entry['mtime'])
      {
        // ----- Change the file status
        if (   (isset($p_options[PCLZIP_OPT_REPLACE_NEWER]))
		    && ($p_options[PCLZIP_OPT_REPLACE_NEWER]===true)) {
	  	  }
		    else {
            $p_entry['status'] = "newer_exist";

            // ----- Look for PCLZIP_OPT_STOP_ON_ERROR
            // For historical reason first PclZip implementation does not stop
            // when this kind of error occurs.
            if (   (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR]))
		        && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) {

                PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL,
			             "Newer version of '".$p_entry['filename']."' exists "
					    ."and option PCLZIP_OPT_REPLACE_NEWER is not selected");

                return PclZip::errorCode();
		      }
		    }
      }
      else {
      }
    }

    // ----- Check the directory availability and create it if necessary
    else {
      if ((($p_entry['external']&0x00000010)==0x00000010) || (substr($p_entry['filename'], -1) == '/'))
        $v_dir_to_check = $p_entry['filename'];
      else if (!strstr($p_entry['filename'], "/"))
        $v_dir_to_check = "";
      else
        $v_dir_to_check = dirname($p_entry['filename']);

        if (($v_result = $this->privDirCheck($v_dir_to_check, (($p_entry['external']&0x00000010)==0x00000010))) != 1) {
  
          // ----- Change the file status
          $p_entry['status'] = "path_creation_fail";
  
          // ----- Return
          //return $v_result;
          $v_result = 1;
        }
      }
    }

    // ----- Look if extraction should be done
    if ($p_entry['status'] == 'ok') {

      // ----- Do the extraction (if not a folder)
      if (!(($p_entry['external']&0x00000010)==0x00000010))
      {
        // ----- Look for not compressed file
        if ($p_entry['compression'] == 0) {

    		  // ----- Opening destination file
          if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0)
          {

            // ----- Change the file status
            $p_entry['status'] = "write_error";

            // ----- Return
            return $v_result;
          }


          // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks
          $v_size = $p_entry['compressed_size'];
          while ($v_size != 0)
          {
            $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
            $v_buffer = @fread($this->zip_fd, $v_read_size);
            /* Try to speed up the code
            $v_binary_data = pack('a'.$v_read_size, $v_buffer);
            @fwrite($v_dest_file, $v_binary_data, $v_read_size);
            */
            @fwrite($v_dest_file, $v_buffer, $v_read_size);            
            $v_size -= $v_read_size;
          }

          // ----- Closing the destination file
          fclose($v_dest_file);

          // ----- Change the file mtime
          touch($p_entry['filename'], $p_entry['mtime']);
          

        }
        else {
          // ----- TBC
          // Need to be finished
          if (($p_entry['flag'] & 1) == 1) {
            PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_ENCRYPTION, 'File \''.$p_entry['filename'].'\' is encrypted. Encrypted files are not supported.');
            return PclZip::errorCode();
          }


          // ----- Look for using temporary file to unzip
          if ( (!isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) 
              && (isset($p_options[PCLZIP_OPT_TEMP_FILE_ON])
                  || (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD])
                      && ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] <= $p_entry['size'])) ) ) {
            $v_result = $this->privExtractFileUsingTempFile($p_entry, $p_options);
            if ($v_result < PCLZIP_ERR_NO_ERROR) {
              return $v_result;
            }
          }
          
          // ----- Look for extract in memory
          else {

          
            // ----- Read the compressed file in a buffer (one shot)
            $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']);
            
            // ----- Decompress the file
            $v_file_content = @gzinflate($v_buffer);
            unset($v_buffer);
            if ($v_file_content === FALSE) {
  
              // ----- Change the file status
              // TBC
              $p_entry['status'] = "error";
              
              return $v_result;
            }
            
            // ----- Opening destination file
            if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) {
  
              // ----- Change the file status
              $p_entry['status'] = "write_error";
  
              return $v_result;
            }
  
            // ----- Write the uncompressed data
            @fwrite($v_dest_file, $v_file_content, $p_entry['size']);
            unset($v_file_content);
  
            // ----- Closing the destination file
            @fclose($v_dest_file);
            
          }

          // ----- Change the file mtime
          @touch($p_entry['filename'], $p_entry['mtime']);
        }

        // ----- Look for chmod option
        if (isset($p_options[PCLZIP_OPT_SET_CHMOD])) {

          // ----- Change the mode of the file
          @chmod($p_entry['filename'], $p_options[PCLZIP_OPT_SET_CHMOD]);
        }

      }
    }

  	// ----- Change abort status
  	if ($p_entry['status'] == "aborted") {
        $p_entry['status'] = "skipped";
  	}
	
    // ----- Look for post-extract callback
    elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) {

      // ----- Generate a local information
      $v_local_header = array();
      $this->privConvertHeader2FileInfo($p_entry, $v_local_header);

      // ----- Call the callback
      // Here I do not use call_user_func() because I need to send a reference to the
      // header.
//      eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT].'(PCLZIP_CB_POST_EXTRACT, $v_local_header);');
      $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header);

      // ----- Look for abort result
      if ($v_result == 2) {
      	$v_result = PCLZIP_ERR_USER_ABORTED;
      }
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privExtractFileUsingTempFile()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privExtractFileUsingTempFile(&$p_entry, &$p_options)
  {
    $v_result=1;
        
    // ----- Creates a temporary file
    $v_gzip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.gz';
    if (($v_dest_file = @fopen($v_gzip_temp_name, "wb")) == 0) {
      fclose($v_file);
      PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary write mode');
      return PclZip::errorCode();
    }


    // ----- Write gz file format header
    $v_binary_data = pack('va1a1Va1a1', 0x8b1f, Chr($p_entry['compression']), Chr(0x00), time(), Chr(0x00), Chr(3));
    @fwrite($v_dest_file, $v_binary_data, 10);

    // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks
    $v_size = $p_entry['compressed_size'];
    while ($v_size != 0)
    {
      $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
      $v_buffer = @fread($this->zip_fd, $v_read_size);
      //$v_binary_data = pack('a'.$v_read_size, $v_buffer);
      @fwrite($v_dest_file, $v_buffer, $v_read_size);
      $v_size -= $v_read_size;
    }

    // ----- Write gz file format footer
    $v_binary_data = pack('VV', $p_entry['crc'], $p_entry['size']);
    @fwrite($v_dest_file, $v_binary_data, 8);

    // ----- Close the temporary file
    @fclose($v_dest_file);

    // ----- Opening destination file
    if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) {
      $p_entry['status'] = "write_error";
      return $v_result;
    }

    // ----- Open the temporary gz file
    if (($v_src_file = @gzopen($v_gzip_temp_name, 'rb')) == 0) {
      @fclose($v_dest_file);
      $p_entry['status'] = "read_error";
      PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary read mode');
      return PclZip::errorCode();
    }


    // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks
    $v_size = $p_entry['size'];
    while ($v_size != 0) {
      $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
      $v_buffer = @gzread($v_src_file, $v_read_size);
      //$v_binary_data = pack('a'.$v_read_size, $v_buffer);
      @fwrite($v_dest_file, $v_buffer, $v_read_size);
      $v_size -= $v_read_size;
    }
    @fclose($v_dest_file);
    @gzclose($v_src_file);

    // ----- Delete the temporary file
    @unlink($v_gzip_temp_name);
    
    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privExtractFileInOutput()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privExtractFileInOutput(&$p_entry, &$p_options)
  {
    $v_result=1;

    // ----- Read the file header
    if (($v_result = $this->privReadFileHeader($v_header)) != 1) {
      return $v_result;
    }


    // ----- Check that the file header is coherent with $p_entry info
    if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) {
        // TBC
    }

    // ----- Look for pre-extract callback
    if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) {

      // ----- Generate a local information
      $v_local_header = array();
      $this->privConvertHeader2FileInfo($p_entry, $v_local_header);

      // ----- Call the callback
      // Here I do not use call_user_func() because I need to send a reference to the
      // header.
//      eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);');
      $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header);
      if ($v_result == 0) {
        // ----- Change the file status
        $p_entry['status'] = "skipped";
        $v_result = 1;
      }

      // ----- Look for abort result
      if ($v_result == 2) {
        // ----- This status is internal and will be changed in 'skipped'
        $p_entry['status'] = "aborted";
      	$v_result = PCLZIP_ERR_USER_ABORTED;
      }

      // ----- Update the informations
      // Only some fields can be modified
      $p_entry['filename'] = $v_local_header['filename'];
    }

    // ----- Trace

    // ----- Look if extraction should be done
    if ($p_entry['status'] == 'ok') {

      // ----- Do the extraction (if not a folder)
      if (!(($p_entry['external']&0x00000010)==0x00000010)) {
        // ----- Look for not compressed file
        if ($p_entry['compressed_size'] == $p_entry['size']) {

          // ----- Read the file in a buffer (one shot)
          $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']);

          // ----- Send the file to the output
          echo $v_buffer;
          unset($v_buffer);
        }
        else {

          // ----- Read the compressed file in a buffer (one shot)
          $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']);
          
          // ----- Decompress the file
          $v_file_content = gzinflate($v_buffer);
          unset($v_buffer);

          // ----- Send the file to the output
          echo $v_file_content;
          unset($v_file_content);
        }
      }
    }

	// ----- Change abort status
	if ($p_entry['status'] == "aborted") {
      $p_entry['status'] = "skipped";
	}

    // ----- Look for post-extract callback
    elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) {

      // ----- Generate a local information
      $v_local_header = array();
      $this->privConvertHeader2FileInfo($p_entry, $v_local_header);

      // ----- Call the callback
      // Here I do not use call_user_func() because I need to send a reference to the
      // header.
//      eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT].'(PCLZIP_CB_POST_EXTRACT, $v_local_header);');
      $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header);

      // ----- Look for abort result
      if ($v_result == 2) {
      	$v_result = PCLZIP_ERR_USER_ABORTED;
      }
    }

    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privExtractFileAsString()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privExtractFileAsString(&$p_entry, &$p_string, &$p_options)
  {
    $v_result=1;

    // ----- Read the file header
    $v_header = array();
    if (($v_result = $this->privReadFileHeader($v_header)) != 1)
    {
      // ----- Return
      return $v_result;
    }


    // ----- Check that the file header is coherent with $p_entry info
    if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) {
        // TBC
    }

    // ----- Look for pre-extract callback
    if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) {

      // ----- Generate a local information
      $v_local_header = array();
      $this->privConvertHeader2FileInfo($p_entry, $v_local_header);

      // ----- Call the callback
      // Here I do not use call_user_func() because I need to send a reference to the
      // header.
//      eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);');
      $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header);
      if ($v_result == 0) {
        // ----- Change the file status
        $p_entry['status'] = "skipped";
        $v_result = 1;
      }
      
      // ----- Look for abort result
      if ($v_result == 2) {
        // ----- This status is internal and will be changed in 'skipped'
        $p_entry['status'] = "aborted";
      	$v_result = PCLZIP_ERR_USER_ABORTED;
      }

      // ----- Update the informations
      // Only some fields can be modified
      $p_entry['filename'] = $v_local_header['filename'];
    }


    // ----- Look if extraction should be done
    if ($p_entry['status'] == 'ok') {

      // ----- Do the extraction (if not a folder)
      if (!(($p_entry['external']&0x00000010)==0x00000010)) {
        // ----- Look for not compressed file
  //      if ($p_entry['compressed_size'] == $p_entry['size'])
        if ($p_entry['compression'] == 0) {
  
          // ----- Reading the file
          $p_string = @fread($this->zip_fd, $p_entry['compressed_size']);
        }
        else {
  
          // ----- Reading the file
          $v_data = @fread($this->zip_fd, $p_entry['compressed_size']);
          
          // ----- Decompress the file
          if (($p_string = @gzinflate($v_data)) === FALSE) {
              // TBC
          }
        }
  
        // ----- Trace
      }
      else {
          // TBC : error : can not extract a folder in a string
      }
      
    }

  	// ----- Change abort status
  	if ($p_entry['status'] == "aborted") {
        $p_entry['status'] = "skipped";
  	}
	
    // ----- Look for post-extract callback
    elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) {

      // ----- Generate a local information
      $v_local_header = array();
      $this->privConvertHeader2FileInfo($p_entry, $v_local_header);
      
      // ----- Swap the content to header
      $v_local_header['content'] = $p_string;
      $p_string = '';

      // ----- Call the callback
      // Here I do not use call_user_func() because I need to send a reference to the
      // header.
//      eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT].'(PCLZIP_CB_POST_EXTRACT, $v_local_header);');
      $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header);

      // ----- Swap back the content to header
      $p_string = $v_local_header['content'];
      unset($v_local_header['content']);

      // ----- Look for abort result
      if ($v_result == 2) {
      	$v_result = PCLZIP_ERR_USER_ABORTED;
      }
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privReadFileHeader()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privReadFileHeader(&$p_header)
  {
    $v_result=1;

    // ----- Read the 4 bytes signature
    $v_binary_data = @fread($this->zip_fd, 4);
    $v_data = unpack('Vid', $v_binary_data);

    // ----- Check signature
    if ($v_data['id'] != 0x04034b50)
    {

      // ----- Error log
      PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Invalid archive structure');

      // ----- Return
      return PclZip::errorCode();
    }

    // ----- Read the first 42 bytes of the header
    $v_binary_data = fread($this->zip_fd, 26);

    // ----- Look for invalid block size
    if (strlen($v_binary_data) != 26)
    {
      $p_header['filename'] = "";
      $p_header['status'] = "invalid_header";

      // ----- Error log
      PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data));

      // ----- Return
      return PclZip::errorCode();
    }

    // ----- Extract the values
    $v_data = unpack('vversion/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len', $v_binary_data);

    // ----- Get filename
    $p_header['filename'] = fread($this->zip_fd, $v_data['filename_len']);

    // ----- Get extra_fields
    if ($v_data['extra_len'] != 0) {
      $p_header['extra'] = fread($this->zip_fd, $v_data['extra_len']);
    }
    else {
      $p_header['extra'] = '';
    }

    // ----- Extract properties
    $p_header['version_extracted'] = $v_data['version'];
    $p_header['compression'] = $v_data['compression'];
    $p_header['size'] = $v_data['size'];
    $p_header['compressed_size'] = $v_data['compressed_size'];
    $p_header['crc'] = $v_data['crc'];
    $p_header['flag'] = $v_data['flag'];
    $p_header['filename_len'] = $v_data['filename_len'];

    // ----- Recuperate date in UNIX format
    $p_header['mdate'] = $v_data['mdate'];
    $p_header['mtime'] = $v_data['mtime'];
    if ($p_header['mdate'] && $p_header['mtime'])
    {
      // ----- Extract time
      $v_hour = ($p_header['mtime'] & 0xF800) >> 11;
      $v_minute = ($p_header['mtime'] & 0x07E0) >> 5;
      $v_seconde = ($p_header['mtime'] & 0x001F)*2;

      // ----- Extract date
      $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980;
      $v_month = ($p_header['mdate'] & 0x01E0) >> 5;
      $v_day = $p_header['mdate'] & 0x001F;

      // ----- Get UNIX date format
      $p_header['mtime'] = @mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year);

    }
    else
    {
      $p_header['mtime'] = time();
    }

    // TBC
    //for(reset($v_data); $key = key($v_data); next($v_data)) {
    //}

    // ----- Set the stored filename
    $p_header['stored_filename'] = $p_header['filename'];

    // ----- Set the status field
    $p_header['status'] = "ok";

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privReadCentralFileHeader()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privReadCentralFileHeader(&$p_header)
  {
    $v_result=1;

    // ----- Read the 4 bytes signature
    $v_binary_data = @fread($this->zip_fd, 4);
    $v_data = unpack('Vid', $v_binary_data);

    // ----- Check signature
    if ($v_data['id'] != 0x02014b50)
    {

      // ----- Error log
      PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Invalid archive structure');

      // ----- Return
      return PclZip::errorCode();
    }

    // ----- Read the first 42 bytes of the header
    $v_binary_data = fread($this->zip_fd, 42);

    // ----- Look for invalid block size
    if (strlen($v_binary_data) != 42)
    {
      $p_header['filename'] = "";
      $p_header['status'] = "invalid_header";

      // ----- Error log
      PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data));

      // ----- Return
      return PclZip::errorCode();
    }

    // ----- Extract the values
    $p_header = unpack('vversion/vversion_extracted/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len/vcomment_len/vdisk/vinternal/Vexternal/Voffset', $v_binary_data);

    // ----- Get filename
    if ($p_header['filename_len'] != 0)
      $p_header['filename'] = fread($this->zip_fd, $p_header['filename_len']);
    else
      $p_header['filename'] = '';

    // ----- Get extra
    if ($p_header['extra_len'] != 0)
      $p_header['extra'] = fread($this->zip_fd, $p_header['extra_len']);
    else
      $p_header['extra'] = '';

    // ----- Get comment
    if ($p_header['comment_len'] != 0)
      $p_header['comment'] = fread($this->zip_fd, $p_header['comment_len']);
    else
      $p_header['comment'] = '';

    // ----- Extract properties

    // ----- Recuperate date in UNIX format
    //if ($p_header['mdate'] && $p_header['mtime'])
    // TBC : bug : this was ignoring time with 0/0/0
    if (1)
    {
      // ----- Extract time
      $v_hour = ($p_header['mtime'] & 0xF800) >> 11;
      $v_minute = ($p_header['mtime'] & 0x07E0) >> 5;
      $v_seconde = ($p_header['mtime'] & 0x001F)*2;

      // ----- Extract date
      $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980;
      $v_month = ($p_header['mdate'] & 0x01E0) >> 5;
      $v_day = $p_header['mdate'] & 0x001F;

      // ----- Get UNIX date format
      $p_header['mtime'] = @mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year);

    }
    else
    {
      $p_header['mtime'] = time();
    }

    // ----- Set the stored filename
    $p_header['stored_filename'] = $p_header['filename'];

    // ----- Set default status to ok
    $p_header['status'] = 'ok';

    // ----- Look if it is a directory
    if (substr($p_header['filename'], -1) == '/') {
      //$p_header['external'] = 0x41FF0010;
      $p_header['external'] = 0x00000010;
    }


    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privCheckFileHeaders()
  // Description :
  // Parameters :
  // Return Values :
  //   1 on success,
  //   0 on error;
  // --------------------------------------------------------------------------------
  function privCheckFileHeaders(&$p_local_header, &$p_central_header)
  {
    $v_result=1;

  	// ----- Check the static values
  	// TBC
  	if ($p_local_header['filename'] != $p_central_header['filename']) {
  	}
  	if ($p_local_header['version_extracted'] != $p_central_header['version_extracted']) {
  	}
  	if ($p_local_header['flag'] != $p_central_header['flag']) {
  	}
  	if ($p_local_header['compression'] != $p_central_header['compression']) {
  	}
  	if ($p_local_header['mtime'] != $p_central_header['mtime']) {
  	}
  	if ($p_local_header['filename_len'] != $p_central_header['filename_len']) {
  	}
  
  	// ----- Look for flag bit 3
  	if (($p_local_header['flag'] & 8) == 8) {
          $p_local_header['size'] = $p_central_header['size'];
          $p_local_header['compressed_size'] = $p_central_header['compressed_size'];
          $p_local_header['crc'] = $p_central_header['crc'];
  	}

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privReadEndCentralDir()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privReadEndCentralDir(&$p_central_dir)
  {
    $v_result=1;

    // ----- Go to the end of the zip file
    $v_size = filesize($this->zipname);
    @fseek($this->zip_fd, $v_size);
    if (@ftell($this->zip_fd) != $v_size)
    {
      // ----- Error log
      PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to go to the end of the archive \''.$this->zipname.'\'');

      // ----- Return
      return PclZip::errorCode();
    }

    // ----- First try : look if this is an archive with no commentaries (most of the time)
    // in this case the end of central dir is at 22 bytes of the file end
    $v_found = 0;
    if ($v_size > 26) {
      @fseek($this->zip_fd, $v_size-22);
      if (($v_pos = @ftell($this->zip_fd)) != ($v_size-22))
      {
        // ----- Error log
        PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to seek back to the middle of the archive \''.$this->zipname.'\'');

        // ----- Return
        return PclZip::errorCode();
      }

      // ----- Read for bytes
      $v_binary_data = @fread($this->zip_fd, 4);
      $v_data = @unpack('Vid', $v_binary_data);

      // ----- Check signature
      if ($v_data['id'] == 0x06054b50) {
        $v_found = 1;
      }

      $v_pos = ftell($this->zip_fd);
    }

    // ----- Go back to the maximum possible size of the Central Dir End Record
    if (!$v_found) {
      $v_maximum_size = 65557; // 0xFFFF + 22;
      if ($v_maximum_size > $v_size)
        $v_maximum_size = $v_size;
      @fseek($this->zip_fd, $v_size-$v_maximum_size);
      if (@ftell($this->zip_fd) != ($v_size-$v_maximum_size))
      {
        // ----- Error log
        PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to seek back to the middle of the archive \''.$this->zipname.'\'');

        // ----- Return
        return PclZip::errorCode();
      }

      // ----- Read byte per byte in order to find the signature
      $v_pos = ftell($this->zip_fd);
      $v_bytes = 0x00000000;
      while ($v_pos < $v_size)
      {
        // ----- Read a byte
        $v_byte = @fread($this->zip_fd, 1);

        // -----  Add the byte
        //$v_bytes = ($v_bytes << 8) | Ord($v_byte);
        // Note we mask the old value down such that once shifted we can never end up with more than a 32bit number 
        // Otherwise on systems where we have 64bit integers the check below for the magic number will fail. 
        $v_bytes = ( ($v_bytes & 0xFFFFFF) << 8) | Ord($v_byte); 

        // ----- Compare the bytes
        if ($v_bytes == 0x504b0506)
        {
          $v_pos++;
          break;
        }

        $v_pos++;
      }

      // ----- Look if not found end of central dir
      if ($v_pos == $v_size)
      {

        // ----- Error log
        PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Unable to find End of Central Dir Record signature");

        // ----- Return
        return PclZip::errorCode();
      }
    }

    // ----- Read the first 18 bytes of the header
    $v_binary_data = fread($this->zip_fd, 18);

    // ----- Look for invalid block size
    if (strlen($v_binary_data) != 18)
    {

      // ----- Error log
      PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid End of Central Dir Record size : ".strlen($v_binary_data));

      // ----- Return
      return PclZip::errorCode();
    }

    // ----- Extract the values
    $v_data = unpack('vdisk/vdisk_start/vdisk_entries/ventries/Vsize/Voffset/vcomment_size', $v_binary_data);

    // ----- Check the global size
    if (($v_pos + $v_data['comment_size'] + 18) != $v_size) {

	  // ----- Removed in release 2.2 see readme file
	  // The check of the file size is a little too strict.
	  // Some bugs where found when a zip is encrypted/decrypted with 'crypt'.
	  // While decrypted, zip has training 0 bytes
	  if (0) {
      // ----- Error log
      PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT,
	                       'The central dir is not at the end of the archive.'
						   .' Some trailing bytes exists after the archive.');

      // ----- Return
      return PclZip::errorCode();
	  }
    }

    // ----- Get comment
    if ($v_data['comment_size'] != 0) {
      $p_central_dir['comment'] = fread($this->zip_fd, $v_data['comment_size']);
    }
    else
      $p_central_dir['comment'] = '';

    $p_central_dir['entries'] = $v_data['entries'];
    $p_central_dir['disk_entries'] = $v_data['disk_entries'];
    $p_central_dir['offset'] = $v_data['offset'];
    $p_central_dir['size'] = $v_data['size'];
    $p_central_dir['disk'] = $v_data['disk'];
    $p_central_dir['disk_start'] = $v_data['disk_start'];

    // TBC
    //for(reset($p_central_dir); $key = key($p_central_dir); next($p_central_dir)) {
    //}

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privDeleteByRule()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privDeleteByRule(&$p_result_list, &$p_options)
  {
    $v_result=1;
    $v_list_detail = array();

    // ----- Open the zip file
    if (($v_result=$this->privOpenFd('rb')) != 1)
    {
      // ----- Return
      return $v_result;
    }

    // ----- Read the central directory informations
    $v_central_dir = array();
    if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1)
    {
      $this->privCloseFd();
      return $v_result;
    }

    // ----- Go to beginning of File
    @rewind($this->zip_fd);

    // ----- Scan all the files
    // ----- Start at beginning of Central Dir
    $v_pos_entry = $v_central_dir['offset'];
    @rewind($this->zip_fd);
    if (@fseek($this->zip_fd, $v_pos_entry))
    {
      // ----- Close the zip file
      $this->privCloseFd();

      // ----- Error log
      PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size');

      // ----- Return
      return PclZip::errorCode();
    }

    // ----- Read each entry
    $v_header_list = array();
    $j_start = 0;
    for ($i=0, $v_nb_extracted=0; $i<$v_central_dir['entries']; $i++)
    {

      // ----- Read the file header
      $v_header_list[$v_nb_extracted] = array();
      if (($v_result = $this->privReadCentralFileHeader($v_header_list[$v_nb_extracted])) != 1)
      {
        // ----- Close the zip file
        $this->privCloseFd();

        return $v_result;
      }


      // ----- Store the index
      $v_header_list[$v_nb_extracted]['index'] = $i;

      // ----- Look for the specific extract rules
      $v_found = false;

      // ----- Look for extract by name rule
      if (   (isset($p_options[PCLZIP_OPT_BY_NAME]))
          && ($p_options[PCLZIP_OPT_BY_NAME] != 0)) {

          // ----- Look if the filename is in the list
          for ($j=0; ($j<sizeof($p_options[PCLZIP_OPT_BY_NAME])) && (!$v_found); $j++) {

              // ----- Look for a directory
              if (substr($p_options[PCLZIP_OPT_BY_NAME][$j], -1) == "/") {

                  // ----- Look if the directory is in the filename path
                  if (   (strlen($v_header_list[$v_nb_extracted]['stored_filename']) > strlen($p_options[PCLZIP_OPT_BY_NAME][$j]))
                      && (substr($v_header_list[$v_nb_extracted]['stored_filename'], 0, strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) == $p_options[PCLZIP_OPT_BY_NAME][$j])) {
                      $v_found = true;
                  }
                  elseif (   (($v_header_list[$v_nb_extracted]['external']&0x00000010)==0x00000010) /* Indicates a folder */
                          && ($v_header_list[$v_nb_extracted]['stored_filename'].'/' == $p_options[PCLZIP_OPT_BY_NAME][$j])) {
                      $v_found = true;
                  }
              }
              // ----- Look for a filename
              elseif ($v_header_list[$v_nb_extracted]['stored_filename'] == $p_options[PCLZIP_OPT_BY_NAME][$j]) {
                  $v_found = true;
              }
          }
      }

      // ----- Look for extract by ereg rule
      // ereg() is deprecated with PHP 5.3
      /*
      else if (   (isset($p_options[PCLZIP_OPT_BY_EREG]))
               && ($p_options[PCLZIP_OPT_BY_EREG] != "")) {

          if (ereg($p_options[PCLZIP_OPT_BY_EREG], $v_header_list[$v_nb_extracted]['stored_filename'])) {
              $v_found = true;
          }
      }
      */

      // ----- Look for extract by preg rule
      else if (   (isset($p_options[PCLZIP_OPT_BY_PREG]))
               && ($p_options[PCLZIP_OPT_BY_PREG] != "")) {

          if (preg_match($p_options[PCLZIP_OPT_BY_PREG], $v_header_list[$v_nb_extracted]['stored_filename'])) {
              $v_found = true;
          }
      }

      // ----- Look for extract by index rule
      else if (   (isset($p_options[PCLZIP_OPT_BY_INDEX]))
               && ($p_options[PCLZIP_OPT_BY_INDEX] != 0)) {

          // ----- Look if the index is in the list
          for ($j=$j_start; ($j<sizeof($p_options[PCLZIP_OPT_BY_INDEX])) && (!$v_found); $j++) {

              if (($i>=$p_options[PCLZIP_OPT_BY_INDEX][$j]['start']) && ($i<=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end'])) {
                  $v_found = true;
              }
              if ($i>=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end']) {
                  $j_start = $j+1;
              }

              if ($p_options[PCLZIP_OPT_BY_INDEX][$j]['start']>$i) {
                  break;
              }
          }
      }
      else {
      	$v_found = true;
      }

      // ----- Look for deletion
      if ($v_found)
      {
        unset($v_header_list[$v_nb_extracted]);
      }
      else
      {
        $v_nb_extracted++;
      }
    }

    // ----- Look if something need to be deleted
    if ($v_nb_extracted > 0) {

        // ----- Creates a temporay file
        $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp';

        // ----- Creates a temporary zip archive
        $v_temp_zip = new PclZip($v_zip_temp_name);

        // ----- Open the temporary zip file in write mode
        if (($v_result = $v_temp_zip->privOpenFd('wb')) != 1) {
            $this->privCloseFd();

            // ----- Return
            return $v_result;
        }

        // ----- Look which file need to be kept
        for ($i=0; $i<sizeof($v_header_list); $i++) {

            // ----- Calculate the position of the header
            @rewind($this->zip_fd);
            if (@fseek($this->zip_fd,  $v_header_list[$i]['offset'])) {
                // ----- Close the zip file
                $this->privCloseFd();
                $v_temp_zip->privCloseFd();
                @unlink($v_zip_temp_name);

                // ----- Error log
                PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size');

                // ----- Return
                return PclZip::errorCode();
            }

            // ----- Read the file header
            $v_local_header = array();
            if (($v_result = $this->privReadFileHeader($v_local_header)) != 1) {
                // ----- Close the zip file
                $this->privCloseFd();
                $v_temp_zip->privCloseFd();
                @unlink($v_zip_temp_name);

                // ----- Return
                return $v_result;
            }
            
            // ----- Check that local file header is same as central file header
            if ($this->privCheckFileHeaders($v_local_header,
			                                $v_header_list[$i]) != 1) {
                // TBC
            }
            unset($v_local_header);

            // ----- Write the file header
            if (($v_result = $v_temp_zip->privWriteFileHeader($v_header_list[$i])) != 1) {
                // ----- Close the zip file
                $this->privCloseFd();
                $v_temp_zip->privCloseFd();
                @unlink($v_zip_temp_name);

                // ----- Return
                return $v_result;
            }

            // ----- Read/write the data block
            if (($v_result = PclZipUtilCopyBlock($this->zip_fd, $v_temp_zip->zip_fd, $v_header_list[$i]['compressed_size'])) != 1) {
                // ----- Close the zip file
                $this->privCloseFd();
                $v_temp_zip->privCloseFd();
                @unlink($v_zip_temp_name);

                // ----- Return
                return $v_result;
            }
        }

        // ----- Store the offset of the central dir
        $v_offset = @ftell($v_temp_zip->zip_fd);

        // ----- Re-Create the Central Dir files header
        for ($i=0; $i<sizeof($v_header_list); $i++) {
            // ----- Create the file header
            if (($v_result = $v_temp_zip->privWriteCentralFileHeader($v_header_list[$i])) != 1) {
                $v_temp_zip->privCloseFd();
                $this->privCloseFd();
                @unlink($v_zip_temp_name);

                // ----- Return
                return $v_result;
            }

            // ----- Transform the header to a 'usable' info
            $v_temp_zip->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]);
        }


        // ----- Zip file comment
        $v_comment = '';
        if (isset($p_options[PCLZIP_OPT_COMMENT])) {
          $v_comment = $p_options[PCLZIP_OPT_COMMENT];
        }

        // ----- Calculate the size of the central header
        $v_size = @ftell($v_temp_zip->zip_fd)-$v_offset;

        // ----- Create the central dir footer
        if (($v_result = $v_temp_zip->privWriteCentralHeader(sizeof($v_header_list), $v_size, $v_offset, $v_comment)) != 1) {
            // ----- Reset the file list
            unset($v_header_list);
            $v_temp_zip->privCloseFd();
            $this->privCloseFd();
            @unlink($v_zip_temp_name);

            // ----- Return
            return $v_result;
        }

        // ----- Close
        $v_temp_zip->privCloseFd();
        $this->privCloseFd();

        // ----- Delete the zip file
        // TBC : I should test the result ...
        @unlink($this->zipname);

        // ----- Rename the temporary file
        // TBC : I should test the result ...
        //@rename($v_zip_temp_name, $this->zipname);
        PclZipUtilRename($v_zip_temp_name, $this->zipname);
    
        // ----- Destroy the temporary archive
        unset($v_temp_zip);
    }
    
    // ----- Remove every files : reset the file
    else if ($v_central_dir['entries'] != 0) {
        $this->privCloseFd();

        if (($v_result = $this->privOpenFd('wb')) != 1) {
          return $v_result;
        }

        if (($v_result = $this->privWriteCentralHeader(0, 0, 0, '')) != 1) {
          return $v_result;
        }

        $this->privCloseFd();
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privDirCheck()
  // Description :
  //   Check if a directory exists, if not it creates it and all the parents directory
  //   which may be useful.
  // Parameters :
  //   $p_dir : Directory path to check.
  // Return Values :
  //    1 : OK
  //   -1 : Unable to create directory
  // --------------------------------------------------------------------------------
  function privDirCheck($p_dir, $p_is_dir=false)
  {
    $v_result = 1;


    // ----- Remove the final '/'
    if (($p_is_dir) && (substr($p_dir, -1)=='/'))
    {
      $p_dir = substr($p_dir, 0, strlen($p_dir)-1);
    }

    // ----- Check the directory availability
    if ((is_dir($p_dir)) || ($p_dir == ""))
    {
      return 1;
    }

    // ----- Extract parent directory
    $p_parent_dir = dirname($p_dir);

    // ----- Just a check
    if ($p_parent_dir != $p_dir)
    {
      // ----- Look for parent directory
      if ($p_parent_dir != "")
      {
        if (($v_result = $this->privDirCheck($p_parent_dir)) != 1)
        {
          return $v_result;
        }
      }
    }

    // ----- Create the directory
    if (!@mkdir($p_dir, 0777))
    {
      // ----- Error log
      PclZip::privErrorLog(PCLZIP_ERR_DIR_CREATE_FAIL, "Unable to create directory '$p_dir'");

      // ----- Return
      return PclZip::errorCode();
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privMerge()
  // Description :
  //   If $p_archive_to_add does not exist, the function exit with a success result.
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privMerge(&$p_archive_to_add)
  {
    $v_result=1;

    // ----- Look if the archive_to_add exists
    if (!is_file($p_archive_to_add->zipname))
    {

      // ----- Nothing to merge, so merge is a success
      $v_result = 1;

      // ----- Return
      return $v_result;
    }

    // ----- Look if the archive exists
    if (!is_file($this->zipname))
    {

      // ----- Do a duplicate
      $v_result = $this->privDuplicate($p_archive_to_add->zipname);

      // ----- Return
      return $v_result;
    }

    // ----- Open the zip file
    if (($v_result=$this->privOpenFd('rb')) != 1)
    {
      // ----- Return
      return $v_result;
    }

    // ----- Read the central directory informations
    $v_central_dir = array();
    if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1)
    {
      $this->privCloseFd();
      return $v_result;
    }

    // ----- Go to beginning of File
    @rewind($this->zip_fd);

    // ----- Open the archive_to_add file
    if (($v_result=$p_archive_to_add->privOpenFd('rb')) != 1)
    {
      $this->privCloseFd();

      // ----- Return
      return $v_result;
    }

    // ----- Read the central directory informations
    $v_central_dir_to_add = array();
    if (($v_result = $p_archive_to_add->privReadEndCentralDir($v_central_dir_to_add)) != 1)
    {
      $this->privCloseFd();
      $p_archive_to_add->privCloseFd();

      return $v_result;
    }

    // ----- Go to beginning of File
    @rewind($p_archive_to_add->zip_fd);

    // ----- Creates a temporay file
    $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp';

    // ----- Open the temporary file in write mode
    if (($v_zip_temp_fd = @fopen($v_zip_temp_name, 'wb')) == 0)
    {
      $this->privCloseFd();
      $p_archive_to_add->privCloseFd();

      PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_zip_temp_name.'\' in binary write mode');

      // ----- Return
      return PclZip::errorCode();
    }

    // ----- Copy the files from the archive to the temporary file
    // TBC : Here I should better append the file and go back to erase the central dir
    $v_size = $v_central_dir['offset'];
    while ($v_size != 0)
    {
      $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
      $v_buffer = fread($this->zip_fd, $v_read_size);
      @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size);
      $v_size -= $v_read_size;
    }

    // ----- Copy the files from the archive_to_add into the temporary file
    $v_size = $v_central_dir_to_add['offset'];
    while ($v_size != 0)
    {
      $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
      $v_buffer = fread($p_archive_to_add->zip_fd, $v_read_size);
      @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size);
      $v_size -= $v_read_size;
    }

    // ----- Store the offset of the central dir
    $v_offset = @ftell($v_zip_temp_fd);

    // ----- Copy the block of file headers from the old archive
    $v_size = $v_central_dir['size'];
    while ($v_size != 0)
    {
      $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
      $v_buffer = @fread($this->zip_fd, $v_read_size);
      @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size);
      $v_size -= $v_read_size;
    }

    // ----- Copy the block of file headers from the archive_to_add
    $v_size = $v_central_dir_to_add['size'];
    while ($v_size != 0)
    {
      $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
      $v_buffer = @fread($p_archive_to_add->zip_fd, $v_read_size);
      @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size);
      $v_size -= $v_read_size;
    }

    // ----- Merge the file comments
    $v_comment = $v_central_dir['comment'].' '.$v_central_dir_to_add['comment'];

    // ----- Calculate the size of the (new) central header
    $v_size = @ftell($v_zip_temp_fd)-$v_offset;

    // ----- Swap the file descriptor
    // Here is a trick : I swap the temporary fd with the zip fd, in order to use
    // the following methods on the temporary fil and not the real archive fd
    $v_swap = $this->zip_fd;
    $this->zip_fd = $v_zip_temp_fd;
    $v_zip_temp_fd = $v_swap;

    // ----- Create the central dir footer
    if (($v_result = $this->privWriteCentralHeader($v_central_dir['entries']+$v_central_dir_to_add['entries'], $v_size, $v_offset, $v_comment)) != 1)
    {
      $this->privCloseFd();
      $p_archive_to_add->privCloseFd();
      @fclose($v_zip_temp_fd);
      $this->zip_fd = null;

      // ----- Reset the file list
      unset($v_header_list);

      // ----- Return
      return $v_result;
    }

    // ----- Swap back the file descriptor
    $v_swap = $this->zip_fd;
    $this->zip_fd = $v_zip_temp_fd;
    $v_zip_temp_fd = $v_swap;

    // ----- Close
    $this->privCloseFd();
    $p_archive_to_add->privCloseFd();

    // ----- Close the temporary file
    @fclose($v_zip_temp_fd);

    // ----- Delete the zip file
    // TBC : I should test the result ...
    @unlink($this->zipname);

    // ----- Rename the temporary file
    // TBC : I should test the result ...
    //@rename($v_zip_temp_name, $this->zipname);
    PclZipUtilRename($v_zip_temp_name, $this->zipname);

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privDuplicate()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privDuplicate($p_archive_filename)
  {
    $v_result=1;

    // ----- Look if the $p_archive_filename exists
    if (!is_file($p_archive_filename))
    {

      // ----- Nothing to duplicate, so duplicate is a success.
      $v_result = 1;

      // ----- Return
      return $v_result;
    }

    // ----- Open the zip file
    if (($v_result=$this->privOpenFd('wb')) != 1)
    {
      // ----- Return
      return $v_result;
    }

    // ----- Open the temporary file in write mode
    if (($v_zip_temp_fd = @fopen($p_archive_filename, 'rb')) == 0)
    {
      $this->privCloseFd();

      PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive file \''.$p_archive_filename.'\' in binary write mode');

      // ----- Return
      return PclZip::errorCode();
    }

    // ----- Copy the files from the archive to the temporary file
    // TBC : Here I should better append the file and go back to erase the central dir
    $v_size = filesize($p_archive_filename);
    while ($v_size != 0)
    {
      $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
      $v_buffer = fread($v_zip_temp_fd, $v_read_size);
      @fwrite($this->zip_fd, $v_buffer, $v_read_size);
      $v_size -= $v_read_size;
    }

    // ----- Close
    $this->privCloseFd();

    // ----- Close the temporary file
    @fclose($v_zip_temp_fd);

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privErrorLog()
  // Description :
  // Parameters :
  // --------------------------------------------------------------------------------
  function privErrorLog($p_error_code=0, $p_error_string='')
  {
    if (PCLZIP_ERROR_EXTERNAL == 1) {
      PclError($p_error_code, $p_error_string);
    }
    else {
      $this->error_code = $p_error_code;
      $this->error_string = $p_error_string;
    }
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privErrorReset()
  // Description :
  // Parameters :
  // --------------------------------------------------------------------------------
  function privErrorReset()
  {
    if (PCLZIP_ERROR_EXTERNAL == 1) {
      PclErrorReset();
    }
    else {
      $this->error_code = 0;
      $this->error_string = '';
    }
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privDisableMagicQuotes()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privDisableMagicQuotes()
  {
    $v_result=1;

    // ----- Look if function exists
    if (   (!function_exists("get_magic_quotes_runtime"))
	    || (!function_exists("set_magic_quotes_runtime"))) {
      return $v_result;
	}

    // ----- Look if already done
    if ($this->magic_quotes_status != -1) {
      return $v_result;
	}

	// ----- Get and memorize the magic_quote value
	$this->magic_quotes_status = @get_magic_quotes_runtime();

	// ----- Disable magic_quotes
	if ($this->magic_quotes_status == 1) {
	  @set_magic_quotes_runtime(0);
	}

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privSwapBackMagicQuotes()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privSwapBackMagicQuotes()
  {
    $v_result=1;

    // ----- Look if function exists
    if (   (!function_exists("get_magic_quotes_runtime"))
	    || (!function_exists("set_magic_quotes_runtime"))) {
      return $v_result;
	}

    // ----- Look if something to do
    if ($this->magic_quotes_status != -1) {
      return $v_result;
	}

	// ----- Swap back magic_quotes
	if ($this->magic_quotes_status == 1) {
  	  @set_magic_quotes_runtime($this->magic_quotes_status);
	}

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  }
  // End of class
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : PclZipUtilPathReduction()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function PclZipUtilPathReduction($p_dir)
  {
    $v_result = "";

    // ----- Look for not empty path
    if ($p_dir != "") {
      // ----- Explode path by directory names
      $v_list = explode("/", $p_dir);

      // ----- Study directories from last to first
      $v_skip = 0;
      for ($i=sizeof($v_list)-1; $i>=0; $i--) {
        // ----- Look for current path
        if ($v_list[$i] == ".") {
          // ----- Ignore this directory
          // Should be the first $i=0, but no check is done
        }
        else if ($v_list[$i] == "..") {
		  $v_skip++;
        }
        else if ($v_list[$i] == "") {
		  // ----- First '/' i.e. root slash
		  if ($i == 0) {
            $v_result = "/".$v_result;
		    if ($v_skip > 0) {
		        // ----- It is an invalid path, so the path is not modified
		        // TBC
		        $v_result = $p_dir;
                $v_skip = 0;
		    }
		  }
		  // ----- Last '/' i.e. indicates a directory
		  else if ($i == (sizeof($v_list)-1)) {
            $v_result = $v_list[$i];
		  }
		  // ----- Double '/' inside the path
		  else {
            // ----- Ignore only the double '//' in path,
            // but not the first and last '/'
		  }
        }
        else {
		  // ----- Look for item to skip
		  if ($v_skip > 0) {
		    $v_skip--;
		  }
		  else {
            $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?"/".$v_result:"");
		  }
        }
      }
      
      // ----- Look for skip
      if ($v_skip > 0) {
        while ($v_skip > 0) {
            $v_result = '../'.$v_result;
            $v_skip--;
        }
      }
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : PclZipUtilPathInclusion()
  // Description :
  //   This function indicates if the path $p_path is under the $p_dir tree. Or,
  //   said in an other way, if the file or sub-dir $p_path is inside the dir
  //   $p_dir.
  //   The function indicates also if the path is exactly the same as the dir.
  //   This function supports path with duplicated '/' like '//', but does not
  //   support '.' or '..' statements.
  // Parameters :
  // Return Values :
  //   0 if $p_path is not inside directory $p_dir
  //   1 if $p_path is inside directory $p_dir
  //   2 if $p_path is exactly the same as $p_dir
  // --------------------------------------------------------------------------------
  function PclZipUtilPathInclusion($p_dir, $p_path)
  {
    $v_result = 1;
    
    // ----- Look for path beginning by ./
    if (   ($p_dir == '.')
        || ((strlen($p_dir) >=2) && (substr($p_dir, 0, 2) == './'))) {
      $p_dir = PclZipUtilTranslateWinPath(getcwd(), FALSE).'/'.substr($p_dir, 1);
    }
    if (   ($p_path == '.')
        || ((strlen($p_path) >=2) && (substr($p_path, 0, 2) == './'))) {
      $p_path = PclZipUtilTranslateWinPath(getcwd(), FALSE).'/'.substr($p_path, 1);
    }

    // ----- Explode dir and path by directory separator
    $v_list_dir = explode("/", $p_dir);
    $v_list_dir_size = sizeof($v_list_dir);
    $v_list_path = explode("/", $p_path);
    $v_list_path_size = sizeof($v_list_path);

    // ----- Study directories paths
    $i = 0;
    $j = 0;
    while (($i < $v_list_dir_size) && ($j < $v_list_path_size) && ($v_result)) {

      // ----- Look for empty dir (path reduction)
      if ($v_list_dir[$i] == '') {
        $i++;
        continue;
      }
      if ($v_list_path[$j] == '') {
        $j++;
        continue;
      }

      // ----- Compare the items
      if (($v_list_dir[$i] != $v_list_path[$j]) && ($v_list_dir[$i] != '') && ( $v_list_path[$j] != ''))  {
        $v_result = 0;
      }

      // ----- Next items
      $i++;
      $j++;
    }

    // ----- Look if everything seems to be the same
    if ($v_result) {
      // ----- Skip all the empty items
      while (($j < $v_list_path_size) && ($v_list_path[$j] == '')) $j++;
      while (($i < $v_list_dir_size) && ($v_list_dir[$i] == '')) $i++;

      if (($i >= $v_list_dir_size) && ($j >= $v_list_path_size)) {
        // ----- There are exactly the same
        $v_result = 2;
      }
      else if ($i < $v_list_dir_size) {
        // ----- The path is shorter than the dir
        $v_result = 0;
      }
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : PclZipUtilCopyBlock()
  // Description :
  // Parameters :
  //   $p_mode : read/write compression mode
  //             0 : src & dest normal
  //             1 : src gzip, dest normal
  //             2 : src normal, dest gzip
  //             3 : src & dest gzip
  // Return Values :
  // --------------------------------------------------------------------------------
  function PclZipUtilCopyBlock($p_src, $p_dest, $p_size, $p_mode=0)
  {
    $v_result = 1;

    if ($p_mode==0)
    {
      while ($p_size != 0)
      {
        $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE);
        $v_buffer = @fread($p_src, $v_read_size);
        @fwrite($p_dest, $v_buffer, $v_read_size);
        $p_size -= $v_read_size;
      }
    }
    else if ($p_mode==1)
    {
      while ($p_size != 0)
      {
        $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE);
        $v_buffer = @gzread($p_src, $v_read_size);
        @fwrite($p_dest, $v_buffer, $v_read_size);
        $p_size -= $v_read_size;
      }
    }
    else if ($p_mode==2)
    {
      while ($p_size != 0)
      {
        $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE);
        $v_buffer = @fread($p_src, $v_read_size);
        @gzwrite($p_dest, $v_buffer, $v_read_size);
        $p_size -= $v_read_size;
      }
    }
    else if ($p_mode==3)
    {
      while ($p_size != 0)
      {
        $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE);
        $v_buffer = @gzread($p_src, $v_read_size);
        @gzwrite($p_dest, $v_buffer, $v_read_size);
        $p_size -= $v_read_size;
      }
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : PclZipUtilRename()
  // Description :
  //   This function tries to do a simple rename() function. If it fails, it
  //   tries to copy the $p_src file in a new $p_dest file and then unlink the
  //   first one.
  // Parameters :
  //   $p_src : Old filename
  //   $p_dest : New filename
  // Return Values :
  //   1 on success, 0 on failure.
  // --------------------------------------------------------------------------------
  function PclZipUtilRename($p_src, $p_dest)
  {
    $v_result = 1;

    // ----- Try to rename the files
    if (!@rename($p_src, $p_dest)) {

      // ----- Try to copy & unlink the src
      if (!@copy($p_src, $p_dest)) {
        $v_result = 0;
      }
      else if (!@unlink($p_src)) {
        $v_result = 0;
      }
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : PclZipUtilOptionText()
  // Description :
  //   Translate option value in text. Mainly for debug purpose.
  // Parameters :
  //   $p_option : the option value.
  // Return Values :
  //   The option text value.
  // --------------------------------------------------------------------------------
  function PclZipUtilOptionText($p_option)
  {
    
    $v_list = get_defined_constants();
    for (reset($v_list); $v_key = key($v_list); next($v_list)) {
	    $v_prefix = substr($v_key, 0, 10);
	    if ((   ($v_prefix == 'PCLZIP_OPT')
           || ($v_prefix == 'PCLZIP_CB_')
           || ($v_prefix == 'PCLZIP_ATT'))
	        && ($v_list[$v_key] == $p_option)) {
        return $v_key;
	    }
    }
    
    $v_result = 'Unknown';

    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : PclZipUtilTranslateWinPath()
  // Description :
  //   Translate windows path by replacing '\' by '/' and optionally removing
  //   drive letter.
  // Parameters :
  //   $p_path : path to translate.
  //   $p_remove_disk_letter : true | false
  // Return Values :
  //   The path translated.
  // --------------------------------------------------------------------------------
  function PclZipUtilTranslateWinPath($p_path, $p_remove_disk_letter=true)
  {
    if (stristr(php_uname(), 'windows')) {
      // ----- Look for potential disk letter
      if (($p_remove_disk_letter) && (($v_position = strpos($p_path, ':')) != false)) {
          $p_path = substr($p_path, $v_position+1);
      }
      // ----- Change potential windows directory separator
      if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) {
          $p_path = strtr($p_path, '\\', '/');
      }
    }
    return $p_path;
  }
  // --------------------------------------------------------------------------------


?>
securiser_action.php000066600000021031152525475610010633 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

/**
 * interface d'appel:
 * - au moins un argument: retourne une URL ou un formulaire securises
 * - sans argument: verifie la securite et retourne _request('arg'), ou exit.
 *
 * http://doc.spip.org/@inc_securiser_action_dist
 *
 * @param string $action
 * @param string $arg
 * @param string $redirect
 * @param bool|int|string $mode
 *   -1 : renvoyer action, arg et hash sous forme de array()
 *   true ou false : renvoyer une url, avec &amp; (false) ou & (true)
 *   string : renvoyer un formulaire
 * @param string|int $att
 *   id_auteur pour lequel generer l'action en mode url ou array()
 *   atributs du formulaire en mode formulaire
 * @param bool $public
 * @return array|string
 */
function inc_securiser_action_dist($action='', $arg='', $redirect="", $mode=false, $att='', $public=false)
{
	if ($action)
		return securiser_action_auteur($action, $arg, $redirect, $mode, $att, $public);
	else {
		$arg = _request('arg');
		$hash = _request('hash');
		$action = _request('action')?_request('action'):_request('formulaire_action');
		if ($a = verifier_action_auteur("$action-$arg", $hash))
			return $arg;
		include_spip('inc/minipres');
		echo minipres();
		exit;
	}
}

/**
 * Attention: PHP applique urldecode sur $_GET mais pas sur $_POST
 * cf http://fr.php.net/urldecode#48481
 * http://doc.spip.org/@securiser_action_auteur
 *
 * @param string $action
 * @param string $arg
 * @param string $redirect
 * @param bool|int|string $mode
 *   -1 : renvoyer action, arg et hash sous forme de array()
 *   true ou false : renvoyer une url, avec &amp; (false) ou & (true)
 *   string : renvoyer un formulaire
 * @param string|int $att
 *   id_auteur pour lequel generer l'action en mode url ou array()
 *   atributs du formulaire en mode formulaire
 * @param bool $public
 * @return array|string
 */
function securiser_action_auteur($action, $arg, $redirect="", $mode=false, $att='', $public=false) {

	// mode URL ou array
	if (!is_string($mode)){
		$hash = calculer_action_auteur("$action-$arg",is_numeric($att)?$att:null);

		$r = rawurlencode($redirect);
		if ($mode===-1)
			return array('action'=>$action,'arg'=>$arg,'hash'=>$hash);
		else
			return generer_url_action($action, "arg=$arg&hash=$hash" . (!$r ? '' : "&redirect=$r"), $mode, $public);
	}

	// mode formulaire
	$hash = calculer_action_auteur("$action-$arg");
	$att .= " style='margin: 0px; border: 0px'";
	if ($redirect)
		$redirect = "\n\t\t<input name='redirect' type='hidden' value='". str_replace("'", '&#39;', $redirect) ."' />";
	$mode .= $redirect . "
<input name='hash' type='hidden' value='$hash' />
<input name='arg' type='hidden' value='$arg' />";

	return generer_form_action($action, $mode, $att, $public);
}

/**
 * Caracteriser un auteur : l'auteur loge si $id_auteur=null
 *
 * http://doc.spip.org/@caracteriser_auteur
 *
 * @param int|null $id_auteur
 * @return array
 */
function caracteriser_auteur($id_auteur=null) {
	static $caracterisation = array();

	if (is_null($id_auteur) AND !isset($GLOBALS['visiteur_session']['id_auteur'])) {
	// si l'auteur courant n'est pas connu alors qu'il peut demander une action
	// c'est une connexion par php_auth ou 1 instal, on se rabat sur le cookie.
	// S'il n'avait pas le droit de realiser cette action, le hash sera faux.
		if (isset($_COOKIE['spip_session'])
		AND (preg_match('/^(\d+)/',$_COOKIE['spip_session'],$r))) {
			  return array($r[1], '');
			  // Necessaire aux forums anonymes.
			  // Pour le reste, ca echouera.
		} else return array('0','');
	}
	// Eviter l'acces SQL si le pass est connu de PHP
	if (is_null($id_auteur)){
		$id_auteur = isset($GLOBALS['visiteur_session']['id_auteur'])?$GLOBALS['visiteur_session']['id_auteur']:0;
		if (isset($GLOBALS['visiteur_session']['pass']) AND $GLOBALS['visiteur_session']['pass'])
			return $caracterisation[$id_auteur] = array($id_auteur, $GLOBALS['visiteur_session']['pass']);
	}

	if (isset($caracterisation[$id_auteur])) return $caracterisation[$id_auteur];

	if ($id_auteur) {
		include_spip('base/abstract_sql');
		$t = sql_fetsel("id_auteur, pass", "spip_auteurs", "id_auteur=$id_auteur");
		if ($t)
			return $caracterisation[$id_auteur] = array($t['id_auteur'], $t['pass']);
		include_spip('inc/minipres');
		echo minipres();
		exit;
	}
	// Visiteur anonyme, pour ls forums par exemple
	else {
		return array('0','');
	}
}

/**
 * Calcule une cle securisee pour une action et un auteur donnes
 * utilisee pour generer des urls personelles pour executer une action qui modifie la base
 * et verifier la legitimite de l'appel a l'action
 *
 * http://doc.spip.org/@_action_auteur
 *
 * @param string $action
 * @param int $id_auteur
 * @param string $pass
 * @param string $alea
 * @return string
 */
function _action_auteur($action, $id_auteur, $pass, $alea) {
	static $sha = array();
	if (!isset($sha[$id_auteur.$pass.$alea])){
		if (!isset($GLOBALS['meta'][$alea]) AND _request('exec')!=='install') {
			include_spip('base/abstract_sql');
			$GLOBALS['meta'][$alea] = sql_getfetsel('valeur', 'spip_meta', "nom=" . sql_quote($alea));
			if (!($GLOBALS['meta'][$alea])) {
				include_spip('inc/minipres');
				echo minipres();
				spip_log("$alea indisponible");
				exit;
			}
		}
		include_spip('auth/sha256.inc');
		$sha[$id_auteur.$pass.$alea] = _nano_sha256($id_auteur.$pass.@$GLOBALS['meta'][$alea]);
	}
	if (function_exists('sha1'))
		return sha1($action.$sha[$id_auteur.$pass.$alea]);
	else
		return md5($action.$sha[$id_auteur.$pass.$alea]);
}

/**
 * Calculer le hash qui signe une action pour un auteur
 * http://doc.spip.org/@calculer_action_auteur
 *
 * @param string $action
 * @param int|null $id_auteur
 * @return string
 */
function calculer_action_auteur($action, $id_auteur=null) {
	list($id_auteur, $pass) = caracteriser_auteur($id_auteur);
	return _action_auteur($action, $id_auteur, $pass, 'alea_ephemere');
}


/**
 * Verifier le hash de signature d'une action
 * toujours exclusivement pour l'auteur en cours
 * http://doc.spip.org/@verifier_action_auteur
 *
 * @param $action
 * @param $hash
 * @return bool
 */
function verifier_action_auteur($action, $hash) {
	list($id_auteur, $pass) = caracteriser_auteur();
	if ($hash == _action_auteur($action, $id_auteur, $pass, 'alea_ephemere'))
		return true;
	if ($hash == _action_auteur($action, $id_auteur, $pass, 'alea_ephemere_ancien'))
		return true;
	return false;
}

//
// Des fonctions independantes du visiteur, qui permettent de controler
// par exemple que l'URL d'un document a la bonne cle de lecture
//

/**
 * Renvoyer le secret du site, et le generer si il n'existe pas encore
 * Le secret du site doit rester aussi secret que possible, et est eternel
 * On ne doit pas l'exporter
 *
 * http://doc.spip.org/@secret_du_site
 *
 * @return string
 */
function secret_du_site() {
	if (!isset($GLOBALS['meta']['secret_du_site'])){
		include_spip('base/abstract_sql');
		$GLOBALS['meta']['secret_du_site'] = sql_getfetsel('valeur', 'spip_meta', "nom='secret_du_site'");
	}
	if (!isset($GLOBALS['meta']['secret_du_site'])
	  OR (strlen($GLOBALS['meta']['secret_du_site'])<64)) {
		include_spip('inc/acces');
		include_spip('auth/sha256.inc');
		ecrire_meta('secret_du_site', _nano_sha256($_SERVER["DOCUMENT_ROOT"] . $_SERVER["SERVER_SIGNATURE"] . creer_uniqid()), 'non');
		lire_metas(); // au cas ou ecrire_meta() ne fonctionne pas
	}
	return $GLOBALS['meta']['secret_du_site'];
}

/**
 * Calculer une signature valable pour une action et pour le site
 * http://doc.spip.org/@calculer_cle_action
 *
 * @param string $action
 * @return string
 */
function calculer_cle_action($action) {
	if (function_exists('sha1'))
		return sha1($action . secret_du_site());
	else
		return md5($action . secret_du_site());
}

/**
 * Verifier la cle de signature d'une action valable pour le site
 * http://doc.spip.org/@verifier_cle_action
 *
 * @param string $action
 * @param string $cle
 * @return bool
 */
function verifier_cle_action($action, $cle) {
	return ($cle == calculer_cle_action($action));
}

?>
math.php000066600000006627152525475610006241 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/


//
if (!defined('_ECRIRE_INC_VERSION')) return;

//
// Gestion du raccourci <math>...</math> en client-serveur
//

// http://doc.spip.org/@image_math
function produire_image_math($tex) {
	global $traiter_math;
	switch ($traiter_math) {
		// Attention: mathml desactiv'e pour l'instant
		case 'mathml':
			$ext = '.xhtml';
			$server = $GLOBALS['mathml_server'];
			break;
		case 'tex':
			$ext = '.png';
			$server = $GLOBALS['tex_server'];
			break;
		default:
			return $tex;
	}

	// Regarder dans le repertoire local des images TeX et blocs MathML
	if (!@is_dir($dir_tex = _DIR_VAR.'cache-TeX/'))
		@mkdir ($dir_tex, _SPIP_CHMOD);
	$fichier = $dir_tex .md5(trim($tex)).$ext;
	

	if (!@file_exists($fichier)) {
		// Aller chercher l'image sur le serveur
		if ($server) {
			spip_log($url = $server.'?'.rawurlencode($tex));
			include_spip('inc/distant');
			recuperer_page($url,$fichier);
		}
	}


	// Composer la reponse selon presence ou non de l'image
	$tex = entites_html($tex);
	if (@file_exists($fichier)) {

		// MathML
		if ($traiter_math == 'mathml') {
			return join(file("$fichier"),"");
		}

		// TeX
		else {
			list(,,,$size) = @getimagesize($fichier);
			$alt = "alt=\"$tex\" title=\"$tex\""; 
			return "<img src=\"$fichier\" style=\"vertical-align:middle;\" $size $alt />";
		}

	}
	else // pas de fichier
		return "<tt><span class='spip_code' dir='ltr'>$tex</span></tt>";

}


// Fonction appelee par propre() s'il repere un mode <math>
// http://doc.spip.org/@traiter_math
function traiter_math($letexte, $source='') {

	$texte_a_voir = $letexte;
	while (($debut = strpos($texte_a_voir, "<math>")) !== false) {
		if (!$fin = strpos($texte_a_voir,"</math>"))
			$fin = strlen($texte_a_voir);

		$texte_debut = substr($texte_a_voir, 0, $debut);
		$texte_milieu = substr($texte_a_voir,
			$debut+strlen("<math>"), $fin-$debut-strlen("<math>"));
		$texte_fin = substr($texte_a_voir,
			$fin+strlen("</math>"), strlen($texte_a_voir));

		// Les doubles $$x^2$$ en mode 'div'
		while((preg_match(",[$][$]([^$]+)[$][$],",$texte_milieu, $regs))) {
			$echap = "\n<p class=\"spip\" style=\"text-align: center;\">".produire_image_math($regs[1])."</p>\n";
			$pos = strpos($texte_milieu, $regs[0]);
			$texte_milieu = substr($texte_milieu,0,$pos)
				. code_echappement($echap, $source)
				. substr($texte_milieu,$pos+strlen($regs[0]));
		}

		// Les simples $x^2$ en mode 'span'
		while((preg_match(",[$]([^$]+)[$],",$texte_milieu, $regs))) {
			$echap = produire_image_math($regs[1]);
			$pos = strpos($texte_milieu, $regs[0]);
			$texte_milieu = substr($texte_milieu,0,$pos)
				. code_echappement($echap, $source)
				. substr($texte_milieu,$pos+strlen($regs[0]));
		}

		$texte_a_voir = $texte_debut.$texte_milieu.$texte_fin;
	}

	return $texte_a_voir;
}

?>
charsets.php000066600000070201152525475610007111 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

/**
 * Gestion des charsets et des conversions
 *
 * Ce fichier contient les fonctions relatives à la gestion de charsets,
 * à la conversion de textes dans différents charsets et
 * propose des fonctions émulant la librairie mb si elle est absente
 * 
 * @package SPIP\Texte\Charsets
**/

// securité
if (!defined('_ECRIRE_INC_VERSION')) return;


/**
 * Charge en mémoire la liste des caractères d'un charset
 *
 * Charsets supportes en natif : voir les tables dans ecrire/charsets/
 * Les autres charsets sont supportes via mbstring()
 * 
 * @param string $charset
 *     Charset à charger
 *     Par défaut (AUTO), utilise le charset du site
 * @return string|bool
 *     Nom du charset
 *     false si le charset n'est pas décrit dans le répertoire charsets/
**/
function load_charset ($charset = 'AUTO') {
	if ($charset == 'AUTO')
		$charset = $GLOBALS['meta']['charset'];
	$charset = trim(strtolower($charset));
	if (isset($GLOBALS['CHARSET'][$charset]))
		return $charset;

	if ($charset == 'utf-8') {
		$GLOBALS['CHARSET'][$charset] = array();
		return $charset;
	}
	
	// Quelques synonymes
	if ($charset == '') $charset = 'iso-8859-1';
	else if ($charset == 'windows-1250') $charset = 'cp1250';
	else if ($charset == 'windows-1251') $charset = 'cp1251';
	else if ($charset == 'windows-1256') $charset = 'cp1256';

	if (find_in_path($charset . '.php', 'charsets/', true)) {
		return $charset;
	} else {
		spip_log("Erreur: pas de fichier de conversion 'charsets/$charset'");
		$GLOBALS['CHARSET'][$charset] = array();
		return false;
	}
}


/**
 * Verifier qu'on peut utiliser mb_string
 *
 * @return bool
 *     true si toutes les fonctions mb nécessaires sont présentes
**/
function init_mb_string() {
	static $mb;

	// verifier que tout est present (fonctions mb_string pour php >= 4.0.6)
	// et que le charset interne est connu de mb_string
	if (!$mb) {
		if (function_exists('mb_internal_encoding')
		AND function_exists('mb_detect_order')
		AND function_exists('mb_substr')
		AND function_exists('mb_strlen')
		AND function_exists('mb_encode_mimeheader')
		AND function_exists('mb_encode_numericentity')
		AND function_exists('mb_decode_numericentity')
		AND mb_detect_order($GLOBALS['meta']['charset'])
		) {
			mb_internal_encoding('utf-8');
			$mb = 1;
		} else
			$mb = -1;
	}

	return ($mb == 1);
}

/**
 * Test le fonctionnement correct d'iconv
 *
 * Celui-ci coupe sur certaines versions la chaine
 * quand un caractère n'appartient pas au charset
 *  
 * @link http://php.net/manual/fr/function.iconv.php
 * 
 * @return bool
 *     true si iconv fonctionne correctement
**/
function test_iconv() {
	static $iconv_ok;

	if (!$iconv_ok) {
		if (!function_exists('iconv'))
			$iconv_ok = -1;
		else {
			if (utf_32_to_unicode(@iconv('utf-8', 'utf-32', 'chaine de test')) == 'chaine de test')
				$iconv_ok = 1;
			else
				$iconv_ok = -1;
		}
	}
	return ($iconv_ok == 1);
}


/**
 * Test de fonctionnement du support UTF-8 dans PCRE
 *
 * Contournement bug Debian Woody
 * 
 * @return bool
 *     true si PCRE supporte l'UTF-8 correctement
**/
function test_pcre_unicode() {
	static $pcre_ok = 0;

	if (!$pcre_ok) {
		$s = " ".chr(195).chr(169)."t".chr(195).chr(169)." ";
		if (preg_match(',\W...\W,u', $s)) $pcre_ok = 1;
		else $pcre_ok = -1;
	}
	return $pcre_ok == 1;
}

/**
 * Renvoie une plage de caractères alphanumeriques unicodes (incomplet...)
 *
 * Retourne pour une expression rationnelle une plage
 * de caractères alphanumériques à utiliser entre crochets [$plage]
 *
 * @internal
 *    N'est pas utilisé
 *    Servait à inc/ortho passé dans le grenier
 * @return string
 *    Plage de caractères
**/
function pcre_lettres_unicode() {
	static $plage_unicode;

	if (!$plage_unicode) {
		if (test_pcre_unicode()) {
			// cf. http://www.unicode.org/charts/
			$plage_unicode = '\w' // iso-latin
				. '\x{100}-\x{24f}' // europeen etendu
				. '\x{300}-\x{1cff}' // des tas de trucs
			;
		}
		else {
			// fallback a trois sous
			$plage_unicode = '\w';
		}
	}
	return $plage_unicode;
}


/**
 * Renvoie une plage de caractères de ponctuation unicode de 0x2000 a 0x206F
 *
 * Retourne pour une expression rationnelle une plage
 * de caractères de ponctuation à utiliser entre crochets [$plage]
 * (i.e. de 226-128-128 a 226-129-176)
 *
 * @internal
 *    N'est pas utilisé
 *    Servait à inc/ortho passé dans le grenier
 * @return string
 *    Plage de caractères
**/
function plage_punct_unicode() {
	return '\xE2(\x80[\x80-\xBF]|\x81[\x80-\xAF])';
}

/**
 * Corriger des caractères non-conformes : 128-159
 *
 * Cf. charsets/iso-8859-1.php (qu'on recopie ici pour aller plus vite)
 * On peut passer un charset cible en parametre pour accelerer le passage iso-8859-1 -> autre charset
 * 
 * @param string $texte
 *     Le texte à corriger
 * @param string $charset
 *     Charset d'origine du texte
 *     Par défaut (AUTO) utilise le charset du site
 * @param string $charset_cible
 *     Charset de destination (unicode par défaut)
 * @return string
 *     Texte corrigé
**/
function corriger_caracteres_windows($texte, $charset='AUTO', $charset_cible='unicode') {
	static $trans;
	
	if (is_array($texte)) {
		return array_map('corriger_caracteres_windows', $texte);
	}
	
	if ($charset=='AUTO') $charset = $GLOBALS['meta']['charset'];
	if ($charset == 'utf-8') {
		$p = chr(194);
		if (strpos($texte,$p)==false)
			return $texte;
	} else if ($charset == 'iso-8859-1') {
		$p = '';
	} else
		return $texte;

	if (!isset($trans[$charset][$charset_cible])) {
		$trans[$charset][$charset_cible] = array(
			$p.chr(128) => "&#8364;",
			$p.chr(129) => ' ', # pas affecte
			$p.chr(130) => "&#8218;",
			$p.chr(131) => "&#402;",
			$p.chr(132) => "&#8222;",
			$p.chr(133) => "&#8230;",
			$p.chr(134) => "&#8224;",
			$p.chr(135) => "&#8225;",
			$p.chr(136) => "&#710;",
			$p.chr(137) => "&#8240;",
			$p.chr(138) => "&#352;",
			$p.chr(139) => "&#8249;",
			$p.chr(140) => "&#338;",
			$p.chr(141) => ' ', # pas affecte
			$p.chr(142) => "&#381;",
			$p.chr(143) => ' ', # pas affecte
			$p.chr(144) => ' ', # pas affecte
			$p.chr(145) => "&#8216;",
			$p.chr(146) => "&#8217;",
			$p.chr(147) => "&#8220;",
			$p.chr(148) => "&#8221;",
			$p.chr(149) => "&#8226;",
			$p.chr(150) => "&#8211;",
			$p.chr(151) => "&#8212;",
			$p.chr(152) => "&#732;",
			$p.chr(153) => "&#8482;", 
			$p.chr(154) => "&#353;",
			$p.chr(155) => "&#8250;",
			$p.chr(156) => "&#339;",
			$p.chr(157) => ' ', # pas affecte
			$p.chr(158) => "&#382;",
			$p.chr(159) => "&#376;",
		);
		if ($charset_cible!='unicode'){
			foreach($trans[$charset][$charset_cible] as $k=>$c)
				$trans[$charset][$charset_cible][$k] = unicode2charset($c, $charset_cible);
		}
	}

	return @str_replace(array_keys($trans[$charset][$charset_cible]),
			   array_values($trans[$charset][$charset_cible]),$texte);
}



/**
 * Transforme les entités HTML en unicode
 * 
 * Transforme les &eacute; en &#123;
 *
 * @param string $texte
 *     Texte à convertir
 * @param bool $secure
 *     true pour *ne pas convertir* les caracteres malins &lt; &amp; etc.
 * @return string
 *     Texte converti
**/
function html2unicode($texte, $secure=false) {
	if (strpos($texte,'&') === false) return $texte;
	static $trans = array();
	if (!$trans) {
		global $CHARSET;
		load_charset('html');
		foreach ($CHARSET['html'] as $key => $val) {
			$trans["&$key;"] = $val;
		}
	}

	if ($secure)
		return str_replace(array_keys($trans),array_values($trans),$texte);
	else
		return str_replace(array('&amp;', '&quot;', '&lt;', '&gt;'),array('&', '"', '<', '>'),
		  str_replace(array_keys($trans),array_values($trans),$texte)			
		);
}


/**
 * Transforme les entités mathématiques (MathML) en unicode
 *
 * Transforme &angle; en &#x2220; ainsi que toutes autres entités mathématiques
 *
 * @param string $texte
 *     Texte à convertir
 * @return string
 *     Texte converti
**/
function mathml2unicode($texte) {
	static $trans;
	if (!$trans) {
		global $CHARSET;
		load_charset('mathml');
		
		foreach ($CHARSET['mathml'] as $key => $val)
			$trans["&$key;"] = $val;
	}

	return str_replace(array_keys($trans),array_values($trans),$texte);
}


/**
 * Transforme une chaine en entites unicode &#129;
 *
 * Utilise la librairie mb si elle est présente.
 * 
 * @internal
 *     Note: l'argument $forcer est obsolete : il visait a ne pas
 *     convertir les accents iso-8859-1
 * 
 * @param string $texte
 *     Texte à convertir
 * @param string $charset
 *     Charset actuel du texte
 *     Par défaut (AUTO), le charset est celui du site.
 * @return string
 *     Texte converti en unicode
**/
function charset2unicode($texte, $charset='AUTO' /* $forcer: obsolete*/) {
	static $trans;

	if ($charset == 'AUTO')
		$charset = $GLOBALS['meta']['charset'];

	if ($charset == '') $charset = 'iso-8859-1';
	$charset = strtolower($charset);

	switch ($charset) {
	case 'utf-8':
	case 'utf8':
		return utf_8_to_unicode($texte);

	case 'iso-8859-1':
		$texte = corriger_caracteres_windows($texte, 'iso-8859-1');
		// pas de break; ici, on suit sur default:

	default:
		// mbstring presente ?
		if (init_mb_string()) {
			if ($order = mb_detect_order() # mb_string connait-il $charset?
			AND mb_detect_order($charset)) {
				$s = mb_convert_encoding($texte, 'utf-8', $charset);
				if ($s && $s != $texte) return utf_8_to_unicode($s);
			}
			mb_detect_order($order); # remettre comme precedemment
		}

		// Sinon, peut-etre connaissons-nous ce charset ?
		if (!isset($trans[$charset])) {
			global $CHARSET;
			if ($cset = load_charset($charset)
			AND is_array($CHARSET[$cset]))
				foreach ($CHARSET[$cset] as $key => $val) {
					$trans[$charset][chr($key)] = '&#'.$val.';';
			}
		}
		if (count($trans[$charset]))
			return str_replace(array_keys($trans[$charset]),array_values($trans[$charset]),$texte);

		// Sinon demander a iconv (malgre le fait qu'il coupe quand un
		// caractere n'appartient pas au charset, mais c'est un probleme
		// surtout en utf-8, gere ci-dessus)
		if (test_iconv()) {
			$s = iconv($charset, 'utf-32le', $texte);
			if ($s) return utf_32_to_unicode($s);
		}

		// Au pire ne rien faire
		spip_log("erreur charset '$charset' non supporte");
		return $texte;
	}
}


/**
 * Transforme les entites unicode &#129; dans le charset specifie
 *
 * Attention on ne transforme pas les entites < &#128; car si elles
 * ont ete encodees ainsi c'est a dessein
 * 
 * @param string $texte
 *     Texte unicode à transformer
 * @param string $charset
 *     Charset à appliquer au texte
 *     Par défaut (AUTO), le charset sera celui du site.
 * @return string
 *     Texte transformé dans le charset souhaité
**/
function unicode2charset($texte, $charset='AUTO') {
	static $CHARSET_REVERSE;
	static $trans = array();
	
	if ($charset == 'AUTO')
		$charset = $GLOBALS['meta']['charset'];

	switch($charset) {
	case 'utf-8':
		return unicode_to_utf_8($texte);
		break;

	default:
		$charset = load_charset($charset);

		if (!is_array($CHARSET_REVERSE[$charset])) {
			$CHARSET_REVERSE[$charset] = array_flip($GLOBALS['CHARSET'][$charset]);
		}

		if (!isset($trans[$charset])){
			$trans[$charset]=array();
			$t = &$trans[$charset];
			for($e=128;$e<255;$e++){
				$h = dechex($e);
				if ($s = isset($CHARSET_REVERSE[$charset][$e])){
					$s = $CHARSET_REVERSE[$charset][$e];
					$t['&#'.$e.';'] = $t['&#0'.$e.';'] = $t['&#00'.$e.';'] = chr($s);
					$t['&#x'.$h.';'] = $t['&#x0'.$h.';'] = $t['&#x00'.$h.';'] = chr($s);
				}
				else{
					$t['&#'.$e.';'] = $t['&#0'.$e.';'] = $t['&#00'.$e.';'] = chr($e);
					$t['&#x'.$h.';'] = $t['&#x0'.$h.';'] = $t['&#x00'.$h.';'] = chr($e);
				}
			}
		}
		$texte = str_replace(array_keys($trans[$charset]),array_values($trans[$charset]),$texte);
		return $texte;
	}
}


/**
 * Importer un texte depuis un charset externe vers le charset du site
 *
 * Les caracteres non resolus sont transformes en &#123;
 * 
 * @param string $texte
 *     Texte unicode à importer
 * @param string $charset
 *     Charset d'origine du texte
 *     Par défaut (AUTO), le charset d'origine est celui du site.
 * @return string
 *     Texte transformé dans le charset site
**/
function importer_charset($texte, $charset = 'AUTO') {
	static $trans = array();
	// on traite le cas le plus frequent iso-8859-1 vers utf directement pour aller plus vite !
	if (($charset == 'iso-8859-1') && ($GLOBALS['meta']['charset']=='utf-8')){
		$texte = corriger_caracteres_windows($texte, 'iso-8859-1',$GLOBALS['meta']['charset']);
		if (init_mb_string()) {
			if ($order = mb_detect_order() # mb_string connait-il $charset?
			AND mb_detect_order($charset)) {
				$s = mb_convert_encoding($texte, 'utf-8', $charset);
			}
			mb_detect_order($order); # remettre comme precedemment
			return $s;
		}
		// Sinon, peut-etre connaissons-nous ce charset ?
		if (!isset($trans[$charset])) {
			global $CHARSET;
			if ($cset = load_charset($charset)
			AND is_array($CHARSET[$cset]))
				foreach ($CHARSET[$cset] as $key => $val) {
					$trans[$charset][chr($key)] = unicode2charset('&#'.$val.';');
			}
		}
		if (count($trans[$charset]))
			return str_replace(array_keys($trans[$charset]),array_values($trans[$charset]),$texte);
		return $texte;
	}
	return unicode2charset(charset2unicode($texte, $charset));
}


/**
 * Transforme un texte UTF-8 en unicode 
 *
 * Utilise la librairie mb si présente
 * 
 * @param string $source
 *    Texte UTF-8 à transformer
 * @return string
 *    Texte transformé en unicode
**/
function utf_8_to_unicode($source) {

	// mb_string : methode rapide
	if (init_mb_string()) {
		$convmap = array(0x7F, 0xFFFFFF, 0x0, 0xFFFFFF);
		return mb_encode_numericentity($source, $convmap, 'UTF-8');
	}

	// Sinon methode pas a pas
	static $decrement;
	static $shift;

	// Cf. php.net, par Ronen. Adapte pour compatibilite < php4
	if (!is_array($decrement)) {
		// array used to figure what number to decrement from character order value
		// according to number of characters used to map unicode to ascii by utf-8
		$decrement[4] = 240;
		$decrement[3] = 224;
		$decrement[2] = 192;
		$decrement[1] = 0;
		// the number of bits to shift each charNum by
		$shift[1][0] = 0;
		$shift[2][0] = 6;
		$shift[2][1] = 0;
		$shift[3][0] = 12;
		$shift[3][1] = 6;
		$shift[3][2] = 0;
		$shift[4][0] = 18;
		$shift[4][1] = 12;
		$shift[4][2] = 6;
		$shift[4][3] = 0;
	}

	$pos = 0;
	$len = strlen ($source);
	$encodedString = '';
	while ($pos < $len) {
		$char = '';
		$ischar = false;
		$asciiPos = ord (substr ($source, $pos, 1));
		if (($asciiPos >= 240) && ($asciiPos <= 255)) {
			// 4 chars representing one unicode character
			$thisLetter = substr ($source, $pos, 4);
			$pos += 4;
		}
		else if (($asciiPos >= 224) && ($asciiPos <= 239)) {
			// 3 chars representing one unicode character
			$thisLetter = substr ($source, $pos, 3);
			$pos += 3;
		}
		else if (($asciiPos >= 192) && ($asciiPos <= 223)) {
			// 2 chars representing one unicode character
			$thisLetter = substr ($source, $pos, 2);
			$pos += 2;
		}
		else {
			// 1 char (lower ascii)
			$thisLetter = substr ($source, $pos, 1);
			$pos += 1;
			$char = $thisLetter;
			$ischar = true;
		}

		if ($ischar)
			$encodedString .= $char;
		else {	// process the string representing the letter to a unicode entity
			$thisLen = strlen ($thisLetter);
			$thisPos = 0;
			$decimalCode = 0;
			while ($thisPos < $thisLen) {
				$thisCharOrd = ord (substr ($thisLetter, $thisPos, 1));
				if ($thisPos == 0) {
					$charNum = intval ($thisCharOrd - $decrement[$thisLen]);
					$decimalCode += ($charNum << $shift[$thisLen][$thisPos]);
				} else {
					$charNum = intval ($thisCharOrd - 128);
					$decimalCode += ($charNum << $shift[$thisLen][$thisPos]);
				}
				$thisPos++;
			}
			$encodedLetter = "&#". preg_replace('/^0+/', '', $decimalCode) . ';';
			$encodedString .= $encodedLetter;
		}
	}
	return $encodedString;
}

/**
 * Transforme un texte UTF-32 en unicode 
 *
 * UTF-32 ne sert plus que si on passe par iconv, c'est-a-dire quand
 * mb_string est absente ou ne connait pas notre charset.
 *
 * Mais on l'optimise quand meme par mb_string
 * => tout ca sera osolete quand on sera surs d'avoir mb_string
 * 
 * @param string $source
 *    Texte UTF-8 à transformer
 * @return string
 *    Texte transformé en unicode
**/
function utf_32_to_unicode($source) {

	// mb_string : methode rapide
	if (init_mb_string()) {
		$convmap = array(0x7F, 0xFFFFFF, 0x0, 0xFFFFFF);
		$source = mb_encode_numericentity($source, $convmap, 'UTF-32LE');
		return str_replace(chr(0), '', $source);
	}

	// Sinon methode lente
	$texte = '';
	while ($source) {
		$words = unpack("V*", substr($source, 0, 1024));
		$source = substr($source, 1024);
		foreach ($words as $word) {
			if ($word < 128)
				$texte .= chr($word);
			// ignorer le BOM - http://www.unicode.org/faq/utf_bom.html
			else if ($word != 65279)
				$texte .= '&#'.$word.';';
		}
	}
	return $texte;

}


/**
 * Transforme un numéro unicode en caractère utf-8
 * 
 * Ce bloc provient de php.net
 * @author Ronen
 * 
 * @param int $num
 *    Numéro de l'entité unicode
 * @return char
 *    Caractère utf8 si trouvé, '' sinon
**/
function caractere_utf_8($num) {
	if($num<128)
		return chr($num);
	if($num<2048)
		return chr(($num>>6)+192).chr(($num&63)+128);
	if($num<65536)
		return chr(($num>>12)+224).chr((($num>>6)&63)+128).chr(($num&63)+128);
	if($num<1114112)
		return chr($num>>18+240).chr((($num>>12)&63)+128).chr(($num>>6)&63+128). chr($num&63+128);
	return '';
}

/**
 * Convertit un texte unicode en utf-8
 * 
 * @param string $texte
 *     Texte à convertir
 * @return string
 *     Texte converti
**/
function unicode_to_utf_8($texte) {

	// 1. Entites &#128; et suivantes
	$vu = array();
	if (preg_match_all(',&#0*([1-9][0-9][0-9]+);,S',
	$texte, $regs, PREG_SET_ORDER))
	foreach ($regs as $reg) {
		if ($reg[1]>127 AND !isset($vu[$reg[0]]))
			$vu[$reg[0]] = caractere_utf_8($reg[1]);
	}
	//$texte = str_replace(array_keys($vu), array_values($vu), $texte);

	// 2. Entites > &#xFF;
	//$vu = array();
	if (preg_match_all(',&#x0*([1-9a-f][0-9a-f][0-9a-f]+);,iS',
	$texte, $regs, PREG_SET_ORDER))
	foreach ($regs as $reg) {
		if (!isset($vu[$reg[0]]))
			$vu[$reg[0]] = caractere_utf_8(hexdec($reg[1]));
	}
	return str_replace(array_keys($vu), array_values($vu), $texte);

}

/**
 * Convertit les unicode &#264; en javascript \u0108
 * 
 * @param string $texte
 *     Texte à convertir
 * @return string
 *     Texte converti
**/
function unicode_to_javascript($texte) {
	$vu = array();
	while (preg_match(',&#0*([0-9]+);,S', $texte, $regs) AND !isset($vu[$regs[1]])) {
		$num = $regs[1];
		$vu[$num] = true;
		$s = '\u'.sprintf("%04x", $num);
		$texte = str_replace($regs[0], $s, $texte);
	}
	return $texte;
}

/**
 * Convertit les %uxxxx (envoyés par javascript) en &#yyy unicode
 * 
 * @param string $texte
 *     Texte à convertir
 * @return string
 *     Texte converti
**/
function javascript_to_unicode ($texte) {
	while (preg_match(",%u([0-9A-F][0-9A-F][0-9A-F][0-9A-F]),", $texte, $regs))
		$texte = str_replace($regs[0],"&#".hexdec($regs[1]).";", $texte);
	return $texte;
}

/**
 * Convertit les %E9 (envoyés par le browser) en chaîne du charset du site (binaire)
 * 
 * @param string $texte
 *     Texte à convertir
 * @return string
 *     Texte converti
**/
function javascript_to_binary ($texte) {
	while (preg_match(",%([0-9A-F][0-9A-F]),", $texte, $regs))
		$texte = str_replace($regs[0],chr(hexdec($regs[1])), $texte);
	return $texte;
}


// http://doc.spip.org/@translitteration_rapide
function translitteration_rapide($texte, $charset='AUTO', $complexe='') {
	static $trans;
	if ($charset == 'AUTO')
		$charset = $GLOBALS['meta']['charset'];
	if (!strlen($texte))
		return $texte;

	$table_translit ='translit'.$complexe;

	// 2. Translitterer grace a la table predefinie
	if (!$trans[$complexe]) {
		global $CHARSET;
		load_charset($table_translit);
		foreach ($CHARSET[$table_translit] as $key => $val)
			$trans[$complexe][caractere_utf_8($key)] = $val;
	}

	return str_replace(array_keys($trans[$complexe]),array_values($trans[$complexe]),$texte);
}

//
// Translitteration charset => ascii (pour l'indexation)
// Attention les caracteres non reconnus sont renvoyes en utf-8
//
// http://doc.spip.org/@translitteration
function translitteration($texte, $charset='AUTO', $complexe='') {
	// 0. Supprimer les caracteres illegaux
	include_spip('inc/filtres');
	$texte = corriger_caracteres($texte);

	// 1. Passer le charset et les &eacute en utf-8
	$texte = unicode_to_utf_8(html2unicode(charset2unicode($texte, $charset, true)));

	return translitteration_rapide($texte,$charset,$complexe);
}

// &agrave; est retourne sous la forme "a`" et pas "a"
// mais si $chiffre=true, on retourne "a8" (vietnamien)
// http://doc.spip.org/@translitteration_complexe
function translitteration_complexe($texte, $chiffres=false) {
	$texte = translitteration($texte,'AUTO','complexe');

	if ($chiffres) {
		$texte = preg_replace("/[aeiuoyd]['`?~.^+(-]{1,2}/eS",
			"translitteration_chiffree('\\0')", $texte);
	}
	
	return $texte;
}
// http://doc.spip.org/@translitteration_chiffree
function translitteration_chiffree($car) {
	return strtr($car, "'`?~.^+(-", "123456789");
}


/**
 * Reconnaitre le BOM utf-8 (0xEFBBBF)
 *
 * @param string $texte
 *    Texte dont on vérifie la présence du BOM
 * @return bool
 *    true s'il a un BOM
**/
function bom_utf8($texte) {
	return (substr($texte, 0,3) == chr(0xEF).chr(0xBB).chr(0xBF));
}

/**
 * Vérifie qu'une chaîne est en utf-8 valide
 * 
 * Note: preg_replace permet de contourner un "stack overflow" sur PCRE
 * 
 * @link http://us2.php.net/manual/fr/function.mb-detect-encoding.php#50087
 * @link http://w3.org/International/questions/qa-forms-utf-8.html
 * 
 * @param string $string
 *     Texte dont on vérifie qu'il est de l'utf-8
 * @return bool
 *     true si c'est le cas
**/
function is_utf8($string) {
	return !strlen(
	preg_replace(
	  ',[\x09\x0A\x0D\x20-\x7E]'            # ASCII
	. '|[\xC2-\xDF][\x80-\xBF]'             # non-overlong 2-byte
	. '|\xE0[\xA0-\xBF][\x80-\xBF]'         # excluding overlongs
	. '|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}'  # straight 3-byte
	. '|\xED[\x80-\x9F][\x80-\xBF]'         # excluding surrogates
	. '|\xF0[\x90-\xBF][\x80-\xBF]{2}'      # planes 1-3
	. '|[\xF1-\xF3][\x80-\xBF]{3}'          # planes 4-15
	. '|\xF4[\x80-\x8F][\x80-\xBF]{2}'      # plane 16
	. ',sS',
	'', $string));
}

/**
 * Vérifie qu'une chaîne est en ascii valide
 * 
 * @param string $string
 *     Texte dont on vérifie qu'il est de l'ascii
 * @return bool
 *     true si c'est le cas
**/
function is_ascii($string) {
	return !strlen(
	preg_replace(
	',[\x09\x0A\x0D\x20-\x7E],sS',
	'', $string));
}

// Transcode une page (attrapee sur le web, ou un squelette) en essayant
// par tous les moyens de deviner son charset (y compris headers HTTP)
// http://doc.spip.org/@transcoder_page
function transcoder_page($texte, $headers='') {

	// Si tout est < 128 pas la peine d'aller plus loin
	if (is_ascii($texte)) {
		#spip_log('charset: ascii');
		return $texte;
	}

	// Reconnaitre le BOM utf-8 (0xEFBBBF)
	if (bom_utf8($texte)) {
		$charset = 'utf-8';
		$texte = substr($texte,3);
	}

	// charset precise par le contenu (xml)
	else if (preg_match(
	',<[?]xml[^>]*encoding[^>]*=[^>]*([-_a-z0-9]+?),UimsS', $texte, $regs))
		$charset = trim(strtolower($regs[1]));
	// charset precise par le contenu (html)
	else if (preg_match(
	',<(meta|html|body)[^>]*charset[^>]*=[^>]*([-_a-z0-9]+?),UimsS',
	$texte, $regs)
	# eviter #CHARSET des squelettes
	AND (($tmp = trim(strtolower($regs[2]))) != 'charset'))
		$charset = $tmp;
	// charset de la reponse http
	else if (preg_match(',charset=([-_a-z0-9]+),i', $headers, $regs))
		$charset = trim(strtolower($regs[1]));
	else $charset = '';
	// normaliser les noms du shif-jis japonais
	if (preg_match(',^(x|shift)[_-]s?jis$,i', $charset))
		$charset = 'shift-jis';

	if ($charset) {
		spip_log("charset: $charset");
	} else {
		// valeur par defaut
		if (is_utf8($texte))
			$charset = 'utf-8';
		else
			$charset = 'iso-8859-1';
		spip_log("charset probable: $charset");
	}

	return importer_charset($texte, $charset);
}


//
// Gerer les outils mb_string
//

/**
 * Coupe un texte selon substr()
 *
 * Coupe une chaîne en utilisant les outils mb* lorsque le site est en utf8
 *
 * @link http://fr.php.net/manual/fr/function.mb-substr.php
 * @link http://www.php.net/manual/fr/function.substr.php
 * 
 * @param string $c         Le texte
 * @param int $start        Début
 * @param null|int $length  Longueur ou fin
 * @return string
 *     Le texte coupé
**/
function spip_substr($c, $start=0, $length = NULL) {
	// Si ce n'est pas utf-8, utiliser substr
	if ($GLOBALS['meta']['charset'] != 'utf-8') {
		if ($length)
			return substr($c, $start, $length);
		else
			substr($c, $start);
	}

	// Si utf-8, voir si on dispose de mb_string
	if (init_mb_string()) {
		if ($length)
			return mb_substr($c, $start, $length);
		else
			return mb_substr($c, $start);
	}

	// Version manuelle (cf. ci-dessous)
	return spip_substr_manuelle($c, $start, $length);
}


/**
 * Coupe un texte comme mb_substr()
 *
 * Version manuelle de substr utf8, pour php vieux et/ou mal installe
 *
 * @link http://fr.php.net/manual/fr/function.mb-substr.php
 * @used-by spip_substr()
 * 
 * @param string $c         Le texte
 * @param int $start        Début
 * @param null|int $length  Longueur ou fin
 * @return string
 *     Le texte coupé
**/
function spip_substr_manuelle($c, $start, $length = NULL) {

	// Cas pathologique
	if ($length === 0)
		return '';

	// S'il y a un demarrage, on se positionne
	if ($start > 0)
		$c = substr($c, strlen(spip_substr_manuelle($c, 0, $start)));
	elseif ($start < 0)
		return spip_substr_manuelle($c, spip_strlen($c)+$start, $length);

	if (!$length)
		return $c;

	if ($length > 0) {
		// on prend n fois la longueur desiree, pour etre surs d'avoir tout
		// (un caractere utf-8 prenant au maximum n bytes)
		$n = 0; while (preg_match(',[\x80-\xBF]{'.(++$n).'},', $c));
		$c = substr($c, 0, $n*$length);
		// puis, tant qu'on est trop long, on coupe...
		while (($l = spip_strlen($c)) > $length)
			$c = substr($c, 0, $length - $l);
		return $c;
	}

	// $length < 0
	return spip_substr_manuelle($c, 0, spip_strlen($c)+$length);
}

/**
 * Rend majuscule le premier caractère d'une chaîne utf-8
 * 
 * Version utf-8 d'ucfirst
 * 
 * @param string $c
 *     La chaîne à transformer
 * @return string
 *     La chaîne avec une majuscule sur le premier mot 
 */
function spip_ucfirst($c){
	// Si ce n'est pas utf-8, utiliser ucfirst 
	if ($GLOBALS['meta']['charset'] != 'utf-8')
		return ucfirst($c);
	// Si on n'a pas mb_* on utilise ucfirst
	if (!init_mb_string())
		return ucfirst($c);
	
	$lettre1 = mb_strtoupper(spip_substr($c,0,1));
	return $lettre1.spip_substr($c,1);
}

/**
 * Retourne la longueur d'une chaîne utf-8
 * 
 * Version utf-8 de strlen
 * 
 * @param string $c
 *     La chaîne à compter
 * @return int
 *     Longueur de la chaîne
 */
function spip_strlen($c) {
	// On transforme les sauts de ligne pour ne pas compter deux caractères
	$c = str_replace("\r\n", "\n", $c);
	
	// Si ce n'est pas utf-8, utiliser strlen
	if ($GLOBALS['meta']['charset'] != 'utf-8')
		return strlen($c);

	// Sinon, utiliser mb_strlen() si disponible
	if (init_mb_string())
		return mb_strlen($c);

	// Methode manuelle : on supprime les bytes 10......,
	// on compte donc les ascii (0.......) et les demarrages
	// de caracteres utf-8 (11......)
	return strlen(preg_replace(',[\x80-\xBF],S', '', $c));
}

// Initialisation
$GLOBALS['CHARSET'] = Array();

// noter a l'occasion dans la meta pcre_u notre capacite a utiliser le flag /u
// dans les preg_replace pour ne pas casser certaines lettres accentuees :
// en utf-8 chr(195).chr(160) = a` alors qu'en iso-latin chr(160) = nbsp
if (!isset($GLOBALS['meta']['pcre_u'])
  OR (isset($_GET['var_mode']) AND !isset($_GET['var_profile']))) {
	include_spip('inc/meta');
	ecrire_meta('pcre_u',
		$u = ($GLOBALS['meta']['charset'] == 'utf-8'
		AND test_pcre_unicode())
			? 'u' :''
	);
}

?>
filtres_images_mini.php000066600000012031152525475610011303 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

if (!defined('_ECRIRE_INC_VERSION')) return;
include_spip('inc/filtres_images_lib_mini'); // par precaution

// http://doc.spip.org/@couleur_html_to_hex
function couleur_html_to_hex($couleur){
	$couleurs_html=array(
		'aqua'=>'00FFFF','black'=>'000000','blue'=>'0000FF','fuchsia'=>'FF00FF','gray'=>'808080','green'=>'008000','lime'=>'00FF00','maroon'=>'800000',
		'navy'=>'000080','olive'=>'808000','purple'=>'800080','red'=>'FF0000','silver'=>'C0C0C0','teal'=>'008080','white'=>'FFFFFF','yellow'=>'FFFF00');
	if (isset($couleurs_html[$lc=strtolower($couleur)]))
		return $couleurs_html[$lc];
	return $couleur;
}

// http://doc.spip.org/@couleur_foncer
function couleur_foncer ($couleur, $coeff=0.5) {
	$couleurs = _couleur_hex_to_dec($couleur);

	$red = $couleurs["red"] - round(($couleurs["red"])*$coeff);
	$green = $couleurs["green"] - round(($couleurs["green"])*$coeff);
	$blue = $couleurs["blue"] - round(($couleurs["blue"])*$coeff);

	$couleur = _couleur_dec_to_hex($red, $green, $blue);
	
	return $couleur;
}

// http://doc.spip.org/@couleur_eclaircir
function couleur_eclaircir ($couleur, $coeff=0.5) {
	$couleurs = _couleur_hex_to_dec($couleur);

	$red = $couleurs["red"] + round((255 - $couleurs["red"])*$coeff);
	$green = $couleurs["green"] + round((255 - $couleurs["green"])*$coeff);
	$blue = $couleurs["blue"] + round((255 - $couleurs["blue"])*$coeff);

	$couleur = _couleur_dec_to_hex($red, $green, $blue);
	
	return $couleur;

}

// selectionner les images qui vont subir une transformation sur un critere de taille
// ls images exclues sont marquees d'une class no_image_filtrer qui bloque les filtres suivants
// dans la fonction image_filtrer
// http://doc.spip.org/@image_select
function image_select($img,$width_min=0, $height_min=0, $width_max=10000, $height_max=1000){
	if (!$img) return $img;
	list ($h,$l) = taille_image($img);
	$select = true;
	if ($l<$width_min OR $l>$width_max OR $h<$height_min OR $h>$height_max)
		$select = false;

	$class = extraire_attribut($img,'class');
	$p = strpos($class,'no_image_filtrer');
	if (($select==false) AND ($p===FALSE)){
		$class .= " no_image_filtrer";
		$img = inserer_attribut($img,'class',$class);
	}
	if (($select==true) AND ($p!==FALSE)){
		$class = preg_replace(",\s*no_image_filtrer,","",$class);
		$img = inserer_attribut($img,'class',$class);
	}
	return $img;
}


// http://doc.spip.org/@image_passe_partout
function image_passe_partout($img,$taille_x = -1, $taille_y = -1,$force = false,$cherche_image=false,$process='AUTO'){
	if (!$img) return '';
	list ($hauteur,$largeur) = taille_image($img);
	if ($taille_x == -1)
		$taille_x = isset($GLOBALS['meta']['taille_preview'])?$GLOBALS['meta']['taille_preview']:150;
	if ($taille_y == -1)
		$taille_y = $taille_x;

	if ($taille_x == 0 AND $taille_y > 0)
		$taille_x = 1; # {0,300} -> c'est 300 qui compte
	elseif ($taille_x > 0 AND $taille_y == 0)
		$taille_y = 1; # {300,0} -> c'est 300 qui compte
	elseif ($taille_x == 0 AND $taille_y == 0)
		return '';
	
	list($destWidth,$destHeight,$ratio) = ratio_passe_partout($largeur,$hauteur,$taille_x,$taille_y);
	$fonction = array('image_passe_partout', func_get_args());
	return process_image_reduire($fonction,$img,$destWidth,$destHeight,$force,$cherche_image,$process);
}

// http://doc.spip.org/@image_reduire
function image_reduire($img, $taille = -1, $taille_y = -1, $force=false, $cherche_image=false, $process='AUTO') {
	// Determiner la taille x,y maxi
	// prendre le reglage de previsu par defaut
	if ($taille == -1)
		$taille = (isset($GLOBALS['meta']['taille_preview']) AND intval($GLOBALS['meta']['taille_preview']))?intval($GLOBALS['meta']['taille_preview']):150;
	if ($taille_y == -1)
		$taille_y = $taille;

	if ($taille == 0 AND $taille_y > 0)
		$taille = 10000; # {0,300} -> c'est 300 qui compte
	elseif ($taille > 0 AND $taille_y == 0)
		$taille_y = 10000; # {300,0} -> c'est 300 qui compte
	elseif ($taille == 0 AND $taille_y == 0)
		return '';

	$fonction = array('image_reduire', func_get_args());
	return process_image_reduire($fonction,$img,$taille,$taille_y,$force,$cherche_image,$process);
}

// Reduire une image d'un certain facteur
// http://doc.spip.org/@image_reduire_par
function image_reduire_par ($img, $val=1, $force=false) {
	list ($hauteur,$largeur) = taille_image($img);

	$l = round($largeur/$val);
	$h = round($hauteur/$val);
	
	if ($l > $h) $h = 0;
	else $l = 0;
	
	$img = image_reduire($img, $l, $h, $force);

	return $img;
}

?>
filtres_ecrire.php000066600000040465152525475610010307 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

/**
 * Fonctions utilisées au calcul des squelette du privé.
 *
 * @package SPIP\Filtres
 */
if (!defined('_ECRIRE_INC_VERSION')) return;

include_spip('inc/filtres_boites');
include_spip('inc/boutons');
include_spip('inc/pipelines_ecrire');


/**
 * Retourne les paramètres de personnalisation css de l'espace privé
 * 
 * Ces paramètres sont (ltr et couleurs) ce qui permet une écriture comme :
 * generer_url_public('style_prive', parametres_css_prive())
 * qu'il est alors possible de récuperer dans le squelette style_prive.html avec
 *
 * #SET{claire,##ENV{couleur_claire,edf3fe}}
 * #SET{foncee,##ENV{couleur_foncee,3874b0}}
 * #SET{left,#ENV{ltr}|choixsiegal{left,left,right}}
 * #SET{right,#ENV{ltr}|choixsiegal{left,right,left}}
 *
 * @return string
 */
function parametres_css_prive(){
	global $visiteur_session;

	$args = array();
	$args['v'] = $GLOBALS['spip_version_code'];
	$args['p'] = substr(md5($GLOBALS['meta']['plugin']),0,4);
	$args['themes'] = implode(',',lister_themes_prives());
	$args['ltr'] = $GLOBALS['spip_lang_left'];
	// un md5 des menus : si un menu change il faut maj la css
	$args['md5b'] = (function_exists('md5_boutons_plugins')?md5_boutons_plugins():'');

	$c = (is_array($visiteur_session)
		AND is_array($visiteur_session['prefs']))
		? $visiteur_session['prefs']['couleur']
		: 1;

	$couleurs = charger_fonction('couleurs', 'inc');
	parse_str($couleurs($c),$c);
	$args = array_merge($args, $c);

	if (_request('var_mode')=='recalcul' OR (defined('_VAR_MODE') AND _VAR_MODE=='recalcul'))
		$args['var_mode'] = 'recalcul';

	return http_build_query($args);
}


/**
 * Afficher le sélecteur de rubrique
 *
 * Il permet de placer un objet dans la hiérarchie des rubriques de SPIP
 *
 * @param string $titre
 * @param int $id_objet
 * @param int $id_parent
 * @param string $objet
 * @param int $id_secteur
 * @param bool $restreint
 * @param bool $actionable
 *   true : fournit le selecteur dans un form directement postable
 * @param bool $retour_sans_cadre
 * @return string
 */
function chercher_rubrique($titre,$id_objet, $id_parent, $objet, $id_secteur, $restreint,$actionable = false, $retour_sans_cadre=false){
	global $spip_lang_right;
	include_spip('inc/autoriser');
	if (intval($id_objet) && !autoriser('modifier', $objet, $id_objet))
		return "";
	if (!sql_countsel('spip_rubriques'))
		return "";
	$chercher_rubrique = charger_fonction('chercher_rubrique', 'inc');
	$form = $chercher_rubrique($id_parent, $objet, $restreint, ($objet=='rubrique')?$id_objet:0);

	if ($id_parent == 0) $logo = "racine-24.png";
	elseif ($id_secteur == $id_parent) $logo = "secteur-24.png";
	else $logo = "rubrique-24.png";

	$confirm = "";
	if ($objet=='rubrique') {
		// si c'est une rubrique-secteur contenant des breves, demander la
		// confirmation du deplacement
		$contient_breves = sql_countsel('spip_breves', "id_rubrique=".intval($id_objet));

		if ($contient_breves > 0) {
			$scb = ($contient_breves>1? 's':'');
			$scb = _T('avis_deplacement_rubrique',
				array('contient_breves' => $contient_breves,
				      'scb' => $scb));
			$confirm .= "\n<div class='confirmer_deplacement verdana2'>"
			            ."<div class='choix'><input type='checkbox' name='confirme_deplace' value='oui' id='confirme-deplace' /><label for='confirme-deplace'>"
			            . $scb .
			            "</label></div></div>\n";
		} else
			$confirm .= "<input type='hidden' name='confirme_deplace' value='oui' />\n";
	}
	$form .= $confirm;
	if ($actionable){
		if (strpos($form,'<select')!==false) {
			$form .= "<div style='text-align: $spip_lang_right;'>"
				. '<input class="fondo" type="submit" value="'._T('bouton_choisir').'"/>'
				. "</div>";
		}
		$form = "<input type='hidden' name='editer_$objet' value='oui' />\n" . $form;
		$form = generer_action_auteur("editer_$objet", $id_objet, self(), $form, " method='post' class='submit_plongeur'");
	}

	if ($retour_sans_cadre)
		return $form;

	include_spip('inc/presentation');
	return debut_cadre_couleur($logo, true, "", $titre) . $form .fin_cadre_couleur(true);

}


/**
 * Tester si le site peut avoir des visiteurs
 *
 * @param bool $past
 *   si true, prendre en compte le fait que le site a *deja* des visiteurs
 *   comme le droit d'en avoir de nouveaux
 * @param bool $accepter
 * @return bool
 */
function avoir_visiteurs($past=false, $accepter=true) {
	if ($GLOBALS['meta']["forums_publics"] == 'abo') return true;
	if ($accepter AND $GLOBALS['meta']["accepter_visiteurs"] <> 'non') return true;
	if (sql_countsel('spip_articles', "accepter_forum='abo'"))return true;
	if (!$past) return false;
	return sql_countsel('spip_auteurs',
	                    "statut NOT IN ('0minirezo','1comite', '5poubelle')
	                    AND (statut<>'nouveau' OR prefs NOT IN ('0minirezo','1comite', '5poubelle'))");
}

/**
 * Lister les status d'article visibles dans l'espace prive
 * en fonction du statut de l'auteur
 *
 * Pour l'extensibilie de SPIP, on se repose sur autoriser('voir','article')
 * en testant un à un les status présents en base
 *
 * On mémorise en static pour éviter de refaire plusieurs fois.
 * 
 * @param string $statut_auteur
 * @return array
 */
function statuts_articles_visibles($statut_auteur){
	static $auth = array();
	if (!isset($auth[$statut_auteur])){
		$auth[$statut_auteur] = array();
		$statuts = array_map('reset',sql_allfetsel('distinct statut','spip_articles'));
		foreach($statuts as $s){
			if (autoriser('voir','article',0,array('statut'=>$statut_auteur),array('statut'=>$s)))
				$auth[$statut_auteur][] = $s;
		}
	}

	return $auth[$statut_auteur];
}

/**
 * Traduire le statut technique de l'auteur en langage compréhensible
 * 
 * Si $statut=='nouveau' et que le statut en attente est fourni,
 * le prendre en compte en affichant que l'auteur est en attente
 *
 * @param string $statut
 * @param string $attente
 * @return string
 */
function traduire_statut_auteur($statut,$attente=""){
	$plus = "";
	if ($statut=='nouveau') {
		if ($attente) {
			$statut = $attente;
			$plus = " ("._T('info_statut_auteur_a_confirmer').")";
		}
		else return _T('info_statut_auteur_a_confirmer');
	}

	$recom = array("info_administrateurs" => _T('item_administrateur_2'),
		       "info_redacteurs" =>  _T('intem_redacteur'),
		       "info_visiteurs" => _T('item_visiteur'),
		       '5poubelle' => _T('texte_statut_poubelle'), // bouh
		       );
	if (isset($recom[$statut]))
		return $recom[$statut].$plus;

	// retrouver directement par le statut sinon
	if ($t = array_search($statut, $GLOBALS['liste_des_statuts'])){
	  if (isset($recom[$t]))
			return $recom[$t].$plus;
		return _T($t).$plus;
	}

	// si on a pas reussi a le traduire, retournons la chaine telle quelle
	// c'est toujours plus informatif que rien du tout
	return $statut;
}

/**
 * Afficher la mention des autres auteurs ayant modifié un objet
 *
 * @param int $id_objet
 * @param string $objet
 * @return string
 */
function afficher_qui_edite($id_objet,$objet){
	static $qui = array();
	if (isset($qui[$objet][$id_objet]))
		return $qui[$objet][$id_objet];

	if ($GLOBALS['meta']['articles_modif'] == 'non')
		return $qui[$objet][$id_objet] = '';
	
	include_spip('inc/drapeau_edition');
	$modif = mention_qui_edite($id_objet, $objet);
	if (!$modif) return $qui[$objet][$id_objet] = '';

	include_spip('base/objets');
	$infos = lister_tables_objets_sql(table_objet_sql($objet));
	if (isset($infos['texte_signale_edition']))
		return $qui[$objet][$id_objet] = _T($infos['texte_signale_edition'], $modif);
	
	return $qui[$objet][$id_objet] = _T('info_qui_edite', $modif);
}

/**
 * Lister les statuts des auteurs
 *
 * @param string $quoi
 *   redacteurs : retourne les statuts des auteurs au moins redacteur, tels que defini par AUTEURS_MIN_REDAC
 *   visiteurs : retourne les statuts des autres auteurs, cad les visiteurs et autres statuts perso
 *   tous : retourne tous les statuts connus
 * @param bool $en_base
 *   si true, ne retourne strictement que les status existants en base
 *   dans tous les cas, les statuts existants en base sont inclus
 * @return array
 */
function auteurs_lister_statuts($quoi='tous',$en_base=true) {
	if (!defined('AUTEURS_MIN_REDAC')) define('AUTEURS_MIN_REDAC', "0minirezo,1comite,5poubelle");

	switch($quoi){
		case "redacteurs":
			$statut = AUTEURS_MIN_REDAC;
			$statut = explode(',',$statut);
			if ($en_base) {
				$check = array_map('reset',sql_allfetsel('DISTINCT statut','spip_auteurs',sql_in('statut',$statut)));
				$retire = array_diff($statut,$check);
				$statut = array_diff($statut,$retire);
			}
			return array_unique($statut);
			break;
		case "visiteurs":
			$statut = array();
			$exclus = AUTEURS_MIN_REDAC;
			$exclus = explode(',',$exclus);
			if (!$en_base){
				// prendre aussi les statuts de la table des status qui ne sont pas dans le define
				$statut = array_diff(array_values($GLOBALS['liste_des_statuts']),$exclus);
			}
			$s_complement = array_map('reset',sql_allfetsel('DISTINCT statut','spip_auteurs',sql_in('statut',$exclus,'NOT')));
			return array_unique(array_merge($statut,$s_complement));
			break;
		default:
		case "tous":
			$statut = array_values($GLOBALS['liste_des_statuts']);
			$s_complement = array_map('reset',sql_allfetsel('DISTINCT statut','spip_auteurs',sql_in('statut',$statut,'NOT')));
			$statut = array_merge($statut,$s_complement);
			if ($en_base) {
				$check = array_map('reset',sql_allfetsel('DISTINCT statut','spip_auteurs',sql_in('statut',$statut)));
				$retire = array_diff($statut,$check);
				$statut = array_diff($statut,$retire);
			}
			return array_unique($statut);
			break;
	}

	// on arrive jamais ici
	return array_values($GLOBALS['liste_des_statuts']);
}

/**
 * Déterminer la rubrique pour la création d'un objet heuristique
 * 
 * Rubrique courante si possible,
 * - sinon rubrique administrée pour les admin restreint
 * - sinon première rubrique de premier niveau autorisée que l'on trouve
 *
 * @param int $id_rubrique Identifiant de rubrique (si connu)
 * @param string $objet    Objet en cours de création
 * @return int             Identifiant de la rubrique dans laquelle créer l'objet
 */
function trouver_rubrique_creer_objet($id_rubrique,$objet){
	global $connect_id_rubrique;
	if (!$id_rubrique AND defined('_CHOIX_RUBRIQUE_PAR_DEFAUT') AND _CHOIX_RUBRIQUE_PAR_DEFAUT){
		$in = !count($connect_id_rubrique)
			? ''
			: (" AND ".sql_in('id_rubrique', $connect_id_rubrique));

		// on tente d'abord l'ecriture a la racine dans le cas des rubriques uniquement
		if ($objet == 'rubrique') {
			$id_rubrique = 0;
		} else {
			$id_rubrique = sql_getfetsel('id_rubrique', 'spip_rubriques', "id_parent=0$in", '', "id_rubrique DESC", 1);
		}
		
		if (!autoriser("creer{$objet}dans", 'rubrique', $id_rubrique)){
			// manque de chance, la rubrique n'est pas autorisee, on cherche un des secteurs autorises
			$res = sql_select("id_rubrique", "spip_rubriques", "id_parent=0");
			while (!autoriser("creer{$objet}dans", 'rubrique', $id_rubrique) && $row_rub = sql_fetch($res)){
				$id_rubrique = $row_rub['id_rubrique'];
			}
		}
	}
  return $id_rubrique;
}

/**
 * Afficher le lien de redirection d'un article virtuel si il y a lieu
 * (rien si l'article n'est pas redirige)
 * 
 * @param string $virtuel
 * @return string
 */
function lien_article_virtuel($virtuel){
	include_spip('inc/lien');
  if (!$virtuel = virtuel_redirige($virtuel))
	  return '';
  return propre("[->".$virtuel."]");
}


/**
 * Filtre pour generer un bouton RSS prive
 * protege par un hash de faible securite
 *
 * http://doc.spip.org/@bouton_spip_rss
 *
 * @param string $op
 * @param array $args
 * @param string $lang
 * @return string
 */
function bouton_spip_rss($op, $args=array(), $lang='', $title='RSS') {
	include_spip('inc/acces');
	$clic = http_img_pack('feed.png', 'RSS', '', $title);
	$args = param_low_sec($op, $args, $lang, 'rss');
	$url = generer_url_public('rss', $args);
	return "<a style='float: ".$GLOBALS['spip_lang_right'].";' href='$url'>$clic</a>";
}


/**
 * Verifier la presence d'alertes pour les auteur
 *
 * http://doc.spip.org/@alertes_auteur
 *
 * @param int $id_auteur
 * @return string
 */
function alertes_auteur($id_auteur) {

	$alertes = array();

	// si on n'est plus compatible avec php4 : le dire a tous ceux qui passent
	// dans l'espace prive
	if (version_compare(phpversion(), _PHP_MIN) == -1)
		$alertes[] = _L('SPIP n&#233;cessite PHP&nbsp;@min@, votre version est @version@.', array('min'=> _PHP_MIN, 'version' => phpversion()));

	if (isset($GLOBALS['meta']['message_crash_tables'])
	AND autoriser('detruire', null, null, $id_auteur)) {
		include_spip('genie/maintenance');
		if ($msg = message_crash_tables())
			$alertes[] = $msg;
	}

	if (isset($GLOBALS['meta']['message_crash_plugins'])
	  AND $GLOBALS['meta']['message_crash_plugins']
	  AND autoriser('configurer', '_plugins', null, $id_auteur)
	  AND is_array($msg = unserialize($GLOBALS['meta']['message_crash_plugins']))) {
			$msg = implode(', ',array_map('joli_repertoire',array_keys($msg)));
			$alertes[] = _T('plugins_erreur', array('plugins' => $msg));
	}

	$a = isset($GLOBALS['meta']['message_alertes_auteurs']) ? $GLOBALS['meta']['message_alertes_auteurs'] : '';
	if ($a
		AND is_array($a = unserialize($a))
	  AND count($a)){
		$update = false;
		if (isset($a[$GLOBALS['visiteur_session']['statut']])){
			$alertes = array_merge($alertes,$a[$GLOBALS['visiteur_session']['statut']]);
			unset($a[$GLOBALS['visiteur_session']['statut']]);
			$update = true;
		}
		if (isset($a[''])){
			$alertes = array_merge($alertes,$a['']);
			unset($a['']);
			$update = true;
		}
		if ($update)
			ecrire_meta("message_alertes_auteurs",serialize($a));
	}

	if (isset($GLOBALS['meta']['plugin_erreur_activation'])
	  AND autoriser('configurer', '_plugins', null, $id_auteur)) {
		include_spip('inc/plugin');
		$alertes[] = plugin_donne_erreurs();
	}

	$alertes = pipeline(
		'alertes_auteur',
			array(
			'args' => array(
				'id_auteur' => $id_auteur,
				'exec' => _request('exec'),
				),
			'data' => $alertes
			)
		);

	if ($alertes = array_filter($alertes))
		return "<div class='wrap-messages-alertes'><div class='messages-alertes'>".
			join(' | ', $alertes)
			."</div></div>";
}

/**
 * Filtre pour afficher les rubriques enfants d'une rubrique
 * @param int $id_rubrique
 * @return string
 */
function filtre_afficher_enfant_rub_dist($id_rubrique){
	include_spip('inc/presenter_enfants');
	return afficher_enfant_rub(intval($id_rubrique));
}

/**
 * Afficher un petit "i" pour lien vers autre page
 *
 * @param string $lien
 * 		URL du lien desire
 * @param string $titre
 * 		Titre au survol de l'icone pointant le lien
 * @param string $titre_lien
 * 		Si present, ajoutera en plus apres l'icone
 * 		un lien simple, vers la meme URL,
 * 		avec le titre indique
 * 
 * @return string
 */
function afficher_plus_info($lien, $titre="+", $titre_lien="") {
	$titre = attribut_html($titre);
	$icone = "\n<a href='$lien' title='$titre' class='plus_info'>" .
		http_img_pack("information-16.png", $titre) ."</a>";

	if (!$titre_lien) {
		return $icone;
	} else {
		return $icone .  "\n<a href='$lien'>$titre_lien</a>";
	}
}

/**
 * Lister les id objet_source associés à l'objet id_objet
 * via la table de lien objet_lien
 * 
 * Utilisé pour les listes de #FORMULAIRE_EDITER_LIENS
 *
 * @param string $objet_source
 * @param string $objet
 * @param int $id_objet
 * @param string $objet_lien
 * @return array
 */
function lister_objets_lies($objet_source,$objet,$id_objet,$objet_lien){
	include_spip('action/editer_liens');
	$l = array();
	// quand $objet == $objet_lien == $objet_source on reste sur le cas par defaut de $objet_lien == $objet_source
	if ($objet_lien==$objet AND $objet_lien!==$objet_source){
		$res = objet_trouver_liens(array($objet=>$id_objet),array($objet_source=>'*'));
	}
	else{
		$res = objet_trouver_liens(array($objet_source=>'*'),array($objet=>$id_objet));
	}
	while ($row = array_shift($res))
		$l[] = $row[$objet_source];

	return $l;
}
?>
csv.php000066600000006154152525475610006076 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

function analyse_csv($t)
{
	$virg = substr_count($t, ',');
	$pvirg = substr_count($t, ';');
	$tab = substr_count($t, "\t");
	if ($virg > $pvirg)
		{ $sep = ','; $hs = '&#44;';}
	else	{ $sep = ';'; $hs = '&#59;'; $virg = $pvirg;}
	if ($tab > $virg) {$sep = "\t"; $hs = "\t";}

	$t = preg_replace('/\r?\n/', "\n",
				      preg_replace('/[\r\n]+/', "\n", $t));
	// un separateur suivi de 3 guillemets attention !
	// attention au ; suceptible d'etre confondu avec un separateur
	// on substitue un # et on remplacera a la fin
	$t = preg_replace("/([\n$sep])\"\"\"/",'\\1"&#34#',$t);
	$t = str_replace('""','&#34#',$t);
	preg_match_all('/"[^"]*"/', $t, $r);
	foreach($r[0] as $cell)
		$t = str_replace($cell,
			str_replace($sep, $hs,
				str_replace("\n", "<br />",
					    substr($cell,1,-1))),
			$t);
	list($entete, $corps) = explode("\n",$t,2);
	$caption = '';
	// sauter la ligne de tete formee seulement de separateurs
	if (substr_count($entete, $sep) == strlen($entete)) {
		list($entete, $corps) = explode("\n",$corps,2);
	}
	// si une seule colonne, en faire le titre
	if (preg_match("/^([^$sep]+)$sep+\$/", $entete, $l)) {
			$caption = "\n||" .  $l[1] . "|";
			list($entete, $corps) = explode("\n",$corps,2);
	}
	// si premiere colonne vide, le raccourci doit quand meme produire <th...
	if ($entete[0] == $sep) $entete = ' ' . $entete;

	$lignes = explode("\n", $corps);

	// retrait des lignes vides finales
	while(count($lignes) > 0
	AND preg_match("/^$sep*$/", $lignes[count($lignes)-1]))
	  unset($lignes[count($lignes)-1]);
	//  calcul du  nombre de colonne a chaque ligne
	$nbcols = array();
	$max = $mil = substr_count($entete, $sep);
	foreach($lignes as $k=>$v) {
	  if ($max <> ($nbcols[$k]= substr_count($v, $sep))) {
	    if ($max > $nbcols[$k])
	      $mil = $nbcols[$k];
	    else { $mil = $max; $max = $nbcols[$k];}
	  }
	}
	// Si pas le meme nombre, cadrer au nombre max
	if ($mil <> $max)
	  foreach($nbcols as $k=>$v) {
	    if ($v < $max) $lignes[$k].= str_repeat($sep, $max-$v);
	    }
	// et retirer les colonnes integralement vides
	while(true) {
	  $nbcols =  ($entete[strlen($entete)-1]===$sep);
	  foreach($lignes as $v) $nbcols &= ($v[strlen($v)-1]===$sep);
	  if (!$nbcols) break;
	  $entete = substr($entete,0,-1);
	  foreach($lignes as $k=>$v) $lignes[$k] = substr($v,0,-1);
	}

	foreach($lignes as &$l) {
		$l = explode($sep, $l);
	}
	return array(explode($sep, $entete), $lignes);
}


?>
nfslock.php000066600000025551152525475610006744 0ustar00<?php
/* 
 *
 *
 * function: nfslock
 * original author: Chuck Cranor <chuck@maria.wustl.edu>
 * rewritten by: Alexis Rosen <alexis@panix.com>
 * rewritten by: Cedric Morin <cedric@yterium.com> for php&SPIP
 *
 * (Excerpts from Chuck's notes:
 *   this becomes complex, due to our dear friend, the NFS mounted mail spool.
 *   the netbsd code didn't do this properly, as far as I could tell.
 *
 *   - you can't trust exclusive creating opens over NFS, the protocol
 *   just doesn't support it.   so to do a lock you have to create
 *   a tmp file and then try and hard link it to your lock file.
 *   - to detect a stale lock file you have to see how old it is, but
 *   you can't use time(0) because that is the time on the local system
 *   and the file gets the times of the NFS server.  when is a lock
 *   file stale?  people seem to like 120 or 300 seconds.)
 *
 * NB: It is _critical_ that nfslock()ed files be unlocked by nfsunlock().
 * Simply unlinking the lock file is a good way to trash someone else's lock
 * file. All it takes is for the process doing the unlink to get hung for
 * a few minutes when it doesn't expect it. Meanwhile, its lock expires and
 * a second process forces the lock and creates its own. Then the first
 * process comes along and kills the second process' lock while it's still
 * valid.
 *
 * Security considerations:
 * If we're root, be very careful to see that the temp file we opened is
 * what we think it is. The problem is that we could lose a race with
 * someone who takes our tmp file and replaces it with, say, a hard
 * link to /etc/passwd. Then, if the first lock attempt fails, we'll
 * write a char to the file (see 4. below); this would truncate the
 * passwd file. So we make sure that the link count is 1. We don't really
 * care about any other screwing around since we don't write anything
 * sensitive to the lock file, nor do we change its owner or mode. If
 * someone beats us on a race and replaces our temp file with anything
 * else, it's no big deal- the file may get truncated, but there's no
 * possible security breach. ...Actually the possibility of the race
 * ever happening, given the random name of the file, is virtually nil.
 *
 * args: path = path to directory of lock file (/net/u/1/a/alexis/.mailspool)
 *       namelock = file name of lock file (alexis.lock)
 *	 max_age = age of lockfile, in seconds, after which the lock is stale.
 *		stale locks are always broken. Defaults to DEFAULT_LOCKTIME
 *		if zero. Panix mail locks go stale at 300 seconds, the default.
 *       notify = 1 if we should tell stdout that we're sleeping on a lock
 *
 * Returns the time that the lock was created on the other system. This is
 * important for nfsunlock(). If the lock already exists, returns NFSL_LOCKED.
 * If there is some other failure, return NFSL_SYSF. If NFSL_LOCKED is
 * returned, errno is also set to EEXIST. If we're root and the link count
 * on the tmp file is wrong, return NFSL_SECV.
 *
 * Mods of 7/13/95: Change a bit of code to re-stat the lockfile after
 * closing it. This is to work around a bug in SunOS that appears to to affect
 * some SunOS 4.1.3 machines (but not all). The bug is that close() updates
 * the stat st_ctime field for that file. So use lstat on fullpath instead
 * of fstat on tmpfd. This alteration applies to both nfslock and nfslock1.
 *
 * Mod of 5/4/95: Change printf's to fprintf(stderr... in nfslock and nfslock1.
 *
 * Mods of 4/29/95: Fix freeing memory before use if a stat fails. Remove
 * code that forbids running as root; instead, if root, check link count on
 * tmp file after opening it.
 *
 * Mods of 4/27/95: Return the create time instead of the lockfile's fd, which
 * is useless. Added new routines nfsunlock(), nfslock_test(), nfslock_renew().
 *
 * Mods of 1/8/95: Eliminate some security checks since this code never
 * runs as root. In particular, we completely eliminate the safeopen
 * routine. But add one check: if we _are_ root, fail immediately.
 *
 * Change arguments: take a path and a filename. Don't assume a global or
 * macro pointing to a mailspool.
 *
 * Add notify argument; if 1, tell user when we're waiting for a lock.
 *
 * Add max_age argument and DEFAULT_LOCKTIME.
 *
 * Change comments drastically.
 *
 */

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

include_spip('inc/acces');
define('_DEFAULT_LOCKTIME',60);
define('_NAME_LOCK','spip_nfs_lock');

// http://doc.spip.org/@spip_nfslock
function spip_nfslock($fichier,$max_age=0) {
	$tries = 0;
		
	if (!$max_age) $max_age = _DEFAULT_LOCKTIME;
	$lock_file = _DIR_TMP . _NAME_LOCK . "-" . substr(md5($fichier),0,8);

  
	/*
	 * 1. create a tmp file with a psuedo random file name. we also make
	 *    tpath which is a buffer to store the full pathname of the tmp file.
	 */

	$id = creer_uniqid();
	$tpath = _DIR_TMP."slock.$id";
	$tmpfd = @fopen($tpath, 'w'); // hum, le 'x' necessite php4,3,2 ...
	if (!$tmpfd) {	/* open failed */
		@fclose($tmpfd);
		spip_unlink($tpath);
		return false; //NFSL_SYSF
	}
  
	/*
	 * 2. make fullpath, a buffer for the full pathname of the lock file.
	 *    then start looping trying to lock it
	 */

	while ($tries < 10) {
		/*
		 * 3. link tmp file to lock file.  if it goes, we win and we clean
		 *    up and return the st_ctime of the lock file.
		 */

		if (link($tpath, $lock_file) == 1) {
			spip_unlink($tpath); /* got it! */
			@fclose($tmpfd);
			if (($our_tmp = lstat($lock_file))==false) {	/* stat failed... shouldn't happen */
				spip_unlink($lock_file);
				return false; // (NFSL_SYSF);
			}
			return($our_tmp['ctime']);
		}

		/*
		 * 4. the lock failed.  check for a stale lock file, being mindful
		 *    of NFS and the fact the time is set from the NFS server.  we
		 *    do a write on the tmp file to update its time to the server's
		 *    idea of "now."
		 */

		$old_stat = lstat($lock_file);
		if (@fputs($tmpfd, "zz", 2)!=2 || !$our_tmp=fstat($tmpfd))
			break; /* something bogus is going on */


		if ($old_stat!=false && (($old_stat['ctime'] + $max_age) < $our_tmp['ctime'])) {
			spip_unlink($lock_file); /* break the stale lock */
			$tries++;
			/* It is CRITICAL that we sleep after breaking
			 * the lock. Otherwise, we could race with
			 * another process and unlink it's newly-
			 * created file.
			 */
			sleep(1+rand(0,4));
			continue;
		}

		/*
		 * 5. try again
		 */

    $tries++;
		sleep(1+rand(0,4));
  }

	/*
	 * 6. give up, failure.
	 */

	spip_unlink($tpath);
	@fclose($tmpfd);
	return false; //(NFSL_LOCKED);
}

/*
 * function: nfsunlock
 * author: Alexis Rosen <alexis@panix.com>
 *
 * Unlock an nfslock()ed file.
 *
 * This can get tricky because the lock may have expired (perhaps even
 * during a process that should be "atomic"). We have to make sure we don't
 * unlock some other process' lock, and return a panic code if we think our
 * lock file has been broken illegally. What's done in reaction to that panic
 * (of anything) is up to the caller. See the comments on nfslock()!
 *
 * args: path = path to directory of lock file (/net/u/1/a/alexis/.mailspool)
 *       namelock = file name of lock file (alexis.lock)
 *       max_age = age of lockfile, in seconds, after which the lock is stale.
 *		stale locks are always broken. Defaults to DEFAULT_LOCKTIME
 *		if zero. Panix mail locks go stale at 300 seconds, the default.
 *	 birth = time the lock was created (as returned by nfslock()).
 *
 * Returns NFSL_OK if successful, NFSL_LOST if the lock has been lost
 * legitimately (because more than max_age has passed since the lock was
 * created), and NFSL_STOLEN if it's been tampered with illegally (i.e.
 * while this program is within the expiry period). Returns NFSL_SYSF if
 * another system failure prevents it from even trying to unlock the file.
 *
 * Note that for many programs, a return code of NFSL_LOST or NFSL_STOLEN is
 * equally disastrous; a NFSL_STOLEN means that some other program may have
 * trashed your file, but a NFSL_LOST may mean that _you_ have trashed someone
 * else's file (if in fact you wrote the file that you locked after you lost
 * the lock) or that you read inconsistent information.
 *
 * In practice, a return code of NFSL_LOST or NFSL_STOLEN will virtually never
 * happen unless someone is violating the locking protocol.
 *
 */

// http://doc.spip.org/@spip_nfsunlock
function spip_nfsunlock($fichier, $birth, $max_age=0, $test = false) {
	$id = creer_uniqid();
	if (!$max_age) $max_age = _DEFAULT_LOCKTIME;

	/*
	 * 1. Build a temp file and stat that to get an idea of what the server
	 *    thinks the current time is (our_tmp.st_ctime)..
	 */

	$tpath = _DIR_TMP."stime.$id";
  $tmpfd = @fopen($tpath,'w');
  if ((!$tmpfd)
    OR (@fputs($tmpfd, "zz", 2) != 2)
		OR !($our_tmp = fstat($tmpfd))) {
		/* The open failed, or we can't write the file, or we can't stat it */
		@fclose($tmpfd);
		spip_unlink($tpath);
		return false; //(NFSL_SYSF);
	}

	@fclose($tmpfd);		/* We don't need this once we have our_tmp.st_ctime. */
	spip_unlink($tpath);

	/*
	 * 2. make fullpath, a buffer for the full pathname of the lock file
	 */

	$lock_file = _DIR_TMP . _NAME_LOCK . "-" . substr(md5($fichier),0,8);

	/*
	 * 3. If the ctime hasn't been modified, unlink the file and return. If the
	 *    lock has expired, sleep the usual random interval before returning.
	 *    If we didn't sleep, there could be a race if the caller immediately
	 *    tries to relock the file.
	 */

  if ( ($old_stat=@lstat($lock_file))	/* stat succeeds so file is there */
	  && ($old_stat['ctime'] == $birth)) {	/* hasn't been modified since birth */
	  if (!$test)
    	spip_unlink($lock_file);			/* so the lock is ours to remove */
    if ($our_tmp['ctime'] >= $birth + $max_age){	/* the lock has expired */
    	if (!$test) return false; //(NFSL_LOST);
      sleep(1+(random(0,4)));		/* so sleep a bit */
    }
		return true;//(NFSL_OK);			/* success */
  }

	/*
	 * 4. Either ctime has been modified, or the entire lock file is missing.
	 *    If the lock should still be ours, based on the ctime of the temp
	 *    file, return with NFSL_STOLEN. If not, then our lock is expired and
	 *    someone else has grabbed the file, so return NFSL_LOST.
	 */

	if ($our_tmp['ctime'] < $birth + $max_age)	/* lock was stolen */
		return false; //(NFSL_STOLEN);

	return false; //(NFSL_LOST);	/* The lock must have expired first. */
}


/*
 * function: nfslock_test
 * author: Alexis Rosen <alexis@panix.com>
 *
 * Test a lock to see if it's still valid.
 *
 * See the comments in nfsunlock() about lost and stolen locks.
 *
 * Args, return codes, and behavior are identical to nfsunlock except
 * that nfslock_test doesn't remove the lock. NFSL_OK means the lock is
 * good, NFLS_LOST and NFSL_STOLEN means it's bad, and NFSL_SYSF means
 * we couldn't tell due to system failure.
 *
 * The source for this routine is almost identical to nfsunlock(), but it's
 * coded separately to make things as clear as possible.
 */

// http://doc.spip.org/@spip_nfslock_test
function spip_nfslock_test($fichier, $birth, $max_age=0) {
	return spip_nfsunlock($fichier, $birth, $max_age, true);
}

?>
editer.php000066600000040214152525475610006552 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

if (!defined('_ECRIRE_INC_VERSION')) return;
include_spip('base/abstract_sql');

// http://doc.spip.org/@formulaires_editer_objet_traiter
function formulaires_editer_objet_traiter($type, $id='new', $id_parent=0, $lier_trad=0, $retour='', $config_fonc='articles_edit_config', $row=array(), $hidden=''){

	$res = array();
	// eviter la redirection forcee par l'action...
	set_request('redirect');
	if ($action_editer = charger_fonction("editer_$type",'action',true)) {
		list($id,$err) = $action_editer($id);
	}
	else {
		$action_editer = charger_fonction("editer_objet",'action');
		list($id,$err) = $action_editer($id,$type);
	}
	$id_table_objet = id_table_objet($type);
	$res[$id_table_objet] = $id;
	if ($err OR !$id){
		$res['message_erreur'] = ($err?$err:_T('erreur'));
	}
	else{
		// Un lien de trad a prendre en compte
		if ($lier_trad){
			// referencer la traduction
			$referencer_traduction = charger_fonction('referencer_traduction','action');
			$referencer_traduction($type, $id, $lier_trad);
			// dupliquer tous les liens sauf les auteurs : le nouvel auteur est celui qui traduit
			// cf API editer_liens
			include_spip('action/editer_liens');
			objet_dupliquer_liens($type,$lier_trad,$id,null,array('auteur'));
		}

		$res['message_ok'] = _T('info_modification_enregistree');
		if ($retour) {
			if (strncmp($retour,'javascript:',11)==0){
				$res['message_ok'] .= '<script type="text/javascript">/*<![CDATA[*/'.substr($retour,11).'/*]]>*/</script>';
				$res['editable'] = true;
			}
			else
				$res['redirect'] = parametre_url($retour,$id_table_objet,$id);
		}
	}
	return $res;
}

// http://doc.spip.org/@formulaires_editer_objet_verifier
function formulaires_editer_objet_verifier($type,$id='new', $oblis = array()){
	$erreurs = array();
	if (intval($id)) {
		$conflits = controler_contenu($type,$id);
		if ($conflits AND count($conflits)) {
			foreach($conflits as $champ=>$conflit) {
				if (!isset($erreurs[$champ])) { $erreurs[$champ] = ''; }
				$erreurs[$champ] .= _T("alerte_modif_info_concourante")."<br /><textarea readonly='readonly' class='forml'>".$conflit['base']."</textarea>";
			}
		}
	}
	foreach($oblis as $obli) {
		$value = _request($obli);
		if (is_null($value) OR !(is_array($value)?count($value):strlen($value))) {
			if (!isset($erreurs[$obli])) { $erreurs[$obli] = ''; }
			$erreurs[$obli] .= _T("info_obligatoire");
		}
	}
	return $erreurs;
}

// http://doc.spip.org/@formulaires_editer_objet_charger
function formulaires_editer_objet_charger($type, $id='new', $id_parent=0, $lier_trad=0, $retour='', $config_fonc='articles_edit_config', $row=array(), $hidden=''){
	$table_objet = table_objet($type);
	$table_objet_sql = table_objet_sql($type);
	$id_table_objet = id_table_objet($type);
	$new = !is_numeric($id);
	// Appel direct dans un squelette
	if (!$row) {
		if  (!$new OR $lier_trad) {
			if ($select = charger_fonction("precharger_" . $type, 'inc', true))
				$row = $select($id, $id_parent, $lier_trad);
			else $row = sql_fetsel('*',$table_objet_sql,$id_table_objet."=".intval($id));
			if (!$new)
				$md5 = controles_md5($row);
		}
		if (!$row) {
			$trouver_table = charger_fonction('trouver_table','base');
			if ($desc = $trouver_table($table_objet))
				foreach($desc['field'] as $k=>$v) $row[$k]='';
		}
	}

	// Gaffe: sans ceci, on ecrase systematiquement l'article d'origine
	// (et donc: pas de lien de traduction)
	$id = ($new OR $lier_trad)
		? 'oui'
		: $row[$id_table_objet];
	$row[$id_table_objet] = $id;

	$contexte = $row;
	if (strlen($id_parent) && is_numeric($id_parent) && (!isset($contexte['id_parent']) OR $new)){
		if (!isset($contexte['id_parent'])) unset($contexte['id_rubrique']);
		$contexte['id_parent']=$id_parent;
	}
	elseif (!isset($contexte['id_parent'])){
		// id_rubrique dans id_parent si possible
		if (isset($contexte['id_rubrique'])) {
			$contexte['id_parent'] = $contexte['id_rubrique'];
			unset($contexte['id_rubrique']);
		}
		else{
			$contexte['id_parent'] = '';
		}
		if (!$contexte['id_parent']
			AND $preselectionner_parent_nouvel_objet = charger_fonction("preselectionner_parent_nouvel_objet","inc",true))
			$contexte['id_parent'] = $preselectionner_parent_nouvel_objet($type,$row);
	}

	if ($config_fonc)
		$contexte['config'] = $config = $config_fonc($contexte);
	if (!isset($config['lignes'])) $config['lignes'] = 0;
	$att_text = " class='textarea' "
	. " rows='"
	. ($config['lignes'] +15)
	. "' cols='40'";
	if (isset($contexte['texte']))
		list($contexte['texte'],$contexte['_texte_trop_long']) = editer_texte_recolle($contexte['texte'],$att_text);

	// on veut conserver la langue de l'interface ;
	// on passe cette donnee sous un autre nom, au cas ou le squelette
	// voudrait l'exploiter
	if (isset($contexte['lang'])) {
		$contexte['langue'] = $contexte['lang'];
		unset($contexte['lang']);
	}

	$contexte['_hidden'] = "<input type='hidden' name='editer_$type' value='oui' />\n" .
		 (!$lier_trad ? '' :
		 ("\n<input type='hidden' name='lier_trad' value='" .
		  $lier_trad .
		  "' />" .
		  "\n<input type='hidden' name='changer_lang' value='" .
		  $config['langue'] .
		  "' />"))
		  . $hidden
		  . (isset($md5) ? $md5 : '');


	if (isset($contexte['extra']))
		$contexte['extra'] = unserialize($contexte['extra']);
	// preciser que le formulaire doit passer dans un pipeline
	$contexte['_pipeline'] = array('editer_contenu_objet',array('type'=>$type,'id'=>$id));

	// preciser que le formulaire doit etre securise auteur/action
	// n'est plus utile lorsque l'action accepte l'id en argument direct
	// on le garde pour compat 
	$contexte['_action'] = array("editer_$type",$id);

	return $contexte;
}

//
// Gestion des textes trop longs (limitation brouteurs)
// utile pour les textes > 32ko

// http://doc.spip.org/@coupe_trop_long
function coupe_trop_long($texte){
	$aider = charger_fonction('aider', 'inc');
	if (strlen($texte) > 28*1024) {
		$texte = str_replace("\r\n","\n",$texte);
		$pos = strpos($texte, "\n\n", 28*1024);	// coupe para > 28 ko
		if ($pos > 0 and $pos < 32 * 1024) {
			$debut = substr($texte, 0, $pos)."\n\n<!--SPIP-->\n";
			$suite = substr($texte, $pos + 2);
		} else {
			$pos = strpos($texte, " ", 28*1024);	// sinon coupe espace
			if (!($pos > 0 and $pos < 32 * 1024)) {
				$pos = 28*1024;	// au pire (pas d'espace trouv'e)
				$decalage = 0; // si y'a pas d'espace, il ne faut pas perdre le caract`ere
			} else {
				$decalage = 1;
			}
			$debut = substr($texte,0,$pos + $decalage); // Il faut conserver l'espace s'il y en a un
			$suite = substr($texte,$pos + $decalage);
		}
		return (array($debut,$suite));
	}
	else
		return (array($texte,''));
}

// http://doc.spip.org/@editer_texte_recolle
function editer_texte_recolle($texte, $att_text)
{
	if ((strlen($texte)<29*1024)
	 OR (include_spip('inc/layer') AND ($GLOBALS['browser_name']!="MSIE")) )
	 return array($texte,"");

	include_spip('inc/barre');
	$textes_supplement = "<br /><span style='color: red'>"._T('info_texte_long')."</span>\n";
	$nombre = 0;

	while (strlen($texte)>29*1024) {
		$nombre ++;
		list($texte1,$texte) = coupe_trop_long($texte);
		$textes_supplement .= "<br />" .
			"<textarea id='texte$nombre' name='texte_plus[$nombre]'$att_text>$texte1</textarea>\n";
		}
	return array($texte,$textes_supplement);
}

/**
 * auto-renseigner le titre si il n'existe pas
 *
 * @param $champ_titre
 * @param $champs_contenu
 * @param int $longueur
 */
function titre_automatique($champ_titre,$champs_contenu,$longueur=null){
	if (!_request($champ_titre)){
		$titrer_contenu = charger_fonction('titrer_contenu','inc');
		if (!is_null($longueur))
			$t = $titrer_contenu($champs_contenu,null,$longueur);
		else
			$t = $titrer_contenu($champs_contenu);
		if ($t)
			set_request($champ_titre,$t);
	}
}

/**
 * Determiner un titre automatique,
 * a partir des champs textes de contenu
 *
 * @param array $champs_contenu
 *   liste des champs contenu textuels
 * @param array|null $c
 *   tableau qui contient les valeurs des champs de contenu
 *   si null on utilise les valeurs du POST
 * @param int $longueur
 *   longueur de coupe
 * @return string
 */
function inc_titrer_contenu_dist($champs_contenu, $c=null, $longueur=50){
	// trouver un champ texte non vide
	$t = "";
	foreach($champs_contenu as $champ){
		if ($t = _request($champ,$c))
			break;
	}

	if ($t){
		include_spip('inc/texte_mini');
		$t = couper($t,$longueur,"...");
	}

	return $t;
}

// Produit la liste des md5 d'un tableau de donnees, sous forme
// de inputs html
// http://doc.spip.org/@controles_md5
function controles_md5($data, $prefixe='ctr_', $format='html'){
	if (!is_array($data))
		return false;

	$ctr = array();
	foreach ($data as $key => $val) {
		$m = md5($val);
		$k = $prefixe.$key;

		switch ($format) {
			case 'html':
				$ctr[$k] = "<input type='hidden' value='$m' name='$k' />";
				break;
			default:
				$ctr[$k] = $m;
				break;
		}
	}

	if ($format == 'html')
		return "\n\n<!-- controles md5 -->\n".join("\n", $ctr)."\n\n";
	else
		return $ctr;
}

// http://doc.spip.org/@controler_contenu
function controler_contenu($type, $id, $options=array(), $c=false, $serveur='') {
	include_spip('inc/filtres');

	$table_objet = table_objet($type);
	$spip_table_objet = table_objet_sql($type);
	$id_table_objet = id_table_objet($type);
	$trouver_table = charger_fonction('trouver_table', 'base');
	$desc = $trouver_table($table_objet, $serveur);

	// Appels incomplets (sans $c)
	if (!is_array($c)) {
		foreach($desc['field'] as $champ=>$ignore)
			if(_request($champ))
				$c[$champ] = _request($champ);
	}

	// Securite : certaines variables ne sont jamais acceptees ici
	// car elles ne relevent pas de autoriser(article, modifier) ;
	// il faut passer par instituer_XX()
	// TODO: faut-il passer ces variables interdites
	// dans un fichier de description separe ?
	unset($c['statut']);
	unset($c['id_parent']);
	unset($c['id_rubrique']);
	unset($c['id_secteur']);

	// Gerer les champs non vides
	if (isset($options['nonvide']) AND is_array($options['nonvide']))
	foreach ($options['nonvide'] as $champ => $sinon)
		if ($c[$champ] === '')
			$c[$champ] = $sinon;

	// N'accepter que les champs qui existent
	// TODO: ici aussi on peut valider les contenus
	// en fonction du type
	$champs = array();
	foreach($desc['field'] as $champ => $ignore)
		if (isset($c[$champ]))
			$champs[$champ] = $c[$champ];

	// Nettoyer les valeurs
	$champs = array_map('corriger_caracteres', $champs);

	// Envoyer aux plugins
	$champs = pipeline('pre_edition',
		array(
			'args' => array(
				'table' => $spip_table_objet, // compatibilite
				'table_objet' => $table_objet,
				'spip_table_objet' => $spip_table_objet,
				'type' =>$type,
				'id_objet' => $id,
				'champs' => isset($options['champs'])?$options['champs']:array(), // [doc] c'est quoi ?
				'action' => 'controler'
			),
			'data' => $champs
		)
	);

	if (!$champs) return false;

	// Verifier si les mises a jour sont pertinentes, datees, en conflit etc
	$conflits = controler_md5($champs, $_POST, $type, $id, $serveur, isset($options['prefix'])?$options['prefix']:'ctr_');

	return $conflits;
}

// Controle la liste des md5 envoyes, supprime les inchanges,
// signale les modifies depuis telle date
// http://doc.spip.org/@controler_md5
function controler_md5(&$champs, $ctr, $type, $id, $serveur, $prefix = 'ctr_') {
	$table_objet = table_objet($type);
	$spip_table_objet = table_objet_sql($type);
	$id_table_objet = id_table_objet($type);

	// Controle des MD5 envoyes
	// On elimine les donnees non modifiees par le formulaire (mais
	// potentiellement modifiees entre temps par un autre utilisateur)
	foreach ($champs as $key => $val) {
		if (isset($ctr[$prefix.$key]) AND $m = $ctr[$prefix.$key]) {
			if ($m == md5($val))
				unset ($champs[$key]);
		}
	}
	if (!$champs) return;

	// On veut savoir si notre modif va avoir un impact
	// par rapport aux donnees contenues dans la base
	// (qui peuvent etre differentes de celles ayant servi a calculer le ctr)
	$s = sql_fetsel(array_keys($champs), $spip_table_objet, "$id_table_objet=$id", $serveur);
	$intact = true;
	foreach ($champs as $ch => $val)
		$intact &= ($s[$ch] == $val);
	if ($intact) return;

	// Detection de conflits :
	// On verifie si notre modif ne provient pas d'un formulaire
	// genere a partir de donnees modifiees dans l'intervalle ; ici
	// on compare a ce qui est dans la base, et on bloque en cas
	// de conflit.
	$ctrh = $ctrq = $conflits = array();
	foreach (array_keys($champs) as $key) {
		if (isset($ctr[$prefix.$key]) AND $m = $ctr[$prefix.$key]) {
			$ctrh[$key] = $m;
			$ctrq[] = $key;
		}
	}
	if ($ctrq) {
		$ctrq = sql_fetsel($ctrq, $spip_table_objet, "$id_table_objet=$id", $serveur);
		foreach ($ctrh as $key => $m) {
			if ($m != md5($ctrq[$key])
			AND $champs[$key] !== $ctrq[$key]) {
				$conflits[$key] = array(
					'base' => $ctrq[$key],
					'post' => $champs[$key]
				);
				unset($champs[$key]); # stocker quand meme les modifs ?
			}
		}
	}

	return $conflits;
}

// http://doc.spip.org/@display_conflit_champ
function display_conflit_champ($x) {
	if (strstr($x, "\n") OR strlen($x)>80)
		return "<textarea style='width:99%; height:10em;'>".entites_html($x)."</textarea>\n";
	else
		return "<input type='text' size='40' style='width:99%' value=\"".entites_html($x)."\" />\n";
}

// http://doc.spip.org/@signaler_conflits_edition
function signaler_conflits_edition($conflits, $redirect='') {
	include_spip('inc/minipres');
	include_spip('inc/revisions');
	include_spip('afficher_diff/champ');
	include_spip('inc/suivi_versions');
	include_spip('inc/diff');
	foreach ($conflits as $champ=>$a) {
		// probleme de stockage ou conflit d'edition ?
		$base = isset($a['save']) ? $a['save'] : $a['base'];

		$diff = new Diff(new DiffTexte);
		$n = preparer_diff($a['post']);
		$o = preparer_diff($base);
		$d = propre_diff(
			afficher_para_modifies(afficher_diff($diff->comparer($n,$o))));

		$titre = isset($a['save']) ? _L('Echec lors de l\'enregistrement du champ @champ@', array('champ' => $champ)) : $champ;

		$diffs[] = "<h2>$titre</h2>\n"
			. "<h3>"._T('info_conflit_edition_differences')."</h3>\n"
			. "<div style='max-height:8em; overflow: auto; width:99%;'>".$d."</div>\n"
			. "<h4>"._T('info_conflit_edition_votre_version')."</h4>"
			. display_conflit_champ($a['post'])
			. "<h4>"._T('info_conflit_edition_version_enregistree')."</h4>"
			. display_conflit_champ($base);
	}

	if ($redirect) {
		$id = uniqid(rand());
		$redirect = "<form action='$redirect' method='get'
			id='$id'
			style='float:".$GLOBALS['spip_lang_right']."; margin-top:2em;'>\n"
		.form_hidden($redirect)
		."<input type='submit' value='"._T('icone_retour')."' />
		</form>\n";

		// pour les documents, on est probablement en ajax : il faut ajaxer
		if (_AJAX)
			$redirect .= '<script type="text/javascript">'
			.'setTimeout(function(){$("#'.$id.'")
			.ajaxForm({target:$("#'.$id.'").parent()});
			}, 200);'
			."</script>\n";

	}

	echo minipres(
		_T('titre_conflit_edition'),

		'<style>
.diff-para-deplace { background: #e8e8ff; }
.diff-para-ajoute { background: #d0ffc0; color: #000; }
.diff-para-supprime { background: #ffd0c0; color: #904040; text-decoration: line-through; }
.diff-deplace { background: #e8e8ff; }
.diff-ajoute { background: #d0ffc0; }
.diff-supprime { background: #ffd0c0; color: #802020; text-decoration: line-through; }
.diff-para-deplace .diff-ajoute { background: #b8ffb8; border: 1px solid #808080; }
.diff-para-deplace .diff-supprime { background: #ffb8b8; border: 1px solid #808080; }
.diff-para-deplace .diff-deplace { background: #b8b8ff; border: 1px solid #808080; }
</style>'
		.'<p>'._T('info_conflit_edition_avis_non_sauvegarde').'</p>'
		.'<p>'._T('texte_conflit_edition_correction').'</p>'
		."<div style='text-align:".$GLOBALS['spip_lang_left'].";'>"
		. join("\n",$diffs)
		."</div>\n"

		. $redirect
	);
}

?>
lang.php000066600000033402152525475610006220 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

/**
 * Gestion des langues et choix de langue 
 *
 * @package SPIP\Langue
**/
if (!defined('_ECRIRE_INC_VERSION')) return;



/**
 * Changer la langue courante
 *
 * Définit la langue utilisée par la langue désignée
 * si elle fait partie des langues utilisables dans le site.
 *
 * Cette fonction définit les globales :
 * spip_lang, spip_lang_rtl, spip_lang_right, spip_lang_left
 * 
 * @param string $lang
 *     La langue à utiliser
 * @return string|bool
 *     string : La langue qui a été utilisée si trouvée
 *     false : aucune langue ne correspondait à la demande
**/
function changer_langue($lang) {
	global $spip_lang_rtl, $spip_lang_right, $spip_lang_left;

	$liste_langues = ',' . @$GLOBALS['meta']['langues_proposees']
	. ',' . @$GLOBALS['meta']['langues_multilingue'] . ',';

	// Si la langue demandee n'existe pas, on essaie d'autres variantes
	// Exemple : 'pt-br' => 'pt_br' => 'pt'
	$lang = str_replace('-', '_', trim($lang));
	if (!$lang)
		return false;

	if (strpos($liste_langues,",$lang,")!==false
	OR ($lang = preg_replace(',_.*,', '', $lang)
	AND strpos($liste_langues,",$lang,")!==false)) {

		$spip_lang_rtl =   lang_dir($lang, '', '_rtl');
		$spip_lang_right = $spip_lang_rtl ? 'left' : 'right';
		$spip_lang_left =  $spip_lang_rtl ? 'right' : 'left';

		return $GLOBALS['spip_lang'] = $lang;
	} else
		return false;
}

//
// Gestion des blocs multilingues
// Selection dans un tableau dont les index sont des noms de langues
// de la valeur associee a la langue en cours
// si absente, retourne le premier
// remarque : on pourrait aussi appeler un service de traduction externe
// ou permettre de choisir une langue "plus proche",
// par exemple le francais pour l'espagnol, l'anglais pour l'allemand, etc.

function choisir_traduction ($trads, $lang='') {
	$k = approcher_langue($trads, $lang);
	return $k ? $trads[$k] : array_shift($trads);
}

// retourne son 2e argument si c'est un index du premier
// ou un index approchant sinon et si possible, 
// la langue X etant consideree comme une approche de X_Y
function approcher_langue ($trads, $lang='') {

	if (!$lang) $lang = $GLOBALS['spip_lang']; 

	if (isset($trads[$lang])) {
		return $lang;
	}
	// cas des langues xx_yy
	else {
		$r = explode('_', $lang);
		if (isset($trads[$r[0]]))
			return $r[0];
	}
	return '';
}

// http://doc.spip.org/@traduire_nom_langue
function traduire_nom_langue($lang) {
	include_spip('inc/lang_liste');
	include_spip('inc/charsets');
	return html2unicode(isset($GLOBALS['codes_langues'][$lang]) ? $GLOBALS['codes_langues'][$lang] : $lang);
}

//
// Filtres de langue
//

// Donne la direction d'ecriture a partir de la langue. Retourne 'gaucher' si
// la langue est arabe, persan, kurde, pachto, ourdou (langues ecrites en
// alphabet arabe a priori), hebreu, yiddish (langues ecrites en alphabet
// hebreu a priori), 'droitier' sinon.
// C'est utilise par #LANG_DIR, #LANG_LEFT, #LANG_RIGHT.
// http://doc.spip.org/@lang_dir
function lang_dir($lang='', $droitier='ltr', $gaucher='rtl') {
	static $lang_rtl = array('ar', 'fa', 'ku', 'prs', 'ps', 'ur', 'he', 'heb', 'hbo', 'yi');

	return in_array(($lang ? $lang : $GLOBALS['spip_lang']), $lang_rtl) ?
		$gaucher : $droitier;
}

// typo francaise ou anglaise ?
// $lang_objet est fixee dans l'interface privee pour editer
// un texte anglais en interface francaise (ou l'inverse) ;
// sinon determiner la typo en fonction de la langue courante

// http://doc.spip.org/@lang_typo
function lang_typo($lang='') {
	if (!$lang) {
		$lang = isset($GLOBALS['lang_objet'])
			? $GLOBALS['lang_objet']
			: $GLOBALS['spip_lang'];
	}
	if ($lang == 'eo'
	  OR $lang == 'fr'
	  OR strncmp($lang, 'fr_', 3)==0
	  OR $lang == 'cpf')
		return 'fr';
	else
		return 'en';
}

// gestion de la globale $lang_objet pour que les textes soient affiches
// avec les memes typo et direction dans l'espace prive que dans le public
// http://doc.spip.org/@changer_typo
function changer_typo($lang = '') {
	if ($lang)
		$GLOBALS['lang_objet'] = $lang;
	else
		unset($GLOBALS['lang_objet']);
}

//
// Afficher un menu de selection de langue
// - 'var_lang_ecrire' = langue interface privee,
// pour var_lang' = langue de l'article, espace public, voir les squelettes
// pour 'changer_lang' (langue de l'article, espace prive), c'est en Ajax
// 
// http://doc.spip.org/@menu_langues
function menu_langues($nom_select, $default='') {
	include_spip('inc/actions');

	$langues = liste_options_langues($nom_select);
	$ret = "";
	if (!count($langues))
		return '';

	if (!$default) $default = $GLOBALS['spip_lang'];
	foreach ($langues as $l) {
		$selected = ($l == $default) ? ' selected=\'selected\'' : '';
		$ret .= "<option value='$l'$selected>[".$l."] ".traduire_nom_langue($l)."</option>\n";
	}

	if (!test_espace_prive()) {
		$cible = self();
		$base = '';
	} else {
		$cible = self();
		$base = spip_connect() ? 'base' : '';
	}

	$change = ' onchange="this.parentNode.parentNode.submit()"';
	return generer_action_auteur('converser',$base, $cible,
		(select_langues($nom_select, $change, $ret)
		 . "<noscript><div style='display:inline'><input type='submit' class='fondo' value='". _T('bouton_changer')."' /></div></noscript>"),
				     " method='post'");
}

// http://doc.spip.org/@select_langues
function select_langues($nom_select, $change, $options, $label="")
{
	static $cpt = 0;
	$id = "menu_langues" . $cpt++;
	return
		"<label for='$id'>".($label?$label:_T('info_langues'))."</label> ".
		"<select name='$nom_select' id='$id' "
	  . ((!test_espace_prive()) ?
	     ("class='forml menu_langues'") :
	     (($nom_select == 'var_lang_ecrire')  ?
	      ("class='lang_ecrire'") :
	      "class='fondl'"))
	  . $change
	  . ">\n"
	  . $options
	  . "</select>";
}

/**
 * Lister les langues disponibles
 *
 * Retourne un tableau de langue utilisables, triées par code de langue,
 * mais pas le même tableau en fonction du paramètre $nom_select.
 * 
 * @param string $nom_select
 *     Attribut name du select
 *     Selon son nom, retourne une liste différente :
 *     - var_lang ou changer_lang :
 *         liste des langues sélectionnées dans la config multilinguisme
 *     - var_lang_ecrire :
 *         toutes les langues présentes en fichier de langue
 * @return array
 *     Liste des langues
 */
function liste_options_langues($nom_select) {

	switch($nom_select) {
		# #MENU_LANG
		case 'var_lang':
		# menu de changement de la langue d'un article
		# les langues selectionnees dans la configuration "multilinguisme"
		case 'changer_lang':
			$langues = explode(',', $GLOBALS['meta']['langues_multilingue']);
			break;
	# menu de l'interface (privee, installation et panneau de login)
	# les langues presentes sous forme de fichiers de langue
	# on force la relecture du repertoire des langues pour etre synchrone.
		case 'var_lang_ecrire':
		default:
			$GLOBALS['meta']['langues_proposees'] = '';
			init_langues();
			$langues = explode(',', $GLOBALS['meta']['langues_proposees']);
			break;

# dernier choix possible : toutes les langues = langues_proposees 
# + langues_multilingues ; mais, ne sert pas
#			$langues = explode(',', $GLOBALS['all_langs']);
	}
	if (count($langues) <= 1) return array();
	sort($langues);
	return $langues;
}



/**
 * Redirige sur la bonne langue lorsque l'option forcer_lang est active
 * 
 * Cette fonction est appelee depuis ecrire/public.php si on a installé
 * la variable de personnalisation $forcer_lang ; elle renvoie le brouteur
 * si necessaire vers l'URL xxxx?lang=ll
 *
 * @return void
**/
function verifier_lang_url() {
	global $spip_lang;

	// quelle langue est demandee ?
	$lang_demandee = (test_espace_prive()?$GLOBALS['spip_lang']:$GLOBALS['meta']['langue_site']);
	if (isset($_COOKIE['spip_lang_ecrire']))
		$lang_demandee = $_COOKIE['spip_lang_ecrire'];
	if (!test_espace_prive() AND isset($_COOKIE['spip_lang']))
		$lang_demandee = $_COOKIE['spip_lang'];
	if (isset($_GET['lang']))
		$lang_demandee = $_GET['lang'];

	// Renvoyer si besoin (et si la langue demandee existe)
	if ($spip_lang != $lang_demandee
	AND changer_langue($lang_demandee)
	AND $lang_demandee != @$_GET['lang']) {
		$destination = parametre_url(self(),'lang', $lang_demandee, '&');
		// ici on a besoin des var_truc
		foreach ($_GET as $var => $val) {
			if (!strncmp('var_', $var, 4))
				$destination = parametre_url($destination, $var, $val, '&');
		}
		include_spip('inc/headers');
		redirige_par_entete($destination);
	}

	// Subtilite : si la langue demandee par cookie est la bonne
	// alors on fait comme si $lang etait passee dans l'URL
	// (pour criteres {lang}).
	$GLOBALS['lang'] = $_GET['lang'] = $spip_lang;
}


/**
 * Utilise la langue du site
 *
 * Change la langue en cours d'utilisation par la langue du site
 * si ce n'est pas déjà le cas.
 *
 * Note : Cette fonction initialise la globale spip_lang au chargement de inc/lang
 *
 * @return string
 *     La langue sélectionnée
**/
function utiliser_langue_site() {
	// s'il existe une langue du site (en gros tout le temps en théorie)
	if (isset($GLOBALS['meta']['langue_site'])
	  // et si spip_langue est pas encore définie (ce que va faire changer_langue())
	  // ou qu'elle n'est pas identique à la langue du site
	  AND (!isset($GLOBALS['spip_lang'])
	    OR $GLOBALS['spip_lang']!=$GLOBALS['meta']['langue_site']))
	{
		return changer_langue($GLOBALS['meta']['langue_site']);//@:install
	}
	// en theorie là, la globale est définie, sinon c'est un problème.
	if (!isset($GLOBALS['spip_lang'])) {
		spip_log("La globale spip_lang est indéfinie dans utiliser_langue_site() !", _LOG_ERREUR);
	}
	return $GLOBALS['spip_lang'];
}

/**
 * Initialise la langue pour un visiteur du site 
 *
 * La langue est choisie dans cet ordre :
 * - Dans le cookie 'spip_lang' ou 'spip_lang_ecrire' s'il existe (selon l'espace public ou privé).
 * - Sinon dans la session du visiteur.
 * - Sinon dans une des langues définie en préférence du navigateur
 * - Sinon la langue du site
 * 
 * @return string
 *     La langue utilisée
**/
function utiliser_langue_visiteur() {

	$l = (!test_espace_prive()  ? 'spip_lang' : 'spip_lang_ecrire');
	if (isset($_COOKIE[$l]))
		if (changer_langue($l = $_COOKIE[$l]))
			return $l;

	if (isset($GLOBALS['visiteur_session']['lang']))
		if (changer_langue($l = $GLOBALS['visiteur_session']['lang']))
			return $l;

	foreach (explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']) as $s)  {
		if (preg_match('#^([a-z]{2,3})(-[a-z]{2,3})?(;q=[0-9.]+)?$#i', trim($s), $r)) {
			if (changer_langue($l=strtolower($r[1])))
				return $l;
		}
	}

	return utiliser_langue_site();
}


/**
 * Retourne le répertoire contenant le module de langue indiqué
 * 
 * Note : pourrait en donner une liste... compliqué
 * 
 * @param string $module
 *     Nom du module de lang
 * @param string $lang
 *     Langue du module de langue
 * @return string
 *     Adresse du répertoire contenant le module
 *     Retourne ecrire/lang/ s'il rien ne semble correspondre.
**/
function repertoire_lang($module='spip', $lang='fr') {
	# valeur forcee (par ex.sur spip.net), old style, a faire disparaitre
	if (defined('_DIR_LANG'))
		return _DIR_LANG;

	# regarder s'il existe une v.f. qq part
	if ($f = find_in_path($module.'_'.$lang . '.php', 'lang/'))
		return dirname($f).'/';

	# sinon, je ne sais trop pas quoi dire...
	return _DIR_RESTREINT . 'lang/';
}


/**
 * Initialisation des listes de langues
 *
 * Initialise les métas :
 * - langues_proposees : liste des traductions disponibles
 * - langue_site       : langue par défaut du site
 *
 * Lorsque ces métas n'existent pas encore (c'est à dire à l'installation),
 * elles sont calculées en obtenant la liste des langues
 * dans les fichiers de lang 
 * 
 * @return void
**/
function init_langues() {

	// liste des langues dans les meta, sauf a l'install
	$all_langs = @$GLOBALS['meta']['langues_proposees'];

	$tout = array();
	if (!$all_langs) {
		if ($d = @opendir(repertoire_lang())) {
			while (($f = readdir($d)) !== false) {
				if (preg_match(',^spip_([a-z_]+)\.php[3]?$,', $f, $regs))
					$tout[] = $regs[1];
			}
			closedir($d);
			sort($tout);
			$tout = join(',', $tout);
			// Si les langues n'ont pas change, ne rien faire
			if ($tout != $all_langs) {
				$GLOBALS['meta']['langues_proposees'] =	$tout;
				include_spip('inc/meta');
				ecrire_meta('langues_proposees', $tout);
			} else $tout = '';
		}
	}
	if (!isset($GLOBALS['meta']['langue_site'])) {
		// Initialisation : le francais si dispo, sinon la premiere langue trouvee
		$GLOBALS['meta']['langue_site'] = $tout =
		(!$all_langs OR (strpos(','._LANGUE_PAR_DEFAUT.',',",$all_langs,")!==false))
		  ? _LANGUE_PAR_DEFAUT :  substr($all_langs,0,strpos($all_langs,','));
		ecrire_meta('langue_site', $tout);
	}
}

/**
 * Retourne une balise <html>
 *
 * Retourne une balise HTML contenant les attributs 'lang' et 'dir'
 * définis sur la langue en cours d'utilisation,
 * ainsi que des classes CSS de ces du nom de la langue et direction choisie.
 *
 * @return string
 *     Code html de la balise <html>
**/
function html_lang_attributes()
{
	$lang = $GLOBALS['spip_lang'];
	$dir = ($GLOBALS['spip_lang_rtl'] ? 'rtl' : 'ltr');
	return  "<html class='$dir $lang no-js' xmlns='http://www.w3.org/1999/xhtml' lang='$lang' dir='$dir'>\n" ;
}

// initialise les globales (liste des langue, langue du site, spip_lang...)
init_langues();
utiliser_langue_site();
?>
modifier.php000066600000025561152525475610007104 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

/**
 * Fonctions d'aides pour les fonctions d'objets de modification de contenus
 *
 * @package SPIP\Objets\Modifications
**/

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

/**
 * Collecte des champs postés
 * 
 * Fonction générique pour la collecte des posts
 * dans action/editer_xxx
 *
 * @param array $white_list
 *     Les champs à récupérer
 * @param array $black_list
 *     Les champs à ignorer
 * @param array|null $set
 *     array : Tableau des champs postés
 *     null  : Les champs sont obtenus par des _request() sur les noms de la white liste
 * @param bool $tous
 *     true : Recupère tous les champs de white_list meme ceux n'ayant pas ete postés
 * @return array
 *     Tableau des champs et valeurs collectées 
 */
function collecter_requests($white_list, $black_list, $set=null, $tous=false){
	$c = $set;
	if (!$c){
		$c = array();
		foreach($white_list as $champ) {
			// on ne collecte que les champs reellement envoyes par defaut.
			// le cas d'un envoi de valeur NULL peut du coup poser probleme.
			$val = _request($champ);
			if ($tous OR $val !== NULL) {
				$c[$champ] = $val;
			}
		}
		// on ajoute toujours la lang en saisie possible
		// meme si pas prevu au depart pour l'objet concerne
		if ($l = _request('changer_lang')){
			$c['lang'] = $l;
		}
	}
	foreach($black_list as $champ) {
		unset($c[$champ]);
	}
	
	return $c;
}

/**
 * Modifie le contenu d'un objet
 * 
 * Fonction generique pour l'API de modification de contenu, qui se
 * charge entre autres choses d'appeler les pipelines pre_edition
 * et post_edition
 *
 * Attention, pour éviter des hacks on interdit des champs
 * (statut, id_secteur, id_rubrique, id_parent),
 * mais la securite doit étre assurée en amont
 *
 * @api
 * @param string $objet
 *     Type d'objet
 * @param int $id_objet
 *     Identifiant de l'objet
 * @param array $options
 *     array data : tableau des donnees sources utilisees pour la detection de conflit ($_POST sinon fourni ou si nul)
 *     array nonvide : valeur par defaut des champs que l'on ne veut pas vide
 *     string date_modif : champ a mettre a date('Y-m-d H:i:s') s'il y a modif
 *     string invalideur : id de l'invalideur eventuel
 *     array champs : non documente (utilise seulement par inc/rechercher ?)
 *     string action : action realisee, passee aux pipelines pre/post edition (par defaut 'modifier')
 *     bool indexation : deprecie
 * @param array|null $c
 *     Couples champ/valeur à modifier
 * @param string $serveur
 *     Nom du connecteur à la base de données
 * @return bool|string
 *     - false  : Aucune modification, aucun champ n'est à modifier
 *     - chaîne vide : Vide si tout s'est bien passé
 *     - chaîne : Texte d'un message d'erreur
 */
function objet_modifier_champs($objet, $id_objet, $options, $c=null, $serveur='') {
	if (!$id_objet = intval($id_objet)) {
		spip_log('Erreur $id_objet non defini', 'warn');
		return _T('erreur_technique_enregistrement_impossible');
	}

	include_spip('inc/filtres');

	$table_objet = table_objet($objet,$serveur);
	$spip_table_objet = table_objet_sql($objet,$serveur);
	$id_table_objet = id_table_objet($objet,$serveur);
	$trouver_table = charger_fonction('trouver_table', 'base');
	$desc = $trouver_table($spip_table_objet, $serveur);

	// Appels incomplets (sans $c)
	if (!is_array($c)) {
		spip_log('erreur appel objet_modifier_champs('.$objet.'), manque $c');
		return _T('erreur_technique_enregistrement_impossible');
	}

	// Securite : certaines variables ne sont jamais acceptees ici
	// car elles ne relevent pas de autoriser(xxx, modifier) ;
	// il faut passer par instituer_XX()
	// TODO: faut-il passer ces variables interdites
	// dans un fichier de description separe ?
	unset($c['statut']);
	unset($c['id_parent']);
	unset($c['id_rubrique']);
	unset($c['id_secteur']);

	// Gerer les champs non vides
	if (isset($options['nonvide']) AND is_array($options['nonvide'])) {
		foreach ($options['nonvide'] as $champ => $sinon) {
			if ($c[$champ] === '') {
				$c[$champ] = $sinon;
			}
		}
	}


	// N'accepter que les champs qui existent
	// TODO: ici aussi on peut valider les contenus
	// en fonction du type
	$champs = array();
	foreach($desc['field'] as $champ => $ignore)
		if (isset($c[$champ]))
			$champs[$champ] = $c[$champ];

	// Nettoyer les valeurs
	$champs = array_map('corriger_caracteres', $champs);

	// Envoyer aux plugins
	$champs = pipeline('pre_edition',
		array(
			'args' => array(
				'table' => $spip_table_objet, // compatibilite
				'table_objet' => $table_objet,
				'spip_table_objet' => $spip_table_objet,
				'type' =>$objet,
				'id_objet' => $id_objet,
				'champs' => isset($options['champs']) ? $options['champs'] : array(), // [doc] c'est quoi ?
				'serveur' => $serveur,
				'action' => isset($options['action']) ? $options['action'] : 'modifier'
			),
			'data' => $champs
		)
	);

	if (!$champs) return false;


	// marquer le fait que l'objet est travaille par toto a telle date
	if ($GLOBALS['meta']['articles_modif'] != 'non') {
		include_spip('inc/drapeau_edition');
		signale_edition ($id_objet, $GLOBALS['visiteur_session'], $objet);
	}

	// Verifier si les mises a jour sont pertinentes, datees, en conflit etc
	include_spip('inc/editer');
	if (!isset($options['data']) OR is_null($options['data'])){
		$options['data'] = &$_POST;
	}
	$conflits = controler_md5($champs, $options['data'], $objet, $id_objet, $serveur);
	// cas hypothetique : normalement inc/editer verifie en amont le conflit edition
	// et gere l'interface
	// ici on ne renvoie donc qu'un messsage d'erreur, au cas ou on y arrive quand meme
	if ($conflits)
		return _T('titre_conflit_edition');

	if ($champs) {
		// cas particulier de la langue : passer par instituer_langue_objet
		if (isset($champs['lang'])){
			if ($changer_lang=$champs['lang']){
				$id_rubrique = 0;
				if ($desc['field']['id_rubrique']){
					$parent = ($objet=='rubrique')?'id_parent':'id_rubrique';
					$id_rubrique = sql_getfetsel($parent, $spip_table_objet, "$id_table_objet=".intval($id_objet));
				}
				$instituer_langue_objet = charger_fonction('instituer_langue_objet', 'action');
				$champs['lang'] = $instituer_langue_objet($objet, $id_objet, $id_rubrique, $changer_lang, $serveur);
			}
			// on laisse 'lang' dans $champs,
			// ca permet de passer dans le pipeline post_edition et de journaliser
			// et ca ne gene pas qu'on refasse un sql_updateq dessus apres l'avoir
			// deja pris en compte
		}

		// la modif peut avoir lieu

		// faut-il ajouter date_modif ?
		if (isset($options['date_modif']) AND $options['date_modif']
		AND !isset($champs[$options['date_modif']]))
			$champs[$options['date_modif']] = date('Y-m-d H:i:s');

		// allez on commit la modif
		sql_updateq($spip_table_objet, $champs, "$id_table_objet=".intval($id_objet), $serveur);

		// on verifie si elle est bien passee
		$moof = sql_fetsel(array_keys($champs), $spip_table_objet, "$id_table_objet=".intval($id_objet), array(), array(), '', array(), $serveur);
		// si difference entre les champs, reperer les champs mal enregistres
		if ($moof != $champs) {
			$liste = array();
			foreach($moof as $k=>$v)
				if ($v !== $champs[$k]
					// ne pas alerter si le champ est numerique est que les valeurs sont equivalentes
					AND (!is_numeric($v) OR intval($v)!=intval($champs[$k]))
					) {
					$liste[] = $k;
					$conflits[$k]['post'] = $champs[$k];
					$conflits[$k]['save'] = $v;
				}
			// si un champ n'a pas ete correctement enregistre, loger et retourner une erreur
			// c'est un cas exceptionnel
			if (count($liste)){
				spip_log("Erreur enregistrement en base $objet/$id_objet champs :".var_export($conflits,true),'modifier.'._LOG_CRITIQUE);
				return _T('erreur_technique_enregistrement_champs',array('champs'=>"<i>'".implode("'</i>,<i>'",$liste)."'</i>"));
			}
		}

		// Invalider les caches
		if (isset($options['invalideur']) and $options['invalideur']) {
			include_spip('inc/invalideur');
			if (is_array($options['invalideur']))
				array_map('suivre_invalideur',$options['invalideur']);
			else
				suivre_invalideur($options['invalideur']);
		}

		// Notifications, gestion des revisions...
		// en standard, appelle |nouvelle_revision ci-dessous
		pipeline('post_edition',
			array(
				'args' => array(
					'table' => $spip_table_objet,
					'table_objet' => $table_objet,
					'spip_table_objet' => $spip_table_objet,
					'type' =>$objet,
					'id_objet' => $id_objet,
					'champs' => isset($options['champs']) ? $options['champs'] : array(), // [doc] kesako ?
					'serveur' => $serveur,
					'action' => isset($options['action']) ? $options['action'] : 'modifier'
				),
				'data' => $champs
			)
		);
	}

	// journaliser l'affaire
	// message a affiner :-)
	include_spip('inc/filtres_mini');
	$qui = ((isset($GLOBALS['visiteur_session']['nom']) AND $GLOBALS['visiteur_session']['nom'])?$GLOBALS['visiteur_session']['nom']:$GLOBALS['ip']);
	journal(_L($qui.' a &#233;dit&#233; l&#8217;'.$objet.' '.$id_objet.' ('.join('+',array_diff(array_keys($champs), array('date_modif'))).')'), array(
		'faire' => 'modifier',
		'quoi' => $objet,
		'id' => $id_objet
	));

	return '';
}

/**
 * Modifie un contenu
 * 
 * Dépreciée :
 * Fonction générique pour l'API de modification de contenu
 *
 * @deprecated
 * @param string $type
 *     Type d'objet
 * @param int $id
 *     Identifiant de l'objet
 * @param array $options
 *     Toutes les options
 * @param array|null $c
 *     Couples champ/valeur à modifier
 * @param string $serveur
 *     Nom du connecteur à la base de données
 * @return bool
 *     true si quelque chose est modifié correctement
 *     false sinon (erreur ou aucun champ modifié)
 */
function modifier_contenu($type, $id, $options, $c=null, $serveur='') {
	$res = objet_modifier_champs($type, $id, $options, $c, $serveur);
	return ($res===''?true:false);
}

/**
 * Crée une modification d'un objet
 * 
 * Wrapper pour remplacer tous les obsoletes revision_xxx
 *
 * @deprecated
 *     Utiliser objet_modifier();
 * @see objet_modifier();
 * 
 * @param string $objet
 *     Nom de l'objet
 * @param int $id_objet
 *     Identifiant de l'objet
 * @param array $c
 *     Couples des champs/valeurs modifiées
 * @return mixed|string
 */
function revision_objet($objet,$id_objet,$c=null){
	$objet = objet_type($objet); // securite
	include_spip('action/editer_objet');
	return objet_modifier($objet,$id_objet,$c);
}


?>
pipelines_ecrire.php000066600000020764152525475610010627 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

/**
 * Fonctions déclarées dans des pipelines (espace privé)
 *
 * @package SPIP\Pipelines
**/
if (!defined('_ECRIRE_INC_VERSION')) return;


/**
 * Inserer jQuery et ses plugins pour l'espace privé
 *
 * La fonction ajoute les balises scripts dans le texte qui appelent
 * les scripts jQuery ainsi que certains de ses plugins. La liste
 * des js chargée peut être complété par le pipeline 'jquery_plugins'
 *
 * Cette fonction est appelée par le pipeline header_prive
 *
 * @see f_jQuery()
 * @link http://doc.spip.org/@f_jQuery
 * 
 * @param string $texte    Contenu qui sera inséré dans le head HTML
 * @return string          Contenu complété des scripts javascripts, dont jQuery
**/
function f_jQuery_prive ($texte) {
	$x = '';
	$jquery_plugins = pipeline('jquery_plugins',
		array(
			'prive/javascript/jquery.js',
			'prive/javascript/jquery.form.js',
			'prive/javascript/jquery.autosave.js',
			'prive/javascript/jquery.placeholder-label.js',
			'prive/javascript/ajaxCallback.js',
			'prive/javascript/jquery.colors.js',
			'prive/javascript/jquery.cookie.js',
			'prive/javascript/spip_barre.js',
		));
	foreach (array_unique($jquery_plugins) as $script)
		if ($script = find_in_path($script))
			$x .= "\n<script src=\"$script\" type=\"text/javascript\"></script>\n";
	// inserer avant le premier script externe ou a la fin
	if (preg_match(",<script[^><]*src=,",$texte,$match)
	  AND $p = strpos($texte,$match[0])){
	  $texte = substr_replace($texte,$x,$p,0);
	}
	else
		$texte .= $x;
	return $texte;
}


/**
 * Ajout automatique du title dans les pages du privé en squelette
 * 
 * Appellé dans le pipeline affichage_final_prive
 *
 * @param string $texte
 * @return string
 */
function affichage_final_prive_title_auto($texte){
	if (strpos($texte,'<title>')===false
	  AND
			(preg_match(",<h1[^>]*>(.+)</h1>,Uims", $texte, $match)
		   OR preg_match(",<h[23][^>]*>(.+)</h[23]>,Uims", $texte, $match))
		AND $match = textebrut(trim($match[1]))
		AND ($p = strpos($texte,'<head>'))!==FALSE) {
		if (!$nom_site_spip = textebrut(typo($GLOBALS['meta']["nom_site"])))
			$nom_site_spip=  _T('info_mon_site_spip');

		$titre = "<title>["
			. $nom_site_spip
			. "] ". $match
		  ."</title>";

		$texte = substr_replace($texte, $titre, $p+6,0);
	}
	return $texte;
}


// Fonction standard pour le pipeline 'boite_infos'
// http://doc.spip.org/@f_boite_infos
function f_boite_infos($flux) {
	$args = $flux['args'];
	$type = $args['type'];
	unset($args['row']);
	if (!trouver_fond($type,"prive/objets/infos/"))
		$type = 'objet';
	$flux['data'] .= recuperer_fond("prive/objets/infos/$type",$args);
	return $flux;
}


/**
 * pipeline recuperer_fond
 * Branchement automatise de affiche_gauche, affiche_droite, affiche_milieu
 * pour assurer la compat avec les versions precedentes des exec en php
 * Branche de affiche_objet
 * 
 * Les pipelines ne recevront plus exactement le meme contenu en entree,
 * mais la compat multi vertions pourra etre assuree
 * par une insertion au bon endroit quand le contenu de depart n'est pas vide
 * 
 * @param array $flux
 * @return array
 */
function f_afficher_blocs_ecrire($flux) {
	static $o=array();
	if (is_string($fond=$flux['args']['fond'])) {
		$exec = isset($flux['args']['contexte']['exec']) ? $flux['args']['contexte']['exec'] : _request('exec');
		if (!isset($o[$exec])){
			$o[$exec] = trouver_objet_exec($exec);
		}
		// cas particulier
		if ($exec=="infos_perso"){
			$flux['args']['contexte']['id_auteur'] = $GLOBALS['visiteur_session']['id_auteur'];
		}
		$typepage = (isset($flux['args']['contexte']['type-page'])?$flux['args']['contexte']['type-page']:$exec);
		if ($fond == "prive/squelettes/navigation/$typepage"){
			$flux['data']['texte'] = pipeline('affiche_gauche',array('args'=>$flux['args']['contexte'],'data'=>$flux['data']['texte']));
		}
		elseif ($fond=="prive/squelettes/extra/$typepage") {
			include_spip('inc/presentation_mini');
			$flux['data']['texte'] = pipeline('affiche_droite',array('args'=>$flux['args']['contexte'],'data'=>$flux['data']['texte'])).liste_objets_bloques($exec,$flux['args']['contexte']);
		}
		elseif ($fond=="prive/squelettes/hierarchie/$typepage" AND $o[$exec]) {
			// id non defini sur les formulaire de nouveaux objets
			$id = isset($flux['args']['contexte'][$o[$exec]['id_table_objet']]) ? intval($flux['args']['contexte'][$o[$exec]['id_table_objet']]) : 0;
			$flux['data']['texte'] = pipeline('affiche_hierarchie',array('args'=>array('objet'=>$o[$exec]['type'],'id_objet'=>$id),'data'=>$flux['data']['texte']));
		}
		elseif ($fond=="prive/squelettes/contenu/$typepage"){
			if (!strpos($flux['data']['texte'],"<!--affiche_milieu-->"))
				$flux['data']['texte'] = preg_replace(',<div id=["\']wysiwyg,',"<!--affiche_milieu-->\\0",$flux['data']['texte']);
			if ($o[$exec]
				AND $objet = $o[$exec]['type']
			  AND $o[$exec]['edition'] == false
			  AND $id = intval($flux['args']['contexte'][$o[$exec]['id_table_objet']])){
				// inserer le formulaire de traduction
				$flux['data']['texte'] = str_replace("<!--affiche_milieu-->",recuperer_fond('prive/objets/editer/traductions',array('objet'=>$objet,'id_objet'=>$id))."<!--affiche_milieu-->",$flux['data']['texte']);
				$flux['data']['texte'] = pipeline('afficher_fiche_objet',array(
																						'args'=>array(
																							'contexte'=>$flux['args']['contexte'],
																							'type'=>$objet,
																							'id'=>$id),
																						'data'=>$flux['data']['texte']));
			}
			$flux['data']['texte'] = pipeline('affiche_milieu',array('args'=>$flux['args']['contexte'],'data'=>$flux['data']['texte']));
		}
		elseif ($fond=="prive/squelettes/inclure/pied"){
			$flux['data']['texte'] = pipeline('affiche_pied',array('args'=>$flux['args']['contexte'],'data'=>$flux['data']['texte']));
		}
		elseif (strncmp($fond,"prive/objets/contenu/",21)==0
			AND $objet=basename($fond)
			AND $objet==substr($fond,21)
			AND isset($o[$objet]) 
			AND $o[$objet]) {
				$id = intval($flux['args']['contexte'][$o[$exec]['id_table_objet']]);
				$flux['data']['texte'] = pipeline('afficher_contenu_objet',array('args'=>array('type'=>$objet,'id_objet'=>$id,'contexte'=>$flux['args']['contexte']),'data'=>$flux['data']['texte']));
		}
	}

	return $flux;
}

/**
 * Afficher les taches en attente liees a un objet
 * @param string $flux
 * @return string
 */
function f_queue_affiche_milieu($flux){
	$args = $flux['args'];
	$res = "";
	foreach($args as $key=>$arg){
		if (preg_match(",^id_,",$key) AND is_numeric($arg) AND $arg=intval($arg)){
			$objet = preg_replace(',^id_,', '', $key);
			$res .= recuperer_fond('modeles/object_jobs_list',array('id_objet'=>$arg,'objet'=>$objet),array('ajax'=>true));
		}
	}
	if ($res)
		$flux['data'] = $res . $flux['data'];

	return $flux;
}

/**
 * Trouver l'objet qui correspond
 * a l'exec de l'espace prive passe en argument
 * renvoie false si pas d'objet en cours, ou un tableau associatif
 * contenant les informations table_objet_sql,table,type,id_table_objet,edition
 *
 * @param string $exec
 *   nom de la page testee
 * @return array|bool
 */
function trouver_objet_exec($exec){
	static $objet_exec=array();
	if (!$exec) return false;
	// cas particulier
	if ($exec=="infos_perso"){
		$exec = "auteur";
		set_request('id_auteur',$GLOBALS['visiteur_session']['id_auteur']);
	}
	if (!isset($objet_exec[$exec])){
		$objet_exec[$exec]=false;
		$infos = lister_tables_objets_sql();
		foreach($infos as $t=>$info){
			if ($exec==$info['url_edit'] AND $info['editable']){
				return $objet_exec[$exec] = array('edition'=>$exec==$info['url_voir']?'':true,'table_objet_sql'=>$t,'table'=>$info['table_objet'],'type'=>$info['type'],'id_table_objet'=>id_table_objet($info['type']));
			}
			if ($exec==$info['url_voir']){
				return $objet_exec[$exec] = array('edition'=>false,'table_objet_sql'=>$t,'table'=>$info['table_objet'],'type'=>$info['type'],'id_table_objet'=>id_table_objet($info['type']));
			}
		}
	}
	return $objet_exec[$exec];
}
?>
bandeau.php000066600000013010152525475610006667 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

include_spip('inc/boutons');

function definir_barre_contexte($contexte = null){
	if (is_null($contexte))
		$contexte = $_GET;
	elseif(is_string($contexte))
		$contexte = unserialize($contexte);
	if (!isset($contexte['id_rubrique']) AND isset($contexte['exec'])){
		if (!function_exists('trouver_objet_exec'))
			include_spip('inc/pipelines_ecrire');
		if ($e=trouver_objet_exec($contexte['exec'])){
			$_id = $e['id_table_objet'];
			if (isset($contexte[$_id]) AND $id=intval($contexte[$_id])){
				$table = $e['table_objet_sql'];
				$row = sql_fetsel('*',$table,"$_id=".intval($id));
				if (isset($row['id_rubrique'])){
					$contexte['id_rubrique'] = $row['id_rubrique'];
					if (isset($row['id_secteur']))
						$contexte['id_secteur'] = $row['id_secteur'];
				}
			}
		}
	}
	return $contexte;
}

/**
 * definir la liste des boutons du haut et de ses sous-menus
 * On defini les boutons a metrtre selon les droits de l'utilisateur
 * puis on balance le tout au pipeline "ajouter_menus" pour que des plugins
 * puissent y mettre leur grain de sel
 *
 * @param array $contexte
 * @param bool $icones // rechercher les icones
 * @param bool $autorise // ne renvoyer que les boutons autorises
 * @return array
 */
function definir_barre_boutons($contexte=array(),$icones = true, $autorise = true) {
    include_spip('inc/autoriser');
	$boutons_admin=array();

	// les boutons du core, issus de prive/navigation.xml
	$liste_boutons = array();

	// ajouter les boutons issus des plugin via plugin.xml
	if (function_exists('boutons_plugins')
	  AND is_array($liste_boutons_plugins = boutons_plugins()))
		$liste_boutons = &$liste_boutons_plugins;

	foreach($liste_boutons as $id => $infos){
		$parent = "";
		// les boutons principaux ne sont pas soumis a autorisation
		if (!isset($infos['parent']) OR !($parent = $infos['parent']) OR !$autorise OR autoriser('menu',"_$id",0,NULL,array('contexte'=>$contexte))){
			if ($parent
			  AND $parent = preg_replace(',^bando_,','menu_',$parent)
			  AND isset($boutons_admin[$parent])){
				if (!is_array($boutons_admin[$parent]->sousmenu))
					$boutons_admin[$parent]->sousmenu = array();
				$position = (isset($infos['position']) AND strlen($infos['position'])) ? intval($infos['position']) : count($boutons_admin[$parent]->sousmenu);
				if ($position<0) $position = count($boutons_admin[$parent]->sousmenu)+1+$position;
				$boutons_admin[$parent]->sousmenu = array_slice($boutons_admin[$parent]->sousmenu,0,$position)
				+ array($id=> new Bouton(
					($icones AND $infos['icone'])?find_in_theme($infos['icone']):'',  // icone
					$infos['titre'],	// titre
					(isset($infos['action']) AND $infos['action'])?$infos['action']:null,
					(isset($infos['parametres']) AND $infos['parametres'])?$infos['parametres']:null
					))
				+ array_slice($boutons_admin[$parent]->sousmenu,$position,100);
			}
			if (!$parent
			// provisoire, eviter les vieux boutons
			AND (!in_array($id,array('forum','statistiques_visites')))

			) {
				$position = (isset($infos['position']) and $infos['position']) ? $infos['position'] : count($boutons_admin);
				$boutons_admin = array_slice($boutons_admin,0,$position)
				+array($id=> new Bouton(
					($icones AND isset($infos['icone']) AND $infos['icone'])?find_in_theme($infos['icone']):'',  // icone
					$infos['titre'],	// titre
					(isset($infos['action']) AND $infos['action'])?$infos['action']:null,
					(isset($infos['parametres']) AND $infos['parametres'])?$infos['parametres']:null
					))
				+ array_slice($boutons_admin,$position,100);
			}
		}
	}

	return pipeline('ajouter_menus', $boutons_admin);
}

/**
 * Creer l'url a partir de exec et args, sauf si c'est deja une url formatee
 *
 * @param string $url
 * @param string $args
 * @return string
 */
// http://doc.spip.org/@bandeau_creer_url
function bandeau_creer_url($url, $args="", $contexte=null){
	if (!preg_match(',[\/\?],',$url)) {
		$url = generer_url_ecrire($url,$args,true);
		// recuperer les parametres du contexte demande par l'url sous la forme
		// &truc=@machin@
		// @machin@ etant remplace par _request('machin')
		$url = str_replace('&amp;','&',$url);
		while (preg_match(",[&?]([a-z_]+)=@([a-z_]+)@,i",$url,$matches)){
			if ($matches[2]=='id_secteur' AND !isset($contexte['id_secteur']) AND isset($contexte['id_rubrique']))
				$contexte['id_secteur'] = sql_getfetsel('id_secteur','spip_rubriques','id_rubrique='.intval($contexte['id_rubrique']));
			$val = _request($matches[2],$contexte);
			$url = parametre_url($url,$matches[1],$val?$val:'','&');
		}
		$url = str_replace('&','&amp;',$url);
	}
	return $url;
}


/**
 * Construire tout le bandeau superieur de l'espace prive
 *
 * @param unknown_type $rubrique
 * @param unknown_type $sous_rubrique
 * @param unknown_type $largeur
 * @return unknown
 */
function inc_bandeau_dist() {
	return recuperer_fond('prive/squelettes/inclure/barre-nav',$_GET);
}

?>
presenter_enfants.php000066600000014424152525475610011027 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/


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

include_spip('inc/autoriser');
include_spip('inc/presentation');

/**
 * Crée l'affichage des listes de rubriques dans le privé
 * 
 * @param int $collection
 *  L'identifiant numérique de la rubrique à lister
 * @param int $debut
 * 	Le numéro de la pagination si paginé (> 500)
 * @param int $limite
 * 	Le pas de pagination si paginé (> 500)
 * @return array $res
 * 	Un tableau des sous rubriques
 */
function enfant_rub($collection,$debut=0,$limite=500){
	$voir_logo = (isset($GLOBALS['meta']['image_process']) AND $GLOBALS['meta']['image_process'] != "non");
	$logo = "";

	if ($voir_logo) {
		$chercher_logo = charger_fonction('chercher_logo', 'inc');
		include_spip('inc/filtres_images_mini');
	}

	$res = array();
	
	$result = sql_select("id_rubrique, id_parent, titre, descriptif, lang ", "spip_rubriques", "id_parent=$collection",'', '0+titre,titre',"$debut,$limite");
	while($row=sql_fetch($result)){
		$id_rubrique=$row['id_rubrique'];
		$id_parent=$row['id_parent'];
		$titre=generer_info_entite($id_rubrique,'rubrique','titre'); // pour etre sur de passer par tous les traitements
		if ('' !== ($rang = recuperer_numero($row['titre'])))
			$rang = "$rang. ";

		if (autoriser('voir','rubrique',$id_rubrique)){

			$les_sous_enfants = sous_enfant_rub($id_rubrique);

			changer_typo($row['lang']);
			$lang_dir = lang_dir($row['lang']);
			$descriptif=propre($row['descriptif']);

			if ($voir_logo) {
				if ($logo = $chercher_logo($id_rubrique, 'id_rubrique', 'on')) {
					list($fid, $dir, $nom, $format) = $logo;
					$logo = image_reduire("<img src='$fid' alt='' />", 48, 36);
					if ($logo)
						$logo =  inserer_attribut($logo,'class','logo');
				}
			}

			$lib_bouton = (!acces_restreint_rubrique($id_rubrique) ? "" :
			   http_img_pack('auteur-0minirezo-16.png', '', " width='16' height='16'", _T('image_administrer_rubrique'))) .
			  " <a dir='$lang_dir' href='" .
			  generer_url_entite($id_rubrique,'rubrique') .
			  "'>".
				$rang . $titre .
			  "</a>";

			$titre = (is_string($logo) ? $logo : '') .
				bouton_block_depliable($lib_bouton,$les_sous_enfants ?false:-1,"enfants$id_rubrique");

			$res[] =
			  debut_cadre_sous_rub(($id_parent ? "rubrique-24.png" : "secteur-24.png"), true, "", $titre) .
			  (!$descriptif ? '' : "\n<div class='descriptif'>$descriptif</div>") .
			  $les_sous_enfants .
			  fin_cadre_sous_rub(true);
		}
	}

	changer_typo($GLOBALS['spip_lang']); # remettre la typo de l'interface pour la suite
	return $res;
}

/**
 * Affiche les enfants d'une sous rubrique dans un bloc dépliable
 * (Utilisé dans les pages du privé)
 * 
 * @param int $collection2
 * 	L'identifiant numérique de la rubrique parente
 * @return string
 * 	Le contenu du bloc dépliable 
 */
function sous_enfant_rub($collection2){
	$nb = sql_countsel('spip_rubriques',"id_parent=$collection2");
	
	$retour = '';
	$pagination = '';
	$debut = 0;
	$limite = 500;
	
	/**
	 * On ne va afficher que 500 résultats max
	 * Si > 500 on affiche une pagination
	 */
	if($nb > $limite){
		$debut = _request('debut_rubrique'.$collection2) ? _request('debut_rubrique'.$collection2) : $debut;
		$pagination = chercher_filtre('pagination');
		$pagination = '<p class="pagination">'.$pagination($nb, '_rubrique'.$collection2, $debut, $limite, true, 'prive').'</p>';
		$limite = $debut + $limite;
	}
	
	$result =  sql_select("id_rubrique, id_parent, titre, lang", "spip_rubriques", "id_parent=$collection2",'', '0+titre,titre',"$debut,$limite");
	
	while($row=sql_fetch($result)){
		$id_rubrique2=$row['id_rubrique'];
		$id_parent2=$row['id_parent'];
		$titre2=generer_info_entite($id_rubrique2,'rubrique','titre'); // pour etre sur de passer par tous les traitements
		if ('' !== ($rang2 = recuperer_numero($row['titre'])))
			$rang2 = "$rang2. ";

		changer_typo($row['lang']);
		$lang_dir = lang_dir($row['lang']);
		if (autoriser('voir','rubrique',$id_rubrique2))
			$retour.="\n<li class='item' dir='$lang_dir'><a href='" . generer_url_entite($id_rubrique2,'rubrique') . "'>".$rang2.$titre2."</a></li>\n";
	}
	
	$retour = $pagination.$retour.$pagination;
	 
	if (!$retour) return '';
	
	return debut_block_depliable($debut > 0 ? true : false,"enfants$collection2")
	."\n<ul class='liste-items sous-sous-rub'>\n"
	. $retour
	. "</ul>\n".fin_block()."\n\n";
}

/**
 * Affiche la liste des rubriques enfants d'une rubrique
 * (Utilisé dans les pages du privé notamment ?exec=rubriques)
 * 
 * Si plus de 500 rubriques enfants, on pagine par 500 les résultats
 * 
 * @param int $id_rubrique
 * 	L'identifiant numérique de la rubrique parente (0 par défaut, la racine)
 * @return string $res
 * 	Le contenu textuel affiché, la liste des sous rubriques
 */
function afficher_enfant_rub($id_rubrique=0) {
	$pagination = '';
	$debut = 0;
	$limite = 500;

	$nb = sql_countsel('spip_rubriques',"id_parent=$id_rubrique");
	
	if($nb > $limite){
		$debut = _request('debut_rubrique'.$collection2) ? _request('debut_rubrique'.$collection2) : $debut;
		$pagination = chercher_filtre('pagination');
		$pagination = '<br class="nettoyeur"><p class="pagination">'.$pagination($nb, '_rubrique'.$collection2, $debut, $limite, true, 'prive').'</p>';
	}

	$les_enfants = enfant_rub($id_rubrique,$debut,$limite);

	if (!$n = count($les_enfants)) return "";

	if ($n==1) {
		$les_enfants=reset($les_enfants);
		$les_enfants2="";
	}
	else{
		$n = ceil($n/2);
		$les_enfants2 = implode('',array_slice($les_enfants,$n));
		$les_enfants = implode('',array_slice($les_enfants,0,$n));
	}
	
	$res =
	$pagination
	. "<div class='gauche'>"
	. $les_enfants
	. "</div>"
	. "<div class='droite'>"
	. $les_enfants2
	. "</div>"
	. $pagination;

	return $res;
}

?>
filtres_mime.php000066600000006132152525475610007756 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

if (!defined('_ECRIRE_INC_VERSION')) return;
include_spip('inc/filtres');

// Fichier des filtres d'incrustation d'un document selon son type MIME
// Les 7 familles de base ne font rien sauf celle des textes

function filtre_image_dist($t) {return '';}
function filtre_audio_dist($t) {return '';}
function filtre_video_dist($t) {return '';}
function filtre_application_dist($t) {return '';}
function filtre_message_dist($t) {return '';}
function filtre_multipart_dist($t) {return '';}

// http://doc.spip.org/@filtre_text_txt_dist
function filtre_text_dist($t) {
	static $t1 = array('&', '<', '>');
	static $t2 = array('&amp;', '&lt;', '&gt;');
	return '<pre>' . str_replace($t1, $t2, $t) . '</pre>';
}

// http://doc.spip.org/@filtre_text_csv_dist
function filtre_text_csv_dist($t) {
	include_spip('inc/csv');
	list($entete, $lignes) = analyse_csv($t);
	foreach ($lignes as &$l)
		$l = join('|', $l);
	$corps = join("\n", $lignes) . "\n";
	$corps = $caption .
		"\n|{{" .
		join('}}|{{',$entete) .
		"}}|" .
		"\n|" .
		str_replace("\n", "|\n|",$corps);
	$corps = str_replace('&#34#','&#34;',$corps);
	include_spip('inc/texte');
	return propre($corps);
}

// Incrustation de HTML, si on est capable de le securiser
// sinon, afficher le source
// http://doc.spip.org/@filtre_text_html_dist
function filtre_text_html_dist($t)
{
	if (!preg_match(',^(.*?)<body[^>]*>(.*)</body>,is', $t, $r))
		return appliquer_filtre($t,'text/plain');

	list(,$h,$t) = $r;

	$style = '';
	// recuperer les styles internes
	if (preg_match_all(',<style>(.*?)</style>,is', $h, $r, PREG_PATTERN_ORDER))
		$style =  join("\n",$r[1]);
	// ... et externes

	include_spip('inc/distant');
	if (preg_match_all(',<link[^>]+type=.text/css[^>]*>,is', $h, $r, PREG_PATTERN_ORDER))
		foreach($r[0] as $l) {
			preg_match("/href='([^']*)'/", str_replace('"',"'",$l), $m);
			$style .= "\n/* $l */\n"
			. str_replace('<','',recuperer_page($m[1]));
		}
	// Pourquoi SafeHtml transforme-t-il en texte les scripts dans Body ?
	$t = safehtml(preg_replace(',<script'.'.*?</script>,is','',$t));
	return (!$style ? '' : "\n<style>".$style."</style>") . $t;
}

// http://doc.spip.org/@filtre_audio_x_pn_realaudio
function filtre_audio_x_pn_realaudio($id)
{
  return "
	<param name='controls' value='PositionSlider' />
	<param name='controls' value='ImageWindow' />
	<param name='controls' value='PlayButton' />
	<param name='console' value='Console$id' />
	<param name='nojava' value='true' />";
}
?>
lister_objets.php000066600000003274152525475610010153 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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



/**
 * affichage des liste d'objets
 * surcharge pour aiguiller vers la mise en skel
 *
 * @param string $vue
 * @param string $titre
 * @param array $requete
 * @param string $formater
 * @param bool $force
 * @return string
 */
function inc_lister_objets_dist($vue, $contexte=array(), $force=false){
	$res = ""; // debug
	if (!is_array($contexte))
		return _L('$contexte doit etre un tableau dans inc/lister_objets');

	$fond = "prive/objets/liste/$vue";
	if (!find_in_path($fond."."._EXTENSION_SQUELETTES)) {
		// traiter les cas particuliers
		include_spip('base/connect_sql');
		$vue = table_objet($vue);
		$fond = "prive/objets/liste/$vue";
		if (!find_in_path($fond."."._EXTENSION_SQUELETTES))
			return _L("vue $vue introuvable pour lister les objets");
	}
		
	
	$contexte['sinon']=($force ? $contexte['titre']:'');

	$res = recuperer_fond($fond,$contexte,array('ajax'=>true));
	if (_request('var_liste'))
		echo var_export($contexte, true);
		
	return $res;
}

?>
notifications.php000066600000014636152525475610010160 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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


/**
 * La fonction de notification de base, qui dispatche le travail
 * http://doc.spip.org/@inc_notifications_dist
 *
 * @param string $quoi
 *   evenement de notification
 * @param int $id
 *   id de l'objet en relation avec l'evenement
 * @param array $options
 *   options de notification, interpretees en fonction de la notification
 */
function inc_notifications_dist($quoi, $id=0, $options=array()) {

	// charger les fichiers qui veulent ajouter des definitions
	// ou faire des trucs aussi dans le pipeline, ca fait deux api pour le prix d'une ...
	pipeline('notifications',array('args'=>array('quoi'=>$quoi,'id'=>$id,'options'=>$options)));

	if ($notification = charger_fonction($quoi,'notifications',true)) {
		spip_log("$notification($quoi,$id"
			.($options?",".serialize($options):"")
			.")",'notifications');
		$notification($quoi, $id, $options);
	}
}

/**
 * Nettoyage des emails avant un envoi
 * on passe par reference pour la perf
 *
 * les emails liste par $eclure seront exclus de la liste
 *
 * @param array $emails
 * @param array $exclure
 */
function notifications_nettoyer_emails(&$emails, $exclure = array()){
	// filtrer et unifier
	include_spip('inc/filtres');
	$emails = array_unique(array_filter(array_map('email_valide',array_map('trim', $emails))));
	if ($exclure AND count($exclure)){
		// nettoyer les exclusions d'abord
		notifications_nettoyer_emails($exclure);
		// faire un diff
		$emails = array_diff($emails,$exclure);
	}
}

/**
 * Envoyer un email de notification
 * Le sujet peut etre vide, dans ce cas il reprendra la premiere ligne non vide du texte
 *
 * @param array/string $emails
 * @param string $texte
 * @param string $sujet
 * @param string $from
 * @param string $headers
 */
function notifications_envoyer_mails($emails, $texte, $sujet="", $from = "", $headers = ""){
	// rien a faire si pas de texte !
	if (!strlen($texte))
		return;

	// si on ne specifie qu'un email, le mettre dans un tableau
	if (!is_array($emails))
		$emails = explode(',',$emails);

	notifications_nettoyer_emails($emails);

	// tester si le mail est deja en html
	if (strpos($texte,"<")!==false // eviter les tests suivants si possible
		AND $ttrim = trim($texte)
		AND substr($ttrim,0,1)=="<"
	  AND substr($ttrim,-1,1)==">"
	  AND stripos($ttrim,"</html>")!==false){

		if(!strlen($sujet)){
			// dans ce cas on ruse un peu : extraire le sujet du title
			if (preg_match(",<title>(.*)</title>,Uims",$texte,$m))
				$sujet = $m[1];
			else {
				// fallback, on prend le body si on le trouve
				if (preg_match(",<body[^>]*>(.*)</body>,Uims",$texte,$m))
					$ttrim = $m[1];

				// et on extrait la premiere ligne de vrai texte...
				// nettoyer le html et les retours chariots
				$ttrim = textebrut($ttrim);
				$ttrim = str_replace("\r\n", "\r", $ttrim);
				$ttrim = str_replace("\r", "\n", $ttrim);
				// decouper
				$ttrim = explode("\n",trim($ttrim));
				// extraire la premiere ligne de texte brut
				$sujet = array_shift($ttrim);
			}
		}

		// si besoin on ajoute le content-type dans les headers
		if (stripos($headers,"Content-Type")===false)
			$headers .= "Content-Type: text/html\n";
	}

	// si le sujet est vide, extraire la premiere ligne du corps
	// du mail qui est donc du texte
	if (!strlen($sujet)){
		// nettoyer un peu les retours chariots
		$texte = str_replace("\r\n", "\r", $texte);
		$texte = str_replace("\r", "\n", $texte);
		// decouper
		$texte = explode("\n",trim($texte));
		// extraire la premiere ligne
		$sujet = array_shift($texte);
		$texte = trim(implode("\n",$texte));
	}

	$envoyer_mail = charger_fonction('envoyer_mail','inc');
	foreach($emails as $email){
		// passer dans un pipeline qui permet un ajout eventuel
		// (url de suivi des notifications par exemple)
		$envoi = pipeline('notifications_envoyer_mails',array('email'=>$email,'sujet'=>$sujet,'texte'=>$texte));
		$email = $envoi['email'];

		job_queue_add('envoyer_mail', ">$email : ".$envoi['sujet'], array($email, $envoi['sujet'], $envoi['texte'], $from, $headers), 'inc/');
	}

}

/**
 * Notifier un evenement sur un objet
 * recupere le fond designe dans $modele,
 * prend la premiere ligne comme sujet
 * et l'interprete pour envoyer l'email
 *
 * @param int $id_objet
 * @param string $type_objet
 * @param string $modele
 * @return string
 */
function email_notification_objet($id_objet, $type_objet, $modele) {
	$envoyer_mail = charger_fonction('envoyer_mail','inc'); // pour nettoyer_titre_email
	$id_type = id_table_objet($type_objet);
	return recuperer_fond($modele,array($id_type=>$id_objet,"id"=>$id_objet));
}

/**
 * Notifier un evenement sur un article
 * recupere le fond designe dans $modele,
 * prend la premiere ligne comme sujet
 * et l'interprete pour envoyer l'email
 *
 * @param int $id_article
 * @param string $modele
 * @return string
 */
function email_notification_article($id_article, $modele) {
	$envoyer_mail = charger_fonction('envoyer_mail','inc'); // pour nettoyer_titre_email

	return recuperer_fond($modele,array('id_article'=>$id_article));
}

// Compatibilite, ne plus utiliser
// http://doc.spip.org/@notifier_publication_article
function notifier_publication_article($id_article) {
	if ($GLOBALS['meta']["suivi_edito"] == "oui") {
		$adresse_suivi = $GLOBALS['meta']["adresse_suivi"];
		$texte = email_notification_article($id_article, "notifications/article_publie");
		notifications_envoyer_mails($adresse_suivi, $texte);
	}
}

// Compatibilite, ne plus utiliser
// http://doc.spip.org/@notifier_proposition_article
function notifier_proposition_article($id_article) {
	if ($GLOBALS['meta']["suivi_edito"] == "oui") {
		$adresse_suivi = $GLOBALS['meta']["adresse_suivi"];
		$texte = email_notification_article($id_article, "notifications/article_propose");
		notifications_envoyer_mails($adresse_suivi, $texte);
	}
}

?>
flock.php000066600000036326152525475610006405 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/


if (!defined('_ECRIRE_INC_VERSION')) return;
// ajouter define('_CREER_DIR_PLAT', true); dans mes_options pour restaurer
// le fonctionnement des faux repertoires en .plat
define('_CREER_DIR_PLAT', false);
if (!defined('_TEST_FILE_EXISTS')) define('_TEST_FILE_EXISTS', preg_match(',(online|free)[.]fr$,', isset($_ENV["HTTP_HOST"]) ? $_ENV["HTTP_HOST"] : ""));

#define('_SPIP_LOCK_MODE',0); // ne pas utiliser de lock (deconseille)
#define('_SPIP_LOCK_MODE',1); // utiliser le flock php
#define('_SPIP_LOCK_MODE',2); // utiliser le nfslock de spip

if (_SPIP_LOCK_MODE==2)
	include_spip('inc/nfslock');

$GLOBALS['liste_verrous'] = array();
// http://doc.spip.org/@spip_fopen_lock
function spip_fopen_lock($fichier,$mode,$verrou){
	if (_SPIP_LOCK_MODE==1){
		if ($fl = @fopen($fichier,$mode))
			// verrou
			@flock($fl, $verrou);
		return $fl;
	}
	elseif(_SPIP_LOCK_MODE==2) {
		if (($verrou = spip_nfslock($fichier)) && ($fl = @fopen($fichier,$mode))){
			$GLOBALS['liste_verrous'][$fl] = array($fichier,$verrou);
			return $fl;
		}
		else return false;
	}
	return @fopen($fichier,$mode);
}
// http://doc.spip.org/@spip_fclose_unlock
function spip_fclose_unlock($handle){
	if (_SPIP_LOCK_MODE==1){
		@flock($handle, LOCK_UN);
	}
	elseif(_SPIP_LOCK_MODE==2) {
		spip_nfsunlock(reset($GLOBALS['liste_verrous'][$handle]),end($GLOBALS['liste_verrous'][$handle]));
		unset($GLOBALS['liste_verrous'][$handle]);
	}
	return @fclose($handle);
}


// http://doc.spip.org/@spip_file_get_contents
function spip_file_get_contents ($fichier) {
	if (substr($fichier, -3) != '.gz') {
		if (function_exists('file_get_contents')
		AND ( 
			// quand on est sous window on ne sait pas si file_get_contents marche
			// on essaye : si ca retourne du contenu alors c'est bon
			// sinon on fait un file() pour avoir le coeur net
		  ($contenu = @file_get_contents ($fichier))
		  OR _OS_SERVEUR != 'windows')
		)
			return $contenu;
		else
			$contenu = @file($fichier);
	} else
			$contenu = @gzfile($fichier);
	return is_array($contenu)?join('', $contenu):(string)$contenu;
}

// options = array(
// 'phpcheck' => 'oui' # verifier qu'on a bien du php
// dezippe automatiquement les fichiers .gz
// http://doc.spip.org/@lire_fichier
function lire_fichier ($fichier, &$contenu, $options=false) {
	$contenu = '';
	// inutile car si le fichier n'existe pas, le lock va renvoyer false juste apres
	// economisons donc les acces disque, sauf chez free qui rale pour un rien
	if (_TEST_FILE_EXISTS AND !@file_exists($fichier))
		return false;

	#spip_timer('lire_fichier');

	// pas de @ sur spip_fopen_lock qui est silencieux de toute facon
	if ($fl = spip_fopen_lock($fichier, 'r', LOCK_SH)) {
		// lire le fichier avant tout
		$contenu = spip_file_get_contents($fichier);

		// le fichier a-t-il ete supprime par le locker ?
		// on ne verifie que si la tentative de lecture a echoue
		// pour discriminer un contenu vide d'un fichier absent
		// et eviter un acces disque
		if (!$contenu AND !@file_exists($fichier)) {
			spip_fclose_unlock($fl);
			return false;
		}

		// liberer le verrou
		spip_fclose_unlock($fl);

		// Verifications
		$ok = true;
		if ($options['phpcheck'] == 'oui')
			$ok &= (preg_match(",[?]>\n?$,", $contenu));

		#spip_log("$fread $fichier ".spip_timer('lire_fichier'));
		if (!$ok)
			spip_log("echec lecture $fichier");

		return $ok;
	}
	return false;
}

//
// Ecrire un fichier de maniere un peu sure
//
// zippe les fichiers .gz
// http://doc.spip.org/@ecrire_fichier
function ecrire_fichier ($fichier, $contenu, $ignorer_echec = false, $truncate=true) {

	#spip_timer('ecrire_fichier');

	// verrouiller le fichier destination
	if ($fp = spip_fopen_lock($fichier, 'a',LOCK_EX)) {
	// ecrire les donnees, compressees le cas echeant
	// (on ouvre un nouveau pointeur sur le fichier, ce qui a l'avantage
	// de le recreer si le locker qui nous precede l'avait supprime...)
		if (substr($fichier, -3) == '.gz')
			$contenu = gzencode($contenu);
		// si c'est une ecriture avec troncation , on fait plutot une ecriture complete a cote suivie unlink+rename
		// pour etre sur d'avoir une operation atomique
		// y compris en NFS : http://www.ietf.org/rfc/rfc1094.txt
		// sauf sous wintruc ou ca ne marche pas
		$ok = false;
		if ($truncate AND _OS_SERVEUR != 'windows'){
			if (!function_exists('creer_uniqid'))
				include_spip('inc/acces');
			$id = creer_uniqid();
			// on ouvre un pointeur sur un fichier temporaire en ecriture +raz
			if ($fp2 = spip_fopen_lock("$fichier.$id", 'w',LOCK_EX)) {
				$s = @fputs($fp2, $contenu, $a = strlen($contenu));
				$ok = ($s == $a);
				spip_fclose_unlock($fp2);
				spip_fclose_unlock($fp);
				// unlink direct et pas spip_unlink car on avait deja le verrou
				// a priori pas besoin car rename ecrase la cible
				// @unlink($fichier);
				// le rename aussitot, atomique quand on est pas sous windows
				// au pire on arrive en second en cas de concourance, et le rename echoue
				// --> on a la version de l'autre process qui doit etre identique
				@rename("$fichier.$id",$fichier);
				// precaution en cas d'echec du rename
				if (!_TEST_FILE_EXISTS OR @file_exists("$fichier.$id"))
					@unlink("$fichier.$id");
				if ($ok)
					$ok = file_exists($fichier);
			}
			else
				// echec mais penser a fermer ..
				spip_fclose_unlock($fp);
		}
		// sinon ou si methode precedente a echoueee
		// on se rabat sur la methode ancienne
		if (!$ok){
			// ici on est en ajout ou sous windows, cas desespere
			if ($truncate)
				@ftruncate($fp,0);
			$s = @fputs($fp, $contenu, $a = strlen($contenu));

			$ok = ($s == $a);
			spip_fclose_unlock($fp);
		}

		// liberer le verrou et fermer le fichier
		@chmod($fichier, _SPIP_CHMOD & 0666);
		if ($ok) {
			if (strpos($fichier,".php")!==false){
				spip_clear_opcode_cache(realpath($fichier));
			}
			return $ok;
		}
	}

	if (!$ignorer_echec){
		include_spip('inc/autoriser');
		if (autoriser('chargerftp'))
			raler_fichier($fichier);
		spip_unlink($fichier);
	}
	spip_log("Ecriture fichier $fichier impossible",_LOG_INFO_IMPORTANTE);
	return false;
}

/**
 * Ecrire un contenu dans un fichier encapsule en php pour en empecher l'acces en l'absence
 * de htaccess
 * @param string $fichier
 * @param <type> $contenu
 * @param <type> $ecrire_quand_meme
 * @param <type> $truncate 
 */
function ecrire_fichier_securise ($fichier, $contenu, $ecrire_quand_meme = false, $truncate=true) {
	if (substr($fichier,-4) !== '.php')
		spip_log('Erreur de programmation: '.$fichier.' doit finir par .php');
	$contenu = "<"."?php die ('Acces interdit'); ?".">\n" . $contenu;
	return ecrire_fichier($fichier, $contenu, $ecrire_quand_meme, $truncate);
}

/**
 * Lire un fichier encapsule en php
 * @param <type> $fichier
 * @param <type> $contenu
 * @param <type> $options 
 */
function lire_fichier_securise ($fichier, &$contenu, $options=false) {
	if ($res = lire_fichier($fichier,$contenu,$options)){
		$contenu = substr($contenu,strlen("<"."?php die ('Acces interdit'); ?".">\n"));
	}
	return $res;
}

// http://doc.spip.org/@raler_fichier
function raler_fichier($fichier)
{
	include_spip('inc/minipres');
	$dir = dirname($fichier);
	http_status(401);
	echo minipres(_T('texte_inc_meta_2'), "<h4 style='color: red'>"
		. _T('texte_inc_meta_1', array('fichier' => $fichier))
		. " <a href='"
		. generer_url_ecrire('install', "etape=chmod&test_dir=$dir")
		. "'>"
		. _T('texte_inc_meta_2')
		. "</a> "
		. _T('texte_inc_meta_3',
		     array('repertoire' => joli_repertoire($dir)))
		. "</h4>\n");
	exit;
}

//
// Retourne Vrai si son premier argument a ete cree il y a moins de N secondes
//

// http://doc.spip.org/@jeune_fichier
function jeune_fichier($fichier, $n)
{
	if (!file_exists($fichier)) return false;
	if (!$c = @filemtime($fichier)) return false;
	return (time()-$n <= $c);
}

//
// Supprimer le fichier de maniere sympa (flock)
//
// http://doc.spip.org/@supprimer_fichier
function supprimer_fichier($fichier, $lock=true) {
	if (!@file_exists($fichier))
		return true;

	if ($lock) {
		// verrouiller le fichier destination
		if (!$fp = spip_fopen_lock($fichier, 'a', LOCK_EX))
			return false;
	
		// liberer le verrou
		spip_fclose_unlock($fp);
	}
	
	// supprimer
	return @unlink($fichier);
}

// Supprimer brutalement, si le fichier existe
// http://doc.spip.org/@spip_unlink
function spip_unlink($f) {
	if (!is_dir($f))
		supprimer_fichier($f,false);
	else {
		@unlink("$f/.ok");
		@rmdir($f);
	}
}

/**
 * clearstatcache adapte a la version PHP
 * @param bool $clear_realpath_cache
 * @param null $filename
 */
function spip_clearstatcache($clear_realpath_cache = false, $filename=null){
	if (!defined('PHP_VERSION_ID') || PHP_VERSION_ID < 50300) {
   // Below PHP 5.3, clearstatcache does not accept any function parameters.
   return clearstatcache();
 }
 else {
	 return clearstatcache($clear_realpath_cache, $filename);
 }

}


/**
 * Invalidates a PHP file from any active opcode caches.
 *
 * If the opcode cache does not support the invalidation of individual files,
 * the entire cache will be flushed.
 * kudo : http://cgit.drupalcode.org/drupal/commit/?id=be97f50
 *
 * @param string $filepath
 *   The absolute path of the PHP file to invalidate.
 */
function spip_clear_opcode_cache($filepath) {
	spip_clearstatcache(TRUE, $filepath);

  // Zend OPcache
  if (function_exists('opcache_invalidate')) {
    opcache_invalidate($filepath, TRUE);
  }
  // APC.
  if (function_exists('apc_delete_file')) {
    // apc_delete_file() throws a PHP warning in case the specified file was
    // not compiled yet.
    // @see http://php.net/apc-delete-file
    @apc_delete_file($filepath);
  }
}

/**
 * si opcache est actif et en mode validate_timestamps
 * le timestamp ne sera checke qu'apres revalidate_freq s
 * il faut donc attendre ce temps la pour etre sur qu'on va bien beneficier de la recompilation
 * NB c'est une config foireuse deconseillee de opcode cache mais malheureusement utilisee par Octave
 * cf http://stackoverflow.com/questions/25649416/when-exactly-does-php-5-5-opcache-check-file-timestamp-based-on-revalidate-freq
 * et http://wiki.mikejung.biz/PHP_OPcache
 *
 * Ne fait rien en dehors de ce cas
 *
 */
function spip_attend_invalidation_opcode_cache(){
	if (function_exists('opcache_get_configuration')
	  AND @ini_get('opcache.enable')
	  AND @ini_get('opcache.validate_timestamps')
	  AND $duree = @ini_get('opcache.revalidate_freq') ) {
		sleep($duree+1);
	}
}

/**
 * Suppression complete d'un repertoire.
 *
 * http://www.php.net/manual/en/function.rmdir.php#92050
 *
 * @param string $dir Chemin du repertoire
 * @return bool Suppression reussie.
 */
function supprimer_repertoire($dir) {
	if (!file_exists($dir)) return true;
	if (!is_dir($dir) || is_link($dir)) return @unlink($dir);
	
	foreach (scandir($dir) as $item) {
		if ($item == '.' || $item == '..') continue;
		if (!supprimer_repertoire($dir . "/" . $item)) {
			@chmod($dir . "/" . $item, 0777);
			if (!supprimer_repertoire($dir . "/" . $item)) return false;
		};
	}
	
	return @rmdir($dir);
}

	
//
// Retourne $base/${subdir}/ si le sous-repertoire peut etre cree,
// $base/${subdir}_ sinon ; $nobase signale qu'on ne veut pas de $base/
// On peut aussi ne donner qu'un seul argument,
// subdir valant alors ce qui suit le dernier / dans $base
//
// http://doc.spip.org/@sous_repertoire
function sous_repertoire($base, $subdir='', $nobase = false, $tantpis=false) {
	static $dirs = array();

	$base = str_replace("//", "/", $base);

	# suppr le dernier caractere si c'est un / ou un _
	$base = rtrim($base, '/_');

	if (!strlen($subdir)) {
		$n = strrpos($base, "/");
		if ($n === false) return $nobase ? '' : ($base .'/');
		$subdir = substr($base, $n+1);
		$base = substr($base, 0, $n+1);
	} else {
		$base .= '/';
		$subdir = str_replace("/", "", $subdir);
	}

	$baseaff = $nobase ? '' : $base;
	if (isset($dirs[$base.$subdir]))
		return $baseaff.$dirs[$base.$subdir];


	if (_CREER_DIR_PLAT AND @file_exists("$base${subdir}.plat"))
		return $baseaff.($dirs[$base.$subdir] = "${subdir}_");

	$path = $base.$subdir; # $path = 'IMG/distant/pdf' ou 'IMG/distant_pdf'

	if (file_exists("$path/.ok"))
		return $baseaff.($dirs[$base.$subdir] = "$subdir/");

	@mkdir($path, _SPIP_CHMOD);
	@chmod($path, _SPIP_CHMOD);

	if (is_dir($path) && is_writable($path)) {
		@touch ("$path/.ok");
		spip_log("creation $base$subdir/");
		return $baseaff.($dirs[$base.$subdir] = "$subdir/");
	}

	// en cas d'echec c'est peut etre tout simplement que le disque est plein :
	// l'inode du fichier dir_test existe, mais impossible d'y mettre du contenu
	// => sauf besoin express (define dans mes_options), ne pas creer le .plat
	if (_CREER_DIR_PLAT
	AND $f = @fopen("$base${subdir}.plat", "w"))
		fclose($f);
	else {
		spip_log("echec creation $base${subdir}");
		if ($tantpis) return '';
		if (!_DIR_RESTREINT)
			$base = preg_replace(',^' . _DIR_RACINE .',', '',$base);
		$base .= $subdir;
		raler_fichier($base . '/.plat');
	}
	spip_log("faux sous-repertoire $base${subdir}");
	return $baseaff.($dirs[$base.$subdir] = "${subdir}_");
}

//
// Cette fonction parcourt recursivement le repertoire $dir, et renvoie les
// fichiers dont le chemin verifie le pattern (preg) donne en argument.
// En cas d'echec retourne un array() vide
//
// Usage: array preg_files('ecrire/data/', '[.]lock$');
//
// Attention, afin de conserver la compatibilite avec les repertoires '.plat'
// si $dir = 'rep/sous_rep_' au lieu de 'rep/sous_rep/' on scanne 'rep/' et on
// applique un pattern '^rep/sous_rep_'
// si $recurs vaut false, la fonction ne descend pas dans les sus repertoires
//
// http://doc.spip.org/@preg_files
function preg_files($dir, $pattern=-1 /* AUTO */, $maxfiles = 10000, $recurs=array()) {
	$nbfiles = 0;
	if ($pattern == -1)
		$pattern = "^$dir";
	$fichiers = array();
	// revenir au repertoire racine si on a recu dossier/truc
	// pour regarder dossier/truc/ ne pas oublier le / final
	$dir = preg_replace(',/[^/]*$,', '', $dir);
	if ($dir == '') $dir = '.';

	if (@is_dir($dir) AND is_readable($dir) AND $d = @opendir($dir)) {
		while (($f = readdir($d)) !== false && ($nbfiles<$maxfiles)) {
			if ($f[0] != '.' # ignorer . .. .svn etc
			AND $f != 'CVS'
			AND $f != 'remove.txt'
			AND is_readable($f = "$dir/$f")) {
				if (is_file($f)) {
					if (preg_match(";$pattern;iS", $f))
					{
						$fichiers[] = $f;
						$nbfiles++;
					}
				} 
				else if (is_dir($f) AND is_array($recurs)){
					$rp = @realpath($f);
					if (!is_string($rp) OR !strlen($rp)) $rp=$f; # realpath n'est peut etre pas autorise
					if (!isset($recurs[$rp])) {
						$recurs[$rp] = true;
						$beginning = $fichiers;
						$end = preg_files("$f/", $pattern,
							$maxfiles-$nbfiles, $recurs);
						$fichiers = array_merge((array)$beginning, (array)$end);
						$nbfiles = count($fichiers);
					}
				}
			}
		}
		closedir($d);
	}
	sort($fichiers);
	return $fichiers;
}

?>
queue.php000066600000047044152525475610006432 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

/**
 * Gestion des queues de travaux
 *
 * @package SPIP\Queue
**/
if (!defined("_ECRIRE_INC_VERSION")) return;

define('_JQ_SCHEDULED',1);
define('_JQ_PENDING',0);
#define('_JQ_MAX_JOBS_EXECUTE',200); // pour personaliser le nombre de jobs traitables a chaque hit
#define('_JQ_MAX_JOBS_TIME_TO_EXECUTE',15); // pour personaliser le temps d'excution dispo a chaque hit
#define('_JQ_NB_JOBS_OVERFLOW',10000); // nombre de jobs a partir duquel on force le traitement en fin de hit pour purger

/**
 * Ajouter une tâche à la file
 * 
 * Les tâches sont ensuites exécutées par date programmée croissant/priorité décroissante
 *
 * @param $function
 *   The function name to call.
 * @param $description
 *   A human-readable description of the queued job.
 * @param $arguments
 *   Optional array of arguments to pass to the function.
 * @param $file
 *   Optional file path which needs to be included for $fucntion.
 * @param $no_duplicate
 *   If TRUE, do not add the job to the queue if one with the same function and
 *   arguments already exists.
 *	 If 'function_only' test of existence is only on function name (for cron job)
 * @param $time
 *		time for starting the job. If 0, job will start as soon as possible
 * @param $priority
 *		-10 (low priority) to +10 (high priority), 0 is the default
 * @return int
 *	id of job
 */
function queue_add_job($function, $description, $arguments = array(), $file = '', $no_duplicate = false, $time=0, $priority=0){
	include_spip('base/abstract_sql');

	// cas pourri de ecrire/action/editer_site avec l'option reload=oui
	if (defined('_GENIE_SYNDIC_NOW'))
		$arguments['id_syndic'] = _GENIE_SYNDIC_NOW;

	// serialiser les arguments
	$arguments = serialize($arguments);
	$md5args = md5($arguments);

	// si pas de date programee, des que possible
	$duplicate_where = 'status='.intval(_JQ_SCHEDULED).' AND ';
	if (!$time){
		$time = time();
		$duplicate_where = ""; // ne pas dupliquer si deja le meme job en cours d'execution
	}
	$date = date('Y-m-d H:i:s',$time);

	$set_job = array(
		'fonction'=>$function,
		'descriptif'=>$description,
		'args'=>$arguments,
		'md5args'=>$md5args,
		'inclure'=>$file,
		'priorite'=>max(-10,min(10,intval($priority))),
		'date'=>$date,
		'status'=>_JQ_SCHEDULED,
	);
	// si option ne pas dupliquer, regarder si la fonction existe deja
	// avec les memes args et file
	if (
			$no_duplicate
		AND
			$id_job = sql_getfetsel('id_job','spip_jobs',
				$duplicate_where =
					$duplicate_where . 'fonction='.sql_quote($function)
				.(($no_duplicate==='function_only')?'':
				 ' AND md5args='.sql_quote($md5args).' AND inclure='.sql_quote($file)))
		)
		return $id_job;

	$id_job = sql_insertq('spip_jobs',$set_job);
	// en cas de concurrence, deux process peuvent arriver jusqu'ici en parallele
	// avec le meme job unique a inserer. Dans ce cas, celui qui a eu l'id le plus grand
	// doit s'effacer
	if (
			$no_duplicate
		AND
			$id_prev = sql_getfetsel('id_job','spip_jobs',"id_job<".intval($id_job)." AND $duplicate_where")){
		sql_delete('spip_jobs','id_job='.intval($id_job));
		return $id_prev;
	}

	// verifier la non duplication qui peut etre problematique en cas de concurence
	// il faut dans ce cas que seul le dernier ajoute se supprime !

	// une option de debug pour verifier que les arguments en base sont bons
	// ie cas d'un char non acceptables sur certains type de champs
	// qui coupe la valeur
	if (defined('_JQ_INSERT_CHECK_ARGS') AND $id_job) {
		$args = sql_getfetsel('args', 'spip_jobs', 'id_job='.intval($id_job));
		if ($args!==$arguments) {
			spip_log('arguments job errones / longueur '.strlen($args)." vs ".strlen($arguments).' / valeur : '.var_export($arguments,true),'queue');
		}
	}

	if ($id_job){
		queue_update_next_job_time($time);
	}
	// si la mise en file d'attente du job echoue,
	// il ne faut pas perdre l'execution de la fonction
	// on la lance immediatement, c'est un fallback
	// sauf en cas d'upgrade necessaire (table spip_jobs inexistante)
	elseif($GLOBALS['meta']['version_installee']==$GLOBALS['spip_version_base']) {
		$set_job['id_job'] = 0;
		queue_start_job($set_job);
	}

	return $id_job;
}

/**
 * Purger la file de tâche et reprgrammer les tâches périodiques
 * 
 * @return void
 */
function queue_purger(){
	include_spip('base/abstract_sql');
	sql_delete('spip_jobs');
  sql_delete("spip_jobs_liens","id_job NOT IN (".sql_get_select("id_job","spip_jobs").")");
  include_spip('inc/genie');
  genie_queue_watch_dist();
}

/**
 * Retirer une tache de la file d'attente
 * @param int $id_job
 *  id de la tache a retirer
 * @return bool
 */
function queue_remove_job($id_job){
	include_spip('base/abstract_sql');

	if ($row = sql_fetsel('fonction,inclure,date','spip_jobs','id_job='.intval($id_job))
	 AND $res = sql_delete('spip_jobs','id_job='.intval($id_job))){
		queue_unlink_job($id_job);
		// est-ce une tache cron qu'il faut relancer ?
		if ($periode = queue_is_cron_job($row['fonction'],$row['inclure'])){
			// relancer avec les nouveaux arguments de temps
			include_spip('inc/genie');
			// relancer avec la periode prevue
			queue_genie_replan_job($row['fonction'],$periode,strtotime($row['date']));
		}
		queue_update_next_job_time();
	}
	return $res;
}

/**
 * Associer une tache avec un objet
 *
 * @param int $id_job
 *	id of job to link
 * @param array $objets
 *  can be a simple array('objet'=>'article','id_objet'=>23)
 *  or an array of simple array to link multiples objet in one time
 */
function queue_link_job($id_job,$objets){
	include_spip('base/abstract_sql');

	if (is_array($objets) AND count($objets)){
		if (is_array(reset($objets))){
			foreach($objets as $k=>$o){
				$objets[$k]['id_job'] = $id_job;
			}
			sql_insertq_multi('spip_jobs_liens',$objets);
		}
		else
			sql_insertq('spip_jobs_liens',array_merge(array('id_job'=>$id_job),$objets));
	}
}

/**
 * Dissocier une tache d'un objet
 *
 * @param int $id_job
 *	id of job to unlink ibject with
 * @return int/bool
 *	result of sql_delete
 */
function queue_unlink_job($id_job){
	return sql_delete("spip_jobs_liens","id_job=".intval($id_job));
}

/**
 * Lancer une tache decrite par sa ligne SQL
 * @param array $row
 *	describe the job, with field of table spip_jobs
 * @return mixed
 *	return the result of job
 */
function queue_start_job($row){

	// deserialiser les arguments
	$args = unserialize($row['args']);
	if ($args===false){
		spip_log('arguments job errones '.var_export($row,true),'queue');
		$args = array();
	}

	$fonction = $row['fonction'];
	if (strlen($inclure = trim($row['inclure']))){
		if (substr($inclure,-1)=='/'){ // c'est un chemin pour charger_fonction
			$f = charger_fonction($fonction,rtrim($inclure,'/'),false);
			if ($f)
				$fonction = $f;
		}
		else
			include_spip($inclure);
	}

	if (!function_exists($fonction)){
		spip_log("fonction $fonction ($inclure) inexistante ".var_export($row,true),'queue');
		return false;
	}

	spip_log("queue [".$row['id_job']."]: $fonction() start", 'queue');
	switch (count($args)) {
		case 0:	$res = $fonction(); break;
		case 1:	$res = $fonction($args[0]); break;
		case 2:	$res = $fonction($args[0],$args[1]); break;
		case 3:	$res = $fonction($args[0],$args[1], $args[2]); break;
		case 4:	$res = $fonction($args[0],$args[1], $args[2], $args[3]); break;
		case 5:	$res = $fonction($args[0],$args[1], $args[2], $args[3], $args[4]); break;
		case 6:	$res = $fonction($args[0],$args[1], $args[2], $args[3], $args[4], $args[5]); break;
		case 7:	$res = $fonction($args[0],$args[1], $args[2], $args[3], $args[4], $args[5], $args[6]); break;
		case 8:	$res = $fonction($args[0],$args[1], $args[2], $args[3], $args[4], $args[5], $args[6], $args[7]); break;
		case 9:	$res = $fonction($args[0],$args[1], $args[2], $args[3], $args[4], $args[5], $args[6], $args[7], $args[8]); break;
		case 10:$res = $fonction($args[0],$args[1], $args[2], $args[3], $args[4], $args[5], $args[6], $args[7], $args[8], $args[9]); break;
		default:
			# plus lent mais completement generique
			$res = call_user_func_array($fonction, $args);
	}
	spip_log("queue [".$row['id_job']."]: $fonction() end", 'queue');
	return $res;

}

/**
 * Scheduler :
 * Prend une par une les taches en attente
 * et les lance, dans la limite d'un temps disponible total
 * et d'un nombre maxi de taches
 *
 * La date de la prochaine tache a executer est mise a jour
 * apres chaque chaque tache finie
 * afin de relancer le scheduler uniquement quand c'est necessaire
 *
 * @param array $force_jobs
 *   list of id_job to execute when provided
 * @return null|false
 */
function queue_schedule($force_jobs = null){
	$time = time();
	if (defined('_DEBUG_BLOCK_QUEUE')) {
		spip_log("_DEBUG_BLOCK_QUEUE : schedule stop",'jq'._LOG_DEBUG);
		return;
	}

	// rien a faire si le prochain job est encore dans le futur
	if (queue_sleep_time_to_next_job()>0 AND (!$force_jobs OR !count($force_jobs))){
		spip_log("queue_sleep_time_to_next_job",'jq'._LOG_DEBUG);
		return;
	}

	include_spip('base/abstract_sql');
	// on ne peut rien faire si pas de connexion SQL
	if (!spip_connect())  return false;

	if (!defined('_JQ_MAX_JOBS_TIME_TO_EXECUTE')){
		$max_time = ini_get('max_execution_time')/2;
		// valeur conservatrice si on a pas reussi a lire le max_execution_time
		if (!$max_time) $max_time=5;
		define('_JQ_MAX_JOBS_TIME_TO_EXECUTE',min($max_time,15)); // une valeur maxi en temps.
	}
	$end_time = $time + _JQ_MAX_JOBS_TIME_TO_EXECUTE;

	spip_log("JQ schedule $time / $end_time",'jq'._LOG_DEBUG);

	if (!defined('_JQ_MAX_JOBS_EXECUTE'))
		define('_JQ_MAX_JOBS_EXECUTE',200);
	$nbj=0;
	// attraper les jobs
	// dont la date est passee (echus en attente),
	// par odre :
	//	- de priorite
	//	- de date
	// lorsqu'un job cron n'a pas fini, sa priorite est descendue
	// pour qu'il ne bloque pas les autres jobs en attente
	if (is_array($force_jobs) AND count($force_jobs))
		$cond = "status=".intval(_JQ_SCHEDULED)." AND ".sql_in("id_job", $force_jobs);
	else {
		$now = date('Y-m-d H:i:s',$time);
		$cond = "status=".intval(_JQ_SCHEDULED)." AND date<=".sql_quote($now);
	}

	register_shutdown_function('queue_error_handler'); // recuperer les erreurs auant que possible
	$res = sql_allfetsel('*','spip_jobs',$cond,'','priorite DESC,date','0,'.(_JQ_MAX_JOBS_EXECUTE+1));
	do {
		if ($row = array_shift($res)){
			$nbj++;
			// il faut un verrou, a base de sql_delete
			if (sql_delete('spip_jobs',"id_job=".intval($row['id_job'])." AND status=".intval(_JQ_SCHEDULED))){
				#spip_log("JQ schedule job ".$nbj." OK",'jq');
				// on reinsert dans la base aussitot avec un status=_JQ_PENDING
				$row['status'] = _JQ_PENDING;
				$row['date'] = date('Y-m-d H:i:s',$time);
				sql_insertq('spip_jobs', $row);

				// on a la main sur le job :
				// l'executer
				$result = queue_start_job($row);

				$time = time();
				queue_close_job($row, $time, $result);
			}
		}
		spip_log("JQ schedule job end time ".$time,'jq'._LOG_DEBUG);
	} while ($nbj<_JQ_MAX_JOBS_EXECUTE AND $row AND $time<$end_time);
	spip_log("JQ schedule end time ".time(),'jq'._LOG_DEBUG);

	if ($row = array_shift($res)){
		queue_update_next_job_time(0); // on sait qu'il y a encore des jobs a lancer ASAP
		spip_log("JQ encore !",'jq'._LOG_DEBUG);
	}
	else
		queue_update_next_job_time();

	return true;
}

/**
 * Terminer un job au status _JQ_PENDING :
 *  - le reprogrammer si c'est un cron
 *  - supprimer ses liens
 *  - le detruire en dernier
 *
 * @param array $row
 * @param int $time
 * @param int $result
 */
function queue_close_job(&$row,$time,$result=0){
	// est-ce une tache cron qu'il faut relancer ?
	if ($periode = queue_is_cron_job($row['fonction'],$row['inclure'])){
		// relancer avec les nouveaux arguments de temps
		include_spip('inc/genie');
		if ($result<0)
			// relancer tout de suite, mais en baissant la priorite
			queue_genie_replan_job($row['fonction'],$periode,0-$result,null,$row['priorite']-1);
		else
			// relancer avec la periode prevue
			queue_genie_replan_job($row['fonction'],$periode,$time);
	}
	// purger ses liens eventuels avec des objets
	sql_delete("spip_jobs_liens","id_job=".intval($row['id_job']));
	// supprimer le job fini
	sql_delete('spip_jobs','id_job='.intval($row['id_job']));
}

/**
 * Recuperer des erreurs auant que possible
 * en terminant la gestion de la queue
 */
function queue_error_handler(){
	// se remettre dans le bon dossier, car Apache le change parfois (toujours?)
	chdir(_ROOT_CWD);

	queue_update_next_job_time();
}


/**
 * Tester si une tache etait une tache periodique a reprogrammer
 *
 * @param <type> $function
 * @param <type> $inclure
 * @return <type>
 */
function queue_is_cron_job($function,$inclure){
	static $taches = null;
	if (strncmp($inclure,'genie/',6)==0){
		if (is_null($taches)){
			include_spip('inc/genie');
			$taches = taches_generales();
		}
		if (isset($taches[$function]))
			return $taches[$function];
	}
	return false;
}

/**
 * Mettre a jour la date du prochain job a lancer
 * Si une date est fournie (au format time unix)
 * on fait simplement un min entre la date deja connue et celle fournie
 * (cas de l'ajout simple
 * ou cas $next_time=0 car l'on sait qu'il faut revenir ASAP)
 *
 * @param int $next_time
 *	temps de la tache ajoutee ou 0 pour ASAP
 */
function queue_update_next_job_time($next_time=null){
	static $nb_jobs_scheduled = null;
	static $deja_la = false;
	// prendre le min des $next_time que l'on voit passer ici, en cas de reentrance
	static $next = null;
	// queue_close_job peut etre reentrant ici
	if ($deja_la) return;
	$deja_la = true;

	include_spip('base/abstract_sql');
	$time = time();

	// traiter les jobs morts au combat (_JQ_PENDING depuis plus de 180s)
	// pour cause de timeout ou autre erreur fatale
	$res = sql_allfetsel("*","spip_jobs","status=".intval(_JQ_PENDING)." AND date<".sql_quote(date('Y-m-d H:i:s',$time-180)));
	if (is_array($res)) {
		foreach ($res as $row)
			queue_close_job($row,$time);
	}

	// chercher la date du prochain job si pas connu
	if (is_null($next) OR is_null(queue_sleep_time_to_next_job())){
		$date = sql_getfetsel('date','spip_jobs',"status=".intval(_JQ_SCHEDULED),'','date','0,1');
		$next = strtotime($date);
	}
	if (!is_null($next_time)){
		if (is_null($next) OR $next>$next_time)
			$next = $next_time;
	}

		if ($next){
			if (is_null($nb_jobs_scheduled))
				$nb_jobs_scheduled = sql_countsel('spip_jobs',"status=".intval(_JQ_SCHEDULED)." AND date<".sql_quote(date('Y-m-d H:i:s',$time)));
			elseif ($next<=$time)
				$nb_jobs_scheduled++;
			// si trop de jobs en attente, on force la purge en fin de hit
			// pour assurer le coup
			if ($nb_jobs_scheduled>defined('_JQ_NB_JOBS_OVERFLOW')?_JQ_NB_JOBS_OVERFLOW:10000)
				define('_DIRECT_CRON_FORCE',true);
		}

	queue_set_next_job_time($next);
	$deja_la = false;
}


/**
 * Mettre a jour la date de prochain job
 * @param int $next
 */
function queue_set_next_job_time($next) {

	// utiliser le temps courant reel plutot que temps de la requete ici
	$time = time();

	// toujours relire la valeur pour comparer, pour tenir compte des maj concourrantes
	// et ne mettre a jour que si il y a un interet a le faire
	// permet ausis d'initialiser le nom de fichier a coup sur
	$curr_next = $_SERVER['REQUEST_TIME'] + max(0,queue_sleep_time_to_next_job(true));
	if (
			($curr_next<=$time AND $next>$time) // le prochain job est dans le futur mais pas la date planifiee actuelle
			OR $curr_next>$next // le prochain job est plus tot que la date planifiee actuelle
		) {
		if (include_spip('inc/memoization') AND defined('_MEMOIZE_MEMORY') AND _MEMOIZE_MEMORY) {
			cache_set(_JQ_NEXT_JOB_TIME_FILENAME,intval($next));
		}
		else {
			ecrire_fichier(_JQ_NEXT_JOB_TIME_FILENAME,intval($next));
		}
		queue_sleep_time_to_next_job($next);
	}

	return queue_sleep_time_to_next_job();
}

/**
 * Déclenche le cron en asynchrone ou retourne le code HTML pour le déclencher
 * 
 * Retourne le HTML à ajouter à la page pour declencher le cron
 * ou rien si on a réussi à le lancer en asynchrone.
 * 
 * @return string
 */
function queue_affichage_cron(){
	$texte = "";

	$time_to_next = queue_sleep_time_to_next_job();
	// rien a faire si le prochain job est encore dans le futur
	if ($time_to_next>0 OR defined('_DEBUG_BLOCK_QUEUE'))
		return $texte;

	// ne pas relancer si on vient de lancer dans la meme seconde par un hit concurent
	if (file_exists($lock=_DIR_TMP."cron.lock") AND !(@filemtime($lock)<$_SERVER['REQUEST_TIME']))
		return $texte;
	@touch($lock);

	// il y a des taches en attentes
	// si depuis plus de 5min, on essaye de lancer le cron par tous les moyens pour rattraper le coup
	// on est sans doute sur un site qui n'autorise pas http sortant ou avec peu de trafic
	$urgent = false;
	if ($time_to_next<-300)
		$urgent = true;

	$url_cron = generer_url_action('cron','',false,true);

	if (!defined('_HTML_BG_CRON_FORCE') OR !_HTML_BG_CRON_FORCE){

		// methode la plus rapide :
		// Si fsockopen est possible, on lance le cron via un socket en asynchrone
		// si fsockopen echoue (disponibilite serveur, firewall) on essaye pas cURL
		// car on a toutes les chances d'echouer pareil mais sans moyen de le savoir
		// on passe direct a la methode background-image
		if(function_exists('fsockopen')){
			$parts=parse_url($url_cron);

			switch ($parts['scheme']) {
				case 'https':
					$scheme = 'ssl://';
					$port = 443;
					break;
				case 'http':
				default:
					$scheme = '';
					$port = 80;
			}

			$fp = @fsockopen($scheme.$parts['host'],
		        isset($parts['port'])?$parts['port']:$port,
		        $errno, $errstr, 1);

			if ($fp) {
				$timeout = 200; // ms
				stream_set_timeout($fp,0,$timeout * 1000);
				$query = $parts['path'].($parts['query']?"?".$parts['query']:"");
				$out = "GET ".$query." HTTP/1.1\r\n";
				$out.= "Host: ".$parts['host']."\r\n";
				$out.= "Connection: Close\r\n\r\n";
				fwrite($fp, $out);
				spip_timer('read');
				$t = 0;
				// on lit la reponse si possible pour fermer proprement la connexion
				// avec un timeout total de 200ms pour ne pas se bloquer
				while (!feof($fp) AND $t<$timeout) {
					@fgets($fp, 1024);
					$t += spip_timer('read',true);
					spip_timer('read');
				}
				fclose($fp);
				if (!$urgent)
					return $texte;
			}
		}
		// si fsockopen n'est pas dispo on essaye cURL :
		// lancer le cron par un cURL asynchrone si cURL est present
		elseif (function_exists("curl_init")){
			//setting the curl parameters.
			$ch = curl_init($url_cron);
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
			// cf bug : http://www.php.net/manual/en/function.curl-setopt.php#104597
			curl_setopt($ch, CURLOPT_NOSIGNAL, 1);
			// valeur mini pour que la requete soit lancee
			curl_setopt($ch, CURLOPT_TIMEOUT_MS, 200);
			// lancer
			curl_exec($ch);
			// fermer
			curl_close($ch);
			if (!$urgent)
				return $texte;
		}
	}

	// si deja force, on retourne sans rien
	if (defined('_DIRECT_CRON_FORCE'))
		return $texte;

	// si c'est un bot
	// inutile de faire un appel par image background,
	// on force un appel direct en fin de hit
	if ((defined('_IS_BOT') AND _IS_BOT)){
		define('_DIRECT_CRON_FORCE',true);
		return $texte;
	}

	// en derniere solution, on insere une image background dans la page
	$texte = '<!-- SPIP-CRON --><div style="background-image: url(\'' .
		generer_url_action('cron') .
		'\');"></div>';

	return $texte;
}
?>
actions.php000066600000007072152525475610006743 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

/**
 * retourne une URL ou un formulaire securises
 *
 * http://doc.spip.org/@generer_action_auteur
 *
 * @param string $action
 * @param string $arg
 * @param string $redirect
 * @param bool|int|string $mode
 *   -1 : renvoyer action, arg et hash sous forme de array()
 *   true ou false : renvoyer une url, avec &amp; (false) ou & (true)
 *   string : renvoyer un formulaire
 * @param string|int $att
 *   id_auteur pour lequel generer l'action en mode url ou array()
 *   atributs du formulaire en mode formulaire
 * @param bool $public
 * @return array|string
 */
function generer_action_auteur($action, $arg, $redirect = "", $mode = false, $att = '', $public = false){
	$securiser_action = charger_fonction('securiser_action', 'inc');
	return $securiser_action($action, $arg, $redirect, $mode, $att, $public);
}

// http://doc.spip.org/@redirige_action_auteur
function redirige_action_auteur($action, $arg, $ret, $gra = '', $mode = false, $atts = '', $public = false) {

	$r = ($public ? _DIR_RESTREINT_ABS : _DIR_RESTREINT) .generer_url_ecrire($ret, $gra, true, true);

	return generer_action_auteur($action, $arg, $r, $mode, $atts, $public);
}

// http://doc.spip.org/@redirige_action_post
function redirige_action_post($action, $arg, $ret, $gra, $corps, $att = ''){
	$r = _DIR_RESTREINT.generer_url_ecrire($ret, $gra, false, true);
	return generer_action_auteur($action, $arg, $r, $corps, $att." method='post'");
}


/**
 * Fonction de formatage du contenu renvoye en ajax
 *
 * http://doc.spip.org/@ajax_retour
 *
 * @param string $corps
 * @param string $content_type
 *   permet de definir le type de contenu renvoye.
 *   Si rien de précisé, ou si true c'est "text/html" avec un entete xml en plus.
 *   La valeur speciale false fournit text/html sans entete xml. Elle equivaut a
 *   passer "text/html" comme $content_type
 */
function ajax_retour($corps, $content_type = null){
	$xml = false;
	if (is_null($content_type) OR $content_type===true){
		$xml = true;
		$content_type = 'text/html';
	}
	elseif (!$content_type OR !is_string($content_type) OR strpos($content_type,'/')===false) {
		$content_type = 'text/html';
	}
	
	$e = "";
	if (isset($_COOKIE['spip_admin'])
	    AND ((_request('var_mode')=='debug') OR !empty($GLOBALS['tableau_des_temps'])))
		$e = erreur_squelette();
	if (isset($GLOBALS['transformer_xml']) OR (isset($GLOBALS['exec']) AND $GLOBALS['exec']=='valider_xml')){
		$debut = _DOCTYPE_ECRIRE
		         ."<html><head><title>Debug Spip Ajax</title></head>"
		         ."<body><div>\n\n"
		         ."<!-- %%%%%%%%%%%%%%%%%%% Ajax %%%%%%%%%%%%%%%%%%% -->\n";

		$fin = '</div></body></html>';

	} else {
		$c = $GLOBALS['meta']["charset"];
		header('Content-Type: '.$content_type.'; charset='.$c);
		$debut = (($xml AND strlen(trim($corps))) ? '<'."?xml version='1.0' encoding='".$c."'?".">\n" : '');
		$fin = "";
	}
	echo $debut, $corps, $fin, $e;
}

?>
lang_liste.php000066600000016375152525475610007432 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/



/*
 Ce fichier contient les codes de langue de base de SPIP.
 La plupart sont des codes ISO 639-1, dont vous pouvez lire
 les definitions/descriptions en francais et en anglais dans
 http://www.loc.gov/standards/iso639-2/langcodes.html
*/

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

$GLOBALS['codes_langues'] = array(
	'aa' => "Afar",
	'ab' => "Abkhazian",
	'af' => "Afrikaans",
	'am' => "Amharic",
	'an' => "Aragon&#233;s",
	'ar' => "&#1593;&#1585;&#1576;&#1610;",
	'as' => "Assamese",
	'ast' => "asturianu",
	'ay' => "Aymara",
	'az' => "Az&#601;rbaycan dili",
	'ba' => "Bashkir",
	'be' => "&#1041;&#1077;&#1083;&#1072;&#1088;&#1091;&#1089;&#1082;&#1072;&#1103;",
	'ber_tam' => "Tamazigh",
	'ber_tam_tfng' => "Tamazigh tifinagh",
	'bg' => "&#1073;&#1098;&#1083;&#1075;&#1072;&#1088;&#1089;&#1082;&#1080;",
	'bh' => "Bihari",
	'bi' => "Bislama",
	'bm' => "Bambara",
	'bn' => "Bengali; Bangla",
	'bo' => "Tibetan",
	'br' => "brezhoneg",
	'bs' => "bosanski",
	'ca' => "catal&#224;",
	'co' => "corsu",
	'cpf' => "Kr&eacute;ol r&eacute;yon&eacute;",
	'cpf_dom' => "Krey&ograve;l",
	'cpf_hat' => "Krey&ograve;l (Peyi Dayiti)",
	'cs' => "&#269;e&#353;tina",
	'cy' => "Cymraeg",	# welsh, gallois
	'da' => "dansk",
	'de' => "Deutsch",
	'dz' => "Bhutani",
	'el' => "&#949;&#955;&#955;&#951;&#957;&#953;&#954;&#940;",
	'en' => "English",
	'en_hx' => "H4ck3R",
	'en_sm' => "Smurf",
	'eo' => "Esperanto",
	'es' => "Espa&#241;ol",
	'es_co' => "Colombiano",
	'es_mx_pop' => "Mexicano a lo g&#252;ey",
	'et' => "eesti",
	'eu' => "euskara",
	'fa' => "&#1601;&#1575;&#1585;&#1587;&#1609;",
	'ff' => "Fulah", // peul
	'fi' => "suomi",
	'fj' => "Fiji",
	'fo' => "f&#248;royskt",
	'fon' => "fongb&egrave;",
	'fr' => "fran&#231;ais",
	'fr_fem' => "fran&#231;ais f&#233;minin",
	'fr_sc' => "schtroumpf",
	'fr_lpc' => "langue parl&#233;e compl&#233;t&#233;e",
	'fr_lsf' => "langue des signes fran&#231;aise",
	'fr_spl' => "fran&#231;ais simplifi&#233;",
	'fr_tu' => "fran&#231;ais copain",
	'fy' => "Frisian",
	'ga' => "Irish",
	'gd' => "Scots Gaelic",
	'gl' => "galego",
	'gn' => "Guarani",
	'grc' => "&#7944;&#961;&#967;&#945;&#943;&#945; &#7961;&#955;&#955;&#951;&#957;&#953;&#954;&#942;", // grec ancien
	'gu' => "Gujarati",
	'ha' => "Hausa",
	'hac' => "&#1705;-&#1607;&#1734;&#1585;&#1575;&#1605;&#1740;", //"Kurdish-Horami"
	'hbo' => "&#1506;&#1489;&#1512;&#1497;&#1514;&#1470;&#1492;&#1514;&#1504;&#1498;", // hebreu classique ou biblique
	'he' => "&#1506;&#1489;&#1512;&#1497;&#1514;",
	'hi' => "&#2361;&#2367;&#2306;&#2342;&#2368;",
	'hr' => "hrvatski",
	'hu' => "magyar",
	'hy' => "&#1344;&#1377;&#1397;&#1381;&#1408;&#1381;&#1398;",// Arménien
	'ia' => "Interlingua",
	'id' => "Indonesia",
	'ie' => "Interlingue",
	'ik' => "Inupiak",
	'is' => "&#237;slenska",
	'it' => "italiano",
	'it_fem' => "italiana",
	'iu' => "Inuktitut",
	'ja' => "&#26085;&#26412;&#35486;",
	'jv' => "Javanese",
	'ka' => "&#4325;&#4304;&#4320;&#4311;&#4323;&#4314;&#4312;",
	'kk' => "&#1179;&#1072;&#1079;&#1072;&#1179;&#32;&#1090;&#1110;&#1083;&#1110;", // Kazakh
	'kl' => "kalaallisut",
	'km' => "&#6039;&#6070;&#6047;&#6070;&#6017;&#6098;&#6040;&#6082;&#6042;",// Khmer
	'kn' => "Kannada",
	'ko' => "&#54620;&#44397;&#50612;",
	'kok' => "&#2325;&#2379;&#2306;&#2325;&#2339;&#2368;", // Konkani
	'ks' => "Kashmiri",
	'ku' => "&#1705;&#1608;&#1585;&#1583;&#1740;",
	'ky' => "Kirghiz",
	'la' => "lingua latina",
	'lb' => "L&euml;tzebuergesch",
	'ln' => "Lingala",
	'lo' => "&#3742;&#3762;&#3754;&#3762;&#3749;&#3762;&#3751;", # lao
	'lt' => "lietuvi&#371;",
	'lu' => "luba-katanga",
	'lv' => "latvie&#353;u",
	'man' => "mandingue", # a traduire en mandingue
	'mfv' => "manjak", # ISO-639-3
	'mg' => "Malagasy",
	'mi' => "Maori",
	'mk' => "&#1084;&#1072;&#1082;&#1077;&#1076;&#1086;&#1085;&#1089;&#1082;&#1080; &#1112;&#1072;&#1079;&#1080;&#1082;",
	'ml' => "Malayalam",
	'mn' => "Mongolian",
	'mo' => "Moldavian",
	'mos' => "Mor&eacute;",
	'mr' => "&#2350;&#2352;&#2366;&#2336;&#2368;",
	'ms' => "Bahasa Malaysia",
	'mt' => "Maltese",
	'my' => "Burmese",
	'na' => "Nauru",
	'nap' => "napulitano",
	'ne' => "Nepali",
	'nqo' => "N'ko", // www.manden.org
	'nl' => "Nederlands",
	'no' => "norsk",
	'nb' => "norsk bokm&aring;l",
	'nn' => "norsk nynorsk",
	'oc' => "&ograve;c",
	'oc_lnc' => "&ograve;c lengadocian",
	'oc_ni' => "&ograve;c ni&ccedil;ard",
	'oc_ni_la' => "&ograve;c ni&ccedil;ard (larg)",
	'oc_ni_mis' => "&ograve;c nissart (mistralenc)",
	'oc_prv' => "&ograve;c proven&ccedil;au",
	'oc_gsc' => "&ograve;c gascon",
	'oc_lms' => "&ograve;c lemosin",
	'oc_auv' => "&ograve;c auvernhat",
	'oc_va' => "&ograve;c vivaroaupenc",
	'om' => "(Afan) Oromo",
	'or' => "Oriya",
	'pa' => "Punjabi",
	'pbb' => 'Nasa Yuwe',
	'pl' => "polski",
	'prs' => "&#1583;&#1585;&#1740;", // ISO-639-3 Dari (Afghanistan)
	'ps' => "&#1662;&#1690;&#1578;&#1608;",
	'pt' => "Portugu&#234;s",
	'pt_br' => "Portugu&#234;s do Brasil",
	'qu' => "Quechua",
	'rm' => "Rhaeto-Romance",
	'rn' => "Kirundi",
	'ro' => "rom&#226;n&#259;",
	'roa' => "ch'ti",
	'ru' => "&#1088;&#1091;&#1089;&#1089;&#1082;&#1080;&#1081;",
	'rw' => "Kinyarwanda",
	'sa' => "&#2360;&#2306;&#2360;&#2381;&#2325;&#2371;&#2340;",
	'sc' => "sardu",
	'scn' => "sicilianu",
	'sd' => "Sindhi",
	'sg' => "Sangho",
	'sh' => "srpskohrvastski",
	'sh_latn' => 'srpskohrvastski',
	'sh_cyrl' => '&#1057;&#1088;&#1087;&#1089;&#1082;&#1086;&#1093;&#1088;&#1074;&#1072;&#1090;&#1089;&#1082;&#1080;',
	'si' => "Sinhalese",
	'sk' => "sloven&#269;ina",	// (Slovakia)
	'sl' => "sloven&#353;&#269;ina",	// (Slovenia)
	'sm' => "Samoan",
	'sn' => "Shona",
	'so' => "Somali",
	'sq' => "shqip",
	'sr' => "&#1089;&#1088;&#1087;&#1089;&#1082;&#1080;",
	'src' => 'sardu logudor&#233;su', // sarde cf 'sc'
	'sro' => 'sardu campidan&#233;su',
	'ss' => "Siswati",
	'st' => "Sesotho",
	'su' => "Sundanese",
	'sv' => "svenska",
	'sw' => "Kiswahili",
	'ta' => "&#2980;&#2990;&#3007;&#2996;&#3021;", // Tamil
	'te' => "Telugu",
	'tg' => "Tajik",
	'th' => "&#3652;&#3607;&#3618;",
	'ti' => "Tigrinya",
	'tk' => "Turkmen",
	'tl' => "Tagalog",
	'tn' => "Setswana",
	'to' => "Tonga",
	'tr' => "T&#252;rk&#231;e",
	'ts' => "Tsonga",
	'tt' => "&#1058;&#1072;&#1090;&#1072;&#1088;",
	'tw' => "Twi",
	'ty' => "reo m&#257;`ohi", // tahitien
	'ug' => "Uighur",
	'uk' => "&#1091;&#1082;&#1088;&#1072;&#1111;&#1085;&#1089;&#1100;&#1082;&#1072;",
	'ur' => "&#1649;&#1585;&#1583;&#1608;",
	'uz' => "O'zbekcha",
	'vi' => "Ti&#7871;ng Vi&#7879;t",
	'vo' => "Volapuk",
	'wa' => "walon",
	'wo' => "Wolof",
	'xh' => "Xhosa",
	'yi' => "Yiddish",
	'yo' => "Yoruba",
	'za' => "Zhuang",
	'zh' => "&#20013;&#25991;", // chinois (ecriture simplifiee)
	'zh_tw' => "&#21488;&#28771;&#20013;&#25991;", // chinois taiwan (ecr. traditionnelle)
	'zu' => "Zulu"

);

?>
plugin.php000066600000071547152525475610006611 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

// l'adresse du repertoire de telechargement et de decompactage des plugins
define('_DIR_PLUGINS_AUTO', _DIR_PLUGINS.'auto/');

#include_spip('inc/texte'); // ????? Appelle public/parametrer trop tot avant la reconstruction du chemin des plugins.
include_spip('plugins/installer');

// lecture des sous repertoire plugin existants
// $dir_plugins pour forcer un repertoire (ex: _DIR_PLUGINS_DIST)
// _DIR_PLUGINS_SUPPL pour aller en chercher ailleurs
// (chemins relatifs a la racine du site, separes par des ":")
// http://doc.spip.org/@liste_plugin_files
function liste_plugin_files($dir_plugins = null){
	static $plugin_files=array();
	if (is_null($dir_plugins))
		$dir_plugins = _DIR_PLUGINS;
	if (!isset($plugin_files[$dir_plugins])
	OR count($plugin_files[$dir_plugins]) == 0){
		$plugin_files[$dir_plugins] = array();
		foreach (fast_find_plugin_dirs($dir_plugins) as $plugin) {
			$plugin_files[$dir_plugins][] = substr($plugin,strlen($dir_plugins));
		}
		
		sort($plugin_files[$dir_plugins]);
		// et on lit le XML de tous les plugins pour le mettre en cache
		// et en profiter pour nettoyer ceux qui n'existent plus du cache
		$get_infos = charger_fonction('get_infos','plugins');
		$get_infos($plugin_files[$dir_plugins],false,$dir_plugins,true);
	}
	return $plugin_files[$dir_plugins];
}

function fast_find_plugin_dirs($dir, $max_prof=100) {
	$fichiers = array();
	// revenir au repertoire racine si on a recu dossier/truc
	// pour regarder dossier/truc/ ne pas oublier le / final
	$dir = preg_replace(',/[^/]*$,', '', $dir);
	if ($dir == '') $dir = '.';

	if (!is_dir($dir))
		return $fichiers;
	if (is_plugin_dir($dir,'')) {
		$fichiers[] = $dir;
		return $fichiers;
	}
	if ($max_prof<=0)
		return $fichiers;

	$subdirs = array();
	if (@is_dir($dir) AND is_readable($dir) AND $d = @opendir($dir)) {
		while (($f = readdir($d)) !== false) {
			if ($f[0] != '.' # ignorer . .. .svn etc
			AND $f != 'CVS'
			AND is_dir($f = "$dir/$f"))
				$subdirs[] = $f;
		}
		closedir($d);
	}

	foreach($subdirs as $d){
		$fichiers = array_merge($fichiers,fast_find_plugin_dirs("$d/",$max_prof-1));
	}
	return $fichiers;
}

function is_plugin_dir($dir,$dir_plugins = null){
	if (is_array($dir)){
		foreach($dir as $k=>$d){
			if (!is_plugin_dir($d,$dir_plugins))
				unset($dir[$k]);
		}
		return $dir;
	}
	if (is_null($dir_plugins))
		$dir_plugins = _DIR_PLUGINS;
	$search = array("$dir_plugins$dir/plugin.xml","$dir_plugins$dir/paquet.xml");
	
	foreach($search as $s){
		if (file_exists($s)){
			return $dir;
		}
	}
	return '';
}

// Regexp d'extraction des informations d'un intervalle de compatibilité
define('_EXTRAIRE_INTERVALLE', ',^[\[\(\]]([0-9.a-zRC\s\-]*)[;]([0-9.a-zRC\s\-\*]*)[\]\)\[]$,');

/**
 * Teste si le numéro de version d'un plugin est dans un intervalle donné.
 *
 * Cette fonction peut être volontairement trompée (phase de développement) :
 * voir commentaire infra sur l'utilisation de la constante _DEV_PLUGINS
 *
 * @param string $intervalle
 * 		Un intervalle entre 2 versions. ex: [2.0.0-dev;2.1.*]
 * @param string $version
 * 		Un numéro de version. ex: 3.1.99]
 * @param string $avec_quoi
 * 		Ce avec quoi est testée la compatibilité. par défaut ('')
 * 		avec un plugin (cas des 'necessite'), parfois ('spip')
 * 		avec SPIP.
 * @return bool
 * 		True si dans l'intervalle, false sinon.
**/
function plugin_version_compatible($intervalle, $version, $avec_quoi = '') {

	if (!strlen($intervalle)) return true;
	if (!preg_match(_EXTRAIRE_INTERVALLE,$intervalle,$regs)) return false;
	// Extraction des bornes et traitement de * pour la borne sup :
	// -- on autorise uniquement les ecritures 3.0.*, 3.*
	$minimum = $regs[1];
	$maximum = $regs[2];

	//  si une borne de compatibilité supérieure a été définie (dans
	//  mes_options.php, sous la forme : define('_DEV_PLUGINS', '3.1.99');
	//  on l'utilise (phase de dev, de test...) mais *que* en cas de comparaison
	//  avec la version de SPIP (ne nuit donc pas aux tests de necessite
	//  entre plugins)
	if (defined('_DEV_PLUGINS') && $avec_quoi == 'spip') {
		$maximum = _DEV_PLUGINS.']';
	}

	$minimum_inc = $intervalle{0}=="[";
	$maximum_inc = substr($intervalle,-1)=="]";

	if (strlen($minimum)){
		if ($minimum_inc AND spip_version_compare($version,$minimum,'<')) return false;
		if (!$minimum_inc AND spip_version_compare($version,$minimum,'<=')) return false;
	}
	if (strlen($maximum)){
		if ($maximum_inc AND spip_version_compare($version,$maximum,'>')) return false;
		if (!$maximum_inc AND spip_version_compare($version,$maximum,'>=')) return false;
	}
	return true;
}


/**
 * Construire la liste des infos strictement necessaires aux plugins a activer
 * afin de les memoriser dans une meta pas trop grosse
 * http://code.spip.net/@liste_plugin_valides
 *
 * @param array $liste_plug
 * @param bool $force
 * @return array
 */
function liste_plugin_valides($liste_plug, $force = false){
	$liste_ext = liste_plugin_files(_DIR_PLUGINS_DIST);
	$get_infos = charger_fonction('get_infos', 'plugins');
	$infos = array(
		// lister les extensions qui sont automatiquement actives
		'_DIR_PLUGINS_DIST' => $get_infos($liste_ext, $force, _DIR_PLUGINS_DIST),
		'_DIR_PLUGINS' => $get_infos($liste_plug, $force, _DIR_PLUGINS)
	);

	// creer une premiere liste non ordonnee mais qui ne retient
	// que les plugins valides, et dans leur derniere version en cas de doublon
	$infos['_DIR_RESTREINT'][''] = $get_infos('./', $force, _DIR_RESTREINT);
	$infos['_DIR_RESTREINT']['SPIP']['version'] = $GLOBALS['spip_version_branche'];
	$infos['_DIR_RESTREINT']['SPIP']['chemin'] = array();
	$liste_non_classee = array('SPIP' => array(
		'nom' => 'SPIP',
		'etat' => 'stable',
		'version' => $GLOBALS['spip_version_branche'],
		'dir_type' => '_DIR_RESTREINT',
		'dir' => '',
	)
	);

	foreach ($liste_ext as $plug){
		if (isset($infos['_DIR_PLUGINS_DIST'][$plug]))
			plugin_valide_resume($liste_non_classee, $plug, $infos, '_DIR_PLUGINS_DIST');
	}
	foreach ($liste_plug as $plug){
		if (isset($infos['_DIR_PLUGINS'][$plug]))
			plugin_valide_resume($liste_non_classee, $plug, $infos, '_DIR_PLUGINS');
	}
	
	if (defined('_DIR_PLUGINS_SUPPL') and _DIR_PLUGINS_SUPPL){
		$infos['_DIR_PLUGINS_SUPPL'] = $get_infos($liste_plug, false, _DIR_PLUGINS_SUPPL);
		foreach ($liste_plug as $plug){
			if (isset($infos['_DIR_PLUGINS_SUPPL'][$plug]))
				plugin_valide_resume($liste_non_classee, $plug, $infos, '_DIR_PLUGINS_SUPPL');
		}
	}
	
	plugin_fixer_procure($liste_non_classee, $infos);

	return array($infos, $liste_non_classee);
}

/**
 * Ne retenir un plugin que s'il est valide
 * et dans leur plus recente version compatible
 * avec la version presente de SPIP
 *
 * @param array $liste
 * @param string $plug
 * @param array $infos
 * @param string $dir_type
 */
function plugin_valide_resume(&$liste, $plug, $infos, $dir_type){
	$i = $infos[$dir_type][$plug];
	if (isset($i['erreur']) AND $i['erreur'])
		return;
	if (!plugin_version_compatible($i['compatibilite'], $GLOBALS['spip_version_branche'], 'spip'))
		return;
	$p = strtoupper($i['prefix']);
	if (!isset($liste[$p]) 
		OR spip_version_compare($i['version'], $liste[$p]['version'], '>')
	  ){
		$liste[$p] = array(
			'nom' => $i['nom'],
			'etat' => $i['etat'],
			'version' => $i['version'],
			'dir' => $plug,
			'dir_type' => $dir_type
		);
	}
}

/**
 * Completer la liste des plugins avec les eventuels procure
 *
 * les <procure> sont consideres comme des plugins proposes,
 * mais surchargeables (on peut activer un plugin qui procure ca pour l'ameliorer,
 * donc avec le meme prefixe, qui sera pris en compte si il a une version plus grande)
 *
 * @param array $liste
 * @param array $infos
 */
function plugin_fixer_procure(&$liste, &$infos){
	foreach($liste as $p=>$resume){
		$i = $infos[$resume['dir_type']][$resume['dir']];
		if (isset($i['procure']) AND $i['procure']){
			foreach($i['procure'] as $procure){
				$p = strtoupper($procure['nom']);
				$dir = $resume['dir'];
				if ($dir) $dir .= "/";
				$dir .= "procure:".$procure['nom'];

				$procure['etat'] = '?';
				$procure['dir_type'] = $resume['dir_type'];
				$procure['dir'] = $dir;

				// si ce plugin n'est pas deja procure, ou dans une version plus ancienne
				// on ajoute cette version a la liste
				if (!isset($liste[$p])
					OR spip_version_compare($procure['version'], $liste[$p]['version'], '>')
				  ){
					$liste[$p] = $procure;

					// on fournit une information minimale pour ne pas perturber la compilation
					$infos[$resume['dir_type']][$dir] = array(
						'prefix' => $procure['nom'],
						'nom' => $procure['nom'],
						'etat' => $procure['etat'],
						'version' => $procure['version'],
						'chemin' => array(),
						'necessite' => array(),
						'utilise' => array(),
						'lib' => array(),
						'menu' => array(),
						'onglet' => array(),
						'procure' => array(),
					);
				}
			}
		}
	}
}

/**
 * extrait les chemins d'une liste de plugin
 * selectionne au passage ceux qui sont dans $dir_plugins uniquement
 * si valeur non vide
 * 
 * @param array $liste
 * @param string $dir_plugins
 * @return array
 */
function liste_chemin_plugin($liste, $dir_plugins=_DIR_PLUGINS){
	foreach ($liste as $prefix=>$infos) {
		if (!$dir_plugins
			OR (
				defined($infos['dir_type'])
		    AND constant($infos['dir_type'])==$dir_plugins))
			$liste[$prefix] = $infos['dir'];
		else
			unset($liste[$prefix]);
	}
	return $liste;
}

/**
 * Liste les chemins vers les plugins actifs du dossier fourni en argument
 * a partir d'une liste d'elelements construits par plugin_valide_resume
 *
 * @return array
 */
// http://doc.spip.org/@liste_chemin_plugin_actifs
function liste_chemin_plugin_actifs($dir_plugins=_DIR_PLUGINS){
	include_spip('plugins/installer');
	return liste_chemin_plugin(liste_plugin_actifs(), $dir_plugins);
}

// Pour tester utilise, il faut connaitre tous les plugins 
// qui seront forcement pas la a la fin,
// car absent de la liste des plugins actifs.
// Il faut donc construire une liste ordonnee
// Cette fonction detecte des dependances circulaires, 
// avec un doute sur un "utilise" qu'on peut ignorer.
// Mais ne pas inserer silencieusement et risquer un bug sournois latent

function plugin_trier($infos, $liste_non_classee)
{
	$toute_la_liste = $liste_non_classee;
	$liste = $ordre = array();
	$count = 0;
	while ($c=count($liste_non_classee) AND $c!=$count){ // tant qu'il reste des plugins a classer, et qu'on ne stagne pas
	  #echo "tour::";var_dump($liste_non_classee);
		$count = $c;
		foreach($liste_non_classee as $p=>$resume) {
			$plug = $resume['dir'];
			$dir_type = $resume['dir_type'];
			$info1 = $infos[$dir_type][$plug];
			// si des plugins sont necessaires,
			// on ne peut inserer qu'apres eux
			foreach($info1['necessite'] as $need){
			  $nom = strtoupper($need['nom']);
			  $compat = isset($need['compatibilite']) ? $need['compatibilite'] : '';
			  if (!isset($liste[$nom]) OR !plugin_version_compatible($compat,$liste[$nom]['version'])) {
			      $info1 = false;
			      break;
			  }
			}
			if (!$info1) continue;
			// idem si des plugins sont utiles,
			// sauf si ils sont de toute facon absents de la liste
			foreach($info1['utilise'] as $need){
			  $nom = strtoupper($need['nom']);
			  $compat = isset($need['compatibilite']) ? $need['compatibilite'] : '';
			  if (isset($toute_la_liste[$nom])) {
			    if (!isset($liste[$nom]) OR 
				!plugin_version_compatible($compat, $liste[$nom]['version'])) {
			      $info1 = false;
			      break;
			    }
			  }
			}
			if ($info1) {
			  $ordre[$p] = $info1;
			  $liste[$p] = $liste_non_classee[$p];
			  unset($liste_non_classee[$p]);
			}
		}
	}
	return array($liste, $ordre, $liste_non_classee);
}

// Collecte les erreurs dans la meta 

function plugins_erreurs($liste_non_classee, $liste, $infos, $msg=array())
{
	static $erreurs = array();
	foreach($liste_non_classee as $p=>$resume){
		$dir_type = $resume['dir_type'];
		$plug = $resume['dir'];
		$k = $infos[$dir_type][$plug];
		$plug = constant($dir_type) . $plug;
		if (!isset($msg[$p])) {
		  if (!$msg[$p] = plugin_necessite($k['necessite'], $liste))
		    $msg[$p] = plugin_necessite($k['utilise'], $liste);
		} else {
		  foreach($msg[$p] as $c => $l)
		    $msg[$p][$c] = plugin_controler_lib($l['nom'], $l['lien']);
		}
		$erreurs[$plug] = $msg[$p];
	}
	ecrire_meta('plugin_erreur_activation',	serialize($erreurs));
}

function plugin_donne_erreurs($raw=false, $raz=true) {
	if (!isset($GLOBALS['meta']['plugin_erreur_activation'])) return $raw?array():'';
	$list = @unserialize($GLOBALS['meta']['plugin_erreur_activation']);
	// Compat ancienne version
	if (!$list)
	  $list = $raw?array():$GLOBALS['meta']['plugin_erreur_activation'];
	elseif(!$raw) {
	  foreach($list as $plug => $msg)
	    $list[$plug] = "<li>" . _T('plugin_impossible_activer', array('plugin' => $plug))
		  . "<ul><li>" . implode("</li><li>", $msg) . "</li></ul></li>";
	  $list ="<ul>" . join("\n", $list) . "</ul>";
	}
	if ($raz)
		effacer_meta('plugin_erreur_activation');
	return $list;
}

/**
 * Teste des dependances
 * Et verifie que chaque dependance est presente
 * dans la liste de plugins donnee
 *
 * @param array $n
 * 		Tableau de dependances dont on souhaite verifier leur presence
 * @param array $liste
 * 		Tableau des plugins presents
 * @return array
 * 		Tableau des messages d'erreurs recus. Il sera vide si tout va bien.
 * 
**/
function plugin_necessite($n, $liste) {
	$msg = array();
	foreach($n as $need){
		$id = strtoupper($need['nom']);
		if ($r = plugin_controler_necessite($liste, $id, $need['compatibilite'])) {
			$msg[] = $r;
		}
	}
	return $msg;
}

/**
 * Verifie qu'une dependance (plugin) est bien presente. 
 *
 * @param $liste
 * 		Liste de description des plugins
 * @param $nom
 * 		Le plugin donc on cherche la presence
 * @param $version
 * 		L'éventuelle intervalle de compatibilité de la dependance. ex: [1.1.0;]
 * @return string.
 * 		Vide si ok,
 * 		Message d'erreur lorsque la dependance est absente.
**/
function plugin_controler_necessite($liste, $nom, $version)
{
	if (isset($liste[$nom]) AND plugin_version_compatible($version,$liste[$nom]['version'])) {
		return '';
	}
	// retrouver le minimum
	if (preg_match(_EXTRAIRE_INTERVALLE, $version, $regs)) {
		$minimum = $regs[1];
		if ($minimum) {
			return _T('plugin_necessite_plugin', array(
				'plugin' => $nom,
				'version' => $minimum));
		}
	}
	return _T('plugin_necessite_plugin_sans_version', array('plugin' => $nom));
}

function plugin_controler_lib($lib, $url)
{
	/* Feature sortie du core, voir STP
	 * if ($url) {
		include_spip('inc/charger_plugin');
		$url = '<br />'	. bouton_telechargement_plugin($url, 'lib');
	}*/
	return _T('plugin_necessite_lib', array('lib'=>$lib)) . " <a href='$url'>$url</a>";
}

// Pour compatibilite et lisibilite du code
function actualise_plugins_actifs($pipe_recherche = false){
	return ecrire_plugin_actifs('', $pipe_recherche, 'force');
}

// mise a jour du meta en fonction de l'etat du repertoire
// Les  ecrire_meta() doivent en principe aussi initialiser la valeur a vide
// si elle n'existe pas
// risque de pb en php5 a cause du typage ou de null (verifier dans la doc php)
// @return true/false si il y a du nouveau
// http://doc.spip.org/@ecrire_plugin_actifs
function ecrire_plugin_actifs($plugin,$pipe_recherche=false,$operation='raz') {

	// creer le repertoire cache/ si necessaire ! (installation notamment)
	sous_repertoire(_DIR_CACHE, '', false,true);
	
	if (!spip_connect()) return false;
	if ($operation!='raz') {
		$plugin_valides = liste_chemin_plugin_actifs();
		$plugin_valides = is_plugin_dir($plugin_valides);
		if(defined('_DIR_PLUGINS_SUPPL') && _DIR_PLUGINS_SUPPL){
			$plugin_valides_supp = liste_chemin_plugin_actifs(_DIR_PLUGINS_SUPPL);
			$plugin_valides_supp = is_plugin_dir($plugin_valides_supp,_DIR_PLUGINS_SUPPL);
			$plugin_valides = array_merge($plugin_valides,$plugin_valides_supp);
		}
		// si des plugins sont en attentes (coches mais impossible a activer)
		// on les reinjecte ici
		if (isset($GLOBALS['meta']['plugin_attente'])
		  AND $a = unserialize($GLOBALS['meta']['plugin_attente']))
		$plugin_valides = $plugin_valides + liste_chemin_plugin($a);
		
		if ($operation=='ajoute')
			$plugin = array_merge($plugin_valides,$plugin);
		elseif ($operation=='enleve')
			$plugin = array_diff($plugin_valides,$plugin);
		else $plugin = $plugin_valides;
	}
	$actifs_avant = $GLOBALS['meta']['plugin'];

	// si une fonction de gestion de dependances existe, l'appeler ici
	if ($ajouter_dependances = charger_fonction("ajouter_dependances","plugins",true)){
		$plugin = $ajouter_dependances($plugin);
	}

	// recharger le xml des plugins a activer
	// on forcer le reload ici, meme si le fichier xml n'a pas change
	// pour ne pas rater l'ajout ou la suppression d'un fichier fonctions/options/administrations
	// pourra etre evite quand on ne supportera plus les plugin.xml
	// en deplacant la detection de ces fichiers dans la compilation ci dessous
	list($infos,$liste) = liste_plugin_valides($plugin,true);
	// trouver l'ordre d'activation
	list($plugin_valides,$ordre,$reste) = plugin_trier($infos, $liste);
	if ($reste) plugins_erreurs($reste, $liste, $infos);
	// Ignorer les plugins necessitant une lib absente
	// et preparer la meta d'entete Http
	$err = $msg = $header = array();
	foreach($plugin_valides as $p => $resume) {
		$header[]= $p.($resume['version']?"(".$resume['version'].")":"");
		if ($resume['dir']){ 
			foreach($infos[$resume['dir_type']][$resume['dir']]['lib'] as $l) {
				if (!find_in_path($l['nom'], 'lib/')) {
					$err[$p] = $resume;
					$msg[$p][] = $l;
					unset($plugin_valides[$p]);
				}
			}
		}
	}
	if ($err) plugins_erreurs($err, '', $infos, $msg);

	if (isset($GLOBALS['meta']['message_crash_plugins']))
		effacer_meta('message_crash_plugins');
	ecrire_meta('plugin',serialize($plugin_valides));
	$liste = array_diff_key($liste,$plugin_valides);
	ecrire_meta('plugin_attente',serialize($liste));
	$header = strtolower(implode(",",$header));
	ecrire_meta('plugin_header',substr($header,0,900));
	if (!isset($GLOBALS['spip_header_silencieux']) OR !$GLOBALS['spip_header_silencieux'])
		ecrire_fichier(_DIR_VAR."config.txt", (defined('_HEADER_COMPOSED_BY') ? _HEADER_COMPOSED_BY:"Composed-By: SPIP") . ' '. $GLOBALS['spip_version_affichee'] . " @ www.spip.net + " . $header);
	else
		@unlink(_DIR_VAR."config.txt");
	// generer charger_plugins_chemin.php
	plugins_precompile_chemin($plugin_valides, $ordre);
	// generer les fichiers
	// 	charger_plugins_options.php
	// 	charger_plugins_fonctions.php
	// et retourner les fichiers a verifier
	plugins_precompile_xxxtions($plugin_valides, $ordre);
	// mise a jour de la matrice des pipelines
	pipeline_matrice_precompile($plugin_valides, $ordre, $pipe_recherche);
	// generer le fichier _CACHE_PIPELINE
	pipeline_precompile();

	// attendre eventuellement l'invalidation du cache opcode
	spip_attend_invalidation_opcode_cache();

	if (spip_connect()) {
		// lancer et initialiser les nouveaux crons !
		include_spip('inc/genie');
		genie_queue_watch_dist();
	}

	return ($GLOBALS['meta']['plugin'] != $actifs_avant);
}

function plugins_precompile_chemin($plugin_valides, $ordre)
{
	$chemins = array();
	$contenu = "";
	foreach($ordre as $p => $info){
		// $ordre peur contenir des plugins en attente et non valides pour ce hit
		if (isset($plugin_valides[$p])){
			$dir_type = $plugin_valides[$p]['dir_type'];
			$plug = $plugin_valides[$p]['dir'];
			// definir le plugin, donc le path avant l'include du fichier options
			// permet de faire des include_spip pour attraper un inc_ du plugin

			$dir = $dir_type.".'" . $plug ."/'";
			
			$prefix = strtoupper(preg_replace(',\W,','_',$info['prefix']));
			if ($prefix!=="SPIP"){
				$contenu .= "define('_DIR_PLUGIN_$prefix',$dir);\n";
				foreach($info['chemin'] as $chemin){
					if (!isset($chemin['version']) OR plugin_version_compatible($chemin['version'],$GLOBALS['spip_version_branche'],'spip')){
						$dir = $chemin['path'];
						if (strlen($dir) AND $dir{0}=="/") $dir = substr($dir,1);
						if (strlen($dir) AND $dir=="./") $dir = '';
						if (strlen($dir)) $dir = rtrim($dir,'/').'/';
						if (!isset($chemin['type']) OR $chemin['type']=='public')
							$chemins['public'][]="_DIR_PLUGIN_$prefix".(strlen($dir)?".'$dir'":"");
						if (!isset($chemin['type']) OR $chemin['type']=='prive')
							$chemins['prive'][]="_DIR_PLUGIN_$prefix".(strlen($dir)?".'$dir'":"");
					}
				}
			}
		}
	}
	if (count($chemins)){
		$contenu .= "if (_DIR_RESTREINT) _chemin(implode(':',array(".implode(',',array_reverse($chemins['public'])).")));\n"
		  . "else _chemin(implode(':',array(".implode(',',array_reverse($chemins['prive'])).")));\n";
	}

	ecrire_fichier_php(_CACHE_PLUGINS_PATH, $contenu);
}

function plugins_precompile_xxxtions($plugin_valides, $ordre)
{
	$contenu = array('options' => '', 'fonctions' =>'');
	$boutons = array();
	$onglets = array();
	$sign = "";

	foreach($ordre as $p => $info){
		// $ordre peur contenir des plugins en attente et non valides pour ce hit
		if (isset($plugin_valides[$p])){
			$dir_type = $plugin_valides[$p]['dir_type'];
			$plug = $plugin_valides[$p]['dir'];
			$dir = constant($dir_type);
			$root_dir_type = str_replace('_DIR_','_ROOT_',$dir_type);
			if ($info['menu'])
				$boutons = array_merge($boutons,$info['menu']);
			if ($info['onglet'])
				$onglets = array_merge($onglets,$info['onglet']);
			foreach($contenu as $charge => $v){
				// si pas declare/detecte a la lecture du paquet.xml,
				// detecer a nouveau ici puisque son ajout ne provoque pas une modif du paquet.xml
				// donc ni sa relecture, ni sa detection
				if (!isset($info[$charge])
					AND $dir // exclure le cas du plugin "SPIP"
					AND strpos($dir,":")===false // exclure le cas des procure:
					AND file_exists("$dir$plug/paquet.xml") // uniquement pour les paquet.xml
					){
					if (is_readable("$dir$plug/".($file=$info['prefix']."_".$charge.".php"))){
						$info[$charge] = array($file);
					}
				}
				if (isset($info[$charge])){
					$files = $info[$charge];
					foreach($files as $k=>$file){
						// on genere un if file_exists devant chaque include
						// pour pouvoir garder le meme niveau d'erreur general
						$file = trim($file);
						if (!is_readable("$dir$plug/$file")
							// uniquement pour les paquet.xml
							AND file_exists("$dir$plug/paquet.xml")){
							unset($info[$charge][$k]);
						}
						else {
							$_file = $root_dir_type . ".'$plug/$file'";
							$contenu[$charge] .= "include_once_check($_file);\n";
						}
					}
				}
			}
			$sign .= md5(serialize($info));
		}
	}

	$contenu['options'] = "define('_PLUGINS_HASH','".md5($sign)."');\n" . $contenu['options'];
	$contenu['fonctions'] .= plugin_ongletbouton("boutons_plugins", $boutons)
	. plugin_ongletbouton("onglets_plugins", $onglets);

	ecrire_fichier_php(_CACHE_PLUGINS_OPT, $contenu['options']);
	ecrire_fichier_php(_CACHE_PLUGINS_FCT, $contenu['fonctions']);
}

function plugin_ongletbouton($nom, $val)
{
	if (!$val) $val = array();
	define("_UPDATED_$nom",$val = serialize($val));
	define("_UPDATED_md5_$nom",$md5=md5($val));
	$val = "unserialize('".str_replace("'","\'",$val)."')";
	return
		"if (!function_exists('$nom')) {\n"
	 ."function $nom(){return defined('_UPDATED_$nom')?unserialize(_UPDATED_$nom):$val;}\n"
		."function md5_$nom(){return defined('_UPDATED_md5_$nom')?_UPDATED_md5_$nom:'".$md5."';}\n"
	 ."}\n";
}

// creer le fichier CACHE_PLUGIN_VERIF a partir de
// $GLOBALS['spip_pipeline']
// $GLOBALS['spip_matrice']

function pipeline_matrice_precompile($plugin_valides, $ordre, $pipe_recherche)
{
	static $liste_pipe_manquants=array();
	if (($pipe_recherche)&&(!in_array($pipe_recherche,$liste_pipe_manquants)))
		$liste_pipe_manquants[]=$pipe_recherche;

	foreach($ordre as $p => $info){
		// $ordre peur contenir des plugins en attente et non valides pour ce hit
		if (isset($plugin_valides[$p])){
			$dir_type = $plugin_valides[$p]['dir_type'];
			$root_dir_type = str_replace('_DIR_','_ROOT_',$dir_type);
			$plug = $plugin_valides[$p]['dir'];
			$prefix = (($info['prefix']=="spip")?"":$info['prefix']."_");
			if (isset($info['pipeline']) AND is_array($info['pipeline'])){
				foreach($info['pipeline'] as $pipe){
					$nom = $pipe['nom'];
					if (isset($pipe['action']))
							$action = $pipe['action'];
					else
							$action = $nom;
					$nomlower = strtolower($nom);
					if ($nomlower!=$nom
					AND isset($GLOBALS['spip_pipeline'][$nom])
					AND !isset($GLOBALS['spip_pipeline'][$nomlower])){
						$GLOBALS['spip_pipeline'][$nomlower] = $GLOBALS['spip_pipeline'][$nom];
						unset($GLOBALS['spip_pipeline'][$nom]);
					}
					$nom = $nomlower;
					// une action vide est une declaration qui ne doit pas etre compilee !
					if (!isset($GLOBALS['spip_pipeline'][$nom])) // creer le pipeline eventuel
						$GLOBALS['spip_pipeline'][$nom]="";
					if ($action){
						if (strpos($GLOBALS['spip_pipeline'][$nom],"|$prefix$action")===FALSE)
							$GLOBALS['spip_pipeline'][$nom] = preg_replace(",(\|\||$),","|$prefix$action\\1",$GLOBALS['spip_pipeline'][$nom],1);
						if (isset($pipe['inclure'])){
							$GLOBALS['spip_matrice']["$prefix$action"] =
								"$root_dir_type:$plug/".$pipe['inclure'];
						}
					}
				}
			}
		}
	}
	
	// on charge les fichiers d'options qui peuvent completer 
	// la globale spip_pipeline egalement
	if (@is_readable(_CACHE_PLUGINS_PATH))
		include_once(_CACHE_PLUGINS_PATH); // securite : a priori n'a pu etre fait plus tot 
	if (@is_readable(_CACHE_PLUGINS_OPT)) {
		include_once(_CACHE_PLUGINS_OPT);
	} else {
		spip_log("pipelines desactives: impossible de produire " . _CACHE_PLUGINS_OPT);
	}
	
	// on ajoute les pipe qui ont ete recenses manquants
	foreach($liste_pipe_manquants as $add_pipe)
		if (!isset($GLOBALS['spip_pipeline'][$add_pipe]))
			$GLOBALS['spip_pipeline'][$add_pipe]= '';
}

// precompilation des pipelines
// http://doc.spip.org/@pipeline_precompile
function pipeline_precompile(){
	global $spip_pipeline, $spip_matrice;

	$content = "";
	foreach($spip_pipeline as $action=>$pipeline){
		$s_inc = "";
		$s_call = "";
		$pipe = array_filter(explode('|',$pipeline));
		// Eclater le pipeline en filtres et appliquer chaque filtre
		foreach ($pipe as $fonc) {
			$fonc = trim($fonc);
			$s_call .= '$val = minipipe(\''.$fonc.'\', $val);'."\n";
			if (isset($spip_matrice[$fonc])){
				$file = $spip_matrice[$fonc];
				$file = "'$file'";
				// si un _DIR_XXX: est dans la chaine, on extrait la constante
				if (preg_match(",(_(DIR|ROOT)_[A-Z_]+):,Ums",$file,$regs)){
					$dir = $regs[1];
					$root_dir = str_replace('_DIR_','_ROOT_',$dir);
					if (defined($root_dir))
						$dir = $root_dir;
					$file = str_replace($regs[0],"'.".$dir.".'",$file);
					$file = str_replace("''.","",$file);
					$file = str_replace(constant($dir), '', $file);
				}
				$s_inc .= "include_once_check($file);\n";
			}
		}
		if (strlen($s_inc))
			$s_inc = "static \$inc=null;\nif (!\$inc){\n$s_inc\$inc=true;\n}\n";
		$content .= "// Pipeline $action \n"
		.	"function execute_pipeline_$action(&\$val){\n"
		. $s_inc
		. $s_call
		. "return \$val;\n}\n";
	}
	ecrire_fichier_php(_CACHE_PIPELINES, $content);
	clear_path_cache();
}


// http://doc.spip.org/@plugin_est_installe
function plugin_est_installe($plug_path){
	$plugin_installes = isset($GLOBALS['meta']['plugin_installes'])?unserialize($GLOBALS['meta']['plugin_installes']):array();
	if (!$plugin_installes) return false;
	return in_array($plug_path,$plugin_installes);
}


function plugin_installes_meta()
{
	$installer_plugins = charger_fonction('installer', 'plugins');
	$meta_plug_installes = array();
	foreach (unserialize($GLOBALS['meta']['plugin']) as $prefix=>$resume) {
		if ($plug = $resume['dir']){
			$infos = $installer_plugins($plug, 'install', $resume['dir_type']);
			if ($infos){
				if (!is_array($infos) OR $infos['install_test'][0])
					$meta_plug_installes[] = $plug;
				if (is_array($infos)){
					list($ok, $trace) = $infos['install_test'];
					include_spip('inc/filtres_boites');
					echo  "<div class='install-plugins svp_retour'>"
						  .boite_ouvrir(_T('plugin_titre_installation', array('plugin' => typo($infos['nom']))), ($ok ? 'success' : 'error'))
					      .$trace
					      ."<div class='result'>"
					      .($ok ? ((isset($infos['upgrade']) && $infos['upgrade']) ? _T("plugin_info_upgrade_ok") : _T("plugin_info_install_ok")) : _T("avis_operation_echec"))
					      ."</div>"
					      .boite_fermer()
					      ."</div>";
				}
			}
		}
	}
	ecrire_meta('plugin_installes',serialize($meta_plug_installes),'non');
}

function ecrire_fichier_php($nom, $contenu, $comment='')
{
	ecrire_fichier($nom, 
		       '<'.'?php' . "\n" . $comment ."\nif (defined('_ECRIRE_INC_VERSION')) {\n". $contenu . "}\n?".'>');
}

filtres_mini.php000066600000012566152525475610007773 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/


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

//
// Filtres d'URLs
//

// Nettoyer une URL contenant des ../
//
// resolve_url('/.././/truc/chose/machin/./.././.././hopla/..');
// inspire (de loin) par PEAR:NetURL:resolvePath
//
// http://doc.spip.org/@resolve_path
function resolve_path($url) {
	list($url, $query) = array_pad(explode('?', $url, 2), 2, null);
	while (preg_match(',/\.?/,', $url, $regs)		# supprime // et /./
	OR preg_match(',/[^/]*/\.\./,S', $url, $regs)	# supprime /toto/../
	OR preg_match(',^/\.\./,S', $url, $regs))		# supprime les /../ du haut
		$url = str_replace($regs[0], '/', $url);

	if ($query)
		$url .= '?'.$query;

	return '/'.preg_replace(',^/,S', '', $url);
}

// 
// Suivre un lien depuis une adresse donnee -> nouvelle adresse
//
// suivre_lien('http://rezo.net/sous/dir/../ect/ory/fi.html..s#toto',
// 'a/../../titi.coco.html/tata#titi');
// http://doc.spip.org/@suivre_lien
function suivre_lien($url, $lien) {

	if (preg_match(',^(mailto|javascript|data):,iS', $lien))
		return $lien;
	if (preg_match(';^((?:[a-z]{3,7}:)?//.*?)(/.*)?$;iS', $lien, $r))
		return $r[1].resolve_path($r[2]);

	# L'url site spip est un lien absolu aussi
	if ($lien == $GLOBALS['meta']['adresse_site']){
		return $lien;
	}

	# lien relatif, il faut verifier l'url de base
	# commencer par virer la chaine de get de l'url de base
	if (preg_match(';^((?:[a-z]{3,7}:)?//[^/]+)(/.*?/?)?([^/#?]*)([?][^#]*)?(#.*)?$;S', $url, $regs)) {
		$debut = $regs[1];
		$dir = !strlen($regs[2]) ? '/' : $regs[2];
		$mot = $regs[3];
		$get = isset($regs[4])?$regs[4]:"";
		$hash = isset($regs[5])?$regs[5]:"";
	}
	switch (substr($lien,0,1)) {
		case '/':
			return $debut . resolve_path($lien);
		case '#':
			return $debut . resolve_path($dir.$mot.$get.$lien);
		case '':
			return $debut . resolve_path($dir.$mot.$get.$hash);
		default:
			return $debut . resolve_path($dir.$lien);
	}
}

// un filtre pour transformer les URLs relatives en URLs absolues ;
// ne s'applique qu'aux #URL_XXXX
// http://doc.spip.org/@url_absolue
function url_absolue($url, $base='') {
	if (strlen($url = trim($url)) == 0)
		return '';
	if (!$base)
		$base = url_de_base() . (_DIR_RACINE ? _DIR_RESTREINT_ABS : '');
	return suivre_lien($base, $url);
}

/**
 * Supprimer le protocole d'une url absolue
 * pour le rendre implicite (URL commencant par "//")
 * @param string $url_absolue
 * @return string
 */
function protocole_implicite($url_absolue){
	return preg_replace(";^[a-z]{3,7}://;i","//",$url_absolue);
}

// un filtre pour transformer les URLs relatives en URLs absolues ;
// ne s'applique qu'aux textes contenant des liens
// http://doc.spip.org/@liens_absolus
function liens_absolus($texte, $base='') {
	if (preg_match_all(',(<(a|link|image|img|script)\s[^<>]*(href|src)=[^<>]*>),imsS', 
	$texte, $liens, PREG_SET_ORDER)) {
		if (!function_exists('extraire_attribut')) {
			include_spip('inc/filtres');
		}
		foreach ($liens as $lien) {
			foreach(array('href', 'src') as $attr) {
				$href = extraire_attribut($lien[0], $attr);
				if (strlen($href)>0) {
					$abs = url_absolue($href, $base);
					if ($href != $abs and !preg_match('/^#/',$href)) {
						$texte_lien = inserer_attribut($lien[0], $attr, $abs);
						$texte = str_replace($lien[0],$texte_lien,$texte);
					}
				}
			}
		}
	}

	return $texte;
}

//
// Ce filtre public va traiter les URL ou les <a href>
//
// http://doc.spip.org/@abs_url
function abs_url($texte, $base='') {
	if ($GLOBALS['mode_abs_url'] == 'url')
		return url_absolue($texte, $base);
	else
		return liens_absolus($texte, $base);
}

/**
* htmlspecialchars wrapper (PHP >= 5.4 compat issue)
*
* @param string $string
* @param int $flags
* @param string $encoding
* @param bool $double_encode
* @return string
*/
function spip_htmlspecialchars($string, $flags=null, $encoding='ISO-8859-1', $double_encode = true){
	if (is_null($flags)) {
		if (!defined('PHP_VERSION_ID') OR PHP_VERSION_ID < 50400)
			$flags = ENT_COMPAT;
		else
			$flags = ENT_COMPAT|ENT_HTML401;
	}

	if (!defined('PHP_VERSION_ID') OR PHP_VERSION_ID < 50203)
		return htmlspecialchars($string,$flags,$encoding);
	else
		return htmlspecialchars($string,$flags,$encoding,$double_encode);
}

/**
* htmlentities wrapper (PHP >= 5.4 compat issue)
*
* @param string $string
* @param int $flags
* @param string $encoding
* @param bool $double_encode
* @return string
*/
function spip_htmlentities($string,$flags=null,$encoding = 'ISO-8859-1',$double_encode = true){
	if (is_null($flags)) {
		if (!defined('PHP_VERSION_ID') OR PHP_VERSION_ID < 50400)
			$flags = ENT_COMPAT;
		else
			$flags = ENT_COMPAT|ENT_HTML401;
	}

	if (!defined('PHP_VERSION_ID') OR PHP_VERSION_ID < 50203)
		return htmlentities($string,$flags,$encoding);
	else
		return htmlentities($string,$flags,$encoding,$double_encode);
}
?>
iconifier.php000066600000002105152525475610007242 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

include_spip('inc/actions');

// http://doc.spip.org/@inc_iconifier_dist
function inc_iconifier_dist($objet, $id,  $script, $visible=false, $flag_modif=true) {
	// compat avec anciens appels
	$objet = objet_type($objet);
	
	return recuperer_fond('prive/objets/editer/logo',array('objet'=>$objet,'id_objet'=>$id,'editable'=>$flag_modif));
}

?>
selectionner.php000066600000010375152525475610007775 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

include_spip('inc/filtres');

//
// Affiche un mini-navigateur ajax positionne sur la rubrique $sel
//

// http://doc.spip.org/@inc_selectionner_dist
function inc_selectionner_dist ($sel, $idom="", $exclus=0, $aff_racine=false, $recur=true, $do='aff') {

	if ($recur) $recur = mini_hier($sel); else $sel = 0;

	if ($aff_racine) {
		$info = generer_url_ecrire('informer', "type=rubrique&rac=$idom&do=$do&id=");
		$idom3 = $idom . "_selection";

		$onClick = "jQuery(this).parent().addClass('on');jQuery('#choix_parent_principal .on').removeClass('on'); aff_selection(0, '$idom3', '$info', event);return false;";

		$ondbClick = strtr(str_replace("'", "&#8217;",
				str_replace('"', "&#34;",
					textebrut(_T('info_racine_site')))),
				"\n\r", "  ");

		$js_func = $do . '_selection_titre';
		$ondbClick = "$js_func('$ondbClick',0,'selection_rubrique','id_parent');";

		$aff_racine = "<div class='petite-racine item'>"
		. "<a href='#'"
			. "onclick=\""
			. $onClick
			. "\"\nondbclick=\""
			. $ondbClick
			. $onClick
		. "\">"
		. _T("info_racine_site")
		. "</a></div>";
	}

	$url_init = generer_url_ecrire('plonger',"rac=$idom&exclus=$exclus&id=0&col=1&do=$do");

	$plonger = charger_fonction('plonger', 'inc');
	$plonger_r = $plonger($sel, $idom, $recur, 1, $exclus, $do);

	// url completee par la fonction JS onkeypress_rechercher
	$url = generer_url_ecrire('rechercher', "exclus=$exclus&rac=$idom&do=$do&type=");
	return construire_selectionner_hierarchie($idom, $plonger_r, $aff_racine, $url, 'id_parent', $url_init);
}

// http://doc.spip.org/@construire_selectionner_hierarchie
function construire_selectionner_hierarchie($idom, $liste, $racine, $url, $name, $url_init='')
{
	global $spip_lang_right;

	$idom1 = $idom . "_champ_recherche";
	$idom2 = $idom . "_principal";
	$idom3 = $idom . "_selection";
	$idom4 = $idom . "_col_1";
	$idom5 = 'img_' . $idom4;
	$idom6 = $idom."_fonc";

	return "<div id='$idom'>"
	. "<a id='$idom6' style='visibility: hidden;'"
	. ($url_init ?  "\nhref='$url_init'" : '')
	. "></a>"
	. "<div class='recherche_rapide_parent'>"
	. http_img_pack("searching.gif", "*", "style='visibility: hidden;float:$spip_lang_right' id='$idom5'")
	. ""
	. "<input style='width: 100px;float:$spip_lang_right;' type='search' id='$idom1'"
	  // eliminer Return car il provoque la soumission (balise unique)
	  // ce serait encore mieux de ne le faire que s'il y a encore plusieurs
	  // resultats retournes par la recherche
	. "\nonkeypress=\"k=event.keyCode;if (k==13 || k==3){return false;}\""
	  // lancer la recherche apres le filtrage ci-dessus
	. "\nonkeyup=\"return onkey_rechercher(this.value,"
	  // la destination de la recherche
	. "'$idom4'"
#	. "this.parentNode.parentNode.parentNode.parentNode.nextSibling.firstChild.id"
	. ",'"
	  // l'url effectuant la recherche
	. $url
	. "',"	
	  // le noeud contenant un gif anime 
	  // . "'idom5'"
	. "this.parentNode.previousSibling.firstChild"
	. ",'"
	  // la valeur de l'attribut Name a remplir
	.  $name
	. "','"
	  // noeud invisible memorisant l'URL initiale (pour re-initialisation)
	. $idom6
	. "')\"" 
	. " />"
	. "\n</div>"
	. ($racine?"<div>$racine</div>":"")
	. "<div id='"
	.  $idom2
	.  "'><div id='$idom4'"
	. " class=''>"
	. $liste
	. "</div></div>\n<div id='$idom3'></div></div>\n";
}

// http://doc.spip.org/@mini_hier
function mini_hier ($id_rubrique) {
	
	$liste = $id_rubrique;
	$id_rubrique = intval($id_rubrique);
	while ($id_rubrique = sql_getfetsel("id_parent", "spip_rubriques", "id_rubrique = " . $id_rubrique))
		$liste = $id_rubrique . ",$liste";
	return explode(',',"0,$liste");
}

?>
chercher_logo.php000066600000003237152525475610010105 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

// http://doc.spip.org/@inc_chercher_logo_dist
function inc_chercher_logo_dist($id, $_id_objet, $mode='on') {
	global $formats_logos;
	# attention au cas $id = '0' pour LOGO_SITE_SPIP : utiliser intval()

	$type = type_du_logo($_id_objet);
	$nom = $type . $mode . intval($id);

	foreach ($formats_logos as $format) {
		if (@file_exists($d = (_DIR_LOGOS . $nom . '.' . $format))) {
			return array($d, _DIR_LOGOS, $nom, $format, @filemtime($d));
		}
	}
	# coherence de type pour servir comme filtre (formulaire_login)
	return array();
}

// http://doc.spip.org/@type_du_logo
function type_du_logo($_id_objet) {
	return isset($GLOBALS['table_logos'][$_id_objet])
		? $GLOBALS['table_logos'][$_id_objet]
		: objet_type(preg_replace(',^id_,','',$_id_objet));
}

// Exceptions standards (historique)
global $table_logos;
$table_logos = array( 
	'id_article' => 'art', 
	'id_auteur' => 'aut', 
	'id_rubrique' => 'rub',
	'id_groupe' => 'groupe',
);

?>
boutons.php000066600000005676152525475610007004 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

/**
 * une classe definissant un bouton dans la barre du haut de l'interface
 * privee ou dans un de ses sous menus
 */
// http://doc.spip.org/@Bouton
class Bouton {
  var $icone;         /* l'icone a mettre dans le bouton */
  var $libelle;       /* le nom de l'entree d'i18n associe */
  var $url= null;     /* l'url de la page (null => ?exec=nom) */
  var $urlArg= null;  /* arguments supplementaires de l'url */
  var $url2= null;    /* url jscript */
  var $target= null;  /* pour ouvrir dans une fenetre a part */
  var $sousmenu= null;/* sous barre de boutons / onglets */

// http://doc.spip.org/@Bouton
  function Bouton($icone, $libelle, $url=null, $urlArg=null,
				  $url2=null, $target=null) {
	$this->icone  = $icone;
	$this->libelle= $libelle;
	$this->url    = $url;
	$this->urlArg = $urlArg;
	$this->url2   = $url2;
	$this->target = $target;
  }
}


/**
 * definir la liste des onglets dans une page de l'interface privee
 * on passe la main au pipeline "ajouter_onglets".
 */
// http://doc.spip.org/@definir_barre_onglets
function definir_barre_onglets($script) {

	$onglets=array();
	$liste_onglets = array();

	// ajouter les onglets issus des plugin via plugin.xml
	if (function_exists('onglets_plugins'))
		$liste_onglets = onglets_plugins();


	foreach($liste_onglets as $id => $infos){
		if (($parent = $infos['parent'])
			&& $parent == $script
			&& autoriser('onglet',"_$id")) {
				$onglets[$id] = new Bouton(
					find_in_theme($infos['icone']),  // icone
					$infos['titre'],	// titre
					(isset($infos['action']) and $infos['action'])
						? generer_url_ecrire($infos['action'],(isset($infos['parametres']) AND $infos['parametres'])?$infos['parametres']:'')
						: null
					);
		}
	}

	return pipeline('ajouter_onglets', array('data'=>$onglets,'args'=>$script));
}


// http://doc.spip.org/@barre_onglets
function barre_onglets($rubrique, $ongletCourant, $class="barre_onglet"){
	include_spip('inc/presentation');

	$res = '';

	foreach(definir_barre_onglets($rubrique) as $exec => $onglet) {
		$url= $onglet->url ? $onglet->url : generer_url_ecrire($exec);
		$res .= onglet(_T($onglet->libelle), $url, $exec, $ongletCourant, $onglet->icone);
	}

	return  !$res ? '' : (debut_onglet($class) . $res . fin_onglet());
}


?>
filtres_images_lib_mini.php000066600000120374152525475610012143 0ustar00<?php

/* *************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

/**
 * Ce fichier contient les fonctions utilisées
 * par les fonctions-filtres de traitement d'image.
 *
 * @package SPIP\image
 */


if (!defined('_ECRIRE_INC_VERSION')) return;
include_spip('inc/filtres'); // par precaution
include_spip('inc/filtres_images_mini'); // par precaution

/**
 * Transforme une couleur vectorielle R,G,B en hexa (par exemple pour usage css)
 *
 * @param int $red
 * 		Valeur du rouge de 0 à 255.
 * @param int $green
 * 		Valeur du vert de 0 à 255.
 * @param int $blue
 * 		Valeur du bleu de 0 à 255.
 * @return string
 * 		le code de la couleur en hexadécimal.
 */
function _couleur_dec_to_hex($red, $green, $blue) {
	$red = dechex($red);
	$green = dechex($green);
	$blue = dechex($blue);
	
	if (strlen($red) == 1) $red = "0".$red;
	if (strlen($green) == 1) $green = "0".$green;
	if (strlen($blue) == 1) $blue = "0".$blue;
	
	return "$red$green$blue";
}

/**
 * Transforme une couleur hexa en vectorielle R,G,B
 *
 * @param string $couleur
 * 		Code couleur en hexa (#000000 à #FFFFFF).
 * @return array
 * 		Un tableau des 3 éléments : rouge, vert, bleu.
 */
function _couleur_hex_to_dec($couleur) {
	$couleur = couleur_html_to_hex($couleur);
	$couleur = preg_replace(",^#,","",$couleur);
	$retour["red"] = hexdec(substr($couleur, 0, 2));
	$retour["green"] = hexdec(substr($couleur, 2, 2));
	$retour["blue"] = hexdec(substr($couleur, 4, 2));
	
	return $retour;
}


/**
 * Donne un statut au fichier-image intermédiaire servant au traitement d'image
 * selon qu'il doit être gravé (fichier .src) ou pas.
 * Un appel php direct aux fonctions de filtre d'image produira ainsi une image
 * permanente (gravée) ; un appel généré par le compilateur via
 * filtrer('image_xx, ...) effacera automatiquement le fichier-image temporaire.
 *
 * @param bool|string $stat
 * 		true, false ou le statut déjà défini si traitements enchaînés.
 * @return bool
 * 		true si il faut supprimer le fichier temporaire ; false sinon.
 */
function statut_effacer_images_temporaires($stat){
	static $statut = false; // par defaut on grave toute les images
	if ($stat==='get') return $statut;
	$statut = $stat?true:false;
}


/**
 * Fonctions de traitement d'image
 * Uniquement pour GD2.
 *
 * @param string $img
 * 		Un tag html <img src=... />.
 * @param string $effet
 * 		Les nom et paramètres de l'effet à apporter sur l'image
 * 		(par exemple : reduire-300-200).
 * @param bool|string $forcer_format
 * 		Un nom d'extension spécifique demandé (par exemple : jpg, png, txt...
 * 		par défaut false : GD se débrouille seule).
 * @param array $fonction_creation
 * 		Un tableau à 2 éléments. Le premier (string) indique le nom du
 * 		filtre de traitement demandé (par exemple : image_reduire) ; le
 * 		second (array) est lui-même un tableau reprenant la valeur de $img
 * 		et chacun des paramètres passés au filtre.
 * @param bool $find_in_path
 * 		false (par défaut) indique que l'on travaille sur un fichier
 * 		temporaire (.src) ; true, sur un fichier définitif déjà existant.
 * @return bool|string|array
 * 		false si pas de tag <img,
 * 		    si l'extension n'existe pas,
 * 		    si le fichier source n'existe pas,
 * 		    si les dimensions de la source ne sont pas accessibles,
 * 		    si le fichier temporaire n'existe pas,
 * 		    si la fonction _imagecreatefrom{extension} n'existe pas ;
 * 		"" (chaîne vide) si le fichier source est distant et n'a pas
 * 		    réussi à être copié sur le serveur ;
 * 		l'appel à la fonction pipeline image_preparer_filtre.
 */
function _image_valeurs_trans($img, $effet, $forcer_format = false, $fonction_creation = NULL, $find_in_path = false) {
	static $images_recalcul = array();
	if (strlen($img)==0) return false;

	$source = trim(extraire_attribut($img, 'src'));
	if (strlen($source) < 1){
		$source = $img;
		$img = "<img src='$source' />";
	}
	# gerer img src="data:....base64"
	else if (preg_match('@^data:image/(jpe?g|png|gif);base64,(.*)$@isS', $source, $regs)) {
		$local = sous_repertoire(_DIR_VAR,'image-data').md5($regs[2]).'.'.str_replace('jpeg', 'jpg', $regs[1]);
		if (!file_exists($local)) {
			ecrire_fichier($local, base64_decode($regs[2]));
		}
		$source = $local;
		$img = inserer_attribut($img, 'src', $source);
		# eviter les mauvaises surprises lors de conversions de format
		$img = inserer_attribut($img, 'width', '');
		$img = inserer_attribut($img, 'height', '');
	}

	// les protocoles web prennent au moins 3 lettres
	if (preg_match(';^(\w{3,7}://);', $source)){
		include_spip('inc/distant');
		$fichier = _DIR_RACINE . copie_locale($source);
		if (!$fichier) return "";
	}	else {
		// enlever le timestamp eventuel
		$source=preg_replace(',[?][0-9]+$,','',$source);
		if (strpos($source,"?")!==false
			AND strncmp($source,_DIR_IMG,strlen(_DIR_IMG))==0
			AND file_exists($f=preg_replace(',[?].*$,','',$source))){
			$source = $f;
		}
		$fichier = $source;
	}

	$terminaison_dest = "";
	if (preg_match(",\.(gif|jpe?g|png)($|[?]),i", $fichier, $regs)) {
		$terminaison = strtolower($regs[1]);
		$terminaison_dest = $terminaison;
		
		if ($terminaison == "gif") $terminaison_dest = "png";
	}
	if ($forcer_format!==false) $terminaison_dest = $forcer_format;

	if (!$terminaison_dest) return false;

	$term_fonction = $terminaison;
	if ($term_fonction == "jpg") $term_fonction = "jpeg";

	$nom_fichier = substr($fichier, 0, strlen($fichier) - (strlen($terminaison) + 1));
	$fichier_dest = $nom_fichier;
	if (($find_in_path AND $f=find_in_path($fichier) AND $fichier=$f)
		OR @file_exists($f = $fichier)){
		// on passe la balise img a taille image qui exraira les attributs si possible
		// au lieu de faire un acces disque sur le fichier
		list ($ret["hauteur"],$ret["largeur"]) = taille_image($find_in_path?$f:$img);
		$date_src = @filemtime($f);
	}
	elseif (@file_exists($f = "$fichier.src")
		AND lire_fichier($f,$valeurs)
		AND $valeurs=unserialize($valeurs)
		AND isset($valeurs["hauteur_dest"])
		AND isset($valeurs["largeur_dest"])) {
		$ret["hauteur"] = $valeurs["hauteur_dest"];
		$ret["largeur"] = $valeurs["largeur_dest"];
		$date_src = $valeurs["date"];
	}
	// pas de fichier source par la
	else
		return false;

	// pas de taille mesurable
	if (!($ret["hauteur"] OR $ret["largeur"]))
		return false;
		

	// cas general :
	// on a un dossier cache commun et un nom de fichier qui varie avec l'effet
	// cas particulier de reduire :
	// un cache par dimension, et le nom de fichier est conserve, suffixe par la dimension aussi
	$cache = "cache-gd2";
	if (substr($effet,0,7)=='reduire') {
		list(,$maxWidth,$maxHeight) = explode('-',$effet);
		list ($destWidth,$destHeight) = _image_ratio($ret['largeur'], $ret['hauteur'], $maxWidth, $maxHeight);
		$ret['largeur_dest'] = $destWidth;
		$ret['hauteur_dest'] = $destHeight;
		$effet = "L{$destWidth}xH$destHeight";
		$cache = "cache-vignettes";
		$fichier_dest = basename($fichier_dest);
		if (($ret['largeur']<=$maxWidth)&&($ret['hauteur']<=$maxHeight)){
			// on garde la terminaison initiale car image simplement copiee
			// et on postfixe son nom avec un md5 du path
			$terminaison_dest = $terminaison;
			$fichier_dest .= '-'.substr(md5("$fichier"),0,5);
		}
		else
			$fichier_dest .= '-'.substr(md5("$fichier-$effet"),0,5);
		$cache = sous_repertoire(_DIR_VAR, $cache);
		$cache = sous_repertoire($cache, $effet);
		# cherche un cache existant
		/*foreach (array('gif','jpg','png') as $fmt)
			if (@file_exists($cache . $fichier_dest . '.' . $fmt)) {
				$terminaison_dest = $fmt;
			}*/
	}
	else 	{
		$fichier_dest = md5("$fichier-$effet");
		$cache = sous_repertoire(_DIR_VAR, $cache);
	}
	
	$fichier_dest = $cache . $fichier_dest . "." .$terminaison_dest;
	
	$GLOBALS["images_calculees"][] =  $fichier_dest;

	$creer = true;
	// si recalcul des images demande, recalculer chaque image une fois
	if (defined('_VAR_IMAGES') AND _VAR_IMAGES AND !isset($images_recalcul[$fichier_dest])){
		$images_recalcul[$fichier_dest] = true;
	}
	else {
		if (@file_exists($f = $fichier_dest)){
			if (filemtime($f)>=$date_src)
				$creer = false;
		}
		else if (@file_exists($f = "$fichier_dest.src")
		  AND lire_fichier($f,$valeurs)
		  AND $valeurs=unserialize($valeurs)
			AND $valeurs["date"]>=$date_src)
				$creer = false;
	}
	if ($creer) {
		if (!@file_exists($fichier)) {
			if (!@file_exists("$fichier.src")) {
				spip_log("Image absente : $fichier");
				return false;
			}
			# on reconstruit l'image source absente a partir de la chaine des .src
			reconstruire_image_intermediaire($fichier);
		}
	}

	if ($creer)
		spip_log("filtre image ".($fonction_creation?reset($fonction_creation):'')."[$effet] sur $fichier","images"._LOG_DEBUG);

	// TODO: si une image png est nommee .jpg, le reconnaitre avec le bon $f
	$ret["fonction_imagecreatefrom"] = "_imagecreatefrom".$term_fonction;
	$ret["fichier"] = $fichier;
	$ret["fonction_image"] = "_image_image".$terminaison_dest;
	$ret["fichier_dest"] = $fichier_dest;
	$ret["format_source"] = ($terminaison != 'jpeg' ? $terminaison : 'jpg');
	$ret["format_dest"] = $terminaison_dest;
	$ret["date_src"] = $date_src;
	$ret["creer"] = $creer;
	$ret["class"] = extraire_attribut($img, 'class');
	$ret["alt"] = extraire_attribut($img, 'alt');
	$ret["style"] = extraire_attribut($img, 'style');
	$ret["tag"] = $img;
	if ($fonction_creation){
		$ret["reconstruction"] = $fonction_creation;
		# ecrire ici comment creer le fichier, car il est pas sur qu'on l'ecrira reelement 
		# cas de image_reduire qui finalement ne reduit pas l'image source
		# ca evite d'essayer de le creer au prochain hit si il n'est pas la
		#ecrire_fichier($ret['fichier_dest'].'.src',serialize($ret),true);
	}

	$ret = pipeline('image_preparer_filtre',array(
			'args'=>array(
				'img'=>$img,
				'effet'=>$effet,
				'forcer_format'=>$forcer_format,
				'fonction_creation'=>$fonction_creation,
				'find_in_path'=>$find_in_path,
			),
			'data'=>$ret)
	);

	// une globale pour le debug en cas de crash memoire
	$GLOBALS["derniere_image_calculee"] = $ret;

	if (!function_exists($ret["fonction_imagecreatefrom"])) return false;
	return $ret;
}

/**
 * Crée une image depuis un fichier ou une URL
 * Utilise les fonctions spécifiques GD.
 *
 * @param string $filename
 * 		Le path vers l'image à traiter (par exemple : IMG/distant/jpg/image.jpg
 * 		ou local/cache-vignettes/L180xH51/image.jpg).
 * @return ressource
 * 		Une ressource de type Image GD.
 */
function _imagecreatefromjpeg($filename){
	$img = @imagecreatefromjpeg($filename);
	if (!$img) {
		spip_log("Erreur lecture imagecreatefromjpeg $filename",_LOG_CRITIQUE);
		erreur_squelette("Erreur lecture imagecreatefromjpeg $filename");
		$img = imagecreate(10,10);
	}
	return $img;
}

/**
 * Crée une image depuis un fichier ou une URL
 * Utilise les fonctions spécifiques GD.
 *
 * @param string $filename
 * 		Le path vers l'image à traiter (par exemple : IMG/distant/png/image.png
 * 		ou local/cache-vignettes/L180xH51/image.png).
 * @return ressource
 * 		Une ressource de type Image GD.
 */
function _imagecreatefrompng($filename){
	$img = @imagecreatefrompng($filename);
	if (!$img) {
		spip_log("Erreur lecture imagecreatefrompng $filename",_LOG_CRITIQUE);
		erreur_squelette("Erreur lecture imagecreatefrompng $filename");
		$img = imagecreate(10,10);
	}
	return $img;
}

/**
 * Crée une image depuis un fichier ou une URL
 * Utilise les fonctions spécifiques GD.
 *
 * @param string $filename
 * 		Le path vers l'image à traiter (par exemple : IMG/distant/gif/image.gif
 * 		ou local/cache-vignettes/L180xH51/image.gif).
 * @return ressource
 * 		Une ressource de type Image GD.
 */
function _imagecreatefromgif($filename){
	$img = @imagecreatefromgif($filename);
	if (!$img) {
		spip_log("Erreur lecture imagecreatefromgif $filename",_LOG_CRITIQUE);
		erreur_squelette("Erreur lecture imagecreatefromgif $filename");
		$img = imagecreate(10,10);
	}
	return $img;
}

/**
 * Affiche ou sauvegarde une image au format PNG
 * Utilise les fonctions spécifiques GD.
 *
 * @param ressource $img
 * 		Une ressource de type Image GD.
 * @param string $fichier
 * 		Le path vers l'image (ex : local/cache-vignettes/L180xH51/image.png).
 * @return bool
 * 		false si l'image créée a une largeur nulle ou n'existe pas ;
 * 		true si une image est bien retournée.
 */
function _image_imagepng($img, $fichier) {
	if (!function_exists('imagepng')) return false;
	$tmp = $fichier.".tmp";
	$ret = imagepng($img,$tmp);
	
	if(file_exists($tmp)){
		$taille_test = getimagesize($tmp);
		if ($taille_test[0] < 1) return false;
	
		spip_unlink($fichier); // le fichier peut deja exister
		@rename($tmp, $fichier);
		return $ret;
	}
	return false;
}

/**
 * Affiche ou sauvegarde une image au format GIF
 * Utilise les fonctions spécifiques GD.
 *
 * @param ressource $img
 * 		Une ressource de type Image GD.
 * @param string $fichier
 * 		Le path vers l'image (ex : local/cache-vignettes/L180xH51/image.gif).
 * @return bool
 * 		false si l'image créée a une largeur nulle ou n'existe pas;
 * 		true si une image est bien retournée.
 */
function _image_imagegif($img,$fichier) {
	if  (!function_exists('imagegif')) return false;
	$tmp = $fichier.".tmp";
	$ret = imagegif($img,$tmp);
	
	if(file_exists($tmp)){
		$taille_test = getimagesize($tmp);
		if ($taille_test[0] < 1) return false;

		spip_unlink($fichier); // le fichier peut deja exister
		@rename($tmp, $fichier);
		return $ret;
	}
	return false;
}

/**
 * Affiche ou sauvegarde une image au format JPG
 * Utilise les fonctions spécifiques GD.
 *
 * @param ressource $img
 * 		Une ressource de type Image GD.
 * @param string $fichier
 * 		Le path vers l'image (ex : local/cache-vignettes/L180xH51/image.jpg).
 * @param int $qualite
 * 		Le niveau de qualité du fichier résultant : de 0 (pire qualité, petit
 * 		fichier) à 100 (meilleure qualité, gros fichier). Par défaut, prend la
 * 		valeur (85) de la constante _IMG_GD_QUALITE (modifiable depuis
 * 		mes_options.php).
 * @return bool
 * 		false si l'image créée a une largeur nulle ou n'existe pas ;
 * 		true si une image est bien retournée.
 */
function _image_imagejpg($img,$fichier,$qualite=_IMG_GD_QUALITE) {
	if (!function_exists('imagejpeg')) return false;
	$tmp = $fichier.".tmp";
	$ret = imagejpeg($img,$tmp, $qualite);
	
	if(file_exists($tmp)){
		$taille_test = getimagesize($tmp);
		if ($taille_test[0] < 1) return false;
	
		spip_unlink($fichier); // le fichier peut deja exister
		@rename($tmp, $fichier);
		return $ret;
	}
	return false;
}

/**
 * Crée un fichier-image au format ICO
 * Utilise les fonctions de la classe phpthumb_functions.
 *
 * @param ressource $img
 * 		Une ressource de type Image GD.
 * @param string $fichier
 * 		Le path vers l'image (ex : local/cache-vignettes/L180xH51/image.jpg).
 * @return bool
 * 		true si le fichier a bien été créé ; false sinon.
 */
function _image_imageico($img, $fichier) {
	$gd_image_array = array($img);

	return ecrire_fichier($fichier, phpthumb_functions::GD2ICOstring($gd_image_array));
}

/**
 * Finalise le traitement GD
 * Crée un fichier_image temporaire .src ou vérifie que le fichier_image
 * définitif a bien été créé.
 *
 * @param ressource $img
 * 		Une ressource de type Image GD.
 * @param array $valeurs
 * 		Un tableau des informations (tailles, traitement, path...) accompagnant
 * 		l'image.
 * @param int $qualite
 * 		N'est utilisé que pour les images jpg.
 * 		Le niveau de qualité du fichier résultant : de 0 (pire qualité, petit
 * 		fichier) à 100 (meilleure qualité, gros fichier). Par défaut, prend la
 * 		valeur (85) de la constante _IMG_GD_QUALITE (modifiable depuis
 * 		mes_options.php).
 * @return bool
 * 		true si le traitement GD s'est bien finalisé ;
 * 		false sinon.
 */
function _image_gd_output($img,$valeurs, $qualite=_IMG_GD_QUALITE){
	$fonction = "_image_image".$valeurs['format_dest'];
	$ret = false;
	#un flag pour reperer les images gravees
	$lock = 
		!statut_effacer_images_temporaires('get') // si la fonction n'a pas ete activee, on grave tout
	  OR (@file_exists($valeurs['fichier_dest']) AND !@file_exists($valeurs['fichier_dest'].'.src'));
	if (
	     function_exists($fonction) 
			  && ($ret = $fonction($img,$valeurs['fichier_dest'],$qualite)) # on a reussi a creer l'image
			  && isset($valeurs['reconstruction']) # et on sait comment la resonctruire le cas echeant
			  && !$lock
	  )
		if (@file_exists($valeurs['fichier_dest'])){
			// dans tous les cas mettre a jour la taille de l'image finale
			list ($valeurs["hauteur_dest"],$valeurs["largeur_dest"]) = taille_image($valeurs['fichier_dest']);
			$valeurs['date'] = @filemtime($valeurs['fichier_dest']); // pour la retrouver apres disparition
			ecrire_fichier($valeurs['fichier_dest'].'.src',serialize($valeurs),true);
		}
	return $ret;
}

// http://doc.spip.org/@reconstruire_image_intermediaire
function reconstruire_image_intermediaire($fichier_manquant){
	$reconstruire = array();
	$fichier = $fichier_manquant;
	while (strpos($fichier,"://")===false
		and !@file_exists($fichier)
		AND lire_fichier($src = "$fichier.src",$source)
		AND $valeurs=unserialize($source)
    AND ($fichier = $valeurs['fichier']) # l'origine est connue (on ne verifie pas son existence, qu'importe ...)
    ) {
			spip_unlink($src); // si jamais on a un timeout pendant la reconstruction, elle se fera naturellement au hit suivant
			$reconstruire[] = $valeurs['reconstruction'];
   }
	while (count($reconstruire)){
		$r = array_pop($reconstruire);
		$fonction = $r[0];
		$args = $r[1];
		call_user_func_array($fonction, $args);
	}
	// cette image intermediaire est commune a plusieurs series de filtre, il faut la conserver
	// mais l'on peut nettoyer les miettes de sa creation
	ramasse_miettes($fichier_manquant);
}

/**
 * Indique qu'un fichier d'image calculé est à conserver
 *
 * Permet de rendre une image définitive et de supprimer les images
 * intermédiaires à son calcul.
 *
 * Supprime le fichier de contrôle de l’image cible (le $fichier.src)
 * ce qui indique que l'image est définitive.
 *
 * Remonte ensuite la chaîne des fichiers de contrôle pour supprimer
 * les images temporaires (mais laisse les fichiers de contrôle permettant
 * de les reconstruire).
 *
 * @param string $fichier
 *     Chemin du fichier d'image calculé
 **/
function ramasse_miettes($fichier) {
	if (strpos($fichier,"://")!==false
		or !lire_fichier($src = "$fichier.src", $source)
		or !$valeurs = unserialize($source)
	) {
		return;
	}
	spip_unlink($src); # on supprime la reference a sa source pour marquer cette image comme non intermediaire
	while (
	     ($fichier = $valeurs['fichier']) # l'origine est connue (on ne verifie pas son existence, qu'importe ...)
		AND (substr($fichier,0,strlen(_DIR_VAR))==_DIR_VAR) # et est dans local
		AND (lire_fichier($src = "$fichier.src",$source)) # le fichier a une source connue (c'est donc une image calculee intermediaire)
		AND ($valeurs=unserialize($source))  # et valide
		) {
		# on efface le fichier
		spip_unlink($fichier);
		# mais laisse le .src qui permet de savoir comment reconstruire l'image si besoin
		#spip_unlink($src);
	}
}

// http://doc.spip.org/@image_graver
function image_graver($img){
	// appeler le filtre post_image_filtrer qui permet de faire
	// des traitements auto a la fin d'une serie de filtres
	$img = pipeline('post_image_filtrer',$img);

	$fichier = extraire_attribut($img, 'src');
	if (($p=strpos($fichier,'?'))!==FALSE)
		$fichier=substr($fichier,0,$p);
	if (strlen($fichier) < 1)
		$fichier = $img;
	# si jamais le fichier final n'a pas ete calcule car suppose temporaire
	# et qu'il ne s'agit pas d'une URL
	if (strpos($fichier,"://")===false and !@file_exists($fichier)) {
		reconstruire_image_intermediaire($fichier);
	}
	ramasse_miettes($fichier);
	return $img; // on ne change rien
}

// Transforme une image a palette indexee (256 couleurs max) en "vraies" couleurs RGB
// Existe seulement pour compatibilite avec PHP < 5.5
// http://doc.spip.org/@imagepalettetotruecolor
if (!function_exists("imagepalettetotruecolor")) {
function imagepalettetotruecolor(&$img) {
	if ($img AND !imageistruecolor($img) AND function_exists('imagecreatetruecolor')) {
		$w = imagesx($img);
		$h = imagesy($img);
		$img1 = imagecreatetruecolor($w,$h);
		//Conserver la transparence si possible
		if(function_exists('ImageCopyResampled')) {
			if (function_exists("imageAntiAlias")) imageAntiAlias($img1,true); 
			@imagealphablending($img1, false); 
			@imagesavealpha($img1,true); 
			@ImageCopyResampled($img1, $img, 0, 0, 0, 0, $w, $h, $w, $h);
		} else {
			imagecopy($img1,$img,0,0,0,0,$w,$h);
		}

		$img = $img1;
	}
}
}

// http://doc.spip.org/@image_tag_changer_taille
function _image_tag_changer_taille($tag,$width,$height,$style=false){
	if ($style===false) $style = extraire_attribut($tag,'style');
	// enlever le width et height du style
	$style = preg_replace(",(^|;)\s*(width|height)\s*:\s*[^;]+,ims","",$style);
	if ($style AND $style{0}==';') $style=substr($style,1);
	// mettre des attributs de width et height sur les images, 
	// ca accelere le rendu du navigateur
	// ca permet aux navigateurs de reserver la bonne taille 
	// quand on a desactive l'affichage des images.
	$tag = inserer_attribut($tag,'width',$width);
	$tag = inserer_attribut($tag,'height',$height);
	
	// Ancien style inline pour IE6 mais qui casse la possibilité de surcharger en CSS
	//$style = "height:".$height."px;width:".$width."px;".$style;
	
	// attributs deprecies. Transformer en CSS
	if ($espace = extraire_attribut($tag, 'hspace')){
		$style = "margin:${espace}px;".$style;
		$tag = inserer_attribut($tag,'hspace','');
	}
	$tag = inserer_attribut($tag,'style',$style, true, $style ? false : true);
	return $tag;
}

// function d'ecriture du de la balise img en sortie des filtre image
// reprend le tag initial et surcharge les tags modifies
function _image_ecrire_tag($valeurs,$surcharge=array()){
	$valeurs = pipeline('image_ecrire_tag_preparer',$valeurs);

	$tag = 	str_replace(">","/>",str_replace("/>",">",$valeurs['tag'])); // fermer les tags img pas bien fermes;
	
	// le style
	$style = $valeurs['style'];
	if (isset($surcharge['style'])){
		$style = $surcharge['style'];
		unset($surcharge['style']);
	}
	
	// traiter specifiquement la largeur et la hauteur
	$width = $valeurs['largeur'];
	if (isset($surcharge['width'])){
		$width = $surcharge['width'];
		unset($surcharge['width']);
	}
	$height = $valeurs['hauteur'];
	if (isset($surcharge['height'])){
		$height = $surcharge['height'];
		unset($surcharge['height']);
	}

	$tag = _image_tag_changer_taille($tag,$width,$height,$style);
	// traiter specifiquement le src qui peut etre repris dans un onmouseout
	// on remplace toute les ref a src dans le tag
	$src = extraire_attribut($tag,'src');
	if (isset($surcharge['src'])){
		$tag = str_replace($src,$surcharge['src'],$tag);
		// si il y a des & dans src, alors ils peuvent provenir d'un &amp
		// pas garanti comme methode, mais mieux que rien
		if (strpos($src,'&') !== false)
			$tag = str_replace(str_replace("&","&amp;",$src),$surcharge['src'],$tag);
		$src = $surcharge['src'];
		unset($surcharge['src']);
	}

	$class = $valeurs['class'];
	if (isset($surcharge['class'])){
		$class = $surcharge['class'];
		unset($surcharge['class']);
	}
	if(strlen($class))
		$tag = inserer_attribut($tag,'class',$class);

	if (count($surcharge))
		foreach($surcharge as $attribut=>$valeur)
			$tag = inserer_attribut($tag,$attribut,$valeur);
	
	$tag = pipeline('image_ecrire_tag_finir',
		array(
			'args' => array(
				'valeurs' => $valeurs,
				'surcharge' => $surcharge,
			),
			'data' => $tag
		)
	);

	return $tag;
}

function _image_creer_vignette($valeurs, $maxWidth, $maxHeight, $process='AUTO', $force=false, $test_cache_only = false) {
	// ordre de preference des formats graphiques pour creer les vignettes
	// le premier format disponible, selon la methode demandee, est utilise
	$image = $valeurs['fichier'];
	$format = $valeurs['format_source'];
	$destdir = dirname($valeurs['fichier_dest']);
	$destfile = basename($valeurs['fichier_dest'],".".$valeurs["format_dest"]);
	
	$format_sortie = $valeurs['format_dest'];
	
	// liste des formats qu'on sait lire
	$img = isset($GLOBALS['meta']['formats_graphiques'])
	  ? (strpos($GLOBALS['meta']['formats_graphiques'], $format)!==false)
	  : false;

	// si le doc n'est pas une image, refuser
	if (!$force AND !$img) return;
	$destination = "$destdir/$destfile";

	// chercher un cache
	$vignette = '';
	if ($test_cache_only AND !$vignette) return;

	// utiliser le cache ?
	if (!$test_cache_only)
	if ($force OR !$vignette OR (@filemtime($vignette) < @filemtime($image))) {

		$creation = true;
		// calculer la taille
		if (($srcWidth=$valeurs['largeur']) && ($srcHeight=$valeurs['hauteur'])){
			if (!($destWidth=$valeurs['largeur_dest']) || !($destHeight=$valeurs['hauteur_dest']))
				list ($destWidth,$destHeight) = _image_ratio($valeurs['largeur'], $valeurs['hauteur'], $maxWidth, $maxHeight);
		}
		elseif ($process == 'convert' OR $process == 'imagick') {
			$destWidth = $maxWidth;
			$destHeight = $maxHeight;
		} else {
			spip_log("echec $process sur $image");
			return;
		}

		// Si l'image est de la taille demandee (ou plus petite), simplement
		// la retourner
		if ($srcWidth
		AND $srcWidth <= $maxWidth AND $srcHeight <= $maxHeight) {
			$vignette = $destination.'.'.$format;
			@copy($image, $vignette);
		}
		// imagemagick en ligne de commande
		else if ($process == 'convert') {
			define('_CONVERT_COMMAND', 'convert');
			define ('_RESIZE_COMMAND', _CONVERT_COMMAND.' -quality '._IMG_CONVERT_QUALITE.' -resize %xx%y! %src %dest');
			$vignette = $destination.".".$format_sortie;
			$commande = str_replace(
				array('%x', '%y', '%src', '%dest'),
				array(
					$destWidth,
					$destHeight,
					escapeshellcmd($image),
					escapeshellcmd($vignette)
				),
				_RESIZE_COMMAND);
			spip_log($commande);
			exec($commande);
			if (!@file_exists($vignette)) {
				spip_log("echec convert sur $vignette");
				return;	// echec commande
			}
		}
		else
		// imagick (php4-imagemagick)
		if ($process == 'imagick') {

			$vignette = "$destination.".$format_sortie;

			$imagick = new Imagick();
			$imagick->readImage($image);
			$imagick->resizeImage($destWidth, $destHeight, Imagick::FILTER_LANCZOS, 1 );//, IMAGICK_FILTER_LANCZOS, _IMG_IMAGICK_QUALITE / 100);
			$imagick->writeImage($vignette);

			if (!@file_exists($vignette)) {
				spip_log("echec imagick sur $vignette");
				return;
			}
		}
		else
		// netpbm
		if ($process == "netpbm") {
			define('_PNMSCALE_COMMAND', 'pnmscale'); // chemin a changer dans mes_options
			if (_PNMSCALE_COMMAND == '') return;
			$vignette = $destination.".".$format_sortie;
			$pnmtojpeg_command = str_replace("pnmscale", "pnmtojpeg", _PNMSCALE_COMMAND);
			if ($format == "jpg") {
				
				$jpegtopnm_command = str_replace("pnmscale", "jpegtopnm", _PNMSCALE_COMMAND);
				exec("$jpegtopnm_command $image | "._PNMSCALE_COMMAND." -width $destWidth | $pnmtojpeg_command > $vignette");
				if (!($s = @filesize($vignette)))
					spip_unlink($vignette);
				if (!@file_exists($vignette)) {
					spip_log("echec netpbm-jpg sur $vignette");
					return;
				}
			} else if ($format == "gif") {
				$giftopnm_command = str_replace("pnmscale", "giftopnm", _PNMSCALE_COMMAND);
				exec("$giftopnm_command $image | "._PNMSCALE_COMMAND." -width $destWidth | $pnmtojpeg_command > $vignette");
				if (!($s = @filesize($vignette)))
					spip_unlink($vignette);
				if (!@file_exists($vignette)) {
					spip_log("echec netpbm-gif sur $vignette");
					return;
				}
			} else if ($format == "png") {
				$pngtopnm_command = str_replace("pnmscale", "pngtopnm", _PNMSCALE_COMMAND);
				exec("$pngtopnm_command $image | "._PNMSCALE_COMMAND." -width $destWidth | $pnmtojpeg_command > $vignette");
				if (!($s = @filesize($vignette)))
					spip_unlink($vignette);
				if (!@file_exists($vignette)) {
					spip_log("echec netpbm-png sur $vignette");
					return;
				}
			}
		}
		// gd ou gd2
		else if ($process == 'gd1' OR $process == 'gd2') {
			if (_IMG_GD_MAX_PIXELS && $srcWidth*$srcHeight>_IMG_GD_MAX_PIXELS){
				spip_log("vignette gd1/gd2 impossible : ".$srcWidth*$srcHeight."pixels");
				return;
			}
			$destFormat = $format_sortie;
			if (!$destFormat) {
				spip_log("pas de format pour $image");
				return;
			}

			$fonction_imagecreatefrom = $valeurs['fonction_imagecreatefrom'];
			if (!function_exists($fonction_imagecreatefrom))
				return '';
			$srcImage = @$fonction_imagecreatefrom($image);
			if (!$srcImage) { 
				spip_log("echec gd1/gd2"); 
				return; 
			} 

			// Initialisation de l'image destination 
				if ($process == 'gd2' AND $destFormat != "gif") 
				$destImage = ImageCreateTrueColor($destWidth, $destHeight); 
			if (!$destImage) 
				$destImage = ImageCreate($destWidth, $destHeight); 

			// Recopie de l'image d'origine avec adaptation de la taille 
			$ok = false; 
			if (($process == 'gd2') AND function_exists('ImageCopyResampled')) { 
				if ($format == "gif") { 
					// Si un GIF est transparent, 
					// fabriquer un PNG transparent  
					$transp = imagecolortransparent($srcImage); 
					if ($transp > 0) $destFormat = "png"; 
				}
				if ($destFormat == "png") { 
					// Conserver la transparence 
					if (function_exists("imageAntiAlias")) imageAntiAlias($destImage,true); 
					@imagealphablending($destImage, false); 
					@imagesavealpha($destImage,true); 
				}
				$ok = @ImageCopyResampled($destImage, $srcImage, 0, 0, 0, 0, $destWidth, $destHeight, $srcWidth, $srcHeight);
			}
			if (!$ok)
				$ok = ImageCopyResized($destImage, $srcImage, 0, 0, 0, 0, $destWidth, $destHeight, $srcWidth, $srcHeight);

			// Sauvegarde de l'image destination
			$valeurs['fichier_dest'] = $vignette = "$destination.$destFormat";
			$valeurs['format_dest'] = $format = $destFormat;
			_image_gd_output($destImage,$valeurs);

			if ($srcImage)
				ImageDestroy($srcImage);
			ImageDestroy($destImage);
		}
	}
	$size = @getimagesize($vignette);
	// Gaffe: en safe mode, pas d'acces a la vignette,
	// donc risque de balancer "width='0'", ce qui masque l'image sous MSIE
	if ($size[0] < 1) $size[0] = $destWidth;
	if ($size[1] < 1) $size[1] = $destHeight;
	
	$retour['width'] = $largeur = $size[0];
	$retour['height'] = $hauteur = $size[1];
	
	$retour['fichier'] = $vignette;
	$retour['format'] = $format;
	$retour['date'] = @filemtime($vignette);
	
	// renvoyer l'image
	return $retour;
}

// Calculer le ratio
// http://doc.spip.org/@image_ratio
function _image_ratio ($srcWidth, $srcHeight, $maxWidth, $maxHeight) {
	$ratioWidth = $srcWidth/$maxWidth;
	$ratioHeight = $srcHeight/$maxHeight;
	
	if ($ratioWidth <=1 AND $ratioHeight <=1) {
		$destWidth = $srcWidth;
		$destHeight = $srcHeight;
	} else if ($ratioWidth < $ratioHeight) {
		$destWidth = $srcWidth/$ratioHeight;
		$destHeight = $maxHeight;
	}
	else {
		$destWidth = $maxWidth;
		$destHeight = $srcHeight/$ratioWidth;
	}
	return array (ceil($destWidth), ceil($destHeight),
		max($ratioWidth,$ratioHeight));
}

// Calculer le ratio ajuste sur la plus petite dimension
// http://doc.spip.org/@ratio_passe_partout
function ratio_passe_partout ($srcWidth, $srcHeight, $maxWidth, $maxHeight) {
	$ratioWidth = $srcWidth/$maxWidth;
	$ratioHeight = $srcHeight/$maxHeight;

	if ($ratioWidth <=1 AND $ratioHeight <=1) {
		$destWidth = $srcWidth;
		$destHeight = $srcHeight;
	} else if ($ratioWidth > $ratioHeight) {
		$destWidth = $srcWidth/$ratioHeight;
		$destHeight = $maxHeight;
	}
	else {
		$destWidth = $maxWidth;
		$destHeight = $srcHeight/$ratioWidth;
	}
	return array (ceil($destWidth), ceil($destHeight),
		min($ratioWidth,$ratioHeight));
}

// http://doc.spip.org/@process_image_reduire
function process_image_reduire($fonction,$img,$taille,$taille_y,$force,$cherche_image,$process){
	$image = false;
	if (($process == 'AUTO') AND isset($GLOBALS['meta']['image_process']))
		$process = $GLOBALS['meta']['image_process'];
	# determiner le format de sortie
	$format_sortie = false; // le choix par defaut sera bon
	if ($process == "netpbm") $format_sortie = "jpg";
	else if ($process == 'gd1' OR $process == 'gd2') {
		$image = _image_valeurs_trans($img, "reduire-{$taille}-{$taille_y}",$format_sortie,$fonction);

		// on verifie que l'extension choisie est bonne (en principe oui)
		$gd_formats = explode(',',$GLOBALS['meta']["gd_formats"]);
		if (!in_array($image['format_dest'],$gd_formats)
		  OR ($image['format_dest']=='gif' AND !function_exists('ImageGif'))
		  ) {
			if ($image['format_source'] == 'jpg')
				$formats_sortie = array('jpg','png','gif');
			else // les gif sont passes en png preferentiellement pour etre homogene aux autres filtres images
				$formats_sortie = array('png','jpg','gif');
			// Choisir le format destination
			// - on sauve de preference en JPEG (meilleure compression)
			// - pour le GIF : les GD recentes peuvent le lire mais pas l'ecrire
			# bug : gd_formats contient la liste des fichiers qu'on sait *lire*,
			# pas *ecrire*
			$format_sortie = "";
			foreach ($formats_sortie as $fmt) {
				if (in_array($fmt, $gd_formats)) {
					if ($fmt <> "gif" OR function_exists('ImageGif'))
						$format_sortie = $fmt;
					break;
				}
			}
			$image = false;
		}
	}

	if (!$image)
		$image = _image_valeurs_trans($img, "reduire-{$taille}-{$taille_y}",$format_sortie,$fonction);

	if (!$image OR !$image['largeur'] OR !$image['hauteur']){
		spip_log("image_reduire_src:pas de version locale de $img");
		// on peut resizer en mode html si on dispose des elements
		if ($srcw = extraire_attribut($img, 'width')
		AND $srch = extraire_attribut($img, 'height')) {
			list($w,$h) = _image_ratio($srcw, $srch, $taille, $taille_y);
			return _image_tag_changer_taille($img,$w,$h);
		}
		// la on n'a pas d'infos sur l'image source... on refile le truc a css
		// sous la forme style='max-width: NNpx;'
		return inserer_attribut($img, 'style',
			"max-width: ${taille}px; max-height: ${taille_y}px");
	}

	// si l'image est plus petite que la cible retourner une copie cachee de l'image
	if (($image['largeur']<=$taille)&&($image['hauteur']<=$taille_y)){
		if ($image['creer']){
			@copy($image['fichier'], $image['fichier_dest']);
		}
		return _image_ecrire_tag($image,array('src'=>$image['fichier_dest']));
	}

	if ($image['creer']==false && !$force)
		return _image_ecrire_tag($image,array('src'=>$image['fichier_dest'],'width'=>$image['largeur_dest'],'height'=>$image['hauteur_dest']));

	if (in_array($image["format_source"],array('jpg','gif','png'))){
		$destWidth = $image['largeur_dest'];
		$destHeight = $image['hauteur_dest'];
		$logo = $image['fichier'];
		$date = $image["date_src"];
		$preview = _image_creer_vignette($image, $taille, $taille_y,$process,$force);

		if ($preview && $preview['fichier']) {
			$logo = $preview['fichier'];
			$destWidth = $preview['width'];
			$destHeight = $preview['height'];
			$date = $preview['date'];
		}
		// dans l'espace prive mettre un timestamp sur l'adresse 
		// de l'image, de facon a tromper le cache du navigateur
		// quand on fait supprimer/reuploader un logo
		// (pas de filemtime si SAFE MODE)
		$date = test_espace_prive() ? ('?'.$date) : '';
		return _image_ecrire_tag($image,array('src'=>"$logo$date",'width'=>$destWidth,'height'=>$destHeight));
	}
	else
		# SVG par exemple ? BMP, tiff ... les redacteurs osent tout!
		return $img;
}

/**
 * Produire des fichiers au format .ico
 * avec du code recupere de :
 * phpThumb() by James Heinrich <info@silisoftware.com>
 * available at http://phpthumb.sourceforge.net
 *
 * Class phpthumb_functions
 */
class phpthumb_functions {

	/**
	 * @param ressource $img
	 * @param int $x
	 * @param int $y
	 * @return array|bool
	 */
	public static function GetPixelColor(&$img, $x, $y) {
		if (!is_resource($img)) {
			return false;
		}
		return @ImageColorsForIndex($img, @ImageColorAt($img, $x, $y));
	}

	/**
	 * @param int $number
	 * @param int $minbytes
	 * @return string
	 */
	public static function LittleEndian2String($number, $minbytes=1) {
		$intstring = '';
		while ($number > 0) {
			$intstring = $intstring.chr($number & 255);
			$number >>= 8;
		}
		return str_pad($intstring, $minbytes, "\x00", STR_PAD_RIGHT);
	}

	/**
	 * @param array $gd_image_array
	 * @return string
	 */
	public static function GD2ICOstring(&$gd_image_array) {
		foreach ($gd_image_array as $key => $gd_image) {

			$ImageWidths[$key]  = ImageSX($gd_image);
			$ImageHeights[$key] = ImageSY($gd_image);
			$bpp[$key]		  = ImageIsTrueColor($gd_image) ? 32 : 24;
			$totalcolors[$key]  = ImageColorsTotal($gd_image);

			$icXOR[$key] = '';
			for ($y = $ImageHeights[$key] - 1; $y >= 0; $y--) {
				for ($x = 0; $x < $ImageWidths[$key]; $x++) {
					$argb = phpthumb_functions::GetPixelColor($gd_image, $x, $y);
					$a = round(255 * ((127 - $argb['alpha']) / 127));
					$r = $argb['red'];
					$g = $argb['green'];
					$b = $argb['blue'];

					if ($bpp[$key] == 32) {
						$icXOR[$key] .= chr($b).chr($g).chr($r).chr($a);
					} elseif ($bpp[$key] == 24) {
						$icXOR[$key] .= chr($b).chr($g).chr($r);
					}

					if ($a < 128) {
						@$icANDmask[$key][$y] .= '1';
					} else {
						@$icANDmask[$key][$y] .= '0';
					}
				}
				// mask bits are 32-bit aligned per scanline
				while (strlen($icANDmask[$key][$y]) % 32) {
					$icANDmask[$key][$y] .= '0';
				}
			}
			$icAND[$key] = '';
			foreach ($icANDmask[$key] as $y => $scanlinemaskbits) {
				for ($i = 0; $i < strlen($scanlinemaskbits); $i += 8) {
					$icAND[$key] .= chr(bindec(str_pad(substr($scanlinemaskbits, $i, 8), 8, '0', STR_PAD_LEFT)));
				}
			}

		}

		foreach ($gd_image_array as $key => $gd_image) {
			$biSizeImage = $ImageWidths[$key] * $ImageHeights[$key] * ($bpp[$key] / 8);

			// BITMAPINFOHEADER - 40 bytes
			$BitmapInfoHeader[$key]  = '';
			$BitmapInfoHeader[$key] .= "\x28\x00\x00\x00";							  // DWORD  biSize;
			$BitmapInfoHeader[$key] .= phpthumb_functions::LittleEndian2String($ImageWidths[$key], 4);	  // LONG   biWidth;
			// The biHeight member specifies the combined
			// height of the XOR and AND masks.
			$BitmapInfoHeader[$key] .= phpthumb_functions::LittleEndian2String($ImageHeights[$key] * 2, 4); // LONG   biHeight;
			$BitmapInfoHeader[$key] .= "\x01\x00";									  // WORD   biPlanes;
			   $BitmapInfoHeader[$key] .= chr($bpp[$key])."\x00";						  // wBitCount;
			$BitmapInfoHeader[$key] .= "\x00\x00\x00\x00";							  // DWORD  biCompression;
			$BitmapInfoHeader[$key] .= phpthumb_functions::LittleEndian2String($biSizeImage, 4);			// DWORD  biSizeImage;
			$BitmapInfoHeader[$key] .= "\x00\x00\x00\x00";							  // LONG   biXPelsPerMeter;
			$BitmapInfoHeader[$key] .= "\x00\x00\x00\x00";							  // LONG   biYPelsPerMeter;
			$BitmapInfoHeader[$key] .= "\x00\x00\x00\x00";							  // DWORD  biClrUsed;
			$BitmapInfoHeader[$key] .= "\x00\x00\x00\x00";							  // DWORD  biClrImportant;
		}


		$icondata  = "\x00\x00";									  // idReserved;   // Reserved (must be 0)
		$icondata .= "\x01\x00";									  // idType;	   // Resource Type (1 for icons)
		$icondata .= phpthumb_functions::LittleEndian2String(count($gd_image_array), 2);  // idCount;	  // How many images?

		$dwImageOffset = 6 + (count($gd_image_array) * 16);
		foreach ($gd_image_array as $key => $gd_image) {
			// ICONDIRENTRY   idEntries[1]; // An entry for each image (idCount of 'em)

			$icondata .= chr($ImageWidths[$key]);					 // bWidth;		  // Width, in pixels, of the image
			$icondata .= chr($ImageHeights[$key]);					// bHeight;		 // Height, in pixels, of the image
			$icondata .= chr($totalcolors[$key]);					 // bColorCount;	 // Number of colors in image (0 if >=8bpp)
			$icondata .= "\x00";									  // bReserved;	   // Reserved ( must be 0)

			$icondata .= "\x01\x00";								  // wPlanes;		 // Color Planes
			$icondata .= chr($bpp[$key])."\x00";					  // wBitCount;	   // Bits per pixel

			$dwBytesInRes = 40 + strlen($icXOR[$key]) + strlen($icAND[$key]);
			$icondata .= phpthumb_functions::LittleEndian2String($dwBytesInRes, 4);	   // dwBytesInRes;	// How many bytes in this resource?

			$icondata .= phpthumb_functions::LittleEndian2String($dwImageOffset, 4);	  // dwImageOffset;   // Where in the file is this image?
			$dwImageOffset += strlen($BitmapInfoHeader[$key]);
			$dwImageOffset += strlen($icXOR[$key]);
			$dwImageOffset += strlen($icAND[$key]);
		}

		foreach ($gd_image_array as $key => $gd_image) {
			$icondata .= $BitmapInfoHeader[$key];
			$icondata .= $icXOR[$key];
			$icondata .= $icAND[$key];
		}

		return $icondata;
	}

}

?>
filtres_boites.php000066600000006302152525475610010313 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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



/**
 * #BOITE_OUVRIR{titre[,type]}
 * Racourci pour ouvrir une boite (info, simple, pour noisette ...)
 *
 * @param <type> $p
 * @return <type>
 */
function balise_BOITE_OUVRIR_dist($p) {
	$_titre = interprete_argument_balise(1,$p);
	$_class = interprete_argument_balise(2,$p);
	$_head_class = interprete_argument_balise(3,$p);
	$_titre = ($_titre?$_titre:"''");
	$_class = ($_class?", $_class":", 'simple'");
	$_head_class = ($_head_class?", $_head_class":"");

	$f = chercher_filtre('boite_ouvrir');
	$p->code = "$f($_titre$_class$_head_class)";
	$p->interdire_scripts = false;
	return $p;
}

/**
 * #BOITE_PIED{class}
 * Racourci pour passer au pied de la boite, avant sa fermeture
 *
 * @param <type> $p
 * @return <type>
 */
function balise_BOITE_PIED_dist($p) {
	$_class = interprete_argument_balise(1,$p);
	$_class = ($_class?"$_class":"");

	$f = chercher_filtre('boite_pied');
	$p->code = "$f($_class)";
	$p->interdire_scripts = false;
	return $p;
}

/**
 * #BOITE_FERMER
 * Racourci pour fermer une boite ouverte
 *
 * @param <type> $p
 * @return <type>
 */
function balise_BOITE_FERMER_dist($p) {
	$f = chercher_filtre('boite_fermer');
	$p->code = "$f()";
	$p->interdire_scripts = false;
	return $p;
}

/**
 * Ouvrir une boite
 * peut etre surcharge par filtre_boite_ouvrir_dist, filtre_boite_ouvrir
 *
 * @param string $titre
 * @param string $class
 * @return <type>
 */
function boite_ouvrir($titre, $class='', $head_class='', $id=""){
	$class = "box $class";
	$head_class = "hd $head_class";
	// dans l'espace prive, titrer en h3 si pas de balise <hn>
	if (test_espace_prive() AND strlen($titre) AND strpos($titre,'<h')===false)
		$titre = "<h3>$titre</h3>";
	return '<div class="'.$class.($id?"\" id=\"$id":"").'">'
	.'<b class="top"><b class="tl"></b><b class="tr"></b></b>'
	.'<div class="inner">'
	.($titre?'<div class="'.$head_class.'">'.$titre.'<div class="nettoyeur"></div><!--/hd--></div>':'')
	.'<div class="bd">';
}

/**
 * Passer au pied d'une boite
 * peut etre surcharge par filtre_boite_pied_dist, filtre_boite_pied
 *
 * @param <type> $class
 * @return <type>
 */
function boite_pied($class='act'){
	$class = "ft $class";
	return 	'<div class="nettoyeur"></div></div>'
	.'<div class="'.$class.'">';
}

/**
 * Fermer une boite
 * peut etre surcharge par filtre_boite_fermer_dist, filtre_boite_fermer
 *
 * @return <type>
 */
function boite_fermer(){
	return '<div class="nettoyeur"></div></div></div>'
	.'<b class="bottom"><b class="bl"></b><b class="br"></b></b>'
	.'</div>';
}




?>
envoyer_mail.php000066600000015736152525475610010002 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

include_spip('inc/charsets');
include_spip('inc/texte');

// http://doc.spip.org/@nettoyer_titre_email
function nettoyer_titre_email($titre) {
	return str_replace("\n", ' ', textebrut(corriger_typo($titre)));
}

// http://doc.spip.org/@nettoyer_caracteres_mail
function nettoyer_caracteres_mail($t) {

	$t = filtrer_entites($t);

	if ($GLOBALS['meta']['charset'] <> 'utf-8') {
		$t = str_replace(
			array("&#8217;","&#8220;","&#8221;"),
			array("'",      '"',      '"'),
		$t);
	}

	$t = str_replace(
		array("&mdash;", "&endash;"),
		array("--","-" ),
	$t);

	return $t;
}

/**
 * Envoi d'un mail
 * http://doc.spip.org/@inc_envoyer_mail_dist
 *
 * @param string $destinataire
 * @param string $sujet
 * @param string|array $corps
 *   au format string, c'est un corps d'email au format texte, comme supporte nativement par le core
 *   au format array, c'est un corps etendu qui peut contenir
 *     string texte : le corps d'email au format texte
 *     string from : email de l'envoyeur (prioritaire sur argument $from de premier niveau, deprecie)
 *     array headers : tableau d'en-tetes personalises, une entree par ligne d'en-tete
 *     --- Support partiel par une fonction mail_embarquer_pieces_jointes a fournir, ---
 *     --- chargee de convertir en texte encodee les pieces jointes ---
 *     array pieces_jointes : listes de pieces a embarquer dans l'email, chacune au format array :
 *       string chemin : chemin file system pour trouver le fichier a embarquer
 *       string nom : nom du document tel qu'apparaissant dans l'email
 *       string encodage : encodage a utiliser, parmi 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
 *       string mime : mime type du document
 *     --- Non implemente ici ---
 *     string html : le corps d'email au format html
 *     string nom_envoyeur : un nom d'envoyeur pour completer l'email from
 *     string cc : destinataires en copie conforme
 *     string bcc : destinataires en copie conforme cachee
 *     string adresse_erreur : addresse de retour en cas d'erreur d'envoi
 * @param string $from (deprecie, utiliser l'entree from de $corps)
 * @param string $headers (deprecie, utiliser l'entree headers de $corps)
 * @return bool
 */
function inc_envoyer_mail_dist($destinataire, $sujet, $corps, $from = "", $headers = "") {

	if (!email_valide($destinataire)) return false;
	if ($destinataire == _T('info_mail_fournisseur')) return false; // tres fort

	// Fournir si possible un Message-Id: conforme au RFC1036,
	// sinon SpamAssassin denoncera un MSGID_FROM_MTA_HEADER

	$email_envoi = $GLOBALS['meta']["email_envoi"];
	if (!email_valide($email_envoi)) {
		spip_log("Meta email_envoi invalide. Le mail sera probablement vu comme spam.");
		$email_envoi = $destinataire;
	}

	if (is_array($corps)){
		$texte = $corps['texte'];
		$from = (isset($corps['from'])?$corps['from']:$from);
		$headers = (isset($corps['headers'])?$corps['headers']:$headers);
		if (is_array($headers))
			$headers = implode("\n",$headers);
		$parts = "";
		if ($corps['pieces_jointes'] AND function_exists('mail_embarquer_pieces_jointes'))
			$parts = mail_embarquer_pieces_jointes($corps['pieces_jointes']);
	} else
		$texte = $corps;

	if (!$from) $from = $email_envoi;

	// ceci est la RegExp NO_REAL_NAME faisant hurler SpamAssassin
	if (preg_match('/^["\s]*\<?\S+\@\S+\>?\s*$/', $from))
		$from .= ' (' . str_replace(')','', translitteration(str_replace('@', ' at ', $from))) . ')';

	// nettoyer les &eacute; &#8217, &emdash; etc...
	// les 'cliquer ici' etc sont a eviter;  voir:
	// http://mta.org.ua/spamassassin-2.55/stuff/wiki.CustomRulesets/20050914/rules/french_rules.cf
	$texte = nettoyer_caracteres_mail($texte);
	$sujet = nettoyer_caracteres_mail($sujet);

	// encoder le sujet si possible selon la RFC
	if (init_mb_string()) {
		# un bug de mb_string casse mb_encode_mimeheader si l'encoding interne
		# est UTF-8 et le charset iso-8859-1 (constate php5-mac ; php4.3-debian)
		$charset = $GLOBALS['meta']['charset'];
		mb_internal_encoding($charset);
		$sujet = mb_encode_mimeheader($sujet, $charset, 'Q', "\n");
		mb_internal_encoding('utf-8');
	}

	if (function_exists('wordwrap') && (preg_match(',multipart/mixed,',$headers) == 0))
		$texte = wordwrap($texte);

	list($headers, $texte) = mail_normaliser_headers($headers, $from, $destinataire, $texte, $parts);

	if (_OS_SERVEUR == 'windows') {
		$texte = preg_replace ("@\r*\n@","\r\n", $texte);
		$headers = preg_replace ("@\r*\n@","\r\n", $headers);
		$sujet = preg_replace ("@\r*\n@","\r\n", $sujet);
	}

	spip_log("mail $destinataire\n$sujet\n$headers",'mails');
	// mode TEST : forcer l'email
	if (defined('_TEST_EMAIL_DEST')) {
		if (!_TEST_EMAIL_DEST)
			return false;
		else {
			$texte = "Dest : $destinataire\r\n" . $texte;
			$destinataire = _TEST_EMAIL_DEST;
		}
	}

	return @mail($destinataire, $sujet, $texte, $headers);
}

function mail_normaliser_headers($headers, $from, $to, $texte, $parts="")
{
	$charset = $GLOBALS['meta']['charset'];

	// Ajouter le Content-Type et consort s'il n'y est pas deja
	if (strpos($headers, "Content-Type: ") === false)
		$type =
		"Content-Type: text/plain;charset=\"$charset\";\n".
		"Content-Transfer-Encoding: 8bit\n";
	else $type = '';

	// calculer un identifiant unique
	preg_match('/@\S+/', $from, $domain);
	$uniq = rand() . '_' . md5($to . $texte) . $domain[0];

	// Si multi-part, s'en servir comme borne ...
	if ($parts) {
		$texte = "--$uniq\n$type\n" . $texte ."\n";
		foreach ($parts as $part) {
			$n = strlen($part[1]) . ($part[0] ? "\n" : '');
			$e = join("\n", $part[0]);
			$texte .= "\n--$uniq\nContent-Length: $n$e\n\n" . $part[1];
		}
		$texte .= "\n\n--$uniq--\n";
		// Si boundary n'est pas entre guillemets,
		// elle est comprise mais le charset est ignoree !
		$type = "Content-Type: multipart/mixed; boundary=\"$uniq\"\n";
	}

	// .. et s'en servir pour plaire a SpamAssassin

	$mid = 'Message-Id: <' . $uniq . ">";

	// indispensable pour les sites qui collent d'office From: serveur-http
	// sauf si deja mis par l'envoyeur
	$rep = (strpos($headers,"Reply-To:")!==FALSE) ? '' : "Reply-To: $from\n";

	// Nettoyer les en-tetes envoyees
	// Ajouter le \n final
	if (strlen($headers = trim($headers))) $headers .= "\n";

	// Et mentionner l'indeboulonable nomenclature ratee 

	$headers .= "From: $from\n$type$rep$mid\nMIME-Version: 1.0\n";

	return array($headers, $texte);
}
?>
prepare_recherche.php000066600000013145152525475610010747 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/


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

include_spip('inc/rechercher');
if (!defined('_DELAI_CACHE_resultats')) define('_DELAI_CACHE_resultats', 600);

/**
 * Preparer les listes id_article IN (...) pour les parties WHERE
 * et points =  des requetes du moteur de recherche
 * http://doc.spip.org/@inc_prepare_recherche_dist
 * 
 * Le parametre $serveur est utilise pour savoir sur quelle base on cherche
 * mais l'index des resultats est toujours stock� sur le serveur principal
 * car on ne sait pas si la base distante dispose d'une table spip_resultats
 * ni meme si on aurait le droit d'ecrire dedans
 *
 * @param string $recherche
 *    chaine recherchee
 * @param string $table
 *    table dans laquelle porte la recherche
 * @param bool $cond
 *    critere conditionnel sur {recherche?}
 * @param string $serveur
 *    serveur de base de donnees
 * @param array $modificateurs
 *    modificateurs de boucle, ie liste des criteres presents
 * @param string $primary
 *    cle primaire de la table de recherche
 * @return array
 */
function inc_prepare_recherche_dist($recherche, $table='articles', $cond=false, $serveur='', $modificateurs = array(), $primary='') {
	static $cache = array();
	$delai_fraicheur = min(_DELAI_CACHE_resultats,time()-$GLOBALS['meta']['derniere_modif']);

	// si recherche n'est pas dans le contexte, on va prendre en globals
	// ca permet de faire des inclure simple.
	if (!isset($recherche) AND isset($GLOBALS['recherche']))
		$recherche = $GLOBALS['recherche'];

	// traiter le cas {recherche?}
	if ($cond AND !strlen($recherche))
		return array("0 as points" /* as points */, /* where */ '');
		
	
	$rechercher = false;

	if (!isset($cache[$serveur][$table][$recherche])){
		$hash_serv = ($serveur?substr(md5($serveur),0,16):'');
		$hash = substr(md5($recherche . $table),0,16);
		$where = "(resultats.recherche='$hash' AND resultats.table_objet=".sql_quote($table)." AND resultats.serveur='$hash_serv')";
		$row = sql_fetsel('UNIX_TIMESTAMP(NOW())-UNIX_TIMESTAMP(resultats.maj) AS fraicheur','spip_resultats AS resultats',$where,'','fraicheur DESC','0,1');
		if (!$row
		  OR ($row['fraicheur']>$delai_fraicheur)
		  OR (defined('_VAR_MODE') AND _VAR_MODE=='recalcul')){
		 	$rechercher = true;
		}
	}

	// si on n'a pas encore traite les donnees dans une boucle precedente
	if ($rechercher) {
		//$tables = liste_des_champs();
		$x = objet_type($table);
		$points = recherche_en_base($recherche,
			$x,
			array(
				'score' => true,
				'toutvoir' => true,
				'jointures' => true
				),
			$serveur);
		// pas de résultat, pas de point
		$points = isset($points[$x]) ? $points[$x] : array();

		// permettre aux plugins de modifier le resultat
		$points = pipeline('prepare_recherche',array(
			'args'=>array('type'=>$x,'recherche'=>$recherche,'serveur'=>$serveur,'modificateurs'=>$modificateurs),
			'data'=>$points
		));

		// supprimer les anciens resultats de cette recherche
		// et les resultats trop vieux avec une marge
		// pas de AS resultats dans un delete (mysql)
		$whered = str_replace(array("resultats.recherche","resultats.table_objet","resultats.serveur"),array("recherche","table_objet","serveur"),$where);
		sql_delete('spip_resultats', 'NOT(' .sql_date_proche('maj', (0-($delai_fraicheur+100)), " SECOND") . ") OR ($whered)");

		// inserer les resultats dans la table de cache des resultats
		if (count($points)){
			$tab_couples = array();
			foreach ($points as $id => $p){
				$tab_couples[] = array(
					'recherche' => $hash,
					'id' => $id,
					'points' => $p['score'],
					'table_objet' => $table,
					'serveur' => $hash_serv,
				);
			}
			sql_insertq_multi('spip_resultats',$tab_couples,array());
		}
	}

	if (!isset($cache[$serveur][$table][$recherche])){
		if (!$serveur)
			$cache[$serveur][$table][$recherche] = array("resultats.points AS points",$where);
		else {
			if (sql_countsel('spip_resultats as resultats',$where))
			$rows = sql_allfetsel('resultats.id,resultats.points','spip_resultats as resultats',$where);
			$cache[$serveur][$table][$recherche] = generer_select_where_explicites($table, $primary, $rows, $serveur);
		}
	}

	return $cache[$serveur][$table][$recherche];
}


/**
 * Generer le select et where qui contiennent explicitement
 * les id et points (ie comme dans SPIP 1.9.x)
 * quand on fait une recherche sur une table externe
 *
 * @param string $table
 * @param string $primary
 * @param array $rows
 * @param string $serveur
 * @return array
 */
function generer_select_where_explicites($table, $primary, $rows, $serveur){
	# calculer le {id_article IN()} et le {... as points}
	if (!count($rows)){
		return array("''", "0=1");
	}
	else {
		$listes_ids = array();
		$select = '0';
		foreach ($rows as $r)
			$listes_ids[$r['points']][] = $r['id'];

		foreach ($listes_ids as $p => $ids)
			$select .= "+$p*(".
			           sql_in("$table.$primary", $ids,'',$serveur)
			           .") ";

		return array("$select AS points ",calcul_mysql_in("$table.$primary",array_map('reset',$rows),'',$serveur));
	}
}


?>
filtres.php000066600000317271152525475610006760 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

include_spip('inc/charsets');
include_spip('inc/filtres_mini');
include_spip('base/objets');
include_spip('public/parametrer'); // charger les fichiers fonctions

/**
 * Charger un filtre depuis le php :
 * - on inclue tous les fichiers fonctions des plugins et du skel
 * - on appelle chercher_filtre
 *
 * @param string $fonc
 * @param string $default
 * @return string
 */
function charger_filtre($fonc, $default='filtre_identite_dist') {
	include_spip('public/parametrer'); // inclure les fichiers fonctions
	return chercher_filtre($fonc, $default);
}

function filtre_identite_dist($texte){return $texte;}

/**
 * http://doc.spip.org/@chercher_filtre
 *
 * @param string $fonc
 * @param null $default
 * @return string
 */
function chercher_filtre($fonc, $default=NULL) {
	if (!$fonc) return $default;
	// Cas des types mime, sans confondre avec les appels de fonction de classe
	// Foo::Bar
	// qui peuvent etre avec un namespace : space\Foo::Bar
	if (preg_match(',^[\w]+/,',$fonc)){
		$nom = preg_replace(',\W,','_', $fonc);
		$f = chercher_filtre($nom);
		// cas du sous-type MIME sans filtre associe, passer au type:
		// si filtre_text_plain pas defini, passe a filtre_text
		if (!$f AND $nom!==$fonc)
			$f = chercher_filtre(preg_replace(',\W.*$,','', $fonc));
		return $f;
	}
	foreach (
	array('filtre_'.$fonc, 'filtre_'.$fonc.'_dist', $fonc) as $f){
		if (isset( $GLOBALS['spip_matrice'][$f]) AND is_string($g = $GLOBALS['spip_matrice'][$f]))
			find_in_path($g,'', true);
		if (function_exists($f)
		OR (preg_match("/^(\w*)::(\w*)$/", $f, $regs)
			AND is_callable(array($regs[1], $regs[2]))
		)) {
			return $f;
		}
	}
	return $default;
}

/**
 * Applique un filtre
 * 
 * Fonction générique qui prend en argument l’objet (texte, etc) à modifier
 * et le nom du filtre. Retrouve les arguments du filtre demandé dans les arguments
 * transmis à cette fonction, via func_get_args().
 *
 * @see filtrer() Assez proche
 * 
 * @param string $arg
 *     Texte sur lequel appliquer le filtre
 * @param string $filtre
 *     Nom du filtre a appliquer
 * @param string $force
 *     La fonction doit-elle retourner le texte ou rien ?
 * @return string
 *     Texte avec le filtre appliqué s'il a été trouvé,
 *     Texte sans le filtre appliqué s'il n'a pas été trouvé et que $force n'a
 *       pas été fourni,
 *     Chaîne vide si le filtre n'a pas été trouvé et que $force a été fourni.
**/
function appliquer_filtre($arg, $filtre, $force=NULL) {
	$f = chercher_filtre($filtre);
	if (!$f) {
		if (!$force) return '';
		else return $arg;
	}

	$args = func_get_args();
	array_shift($args); // enlever $arg
	array_shift($args); // enlever $filtre
	array_unshift($args, $arg); // remettre $arg
	return call_user_func_array($f,$args);
}

// http://doc.spip.org/@spip_version
function spip_version() {
	$version = $GLOBALS['spip_version_affichee'];
	if ($svn_revision = version_svn_courante(_DIR_RACINE))
		$version .= ($svn_revision<0 ? ' SVN':'').' ['.abs($svn_revision).']';
	return $version;
}


//
// Mention de la revision SVN courante de l'espace restreint standard
// (numero non garanti pour l'espace public et en cas de mutualisation)
// on est negatif si on est sur .svn, et positif si on utilise svn.revision
// http://doc.spip.org/@version_svn_courante
function version_svn_courante($dir) {
	if (!$dir) $dir = '.';

	// version installee par paquet ZIP
	if (lire_fichier($dir.'/svn.revision', $c)
	AND preg_match(',Revision: (\d+),', $c, $d))
		return intval($d[1]);

	// version installee par SVN
	if (lire_fichier($dir . '/.svn/entries', $c)
	AND (
	(preg_match_all(
	',committed-rev="([0-9]+)",', $c, $r1, PREG_PATTERN_ORDER)
	AND $v = max($r1[1])
	)
	OR
	(preg_match(',^\d.*dir[\r\n]+(\d+),ms', $c, $r1) # svn >= 1.4
	AND $v = $r1[1]
	)))
		return -$v;

	// Bug ou paquet fait main
	return 0;
}

// La matrice est necessaire pour ne filtrer _que_ des fonctions definies dans filtres_images
// et laisser passer les fonctions personnelles baptisees image_...
$GLOBALS['spip_matrice']['image_graver'] = true;//'inc/filtres_images_mini.php';
$GLOBALS['spip_matrice']['image_select'] = true;//'inc/filtres_images_mini.php';
$GLOBALS['spip_matrice']['image_reduire'] = true;//'inc/filtres_images_mini.php';
$GLOBALS['spip_matrice']['image_reduire_par'] = true;//'inc/filtres_images_mini.php';
$GLOBALS['spip_matrice']['image_passe_partout'] = true;//'inc/filtres_images_mini.php';

$GLOBALS['spip_matrice']['couleur_html_to_hex'] = 'inc/filtres_images_mini.php';
$GLOBALS['spip_matrice']['couleur_foncer'] = 'inc/filtres_images_mini.php';
$GLOBALS['spip_matrice']['couleur_eclaircir'] = 'inc/filtres_images_mini.php';

// ou pour inclure un script au moment ou l'on cherche le filtre
$GLOBALS['spip_matrice']['filtre_image_dist'] = 'inc/filtres_mime.php';
$GLOBALS['spip_matrice']['filtre_audio_dist'] = 'inc/filtres_mime.php';
$GLOBALS['spip_matrice']['filtre_video_dist'] = 'inc/filtres_mime.php';
$GLOBALS['spip_matrice']['filtre_application_dist'] = 'inc/filtres_mime.php';
$GLOBALS['spip_matrice']['filtre_message_dist'] = 'inc/filtres_mime.php';
$GLOBALS['spip_matrice']['filtre_multipart_dist'] = 'inc/filtres_mime.php';
$GLOBALS['spip_matrice']['filtre_text_dist'] = 'inc/filtres_mime.php';
$GLOBALS['spip_matrice']['filtre_text_csv_dist'] = 'inc/filtres_mime.php';
$GLOBALS['spip_matrice']['filtre_text_html_dist'] = 'inc/filtres_mime.php';
$GLOBALS['spip_matrice']['filtre_audio_x_pn_realaudio'] = 'inc/filtres_mime.php';


// charge les fonctions graphiques et applique celle demandee
// http://doc.spip.org/@filtrer
function filtrer($filtre) {
	if (isset($GLOBALS['spip_matrice'][$filtre]) and is_string($f = $GLOBALS['spip_matrice'][$filtre])){
		find_in_path($f,'', true);
		$GLOBALS['spip_matrice'][$filtre] = true;
	}
	$tous = func_get_args();
	if (substr($filtre,0,6)=='image_' && $GLOBALS['spip_matrice'][$filtre])
		return image_filtrer($tous);
	elseif($f = chercher_filtre($filtre)) {
		array_shift($tous);
		return call_user_func_array($f, $tous);
	}
	else {
		// le filtre n'existe pas, on provoque une erreur
		$msg = array('zbug_erreur_filtre', array('filtre'=>texte_script($filtre)));
		erreur_squelette($msg);
		return '';
	}
}

/*
 *
 * [(#CALCUL|set{toto})] enregistre le résultat de #CALCUL
 *           dans la variable toto et renvoie vide
 *
 * [(#CALCUL|set{toto,1})] enregistre le résultat de #CALCUL
 *           dans la variable toto et renvoie la valeur
 *
 */
function filtre_set(&$Pile, $val, $key, $continue = null) {
	$Pile['vars'][$key] = $val;
	return $continue ? $val : '';
}

/*
 * [(#TRUC|debug{avant}|calcul|debug{apres}|etc)] affiche
 *   la valeur de #TRUC avant et après le calcul
 */
function filtre_debug($val, $key=null) {
	$debug = (
		is_null($key) ? '' :  (var_export($key,true)." = ")
	) . var_export($val, true);

	include_spip('inc/autoriser');
	if (autoriser('webmestre'))
		echo "<div class='spip_debug'>\n",$debug,"</div>\n";

	spip_log($debug, 'debug');

	return $val;
}


// fonction generique d'entree des filtres images
// accepte en entree un texte complet, un img-log (produit par #LOGO_XX),
// un tag <img ...> complet, ou encore un nom de fichier *local* (passer
// le filtre |copie_locale si on veut l'appliquer a un document)
// applique le filtre demande a chacune des occurrences

// http://doc.spip.org/@image_filtrer
function image_filtrer($args){
	$filtre = array_shift($args); # enlever $filtre
	$texte = array_shift($args);
	if (!strlen($texte)) return;
	find_in_path('filtres_images_mini.php','inc/', true);
	statut_effacer_images_temporaires(true); // activer la suppression des images temporaires car le compilo finit la chaine par un image_graver
	// Cas du nom de fichier local
	if ( strpos(substr($texte,strlen(_DIR_RACINE)),'..')===FALSE
	AND !preg_match(',^/|[<>]|\s,S', $texte)
	AND (
		file_exists(preg_replace(',[?].*$,','',$texte))
		OR preg_match(';^(\w{3,7}://);', $texte) 
		)) {
		array_unshift($args,"<img src='$texte' />");
		$res = call_user_func_array($filtre, $args);
		statut_effacer_images_temporaires(false); // desactiver pour les appels hors compilo
		return $res;
	}

	// Cas general : trier toutes les images, avec eventuellement leur <span>
	if (preg_match_all(
		',(<([a-z]+) [^<>]*spip_documents[^<>]*>)?\s*(<img\s.*>),UimsS',
		$texte, $tags, PREG_SET_ORDER)) {
		foreach ($tags as $tag) {
			$class = extraire_attribut($tag[3],'class');
			if (!$class || (strpos($class,'no_image_filtrer')===FALSE)){
				array_unshift($args,$tag[3]);
				if ($reduit = call_user_func_array($filtre, $args)) {
					// En cas de span spip_documents, modifier le style=...width:
					if($tag[1]){
						$w = extraire_attribut($reduit, 'width');
						if (!$w AND preg_match(",width:\s*(\d+)px,S",extraire_attribut($reduit,'style'),$regs))
							$w = $regs[1];
						if ($w AND ($style = extraire_attribut($tag[1], 'style'))){
							$style = preg_replace(",width:\s*\d+px,S", "width:${w}px", $style);
							$replace = inserer_attribut($tag[1], 'style', $style);
							$texte = str_replace($tag[1], $replace, $texte);
						}
					}
					// traiter aussi un eventuel mouseover
					if ($mouseover = extraire_attribut($reduit,'onmouseover')){
						if (preg_match(",this[.]src=['\"]([^'\"]+)['\"],ims", $mouseover, $match)){
							$srcover = $match[1];
							array_shift($args);
							array_unshift($args,"<img src='".$match[1]."' />");
							$srcover_filter = call_user_func_array($filtre, $args);
							$srcover_filter = extraire_attribut($srcover_filter,'src');
							$reduit = str_replace($srcover,$srcover_filter,$reduit);
						}
					}
					$texte = str_replace($tag[3], $reduit, $texte);
				}
				array_shift($args);
			}
		}
	}
	statut_effacer_images_temporaires(false); // desactiver pour les appels hors compilo
	return $texte;
}

//
// Retourner taille d'une image
// pour les filtres |largeur et |hauteur
//
// http://doc.spip.org/@taille_image
function taille_image($img) {

	static $largeur_img =array(), $hauteur_img= array();
	$srcWidth = 0;
	$srcHeight = 0;

	$logo = extraire_attribut($img,'src');

	if (!$logo) $logo = $img;
	else {
		$srcWidth = extraire_attribut($img,'width');
		$srcHeight = extraire_attribut($img,'height');
	}

	// ne jamais operer directement sur une image distante pour des raisons de perfo
	// la copie locale a toutes les chances d'etre la ou de resservir
	if (preg_match(';^(\w{3,7}://);', $logo)){
		include_spip('inc/distant');
		$fichier = copie_locale($logo);
		$logo = $fichier ? _DIR_RACINE . $fichier : $logo;
	}
	if (($p=strpos($logo,'?'))!==FALSE)
		$logo=substr($logo,0,$p);

	$srcsize = false;
	if (isset($largeur_img[$logo]))
		$srcWidth = $largeur_img[$logo];
	if (isset($hauteur_img[$logo]))
		$srcHeight = $hauteur_img[$logo];
	if (!$srcWidth OR !$srcHeight){
		if (file_exists($logo)
			AND $srcsize = @getimagesize($logo)){
			if (!$srcWidth)	$largeur_img[$logo] = $srcWidth = $srcsize[0];
			if (!$srcHeight)	$hauteur_img[$logo] = $srcHeight = $srcsize[1];
		}
		// $logo peut etre une reference a une image temporaire dont a n'a que le log .src
		// on s'y refere, l'image sera reconstruite en temps utile si necessaire
		elseif(@file_exists($f = "$logo.src")
		  AND lire_fichier($f,$valeurs)
		  AND $valeurs=unserialize($valeurs)) {
			if (!$srcWidth)	$largeur_img[$logo] = $srcWidth = $valeurs["largeur_dest"];
			if (!$srcHeight)	$hauteur_img[$logo] = $srcHeight = $valeurs["hauteur_dest"];
	  }
	}
	return array($srcHeight, $srcWidth);
}
// http://doc.spip.org/@largeur
function largeur($img) {
	if (!$img) return;
	list ($h,$l) = taille_image($img);
	return $l;
}
// http://doc.spip.org/@hauteur
function hauteur($img) {
	if (!$img) return;
	list ($h,$l) = taille_image($img);
	return $h;
}


// Echappement des entites HTML avec correction des entites "brutes"
// (generees par les butineurs lorsqu'on rentre des caracteres n'appartenant
// pas au charset de la page [iso-8859-1 par defaut])
//
// Attention on limite cette correction aux caracteres "hauts" (en fait > 99
// pour aller plus vite que le > 127 qui serait logique), de maniere a
// preserver des echappements de caracteres "bas" (par exemple [ ou ")
// et au cas particulier de &amp; qui devient &amp;amp; dans les url
// http://doc.spip.org/@corriger_entites_html
function corriger_entites_html($texte) {
	if (strpos($texte,'&amp;') === false) return $texte;
	return preg_replace(',&amp;(#[0-9][0-9][0-9]+;|amp;),iS', '&\1', $texte);
}
// idem mais corriger aussi les &amp;eacute; en &eacute;
// http://doc.spip.org/@corriger_toutes_entites_html
function corriger_toutes_entites_html($texte) {
	if (strpos($texte,'&amp;') === false) return $texte;
	return preg_replace(',&amp;(#?[a-z0-9]+;),iS', '&\1', $texte);
}

// http://doc.spip.org/@proteger_amp
function proteger_amp($texte){
	return str_replace('&','&amp;',$texte);
}

//
/**
 * http://doc.spip.org/@entites_html
 *
 * @param string $texte
 *   chaine a echapper
 * @param bool $tout
 *   corriger toutes les &amp;xx; en &xx;
 * @param bool $quote
 *   echapper aussi les simples quotes en &#039;
 * @return mixed|string
 */
function entites_html($texte, $tout=false, $quote=true) {
	if (!is_string($texte) OR !$texte
	OR strpbrk($texte, "&\"'<>")==false
	) return $texte;
	include_spip('inc/texte');
	$flags = !defined('PHP_VERSION_ID') OR PHP_VERSION_ID < 50400 ? ENT_COMPAT : ENT_COMPAT|ENT_HTML401;
	$texte = spip_htmlspecialchars(echappe_retour(echappe_html($texte, '', true), '', 'proteger_amp'), $quote?ENT_QUOTES:$flags);
	if ($tout)
		return corriger_toutes_entites_html($texte);
	else
		return corriger_entites_html($texte);
}

// Transformer les &eacute; dans le charset local
// http://doc.spip.org/@filtrer_entites
function filtrer_entites($texte) {
	if (strpos($texte,'&') === false) return $texte;
	// filtrer
	$texte = html2unicode($texte);
	// remettre le tout dans le charset cible
	$texte = unicode2charset($texte);
	// cas particulier des " et ' qu'il faut filtrer aussi
	// (on le faisait deja avec un &quot;)
	if (strpos($texte,"&#")!==false)
		$texte = str_replace(array("&#039;","&#39;","&#034;","&#34;"), array("'","'",'"','"'), $texte);
	return $texte;
}

/**
 * Version securisee de filtrer_entites
 * @param string $t
 * @return string
 */
if (!function_exists('filtre_filtrer_entites_dist')) {
	function filtre_filtrer_entites_dist($t) {
		include_spip('inc/texte');
		return interdire_scripts(filtrer_entites($t));
	}
}


// caracteres de controle - http://www.w3.org/TR/REC-xml/#charsets
// http://doc.spip.org/@supprimer_caracteres_illegaux
function supprimer_caracteres_illegaux($texte) {
	static $from = "\x0\x1\x2\x3\x4\x5\x6\x7\x8\xB\xC\xE\xF\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F";
	static $to = null;
	
	if (is_array($texte)) {
		return array_map('supprimer_caracteres_illegaux', $texte);
	}
	
	if (!$to) $to = str_repeat('-', strlen($from));
	return strtr($texte, $from, $to);
}

// Supprimer caracteres windows et les caracteres de controle ILLEGAUX
// http://doc.spip.org/@corriger_caracteres
function corriger_caracteres ($texte) {
	$texte = corriger_caracteres_windows($texte);
	$texte = supprimer_caracteres_illegaux($texte);
	return $texte;
}

/**
 * Encode du HTML pour transmission XML
 * notamment dans les flux RSS
 *
 * http://doc.spip.org/@texte_backend
 *
 * @param $texte
 * @return mixed
 */
function texte_backend($texte) {

	static $apostrophe = array("&#8217;", "'"); # n'allouer qu'une fois

	// si on a des liens ou des images, les passer en absolu
	$texte = liens_absolus($texte);

	// echapper les tags &gt; &lt;
	$texte = preg_replace(',&(gt|lt);,S', '&amp;\1;', $texte);

	// importer les &eacute;
	$texte = filtrer_entites($texte);

	// " -> &quot; et tout ce genre de choses
	$u = $GLOBALS['meta']['pcre_u'];
	$texte = str_replace("&nbsp;", " ", $texte);
	$texte = preg_replace('/\s{2,}/S'.$u, " ", $texte);
	// ne pas echapper les sinqle quotes car certains outils de syndication gerent mal
	$texte = entites_html($texte, false, false);
	// mais bien echapper les double quotes !
	$texte = str_replace('"','&#034;',$texte);

	// verifier le charset
	$texte = charset2unicode($texte);

	// Caracteres problematiques en iso-latin 1
	if ($GLOBALS['meta']['charset'] == 'iso-8859-1') {
		$texte = str_replace(chr(156), '&#156;', $texte);
		$texte = str_replace(chr(140), '&#140;', $texte);
		$texte = str_replace(chr(159), '&#159;', $texte);
	}

	// l'apostrophe curly pose probleme a certains lecteure de RSS
	// et le caractere apostrophe alourdit les squelettes avec PHP
	// ==> on les remplace par l'entite HTML
	return str_replace($apostrophe, "'", $texte);
}

// Comme ci-dessus, mais avec addslashes final pour squelettes avec PHP (rss)

function texte_backendq($texte) {
	return addslashes(texte_backend($texte));
}

// Enleve le numero des titres numerotes ("1. Titre" -> "Titre")
// http://doc.spip.org/@supprimer_numero
function supprimer_numero($texte) {
	return preg_replace(
	",^[[:space:]]*([0-9]+)([.)]|".chr(194).'?'.chr(176).")[[:space:]]+,S",
	"", $texte);
}

// et la fonction inverse
// http://doc.spip.org/@recuperer_numero
function recuperer_numero($texte) {
	if (preg_match(
	",^[[:space:]]*([0-9]+)([.)]|".chr(194).'?'.chr(176).")[[:space:]]+,S",
	$texte, $regs))
		return strval($regs[1]);
	else
		return '';
}

// Suppression basique et brutale de tous les <...>
// http://doc.spip.org/@supprimer_tags
function supprimer_tags($texte, $rempl = "") {
	$texte = preg_replace(",<[^>]*>,US", $rempl, $texte);
	// ne pas oublier un < final non ferme
	// mais qui peut aussi etre un simple signe plus petit que
	$texte = str_replace('<', ' ', $texte);
	return $texte;
}

// Convertit les <...> en la version lisible en HTML
// http://doc.spip.org/@echapper_tags
function echapper_tags($texte, $rempl = "") {
	$texte = preg_replace("/<([^>]*)>/", "&lt;\\1&gt;", $texte);
	return $texte;
}

// Convertit un texte HTML en texte brut
// http://doc.spip.org/@textebrut
function textebrut($texte) {
	$u = $GLOBALS['meta']['pcre_u'];
	$texte = preg_replace('/\s+/S'.$u, " ", $texte);
	$texte = preg_replace("/<(p|br)( [^>]*)?".">/iS", "\n\n", $texte);
	$texte = preg_replace("/^\n+/", "", $texte);
	$texte = preg_replace("/\n+$/", "", $texte);
	$texte = preg_replace("/\n +/", "\n", $texte);
	$texte = supprimer_tags($texte);
	$texte = preg_replace("/(&nbsp;| )+/S", " ", $texte);
	// nettoyer l'apostrophe curly qui pose probleme a certains rss-readers, lecteurs de mail...
	$texte = str_replace("&#8217;","'",$texte);
	return $texte;
}

// Remplace les liens SPIP en liens ouvrant dans une nouvelle fenetre (target=blank)
// http://doc.spip.org/@liens_ouvrants
function liens_ouvrants ($texte) {
	return preg_replace(",<a\s+([^>]*https?://[^>]*class=[\"']spip_(out|url)\b[^>]+)>,",
		"<a \\1 target=\"_blank\">", $texte);
}

/**
 * Ajouter un attribut rel="nofollow" sur tous les liens d'un texte
 * @param string $texte
 * @return string
 */
function liens_nofollow($texte) {
	if (stripos($texte,"<a")===false)
		return $texte;

	if (preg_match_all(",<a\b[^>]*>,UimsS",$texte, $regs, PREG_PATTERN_ORDER)){
		foreach($regs[0] as $a){
			$rel = extraire_attribut($a,"rel");
			if (strpos($rel,"nofollow")===false){
				$rel = "nofollow" . ($rel?" $rel":"");
				$anofollow = inserer_attribut($a,"rel",$rel);
				$texte = str_replace($a,$anofollow,$texte);
			}
		}
	}

	return $texte;
}

// Transformer les sauts de paragraphe en simples passages a la ligne
// http://doc.spip.org/@PtoBR
function PtoBR($texte){
	$u = $GLOBALS['meta']['pcre_u'];
	$texte = preg_replace("@</p>@iS", "\n", $texte);
	$texte = preg_replace("@<p\b.*>@UiS", "<br />", $texte);
	$texte = preg_replace("@^\s*<br />@S".$u, "", $texte);
	return $texte;
}


/**
 * lignes_longues assure qu'un texte ne vas pas deborder d'un bloc
 * par la faute d'un mot trop long (souvent des URLs)
 * Ne devrait plus etre utilise et fait directement en CSS par un style
 * word-wrap:break-word;
 * cf http://www.alsacreations.com/tuto/lire/1038-gerer-debordement-contenu-css.html
 *
 * Pour assurer la compatibilite du filtre, on encapsule le contenu par
 * un div ou span portant ce style inline.
 *
 * http://doc.spip.org/@lignes_longues
 *
 * @param string $texte
 * @return string
 */
function lignes_longues($texte) {
	if (!strlen(trim($texte))) return $texte;
	include_spip('inc/texte');
	$tag = preg_match(',</?('._BALISES_BLOCS.')[>[:space:]],iS', $texte) ?
		'div' : 'span';

	return "<$tag style='word-wrap:break-word;'>$texte</$tag>";
}

// Majuscules y compris accents, en HTML
// http://doc.spip.org/@majuscules
function majuscules($texte) {
	if (!strlen($texte)) return '';

	// Cas du turc
	if ($GLOBALS['spip_lang'] == 'tr') {
		# remplacer hors des tags et des entites
		if (preg_match_all(',<[^<>]+>|&[^;]+;,S', $texte, $regs, PREG_SET_ORDER))
			foreach ($regs as $n => $match)
				$texte = str_replace($match[0], "@@SPIP_TURC$n@@", $texte);

		$texte = str_replace('i', '&#304;', $texte);

		if ($regs)
			foreach ($regs as $n => $match)
				$texte = str_replace("@@SPIP_TURC$n@@", $match[0], $texte);
	}

	// Cas general
	return "<span style='text-transform: uppercase;'>$texte</span>";
}

// "127.4 ko" ou "3.1 Mo"
// http://doc.spip.org/@taille_en_octets
function taille_en_octets ($taille) {
	if ($taille < 1) return '';
	if ($taille < 1024) {$taille = _T('taille_octets', array('taille' => $taille));}
	else if ($taille < 1024*1024) {
		$taille = _T('taille_ko', array('taille' => round($taille/1024, 1)));
	} else if ($taille < 1024*1024*1024) {
		$taille = _T('taille_mo', array('taille' => round($taille/1024/1024, 1)));
	} else {
		$taille = _T('taille_go', array('taille' => round($taille/1024/1024/1024, 2)));
	}
	return $taille;
}


// Rend une chaine utilisable sans dommage comme attribut HTML
// http://doc.spip.org/@attribut_html
function attribut_html($texte,$textebrut = true) {
	$u = $GLOBALS['meta']['pcre_u'];
	if ($textebrut)
		$texte = preg_replace(array(",\n,",",\s(?=\s),msS".$u),array(" ",""),textebrut($texte));
	$texte = texte_backend($texte);
	$texte = str_replace(array("'",'"'),array('&#039;', '&#034;'), $texte);
	
	return preg_replace(array("/&(amp;|#38;)/","/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,5};)/"),array("&","&#38;") , $texte);
}

// Vider les url nulles comme 'http://' ou 'mailto:'
// et leur appliquer un htmlspecialchars() + gerer les &amp;
// http://doc.spip.org/@vider_url
function vider_url($url, $entites = true) {
	# un message pour abs_url
	$GLOBALS['mode_abs_url'] = 'url';
	$url = trim($url);
	$r = ",^(?:" . _PROTOCOLES_STD . '):?/?/?$,iS';
	return preg_match($r, $url) ? '': ($entites ? entites_html($url) : $url);
}

// Extraire une date de n'importe quel champ (a completer...)
// http://doc.spip.org/@extraire_date
function extraire_date($texte) {
	// format = 2001-08
	if (preg_match(",([1-2][0-9]{3})[^0-9]*(1[0-2]|0?[1-9]),",$texte,$regs))
		return $regs[1]."-".sprintf("%02d", $regs[2])."-01";
}

// Maquiller une adresse e-mail
// http://doc.spip.org/@antispam
function antispam($texte) {
	include_spip('inc/acces');
	$masque = creer_pass_aleatoire(3);
	return preg_replace("/@/", " $masque ", $texte);
}

// http://doc.spip.org/@securiser_acces
function securiser_acces($id_auteur, $cle, $dir, $op='', $args='')
{
	include_spip('inc/acces');
	if ($op) $dir .= " $op $args";
	return verifier_low_sec($id_auteur, $cle, $dir);
}

/**
 * La fonction sinon retourne le second parametre lorsque
 * le premier est considere vide, sinon retourne le premier parametre.
 *
 * En php sinon($a, 'rien') retourne $a ou 'rien' si $a est vide.
 * En filtre spip |sinon{#TEXTE, rien} : affiche #TEXTE ou "rien" si #TEXTE est vide,
 *
 * Note : l'utilisation de |sinon en tant que filtre de squelette
 * est directement compile dans public/references par la fonction filtre_logique()
 * 
 * @param mixed $texte
 * 		Contenu de reference a tester
 * @param mixed $sinon
 * 		Contenu a retourner si le contenu de reference est vide
 * @return mixed
 * 		Retourne $texte, sinon $sinon.
**/
function sinon ($texte, $sinon='') {
	if ($texte OR (!is_array($texte) AND strlen($texte)))
		return $texte;
	else
		return $sinon;
}

// |choixsivide{vide,pasvide} affiche pasvide si la chaine n'est pas vide...
// http://doc.spip.org/@choixsivide
function choixsivide($a, $vide, $pasvide) {
	return $a ? $pasvide : $vide;
}

// |choixsiegal{aquoi,oui,non} affiche oui si la chaine est egal a aquoi ...
// http://doc.spip.org/@choixsiegal
function choixsiegal($a1,$a2,$v,$f) {
	return ($a1 == $a2) ? $v : $f;
}


//
// Date, heure, saisons
//

// on normalise la date, si elle vient du contexte (public/parametrer.php), on force le jour
// http://doc.spip.org/@normaliser_date
function normaliser_date($date, $forcer_jour = false) {
	$date = vider_date($date);
	if ($date) {
		if (preg_match("/^[0-9]{8,10}$/", $date))
			$date = date("Y-m-d H:i:s", $date);
		if (preg_match("#^([12][0-9]{3})([-/]00)?( [-0-9:]+)?$#", $date, $regs))
			$date = $regs[1]."-00-00".$regs[3];
		else if (preg_match("#^([12][0-9]{3}[-/][01]?[0-9])([-/]00)?( [-0-9:]+)?$#", $date, $regs))
			$date = preg_replace("@/@","-",$regs[1])."-00".$regs[3];
		else
			$date = date("Y-m-d H:i:s", strtotime($date));

		if ($forcer_jour)
			$date = str_replace('-00', '-01', $date);
	}
	return $date;
}

// http://doc.spip.org/@vider_date
function vider_date($letexte) {
	if (strncmp("0000-00-00", $letexte,10)==0) return '';
	if (strncmp("0001-01-01", $letexte,10)==0) return '';
	if (strncmp("1970-01-01", $letexte,10)==0) return '';	// eviter le bug GMT-1
	return $letexte;
}

// http://doc.spip.org/@recup_heure
function recup_heure($date){

	static $d = array(0,0,0);
	if (!preg_match('#([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2})#', $date, $r)) 
		return $d;
	
	array_shift($r);
	return $r;
}

// http://doc.spip.org/@heures
function heures($numdate) {
	$date_array = recup_heure($numdate);
	if ($date_array)
		list($heures, $minutes, $secondes) = $date_array;
	return $heures;
}

// http://doc.spip.org/@minutes
function minutes($numdate) {
	$date_array = recup_heure($numdate);
	if ($date_array)
		list($heures, $minutes, $secondes) = $date_array;
	return $minutes;
}

// http://doc.spip.org/@secondes
function secondes($numdate) {
	$date_array = recup_heure($numdate);
	if ($date_array)
		list($heures,$minutes,$secondes) = $date_array;
	return $secondes;
}

// http://doc.spip.org/@heures_minutes
function heures_minutes($numdate) {
	return _T('date_fmt_heures_minutes', array('h'=> heures($numdate), 'm'=> minutes($numdate)));
}

// http://doc.spip.org/@recup_date
function recup_date($numdate, $forcer_jour = true){
	if (!$numdate) return '';
	$heures = $minutes = $secondes = 0;
	if (preg_match('#([0-9]{1,2})/([0-9]{1,2})/([0-9]{4}|[0-9]{1,2})#', $numdate, $regs)) {
		$jour = $regs[1];
		$mois = $regs[2];
		$annee = $regs[3];
		if ($annee < 90){
			$annee = 2000 + $annee;
		} elseif ($annee<100) {
			$annee = 1900 + $annee ;
		}
		list($heures, $minutes, $secondes) = recup_heure($numdate);

	}
	elseif (preg_match('#([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})#',$numdate, $regs)) {
		$annee = $regs[1];
		$mois = $regs[2];
		$jour = $regs[3];
		list($heures, $minutes, $secondes) = recup_heure($numdate);
	}
	elseif (preg_match('#([0-9]{4})-([0-9]{2})#', $numdate, $regs)){
		$annee = $regs[1];
		$mois = $regs[2];
		$jour ='';
		list($heures, $minutes, $secondes) = recup_heure($numdate);
	}
	elseif (preg_match('#^([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})$#', $numdate, $regs)){
		$annee = $regs[1];
		$mois = $regs[2];
		$jour = $regs[3];
		$heures = $regs[4];
		$minutes = $regs[5];
		$secondes = $regs[6];
	} else $annee = $mois =  $jour =''; 
	if ($annee > 4000) $annee -= 9000;
	if (substr($jour, 0, 1) == '0') $jour = substr($jour, 1);

	if ($forcer_jour AND $jour == '0') $jour = '1';
	if ($forcer_jour AND $mois == '0') $mois = '1';
	if ($annee OR $mois OR $jour OR $heures OR $minutes OR $secondes)
		return array($annee, $mois, $jour, $heures, $minutes, $secondes);
}

// une date pour l'interface : utilise date_relative si le decalage
// avec time() est de moins de douze heures, sinon la date complete
// http://doc.spip.org/@date_interface
function date_interface($date, $decalage_maxi = 43200/* 12*3600 */) {
	return sinon(
		date_relative($date, $decalage_maxi),
		affdate_heure($date)
	);
}

// http://doc.spip.org/@date_relative
function date_relative($date, $decalage_maxi=0,$ref_date=null) {
	
	if (is_null($ref_date))
		$ref_time = time();
	else
		$ref_time = strtotime($ref_date);
	
	if (!$date) return;
	$decal = date("U",$ref_time) - date("U", strtotime($date));

	if ($decalage_maxi AND ($decal > $decalage_maxi OR $decal < 0))
		return '';

	if ($decal < 0) {
		$il_y_a = "date_dans";
		$decal = -1 * $decal;
	} else {
		$il_y_a = "date_il_y_a";
	}

	if ($decal > 3600 * 24 * 30 * 6)
		return affdate_court($date);

	if ($decal > 3600 * 24 * 30) {
		$mois = floor ($decal / (3600 * 24 * 30));
		if ($mois < 2)
			$delai = "$mois "._T("date_un_mois");
		else
			$delai = "$mois "._T("date_mois");
	}
	else if ($decal > 3600 * 24 * 7) {
		$semaines = floor ($decal / (3600 * 24 * 7));
		if ($semaines < 2)
			$delai = "$semaines "._T("date_une_semaine");
		else
			$delai = "$semaines "._T("date_semaines");
	}
	else if ($decal > 3600 * 24) {
		$jours = floor ($decal / (3600 * 24));
		if ($jours < 2)
			return $il_y_a=="date_dans"?_T("date_demain"):_T("date_hier");
		else
			$delai = "$jours "._T("date_jours");
	}
	else if ($decal >= 3600) {
		$heures = floor ($decal / 3600);
		if ($heures < 2)
			$delai = "$heures "._T("date_une_heure");
		else
			$delai = "$heures "._T("date_heures");
	}
	else if ($decal >= 60) {
		$minutes = floor($decal / 60);
		if ($minutes < 2)
			$delai = "$minutes "._T("date_une_minute");
		else
			$delai = "$minutes "._T("date_minutes");
	} else {
		$secondes = ceil($decal);
		if ($secondes < 2)
			$delai = "$secondes "._T("date_une_seconde");
		else
			$delai = "$secondes "._T("date_secondes");
	}

	return _T($il_y_a, array("delai"=> $delai));
}


// http://doc.spip.org/@date_relativecourt
function date_relativecourt($date, $decalage_maxi=0) {
	
	if (!$date) return;
	$decal = date("U",strtotime(date('Y-m-d'))-strtotime(date('Y-m-d',strtotime($date))));

	if ($decalage_maxi AND ($decal > $decalage_maxi OR $decal < 0))
		return '';

	if ($decal < -24*3600) {
		$retour = date_relative($date, $decalage_maxi);
	}
	elseif ($decal < 0) {
		$retour = _T("date_demain");
	}
	else if ($decal < (3600 * 24) ) {
		$retour = _T("date_aujourdhui");
	}
	else if ($decal < (3600 * 24 *2) ) {
		$retour = _T("date_hier");
	}
	else {
		$retour = date_relative($date, $decalage_maxi);
	}



	return $retour;
}

/**
 * Formatage humain de la date $numdate selon le format $vue
 * http://doc.spip.org/@affdate_base
 *
 * @param $numdate
 * @param $vue
 * @param array $options
 *   param : 'abbr' ou 'initiale' permet d'afficher les jours au format court ou initiale
 *   annee_courante : permet de definir l'annee de reference pour l'affichage des dates courtes
 * @return mixed|string
 */
function affdate_base($numdate, $vue, $options = array()) {
	if (is_string($options))
		$options = array('param'=>$options);
	$date_array = recup_date($numdate, false);
	if (!$date_array) return;
	list($annee, $mois, $jour, $heures, $minutes, $secondes)= $date_array;

	// 1er, 21st, etc.
	$journum = $jour;

	if ($jour == 0) {
		$jour = '';
	} else {
		$njour = intval($jour);
		if ($jourth = _T('date_jnum'.$jour))
			$jour = $jourth;
	}

	$mois = intval($mois);
	if ($mois > 0 AND $mois < 13) {
		$nommois = _T('date_mois_'.$mois);
		if ($jour)
			$jourmois = _T('date_de_mois_'.$mois, array('j'=>$jour, 'nommois'=>$nommois));
		else
			$jourmois = $nommois;
	} else $nommois = '';

	if ($annee < 0) {
		$annee = -$annee." "._T('date_avant_jc');
		$avjc = true;
	}
	else $avjc = false;

	switch ($vue) {
	case 'saison':
	case 'saison_annee':
		$saison = '';
		if ($mois > 0){
			$saison = 1;
			if (($mois == 3 AND $jour >= 21) OR $mois > 3) $saison = 2;
			if (($mois == 6 AND $jour >= 21) OR $mois > 6) $saison = 3;
			if (($mois == 9 AND $jour >= 21) OR $mois > 9) $saison = 4;
			if (($mois == 12 AND $jour >= 21) OR $mois > 12) $saison = 1;
		}
		if($vue == 'saison')
			return $saison?_T('date_saison_'.$saison):'';
		else
			return $saison?trim(_T('date_fmt_saison_annee', array('saison'=>_T('date_saison_'.$saison), 'annee'=>$annee))) :'';

	case 'court':
		if ($avjc) return $annee;
		$a = ((isset($options['annee_courante']) AND $options['annee_courante'])?$options['annee_courante']:date('Y'));
		if ($annee < ($a - 100) OR $annee > ($a + 100)) return $annee;
		if ($annee != $a) return _T('date_fmt_mois_annee', array ('mois'=>$mois, 'nommois'=>spip_ucfirst($nommois), 'annee'=>$annee));
		return _T('date_fmt_jour_mois', array ('jourmois'=>$jourmois, 'jour'=>$jour, 'mois'=>$mois, 'nommois'=>$nommois, 'annee'=>$annee));

	case 'jourcourt':
		if ($avjc) return $annee;
		$a = ((isset($options['annee_courante']) AND $options['annee_courante'])?$options['annee_courante']:date('Y'));
		if ($annee < ($a - 100) OR $annee > ($a + 100)) return $annee;
		if ($annee != $a) return _T('date_fmt_jour_mois_annee', array ('jourmois'=>$jourmois, 'jour'=>$jour, 'mois'=>$mois, 'nommois'=>$nommois, 'annee'=>$annee));
		return _T('date_fmt_jour_mois', array ('jourmois'=>$jourmois, 'jour'=>$jour, 'mois'=>$mois, 'nommois'=>$nommois, 'annee'=>$annee));

	case 'entier':
		if ($avjc) return $annee;
		if ($jour)
			return _T('date_fmt_jour_mois_annee', array ('jourmois'=>$jourmois, 'jour'=>$jour, 'mois'=>$mois, 'nommois'=>$nommois, 'annee'=>$annee));
		elseif ($mois)
			return trim(_T('date_fmt_mois_annee', array ('mois'=>$mois, 'nommois'=>$nommois, 'annee'=>$annee)));
		else
			return $annee;

	case 'nom_mois':
		$param = ((isset($options['param']) AND $options['param']) ? '_'.$options['param'] : '');
		if ($param and $mois) {
			return _T('date_mois_'.$mois.$param);
		}
		return $nommois;

	case 'mois':
		return sprintf("%02s",$mois);

	case 'jour':
		return $jour;

	case 'journum':
		return $journum;

	case 'nom_jour':
		if (!$mois OR !$njour)
			return '';
		$nom = mktime(1,1,1,$mois,$njour,$annee);
		$nom = 1+date('w',$nom);
		$param = ((isset($options['param']) AND $options['param']) ? '_'.$options['param'] : '');
		return _T('date_jour_'.$nom.$param);

	case 'mois_annee':
		if ($avjc) return $annee;
		return trim(_T('date_fmt_mois_annee', array('mois'=>$mois, 'nommois'=>$nommois, 'annee'=>$annee)));

	case 'annee':
		return $annee;

	// Cas d'une vue non definie : retomber sur le format
	// de date propose par http://www.php.net/date
	default:
		return date($vue, strtotime($numdate));
	}
}

// http://doc.spip.org/@nom_jour
function nom_jour($numdate, $forme = '') {
	if(!($forme == 'abbr' OR $forme == 'initiale')) $forme = '';
	return affdate_base($numdate, 'nom_jour', $forme);
}

// http://doc.spip.org/@jour
function jour($numdate) {
	return affdate_base($numdate, 'jour');
}

// http://doc.spip.org/@journum
function journum($numdate) {
	return affdate_base($numdate, 'journum');
}

// http://doc.spip.org/@mois
function mois($numdate) {
	return affdate_base($numdate, 'mois');
}

// http://doc.spip.org/@nom_mois
function nom_mois($numdate, $forme='') {
	if(!($forme == 'abbr')) $forme = '';
	return affdate_base($numdate, 'nom_mois', $forme);
}

// http://doc.spip.org/@annee
function annee($numdate) {
	return affdate_base($numdate, 'annee');
}

// http://doc.spip.org/@saison
function saison($numdate) {
	return affdate_base($numdate, 'saison');
}

// http://doc.spip.org/@saison_annee
function saison_annee($numdate) {
	return affdate_base($numdate, 'saison_annee');
}

// http://doc.spip.org/@affdate
function affdate($numdate, $format='entier') {
	return affdate_base($numdate, $format);
}

// http://doc.spip.org/@affdate_court
function affdate_court($numdate, $annee_courante=null) {
	return affdate_base($numdate, 'court', array('annee_courante'=>$annee_courante));
}

// http://doc.spip.org/@affdate_jourcourt
function affdate_jourcourt($numdate, $annee_courante=null) {
	return affdate_base($numdate, 'jourcourt', array('annee_courante'=>$annee_courante));
}

// http://doc.spip.org/@affdate_mois_annee
function affdate_mois_annee($numdate) {
	return affdate_base($numdate, 'mois_annee');
}

// http://doc.spip.org/@affdate_heure
function affdate_heure($numdate) {
	$date_array = recup_date($numdate);
	if (!$date_array) return;
	list($annee, $mois, $jour, $heures, $minutes, $sec)= $date_array;
	return _T('date_fmt_jour_heure', array('jour' => affdate($numdate), 'heure' =>  _T('date_fmt_heures_minutes', array('h'=> $heures, 'm'=> $minutes))));
}

/**
 * Afficher de facon textuelle les dates de debut et fin en fonction des cas
 * - Lundi 20 fevrier a 18h
 * - Le 20 fevrier de 18h a 20h
 * - Du 20 au 23 fevrier
 * - Du 20 fevrier au 30 mars
 * - Du 20 fevrier 2007 au 30 mars 2008
 * $horaire='oui' ou true permet d'afficher l'horaire, toute autre valeur n'indique que le jour
 * $forme peut contenir une ou plusieurs valeurs parmi
 *  - abbr (afficher le nom des jours en abrege)
 *  - hcal (generer une date au format hcal)
 *  - jour (forcer l'affichage des jours)
 *  - annee (forcer l'affichage de l'annee)
 *
 * @param string $date_debut
 * @param string $date_fin
 * @param string $horaire
 * @param string $forme
 *   abbr pour afficher le nom du jour en abrege (Dim. au lieu de Dimanche)
 *   annee pour forcer l'affichage de l'annee courante
 *   jour pour forcer l'affichage du nom du jour
 *   hcal pour avoir un markup microformat abbr
 * @return string
 */
function affdate_debut_fin($date_debut, $date_fin, $horaire = 'oui', $forme=''){
	$abbr = $jour = '';
	$affdate = "affdate_jourcourt";
	if (strpos($forme,'abbr') !==false) $abbr = 'abbr';
	if (strpos($forme,'annee')!==false) $affdate = 'affdate';
	if (strpos($forme,'jour') !==false) $jour = 'jour';
	
	$dtstart = $dtend = $dtabbr = "";
	if (strpos($forme,'hcal')!==false) {
		$dtstart = "<abbr class='dtstart' title='".date_iso($date_debut)."'>";
		$dtend = "<abbr class='dtend' title='".date_iso($date_fin)."'>";
		$dtabbr = "</abbr>";
	}

	$date_debut = strtotime($date_debut);
	$date_fin = strtotime($date_fin);
	$d = date("Y-m-d", $date_debut);
	$f = date("Y-m-d", $date_fin);
	$h = ($horaire==='oui' OR $horaire===true);
	$hd = _T('date_fmt_heures_minutes_court', array('h'=> date("H",$date_debut), 'm'=> date("i",$date_debut)));
	$hf = _T('date_fmt_heures_minutes_court', array('h'=> date("H",$date_fin), 'm'=> date("i",$date_fin)));

	if ($d==$f)
	{ // meme jour
		$nomjour = nom_jour($d,$abbr);
		$s = $affdate($d);
		$s = _T('date_fmt_jour',array('nomjour'=>$nomjour,'jour' => $s));
		if ($h){
			if ($hd==$hf){
				// Lundi 20 fevrier a 18h25
				$s = spip_ucfirst(_T('date_fmt_jour_heure',array('jour'=>$s,'heure'=>$hd)));
				$s = "$dtstart$s$dtabbr";
			}else{
				// Le <abbr...>lundi 20 fevrier de 18h00</abbr> a <abbr...>20h00</abbr>
				if($dtabbr && $dtstart && $dtend)
					$s = _T('date_fmt_jour_heure_debut_fin_abbr',array('jour'=>spip_ucfirst($s),'heure_debut'=>$hd,'heure_fin'=>$hf,'dtstart'=>$dtstart,'dtend'=>$dtend,'dtabbr'=>$dtabbr));
				// Le lundi 20 fevrier de 18h00 a 20h00
				else
					$s = spip_ucfirst(_T('date_fmt_jour_heure_debut_fin',array('jour'=>$s,'heure_debut'=>$hd,'heure_fin'=>$hf)));
			}
		}else{
			if($dtabbr && $dtstart)
				$s = $dtstart.spip_ucfirst($s).$dtabbr;
			else
				$s = spip_ucfirst($s);
		}
	}
	else if ((date("Y-m",$date_debut))==date("Y-m",$date_fin))
	{ // meme annee et mois, jours differents
		if(!$h)
			$date_debut = jour($d);
		else
			$date_debut = affdate_jourcourt($d,date("Y",$date_fin));
		$date_fin = $affdate($f);
		if($jour){
			$nomjour_debut = nom_jour($d,$abbr);
			$date_debut = _T('date_fmt_jour',array('nomjour'=>$nomjour_debut,'jour' => $date_debut));
			$nomjour_fin = nom_jour($f,$abbr);
			$date_fin = _T('date_fmt_jour',array('nomjour'=>$nomjour_fin,'jour' => $date_fin));
		}
		if ($h){
			$date_debut = _T('date_fmt_jour_heure',array('jour'=>$date_debut,'heure'=>$hd));
			$date_fin = _T('date_fmt_jour_heure',array('jour'=>$date_fin,'heure'=>$hf));
		}
		$date_debut = $dtstart.$date_debut.$dtabbr;
		$date_fin = $dtend.$date_fin.$dtabbr;
		
		$s = _T('date_fmt_periode',array('date_debut' => $date_debut,'date_fin'=>$date_fin));
	}
	else {
		$date_debut = affdate_jourcourt($d,date("Y",$date_fin));
		$date_fin = $affdate($f);
		if($jour){
			$nomjour_debut = nom_jour($d,$abbr);
			$date_debut = _T('date_fmt_jour',array('nomjour'=>$nomjour_debut,'jour' => $date_debut));
			$nomjour_fin = nom_jour($f,$abbr);
			$date_fin = _T('date_fmt_jour',array('nomjour'=>$nomjour_fin,'jour' => $date_fin));
		}
		if ($h){
			$date_debut = _T('date_fmt_jour_heure',array('jour'=>$date_debut,'heure'=>$hd)); 
			$date_fin = _T('date_fmt_jour_heure',array('jour'=>$date_fin,'heure'=>$hf));
		}
		
		$date_debut = $dtstart.$date_debut.$dtabbr;
		$date_fin=$dtend.$date_fin.$dtabbr;
		$s = _T('date_fmt_periode',array('date_debut' => $date_debut,'date_fin'=>$date_fin));
		
	}
	return $s;
}

/**
 * Alignements en HTML (Old-style, preferer CSS)
 * Cette fonction ne cree pas de paragraphe
 *
 * http://doc.spip.org/@aligner
 *
 * @param  $letexte
 * @param string $justif
 * @return string
 */
function aligner($letexte, $justif='') {
	$letexte = trim($letexte);
	if (!strlen($letexte)) return '';

	// Paragrapher rapidement
	$letexte = "<div style='text-align:$justif'>"
		. $letexte
	  ."</div>";

	return $letexte;
}
// http://doc.spip.org/@justifier
function justifier($letexte) { return aligner($letexte,'justify');}
// http://doc.spip.org/@aligner_droite
function aligner_droite($letexte) { return aligner($letexte,'right');}
// http://doc.spip.org/@aligner_gauche
function aligner_gauche($letexte) {return aligner($letexte,'left');}
// http://doc.spip.org/@centrer
function centrer($letexte) {return aligner($letexte,'center');}

// http://doc.spip.org/@style_align
function style_align($bof) {
	global $spip_lang_left;
	return "text-align: $spip_lang_left";
}

//
// Export iCal
//

// http://doc.spip.org/@filtrer_ical
function filtrer_ical($texte) {
	#include_spip('inc/charsets');
	$texte = html2unicode($texte);
	$texte = unicode2charset(charset2unicode($texte, $GLOBALS['meta']['charset'], 1), 'utf-8');
	$texte = preg_replace("/\n/", " ", $texte);
	$texte = preg_replace("/,/", "\,", $texte);

	return $texte;
}

// http://doc.spip.org/@date_ical
function date_ical($date, $addminutes = 0) {
	list($heures, $minutes, $secondes) = recup_heure($date);
	list($annee, $mois, $jour) = recup_date($date);
	return date("Ymd\THis", 
		    mktime($heures, $minutes+$addminutes,$secondes,$mois,$jour,$annee));
}

// date_iso retourne la date au format "RFC 3339" / "ISO 8601"
// voir http://www.php.net/manual/fr/ref.datetime.php#datetime.constants
// http://doc.spip.org/@date_iso
function date_iso($date_heure) {
	list($annee, $mois, $jour) = recup_date($date_heure);
	list($heures, $minutes, $secondes) = recup_heure($date_heure);
	$time = @mktime($heures, $minutes, $secondes, $mois, $jour, $annee);
	return gmdate('Y-m-d\TH:i:s\Z', $time);
}

// date_822 retourne la date au format "RFC 822"
// utilise pour <pubdate> dans certains feeds RSS
// http://doc.spip.org/@date_822
function date_822($date_heure) {
	list($annee, $mois, $jour) = recup_date($date_heure);
	list($heures, $minutes, $secondes) = recup_heure($date_heure);
	$time = mktime($heures, $minutes, $secondes, $mois, $jour, $annee);
	return date('r', $time);
}

// http://doc.spip.org/@date_anneemoisjour
function date_anneemoisjour($d)  {
	if (!$d) $d = date("Y-m-d");
	return  substr($d, 0, 4) . substr($d, 5, 2) .substr($d, 8, 2);
}

// http://doc.spip.org/@date_anneemois
function date_anneemois($d)  {
	if (!$d) $d = date("Y-m-d");
	return  substr($d, 0, 4) . substr($d, 5, 2);
}

// http://doc.spip.org/@date_debut_semaine
function date_debut_semaine($annee, $mois, $jour) {
  $w_day = date("w", mktime(0,0,0,$mois, $jour, $annee));
  if ($w_day == 0) $w_day = 7; // Gaffe: le dimanche est zero
  $debut = $jour-$w_day+1;
  return date("Ymd", mktime(0,0,0,$mois,$debut,$annee));
}

// http://doc.spip.org/@date_fin_semaine
function date_fin_semaine($annee, $mois, $jour) {
  $w_day = date("w", mktime(0,0,0,$mois, $jour, $annee));
  if ($w_day == 0) $w_day = 7; // Gaffe: le dimanche est zero
  $debut = $jour-$w_day+1;
  return date("Ymd", mktime(0,0,0,$mois,$debut+6,$annee));
}


//
// Recuperation de donnees dans le champ extra
// Ce filtre n'a de sens qu'avec la balise #EXTRA
//
// http://doc.spip.org/@extra
function extra($letexte, $champ) {
	$champs = unserialize($letexte);
	return $champs[$champ];
}

// postautobr : transforme les sauts de ligne en _
// http://doc.spip.org/@post_autobr
function post_autobr($texte, $delim="\n_ ") {
	if (!function_exists('echappe_html'))
		include_spip('inc/texte_mini');
	$texte = str_replace("\r\n", "\r", $texte);
	$texte = str_replace("\r", "\n", $texte);

	if (preg_match(",\n+$,", $texte, $fin))
		$texte = substr($texte, 0, -strlen($fin = $fin[0]));
	else
		$fin = '';

	$texte = echappe_html($texte, '', true);

	// echapper les modeles
	if (strpos($texte,"<")!==false){
		include_spip('inc/lien');
		if (defined('_PREG_MODELE')){
			$preg_modeles = "@"._PREG_MODELE."@imsS";
			$texte = echappe_html($texte, '', true, $preg_modeles);
		}
	}

	$debut = '';
	$suite = $texte;
	while ($t = strpos('-'.$suite, "\n", 1)) {
		$debut .= substr($suite, 0, $t-1);
		$suite = substr($suite, $t);
		$car = substr($suite, 0, 1);
		if (($car<>'-') AND ($car<>'_') AND ($car<>"\n") AND ($car<>"|") AND ($car<>"}")
		AND !preg_match(',^\s*(\n|</?(quote|div|dl|dt|dd)|$),S',($suite))
		AND !preg_match(',</?(quote|div|dl|dt|dd)> *$,iS', $debut)) {
			$debut .= $delim;
		} else
			$debut .= "\n";
		if (preg_match(",^\n+,", $suite, $regs)) {
			$debut.=$regs[0];
			$suite = substr($suite, strlen($regs[0]));
		}
	}
	$texte = $debut.$suite;

	$texte = echappe_retour($texte);
	return $texte.$fin;
}


define('_EXTRAIRE_MULTI', "@<multi>(.*?)</multi>@sS");

// Extraire et transformer les blocs multi ; on indique la langue courante
// pour ne pas mettre de span@lang=fr si on est deja en fr
// http://doc.spip.org/@extraire_multi
function extraire_multi($letexte, $lang=null, $echappe_span=false) {
	if (preg_match_all(_EXTRAIRE_MULTI, $letexte, $regs, PREG_SET_ORDER)) {
		if (!$lang) $lang = $GLOBALS['spip_lang'];

		foreach ($regs as $reg) {
			// chercher la version de la langue courante
			$trads = extraire_trads($reg[1]);
			if ($l = approcher_langue($trads, $lang)) {
				$trad = $trads[$l];
			} else {
				include_spip('inc/texte');
				// langue absente, prendre la premiere dispo
				// mais typographier le texte selon les regles de celle-ci
				// Attention aux blocs multi sur plusieurs lignes
				$l = key($trads);
				$trad = $trads[$l];
				$typographie = charger_fonction(lang_typo($l), 'typographie');
				$trad = $typographie($trad);
				include_spip('inc/texte');
				// Tester si on echappe en span ou en div
				// il ne faut pas echapper en div si propre produit un seul paragraphe
				$trad_propre = preg_replace(",(^<p[^>]*>|</p>$),Uims","",propre($trad));
				$mode = preg_match(',</?('._BALISES_BLOCS.')[>[:space:]],iS', $trad_propre) ? 'div' : 'span';
				$trad = code_echappement($trad, 'multi', false, $mode);
				$trad = str_replace("'", '"', inserer_attribut($trad, 'lang', $l));
				if (lang_dir($l) !== lang_dir($lang))
					$trad = str_replace("'", '"', inserer_attribut($trad, 'dir', lang_dir($l)));
				if (!$echappe_span)
					$trad = echappe_retour($trad, 'multi');
			}
			$letexte = str_replace($reg[0], $trad, $letexte);
		}
	}

	return $letexte;
}

// convertit le contenu d'une balise multi en un tableau
// http://doc.spip.org/@extraire_trad
function extraire_trads($bloc) {
	$lang = '';
// ce reg fait planter l'analyse multi s'il y a de l'{italique} dans le champ
//	while (preg_match("/^(.*?)[{\[]([a-z_]+)[}\]]/siS", $bloc, $regs)) {
	while (preg_match("/^(.*?)[\[]([a-z_]+)[\]]/siS", $bloc, $regs)) {
		$texte = trim($regs[1]);
		if ($texte OR $lang)
			$trads[$lang] = $texte;
		$bloc = substr($bloc, strlen($regs[0]));
		$lang = $regs[2];
	}
	$trads[$lang] = $bloc;

	return $trads;
}

// Calculer l'initiale d'un nom
function initiale($nom){
	return spip_substr(trim(strtoupper(extraire_multi($nom))),0,1);
}

//
// Ce filtre retourne la donnee si c'est la premiere fois qu'il la voit ;
// possibilite de gerer differentes "familles" de donnees |unique{famille}
# |unique{famille,1} affiche le nombre d'elements affiches (preferer toutefois #TOTAL_UNIQUE)
# ameliorations possibles :
# 1) si la donnee est grosse, mettre son md5 comme cle
# 2) purger $mem quand on change de squelette (sinon bug inclusions)
//
// http://www.spip.net/@unique
// http://doc.spip.org/@unique
function unique($donnee, $famille='', $cpt = false) {
	static $mem = array();
	// permettre de vider la pile et de la restaurer
	// pour le calcul de introduction...
	if ($famille=='_spip_raz_'){
		$tmp = $mem;
		$mem = array();
		return $tmp;
	} elseif ($famille=='_spip_set_'){
		$mem = $donnee;
		return;
	}
	// eviter une notice
	if (!isset($mem[$famille])) {
		$mem[$famille] = array();
	}
	if ($cpt) {
		return count($mem[$famille]);
	}
	// eviter une notice
	if (!isset($mem[$famille][$donnee])) {
		$mem[$famille][$donnee] = 0;
	}
	if (!($mem[$famille][$donnee]++)) {
		return $donnee;
	}
}

//
// Filtre |alterner
//
// Exemple [(#COMPTEUR_BOUCLE|alterner{'bleu','vert','rouge'})]
//
// http://doc.spip.org/@alterner
function alterner($i) {
	// recuperer les arguments (attention fonctions un peu space)
	$num = func_num_args();
	$args = func_get_args();

	if($num == 2 && is_array($args[1])) {
    $args = $args[1];
    array_unshift($args,'');
    $num = count($args);
  }

	// renvoyer le i-ieme argument, modulo le nombre d'arguments
	return $args[(intval($i)-1)%($num-1)+1];
}

// recuperer un attribut d'une balise html
// ($complet demande de retourner $r)
// la regexp est mortelle : cf. tests/filtres/extraire_attribut.php
// Si on a passe un tableau de balises, renvoyer un tableau de resultats
// (dans ce cas l'option $complet n'est pas disponible)
// http://doc.spip.org/@extraire_attribut
function extraire_attribut($balise, $attribut, $complet = false) {
	if (is_array($balise)) {
		array_walk($balise,
			create_function('&$a,$key,$t',
				'$a = extraire_attribut($a,$t);'
			),
			$attribut);
		return $balise;
	}
	if (preg_match(
	',(^.*?<(?:(?>\s*)(?>[\w:.-]+)(?>(?:=(?:"[^"]*"|\'[^\']*\'|[^\'"]\S*))?))*?)(\s+'
	.$attribut
	.'(?:=\s*("[^"]*"|\'[^\']*\'|[^\'"]\S*))?)()([^>]*>.*),isS',

	$balise, $r)) {
		if ($r[3][0] == '"' || $r[3][0] == "'") {
			$r[4] = substr($r[3], 1, -1);
			$r[3] = $r[3][0];
		} elseif ($r[3]!=='') {
			$r[4] = $r[3]; 
			$r[3] = '';
		} else {
			$r[4] = trim($r[2]); 
		}
		$att = $r[4];
		if (strpos($att,"&#")!==false)
			$att = str_replace(array("&#039;","&#39;","&#034;","&#34;"), array("'","'",'"','"'), $att);
		$att = filtrer_entites($att);
	}
	else
		$att = NULL;

	if ($complet)
		return array($att, $r);
	else
		return $att;
}

/**
 * modifier (ou inserer) un attribut html dans une balise
 *
 * http://doc.spip.org/@inserer_attribut
 *
 * @param string $balise
 * @param string $attribut
 * @param string $val
 * @param bool $proteger
 * @param bool $vider
 * @return string
 */
function inserer_attribut($balise, $attribut, $val, $proteger=true, $vider=false) {
	// preparer l'attribut
	// supprimer les &nbsp; etc mais pas les balises html
	// qui ont un sens dans un attribut value d'un input
	if ($proteger) $val = attribut_html($val,false);

	// echapper les ' pour eviter tout bug
	$val = str_replace("'", "&#039;", $val);
	if ($vider AND strlen($val)==0)
		$insert = '';
	else
		$insert = " $attribut='$val'";

	list($old, $r) = extraire_attribut($balise, $attribut, true);

	if ($old !== NULL) {
		// Remplacer l'ancien attribut du meme nom
		$balise = $r[1].$insert.$r[5];
	}
	else {
		// preferer une balise " />" (comme <img />)
		if (preg_match(',/>,', $balise))
			$balise = preg_replace(",\s?/>,S", $insert." />", $balise, 1);
		// sinon une balise <a ...> ... </a>
		else
			$balise = preg_replace(",\s?>,S", $insert.">", $balise, 1);
	}

	return $balise;
}

// http://doc.spip.org/@vider_attribut
function vider_attribut ($balise, $attribut) {
	return inserer_attribut($balise, $attribut, '', false, true);
}


/**
 * Un filtre pour determiner le nom du satut des inscrits
 *
 * @param void|int $id
 * @param string $mode
 * @return string
 */
function tester_config($id, $mode='') {
	include_spip('action/inscrire_auteur');
	return tester_statut_inscription($mode, $id);
}

//
// Quelques fonctions de calcul arithmetique
//
// http://doc.spip.org/@plus
function plus($a,$b) {
	return $a+$b;
}
// http://doc.spip.org/@moins
function moins($a,$b) {
	return $a-$b;
}
// http://doc.spip.org/@mult
function mult($a,$b) {
	return $a*$b;
}
// http://doc.spip.org/@div
function div($a,$b) {
	return $b?$a/$b:0;
}
// http://doc.spip.org/@modulo
function modulo($nb, $mod, $add=0) {
	return ($mod?$nb%$mod:0)+$add;
}


/**
 * Vérifie qu'un nom (d'auteur) ne comporte pas d'autres tags que <multi>
 * et ceux volontairement spécifiés dans la constante
 *
 * @param string $nom
 *      Nom (signature) proposé
 * @return bool
 *      - false si pas conforme,
 *      - true sinon
**/
function nom_acceptable($nom) {
	if (!is_string($nom)) {
		return false;
	}
	if (!defined('_TAGS_NOM_AUTEUR')) define('_TAGS_NOM_AUTEUR','');
	$tags_acceptes = array_unique(explode(',', 'multi,' . _TAGS_NOM_AUTEUR));
	foreach($tags_acceptes as $tag) {
		if (strlen($tag)) {
			$remp1[] = '<'.trim($tag).'>';
			$remp1[] = '</'.trim($tag).'>';
			$remp2[] = '\x60'.trim($tag).'\x61';
			$remp2[] = '\x60/'.trim($tag).'\x61';
		}
	}	
	$v_nom = str_replace($remp2, $remp1, supprimer_tags(str_replace($remp1, $remp2, $nom)));
	return str_replace('&lt;', '<', $v_nom) == $nom;
}

// Verifier la conformite d'une ou plusieurs adresses email
//  retourne false ou la  normalisation de la derniere adresse donnee
// http://doc.spip.org/@email_valide
function email_valide($adresses) {
	// eviter d'injecter n'importe quoi dans preg_match
	if (!is_string($adresses))
		return false;

	// Si c'est un spammeur autant arreter tout de suite
	if (preg_match(",[\n\r].*(MIME|multipart|Content-),i", $adresses)) {
		spip_log("Tentative d'injection de mail : $adresses");
		return false;
	}

	foreach (explode(',', $adresses) as $v) {
		// nettoyer certains formats
		// "Marie Toto <Marie@toto.com>"
		$adresse = trim(preg_replace(",^[^<>\"]*<([^<>\"]+)>$,i", "\\1", $v));
		// RFC 822
		if (!preg_match('#^[^()<>@,;:\\"/[:space:]]+(@([-_0-9a-z]+\.)*[-_0-9a-z]+)$#i', $adresse))
			return false;
	}
	return $adresse;
}

// http://doc.spip.org/@afficher_enclosures
function afficher_enclosures($tags) {
	$s = array();
	foreach (extraire_balises($tags, 'a') as $tag) {
		if (extraire_attribut($tag, 'rel') == 'enclosure'
		AND $t = extraire_attribut($tag, 'href')) {
			$s[] = preg_replace(',>[^<]+</a>,S', 
				'>'
				.http_img_pack('attachment-16.png', $t,
					'title="'.attribut_html($t).'"')
				.'</a>', $tag);
		}
	}
	return join('&nbsp;', $s);
}
// http://doc.spip.org/@afficher_tags
function afficher_tags($tags, $rels='tag,directory') {
	$s = array();
	foreach (extraire_balises($tags, 'a') as $tag) {
		$rel = extraire_attribut($tag, 'rel');
		if (strstr(",$rels,", ",$rel,"))
			$s[] = $tag;
	}
	return join(', ', $s);
}

// Passe un <enclosure url="fichier" length="5588242" type="audio/mpeg"/>
// au format microformat <a rel="enclosure" href="fichier" ...>fichier</a>
// attention length="zz" devient title="zz", pour rester conforme
// http://doc.spip.org/@enclosure2microformat
function enclosure2microformat($e) {
	if (!$url = filtrer_entites(extraire_attribut($e, 'url')))
		$url = filtrer_entites(extraire_attribut($e, 'href'));
	$type = extraire_attribut($e, 'type');
	if (!$length = extraire_attribut($e, 'length')) {
		# <media:content : longeur dans fileSize. On tente.
		$length = extraire_attribut($e, 'fileSize');
	}
	$fichier = basename($url);
	return '<a rel="enclosure"'
		. ($url? ' href="'.spip_htmlspecialchars($url).'"' : '')
		. ($type? ' type="'.spip_htmlspecialchars($type).'"' : '')
		. ($length? ' title="'.spip_htmlspecialchars($length).'"' : '')
		. '>'.$fichier.'</a>';
}
// La fonction inverse
// http://doc.spip.org/@microformat2enclosure
function microformat2enclosure($tags) {
	$enclosures = array();
	foreach (extraire_balises($tags, 'a') as $e)
	if (extraire_attribut($e, 'rel') == 'enclosure') {
		$url = filtrer_entites(extraire_attribut($e, 'href'));
		$type = extraire_attribut($e, 'type');
		if (!$length = intval(extraire_attribut($e, 'title')))
			$length = intval(extraire_attribut($e, 'length')); # vieux data
		$fichier = basename($url);
		$enclosures[] = '<enclosure'
			. ($url? ' url="'.spip_htmlspecialchars($url).'"' : '')
			. ($type? ' type="'.spip_htmlspecialchars($type).'"' : '')
			. ($length? ' length="'.$length.'"' : '')
			. ' />';
	}
	return join("\n", $enclosures);
}
// Creer les elements ATOM <dc:subject> a partir des tags
// http://doc.spip.org/@tags2dcsubject
function tags2dcsubject($tags) {
	$subjects = '';
	foreach (extraire_balises($tags, 'a') as $e) {
		if (extraire_attribut($e, rel) == 'tag') {
			$subjects .= '<dc:subject>'
				. texte_backend(textebrut($e))
				. '</dc:subject>'."\n";
		}
	}
	return $subjects;
}

// retourne la premiere balise du type demande
// ex: [(#DESCRIPTIF|extraire_balise{img})]
// Si on a passe un tableau de textes, renvoyer un tableau de resultats
// http://doc.spip.org/@extraire_balise
function extraire_balise($texte, $tag='a') {
	if (is_array($texte)) {
		array_walk($texte,
			create_function('&$a,$key,$t', '$a = extraire_balise($a,$t);'),
			$tag);
		return $texte;
	}

	if (preg_match(
	",<$tag\b[^>]*(/>|>.*</$tag\b[^>]*>|>),UimsS",
	$texte, $regs))
		return $regs[0];
}

// extraire toutes les balises du type demande, sous forme de tableau
// Si on a passe un tableau de textes, renvoyer un tableau de resultats
// http://doc.spip.org/@extraire_balises
function extraire_balises($texte, $tag='a') {
	if (is_array($texte)) {
		array_walk($texte,
			create_function('&$a,$key,$t', '$a = extraire_balises($a,$t);'),
			$tag);
		return $texte;
	}

	if (preg_match_all(
	",<${tag}\b[^>]*(/>|>.*</${tag}\b[^>]*>|>),UimsS",
	$texte, $regs, PREG_PATTERN_ORDER))
		return $regs[0];
	else
		return array();
}

// comme in_array mais renvoie son 3e arg si le 2er arg n'est pas un tableau
// prend ' ' comme representant de vrai et '' de faux

// http://doc.spip.org/@in_any
function in_any($val, $vals, $def='') {
	if (!is_array($vals) AND $v=unserialize($vals)) $vals = $v;
  return (!is_array($vals) ? $def : (in_array($val, $vals) ? ' ' : ''));
}

// valeur_numerique("3*2") => 6
// n'accepte que les *, + et - (a ameliorer si on l'utilise vraiment)
// http://doc.spip.org/@valeur_numerique
function valeur_numerique($expr) {
	$a = 0;
	if (preg_match(',^[0-9]+(\s*[+*-]\s*[0-9]+)*$,S', trim($expr)))
		eval("\$a = $expr;");
	return intval($a);
}

// http://doc.spip.org/@regledetrois
function regledetrois($a,$b,$c)
{
  return round($a*$b/$c);
}

// Fournit la suite de Input-Hidden correspondant aux parametres de
// l'URL donnee en argument, compatible avec les types_urls depuis [14447].
// cf. tests/filtres/form_hidden.html
// http://doc.spip.org/@form_hidden
function form_hidden($action) {

	$contexte = array();
	include_spip('inc/urls');
	if ($p = urls_decoder_url($action, '')
		AND reset($p)) {
		$fond = array_shift($p);
		if ($fond!='404'){
			$contexte = array_shift($p);
			$contexte['page'] = $fond;
			$action = preg_replace('/([?]'.preg_quote($fond).'[^&=]*[0-9]+)(&|$)/', '?&', $action);
		}
	}
	// defaire ce qu'a injecte urls_decoder_url : a revoir en modifiant la signature de urls_decoder_url
	if (defined('_DEFINIR_CONTEXTE_TYPE') AND _DEFINIR_CONTEXTE_TYPE)
		unset($contexte['type']);
	if (defined('_DEFINIR_CONTEXTE_TYPE_PAGE') AND _DEFINIR_CONTEXTE_TYPE_PAGE)
		unset($contexte['type-page']);

	// on va remplir un tableau de valeurs en prenant bien soin de ne pas
	// ecraser les elements de la forme mots[]=1&mots[]=2
	$values = array();

	// d'abord avec celles de l'url
	if (false !== ($p = strpos($action, '?'))) {
		foreach(preg_split('/&(amp;)?/S',substr($action,$p+1)) as $c){
			list($var,$val) = explode('=', $c, 2);
			if ($var) {
				$val =  rawurldecode($val);
				$var =  rawurldecode($var); // decoder les [] eventuels
				if (preg_match(',\[\]$,S', $var))
					$values[] = array($var, $val);
				else if (!isset($values[$var]))
					$values[$var] = array($var, $val);
			}
		}
	}

	// ensuite avec celles du contexte, sans doublonner !
	foreach($contexte as $var=>$val)
		if (preg_match(',\[\]$,S', $var))
			$values[] = array($var, $val);
		else if (!isset($values[$var]))
			$values[$var] = array($var, $val);

	// puis on rassemble le tout
	$hidden = array();
	foreach($values as $value) {
		list($var,$val) = $value;
		$hidden[] = '<input name="'
			. entites_html($var)
			.'"'
			. (is_null($val)
				? ''
				: ' value="'.entites_html($val).'"'
				)
			. ' type="hidden"'."\n/>";
	}
	return join("", $hidden);
}

// http://doc.spip.org/@filtre_bornes_pagination_dist
function filtre_bornes_pagination_dist($courante, $nombre, $max = 10) {
	if($max<=0 OR $max>=$nombre)
		return array(1, $nombre);

	$premiere = max(1, $courante-floor(($max-1)/2));
	$derniere = min($nombre, $premiere+$max-2);
	$premiere = $derniere == $nombre ? $derniere-$max+1 : $premiere;
	return array($premiere, $derniere);
}


// Ces trois fonctions permettent de simuler les filtres |reset et |end
// pour extraire la premiere ou la derniere valeur d'un tableau ; utile
// pour la pagination (mais peut-etre a refaire plus simplement)
// http://doc.spip.org/@filtre_valeur_tableau
function filtre_valeur_tableau($array, $index) {
	if (!is_array($array)
	OR !isset($array[$index]))
		return null;
	return $array[$index];
}
// http://doc.spip.org/@filtre_reset
function filtre_reset($array) {
	return !is_array($array) ? null : reset($array);
}
// http://doc.spip.org/@filtre_end
function filtre_end($array) {
	return !is_array($array) ? null : end($array);
}

// http://doc.spip.org/@filtre_push
function filtre_push($array, $val) {
	if($array == '' OR !array_push($array, $val)) return '';
	return $array;
}

// http://doc.spip.org/@filtre_find
function filtre_find($array, $val) {
	return (is_array($array) AND in_array($val, $array));
}


//
// fonction standard de calcul de la balise #PAGINATION
// on peut la surcharger en definissant filtre_pagination dans mes_fonctions
//

// http://doc.spip.org/@filtre_pagination_dist
function filtre_pagination_dist($total, $nom, $position, $pas, $liste = true, $modele='', $connect='', $env=array()) {
	static $ancres = array();
	if ($pas<1) return '';
	$ancre = 'pagination'.$nom; // #pagination_articles
	$debut = 'debut'.$nom; // 'debut_articles'

	// n'afficher l'ancre qu'une fois
	if (!isset($ancres[$ancre]))
		$bloc_ancre = $ancres[$ancre] = "<a name='".$ancre."' id='".$ancre."'></a>";
	else $bloc_ancre = '';
	// liste = false : on ne veut que l'ancre
	if (!$liste)
		return $ancres[$ancre];

	$pagination = array(
		'debut' => $debut,
		'url' => parametre_url(self(),'fragment',''), // nettoyer l'id ahah eventuel
		'total' => $total,
		'position' => intval($position),
		'pas' => $pas,
		'nombre_pages' => floor(($total-1)/$pas)+1,
		'page_courante' => floor(intval($position)/$pas)+1,
		'ancre' => $ancre,
		'bloc_ancre' => $bloc_ancre
	);
	if (is_array($env))
		$pagination = array_merge($env,$pagination);

	// Pas de pagination
	if ($pagination['nombre_pages']<=1)
		return '';

	if ($modele) $modele = '_'.$modele;

	return recuperer_fond("modeles/pagination$modele", $pagination, array('trim'=>true), $connect);
}

// passer les url relatives a la css d'origine en url absolues
// http://doc.spip.org/@urls_absolues_css
function urls_absolues_css($contenu, $source) {
	$path = suivre_lien(url_absolue($source),'./');

	return preg_replace_callback(
		",url\s*\(\s*['\"]?([^'\"/][^:]*)['\"]?\s*\),Uims",
		create_function('$x',
			'return "url(\"".suivre_lien("'.$path.'",$x[1])."\")";'
		), $contenu);
}

// recuperere le chemin d'une css existante et :
// 1. regarde si une css inversee droite-gauche existe dans le meme repertoire
// 2. sinon la cree (ou la recree) dans _DIR_VAR/cache_css/
// SI on lui donne a manger une feuille nommee _rtl.css il va faire l'inverse
// http://doc.spip.org/@direction_css
function direction_css ($css, $voulue='') {
	if (!preg_match(',(_rtl)?\.css$,i', $css, $r)) return $css;

	// si on a precise le sens voulu en argument, le prendre en compte
	if ($voulue = strtolower($voulue)) {
		if ($voulue != 'rtl' AND $voulue != 'ltr')
			$voulue = lang_dir($voulue);
	}
	else
		$voulue =  lang_dir();

	$r = count($r) > 1;
	$right = $r ? 'left' : 'right'; // 'right' de la css lue en entree
	$dir = $r ? 'rtl' : 'ltr';
	$ndir = $r ? 'ltr' : 'rtl';

	if ($voulue == $dir)
		return $css;

	if (
		// url absolue
		preg_match(",^http:,i",$css)
		// ou qui contient un ?
		OR (($p=strpos($css,'?'))!==FALSE)) {
		$distant = true;
		$cssf = parse_url($css);
		$cssf = $cssf['path'].($cssf['query']?"?".$cssf['query']:"");
		$cssf = preg_replace(',[?:&=],', "_", $cssf);
	}
	else {
		$distant = false;
		$cssf = $css;
		// 1. regarder d'abord si un fichier avec la bonne direction n'est pas aussi
		//propose (rien a faire dans ce cas)
		$f = preg_replace(',(_rtl)?\.css$,i', '_'.$ndir.'.css', $css);
		if (@file_exists($f))
			return $f;
	}

	// 2.
	$dir_var = sous_repertoire (_DIR_VAR, 'cache-css');
	$f = $dir_var
		. preg_replace(',.*/(.*?)(_rtl)?\.css,', '\1', $cssf)
		. '.' . substr(md5($cssf), 0,4) . '_' . $ndir . '.css';

	// la css peut etre distante (url absolue !)
	if ($distant){
		include_spip('inc/distant');
		$contenu = recuperer_page($css);
		if (!$contenu) return $css;
	}
	else {
		if ((@filemtime($f) > @filemtime($css))
			AND (_VAR_MODE != 'recalcul'))
			return $f;
		if (!lire_fichier($css, $contenu))
			return $css;
	}

	$contenu = str_replace(
		array('right', 'left', '@@@@L E F T@@@@'),
		array('@@@@L E F T@@@@', 'right', 'left'),
		$contenu);
	
	// reperer les @import auxquels il faut propager le direction_css
	preg_match_all(",\@import\s*url\s*\(\s*['\"]?([^'\"/][^:]*)['\"]?\s*\),Uims",$contenu,$regs);
	$src = array();$src_direction_css = array();$src_faux_abs=array();
	$d = dirname($css);
	foreach($regs[1] as $k=>$import_css){
		$css_direction = direction_css("$d/$import_css",$voulue);
		// si la css_direction est dans le meme path que la css d'origine, on tronque le path, elle sera passee en absolue
		if (substr($css_direction,0,strlen($d)+1)=="$d/") $css_direction = substr($css_direction,strlen($d)+1);
		// si la css_direction commence par $dir_var on la fait passer pour une absolue
		elseif (substr($css_direction,0,strlen($dir_var))==$dir_var) {
			$css_direction = substr($css_direction,strlen($dir_var));
			$src_faux_abs["/@@@@@@/".$css_direction] = $css_direction;
			$css_direction = "/@@@@@@/".$css_direction;
		}
		$src[] = $regs[0][$k];
		$src_direction_css[] = str_replace($import_css,$css_direction,$regs[0][$k]);
	}
	$contenu = str_replace($src,$src_direction_css,$contenu);

	$contenu = urls_absolues_css($contenu, $css);

	// virer les fausses url absolues que l'on a mis dans les import
	if (count($src_faux_abs))
		$contenu = str_replace(array_keys($src_faux_abs),$src_faux_abs,$contenu);

	if (!ecrire_fichier($f, $contenu))
		return $css;

	return $f;
}

// recuperere le chemin d'une css existante et :
// cree (ou recree) dans _DIR_VAR/cache_css/ une css dont les url relatives sont passees en url absolues
// http://doc.spip.org/@url_absolue_css
function url_absolue_css ($css) {
	if (!preg_match(',\.css$,i', $css, $r)) return $css;

	$url_absolue_css = url_absolue($css);

	$f = basename($css,'.css');
	$f = sous_repertoire (_DIR_VAR, 'cache-css') 
		. preg_replace(",(.*?)(_rtl|_ltr)?$,","\\1-urlabs-" . substr(md5("$css-urlabs"), 0,4) . "\\2",$f) 
		. '.css';

	if ((@filemtime($f) > @filemtime($css))
	AND (_VAR_MODE != 'recalcul'))
		return $f;

	if ($url_absolue_css==$css){
		if (strncmp($GLOBALS['meta']['adresse_site'],$css,$l=strlen($GLOBALS['meta']['adresse_site']))!=0
		 OR !lire_fichier(_DIR_RACINE . substr($css,$l), $contenu)){
		 		include_spip('inc/distant');
		 		if (!$contenu = recuperer_page($css))
					return $css;
		}
	}
	elseif (!lire_fichier($css, $contenu))
		return $css;

	// passer les url relatives a la css d'origine en url absolues
	$contenu = urls_absolues_css($contenu, $css);

	// ecrire la css
	if (!ecrire_fichier($f, $contenu))
		return $css;

	return $f;
}



/**
 * Le filtre table_valeur
 * permet de recuperer la valeur d'une cle donnee
 * dans un tableau (ou un objet).
 * 
 * @param mixed $table
 * 		Tableau ou objet
 * 		(ou chaine serialisee de tableau, ce qui permet d'enchainer le filtre)
 * 		
 * @param string $cle
 * 		Cle du tableau (ou parametre public de l'objet)
 * 		Cette cle peut contenir des caracteres / pour selectionner
 * 		des sous elements dans le tableau, tel que "sous/element/ici"
 * 		pour obtenir la valeur de $tableau['sous']['element']['ici']
 *
 * @param mixed $defaut
 * 		Valeur par defaut retournee si la cle demandee n'existe pas
 * 
 * @return mixed Valeur trouvee ou valeur par defaut.
**/
function table_valeur($table, $cle, $defaut='') {
	foreach (explode('/', $cle) as $k) {

		$table = is_string($table) ? @unserialize($table) : $table;

		if (is_object($table)) {
			$table =  (($k !== "") and isset($table->$k)) ? $table->$k : $defaut;
		} elseif (is_array($table)) {
			$table = isset($table[$k]) ? $table[$k] : $defaut;
		} else {
			$table = $defaut;
		}
	}
	return $table;
}

// filtre match pour faire des tests avec expression reguliere
// [(#TEXTE|match{^ceci$,Uims})]
// retourne le fragment de chaine qui "matche"
// il est possible de passer en 3eme argument optionnel le numero de parenthese capturante
// accepte egalement la syntaxe #TRUC|match{truc(...)$,1} ou le modificateur n'est pas passe en second argument
// http://doc.spip.org/@match
function match($texte, $expression, $modif="UimsS",$capte=0) {
	if (intval($modif) AND $capte==0){
		$capte = $modif;
		$modif = "UimsS";
	}
	$expression=str_replace("\/","/",$expression);
	$expression=str_replace("/","\/",$expression);

	if (preg_match('/' . $expression . '/' . $modif,$texte, $r)) {
		if (isset($r[$capte]))
			return $r[$capte];
		else
			return true;
	}
	return false;
}

// filtre replace pour faire des operations avec expression reguliere
// [(#TEXTE|replace{^ceci$,cela,UimsS})]
// http://doc.spip.org/@replace
function replace($texte, $expression, $replace='', $modif="UimsS") {
	$expression=str_replace("\/","/", $expression);
	$expression=str_replace("/","\/",$expression);
	return preg_replace('/' . $expression . '/' . $modif, $replace, $texte);
}


// cherche les documents numerotes dans un texte traite par propre()
// et affecte les doublons['documents']
// http://doc.spip.org/@traiter_doublons_documents
// http://doc.spip.org/@traiter_doublons_documents
function traiter_doublons_documents(&$doublons, $letexte) {

	// Verifier dans le texte & les notes (pas beau, helas)
	$t = $letexte.$GLOBALS['les_notes'];

	if (strstr($t, 'spip_document_') // evite le preg_match_all si inutile
	AND preg_match_all(
	',<[^>]+\sclass=["\']spip_document_([0-9]+)[\s"\'],imsS',
	$t, $matches, PREG_PATTERN_ORDER))
		$doublons['documents'] .= "," . join(',', $matches[1]);

	return $letexte;
}

// filtre vide qui ne renvoie rien
// http://doc.spip.org/@vide
function vide($texte){
	return "";
}

//
// Filtres pour le modele/emb (embed document)
//

// A partir d'un #ENV, retourne des <param ...>
// http://doc.spip.org/@env_to_params
function env_to_params ($texte, $ignore_params=array()) {
	$ignore_params = array_merge (
		array('id', 'lang', 'id_document', 'date', 'date_redac', 'align', 'fond', '', 'recurs', 'emb', 'dir_racine'),
	$ignore_params);
	$tableau = unserialize($texte);
	$texte = "";
	foreach ($tableau as $i => $j)
		if (is_string($j) AND !in_array($i,$ignore_params))
			$texte .= "<param name='".$i."'\n\tvalue='".$j."' />";
	return $texte;
}
// A partir d'un #ENV, retourne des attributs
// http://doc.spip.org/@env_to_attributs
function env_to_attributs ($texte, $ignore_params=array()) {
	$ignore_params = array_merge (
		array('id', 'lang', 'id_document', 'date', 'date_redac', 'align', 'fond', '', 'recurs', 'emb', 'dir_racine'),
	$ignore_params);
	$tableau = unserialize($texte);
	$texte = "";
	foreach ($tableau as $i => $j)
		if (is_string($j) AND !in_array($i,$ignore_params))
			$texte .= $i."='".$j."' ";
	return $texte;
}

// Concatener des chaines
// #TEXTE|concat{texte1,texte2,...}
// http://doc.spip.org/@concat
function concat(){
	$args = func_get_args();
	return join('', $args);
}


// http://doc.spip.org/@charge_scripts
// http://doc.spip.org/@charge_scripts
function charge_scripts($files, $script = true) {
	$flux = "";
	foreach(is_array($files)?$files:explode("|",$files) as $file) {
		if (!is_string($file)) continue;
		if ($script)
			$file = preg_match(",^\w+$,",$file) ? "javascript/$file.js" : '';
		if ($file) $path = find_in_path($file);
		if ($path) $flux .= spip_file_get_contents($path);
	}
	return $flux;
}



/**
 * produit une balise img avec un champ alt d'office si vide
 * attention le htmlentities et la traduction doivent etre appliques avant.
 *
 * http://doc.spip.org/@http_img_pack
 *
 * @param $img
 * @param $alt
 * @param string $atts
 * @param string $title
 * @param array $options
 *   chemin_image : utiliser chemin_image sur $img fourni, ou non (oui par dafaut)
 *   utiliser_suffixe_size : utiliser ou non le suffixe de taille dans le nom de fichier de l'image
 *   sous forme -xx.png (pour les icones essentiellement) (oui par defaut)
 * @return string
 */
function http_img_pack($img, $alt, $atts='', $title='', $options = array()) {
	if (!isset($options['chemin_image']) OR $options['chemin_image']==true)
		$img = chemin_image($img);
	if (stripos($atts, 'width')===false){
		// utiliser directement l'info de taille presente dans le nom
		if ((!isset($options['utiliser_suffixe_size']) OR $options['utiliser_suffixe_size']==true)
		    AND preg_match(',-([0-9]+)[.](png|gif)$,',$img,$regs)){
			$largeur = $hauteur = intval($regs[1]);
		}
		else{
			$taille = taille_image($img);
			list($hauteur,$largeur) = $taille;
			if (!$hauteur OR !$largeur)
				return "";
		}
		$atts.=" width='".$largeur."' height='".$hauteur."'";
	}
	return  "<img src='$img' alt='" . attribut_html($alt ? $alt : $title) . "'"
	  . ($title ? ' title="'.attribut_html($title).'"' : '')
	  . " ".ltrim($atts)
	  . " />";
}

/**
 * generer une directive style='background:url()' a partir d'un fichier image
 * 
 * http://doc.spip.org/@http_style_background
 *
 * @param string $img
 * @param string $att
 * @return string
 */
function http_style_background($img, $att=''){
  return " style='background".($att?"":"-image").": url(\"".chemin_image($img)."\")" . ($att ? (' ' . $att) : '') . ";'";
}

/**
 * une fonction pour generer une balise img a partir d'un nom de fichier
 *
 * @param string $img
 * @param string $alt
 * @param string $class
 * @return string
 */
function filtre_balise_img_dist($img,$alt="",$class=""){
	return http_img_pack($img, $alt, $class?" class='".attribut_html($class)."'":'', '', array('chemin_image'=>false,'utiliser_suffixe_size'=>false));
}


//[(#ENV*|unserialize|foreach)]
// http://doc.spip.org/@filtre_foreach_dist
function filtre_foreach_dist($balise_deserializee, $modele = 'foreach') {
	$texte = '';
	if(is_array($balise_deserializee))
		foreach($balise_deserializee as $k => $v) {
			$res = recuperer_fond('modeles/'.$modele,
				array_merge(array('cle' => $k), (is_array($v) ? $v : array('valeur' => $v)))
			);
			$texte .= $res;
		}
	return $texte;
}

// renvoie la liste des plugins actifs du site
// si le premier parametre est un prefix de cette liste, renvoie vrai, faux sinon
// la valeur du second parametre si celui-ci renvoie a une information connue
// cf liste_plugin_actifs() pour connaitre les informations affichables
// appelee par la balise #PLUGIN
// http://doc.spip.org/@filtre_info_plugin_dist
function filtre_info_plugin_dist($plugin, $type_info) {
	include_spip('inc/plugin');
	$plugin = strtoupper($plugin);
	$plugins_actifs = liste_plugin_actifs();

	if (!$plugin)
		return serialize(array_keys($plugins_actifs));
	elseif (empty($plugins_actifs[$plugin]))
		return '';
	elseif ($type_info == 'est_actif')
		return $plugins_actifs[$plugin] ? 1 : 0;
	elseif (isset($plugins_actifs[$plugin][$type_info]))
		return $plugins_actifs[$plugin][$type_info];
	else {
		$get_infos = charger_fonction('get_infos','plugins');
		// On prend en compte les extensions
		if (!is_dir($plugins_actifs[$plugin]['dir_type']))
			$dir_plugins = constant($plugins_actifs[$plugin]['dir_type']);
		else
			$dir_plugins = $plugins_actifs[$plugin]['dir_type'];
		if (!$infos = $get_infos($plugins_actifs[$plugin]['dir'], false, $dir_plugins))
			return '';
		if ($type_info == 'tout')
			return $infos;
		else
			return strval($infos[$type_info]);
	}
}


// http://doc.spip.org/@puce_changement_statut
function puce_changement_statut($id_objet, $statut, $id_rubrique, $type, $ajax=false){
	$puce_statut = charger_fonction('puce_statut','inc');
	return $puce_statut($id_objet, $statut, $id_rubrique, $type, $ajax);
}

/**
 * [(#STATUT|puce_statut{article})] affiche une puce passive
 * [(#STATUT|puce_statut{article,#ID_ARTICLE,#ID_RUBRIQUE})] affiche une puce avec changement rapide
 *
 * utilisable sur tout objet qui a declare
 * @param string $statut
 * @param string $objet
 * @param int $id_objet
 * @param int $id_parent
 * @return string
 */
function filtre_puce_statut_dist($statut,$objet,$id_objet=0,$id_parent=0){
	static $puce_statut = null;
	if (!$puce_statut)
		$puce_statut = charger_fonction('puce_statut','inc');
	return $puce_statut($id_objet, $statut, $id_parent, $objet, false, objet_info($objet,'editable')?_ACTIVER_PUCE_RAPIDE:false);
}


/**
 * Encoder un contexte pour l'ajax, le signer avec une cle, le crypter
 * avec le secret du site, le gziper si possible...
 * l'entree peut etre serialisee (le #ENV** des fonds ajax et ajax_stat)
 *
 * http://doc.spip.org/@encoder_contexte_ajax
 *
 * @param string|array $c
 *   contexte, peut etre un tableau serialize
 * @param string $form
 *   nom du formulaire eventuel
 * @param string $emboite
 *   contenu a emboiter dans le conteneur ajax
 * @param string $ajaxid
 *   ajaxid pour cibler le bloc et forcer sa mise a jour
 * @return string
 */
function encoder_contexte_ajax($c,$form='', $emboite=NULL, $ajaxid='') {
	if (is_string($c)
	AND !is_null(@unserialize($c))) {
		$c = unserialize($c);
	}

	// supprimer les parametres debut_x
	// pour que la pagination ajax ne soit pas plantee
	// si on charge la page &debut_x=1 : car alors en cliquant sur l'item 0,
	// le debut_x=0 n'existe pas, et on resterait sur 1
	foreach ($c as $k => $v) {
		if (strpos($k,'debut_') === 0) {
			unset($c[$k]);
		}
	}
	
	if (!function_exists('calculer_cle_action'))
		include_spip("inc/securiser_action");

	$c = serialize($c);
	$cle = calculer_cle_action($form . $c);
	$c = "$cle:$c";

	// on ne stocke pas les contextes dans des fichiers caches
	// par defaut, sauf si cette configuration a ete forcee
	// OU que la longueur de l''argument generee est plus long
	// que ce que telere Suhosin.
	$cache_contextes_ajax = (defined('_CACHE_CONTEXTES_AJAX') AND _CACHE_CONTEXTES_AJAX);
	if (!$cache_contextes_ajax) {
		$env = $c;
		if (function_exists('gzdeflate') && function_exists('gzinflate')) {
			$env = gzdeflate($env);
			// http://core.spip.org/issues/2667 | https://bugs.php.net/bug.php?id=61287
			if (substr(phpversion(),0,5) == '5.4.0' AND !@gzinflate($env)) {
				$cache_contextes_ajax = true;
				spip_log("Contextes AJAX forces en fichiers ! Erreur PHP 5.4.0", _LOG_AVERTISSEMENT);
			}
		}
		$env = _xor($env);
		$env = base64_encode($env);
		// tester Suhosin et la valeur maximale des variables en GET...
		if ($max_len = @ini_get('suhosin.get.max_value_length')
		and $max_len < ($len = strlen($env))) {
			$cache_contextes_ajax = true;
			spip_log("Contextes AJAX forces en fichiers !"
				. " Cela arrive lorsque la valeur du contexte"
				. " depasse la longueur maximale autorisee par Suhosin"
				. " ($max_len) dans 'suhosin.get.max_value_length'. Ici : $len."
				. " Vous devriez modifier les parametres de Suhosin"
				. " pour accepter au moins 1024 caracteres.", _LOG_AVERTISSEMENT);
		}
	}
	
	if ($cache_contextes_ajax) {
		$dir = sous_repertoire(_DIR_CACHE, 'contextes');
		// stocker les contextes sur disque et ne passer qu'un hash dans l'url
		$md5 = md5($c);
		ecrire_fichier("$dir/c$md5",$c);
		$env = $md5;
	} 
	
	if ($emboite === NULL) return $env;
	if (!trim($emboite)) return "";
	// toujours encoder l'url source dans le bloc ajax
	$r = self();
	$r = ' data-origin="'.$r.'"';
	$class = 'ajaxbloc';
	if ($ajaxid and is_string($ajaxid)) {
		// ajaxid est normalement conforme a un nom de classe css
		// on ne verifie pas la conformite, mais on passe entites_html par dessus par precaution
		$class .= ' ajax-id-' . entites_html($ajaxid);
	}
	return "<div class='$class' "."data-ajax-env='$env'$r>\n$emboite</div><!--ajaxbloc-->\n";
}

// la procedure inverse de encoder_contexte_ajax()
// http://doc.spip.org/@decoder_contexte_ajax
function decoder_contexte_ajax($c,$form='') {
	if (!function_exists('calculer_cle_action'))
		include_spip("inc/securiser_action");
	if (( (defined('_CACHE_CONTEXTES_AJAX') AND _CACHE_CONTEXTES_AJAX) OR strlen($c)==32)
		AND $dir = sous_repertoire(_DIR_CACHE, 'contextes')
		AND lire_fichier("$dir/c$c",$contexte)) {
			$c = $contexte;
	} else {
		$c = @base64_decode($c);
		$c = _xor($c);
		if (function_exists('gzdeflate') && function_exists('gzinflate'))
			$c = @gzinflate($c);
	}

	// extraire la signature en debut de contexte
	// et la verifier avant de deserializer
	// format : signature:donneesserializees
	if ($p = strpos($c,":")){
		$cle = substr($c,0,$p);
		$c = substr($c,$p+1);

		if ($cle == calculer_cle_action($form . $c)) {
			$env = @unserialize($c);
			return $env;
		}
	}

	return false;
}

// encrypter/decrypter un message
// http://www.php.net/manual/fr/language.operators.bitwise.php#81358
// http://doc.spip.org/@_xor
function _xor($message, $key=null){
	if (is_null($key)) {
		if (!function_exists('calculer_cle_action'))
			include_spip("inc/securiser_action");
		$key = pack("H*", calculer_cle_action('_xor'));
	}

	$keylen = strlen($key);
	$messagelen = strlen($message);
	for($i=0; $i<$messagelen; $i++)
		$message[$i] = ~($message[$i]^$key[$i%$keylen]);

	return $message;
}

// Les vrai fonctions sont dans le plugin forum, mais on evite ici une erreur du compilateur
// en absence du plugin
function url_reponse_forum($texte){return $texte;}
function url_rss_forum($texte){return $texte;}


/**
 * une fonction pour generer des menus avec liens
 * ou un <strong class='on'> non clicable lorsque l'item est selectionne
 *
 * @param string $url
 * @param string $libelle
 *   le texte du lien
 * @param bool $on
 *   etat expose (genere un strong) ou non (genere un lien)
 * @param string $class
 * @param string $title
 * @param string $rel
 * @param string $evt
 *   complement a la balise a pour gerer un evenement javascript, de la forme " onclick='...'"
 * @return string
 */
function lien_ou_expose($url,$libelle=NULL,$on=false,$class="",$title="",$rel="", $evt=''){
	if ($on) {
		$bal = "strong";
		$att = "class='on'";
	} else {
		$bal = 'a';
		$att = "href='$url'"
	  	.($title?" title='".attribut_html($title)."'":'')
	  	.($class?" class='".attribut_html($class)."'":'')
	  	.($rel?" rel='".attribut_html($rel)."'":'')
		.$evt;
	}
	if ($libelle === NULL)
		$libelle = $url;
	return "<$bal $att>$libelle</$bal>";
}


/**
 * Afficher un message "un truc"/"N trucs"
 * Les items sont à indiquer comme pour la fonction _T() sous la forme :
 * "module:chaine"
 *
 * @param int $nb : le nombre
 * @param string $chaine_un : l'item de langue si $nb vaut un 
 * @param string $chaine_plusieurs : l'item de lanque si $nb > 1 
 * @param string $var : La variable à remplacer par $nb dans l'item de langue (facultatif, défaut "nb")
 * @param array $vars : Les autres variables nécessaires aux chaines de langues (facultatif)
 * @return string : la chaine de langue finale en utilisant la fonction _T()
 */
function singulier_ou_pluriel($nb,$chaine_un,$chaine_plusieurs,$var='nb',$vars=array()){
	if (!$nb=intval($nb)) return "";
	if (!is_array($vars)) return "";
	$vars[$var] = $nb;
	if ($nb>1) return _T($chaine_plusieurs, $vars);
	else return _T($chaine_un,$vars);
}


/**
 * Fonction de base pour une icone dans un squelette
 * structure html : <span><a><img><b>texte</b></span>
 *
 * @param string $type
 *  'lien' ou 'bouton'
 * @param string $lien
 *  url
 * @param string $texte
 *  texte du lien / alt de l'image
 * @param string $fond
 *  objet avec ou sans son extension et sa taille (article, article-24, article-24.png)
 * @param string $fonction
 *  new/del/edit
 * @param string $class
 *  classe supplementaire (horizontale, verticale, ajax ...)
 * @param string $javascript
 *  "onclick='...'" par exemple
 * @return string 
 */
function prepare_icone_base($type, $lien, $texte, $fond, $fonction="", $class="",$javascript=""){
	if (in_array($fonction,array("del","supprimer.gif")))
		$class .= ' danger';
	elseif ($fonction == "rien.gif")
		$fonction = "";
	elseif ($fonction == "delsafe")
		$fonction = "del";

	// remappage des icone : article-24.png+new => article-new-24.png
	if ($icone_renommer = charger_fonction('icone_renommer','inc',true))
		list($fond,$fonction) = $icone_renommer($fond,$fonction);

	// ajouter le type d'objet dans la class de l'icone
	$class .= " " . substr(basename($fond),0,-4);

	$alt = attribut_html($texte);
	$title = " title=\"$alt\""; // est-ce pertinent de doubler le alt par un title ?

	$ajax = "";
	if (strpos($class,"ajax")!==false) {
			$ajax="ajax";
		if (strpos($class,"preload")!==false)
			$ajax.=" preload";
		if (strpos($class,"nocache")!==false)
			$ajax.=" nocache";
		$ajax=" class='$ajax'";
	}

	$size = 24;
	if (preg_match("/-([0-9]{1,3})[.](gif|png)$/i",$fond,$match))
		$size = $match[1];

	if ($fonction){
		// 2 images pour composer l'icone : le fond (article) en background,
		// la fonction (new) en image
		$icone = http_img_pack($fonction, $alt, "width='$size' height='$size'\n" .
					http_style_background($fond));
	}
	else {
		$icone = http_img_pack($fond, $alt, "width='$size' height='$size'");
	}

	if ($type=='lien')
		return "<span class='icone s$size $class'>"
		. "<a href='$lien'$title$ajax$javascript>"
		. $icone
		. "<b>$texte</b>"
		. "</a></span>\n";

	else
		return bouton_action("$icone<b>$texte</b>",$lien,"icone s$size $class",$javascript,$alt);
}

function icone_base($lien, $texte, $fond, $fonction="", $class="",$javascript=""){
	return prepare_icone_base('lien', $lien, $texte, $fond, $fonction, $class, $javascript);
}
function filtre_icone_verticale_dist($lien, $texte, $fond, $fonction="", $class="",$javascript=""){
	return icone_base($lien,$texte,$fond,$fonction,"verticale $class",$javascript);
}
function filtre_icone_horizontale_dist($lien, $texte, $fond, $fonction="", $class="",$javascript=""){
	return icone_base($lien,$texte,$fond,$fonction,"horizontale $class",$javascript);
}

function filtre_bouton_action_horizontal_dist($lien, $texte, $fond, $fonction="", $class="",$confirm=""){
	return prepare_icone_base('bouton', $lien, $texte, $fond, $fonction, "horizontale $class", $confirm);
}
/*
 * Filtre icone pour compatibilite
 * mappe sur icone_base
 */
function filtre_icone_dist($lien, $texte, $fond, $align="", $fonction="", $class="",$javascript=""){
	return icone_base($lien,$texte,$fond,$fonction,"verticale $align $class",$javascript);
}


/**
 * filtre explode pour les squelettes permettant d'ecrire
 * #GET{truc}|explode{-}
 *
 * @param strong $a
 * @param string $b
 * @return array
 */
function filtre_explode_dist($a,$b){return explode($b,$a);}

/**
 * filtre implode pour les squelettes permettant d'ecrire
 * #GET{truc}|implode{-}
 *
 * @param array $a
 * @param string $b
 * @return string
 */
function filtre_implode_dist($a,$b){return is_array($a)?implode($b,$a):$a;}

/**
 * Produire les styles prives qui associent item de menu avec icone en background
 * @return string
 */
function bando_images_background(){
	include_spip('inc/bandeau');
	// recuperer tous les boutons et leurs images
	$boutons = definir_barre_boutons(definir_barre_contexte(),true,false);

	$res = "";
	foreach($boutons as $page => $detail){
		if ($detail->icone AND strlen(trim($detail->icone)))
			$res .="\n.navigation_avec_icones #bando1_$page {background-image:url(".$detail->icone.");}";
		$selecteur = (in_array($page,array('outils_rapides','outils_collaboratifs'))?"":".navigation_avec_icones ");
		if (is_array($detail->sousmenu))
			foreach($detail->sousmenu as $souspage=>$sousdetail)
				if ($sousdetail->icone AND strlen(trim($sousdetail->icone)))
					$res .="\n$selecteur.bando2_$souspage {background-image:url(".$sousdetail->icone.");}";
	}
	return $res;
}

/**
 * Generer un bouton_action
 * utilise par #BOUTON_ACTION
 *
 * @param string $libelle
 * @param string $url
 * @param string $class
 * @param string $confirm
 *   message de confirmation oui/non avant l'action
 * @param string $title
 * @param string $callback
 *   callback js a appeler lors de l'evenement action (apres confirmation eventuelle si $confirm est non vide)
 *   et avant execution de l'action. Si la callback renvoie false, elle annule le declenchement de l'action
 * @return string
 */
function bouton_action($libelle, $url, $class="", $confirm="", $title="", $callback=""){
	if ($confirm) {
		$confirm = "confirm(\"" . attribut_html($confirm) . "\")";
	  if ($callback)
		  $callback = "$confirm?($callback):false";
	  else
		  $callback = $confirm;
	}
	$onclick = $callback?" onclick='return ".addcslashes($callback,"'")."'":"";
	$title = $title ? " title='$title'" : "";
	return "<form class='bouton_action_post $class' method='post' action='$url'><div>".form_hidden($url)
		."<button type='submit' class='submit'$title$onclick>$libelle</button></div></form>";
}


/**
 * Proteger les champs passes dans l'url et utiliser dans {tri ...}
 * preserver l'espace pour interpreter ensuite num xxx et multi xxx
 * @param string $t
 * @return string
 */
function tri_protege_champ($t){
	return preg_replace(',[^\s\w.+],','',$t);
}

/**
 * Interpreter les multi xxx et num xxx utilise comme tri
 * pour la clause order
 * 'multi xxx' devient simplement 'multi' qui est calcule dans le select
 * @param string $t
 * @param array $from
 * @return string
 */
function tri_champ_order($t, $from=null){
	if(strncmp($t,'multi ',6)==0){
		return "multi";
	}

	$champ = $t;

	if (strncmp($t,'num ',4)==0)
		$champ = substr($t,4);
	// enlever les autres espaces non evacues par tri_protege_champ
	$champ = preg_replace(',\s,','',$champ);

	if (is_array($from)){
		$trouver_table = charger_fonction('trouver_table','base');
		foreach($from as $idt=>$table_sql){
			if ($desc = $trouver_table($table_sql)
				AND isset($desc['field'][$champ])){
				$champ = "$idt.$champ";
				break;
			}
		}
	}
	if (strncmp($t,'num ',4)==0)
		return "0+$champ";
	else
		return $champ;
}

/**
 * Interpreter les multi xxx et num xxx utilise comme tri
 * pour la clause select
 * 'multi xxx' devient select "...." as multi
 * les autres cas ne produisent qu'une chaine vide '' en select
 * 'hasard' devient 'rand() AS hasard' dans le select
 *
 * @param string $t
 * @return string
 */
function tri_champ_select($t){
	if(strncmp($t,'multi ',6)==0){
		$t = substr($t,6);
		$t = preg_replace(',\s,','',$t);
		$t = sql_multi($t,$GLOBALS['spip_lang']);
		return $t;
	}
	if(trim($t)=='hasard'){
		return 'rand() AS hasard';
	}
	return "''";
}


/**
 * Donner n'importe quelle information sur un objet de maniere generique.
 *
 * La fonction va gerer en interne deux cas particuliers les plus utilises :
 * l'URL et le titre (qui n'est pas forcemment le champ SQL "titre").
 *
 * On peut ensuite personnaliser les autres infos en creant une fonction
 * generer_<nom_info>_entite($id_objet, $type_objet, $ligne).
 * $ligne correspond a la ligne SQL de tous les champs de l'objet, les fonctions
 * de personnalisation n'ont donc pas a refaire de requete.
 *
 * @param int $id_objet
 * @param string $type_objet
 * @param string $info
 * @param string $etoile
 * @return string
 */
function generer_info_entite($id_objet, $type_objet, $info, $etoile=""){
	global $table_des_traitements;
	static $trouver_table=null;
	static $objets;

	// On verifie qu'on a tout ce qu'il faut
	$id_objet = intval($id_objet);
	if (!($id_objet and $type_objet and $info))
		return '';

	// si on a deja note que l'objet n'existe pas, ne pas aller plus loin
	if (isset($objets[$type_objet]) AND $objets[$type_objet]===false)
		return '';

	// Si on demande l'url, on retourne direct la fonction
	if ($info == 'url')
		return generer_url_entite($id_objet, $type_objet);

	// Sinon on va tout chercher dans la table et on garde en memoire
	$demande_titre = ($info == 'titre');

	// On ne fait la requete que si on a pas deja l'objet ou si on demande le titre mais qu'on ne l'a pas encore
	if (!isset($objets[$type_objet][$id_objet])
	  OR
	  ($demande_titre AND !isset($objets[$type_objet][$id_objet]['titre']))
	  ){
		if (!$trouver_table)
			$trouver_table = charger_fonction('trouver_table','base');
		$desc = $trouver_table(table_objet_sql($type_objet));
		if (!$desc)
			return $objets[$type_objet] = false;

		// Si on demande le titre, on le gere en interne
		$champ_titre = "";
		if ($demande_titre){
			// si pas de titre declare mais champ titre, il sera peuple par le select *
			$champ_titre = (!empty($desc['titre'])) ? ', ' . $desc['titre']:'';
		}
		include_spip('base/abstract_sql');
		include_spip('base/connect_sql');
		$objets[$type_objet][$id_objet] = sql_fetsel(
			'*'.$champ_titre,
			$desc['table_sql'],
			id_table_objet($type_objet).' = '.intval($id_objet)
		);
	}

	// Si la fonction generer_TRUC_TYPE existe, on l'utilise pour formater $info_generee
	if ($generer = charger_fonction("generer_${info}_${type_objet}", '', true))
		$info_generee = $generer($id_objet, $objets[$type_objet][$id_objet]);
	// Si la fonction generer_TRUC_entite existe, on l'utilise pour formater $info_generee
	else if ($generer = charger_fonction("generer_${info}_entite", '', true))
		$info_generee = $generer($id_objet, $type_objet, $objets[$type_objet][$id_objet]);
	// Sinon on prend directement le champ SQL tel quel
	else
		$info_generee = (isset($objets[$type_objet][$id_objet][$info])?$objets[$type_objet][$id_objet][$info]:'');

	// On va ensuite chercher les traitements automatiques a faire
	$champ = strtoupper($info);
	$traitement = isset($table_des_traitements[$champ]) ? $table_des_traitements[$champ] : false;
	$table_sql = table_objet_sql($type_objet);

	if (!$etoile
		AND is_array($traitement)
	  AND (isset($traitement[$table_sql]) OR isset($traitement[0]))){
	  	include_spip('inc/texte');
		$traitement = $traitement[isset($traitement[$table_sql]) ? $table_sql : 0];
		$traitement = str_replace('%s', "'".texte_script($info_generee)."'", $traitement);
		// FIXME: $connect et $Pile[0] font souvent partie des traitements.
		// on les definit pour eviter des notices, mais ce fonctionnement est a ameliorer !
		$connect = ""; $Pile = array(0 => array('id_objet'=>$id_objet,'objet'=>$type_objet));
		eval("\$info_generee = $traitement;");
	}

	return $info_generee;
}

/**
 * Wrap un texte avec des balises
 * wrap('mot','<b>') => '<b>mot</b>'
 * @param string $texte
 * @param string $wrap
 * @return string
 */
function wrap($texte,$wrap) {
	$balises = extraire_balises($wrap);
	if (preg_match_all(",<([a-z]\w*)\b[^>]*>,UimsS",$wrap, $regs, PREG_PATTERN_ORDER)) {
		$texte = $wrap . $texte;
		$regs = array_reverse($regs[1]);
		$wrap = "</".implode("></",$regs).">";
		$texte = $texte . $wrap;
	}
	return $texte;
}


/**
 * afficher proprement n'importe quoi
 * On affiche in fine un pseudo-yaml qui premet de lire humainement les tableaux et de s'y reperer
 *
 * Les textes sont retournes avec simplement mise en forme typo
 *
 * le $join sert a separer les items d'un tableau, c'est en general un \n ou <br /> selon si on fait du html ou du texte
 * les tableaux-listes (qui n'ont que des cles numeriques), sont affiches sous forme de liste separee par des virgules :
 * c'est VOULU !
 *
 * @param $u
 * @param string $join
 * @param int $indent
 * @return array|mixed|string
 */
function filtre_print_dist($u, $join="<br />", $indent=0) {
	if (is_string($u)){
		$u = typo($u);
		return $u;
	}

	// caster $u en array si besoin
	if (is_object($u))
		$u = (array) $u;

	if (is_array($u)){
		$out = "";
		// toutes les cles sont numeriques ?
		// et aucun enfant n'est un tableau
		// liste simple separee par des virgules
		$numeric_keys = array_map('is_numeric',array_keys($u));
		$array_values = array_map('is_array',$u);
		$object_values = array_map('is_object',$u);
		if (array_sum($numeric_keys)==count($numeric_keys)
		  AND !array_sum($array_values)
		  AND !array_sum($object_values)){
			return join(", ", array_map('filtre_print_dist', $u));
		}

		// sinon on passe a la ligne et on indente
		$i_str = str_pad("",$indent," ");
		foreach($u as $k => $v){
			$out .= $join . $i_str . "$k: " . filtre_print_dist($v,$join,$indent+2);
		}
		return $out;
	}

	// on sait pas quoi faire...
	return $u;
}


/**
 * Renvoyer l'info d'un objet
 * telles que definies dans declarer_tables_objets_sql
 *
 * @param string $objet
 * @param string $info
 * @return string
 */
function objet_info($objet,$info){
	$table = table_objet_sql($objet);
	$infos = lister_tables_objets_sql($table);
	return (isset($infos[$info])?$infos[$info]:'');
}

/**
 * Filtre pour afficher 'Aucun truc' ou '1 truc' ou 'N trucs'
 * avec la bonne chaine de langue en fonction de l'objet utilise
 * @param  $nb
 * @param  $objet
 * @return mixed|string
 */
function objet_afficher_nb($nb, $objet){
	if (!$nb)
		return _T(objet_info($objet,'info_aucun_objet'));
	else
		return _T(objet_info($objet,$nb==1?'info_1_objet':'info_nb_objets'),array('nb'=>$nb));
}

/**
 * Filtre pour afficher l'img icone d'un objet
 *
 * @param string $objet
 * @param int $taille
 * @return string
 */
function objet_icone($objet,$taille=24){
	$icone = objet_info($objet,'icone_objet')."-".$taille.".png";
	$icone = chemin_image($icone);
	$balise_img = charger_filtre('balise_img');
	return $icone?$balise_img($icone,_T(objet_info($objet,'texte_objet'))):'';
}

/**
 * Fonction de secours pour inserer le head_css de facon conditionnelle
 * 
 * Appelée en filtre sur le squelette qui contient #INSERT_HEAD,
 * elle vérifie l'absence éventuelle de #INSERT_HEAD_CSS et y suplée si besoin
 * pour assurer la compat avec les squelettes qui n'utilisent pas.
 * 
 * @param string $flux Code HTML
 * @return string      Code HTML
 */
function insert_head_css_conditionnel($flux){
	if (strpos($flux,'<!-- insert_head_css -->')===false
		AND $p=strpos($flux,'<!-- insert_head -->')){
		// plutot avant le premier js externe (jquery) pour etre non bloquant
		if ($p1 = stripos($flux,'<script src=') AND $p1<$p)
			$p = $p1;
		$flux = substr_replace($flux,pipeline('insert_head_css','<!-- insert_head_css -->'),$p,0);
	}
	return $flux;
}

/**
 * Produire un fichier statique a partir d'un squelette dynamique
 * Permet ensuite a apache de le servir en statique sans repasser
 * par spip.php a chaque hit sur le fichier
 * si le format (css ou js) est passe dans contexte['format'], on l'utilise
 * sinon on regarde si le fond finit par .css ou .js
 * sinon on utilie "html"
 *
 * @param string $fond
 * @param array $contexte
 * @param array $options
 * @param string $connect
 * @return string
 */
function produire_fond_statique($fond, $contexte=array(), $options = array(), $connect=''){
	if (isset($contexte['format'])){
		$extension = $contexte['format'];
		unset($contexte['format']);
	}
	else {
		$extension = "html";
		if (preg_match(',[.](css|js|json)$,',$fond,$m))
			$extension = $m[1];
	}
	// recuperer le contenu produit par le squelette
	$options['raw'] = true;
	$cache = recuperer_fond($fond,$contexte,$options,$connect);
	
	// calculer le nom de la css
	$dir_var = sous_repertoire (_DIR_VAR, 'cache-'.$extension);
	$nom_safe = preg_replace(",\W,",'_',str_replace('.','_',$fond));
	$filename = $dir_var . $extension."dyn-$nom_safe-".substr(md5($fond.serialize($contexte).$connect),0,8) .".$extension";

	// mettre a jour le fichier si il n'existe pas
	// ou trop ancien
	// le dernier fichier produit est toujours suffixe par .last
	// et recopie sur le fichier cible uniquement si il change
	if (!file_exists($filename)
		OR !file_exists($filename.".last")
		OR (isset($cache['lastmodified']) AND $cache['lastmodified'] AND filemtime($filename.".last")<$cache['lastmodified'])
		OR (defined('_VAR_MODE') AND _VAR_MODE=='recalcul')) {
		$contenu = $cache['texte'];
		// passer les urls en absolu si c'est une css
		if ($extension=="css")
			$contenu = urls_absolues_css($contenu, test_espace_prive()?generer_url_ecrire('accueil'):generer_url_public($fond));
		
		// ne pas insérer de commentaire si c'est du json
		if ($extension!="json") {
			$comment = "/* #PRODUIRE{fond=$fond";
			foreach($contexte as $k=>$v)
				$comment .= ",$k=$v";
			// pas de date dans le commentaire car sinon ca invalide le md5 et force la maj
			// mais on peut mettre un md5 du contenu, ce qui donne un aperu rapide si la feuille a change ou non
			$comment .="}\n   md5:".md5($contenu)." */\n";
		}
		// et ecrire le fichier
		ecrire_fichier($filename.".last",$comment.$contenu);
		// regarder si on recopie
		if (!file_exists($filename)
		  OR md5_file($filename)!==md5_file($filename.".last")){
			@copy($filename.".last",$filename);
			spip_clearstatcache(true,$filename); // eviter que PHP ne reserve le vieux timestamp
		}
	}
	
	return $filename;
}

/**
 * Ajouter un timestamp a une url de fichier
 * [(#CHEMIN{monfichier}|timestamp)]
 *
 * @param string $fichier
 * @return string
 */
function timestamp($fichier){
	if (!$fichier OR !file_exists($fichier)) return $fichier;
	$m = filemtime($fichier);
	return "$fichier?$m";
}

/**
 * Nettoyer le titre d'un email
 * eviter une erreur lorsqu'on utilise |nettoyer_titre_email dans un squelette de mail
 * @param  $titre
 * @return mixed
 */
function filtre_nettoyer_titre_email_dist($titre){
	include_spip('inc/envoyer_mail');
	return nettoyer_titre_email($titre);
}

/**
 * Afficher le sélecteur de rubrique
 *
 * Il permet de placer un objet dans la hiérarchie des rubriques de SPIP
 *
 * @param string $titre
 * @param int $id_objet
 * @param int $id_parent
 * @param string $objet
 * @param int $id_secteur
 * @param bool $restreint
 * @param bool $actionable
 *   true : fournit le selecteur dans un form directement postable
 * @param bool $retour_sans_cadre
 * @return string
 */
function filtre_chercher_rubrique_dist($titre,$id_objet, $id_parent, $objet, $id_secteur, $restreint,$actionable = false, $retour_sans_cadre=false){
	include_spip('inc/filtres_ecrire');
	return chercher_rubrique($titre,$id_objet, $id_parent, $objet, $id_secteur, $restreint,$actionable, $retour_sans_cadre);
}

/**
 * Rediriger une page suivant une autorisation,
 * et ce, n'importe où dans un squelette, même dans les inclusions.
 * Exemple :
 * [(#AUTORISER{non}|sinon_interdire_acces)]
 * [(#AUTORISER{non}|sinon_interdire_acces{#URL_PAGE{login}, 401})]
 *
 * @param bool $ok Indique si l'on doit rediriger ou pas
 * @param string $url Adresse vers laquelle rediriger
 * @param int $statut Statut HTML avec lequel on redirigera
 * @return string
 */
function sinon_interdire_acces($ok=false, $url='', $statut=0){
	if ($ok) return '';
	
	// Vider tous les tampons
	$level = @ob_get_level();
	while ($level--)
		@ob_end_clean();
	
	include_spip('inc/headers');
	$statut = intval($statut);
	
	// Si aucun argument on essaye de deviner quoi faire par défaut
	if (!$url and !$statut){
		// Si on est dans l'espace privé, on génère du 403 Forbidden
		if (test_espace_prive()){
			http_status(403);
			$echec = charger_fonction('403','exec');
			$echec();
		}
		// Sinon dans l'espace public on redirige vers une 404 par défaut, car elle toujours présente normalement
		else{
			$statut = 404;
		}
	}
	
	// On suit les directives indiquées dans les deux arguments
	
	// S'il y a un statut
	if ($statut){
		// Dans tous les cas on modifie l'entité avec ce qui est demandé
		http_status($statut);
		// Si le statut est une erreur et qu'il n'y a pas de redirection on va chercher le squelette du même nom
		if ($statut >= 400 and !$url)
			echo recuperer_fond("$statut");
	}
	
	// S'il y a une URL, on redirige (si pas de statut, la fonction mettra 302 par défaut)
	if ($url) redirige_par_entete($url, '', $statut);
	
	exit;
}

/**
 * Assurer le fonctionnement de |compacte meme sans l'extension compresseur
 * @param string $source
 * @param null|string $format
 * @return string
 */
function filtre_compacte_dist($source, $format = null){
	if (function_exists('compacte'))
		return compacte($source, $format);
	return $source;
}

?>
admin.php000066600000015535152525475610006376 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

// demande/verifie le droit de creation de repertoire par le demandeur;
// memorise dans les meta que ce script est en cours d'execution
// si elle y est deja c'est qu'il y a eu suspension du script, on reprend.

// http://doc.spip.org/@inc_admin_dist
function inc_admin_dist($script, $titre, $comment='', $anonymous=false)
{
	$reprise = true;
	if (!isset($GLOBALS['meta'][$script])
	OR  !isset($GLOBALS['meta']['admin'])) {
		$reprise = false;
		$res = debut_admin($script, $titre, $comment); 
		if ($res) return $res;
		spip_log("meta: $script " . join(',', $_POST));
		ecrire_meta($script, serialize($_POST));
	} 

	$res = admin_verifie_session($script,$anonymous);
	if ($res) return $res;
	$base = charger_fonction($script, 'base');
	$base($titre,$reprise);
	fin_admin($script);
	return '';
}

// Gestion dans la meta "admin" du script d'administation demande,
// pour eviter des executions en parallele, notamment apres Time-Out.
// Cette meta contient le nom du script et, a un hachage pres, du demandeur.
// Le code de ecrire/index.php devie toute demande d'execution d'un script
// vers le script d'administration indique par cette meta si elle est l�.
// Au niveau de la fonction inc_admin, on controle la meta 'admin'.
// Si la meta n'est pas la, 
//	c'est le debut on la cree.
// Sinon, si le hachage actuel est le meme que celui en base, 
//	c'est une reprise, on continue
// Sinon, si le hachage differe a cause du connecte,
// 	c'est une arrivee inoppotune, on refuse sa connexion.
// Enfin, si hachage differe pour une autre raison
// 	c'est que l'operation se passe mal, on la stoppe

// http://doc.spip.org/@admin_verifie_session
function admin_verifie_session($script, $anonymous=false) {

	include_spip('base/abstract_sql');
	$pref = sprintf("_%d_",$GLOBALS['visiteur_session']['id_auteur']);
	$signal = fichier_admin($script, "$script$pref");
	$valeur = sql_getfetsel('valeur', 'spip_meta', "nom='admin'");
	if ($valeur === NULL) {
		ecrire_meta('admin', $signal, 'non');
	} else {
		if (!$anonymous AND ($valeur != $signal)) {
			if (!preg_match('/^(.*)_(\d+)_/', $GLOBALS['meta']["admin"], $l)
				OR intval($l[2])!=$GLOBALS['visiteur_session']['id_auteur']) {
				include_spip('inc/minipres');
				spip_log("refus de lancer $script, priorite a $valeur");
				return minipres(_T('info_travaux_texte'));
			}
		}
	}
	$journal = "spip";
	if (autoriser('configurer')) // c'est une action webmestre, soit par ftp soit par statut webmestre
		$journal = 'webmestre';
	// on pourrait statuer automatiquement les webmestres a l'init d'une action auth par ftp ... ?

	spip_log("admin $pref" . ($valeur ? " (reprise)" : ' (init)'),$journal);
	return '';
}

// http://doc.spip.org/@dir_admin
function dir_admin()
{
	if (autoriser('configurer')) {
		return _DIR_TMP;
	} else {
		return  _DIR_TRANSFERT . $GLOBALS['visiteur_session']['login'] . '/';
	}
}

// http://doc.spip.org/@fichier_admin
function fichier_admin($action, $pref='admin_') {

	return $pref . 
	  substr(md5($action.(time() & ~2047).$GLOBALS['visiteur_session']['login']), 0, 10);
}

// demande la creation d'un repertoire et sort
// ou retourne sans rien faire si repertoire deja la.

// http://doc.spip.org/@debut_admin
function debut_admin($script, $action='', $corps='') {

	if ((!$action) || !(autoriser('webmestre') OR autoriser('chargerftp'))) {
		include_spip('inc/minipres');
		return minipres();
	} else {
		$dir = dir_admin();
		$signal = fichier_admin($script);
		if (@file_exists($dir . $signal)) {
			spip_log ("Action admin: $action");
			return '';
		}
		include_spip('inc/minipres');

	// Si on est un super-admin, un bouton de validation suffit
	// sauf dans les cas destroy
		if ((autoriser('webmestre') OR $script === 'repair')
		AND $script != 'delete_all') {
			if (_request('validation_admin') == $signal) {
				spip_log ("Action super-admin: $action");
				return '';
			}
			$corps .= '<input type="hidden" name="validation_admin" value="'.$signal.'" />';
			$suivant = _T('bouton_valider');
			$js = '';
		} else {
			// cet appel permet d'assurer un copier-coller du nom du repertoire a creer dans tmp (esj)
			// l'insertion du script a cet endroit n'est pas xhtml licite mais evite de l'embarquer dans toutes les pages minipres
			$corps .= http_script('',  "spip_barre.js");

			$corps .= "<fieldset><legend>"
			. _T('info_authentification_ftp')
			. aide("ftp_auth")
			. "</legend>\n<label for='fichier'>"
			. _T('info_creer_repertoire')
			. "</label>\n"
            . "<span id='signal' class='formo'>".$signal."</span>"
            . "<input type='hidden' id='fichier' name='fichier' value='" 
			. $signal
			. "' />"
			. _T('info_creer_repertoire_2', array('repertoire' => joli_repertoire($dir)))
			. "</fieldset>";

			$suivant = _T('bouton_recharger_page');

			// code volontairement tordu:
			// provoquer la copie dans le presse papier du nom du repertoire
			// en remettant a vide le champ pour que ca marche aussi en cas
			// de JavaScript inactif.
			$js = " onload='var range=document.createRange(); var signal = document.getElementById(\"signal\"); var userSelection = window.getSelection(); range.setStart(signal,0); range.setEnd(signal,1); userSelection.addRange(range);'";

		}

		// admin/xxx correspond
		// a exec/base_xxx de preference
		// et exec/xxx sinon (compat)
		if (tester_url_ecrire("base_$script"))
			$script = "base_$script";
		$form = copy_request($script, $corps, $suivant);
		$info_action = _T('info_action', array('action' => "$action"));
		return minipres($info_action, $form, $js);
	}
}

// http://doc.spip.org/@fin_admin
function fin_admin($action) {
	$signal = dir_admin() . fichier_admin($action);
	spip_unlink($signal);
	if ($action != 'delete_all') {
		effacer_meta($action);
		effacer_meta('admin');
		spip_log("efface les meta admin et $action ");
	}
}

// http://doc.spip.org/@copy_request
function copy_request($script, $suite, $submit='')
{
	include_spip('inc/filtres');
	foreach(array_merge($_POST,$_GET) as $n => $c) {
		if (!in_array($n,array('fichier','exec','validation_admin')) AND !is_array($c))
		$suite .= "\n<input type='hidden' name='".spip_htmlspecialchars($n)."' value='" .
			entites_html($c) .
			"'  />";
	}
	return  generer_form_ecrire($script, $suite, '', $submit);
}
?>
minipres.php000066600000010363152525475610007126 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

include_spip('inc/headers');
include_spip('inc/texte'); //inclue inc/lang et inc/filtres

//
// Presentation des pages d'installation et d'erreurs
//

/**
 * http://doc.spip.org/@install_debut_html
 *
 * @param string $titre
 * @param string $onLoad
 * @param bool $all_inline
 *   inliner les css et js dans la page (limiter le nombre de hits)
 * @return string
 */
function install_debut_html($titre = 'AUTO', $onLoad = '', $all_inline = false) {
	global $spip_lang_right,$spip_lang_left;
	
	utiliser_langue_visiteur();

	http_no_cache();

	if ($titre=='AUTO')
		$titre=_T('info_installation_systeme_publication');

	# le charset est en utf-8, pour recuperer le nom comme il faut
	# lors de l'installation
	if (!headers_sent())
		header('Content-Type: text/html; charset=utf-8');

	$css = "";
	$files = array('reset.css','clear.css','minipres.css');
	if ($all_inline){
		// inliner les CSS (optimisation de la page minipres qui passe en un seul hit a la demande)
		foreach ($files as $name){
			$file = direction_css(find_in_theme($name));
			if (function_exists("compacte"))
				$file = compacte($file);
			else
				$file = url_absolue_css($file); // precaution
			lire_fichier($file,$c);
			$css .= $c;
		}
		$css = "<style type='text/css'>".$css."</style>";
	}
	else{
		foreach ($files as $name){
			$file = direction_css(find_in_theme($name));
			$css .= "<link rel='stylesheet' href='$file' type='text/css' />\n";
		}
	}

	// au cas ou minipres() est appele avant spip_initialisation_suite()
	if (!defined('_DOCTYPE_ECRIRE')) define('_DOCTYPE_ECRIRE', '');
	return  _DOCTYPE_ECRIRE.
		html_lang_attributes().
		"<head>\n".
		"<title>".
		textebrut($titre).
		"</title>\n".
		"<meta name='viewport' content='width=device-width' />\n".
		$css .
"</head>
<body".$onLoad." class='minipres'>
	<div id='minipres'>
	<h1>".
	  $titre .
	  "</h1>
	<div>\n";
}

// http://doc.spip.org/@install_fin_html
function install_fin_html() {
	return "\n\t</div>\n\t</div>\n</body>\n</html>";
}


/**
 * http://doc.spip.org/@minipres
 *
 * @param string $titre
 *   titre de la page
 * @param string $corps
 *   corps de la page
 * @param string $onload
 *   attribut onload de <body>
 * @param bool $all_inline
 *   inliner les css et js dans la page (limiter le nombre de hits)
 * @return string
 */
function minipres($titre='', $corps="", $onload='', $all_inline = false)
{
	if (!defined('_AJAX')) define('_AJAX', false); // par securite
	if (!$titre) {
		if (!_AJAX)
			http_status(403);
		if (!$titre = _request('action')
		AND !$titre = _request('exec')
		AND !$titre = _request('page'))
			$titre = '?';

		$titre = spip_htmlspecialchars($titre);

		$titre = ($titre == 'install')
		  ?  _T('avis_espace_interdit')
		  : $titre . '&nbsp;: '. _T('info_acces_interdit');

		$statut = isset($GLOBALS['visiteur_session']['statut']) ? $GLOBALS['visiteur_session']['statut'] : '';
		$nom    = isset($GLOBALS['visiteur_session']['nom']) ? $GLOBALS['visiteur_session']['nom'] : '';

		if ($statut != '0minirezo') {
			$titre = _T('info_acces_interdit');
		}

		$corps = generer_form_ecrire('accueil', '','',
						$statut ? _T('public:accueil_site') : _T('public:lien_connecter')
		);
		spip_log($nom . " $titre " . $_SERVER['REQUEST_URI']);
	}

	if (!_AJAX)
		return install_debut_html($titre, $onload, $all_inline)
		. $corps
		. install_fin_html();
	else {
		include_spip('inc/headers');
		include_spip('inc/actions');
		$url = self('&',true);
		foreach ($_POST as $v => $c)
			$url = parametre_url($url, $v, $c, '&');
		ajax_retour("<div>".$titre . redirige_formulaire($url)."</div>",false);
	}
}
?>
chercher_rubrique.php000066600000020267152525475610011005 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

define('_SPIP_SELECT_RUBRIQUES', 20); /* mettre 100000 pour desactiver ajax */

//
// Selecteur de rubriques pour l'espace prive
// En entree :
// - l'id_rubrique courante (0 si NEW)
// - le type d'objet a placer (une rubrique peut aller a la racine
//    mais pas dans elle-meme, les articles et sites peuvent aller
//    n'importe ou (defaut), et les breves dans les secteurs.
// $idem : en mode rubrique = la rubrique soi-meme
// http://doc.spip.org/@inc_chercher_rubrique_dist
function inc_chercher_rubrique_dist ($id_rubrique, $type, $restreint, $idem=0, $do='aff') {
	if (sql_countsel('spip_rubriques')<1)
		return '';

	// Mode sans Ajax :
	// - soit parce que le cookie ajax n'est pas la
	// - soit parce qu'il y a peu de rubriques
	if (_SPIP_AJAX < 1
	OR $type == 'breve'
	OR sql_countsel('spip_rubriques') < _SPIP_SELECT_RUBRIQUES)
		return selecteur_rubrique_html($id_rubrique, $type, $restreint, $idem);

	else return selecteur_rubrique_ajax($id_rubrique, $type, $restreint, $idem, $do);

}

// compatibilite pour extensions qui utilisaient l'ancien nom
$GLOBALS['selecteur_rubrique'] = 'inc_chercher_rubrique_dist';

// http://doc.spip.org/@style_menu_rubriques
function style_menu_rubriques($i) {
	global $browser_name, $spip_lang_left;

	$espace = '';
	if (preg_match(",mozilla,i", $browser_name)) {
		$style = "padding-$spip_lang_left: 16px; "
		. "margin-$spip_lang_left: ".(($i-1)*16)."px;";
	} else {
		$style = '';
		for ($count = 0; $count <= $i; $count ++)
			$espace .= "&nbsp;&nbsp;&nbsp;&nbsp;";
	}
	if ($i ==1)
		$espace= "";
	$class = "niveau_$i";
	return array($class,$style,$espace);
}

// http://doc.spip.org/@sous_menu_rubriques
function sous_menu_rubriques($id_rubrique, $root, $niv, &$data, &$enfants, $exclus, $restreint, $type) {
	static $decalage_secteur;

	// Si on a demande l'exclusion ne pas descendre dans la rubrique courante
	if ($exclus > 0
	AND $root == $exclus) return '';

	// en fonction du niveau faire un affichage plus ou moins kikoo

	// selected ?
	$selected = ($root == $id_rubrique) ? ' selected="selected"' : '';

	// le style en fonction de la profondeur
	list($class, $style, $espace) = style_menu_rubriques($niv);

	$class .= " selec_rub";

	// creer l'<option> pour la rubrique $root

	if (isset($data[$root])) # pas de racine sauf pour les rubriques
	{
		$r = "<option$selected value='$root' class='$class' style='$style'>$espace"
		.$data[$root]
		.'</option>'."\n";
	} else 	$r = '';
	
	// et le sous-menu pour ses enfants
	$sous = '';
	if (isset($enfants[$root]))
		foreach ($enfants[$root] as $sousrub)
			$sous .= sous_menu_rubriques($id_rubrique, $sousrub,
				$niv+1, $data, $enfants, $exclus, $restreint, $type);

	// si l'objet a deplacer est publie, verifier qu'on a acces aux rubriques
	if ($restreint AND $root!=$id_rubrique AND !autoriser('publierdans','rubrique',$root))
		return $sous;

	// et voila le travail
	return $r.$sous;
}

// Le selecteur de rubriques en mode classique (menu)
// http://doc.spip.org/@selecteur_rubrique_html
function selecteur_rubrique_html($id_rubrique, $type, $restreint, $idem=0) {
	$data = array();
	if ($type == 'rubrique' AND autoriser('publierdans','rubrique',0))
		$data[0] = _T('info_racine_site');
	# premier choix = neant
	# si auteur (rubriques restreintes)
	# ou si creation avec id_rubrique=0
	elseif ($type == 'auteur' OR !$id_rubrique)
		$data[0] = '&nbsp;';

	//
	// creer une structure contenant toute l'arborescence
	//

	include_spip('base/abstract_sql');
	$q = sql_select("id_rubrique, id_parent, titre, statut, lang, langue_choisie", "spip_rubriques", ($type == 'breve' ?  ' id_parent=0 ' : ''), '', "0+titre,titre");
	while ($r = sql_fetch($q)) {
		if (autoriser('voir','rubrique',$r['id_rubrique'])){
			// titre largeur maxi a 50
			$titre = couper(supprimer_tags(typo($r['titre']))." ", 50);
			if ($GLOBALS['meta']['multi_rubriques'] == 'oui'
			AND ($r['langue_choisie'] == "oui" OR $r['id_parent'] == 0))
				$titre .= ' ['.traduire_nom_langue($r['lang']).']';
			$data[$r['id_rubrique']] = $titre;
			$enfants[$r['id_parent']][] = $r['id_rubrique'];
			if ($id_rubrique == $r['id_rubrique']) $id_parent = $r['id_parent'];
		}
	}

	// si une seule rubrique comme choix possible,
	// inutile de mettre le selecteur sur un choix vide par defaut
	// sauf si le selecteur s'adresse a une rubrique puisque on peut la mettre a la racine dans ce cas
	if (count($data)==2
	  AND isset($data[0])
	  AND !in_array($type,array('auteur','rubrique'))
	  AND !$id_rubrique)
		unset($data[0]);


	$opt = sous_menu_rubriques($id_rubrique,0, 0,$data,$enfants,$idem, $restreint, $type);
	$att = " id='id_parent' name='id_parent'\nclass='selecteur_parent verdana1'";

	if (preg_match(',^<option[^<>]*value=.(\d*).[^<>]*>([^<]*)</option>$,',$opt,$r))
	  $r = "<input$att type='hidden' value='" . $r[1] . "' />" . $r[2] ;
	else 
	  $r = "<select".$att." size='1'>\n$opt</select>\n";

	# message pour neuneus (a supprimer ?)
#	if ($type != 'auteur' AND $type != 'breve')
#		$r .= "\n<br />"._T('texte_rappel_selection_champs');

	return $r;
}

/**
 * http://doc.spip.org/@selecteur_rubrique_ajax
 *
 * $restreint indique qu'il faut limiter les rubriques affichees
 * aux rubriques editables par l'admin restreint... or, ca ne marche pas.
 * Pour la version HTML c'est bon (cf. ci-dessus), mais pour l'ajax...
 * je laisse ca aux specialistes de l'ajax & des admins restreints
 * note : toutefois c'est juste un pb d'interface, car question securite
 * la verification est faite a l'arrivee des donnees (Fil)
 *
 *
 * @param int $id_rubrique
 * @param string $type
 * @param bool $restreint
 * @param int $idem
 * @param string $do
 * @return string
 */
function selecteur_rubrique_ajax($id_rubrique, $type, $restreint, $idem=0, $do) {

	if ($id_rubrique) {
		$titre = sql_fetsel("titre", "spip_rubriques", "id_rubrique=$id_rubrique");
		$titre = $titre['titre'];
	} else if ($type == 'auteur')
		$titre = '&nbsp;';
	else
		$titre = _T('info_racine_site');

	$titre = str_replace('&amp;', '&', entites_html(textebrut(typo($titre))));
	$init = " disabled='disabled' type='text' value=\"" . $titre . "\"\nstyle='width:300px;'";

	$url = generer_url_ecrire('selectionner',"id=$id_rubrique&type=$type&do=$do"
	. (!$idem ? '' : "&exclus=$idem")
	. ($restreint ? "" : "&racine=oui")
	. (isset($GLOBALS['var_profile']) ? '&var_profile=1' : ''));


	return construire_selecteur($url, '', 'selection_rubrique', 'id_parent', $init, $id_rubrique);
}

// construit un bloc comportant une icone clicable avec image animee a cote
// pour charger en Ajax du code a mettre sous cette icone.
// Attention: changer le onclick si on change le code Html.
// (la fonction JS charger_node ignore l'attribut id qui ne sert en fait pas;
// getElement en mode Ajax est trop couteux).

// http://doc.spip.org/@construire_selecteur
function construire_selecteur($url, $js, $idom, $name, $init='', $id=0)
{
	$icone = (strpos($idom, 'auteur')!==false) ? 'auteur-24.png' : 'rechercher-20.png';
	return
 	"<div class='rubrique_actuelle'><a onclick=\""
	.  $js
	. "return charger_node_url_si_vide('"
	. $url
	. "', this.parentNode.nextSibling, this.nextSibling,'',event)\"><img src='"
	. chemin_image($icone)
	. "'\nstyle='vertical-align: middle;' alt='"._T('titre_image_selecteur')."' /></a><img src='"
	. chemin_image('searching.gif') 
	. "' id='img_"
	.  $idom
	. "'\nstyle='visibility: hidden;' alt='*' />"
	. "<input id='titreparent' name='titreparent'"
	. $init
	. " />" 
	. "<input type='hidden' id='$name' name='$name' value='"
	. $id
	. "' /><div class='nettoyeur'></div></div><div id='"
	. $idom
	. "'\nstyle='display: none;'></div>";
}
?>
precharger_objet.php000066600000014572152525475610010613 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

include_spip('inc/autoriser'); // necessaire si appel de l'espace public



/**
 * Retourne les valeurs a charger pour un formulaire d'edition d'un objet
 * Lors d'une creation, certains champs peuvent être preremplis (c'est le cas des traductions) 
 *
 * @param string $type Type d'objet (article,breve...)
 * @param string|int $id_objet, Identifiant de l'objet, ou "new" pour une creation
 * @param int $id_rubrique, identifiant eventuel de la rubrique parente
 * @param int $lier_trad, identifiant eventuel de la traduction de reference
 * @param string $champ_titre, nom de la colonne SQL de l'objet donnant le titre :
 *                             pas ideal ? on devrait pouvoir le savoir
 *                             dans la declaration de l'objet
 * @return array couples cles / valeurs des champs du formulaire à charger.
**/
function precharger_objet($type, $id_objet, $id_rubrique=0, $lier_trad=0, $champ_titre = 'titre') {
	global $connect_id_rubrique, $spip_lang;
	
	$table = table_objet_sql($type);
	$_id_objet = id_table_objet($table);

	// si l'objet existe deja, on retourne simplement ses valeurs
	if (is_numeric($id_objet))
		return sql_fetsel("*", $table, "$_id_objet=$id_objet");

	// ici, on demande une creation.
	// on prerempli certains elements : les champs si traduction,
	// les id_rubrique et id_secteur si l'objet a ces champs
	$desc = lister_tables_objets_sql($table);
	# il faudrait calculer $champ_titre ici
	$is_rubrique = isset($desc['field']['id_rubrique']);
	$is_secteur  = isset($desc['field']['id_secteur']);

	// si demande de traduction
	// on recupere les valeurs de la traduction
	if ($lier_trad){
		if ($select = charger_fonction("precharger_traduction_" . $type,'inc',true))
			$row = $select($id_objet, $id_rubrique, $lier_trad);
		else
			$row = precharger_traduction_objet($type, $id_objet, $id_rubrique, $lier_trad, $champ_titre);
	} else {
		$row[$champ_titre] = '';
		if ($is_rubrique) {
			$row['id_rubrique'] = $id_rubrique;
		}
	}

	// calcul de la rubrique
	# note : comment faire pour des traductions sur l'objet rubriques ?
	if ($is_rubrique) {
		// appel du script a la racine, faut choisir 
		// admin restreint ==> sa premiere rubrique
		// autre ==> la derniere rubrique cree
		if (!$row['id_rubrique']) {
			if ($connect_id_rubrique)
				$row['id_rubrique'] = $id_rubrique = current($connect_id_rubrique); 
			else {
				$row_rub = sql_fetsel("id_rubrique", "spip_rubriques", "", "", "id_rubrique DESC", 1);
				$row['id_rubrique'] = $id_rubrique = $row_rub['id_rubrique'];
			}
			if (!autoriser('creerarticledans','rubrique',$row['id_rubrique'] )){
				// manque de chance, la rubrique n'est pas autorisee, on cherche un des secteurs autorises
				$res = sql_select("id_rubrique", "spip_rubriques", "id_parent=0");
				while (!autoriser('creerarticledans','rubrique',$row['id_rubrique'] ) && $row_rub = sql_fetch($res)){
					$row['id_rubrique'] = $row_rub['id_rubrique'];
				}
			}
		}
	}
	
	// recuperer le secteur, pour affecter les bons champs extras
	if ($id_rubrique and $is_secteur) {
		if (!$row['id_secteur']) {
			$row_rub = sql_getfetsel("id_secteur", "spip_rubriques", "id_rubrique=" . sql_quote($id_rubrique));
			$row['id_secteur'] = $row_rub;
		}
	}

	return $row;
}


/**
 * Recupere les valeurs d'une traduction de reference pour la creation
 * d'un objet (preremplissage du formulaire). 
 *
 * @param string $type Type d'objet (article,breve...)
 * @param string|int $id_objet, Identifiant de l'objet, ou "new" pour une creation
 * @param int $id_rubrique, identifiant eventuel de la rubrique parente
 * @param int $lier_trad, identifiant eventuel de la traduction de reference
 * @param string $champ_titre, nom de la colonne SQL de l'objet donnant le titre
 * 
 * @return array couples cles / valeurs des champs du formulaire à charger
**/
function precharger_traduction_objet($type, $id_objet, $id_rubrique=0, $lier_trad=0, $champ_titre = 'titre') {
	$table = table_objet_sql($type);
	$_id_objet = id_table_objet($table);

	// Recuperer les donnees de l'objet original
	$row = sql_fetsel("*", $table, "$_id_objet=$lier_trad");
	if ($row) {
		$row[$champ_titre] = filtrer_entites(_T('info_nouvelle_traduction')).' '.$row[$champ_titre];
	} else {
		$row = array();
	}

	// on met l'objet dans une rubrique si l'objet le peut
	$desc = lister_tables_objets_sql($table);
	$is_rubrique = isset($desc['field']['id_rubrique']);
	
	if ($is_rubrique) {
		if ($id_rubrique) {
			$row['id_rubrique'] = $id_rubrique;
			return $row;
		}
		$id_rubrique = $row['id_rubrique'];
	

		// Regler la langue, si possible, sur celle du redacteur
		// Cela implique souvent de choisir une rubrique ou un secteur
		if (in_array($GLOBALS['spip_lang'],
		explode(',', $GLOBALS['meta']['langues_multilingue']))) {

			// Si le menu de langues est autorise sur l'objet,
			// on peut changer la langue quelle que soit la rubrique
			// donc on reste dans la meme rubrique
			if (in_array($table, explode(',',$GLOBALS['meta']['multi_objets']))) {
				$row['id_rubrique'] = $row['id_rubrique']; # explicite :-)

			// Sinon, chercher la rubrique la plus adaptee pour
			// accueillir l'objet dans la langue du traducteur
			} elseif ($is_rubrique and $GLOBALS['meta']['multi_rubriques'] == 'oui') {
				if ($GLOBALS['meta']['multi_secteurs'] == 'oui') {
					$id_parent = 0;
				} else {
					// on cherche une rubrique soeur dans la bonne langue
					$row_rub = sql_fetsel("id_parent", "spip_rubriques", "id_rubrique=$id_rubrique");
					$id_parent = $row_rub['id_parent'];
				}
				
				$row_rub = sql_fetsel("id_rubrique", "spip_rubriques", "lang='".$GLOBALS['spip_lang']."' AND id_parent=$id_parent");
				if ($row_rub)
					$row['id_rubrique'] = $row_rub['id_rubrique'];	
			}
		}
	}
	return $row;
}



?>
utils.php000066600000234114152525475610006442 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

//
// Utilitaires indispensables autour du serveur Http.
//

/**
 * charge un fichier perso ou, a defaut, standard
 * et retourne si elle existe le nom de la fonction homonyme (exec_$nom),
 * ou de suffixe _dist
 * Peut etre appelee plusieurs fois, donc optimiser
 *
 * http://doc.spip.org/@charger_fonction
 *
 * @param string $nom
 * @param string $dossier
 * @param bool $continue
 * @return string
 */
function charger_fonction($nom, $dossier='exec', $continue=false) {
	static $echecs = array();

	if (strlen($dossier) AND substr($dossier,-1) != '/') $dossier .= '/';
	$f = str_replace('/','_',$dossier) . $nom;

	if (function_exists($f))
		return $f;
	if (function_exists($g = $f . '_dist'))
		return $g;

	if (isset($echecs[$f])) return $echecs[$f];
	// Sinon charger le fichier de declaration si plausible

	if (!preg_match(',^\w+$,', $f)){
		if ($continue) return false; //appel interne, on passe
		include_spip('inc/minipres');
		echo minipres();
		exit;
	}

	// passer en minuscules (cf les balises de formulaires)
	// et inclure le fichier
	if (!$inc = include_spip($dossier.($d = strtolower($nom)))
		// si le fichier truc/machin/nom.php n'existe pas,
		// la fonction peut etre definie dans truc/machin.php qui regroupe plusieurs petites fonctions
		AND strlen(dirname($dossier)) AND dirname($dossier)!='.')
		include_spip(substr($dossier,0,-1));
	if (function_exists($f)) return $f;
	if (function_exists($g)) return $g;

	if ($continue) return $echecs[$f] = false;

	// Echec : message d'erreur
	spip_log("fonction $nom ($f ou $g) indisponible" .
		($inc ? "" : " (fichier $d absent de $dossier)"));

	include_spip('inc/minipres');
	echo minipres(_T('forum_titre_erreur'),
		 _T('fichier_introuvable', array('fichier'=> '<b>'.spip_htmlentities($d).'</b>')),
		array('all_inline'=>true,'status'=>404));
	exit;
}

/**
 * Inclusion unique avec verification d'existence du fichier + log en crash sinon
 * @param string $file
 * @return bool
 */
function include_once_check($file){
	if (file_exists($file)) {include_once $file;return true;}
	$crash = (isset($GLOBALS['message_crash_plugins'])?unserialize($GLOBALS['message_crash_plugins']):'');
	$crash = ($crash?$crash:array());
	$crash[$file] = true;
	ecrire_meta('message_crash_plugins',serialize($crash));
	return false;
}

//
// la fonction cherchant un fichier PHP dans le SPIP_PATH
//
// http://doc.spip.org/@include_spip
function include_spip($f, $include = true) {
	return find_in_path($f . '.php', '', $include);
}


function require_spip($f) {
	return find_in_path($f . '.php', '', 'required');
}

// un pipeline est lie a une action et une valeur
// chaque element du pipeline est autorise a modifier la valeur
//
// le pipeline execute les elements disponibles pour cette action,
// les uns apres les autres, et retourne la valeur finale
//
// Cf. compose_filtres dans references.php, qui est la
// version compilee de cette fonctionnalite

// appel unitaire d'une fonction du pipeline
// utilisee dans le script pipeline precompile
// on passe $val par reference pour limiter les allocations memoire
// http://doc.spip.org/@minipipe
function minipipe($fonc,&$val){
	// fonction
	if (function_exists($fonc))
		$val = call_user_func($fonc, $val);
	// Class::Methode
	else if (preg_match("/^(\w*)::(\w*)$/S", $fonc, $regs)
	AND $methode = array($regs[1], $regs[2])
	AND is_callable($methode))
		$val = call_user_func($methode, $val);
	else {
		spip_log("Erreur - '$fonc' non definie !");
	}
	return $val;
}

// chargement du pipeline sous la forme d'un fichier php prepare
// http://doc.spip.org/@pipeline
function pipeline($action, $val=null) {
	static $charger;

	// chargement initial des fonctions mises en cache, ou generation du cache
	if (!$charger) {
		if (!($ok = @is_readable($charger = _CACHE_PIPELINES))) {
			include_spip('inc/plugin');
			// generer les fichiers php precompiles
			// de chargement des plugins et des pipelines
			actualise_plugins_actifs();
			if (!($ok = @is_readable($charger)))
				spip_log("fichier $charger pas cree");
		}

		if ($ok) {
			include_once $charger;
		}
	}

	// appliquer notre fonction si elle existe
	$fonc = 'execute_pipeline_'.strtolower($action);
	if (function_exists($fonc)) {
		$val = $fonc($val);
	}
	// plantage ?
	else {
		spip_log("fonction $fonc absente : pipeline desactive",_LOG_ERREUR);
	}

	// si le flux est une table avec 2 cle args&data
	// on ne ressort du pipe que les donnees dans 'data'
	// array_key_exists pour php 4.1.0
	if (is_array($val)
	  AND count($val)==2
	  AND (array_key_exists('data',$val)))
		$val = $val['data'];
	return $val;
}

/**
 * Enregistrement des evenements
 * spip_log($message)
 * spip_log($message,'recherche')
 * spip_log($message,_LOG_DEBUG)
 * spip_log($message,'recherche.'._LOG_DEBUG)
 *  cette derniere notation est controversee mais le 3eme
 *  parametre est plante pour cause de compat ascendante.
 * le niveau par defaut est _LOG_INFO
 * 
 * http://doc.spip.org/@spip_log
 *
 * @param string $message
 * @param string|int $name
 * @param string $logdir  ## inutile !! a supprimer ?
 * @param string $logsuf  ## inutile !! a supprimer ?
 */
function spip_log($message=NULL, $name=NULL) {
	static $pre = array();
	static $log;
	preg_match('/^([a-z_]*)\.?(\d)?$/iS', (string) $name, $regs);
	if (!isset($regs[1]) OR !$logname = $regs[1])
		$logname = null;
	if (!isset($regs[2]) OR !$niveau = $regs[2])
		$niveau = _LOG_INFO;

	if ($niveau <= (defined('_LOG_FILTRE_GRAVITE') ? _LOG_FILTRE_GRAVITE : _LOG_INFO_IMPORTANTE)) {
		if (!$pre){
			$pre = array(
				_LOG_HS=>'HS:',
				_LOG_ALERTE_ROUGE=>'ALERTE:',
				_LOG_CRITIQUE=>'CRITIQUE:',
				_LOG_ERREUR=>'ERREUR:',
				_LOG_AVERTISSEMENT=>'WARNING:',
				_LOG_INFO_IMPORTANTE=>'!INFO:',
				_LOG_INFO=>'info:',
				_LOG_DEBUG=>'debug:');
			$log = charger_fonction('log', 'inc');
		}
		if (!is_string($message)) $message = var_export($message, true);
		$log($pre[$niveau].' '.$message, $logname);
	}
}

//
// Enregistrement des journaux
//
function journal($phrase, $opt = array()) {
	$journal = charger_fonction('journal', 'inc');
	$journal($phrase, $opt);
}

// Renvoie le _GET ou le _POST emis par l'utilisateur
// ou pioche dans $c si c'est un array()
// http://doc.spip.org/@_request
function _request($var, $c=false) {

	if (is_array($c))
		return isset($c[$var]) ? $c[$var] : NULL;

	if (isset($_GET[$var])) $a = $_GET[$var];
	elseif (isset($_POST[$var])) $a = $_POST[$var];
	else return NULL;

	// Si on est en ajax et en POST tout a ete encode
	// via encodeURIComponent, il faut donc repasser
	// dans le charset local...
	if (defined('_AJAX')
	AND _AJAX
	AND isset($GLOBALS['meta']['charset'])
	AND $GLOBALS['meta']['charset'] != 'utf-8'
	AND is_string($a)
	// check rapide mais pas fiable
	AND preg_match(',[\x80-\xFF],', $a)
	// check fiable
	AND include_spip('inc/charsets')
	AND is_utf8($a)
	) {
		return importer_charset($a, 'utf-8');
	}

	return $a;
}

// Methode set de la fonction _request()
// Attention au cas ou l'on fait set_request('truc', NULL);
// http://doc.spip.org/@set_request
function set_request($var, $val = NULL, $c=false) {
	if (is_array($c)) {
		unset($c[$var]);
		if ($val !== NULL)
			$c[$var] = $val;
		return $c;
	}

	unset($_GET[$var]);
	unset($_POST[$var]);
	if ($val !== NULL)
		$_GET[$var] = $val;

	return false; # n'affecte pas $c
}


/**
 * Tester si une URL est absolue
 * 
 * On est sur le web, on exclut certains protocoles, 
 * notamment 'file://', 'php://' et d'autres…

 * @param string $url
 * @return bool
 */
function tester_url_absolue($url) {
	$url = trim($url);
	if (preg_match(";^([a-z]{3,7}:)?//;Uims", $url, $m)) {
		if (
			isset($m[1])
			and $p = strtolower(rtrim($m[1], ':'))
			and in_array($p, array('file', 'php', 'zlib', 'glob', 'phar', 'ssh2', 'rar', 'ogg', 'expect', 'zip'))
		  ) {
			return false;
		}
		return true;
	}
	return false;
}

/**
 * Prend une URL et lui ajoute/retire un parametre.
 * Exemples : [(#SELF|parametre_url{suite,18})] (ajout)
 *            [(#SELF|parametre_url{suite,''})] (supprime)
 *            [(#SELF|parametre_url{suite})]    (prend $suite dans la _request)
 *            [(#SELF|parametre_url{suite[],1})] (tableaux valeurs multiples)
 *
 * http://doc.spip.org/@parametre_url
 *
 * @param string $url
 * @param string $c
 * @param string|array $v
 * @param string $sep
 * @return string
 */
function parametre_url($url, $c, $v=NULL, $sep='&amp;') {
	// requete erronnee : plusieurs variable dans $c et aucun $v
	if (strpos($c,"|")!==false AND is_null($v))
		return null;

	// lever l'#ancre
	if (preg_match(',^([^#]*)(#.*)$,', $url, $r)) {
		$url = $r[1];
		$ancre = $r[2];
	} else
		$ancre = '';

	// eclater
	$url = preg_split(',[?]|&amp;|&,', $url);

	// recuperer la base
	$a = array_shift($url);
	if (!$a) $a= './';

	$regexp = ',^(' . str_replace('[]','\[\]',$c) . '[[]?[]]?)(=.*)?$,';
	$ajouts = array_flip(explode('|',$c));
	$u = is_array($v) ? $v : rawurlencode($v);
	$testv = (is_array($v)?count($v):strlen($v));
	// lire les variables et agir
	foreach ($url as $n => $val) {
		if (preg_match($regexp, urldecode($val), $r)) {
			if ($v === NULL) {
				return $r[2]?substr($r[2],1):'';
			}
			// suppression
			elseif (!$testv) {
				unset($url[$n]);
			}
	// Ajout. Pour une variable, remplacer au meme endroit,
	// pour un tableau ce sera fait dans la prochaine boucle
			elseif (substr($r[1],-2) != '[]') {
				$url[$n] = $r[1].'='.$u;
				unset($ajouts[$r[1]]);
			}
		}
	}

	// traiter les parametres pas encore trouves
	if ($v === NULL
	AND $args = func_get_args()
	AND count($args)==2)
		return $v;
	elseif ($testv) {
		foreach($ajouts as $k => $n) {
		  if (!is_array($v))
		    $url[] = $k .'=' . $u;
		  else {
		  	$id = (substr($k,-2) == '[]') ? $k : ($k ."[]");
		    foreach ($v as $w) $url[]= $id .'=' . $w;
		  }
		}
	}

	// eliminer les vides
	$url = array_filter($url);

	// recomposer l'adresse
	if ($url)
		$a .= '?' . join($sep, $url);

	return $a . $ancre;
}

// Prend une URL et lui ajoute/retire une ancre apres l'avoir nettoyee
// pour l'ancre on translitere, vire les non alphanum du debut,
// et on remplace ceux a l'interieur ou au bout par -
// http://doc.spip.org/@ancre_url
function ancre_url($url, $ancre) {
	// lever l'#ancre
	if (preg_match(',^([^#]*)(#.*)$,', $url, $r)) {
		$url = $r[1];
	}
	if (preg_match('/[^-_a-zA-Z0-9]+/S',$ancre)){
		if (!function_exists('translitteration'))
			include_spip('inc/charsets');
		$ancre = preg_replace(array('/^[^-_a-zA-Z0-9]+/', '/[^-_a-zA-Z0-9]/'), array('', '-'),
						translitteration($ancre));
	}
	return $url . (strlen($ancre) ? '#'. $ancre : '');
}

/**
 * pour le nom du cache, les types_urls et self
 * http://doc.spip.org/@nettoyer_uri
 *
 * @param string|null $reset
 * @return string
 */
function nettoyer_uri($reset = null)
{
	static $done = false;
	static $propre = '';
	if (!is_null($reset)) return $propre=$reset;
	if ($done) return $propre;
	$done = true;

	$uri1 = $GLOBALS['REQUEST_URI'];
	do {
		$uri = $uri1;
		$uri1 = preg_replace
			(',([?&])(PHPSESSID|(var_[^=&]*))=[^&]*(&|$),i',
			'\1', $uri);
	} while ($uri<>$uri1);

	return $propre = (preg_replace(',[?&]$,', '', $uri1));
}


/**
 * Donner l'URL de base d'un lien vers "soi-meme", modulo les trucs inutiles
 *
 * @param string $amp
 *    Style des esperluettes
 * @param bool $root
 * @return string
 *    URL vers soi-même
**/
function self($amp = '&amp;', $root = false) {
	$url = nettoyer_uri();
	if (!$root
		AND (
			// si pas de profondeur on peut tronquer
			$GLOBALS['profondeur_url']<(_DIR_RESTREINT?1:2)
			// sinon c'est OK si _SET_HTML_BASE a ete force a false
			OR (defined('_SET_HTML_BASE') AND !_SET_HTML_BASE))
		)
		$url = preg_replace(',^[^?]*/,', '', $url);
	// ajouter le cas echeant les variables _POST['id_...']
	foreach ($_POST as $v => $c)
		if (substr($v,0,3) == 'id_')
			$url = parametre_url($url, $v, $c, '&');

	// supprimer les variables sans interet
	if (test_espace_prive()) {
		$url = preg_replace (',([?&])('
		.'lang|show_docs|'
		.'changer_lang|var_lang|action)=[^&]*,i', '\1', $url);
		$url = preg_replace(',([?&])[&]+,', '\1', $url);
		$url = preg_replace(',[&]$,', '\1', $url);
	}

	// eviter les hacks
	include_spip('inc/filtres_mini');
	$url = spip_htmlspecialchars($url);

	// &amp; ?
	if ($amp != '&amp;')
		$url = str_replace('&amp;', $amp, $url);

	// Si ca demarre par ? ou vide, donner './'
	$url = preg_replace(',^([?].*)?$,', './\1', $url);

	return $url;
}

// Indique si on est dans l'espace prive
// http://doc.spip.org/@test_espace_prive
function test_espace_prive() {
	return defined('_ESPACE_PRIVE') ? _ESPACE_PRIVE : false;
}

/**
 * Verifie la presence d'un plugin active, identifie par son prefix
 *
 *
 * @param string $plugin
 * @return bool
 */
function test_plugin_actif($plugin){
	return ($plugin AND defined('_DIR_PLUGIN_'.strtoupper($plugin)))? true:false;
}

/**
 * Traduction des textes de SPIP
 * http://doc.spip.org/@_T
 *
 * @param string $texte
 * @param array $args
 * @param array $options
 *   string class : nom d'une classe a ajouter sur un span pour encapsuler la chaine
 *   bool force : forcer un retour meme si la chaine n'a pas de traduction
 * @return mixed|string
 */
function _T($texte, $args=array(), $options=array()) {
	static $traduire=false ;
	$o = array('class'=>'', 'force'=>true);
	if ($options){
		// support de l'ancien argument $class
		if (is_string($options))
			$options = array('class'=>$options);
		$o = array_merge($o,$options);
	}

 	if (!$traduire) {
		$traduire = charger_fonction('traduire', 'inc');
		include_spip('inc/lang');
	}
	
	// On peut passer explicitement la langue dans le tableau
	// On utilise le même nom de variable que la globale
	if (isset($args['spip_lang'])){
		$lang = $args['spip_lang'];
		// On l'enleve pour ne pas le passer au remplacement
		unset($args['spip_lang']);
	}
	// Sinon on prend la langue du contexte
	else {
		$lang = $GLOBALS['spip_lang'];
	}
	$text = $traduire($texte, $lang);

	if (!strlen($text)){
		if (!$o['force'])
			return '';

		$text = $texte;

		// pour les chaines non traduites, assurer un service minimum
		if (!$GLOBALS['test_i18n'] AND (_request('var_mode') != 'traduction'))
			$text = str_replace('_', ' ',
				 (($n = strpos($text,':')) === false ? $texte :
					substr($texte, $n+1)));
		$o['class'] = null;

	}

	return _L($text, $args, $o['class']);

}

// Remplacer les variables @....@ par leur valeur dans une chaine de langue.
// Aussi appelee quand une chaine n'est pas encore dans les fichiers de langue
// http://doc.spip.org/@_L
function _L($text, $args=array(), $class=null) {
	$f = $text;
	if (is_array($args)) {
		foreach ($args as $name => $value) {
			if ($class)
				$value = "<span class='$class'>$value</span>";
			$t = str_replace ("@$name@", $value, $text);
			if ($text !== $t) {unset($args[$name]); $text = $t;}
		}
		// Si des variables n'ont pas ete inserees, le signaler
		// (chaines de langues pas a jour)
		if ($args) spip_log("$f:  variables inutilisees " . join(', ', array_keys($args)),_LOG_DEBUG);
	}

	if (($GLOBALS['test_i18n'] OR (_request('var_mode') == 'traduction')) AND $class===null)
		return "<span class=debug-traduction-erreur>$text</span>";
	else
		return $text;
}

// Afficher "ecrire/data/" au lieu de "data/" dans les messages
// ou tmp/ au lieu de ../tmp/
// http://doc.spip.org/@joli_repertoire
function joli_repertoire($rep) {
	$a = substr($rep,0,1);
	if ($a<>'.' AND $a<>'/')
		$rep = (_DIR_RESTREINT?'':_DIR_RESTREINT_ABS).$rep;
	$rep = preg_replace(',(^\.\.\/),', '', $rep);
	return $rep;
}


//
// spip_timer : on l'appelle deux fois et on a la difference, affichable
//
// http://doc.spip.org/@spip_timer
function spip_timer($t='rien', $raw = false) {
	static $time;
	$a=time(); $b=microtime();
	// microtime peut contenir les microsecondes et le temps
	$b=explode(' ',$b);
	if (count($b)==2) $a = end($b); // plus precis !
	$b = reset($b);
	if (!isset($time[$t])) {
		$time[$t] = $a + $b;
	} else {
		$p = ($a + $b - $time[$t]) * 1000;
		unset($time[$t]);
#			echo "'$p'";exit;
		if ($raw) return $p;
		if ($p < 1000)
			$s = '';
		else {
			$s = sprintf("%d ", $x = floor($p/1000));
			$p -= ($x*1000);
		}
		return $s . sprintf($s?"%07.3f ms":"%.3f ms", $p);
	}
}


// Renvoie False si un fichier n'est pas plus vieux que $duree secondes,
// sinon renvoie True et le date sauf si ca n'est pas souhaite
// http://doc.spip.org/@spip_touch
function spip_touch($fichier, $duree=0, $touch=true) {
	if ($duree) {
		clearstatcache();
		if ((@$f=filemtime($fichier)) AND ($f >= time() - $duree))
			return false;
	}
	if ($touch!==false) {
		if (!@touch($fichier)) { spip_unlink($fichier); @touch($fichier); };
		@chmod($fichier, _SPIP_CHMOD & ~0111);
	}
	return true;
}

// Ce declencheur de tache de fond, de l'espace prive (cf inc_presentation)
// et de l'espace public (cf #SPIP_CRON dans inc_balise), est appelee
// par un background-image  car contrairement a un iframe vide,
// les navigateurs ne diront pas qu'ils n'ont pas fini de charger,
// c'est plus rassurant.
// C'est aussi plus discret qu'un <img> sous un navigateur non graphique.

// http://doc.spip.org/@action_cron
function action_cron() {
	include_spip('inc/headers');
	http_status(204); // No Content
	header("Connection: close");
	define('_DIRECT_CRON_FORCE',true);
	cron();
}

/**
 * cron() : execution des taches de fond
 * On peut lui passer en 1er (ou 2e arg pour compat)
 * le tableau de taches attendu par inc_genie()
 * Retourne Vrai si un tache a pu etre effectuee
 * pas de verrou ici : les verrous sont geres sur chaque tache
 * a chaque execution
 *
 * http://doc.spip.org/@cron
 *
 * @param array $taches
 *   taches forcees
 * @param array $taches_old
 *   taches forcees, pour compat avec ancienne syntaxe
 * @return bool
 */
function cron ($taches=array(), $taches_old= array()) {
	// si pas en mode cron force, laisser tomber.
	if (!defined('_DIRECT_CRON_FORCE')) return false;
	if (!is_array($taches)) $taches = $taches_old; // compat anciens appels
	// si taches a inserer en base et base inaccessible, laisser tomber
	// sinon on ne verifie pas la connexion tout de suite, car si ca se trouve
	// queue_sleep_time_to_next_job() dira qu'il n'y a rien a faire
	// et on evite d'ouvrir une connexion pour rien (utilisation de _DIRECT_CRON_FORCE dans mes_options.php)
	if ($taches AND count($taches) AND !spip_connect())  return false;
	spip_log("cron !",'jq'._LOG_DEBUG);
	if ($genie = charger_fonction('genie', 'inc', true)) {
		return $genie($taches);
	}
	return false;
}

/**
 * Ajout d'une tache dans la file d'attente
 *
 * @param $function
 *   The function name to call.
 * @param $description
 *   A human-readable description of the queued job.
 * @param $arguments
 *   Optional array of arguments to pass to the function.
 * @param $file
 *   Optional file path which needs to be included for $function.
 *   if ends with '/', will do charger_fonction($function,$file);
 * @param $no_duplicate
 *   If TRUE, do not add the job to the queue if one with the same function and
 *   arguments already exists.
 * @param $time
 *		time for starting the job. If 0, job will start as soon as possible
 * @param $priority
 *		-10 (low priority) to +10 (high priority), 0 is the default
 * @return int
 *	id of job
 */
function job_queue_add($function, $description, $arguments = array(), $file = '', $no_duplicate = FALSE, $time=0, $priority=0) {
	include_spip('inc/queue');
	return queue_add_job($function, $description, $arguments, $file, $no_duplicate, $time, $priority);
}

/**
 * Supprimer une tache de la file d'attente
 * @param int $id_job
 *  id of jonb to delete
 * @return bool
 */
function job_queue_remove($id_job){
	include_spip('inc/queue');
	return queue_remove_job($id_job);
}

/**
 * Associer une tache a un/des objets de SPIP
 * @param int $id_job
 *	id of job to link
 * @param array $objets
 *  can be a simple array('objet'=>'article','id_objet'=>23)
 *  or an array of simple array to link multiples objet in one time
 */
function job_queue_link($id_job,$objets){
	include_spip('inc/queue');
	return queue_link_job($id_job,$objets);
}


/**
 * Renvoyer le temps de repos restant jusqu'au prochain job
 * 0 si un job est a traiter
 * null si la queue n'est pas encore initialise
 * $force est utilisee par queue_set_next_job_time() pour maj la valeur
 *  - si true, force la relecture depuis le fichier
 *  - si int, affecte la static directement avec la valeur
 *
 * @staticvar int $queue_next_job_time
 * @param int/bool $force_next
 * @return int
 */
function queue_sleep_time_to_next_job($force=null) {
	static $queue_next_job_time = -1;
	if ($force===true)
		$queue_next_job_time = -1;
	elseif ($force)
		$queue_next_job_time = $force;

	if ($queue_next_job_time==-1) {
		define('_JQ_NEXT_JOB_TIME_FILENAME',_DIR_TMP . "job_queue_next.txt");
		// utiliser un cache memoire si dispo
		if (include_spip('inc/memoization') AND defined('_MEMOIZE_MEMORY') AND _MEMOIZE_MEMORY) {
			$queue_next_job_time = cache_get(_JQ_NEXT_JOB_TIME_FILENAME);
		}
		else {
			$queue_next_job_time = null;
			if (lire_fichier(_JQ_NEXT_JOB_TIME_FILENAME, $contenu))
				$queue_next_job_time = intval($contenu);
		}
	}

	if (is_null($queue_next_job_time))
		return null;
	if (!$_SERVER['REQUEST_TIME'])
		$_SERVER['REQUEST_TIME'] = time();
	return $queue_next_job_time-$_SERVER['REQUEST_TIME'];
}


// transformation XML des "&" en "&amp;"
// http://doc.spip.org/@quote_amp
function quote_amp($u) {
	return preg_replace(
		"/&(?![a-z]{0,4}\w{2,3};|#x?[0-9a-f]{2,5};)/i",
		"&amp;",$u);
}

// Production d'une balise Script valide
// http://doc.spip.org/@http_script
function http_script($script, $src='', $noscript='') {
	static $done = array();

	if ($src && !isset($done[$src])){
		$done[$src] = true;
		$src = find_in_path($src, _JAVASCRIPT);
		$src = " src='$src'";
	}
	else $src = '';
	if ($script)
		$script = ("/*<![CDATA[*/\n" .
		preg_replace(',</([^>]*)>,','<\/\1>', $script) .
		"/*]]>*/");
	if ($noscript)
		$noscript = "<noscript>\n\t$noscript\n</noscript>\n";

	return ($src OR $script OR $noscript)
	? "<script type='text/javascript'$src>$script</script>$noscript"
	: '';
}

// Transforme n'importe quel champ en une chaine utilisable
// en PHP ou Javascript en toute securite
// < ? php $x = '[(#TEXTE|texte_script)]'; ? >
// http://doc.spip.org/@texte_script
function texte_script($texte) {
	return str_replace('\'', '\\\'', str_replace('\\', '\\\\', $texte));
}

// Chaque appel a cette fonction ajoute un repertoire en tete du chemin courant (path)
// si un repertoire lui est passe en parametre
// retourne le chemin courant sinon, sous forme de array.
// Si l'argument est de la forme dir1:dir2:dir3, ces 3 chemins sont places en tete
// du path, dans cet ordre.
// Exception: si un $dossier_squelette est defini, il reste en tete, pour raison historique
// http://doc.spip.org/@_chemin
function _chemin($dir_path=NULL){
	static $path_base = NULL;
	static $path_full = NULL;
	if ($path_base==NULL){
		// Chemin standard depuis l'espace public
		$path = defined('_SPIP_PATH') ? _SPIP_PATH :
			_DIR_RACINE.':'.
			_DIR_RACINE.'squelettes-dist/:'.
			_DIR_RACINE.'prive/:'.
			_DIR_RESTREINT;
		// Ajouter squelettes/
		if (@is_dir(_DIR_RACINE.'squelettes'))
			$path = _DIR_RACINE.'squelettes/:' . $path;
		foreach (explode(':', $path) as $dir) {
			if (strlen($dir) AND substr($dir,-1) != '/')
				$dir .= "/";
			$path_base[] = $dir;
		}
		$path_full = $path_base;
		// Et le(s) dossier(s) des squelettes nommes
		if (strlen($GLOBALS['dossier_squelettes']))
			foreach (array_reverse(explode(':', $GLOBALS['dossier_squelettes'])) as $d)
				array_unshift($path_full, ($d[0] == '/' ? '' : _DIR_RACINE) . $d . '/');
		$GLOBALS['path_sig'] = md5(serialize($path_full));
	}
	if ($dir_path===NULL) return $path_full;

	if (strlen($dir_path)){
		$tete = "";
		if (reset($path_base)==_DIR_RACINE.'squelettes/')
			$tete = array_shift($path_base);
		$dirs = array_reverse(explode(':',$dir_path));
		foreach($dirs as $dir_path){
				#if ($dir_path{0}!='/')
				#	$dir_path = $dir_path;
				if (substr($dir_path,-1) != '/')
					$dir_path .= "/";
				if (!in_array($dir_path,$path_base))
					array_unshift($path_base,$dir_path);
		}
		if (strlen($tete))
			array_unshift($path_base,$tete);
	}
	$path_full = $path_base;
	// Et le(s) dossier(s) des squelettes nommes
	if (strlen($GLOBALS['dossier_squelettes']))
		foreach (array_reverse(explode(':', $GLOBALS['dossier_squelettes'])) as $d)
			array_unshift($path_full, ($d[0] == '/' ? '' : _DIR_RACINE) . $d . '/');

	$GLOBALS['path_sig'] = md5(serialize($path_full));
	return $path_full;
}

// http://doc.spip.org/@creer_chemin
function creer_chemin() {
	$path_a = _chemin();
	static $c = '';

	// on calcule le chemin si le dossier skel a change
	if ($c != $GLOBALS['dossier_squelettes']) {
		// assurer le non plantage lors de la montee de version :
		$c = $GLOBALS['dossier_squelettes'];
		$path_a = _chemin(''); // forcer un recalcul du chemin
	}
	return $path_a;
}


function lister_themes_prives(){
	static $themes = null;
	if (is_null($themes)){
		// si pas encore definie
		if (!defined('_SPIP_THEME_PRIVE'))
			define('_SPIP_THEME_PRIVE', 'spip');
		$themes = array(_SPIP_THEME_PRIVE);
		// lors d'une installation neuve, prefs n'est pas definie.
		if (isset($GLOBALS['visiteur_session']['prefs'])) {
			$prefs = $GLOBALS['visiteur_session']['prefs'];
		} else {
			$prefs = array();
		}
		if (is_string($prefs))
			$prefs = unserialize($GLOBALS['visiteur_session']['prefs']);
		if (
			((isset($prefs['theme']) AND $theme = $prefs['theme'])
			OR (isset($GLOBALS['theme_prive_defaut']) AND $theme = $GLOBALS['theme_prive_defaut']))
			AND $theme != _SPIP_THEME_PRIVE)
			array_unshift($themes,$theme); // placer le theme choisi en tete
	}
	return $themes;
}

function find_in_theme($file, $subdir='', $include=false){
	static $themefiles=array();
	if (isset($themefiles["$subdir$file"])) return $themefiles["$subdir$file"];
	$themes = lister_themes_prives();
	foreach($themes as $theme){
		if ($f = find_in_path($file,"prive/themes/$theme/$subdir",$include))
			return $themefiles["$subdir$file"] = $f;
	}
	spip_log("$file introuvable dans le theme prive ".reset($themes),'theme');
	return $themefiles["$subdir$file"] = "";
}

// Cherche une image dans les dossiers images
// gere le renommage des icones de facon temporaire (le temps de la migration)
// definis par _NOM_IMG_PACK et _DIR_IMG_PACK
// peut se trouver dans un dossier plugin, donc on passe par un find_in_path si elle n'est pas
// dans _DIR_IMG_PACK
// http://doc.spip.org/@chemin_image
function chemin_image($icone){
	static $icone_renommer;
	// gerer le cas d'un double appel en evitant de refaire le travail inutilement
	if (strpos($icone,"/")!==false AND file_exists($icone)) return $icone;
	
	// si c'est un nom d'image complet (article-24.png) essayer de le renvoyer direct
	if (preg_match(',[.](png|gif|jpg)$,',$icone) AND $f = find_in_theme("images/$icone"))
		return $f;
	// sinon passer par le module de renommage
	if (is_null($icone_renommer))
		$icone_renommer = charger_fonction('icone_renommer','inc',true);
	if ($icone_renommer){
		list($icone,$fonction) = $icone_renommer($icone,"");
		if (file_exists($icone))
			return $icone;
	}
	return find_in_path ($icone, _NOM_IMG_PACK);
}

//
// chercher un fichier $file dans le SPIP_PATH
// si on donne un sous-repertoire en 2e arg optionnel, il FAUT le / final
// si 3e arg vrai, on inclut si ce n'est fait.
$GLOBALS['path_sig'] = '';
$GLOBALS['path_files'] = null;

// http://doc.spip.org/@find_in_path
function find_in_path ($file, $dirname='', $include=false) {
	static $dirs=array();
	static $inc = array(); # cf http://trac.rezo.net/trac/spip/changeset/14743
	static $c = '';

	// on calcule le chemin si le dossier skel a change
	if ($c != $GLOBALS['dossier_squelettes']){
		// assurer le non plantage lors de la montee de version :
		$c = $GLOBALS['dossier_squelettes'];
		creer_chemin(); // forcer un recalcul du chemin et la mise a jour de path_sig
	}

	if (isset($GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file])) {
		if (!$GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file])
			return false;
		if ($include AND !isset($inc[$dirname][$file])) {
			include_once _ROOT_CWD . $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file];
			$inc[$dirname][$file] = $inc[''][$dirname . $file] = true;
		}
		return $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file];
	}

	$a = strrpos($file,'/');
	if ($a !== false) {
		$dirname .= substr($file, 0, ++$a);
		$file = substr($file, $a);
	}

	foreach(creer_chemin() as $dir) {
		if (!isset($dirs[$a = $dir . $dirname]))
			$dirs[$a] = (is_dir(_ROOT_CWD . $a) || !$a) ;
		if ($dirs[$a]) {
			if (file_exists(_ROOT_CWD . ($a .= $file))) {
				if ($include AND !isset($inc[$dirname][$file])) {
					include_once _ROOT_CWD . $a;
					$inc[$dirname][$file] = $inc[''][$dirname . $file] = true;
				}
				if (!defined('_SAUVER_CHEMIN')){
					// si le chemin n'a pas encore ete charge, ne pas lever le flag, ne pas cacher
					if (is_null($GLOBALS['path_files'])) return $a;
					define('_SAUVER_CHEMIN', true);
				}
				return $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file] = $GLOBALS['path_files'][$GLOBALS['path_sig']][''][$dirname . $file] = $a;
			}
		}
	}

	if ($include){
		spip_log("include_spip $dirname$file non trouve");
		if ($include==='required'){
			echo '<pre>',
			"<strong>Erreur Fatale</strong><br />";
			if (function_exists('debug_print_backtrace'))
				echo debug_print_backtrace();
			echo '</pre>';
			die("Erreur interne: ne peut inclure $dirname$file");
		}
	}

	if (!defined('_SAUVER_CHEMIN')){
		// si le chemin n'a pas encore ete charge, ne pas lever le flag, ne pas cacher
		if (is_null($GLOBALS['path_files'])) return false;
		define('_SAUVER_CHEMIN', true);
	}
	return $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file] = $GLOBALS['path_files'][$GLOBALS['path_sig']][''][$dirname . $file] = false;
}

function clear_path_cache(){
	$GLOBALS['path_files'] = array();
	spip_unlink(_CACHE_CHEMIN);
}
function load_path_cache(){
	// charger le path des plugins
	if (@is_readable(_CACHE_PLUGINS_PATH)){
		include_once(_CACHE_PLUGINS_PATH);
	}
	$GLOBALS['path_files'] = array();
	// si le visiteur est admin,
	// on ne recharge pas le cache pour forcer sa mise a jour
	if (
		// la session n'est pas encore chargee a ce moment, on ne peut donc pas s'y fier
		//AND (!isset($GLOBALS['visiteur_session']['statut']) OR $GLOBALS['visiteur_session']['statut']!='0minirezo')
		// utiliser le cookie est un pis aller qui marche 'en general'
		// on blinde par un second test au moment de la lecture de la session
		// !isset($_COOKIE[$GLOBALS['cookie_prefix'].'_admin'])
		// et en ignorant ce cache en cas de recalcul explicite
		!_request('var_mode')
		){
		// on essaye de lire directement sans verrou pour aller plus vite
		if ($contenu = spip_file_get_contents(_CACHE_CHEMIN)){
			// mais si semble corrompu on relit avec un verrou
			if (!$GLOBALS['path_files']=unserialize($contenu)){
				lire_fichier(_CACHE_CHEMIN,$contenu);
				if (!$GLOBALS['path_files']=unserialize($contenu))
					$GLOBALS['path_files'] = array();
			}
		}
	}
}

function save_path_cache(){
	if (defined('_SAUVER_CHEMIN')
		AND _SAUVER_CHEMIN)
		ecrire_fichier(_CACHE_CHEMIN,serialize($GLOBALS['path_files']));
}


/**
 * Trouve tous les fichiers du path correspondants a un pattern
 * pour un nom de fichier donne, ne retourne que le premier qui sera trouve
 * par un find_in_path
 *
 * @param string $dir
 * @param string $pattern
 * @param bool $recurs
 * @return array
 */
// http://doc.spip.org/@find_all_in_path
function find_all_in_path($dir,$pattern, $recurs=false){
	$liste_fichiers=array();
	$maxfiles = 10000;

	// Parcourir le chemin
	foreach (creer_chemin() as $d) {
		$f = $d.$dir;
		if (@is_dir($f)){
			$liste = preg_files($f,$pattern,$maxfiles-count($liste_fichiers),$recurs===true?array():$recurs);
			foreach($liste as $chemin){
				$nom = basename($chemin);
				// ne prendre que les fichiers pas deja trouves
				// car find_in_path prend le premier qu'il trouve,
				// les autres sont donc masques
				if (!isset($liste_fichiers[$nom]))
					$liste_fichiers[$nom] = $chemin;
			}
		}
	}
	return $liste_fichiers;
}

// predicat sur les scripts de ecrire qui n'authentifient pas par cookie

// http://doc.spip.org/@autoriser_sans_cookie
function autoriser_sans_cookie($nom)
{
  static $autsanscookie = array('install', 'base_repair');
  $nom = preg_replace('/.php[3]?$/', '', basename($nom));
  return in_array($nom, $autsanscookie);
}

/**
 * Fonction codant et decodant les URLS des objets SQL mis en page par SPIP
 *
 * http://doc.spip.org/@generer_url_entite
 *
 *
 * @param string $id
 *   numero de la cle primaire si nombre, URL a decoder si pas numerique
 * @param string $entite
 *   surnom de la table SQL (donne acces au nom de cle primaire)
 * @param string $args
 *   query_string a placer apres cle=$id&....
 * @param string $ancre
 *   ancre a mettre a la fin de l'URL a produire
 * @param bool|string $public
 *   produire l'URL publique ou privee (par defaut: selon espace)
 *   si string : serveur de base de donnee (nom du connect)
 * @param string $type
 *   fichier dans le repertoire ecrire/urls determinant l'apparence
 * @return string|array
 *   url codee ou fonction de decodage
 *   array : derogatoire, la fonction d'url retourne (objet,id_objet) utilises par nettoyer_raccourcis_typo() pour generer un lien titre
 *           (cas des raccourcis personalises [->spip20] : il faut implementer une fonction generer_url_spip et une fonction generer_url_ecrire_spip)
 */
function generer_url_entite($id='', $entite='', $args='', $ancre='', $public=NULL, $type=NULL)
{
	if ($public === NULL) $public = !test_espace_prive();
	$entite = objet_type($entite); // cas particulier d'appels sur objet/id_objet...

	if (!$public) {
		if (!$entite) return '';
		if (!function_exists('generer_url_ecrire_objet'))
			include_spip('inc/urls');
		$res = generer_url_ecrire_objet($entite,$id, $args, $ancre, false);
	} else {
		if ($type === NULL) {
			$type = ($GLOBALS['type_urls'] === 'page'
				AND $GLOBALS['meta']['type_urls'])
			?  $GLOBALS['meta']['type_urls']
			:  $GLOBALS['type_urls']; // pour SPIP <2
		}

		$f = charger_fonction($type, 'urls', true);
		// se rabattre sur les urls page si les urls perso non dispo
		if (!$f) $f = charger_fonction('page', 'urls', true);

		// si $entite='', on veut la fonction de passage URL ==> id
		// sinon on veut effectuer le passage id ==> URL
		if (!$entite) return $f;

		// mais d'abord il faut tester le cas des urls sur une
		// base distante
		if (is_string($public)
		AND $g = charger_fonction('connect', 'urls', true))
			$f = $g;

		$res = $f(intval($id), $entite, $args, $ancre, $public);

	}
	if ($res) return $res;
	// Sinon c'est un raccourci ou compat SPIP < 2
	if (!function_exists($f = 'generer_url_' . $entite)) {
		if (!function_exists($f .= '_dist')) $f = '';
	}
	if ($f) {
		$url = $f($id, $args, $ancre);
		if (strlen($args))
			$url .= strstr($url, '?')
				? '&amp;'.$args
				: '?'.$args;
		return $url;
	}
	// On a ete gentil mais la ....
	spip_log("generer_url_entite: entite $entite ($f) inconnue $type $public");
	return '';
}

function generer_url_ecrire_entite_edit($id, $entite, $args='', $ancre=''){
	$exec = objet_info($entite,'url_edit');
	$url = generer_url_ecrire($exec,$args);
	if (intval($id))
		$url = parametre_url($url,id_table_objet($entite),$id);
	else
		$url = parametre_url($url,'new','oui');
	if ($ancre)
		$url = ancre_url($url,$ancre);
	return $url;
}

// http://doc.spip.org/@urls_connect_dist
function urls_connect_dist($i, &$entite, $args='', $ancre='', $public=null) {
	include_spip('base/connect_sql');
	$id_type = id_table_objet($entite,$public);
	return _DIR_RACINE . get_spip_script('./')
	  . "?"._SPIP_PAGE."=$entite&$id_type=$i&connect=$public"
	  . (!$args ? '' : "&$args")
	  . (!$ancre ? '' : "#$ancre");
}


// Transformer les caracteres utf8 d'une URL (farsi par ex) selon la RFC 1738
function urlencode_1738($url) {
	if (preg_match(',[^\x00-\x7E],sS', $url)){
		$uri = '';
		for ($i=0; $i < strlen($url); $i++) {
			if (ord($a = $url[$i]) > 127)
				$a = rawurlencode($a);
			$uri .= $a;
		}
		$url = $uri;
	}
	return quote_amp($url);
}

// http://doc.spip.org/@generer_url_entite_absolue
function generer_url_entite_absolue($id='', $entite='', $args='', $ancre='', $connect=NULL)
{
	if (!$connect) $connect = true;
	$h = generer_url_entite($id, $entite, $args, $ancre, $connect);
	if (!preg_match(',^\w+:,', $h)) {
		include_spip('inc/filtres_mini');
		$h = url_absolue($h);
	}
	return  $h;
}

// Sur certains serveurs, la valeur 'Off' tient lieu de false dans certaines
// variables d'environnement comme $_SERVER[HTTPS] ou ini_get(register_globals)
// http://doc.spip.org/@test_valeur_serveur
function test_valeur_serveur($truc) {
	if (!$truc) return false;
	return (strtolower($truc) !== 'off');
}

//
// Fonctions de fabrication des URL des scripts de Spip
//
/**
 * L'URL de base du site, en priorité sans se fier a meta(adresse_site) qui
 * peut etre fausse (sites a plusieurs noms d'hotes, deplacements, erreurs).
 * En dernier recours, lorsqu'on ne trouve rien, on utilise adresse_site comme fallback.
 * Note : la globale $profondeur_url doit etre initialisee de maniere a
 * indiquer le nombre de sous-repertoires de l'url courante par rapport a la
 * racine de SPIP : par exemple, sur ecrire/ elle vaut 1, sur sedna/ 1, et a
 * la racine 0. Sur url/perso/ elle vaut 2
 * http://doc.spip.org/@url_de_base
 *
 * @param int|boo|array $profondeur
 *    si non renseignee : retourne l'url pour la profondeur $GLOBALS['profondeur_url']
 *    si int : indique que l'on veut l'url pour la prondeur indiquee
 *    si bool : retourne le tableau static complet
 *    si array : reinitialise le tableau static complet avec la valeur fournie
 * @return string|array
 */
function url_de_base($profondeur=null) {

	static $url = array();
	if (is_array($profondeur)) return $url = $profondeur;
	if ($profondeur===false) return $url;

	if (is_null($profondeur)) $profondeur = $GLOBALS['profondeur_url'];

	if (isset($url[$profondeur]))
		return $url[$profondeur];

	$http = (
		(isset($_SERVER["SCRIPT_URI"]) AND
			substr($_SERVER["SCRIPT_URI"],0,5) == 'https')
		OR (isset($_SERVER['HTTPS']) AND
		    test_valeur_serveur($_SERVER['HTTPS']))
	) ? 'https' : 'http';
	// note : HTTP_HOST contient le :port si necessaire
	$host = $_SERVER['HTTP_HOST'];
	// si on n'a pas trouvé d'hôte du tout, en dernier recours on utilise adresse_site comme fallback
	if (is_null($host) and isset($GLOBALS['meta']['adresse_site'])) {
		$host = $GLOBALS['meta']['adresse_site'];
		if ($scheme = parse_url($host, PHP_URL_SCHEME)) {
			$http = $scheme;
			$host = str_replace("{$scheme}://", '', $host);
		}
	}
	if (isset($_SERVER['SERVER_PORT'])
		AND $port=$_SERVER['SERVER_PORT']
		AND strpos($host,":")==false){
		if ($http=="http" AND $port!=80) $host.=":$port";
		if ($http=="https" AND $port!=443) $host.=":$port";
	}
	if (!$GLOBALS['REQUEST_URI']){
		if (isset($_SERVER['REQUEST_URI'])) {
			$GLOBALS['REQUEST_URI'] = $_SERVER['REQUEST_URI'];
		} else {
			$GLOBALS['REQUEST_URI'] = $_SERVER['PHP_SELF'];
			if ($_SERVER['QUERY_STRING']
			AND !strpos($_SERVER['REQUEST_URI'], '?'))
				$GLOBALS['REQUEST_URI'] .= '?'.$_SERVER['QUERY_STRING'];
		}
	}

	$url[$profondeur] = url_de_($http,$host,$GLOBALS['REQUEST_URI'],$profondeur);

	return $url[$profondeur];
}
/**
 * fonction testable de construction d'une url appelee par url_de_base()
 * @param string $http
 * @param string $host
 * @param string $request
 * @param int $prof
 * @return string
 */
function url_de_($http,$host,$request,$prof=0){
	$prof = max($prof,0);

	$myself = ltrim($request,'/');
	// vieux mode HTTP qui envoie après le nom de la methode l'URL compléte
	// protocole, "://", nom du serveur avant le path dans _SERVER["REQUEST_URI"]
	if (strpos($myself,'://') !== false) {
		$myself = explode('://',$myself);
		array_shift($myself);
		$myself = implode('://',$myself);
		$myself = explode('/',$myself);
		array_shift($myself);
		$myself = implode('/',$myself);
	}
	# supprimer la chaine de GET
	list($myself) = explode('?', $myself);
	$url = join('/', array_slice(explode('/', $myself), 0, -1-$prof)).'/';

	$url = $http.'://'.rtrim($host,'/').'/'.ltrim($url,'/');
	return $url;
}


// Pour une redirection, la liste des arguments doit etre separee par "&"
// Pour du code XHTML, ca doit etre &amp;
// Bravo au W3C qui n'a pas ete capable de nous eviter ca
// faute de separer proprement langage et meta-langage

// Attention, X?y=z et "X/?y=z" sont completement differents!
// http://httpd.apache.org/docs/2.0/mod/mod_dir.html

// http://doc.spip.org/@generer_url_ecrire
function generer_url_ecrire($script='', $args="", $no_entities=false, $rel=false) {
	if (!$rel)
		$rel = url_de_base() . _DIR_RESTREINT_ABS . _SPIP_ECRIRE_SCRIPT;
	else if (!is_string($rel))
		$rel = _DIR_RESTREINT ? _DIR_RESTREINT :
			('./'  . _SPIP_ECRIRE_SCRIPT);

	@list($script, $ancre) = explode('#', $script);
	if ($script AND ($script<>'accueil' OR $rel))
		$args = "?exec=$script" . (!$args ? '' : "&$args");
	elseif ($args)
		$args ="?$args";
	if ($ancre) $args .= "#$ancre";
	return $rel . ($no_entities ? $args : str_replace('&', '&amp;', $args));
}

// http://doc.spip.org/@generer_url_retour
function generer_url_retour($script, $args="")
{
	return rawurlencode(generer_url_ecrire($script, $args, true, true));
}

//
// Adresse des scripts publics (a passer dans inc-urls...)
//

// Detecter le fichier de base, a la racine, comme etant spip.php ou ''
// dans le cas de '', un $default = './' peut servir (comme dans urls/page.php)
// http://doc.spip.org/@get_spip_script
function get_spip_script($default='') {
	# cas define('_SPIP_SCRIPT', '');
	if (_SPIP_SCRIPT)
		return _SPIP_SCRIPT;
	else
		return $default;
}

// http://doc.spip.org/@generer_url_public
function generer_url_public($script='', $args="", $no_entities=false, $rel=true, $action='') {
	// si le script est une action (spip_pass, spip_inscription),
	// standardiser vers la nouvelle API

	if (!$action) $action = get_spip_script();
	if ($script)
		$action = parametre_url($action, _SPIP_PAGE, $script, '&');

	if ($args) {
		if (is_array($args)) {
			$r = '';
			foreach($args as $k => $v) $r .= '&' . $k . '=' . $v;
			$args = substr($r,1);
		}
		$action .=
			(strpos($action, '?') !== false ? '&' : '?') . $args;
	}
	if (!$no_entities)
		$action = quote_amp($action);

	// ne pas generer une url avec /./?page= en cas d'url absolue et de _SPIP_SCRIPT vide
	return ($rel ? _DIR_RACINE . $action : rtrim(url_de_base(),'/') . preg_replace(",^/[.]/,","/","/$action"));
}

// http://doc.spip.org/@generer_url_prive
function generer_url_prive($script, $args="", $no_entities=false) {

	return generer_url_public($script, $args, $no_entities, false, _DIR_RESTREINT_ABS .  'prive.php');
}

// Pour les formulaires en methode POST,
// mettre le nom du script a la fois en input-hidden et dans le champ action:
// 1) on peut ainsi memoriser le signet comme si c'etait un GET
// 2) ca suit http://en.wikipedia.org/wiki/Representational_State_Transfer

// http://doc.spip.org/@generer_form_ecrire
function generer_form_ecrire($script, $corps, $atts='', $submit='') {
	global $spip_lang_right;

	$script1 = explode('&', $script);
	$script1 = reset($script1);

	return "<form action='"
	. ($script ? generer_url_ecrire($script) : '')
	. "' "
	. ($atts ? $atts : " method='post'")
	.  "><div>\n"
	. "<input type='hidden' name='exec' value='$script1' />"
	. $corps
	. (!$submit ? '' :
	     ("<div style='text-align: $spip_lang_right'><input class='fondo' type='submit' value=\"".entites_html($submit)."\" /></div>"))
	. "</div></form>\n";
}

/**
 * Generer un formulaire pour lancer une action vers $script
 *
 * Attention, JS/Ajax n'aime pas le melange de param GET/POST
 * On n'applique pas la recommandation ci-dessus pour les scripts publics
 * qui ne sont pas destines a etre mis en signets
 * http://doc.spip.org/@generer_form_action
 *
 * @param string $script
 * @param string $corps
 * @param string $atts
 * @param bool $public
 * @return string
 */
function generer_form_action($script, $corps, $atts='', $public=false) {
	// si l'on est dans l'espace prive, on garde dans l'url
	// l'exec a l'origine de l'action, qui permet de savoir si il est necessaire
	// ou non de proceder a l'authentification (cas typique de l'install par exemple)
	$h = (_DIR_RACINE AND !$public)
	? generer_url_ecrire(_request('exec'))
	: generer_url_public();

	return "\n<form action='" .
	  $h .
	  "'" .
	  $atts .
	  ">\n" .
	  "<div>" .
  	  "\n<input type='hidden' name='action' value='$script' />" .
	  $corps .
	  "</div></form>";
}

// http://doc.spip.org/@generer_url_action
function generer_url_action($script, $args="", $no_entities=false , $public = false) {
	// si l'on est dans l'espace prive, on garde dans l'url
	// l'exec a l'origine de l'action, qui permet de savoir si il est necessaire
	// ou non de proceder a l'authentification (cas typique de l'install par exemple)
	$url = (_DIR_RACINE  AND !$public)
	  ? generer_url_ecrire(_request('exec'))
	  :  generer_url_public('','',false,false);
	$url = parametre_url($url,'action',$script);
	if ($args) $url .= quote_amp('&'.$args);

	if ($no_entities) $url = str_replace('&amp;','&',$url);
	return $url;
}


/**
 * Fonction d'initialisation groupee pour compatibilite ascendante
 *
 * @param string $pi
 * @param string $pa
 * @param string $ti
 * @param string $ta
 */
function spip_initialisation($pi=NULL, $pa=NULL, $ti=NULL, $ta=NULL) {
	spip_initialisation_core($pi,$pa,$ti,$ta);
	spip_initialisation_suite();
}

/**
 * Fonction d'initialisation, appellee dans inc_version ou mes_options
 * Elle definit les repertoires et fichiers non partageables
 * et indique dans $test_dirs ceux devant etre accessibles en ecriture
 * mais ne touche pas a cette variable si elle est deja definie
 * afin que mes_options.php puisse en specifier d'autres.
 * Elle definit ensuite les noms des fichiers et les droits.
 * Puis simule un register_global=on securise.
 *
 * @param string $pi
 * @param string $pa
 * @param string $ti
 * @param string $ta
 */
function spip_initialisation_core($pi=NULL, $pa=NULL, $ti=NULL, $ta=NULL) {
	static $too_late = 0;
	if ($too_late++) return;
	
	// Declaration des repertoires

	// le nom du repertoire plugins/ activables/desactivables
	if (!defined('_DIR_PLUGINS')) define('_DIR_PLUGINS', _DIR_RACINE . "plugins/");

	// le nom du repertoire des extensions/ permanentes du core, toujours actives
	if (!defined('_DIR_PLUGINS_DIST')) define('_DIR_PLUGINS_DIST', _DIR_RACINE . "plugins-dist/");

	// le nom du repertoire des librairies
	if (!defined('_DIR_LIB')) define('_DIR_LIB', _DIR_RACINE . "lib/");
	
	if (!defined('_DIR_IMG')) define('_DIR_IMG', $pa);
	if (!defined('_DIR_LOGOS')) define('_DIR_LOGOS', $pa);
	if (!defined('_DIR_IMG_ICONES')) define('_DIR_IMG_ICONES', _DIR_LOGOS . "icones/");

	if (!defined('_DIR_DUMP')) define('_DIR_DUMP', $ti . "dump/");
	if (!defined('_DIR_SESSIONS')) define('_DIR_SESSIONS', $ti . "sessions/");
	if (!defined('_DIR_TRANSFERT')) define('_DIR_TRANSFERT', $ti . "upload/");
	if (!defined('_DIR_CACHE')) define('_DIR_CACHE', $ti . "cache/");
	if (!defined('_DIR_CACHE_XML')) define('_DIR_CACHE_XML', _DIR_CACHE . "xml/");
	if (!defined('_DIR_SKELS')) define('_DIR_SKELS',  _DIR_CACHE . "skel/");
	if (!defined('_DIR_AIDE')) define('_DIR_AIDE',  _DIR_CACHE . "aide/");
	if (!defined('_DIR_TMP')) define('_DIR_TMP', $ti);

	if (!defined('_DIR_VAR')) define('_DIR_VAR', $ta);

	if (!defined('_DIR_ETC')) define('_DIR_ETC', $pi);
	if (!defined('_DIR_CONNECT')) define('_DIR_CONNECT', $pi);
	if (!defined('_DIR_CHMOD')) define('_DIR_CHMOD', $pi);

	if (!isset($GLOBALS['test_dirs']))
	  // Pas $pi car il est bon de le mettre hors ecriture apres intstall
	  // il sera rajoute automatiquement si besoin a l'etape 2 de l'install
		$GLOBALS['test_dirs'] =  array($pa, $ti, $ta);

	// Declaration des fichiers

	if (!defined('_CACHE_PLUGINS_PATH')) define('_CACHE_PLUGINS_PATH', _DIR_CACHE . "charger_plugins_chemins.php");
	if (!defined('_CACHE_PLUGINS_OPT')) define('_CACHE_PLUGINS_OPT', _DIR_CACHE . "charger_plugins_options.php");
	if (!defined('_CACHE_PLUGINS_FCT')) define('_CACHE_PLUGINS_FCT', _DIR_CACHE . "charger_plugins_fonctions.php");
	if (!defined('_CACHE_PIPELINES')) define('_CACHE_PIPELINES',  _DIR_CACHE."charger_pipelines.php");
	if (!defined('_CACHE_CHEMIN')) define('_CACHE_CHEMIN',  _DIR_CACHE."chemin.txt");

	# attention .php obligatoire pour ecrire_fichier_securise
	if (!defined('_FILE_META')) define('_FILE_META', $ti . 'meta_cache.php');
	if (!defined('_DIR_LOG')) define('_DIR_LOG', _DIR_TMP . 'log/');
	if (!defined('_FILE_LOG')) define('_FILE_LOG', 'spip');
	if (!defined('_FILE_LOG_SUFFIX')) define('_FILE_LOG_SUFFIX', '.log');

	// Le fichier de connexion a la base de donnees
	// tient compte des anciennes versions (inc_connect...)
	if (!defined('_FILE_CONNECT_INS')) define('_FILE_CONNECT_INS', 'connect');
	if (!defined('_FILE_CONNECT')) define('_FILE_CONNECT',
		(@is_readable($f = _DIR_CONNECT . _FILE_CONNECT_INS . '.php') ? $f
	:	(@is_readable($f = _DIR_RESTREINT . 'inc_connect.php') ? $f
	:	false)));

	// Le fichier de reglages des droits
	if (!defined('_FILE_CHMOD_INS')) define('_FILE_CHMOD_INS', 'chmod');
	if (!defined('_FILE_CHMOD')) define('_FILE_CHMOD',
		(@is_readable($f = _DIR_CHMOD . _FILE_CHMOD_INS . '.php') ? $f
	:	false));

	if (!defined('_FILE_LDAP')) define('_FILE_LDAP', 'ldap.php');

	if (!defined('_FILE_TMP_SUFFIX')) define('_FILE_TMP_SUFFIX', '.tmp.php');
	if (!defined('_FILE_CONNECT_TMP')) define('_FILE_CONNECT_TMP', _DIR_CONNECT . _FILE_CONNECT_INS . _FILE_TMP_SUFFIX);
	if (!defined('_FILE_CHMOD_TMP')) define('_FILE_CHMOD_TMP', _DIR_CHMOD . _FILE_CHMOD_INS . _FILE_TMP_SUFFIX);

	// Definition des droits d'acces en ecriture
	if (!defined('_SPIP_CHMOD') AND _FILE_CHMOD)
		include_once _FILE_CHMOD;

	// Se mefier des fichiers mal remplis!
	if (!defined('_SPIP_CHMOD')) define('_SPIP_CHMOD', 0777);

	// Le charset par defaut lors de l'installation
	if (!defined('_DEFAULT_CHARSET')) define('_DEFAULT_CHARSET', 'utf-8');
	if (!defined('_ROOT_PLUGINS')) define('_ROOT_PLUGINS', _ROOT_RACINE . "plugins/");
	if (!defined('_ROOT_PLUGINS_DIST')) define('_ROOT_PLUGINS_DIST', _ROOT_RACINE . "plugins-dist/");
	if (!defined('_ROOT_PLUGINS_SUPPL') && defined('_DIR_PLUGINS_SUPPL') && _DIR_PLUGINS_SUPPL) define('_ROOT_PLUGINS_SUPPL', _ROOT_RACINE . str_replace(_DIR_RACINE,'',_DIR_PLUGINS_SUPPL));

	// La taille des Log
	if (!defined('_MAX_LOG')) define('_MAX_LOG', 100);

	// Sommes-nous dans l'empire du Mal ?
	// (ou sous le signe du Pingouin, ascendant GNU ?)
	if (strpos($_SERVER['SERVER_SOFTWARE'], '(Win') !== false){
		if (!defined('_OS_SERVEUR')) define('_OS_SERVEUR', 'windows');
		if (!defined('_SPIP_LOCK_MODE')) define('_SPIP_LOCK_MODE',1); // utiliser le flock php
	}
	else {
		if (!defined('_OS_SERVEUR')) define('_OS_SERVEUR', '');
		if (!defined('_SPIP_LOCK_MODE')) define('_SPIP_LOCK_MODE',1); // utiliser le flock php
		#if (!defined('_SPIP_LOCK_MODE')) define('_SPIP_LOCK_MODE',2); // utiliser le nfslock de spip mais link() est tres souvent interdite
	}

	// Langue par defaut
	if (!defined('_LANGUE_PAR_DEFAUT')) define('_LANGUE_PAR_DEFAUT','fr');

	// PHP_VERSION_ID dispo depuis PHP 5.2.7
	if (!defined('PHP_VERSION_ID')) {
	   $version = explode('.',PHP_VERSION);
	   define('PHP_VERSION_ID', ($version[0] * 10000 + $version[1] * 100 + $version[2]));
	}

	//
	// Module de lecture/ecriture/suppression de fichiers utilisant flock()
	// (non surchargeable en l'etat ; attention si on utilise include_spip()
	// pour le rendre surchargeable, on va provoquer un reecriture
	// systematique du noyau ou une baisse de perfs => a etudier)
	include_once _ROOT_RESTREINT . 'inc/flock.php';

	// charger tout de suite le path et son cache
	load_path_cache();

	// *********** traiter les variables ************

	//
	// Securite
	//

	// Ne pas se faire manger par un bug php qui accepte ?GLOBALS[truc]=toto
	if (isset($_REQUEST['GLOBALS'])) die();
	// nettoyer les magic quotes \' et les caracteres nuls %00
	spip_desinfecte($_GET);
	spip_desinfecte($_POST);
	spip_desinfecte($_COOKIE);
	spip_desinfecte($_REQUEST);

	// Par ailleurs on ne veut pas de magic_quotes au cours de l'execution
	if (PHP_VERSION_ID<50300) {
		@set_magic_quotes_runtime(0);
	}

	// Si les variables sont passees en global par le serveur,
	// il faut faire quelques verifications de base
	$avertir_register_globals = false;
	if (test_valeur_serveur(@ini_get('register_globals'))) {
		// ne pas desinfecter les globales en profondeur car elle contient aussi les
		// precedentes, qui seraient desinfectees 2 fois.
		spip_desinfecte($GLOBALS,false);
		if (include_spip('inc/php3'))
			spip_register_globals(true);

		$avertir_register_globals = true;
	}

	// appliquer le cookie_prefix
	if ($GLOBALS['cookie_prefix'] != 'spip') {
		include_spip('inc/cookie');
		recuperer_cookies_spip($GLOBALS['cookie_prefix']);
	}

	//
	// Capacites php (en fonction de la version)
	//
	$GLOBALS['flag_ob'] = (function_exists("ob_start")
		&& function_exists("ini_get")
		&& !strstr(@ini_get('disable_functions'), 'ob_'));
	$GLOBALS['flag_sapi_name'] = function_exists("php_sapi_name");
	$GLOBALS['flag_get_cfg_var'] = (@get_cfg_var('error_reporting') != "");
	$GLOBALS['flag_upload'] = (!$GLOBALS['flag_get_cfg_var'] ||
		(get_cfg_var('upload_max_filesize') > 0));


	// Compatibilite avec serveurs ne fournissant pas $REQUEST_URI
	if (isset($_SERVER['REQUEST_URI'])) {
		$GLOBALS['REQUEST_URI'] = $_SERVER['REQUEST_URI'];
	} else {
		$GLOBALS['REQUEST_URI'] = $_SERVER['PHP_SELF'];
		if ($_SERVER['QUERY_STRING']
		AND !strpos($_SERVER['REQUEST_URI'], '?'))
			$GLOBALS['REQUEST_URI'] .= '?'.$_SERVER['QUERY_STRING'];
	}

	// Duree de validite de l'alea pour les cookies et ce qui s'ensuit.
	if (!defined('_RENOUVELLE_ALEA')) define('_RENOUVELLE_ALEA', 12 * 3600);

	// charger les meta si possible et renouveller l'alea au besoin
	// charge aussi effacer_meta et ecrire_meta
	$inc_meta = charger_fonction('meta', 'inc');
	$inc_meta();

	// on a pas pu le faire plus tot
	if  ($avertir_register_globals)
		avertir_auteurs("register_globals",_L("Probl&egrave;me de s&eacute;curit&eacute; : register_globals=on; dans php.ini &agrave; corriger."));

	// nombre de repertoires depuis la racine
	// on compare a l'adresse de spip.php : $_SERVER["SCRIPT_NAME"]
	// ou a defaut celle donnee en meta ; (mais si celle-ci est fausse
	// le calcul est faux)
	if (!_DIR_RESTREINT)
		$GLOBALS['profondeur_url'] = 1;
	else {
		$uri = isset($_SERVER['REQUEST_URI']) ? explode('?', $_SERVER['REQUEST_URI']) : '';
		$uri_ref = $_SERVER["SCRIPT_NAME"];
		if (!$uri_ref
			// si on est appele avec un autre ti, on est sans doute en mutu
			// si jamais c'est de la mutu avec sous rep, on est perdu si on se fie
			// a spip.php qui est a la racine du spip, et vue qu'on sait pas se reperer
			// s'en remettre a l'adresse du site. alea jacta est.
			OR $ti!==_NOM_TEMPORAIRES_INACCESSIBLES){

			if (isset($GLOBALS['meta']['adresse_site'])) {
				$uri_ref = parse_url($GLOBALS['meta']['adresse_site']);
				$uri_ref = $uri_ref['path'].'/';
			}
		  else
			  $uri_ref = "";
		}
		if (!$uri OR !$uri_ref)
			$GLOBALS['profondeur_url'] = 0;
		else {
			$GLOBALS['profondeur_url'] = max(0,
				substr_count($uri[0], '/')
				- substr_count($uri_ref,'/'));
		}
	}
	// s'il y a un cookie ou PHP_AUTH, initialiser visiteur_session
	if (_FILE_CONNECT) {
		if (verifier_visiteur()=='0minirezo'
			// si c'est un admin sans cookie admin, il faut ignorer le cache chemin !
		  AND !isset($_COOKIE['spip_admin']))
			clear_path_cache();
	}

}

/**
 * Complements d'initialisation non critiques pouvant etre realises
 * par les plugins
 *
 */
function spip_initialisation_suite() {
	static $too_late = 0;
	if ($too_late++) return;

	// taille mini des login
	if (!defined('_LOGIN_TROP_COURT')) define('_LOGIN_TROP_COURT', 4);

	// la taille maxi des logos (0 : pas de limite)
	if (!defined('_LOGO_MAX_SIZE')) define('_LOGO_MAX_SIZE', 0); # poids en ko
	if (!defined('_LOGO_MAX_WIDTH')) define('_LOGO_MAX_WIDTH', 0); # largeur en pixels
	if (!defined('_LOGO_MAX_HEIGHT')) define('_LOGO_MAX_HEIGHT', 0); # hauteur en pixels

	if (!defined('_DOC_MAX_SIZE')) define('_DOC_MAX_SIZE', 0); # poids en ko

	if (!defined('_IMG_MAX_SIZE')) define('_IMG_MAX_SIZE', 0); # poids en ko
	if (!defined('_IMG_MAX_WIDTH')) define('_IMG_MAX_WIDTH', 0); # largeur en pixels
	if (!defined('_IMG_MAX_HEIGHT')) define('_IMG_MAX_HEIGHT', 0); # hauteur en pixels
	if (!defined('_PASS_LONGUEUR_MINI')) define('_PASS_LONGUEUR_MINI',6);


	// Qualite des images calculees automatiquement. C'est un nombre entre 0 et 100, meme pour imagick (on ramene a 0..1 par la suite)
        if (!defined('_IMG_QUALITE')) define('_IMG_QUALITE', 85); # valeur par defaut
        if (!defined('_IMG_GD_QUALITE')) define('_IMG_GD_QUALITE', _IMG_QUALITE); # surcharge pour la lib GD
        if (!defined('_IMG_CONVERT_QUALITE')) define('_IMG_CONVERT_QUALITE', _IMG_QUALITE); # surcharge pour imagick en ligne de commande
	// Historiquement la valeur pour imagick semble differente. Si ca n'est pas necessaire, il serait preferable de garder _IMG_QUALITE
        if (!defined('_IMG_IMAGICK_QUALITE')) define('_IMG_IMAGICK_QUALITE', 75); # surcharge pour imagick en PHP

	if (!defined('_COPIE_LOCALE_MAX_SIZE')) define('_COPIE_LOCALE_MAX_SIZE',16777216); // poids en octet

	// qq chaines standard
	if (!defined('_ACCESS_FILE_NAME')) define('_ACCESS_FILE_NAME', '.htaccess');
	if (!defined('_AUTH_USER_FILE')) define('_AUTH_USER_FILE', '.htpasswd');
	if (!defined('_SPIP_DUMP')) define('_SPIP_DUMP', 'dump@nom_site@@stamp@.xml');
	if (!defined('_CACHE_RUBRIQUES')) define('_CACHE_RUBRIQUES', _DIR_TMP.'menu-rubriques-cache.txt');
	if (!defined('_CACHE_RUBRIQUES_MAX')) define('_CACHE_RUBRIQUES_MAX', 500);

	if (!defined('_EXTENSION_SQUELETTES')) define('_EXTENSION_SQUELETTES', 'html');

	if (!defined('_DOCTYPE_ECRIRE')) define('_DOCTYPE_ECRIRE',
		// "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN' 'http://www.w3.org/TR/html4/loose.dtd'>\n");
		//"<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\n");
		//"<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'>\n");
	       // "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.1 //EN' 'http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd'>\n");
		"<!DOCTYPE html>\n");
	if (!defined('_DOCTYPE_AIDE')) define('_DOCTYPE_AIDE',
	       "<!DOCTYPE html PUBLIC '-//W3C//DTD HTML 4.01 Frameset//EN' 'http://www.w3.org/TR/1999/REC-html401-19991224/frameset.dtd'>");

	// L'adresse de base du site ; on peut mettre '' si la racine est geree par
	// le script de l'espace public, alias  index.php
	if (!defined('_SPIP_SCRIPT')) define('_SPIP_SCRIPT', 'spip.php');
	// argument page, personalisable en cas de conflit avec un autre script
	if (!defined('_SPIP_PAGE')) define('_SPIP_PAGE', 'page');

	// le script de l'espace prive
	// Mettre a "index.php" si DirectoryIndex ne le fait pas ou pb connexes:
	// les anciens IIS n'acceptent pas les POST sur ecrire/ (#419)
	// meme pb sur thttpd cf. http://forum.spip.org/fr_184153.html

	if (!defined('_SPIP_ECRIRE_SCRIPT')) define('_SPIP_ECRIRE_SCRIPT', // true ? #decommenter ici et commenter la
	       preg_match(',IIS|thttpd,',$_SERVER['SERVER_SOFTWARE']) ?
	       'index.php' : '');


	if (!defined('_SPIP_AJAX'))
		define('_SPIP_AJAX', ((!isset($_COOKIE['spip_accepte_ajax']))
			? 1
		       : (($_COOKIE['spip_accepte_ajax'] != -1) ? 1 : 0)));

	// La requete est-elle en ajax ?
	if (!defined('_AJAX')) define('_AJAX',
		(isset($_SERVER['HTTP_X_REQUESTED_WITH']) # ajax jQuery
		OR @$_REQUEST['var_ajax_redir'] # redirection 302 apres ajax jQuery
		OR @$_REQUEST['var_ajaxcharset'] # compat ascendante pour plugins
		OR @$_REQUEST['var_ajax'] # forms ajax & inclure ajax de spip
		)
		AND !@$_REQUEST['var_noajax'] # horrible exception, car c'est pas parce que la requete est ajax jquery qu'il faut tuer tous les formulaires ajax qu'elle contient
	);

	# nombre de pixels maxi pour calcul de la vignette avec gd
	# au dela de 5500000 on considere que php n'est pas limite en memoire pour cette operation
	# les configurations limitees en memoire ont un seuil plutot vers 1MPixel
	if (!defined('_IMG_GD_MAX_PIXELS')) define('_IMG_GD_MAX_PIXELS',
		(isset($GLOBALS['meta']['max_taille_vignettes'])&&$GLOBALS['meta']['max_taille_vignettes']<5500000)
		 ? $GLOBALS['meta']['max_taille_vignettes']
		 : 0);

	if (!defined('_MEMORY_LIMIT_MIN')) define('_MEMORY_LIMIT_MIN',10); // en Mo
	// si on est dans l'espace prive et si le besoin est superieur a 8Mo (qui est vraiment le standard)
	// on verifie que la memoire est suffisante pour le compactage css+js pour eviter la page blanche
	// il y aura d'autres problemes et l'utilisateur n'ira pas tres loin, mais ce sera plus comprehensible qu'une page blanche
	if (test_espace_prive() AND _MEMORY_LIMIT_MIN>8){
		if ($memory = trim(ini_get('memory_limit')) and $memory != -1) {
			$unit = strtolower(substr($memory,strlen($memory/1),1));
			switch($unit) {
				// Le modifieur 'G' est disponible depuis PHP 5.1.0
				case 'g': $memory *= 1024;
				case 'm': $memory *= 1024;
				case 'k': $memory *= 1024;
			}
			if ($memory<_MEMORY_LIMIT_MIN*1024*1024){
				ini_set('memory_limit',$m=_MEMORY_LIMIT_MIN.'M');
				if (trim(ini_get('memory_limit'))!=$m){
					if (!defined('_INTERDIRE_COMPACTE_HEAD_ECRIRE')) define('_INTERDIRE_COMPACTE_HEAD_ECRIRE',true); // evite une page blanche car on ne saura pas calculer la css dans ce hit
				}
			}
		}
		else
			if (!defined('_INTERDIRE_COMPACTE_HEAD_ECRIRE')) define('_INTERDIRE_COMPACTE_HEAD_ECRIRE',true); // evite une page blanche car on ne saura pas calculer la css dans ce hit
	}
	// Protocoles a normaliser dans les chaines de langues
	if (!defined('_PROTOCOLES_STD'))
		define('_PROTOCOLES_STD', 'http|https|ftp|mailto|webcal');

	init_var_mode();
}

// Reperer les variables d'URL qui conditionnent la perennite du cache, des urls
// ou d'autres petit caches (trouver_table, css et js compactes ...)
// http://doc.spip.org/@init_var_mode
function init_var_mode(){
	static $done = false;
	if (!$done) {

		if (isset($_GET['var_mode'])) {
			// tout le monde peut calcul/recalcul
			if ($_GET['var_mode'] == 'calcul'
			OR $_GET['var_mode'] == 'recalcul') {
				if (!defined('_VAR_MODE')) define('_VAR_MODE',$_GET['var_mode']);
			}
			// preview, debug, blocs, urls et images necessitent une autorisation
			else if (in_array($_GET['var_mode'],array('preview','debug','inclure','urls','images','traduction'))) {
				include_spip('inc/autoriser');
				if (autoriser(
					($_GET['var_mode'] == 'preview')
						? 'previsualiser'
						: 'debug'
				)) {
					switch($_GET['var_mode']){
						case 'traduction':
							// forcer le calcul pour passer dans traduire
							if (!defined('_VAR_MODE')) define('_VAR_MODE','calcul');
							// et ne pas enregistrer de cache pour ne pas trainer les surlignages sur d'autres pages
							if (!defined('_VAR_NOCACHE')) define('_VAR_NOCACHE',true);
							break;
						case 'preview':
							// basculer sur les criteres de preview dans les boucles
							if (!defined('_VAR_PREVIEW')) define('_VAR_PREVIEW',true);
							// forcer le calcul
							if (!defined('_VAR_MODE')) define('_VAR_MODE','calcul');
							// et ne pas enregistrer de cache
							if (!defined('_VAR_NOCACHE')) define('_VAR_NOCACHE',true);
							break;
						case 'inclure':
							// forcer le compilo et ignorer les caches existants
							if (!defined('_VAR_MODE')) define('_VAR_MODE','calcul');
							if (!defined('_VAR_INCLURE')) define('_VAR_INCLURE',true);
							// et ne pas enregistrer de cache
							if (!defined('_VAR_NOCACHE')) define('_VAR_NOCACHE',true);
							break;
						case 'urls':
							// forcer le compilo et ignorer les caches existants
							if (!defined('_VAR_MODE')) define('_VAR_MODE','calcul');
							if (!defined('_VAR_URLS')) define('_VAR_URLS',true);
							break;
						case 'images':
							// forcer le compilo et ignorer les caches existants
							if (!defined('_VAR_MODE')) define('_VAR_MODE','calcul');
							// indiquer qu'on doit recalculer les images
							if (!defined('_VAR_IMAGES')) define('_VAR_IMAGES',true);
							break;
						case 'debug':
							if (!defined('_VAR_MODE')) define('_VAR_MODE','debug');
							// et ne pas enregistrer de cache
							if (!defined('_VAR_NOCACHE')) define('_VAR_NOCACHE',true);
							break;
						default :
							if (!defined('_VAR_MODE')) define('_VAR_MODE',$_GET['var_mode']);
							break;
					}
          if (isset($GLOBALS['visiteur_session']['nom']))
					spip_log($GLOBALS['visiteur_session']['nom']
						. " "._VAR_MODE);
				}
				// pas autorise ?
				else {
					// si on n'est pas connecte on se redirige
					if (!$GLOBALS['visiteur_session']) {
						include_spip('inc/headers');
						redirige_par_entete(generer_url_public('login',
						'url='.rawurlencode(
						parametre_url(self(), 'var_mode', $_GET['var_mode'], '&')
						), true));
					}
					// sinon tant pis
				}
			}
			if (!defined('_VAR_MODE')) define('_VAR_MODE',false);
		}
		$done = true;
	}
}

// Annuler les magic quotes \' sur GET POST COOKIE et GLOBALS ;
// supprimer aussi les eventuels caracteres nuls %00, qui peuvent tromper
// la commande is_readable('chemin/vers/fichier/interdit%00truc_normal')
// http://doc.spip.org/@spip_desinfecte
function spip_desinfecte(&$t,$deep = true) {
	static $magic_quotes;
	if (!isset($magic_quotes))
		$magic_quotes = @get_magic_quotes_gpc();

	foreach ($t as $key => $val) {
		if (is_string($t[$key])) {
			if ($magic_quotes)
				$t[$key] = stripslashes($t[$key]);
			$t[$key] = str_replace(chr(0), '-', $t[$key]);
		}
		// traiter aussi les "texte_plus" de article_edit
		else if ($deep AND is_array($t[$key]) AND $key!=='GLOBALS')
			spip_desinfecte($t[$key],$deep);
	}
}

//  retourne le statut du visiteur s'il s'annonce

// http://doc.spip.org/@verifier_visiteur
function verifier_visiteur() {
	// Rq: pour que cette fonction marche depuis mes_options
	// il faut forcer l'init si ce n'est fait
	// mais on risque de perturber des plugins en initialisant trop tot
	// certaines constantes
	@spip_initialisation_core(
		(_DIR_RACINE  . _NOM_PERMANENTS_INACCESSIBLES),
		(_DIR_RACINE  . _NOM_PERMANENTS_ACCESSIBLES),
		(_DIR_RACINE  . _NOM_TEMPORAIRES_INACCESSIBLES),
		(_DIR_RACINE  . _NOM_TEMPORAIRES_ACCESSIBLES)
	);

	// Demarrer une session NON AUTHENTIFIEE si on donne son nom
	// dans un formulaire sans login (ex: #FORMULAIRE_FORUM)
	// Attention on separe bien session_nom et nom, pour eviter
	// les melanges entre donnees SQL et variables plus aleatoires
	$variables_session = array('session_nom', 'session_email');
	foreach($variables_session as $var) {
		if (_request($var) !== null) {
			$init = true;
			break;
		}
	}
	if (isset($init)) {
		#@spip_initialisation_suite();
		$session = charger_fonction('session', 'inc');
		$session();
		include_spip('inc/texte');
		foreach($variables_session as $var)
			if (($a = _request($var)) !== null)
				$GLOBALS['visiteur_session'][$var] = safehtml($a);
		if (!isset($GLOBALS['visiteur_session']['id_auteur']))
			$GLOBALS['visiteur_session']['id_auteur'] = 0;
		$session($GLOBALS['visiteur_session']);
		return 0;
	}

	$h = (isset($_SERVER['PHP_AUTH_USER'])  AND !$GLOBALS['ignore_auth_http']);
	if ($h OR isset($_COOKIE['spip_session']) OR isset($_COOKIE[$GLOBALS['cookie_prefix'].'_session'])) {

		$session = charger_fonction('session', 'inc');
		if ($session()) {
			return $GLOBALS['visiteur_session']['statut'];
		}
		if ($h  AND isset($_SERVER['PHP_AUTH_PW'])) {
			include_spip('inc/auth');
			$h = lire_php_auth($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
		}
		if ($h) {
			$GLOBALS['visiteur_session'] = $h;
			return $GLOBALS['visiteur_session']['statut'];
		}
	}

	// au moins son navigateur nous dit la langue preferee de cet inconnu
	include_spip('inc/lang');
	utiliser_langue_visiteur();

	return false;
}

// selectionne la langue donnee en argument et memorise la courante
// ou restaure l'ancienne si appel sans argument
// On pourrait economiser l'empilement en cas de non changemnt
// et lui faire retourner False pour prevenir l'appelant
// Le noyau de Spip sait le faire, mais pour assurer la compatibilite
// cette fonction retourne toujours non False

// http://doc.spip.org/@lang_select
function lang_select ($lang=NULL) {
	static $pile_langues = array();
	if (!function_exists('changer_langue'))
		include_spip('inc/lang');
	if ($lang === NULL)
		$lang = array_pop($pile_langues);
	else {
		array_push($pile_langues, $GLOBALS['spip_lang']);
	}
	if (isset($GLOBALS['spip_lang']) AND $lang == $GLOBALS['spip_lang'])
		return $lang;
	changer_langue($lang);
	return $lang;
}


// Renvoie une chaine qui decrit la session courante pour savoir si on peut
// utiliser un cache enregistre pour cette session.
// Par convention cette chaine ne doit pas contenir de caracteres [^0-9A-Za-z]
// Attention on ne peut *pas* inferer id_auteur a partir de la session, qui
// est une chaine arbitraire
// Cette chaine est courte (8 cars) pour pouvoir etre utilisee dans un nom
// de fichier cache
// http://doc.spip.org/@spip_session
function spip_session($force = false) {
	static $session;
	if ($force OR !isset($session)) {
		$s = pipeline('definir_session',
			$GLOBALS['visiteur_session']
			? serialize($GLOBALS['visiteur_session'])
				. '_' . @$_COOKIE['spip_session']
			: ''
		);
		$session = $s ? substr(md5($s), 0, 8) : '';
	}
	#spip_log('session: '.$session);
	return $session;
}


/**
 * Aide, aussi depuis l'espace prive a present.
 * Surchargeable mais pas d'erreur fatale si indisponible.
 * 
 * @param string $aide
 * 		Cle d'identification de l'aide desiree
 * @param bool $distante
 * 		Generer une url locale (par defaut)
 * 		ou une url distante [directement sur spip.net]
 * @return Lien sur une icone d'aide
**/
// http://doc.spip.org/@aide
function aide($aide='', $distante = false) {
		$aider = charger_fonction('aider', 'inc', true);
	return $aider ?  $aider($aide, '', array(), $distante) : '';
}

// normalement il faudrait creer exec/info.php, mais pour mettre juste ca:
// http://doc.spip.org/@exec_info_dist
function exec_info_dist() {
	global $connect_statut;
	if ($connect_statut == '0minirezo')
		phpinfo();
	else
		echo "pas admin";
}

/**
 * Génère une erreur de squelette
 *
 * Génère une erreur de squelette qui sera bien visible par un
 * administrateur authentifié lors d'une visite de la page en erreur 
 *
 * @param bool|string|array $message
 * 		- Message d'erreur (string|array)
 * 		- false pour retourner le texte des messages d'erreurs
 * 		- vide pour afficher les messages d'erreurs
 * @param string|array|object $lieu
 * 		Lieu d'origine de l'erreur
 * @return null|string
 * 		Rien dans la plupart des cas
 * 		- string si $message à false.
**/
function erreur_squelette($message='', $lieu='') {
	$debusquer = charger_fonction('debusquer', 'public');
	if (is_array($lieu)) {
		include_spip('public/compiler');
		$lieu = reconstruire_contexte_compil($lieu);
	}
	return $debusquer($message, $lieu);
}

/**
 * Calcule un squelette avec un contexte et retourne son contenu
 * 
 * La fonction de base de SPIP : un squelette + un contexte => une page.
 * $fond peut etre un nom de squelette, ou une liste de squelette au format array.
 * Dans ce dernier cas, les squelettes sont tous evalues et mis bout a bout
 * $options permet de selectionner les options suivantes :
 * 	trim => true (valeur par defaut) permet de ne rien renvoyer si le fond ne produit que des espaces ;
 * 	raw  => true permet de recuperer la strucure $page complete avec entetes et invalideurs
 *          pour chaque $fond fourni.
 *
 * @api
 * @param string/array $fond
 * 		Le ou les squelettes à utiliser, sans l'extension, {@example prive/liste/auteurs}
 * 		Le fichier sera retrouvé dans la liste des chemins connus de SPIP (squelettes, plugins, spip)
 * @param array $contexte
 * 		Informations de contexte envoyées au squelette, {@example array('id_rubrique' => 8)}
 * 		La langue est transmise automatiquement (sauf option étoile).
 * @param array $options
 * 		Options complémentaires :
 * 		- trim   : applique un trim sur le résultat (true par défaut)
 * 		- raw    : retourne un tableau d'information sur le squelette (false par défaut)
 * 		- etoile : ne pas transmettre la langue au contexte automatiquement (false par défaut),
 * 		           équivalent de INCLURE*
 * 		- ajax   : gere les liens internes du squelette en ajax (équivalent du paramètre {ajax})
 * @param string $connect
 * 		Non du connecteur de bdd a utiliser
 * @return string|array
 * 		Contenu du squelette calculé
 * 		ou tableau d'information sur le squelette.
 */
function recuperer_fond($fond, $contexte=array(), $options = array(), $connect='') {
	if (!function_exists('evaluer_fond'))
		include_spip('public/assembler');
	// assurer la compat avec l'ancienne syntaxe
	// (trim etait le 3eme argument, par defaut a true)
	if (!is_array($options)) $options = array('trim'=>$options);
	if (!isset($options['trim'])) $options['trim']=true;

	if (isset($contexte['connect'])){
		$connect = ($connect ? $connect : $contexte['connect']);
		unset($contexte['connect']);
	}

	$texte = "";
	$pages = array();
	$lang_select = '';
	if (!isset($options['etoile']) OR !$options['etoile']){
		// Si on a inclus sans fixer le critere de lang, on prend la langue courante
		if (!isset($contexte['lang']))
			$contexte['lang'] = $GLOBALS['spip_lang'];

		if ($contexte['lang'] != $GLOBALS['meta']['langue_site']) {
			$lang_select = lang_select($contexte['lang']);
		}
	}

	@$GLOBALS['_INC_PUBLIC']++;

	foreach(is_array($fond) ? $fond : array($fond) as $f){
		$page = evaluer_fond($f, $contexte, $connect);
		if ($page === '') {
			$c = isset($options['compil']) ? $options['compil'] :'';
			$a = array('fichier'=>$fond);
			$erreur = _T('info_erreur_squelette2', $a); // squelette introuvable
			erreur_squelette($erreur, $c);
			// eviter des erreurs strictes ensuite sur $page['cle'] en PHP >= 5.4
			$page = array('texte' => '', 'erreur' => $erreur);
		}

		$page = pipeline('recuperer_fond',array(
			'args'=>array('fond'=>$f,'contexte'=>$contexte,'options'=>$options,'connect'=>$connect),
			'data'=>$page
		));
		if (isset($options['ajax']) AND $options['ajax']){
			if (!function_exists('encoder_contexte_ajax'))
				include_spip('inc/filtres');
			$page['texte'] = encoder_contexte_ajax(array_merge($contexte,array('fond'=>$f)),'',$page['texte'], $options['ajax']);
		}

		if (isset($options['raw']) AND $options['raw'])
			$pages[] = $page;
		else
			$texte .= $options['trim'] ? rtrim($page['texte']) : $page['texte'];
	}

	$GLOBALS['_INC_PUBLIC']--;

	if ($lang_select) lang_select();
	if (isset($options['raw']) AND $options['raw'])
		return is_array($fond)?$pages:reset($pages);
	else
		return $options['trim'] ? ltrim($texte) : $texte;
}

/**
 * Trouve un squelette dans le repertoire modeles/
 *
 * @param  $nom
 * @return string
 */
function trouve_modele($nom) {
	return trouver_fond($nom,'modeles/');
}

/**
 * Trouver un squelette dans le chemin
 * on peut specifier un sous-dossier dans $dir
 * si $pathinfo est a true, retourne un tableau avec
 * les composantes du fichier trouve
 * + le chemin complet sans son extension dans fond
 *
 * @param string $nom
 * @param string $dir
 * @param bool $pathinfo
 * @return array|string
 */
function trouver_fond($nom, $dir='', $pathinfo = false) {
	$f = find_in_path($nom.'.'. _EXTENSION_SQUELETTES, $dir?rtrim($dir,'/').'/':'');
	if (!$pathinfo) return $f;
	// renvoyer un tableau detaille si $pathinfo==true
	$p = pathinfo($f);
	if (!isset($p['extension']) OR !$p['extension']) {
		$p['extension'] = _EXTENSION_SQUELETTES;
	}
	if (!isset($p['extension']) OR !$p['filename']) {
		$p['filename'] = ($p['basename']?substr($p['basename'],0,-strlen($p['extension'])-1):'');
	}
	$p['fond'] = ($f?substr($f,0,-strlen($p['extension'])-1):'');
	return $p;
}

function tester_url_ecrire($nom){
	static $exec=array();
	if (isset($exec[$nom])) return $exec[$nom];
	// tester si c'est une page en squelette
	if (trouver_fond($nom, 'prive/squelettes/contenu/'))
		return $exec[$nom] = 'fond';
	// compat skels orthogonaux version precedente
	elseif (trouver_fond($nom, 'prive/exec/'))
		return $exec[$nom] = 'fond_monobloc';
	// echafaudage d'un fond !
	elseif(include_spip('public/styliser_par_z') AND z_echafaudable($nom))
		return $exec[$nom] = 'fond';
	// attention, il ne faut pas inclure l'exec ici
	// car sinon #URL_ECRIRE provoque des inclusions
	// et des define intrusifs potentiels
	return $exec[$nom] = ((find_in_path("{$nom}.php",'exec/') OR charger_fonction($nom,'exec',true))?$nom:'');
}

// Charger dynamiquement une extension php
// http://doc.spip.org/@charger_php_extension
function charger_php_extension($module) {
	if (extension_loaded($module)) {
		return true;
	} else {
		$charger_php_extension = charger_fonction('charger_php_extension','inc');
		return $charger_php_extension($module);
	}
}

// Renvoie TRUE si et seulement si la configuration autorise
// le code HTML5 sur le site public
function html5_permis() {
        return (isset($GLOBALS['meta']['version_html_max'])
		AND ('html5' == $GLOBALS['meta']['version_html_max']));
}

/*
 * Bloc de compatibilite : quasiment tous les plugins utilisent ces fonctions
 * desormais depreciees ; plutot que d'obliger tout le monde a charger
 * vieilles_defs, on va assumer l'histoire de ces 3 fonctions ubiquitaires
 */
// Fonction depreciee
// http://doc.spip.org/@lire_meta
function lire_meta($nom) {
	return $GLOBALS['meta'][$nom];
}

// Fonction depreciee
// http://doc.spip.org/@ecrire_metas
function ecrire_metas() {}

// Fonction depreciee, cf. http://doc.spip.org/@sql_fetch
// http://doc.spip.org/@spip_fetch_array
function spip_fetch_array($r, $t=NULL) {
	if (!isset($t)) {
		if ($r) return sql_fetch($r);
	} else {
		if ($t=='SPIP_NUM') $t = MYSQL_NUM;
		if ($t=='SPIP_BOTH') $t = MYSQL_BOTH;
		if ($t=='SPIP_ASSOC') $t = MYSQL_ASSOC;
		spip_log("appel deprecie de spip_fetch_array(..., $t)", 'vieilles_defs');
		if ($r) return mysql_fetch_array($r, $t);
	}
}

/**
 * Poser une alerte qui sera affiche aux auteurs de bon statut ('' = tous)
 * au prochain passage dans l'espace prive
 * chaque alerte doit avoir un nom pour eviter duplication a chaque hit
 * les alertes affichees une fois sont effacees
 *
 * @param string $nom
 * @param string $message
 * @param string $statut
 */
function avertir_auteurs($nom,$message, $statut=''){
	$alertes = $GLOBALS['meta']['message_alertes_auteurs'];
	if (!$alertes
		OR !is_array($alertes = unserialize($alertes)))
		$alertes = array();

	if (!isset($alertes[$statut]))
		$alertes[$statut] = array();
	$alertes[$statut][$nom] = $message;
	ecrire_meta("message_alertes_auteurs",serialize($alertes));
}
?>
drapeau_edition.php000066600000012323152525475610010432 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

// Drapeau d'edition : on regarde qui a ouvert quel objet editorial en 
// edition, et on le signale aux autres redacteurs pour eviter de se marcher 
// sur les pieds

// Le format est une meta drapeau_edition qui contient un tableau
// serialise
// type_objet => (id_objet => (id_auteur => (nom_auteur => (date_modif))))

// a chaque mise a jour de ce tableau on oublie les enregistrements datant
// de plus d'une heure

// Attention ce n'est pas un verrou "bloquant", juste un drapeau qui signale
// que l'on bosse sur cet objet editorial ; les autres peuvent passer outre
// (en cas de communication orale c'est plus pratique)


// http://doc.spip.org/@lire_tableau_edition
function lire_tableau_edition () {
	$edition = @unserialize($GLOBALS['meta']['drapeau_edition']);
	if (!$edition) return array();
	$changed = false;

	$bon_pour_le_service = time()-3600;
	// parcourir le tableau et virer les vieux
	foreach ($edition as $objet => $data) {
		if (!is_array($data))
			unset ($edition[$objet]); // vieille version
		else foreach ($data as $id => $tab) {
			if (!is_array($tab))
			  unset ($edition[$objet][$tab]); // vieille version
			else foreach ($tab as $n => $duo) {
				if (current($duo) < $bon_pour_le_service) {
					unset($edition[$objet][$id][$n]);
					$changed = true;
				}
			}
			if (!$edition[$objet][$id])
				unset($edition[$objet][$id]);
		}
		if (!$edition[$objet])
			unset($edition[$objet]);
	}

	if ($changed)
		ecrire_tableau_edition($edition);

	return $edition;
}

// http://doc.spip.org/@ecrire_tableau_edition
function ecrire_tableau_edition($edition) {
	ecrire_meta('drapeau_edition', serialize($edition));
}

/**
 * J'edite tel objet
 * si l'objet est non editable dans l'espace prive, ne pas retenir le signalement
 * qui correspond a un process unique
 *
 * http://doc.spip.org/@signale_edition
 *
 * @param int $id
 * @param $auteur
 * @param string $type
 * @return mixed
 */
function signale_edition ($id, $auteur, $type='article') {
	include_spip('base/objets');
	include_spip('inc/filtres');
	if (objet_info($type,'editable')!=='oui')
		return;

	$edition = lire_tableau_edition();
	if (isset($auteur['id_auteur']) and $id_a = $auteur['id_auteur'])
		$nom = $auteur['nom'];
	else
		$nom = $id_a = $GLOBALS['ip'];
	if (!is_array($edition[$type][$id]))
		$edition[$type][$id] = array();
	$edition[$type][$id][$id_a][$nom] = time();
	ecrire_tableau_edition($edition);
}

// Qui edite mon objet ?
// http://doc.spip.org/@qui_edite
function qui_edite ($id, $type='article') {

	$edition = lire_tableau_edition();

	return $edition ? $edition[$type][$id] : array();
}

// http://doc.spip.org/@mention_qui_edite
function mention_qui_edite ($id, $type='article') {
	$modif = qui_edite($id, $type);
	unset($modif[$GLOBALS['visiteur_session']['id_auteur']]);

	if ($modif) {
		$quand = 0;
		foreach ($modif as $duo) {
			$auteurs[] = typo(key($duo));
			$quand = max($quand, current($duo));
		}
		// format lie a la chaine de langue 'avis_article_modifie'
		return array(
			'nom_auteur_modif' => join(' | ', $auteurs),
			'date_diff' => ceil((time()-$quand) / 60)
		);
	}
}

/**
 * Quels sont les objets en cours d'edition par X ?
 *
 * http://doc.spip.org/@liste_drapeau_edition
 *
 * @param $id_auteur
 * @return array
 */
function liste_drapeau_edition ($id_auteur) {
	$edition = lire_tableau_edition();
	$objets_ouverts = array();

	foreach ($edition as $objet => $data)
		foreach ($data as $id => $auteurs)
		{
			if (isset($auteurs[$id_auteur])
			AND is_array($auteurs[$id_auteur]) // precaution
			AND (array_pop($auteurs[$id_auteur]) > time()-3600)) {
				$objets_ouverts[] = array(
					'objet'=>$objet,
					'id_objet' => $id,
				);
			}
		}
	return $objets_ouverts;
}

// Quand l'auteur veut liberer tous ses objets (tous types)
// http://doc.spip.org/@debloquer_tous
function debloquer_tous($id_auteur) {
	$edition = lire_tableau_edition();
	foreach ($edition as $objet => $data) {
		foreach ($data as $id => $auteurs)
		{
			if (isset($auteurs[$id_auteur])) {
				unset ($edition[$objet][$id][$id_auteur]);
				ecrire_tableau_edition($edition);
			}
		}
	}
}

// quand l'auteur libere un objet precis
// http://doc.spip.org/@debloquer_edition
function debloquer_edition($id_auteur, $id_objet, $type='article') {
	$edition = lire_tableau_edition();

	foreach ($edition as $objet => $data){
		if ($objet == $type) {
			foreach ($data as $id => $auteurs)
			{
				if ($id == $id_objet
				AND isset($auteurs[$id_auteur])) {
					unset ($edition[$objet][$id][$id_auteur]);
					ecrire_tableau_edition($edition);
				}
			}
		}
	}
}
?>
precharger_article.php000066600000003232152525475610011122 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

include_spip('inc/precharger_objet');


// Recupere les donnees d'un article pour composer un formulaire d'edition
// id_article = numero d'article existant
// id_rubrique = ou veut-on l'installer (pas obligatoire)
// lier_trad = l'associer a l'article numero $lier_trad
// new=oui = article a creer si on valide le formulaire
// http://doc.spip.org/@inc_article_select_dist
function inc_precharger_article_dist($id_article, $id_rubrique=0, $lier_trad=0) {
	return precharger_objet('article', $id_article, $id_rubrique, $lier_trad, 'titre');
}


//
// Si un article est demande en creation (new=oui) avec un lien de trad,
// on initialise les donnees de maniere specifique
//
// (fonction facultative si pas de changement dans les traitements)
function inc_precharger_traduction_article_dist($id_article, $id_rubrique=0, $lier_trad=0) {
	return precharger_traduction_objet('article', $id_article, $id_rubrique, $lier_trad, 'titre');
}



?>
surligne.php000066600000007460152525475610007134 0ustar00<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

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

// Ces commentaires vont etre substitue's en mode recherche
// voir balise_DEBUT_SURLIGNE et balise_FIN_SURLIGNE

define('MARQUEUR_SURLIGNE', 'debut_surligneconditionnel');
define('MARQUEUR_FSURLIGNE', 'finde_surligneconditionnel');


// http://doc.spip.org/@surligner_mots
function surligner_mots($page, $surcharge_surligne = '') {
	$surlignejs_engines = array(
		array(",".str_replace(array("/", "."), array("\/", "\."), $GLOBALS['meta']['adresse_site']).",i", ",recherche=([^&]+),i"), //SPIP
		array(",^http://(www\.)?google\.,i", ",q=([^&]+),i"), // Google
		array(",^http://(www\.)?search\.yahoo\.,i", ",p=([^&]+),i"), // Yahoo
		array(",^http://(www\.)?search\.msn\.,i", ",q=([^&]+),i"), // MSN
		array(",^http://(www\.)?search\.live\.,i", ",query=([^&]+),i"), // MSN Live
		array(",^http://(www\.)?search\.aol\.,i", ",userQuery=([^&]+),i"), // AOL
		array(",^http://(www\.)?ask\.com,i", ",q=([^&]+),i"), // Ask.com
		array(",^http://(www\.)?altavista\.,i", ",q=([^&]+),i"), // AltaVista
		array(",^http://(www\.)?feedster\.,i", ",q=([^&]+),i"), // Feedster
		array(",^http://(www\.)?search\.lycos\.,i", ",q=([^&]+),i"), // Lycos
		array(",^http://(www\.)?alltheweb\.,i", ",q=([^&]+),i"), // AllTheWeb
		array(",^http://(www\.)?technorati\.com,i", ",([^\?\/]+)(?:\?.*)$,i"), // Technorati
	);


	$ref = $_SERVER['HTTP_REFERER'];
	//avoid a js injection
	if ($surcharge_surligne){
		$surcharge_surligne = preg_replace(",(?<!\\\\)((?:(?>\\\\){2})*)('),", "$1\\\\$2", $surcharge_surligne);
		$surcharge_surligne = str_replace("\\", "\\\\", $surcharge_surligne);
		if ($GLOBALS['meta']['charset']=='utf-8'){
			include_spip('inc/charsets');
			if (!is_utf8($surcharge_surligne)) $surcharge_surligne = utf8_encode($surcharge_surligne);
		}
		$surcharge_surligne = preg_replace(',\*$,', '', trim($surcharge_surligne)); # supprimer un * final
	}
	foreach ($surlignejs_engines as $engine)
		if ($surcharge_surligne || (preg_match($engine[0], $ref) && preg_match($engine[1], $ref))){

			//good referrer found or var_recherche is not null
			include_spip('inc/filtres');
			$script = "
      <script type='text/javascript' src='".url_absolue(find_in_path('javascript/SearchHighlight.js'))."'></script>
      <script type='text/javascript'>/*<![CDATA[*/
      if (window.jQuery)
        (function(\$){\$(function(){
          \$(document).SearchHighlight({
            tag_name:'".(html5_permis() ? 'mark' : 'span')."',
            style_name:'spip_surligne',
            exact:'whole',
            style_name_suffix:false,
            engines:[/^".str_replace(array("/", "."), array("\/", "\."), $GLOBALS['meta']['adresse_site'])."/i,/recherche=([^&]+)/i],
            highlight:'.surlignable',
            nohighlight:'.pas_surlignable'".
			          ($surcharge_surligne ? ",
            keys:'$surcharge_surligne'" : "").",
            min_length: 3
          })
        });
      })(jQuery);
      /*]]>*/</script>
      ";
			// on l'insere juste avant </head>, sinon tout en bas
			if (is_null($l = strpos($page, '</head>')))
				$l = strlen($page);
			$page = substr_replace($page, $script, $l, 0);
			break;
		}
	return $page;
}

?>
filtres_images.php000066600000002321152525606440010265 0ustar00<?php
/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/


/**
 * Ce fichier ne sert plus
 * Il est maintenu pour assurer la compatibilite des anciens scripts
 *  
 */

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

// prise en charge des renomage de fonction
include_spip('inc/filtres_images_compat');

// prise en charge des fonctions de transformation d'image
include_spip('filtres/images_transforme');

// prise en charge des fonctions d'image typo
include_spip('filtres/images_typo');

// prise en charge des fonctions de transformation de couleur
include_spip('filtres/couleurs');

?>filtres_images_compat.php000066600000010273152525606440011635 0ustar00<?php
/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

/**
 * Ce fichier ne sert pas
 * Il est maintenu pour assurer la compatibilite des anciens scripts avec les anciens nommages de fonction
 *  
 */

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

function couleur_dec_to_hex($red, $green, $blue) {include_spip('filtres/images_lib');return _couleur_dec_to_hex($red, $green, $blue);}
function couleur_hex_to_dec($couleur) {include_spip('filtres/images_lib');return _couleur_hex_to_dec($couleur);}
function couleur_rgb2hsv($R,$G,$B) {include_spip('filtres/images_lib');return _couleur_rgb2hsv($R,$G,$B);}
function couleur_hsv2rgb($H,$S,$V) {include_spip('filtres/images_lib');return _couleur_hsv2rgb($H,$S,$V);}
function couleur_rgb2hsl($R,$G,$B) {include_spip('filtres/images_lib');return _couleur_rgb2hsl($R,$G,$B);}
function couleur_hsl2rgb($H,$S,$L) {include_spip('filtres/images_lib');return _couleur_hsl2rgb($H,$S,$L);}
function image_couleur_extraire($img, $x=10, $y=6) {include_spip('filtres/images_lib');return _image_couleur_extraire($img, $x, $y);}
function image_distance_pixel($xo, $yo, $x0, $y0) {include_spip('filtres/images_lib');return _image_distance_pixel($xo, $yo, $x0, $y0);}
function image_decal_couleur($coul, $gamma) {include_spip('filtres/images_lib');return _image_decale_composante($coul, $gamma);}
function image_decal_couleur_127($coul, $val) {include_spip('filtres/images_lib');return _image_decale_composante_127 ($coul, $val);}

function image_creer_vignette($valeurs, $maxWidth, $maxHeight, $process='AUTO', $force=false, $test_cache_only = false) {include_spip('inc/filtres_images_lib'); return _image_creer_vignette($valeurs, $maxWidth, $maxHeight, $process, $force, $test_cache_only);}
function image_ecrire_tag($valeurs,$surcharge){include_spip('inc/filtres_images_lib'); return _image_ecrire_tag($valeurs,$surcharge);}
function image_gd_output($img,$valeurs, $qualite=_IMG_GD_QUALITE){include_spip('inc/filtres_images_lib'); return _image_gd_output($img,$valeurs, $qualite);}
function image_imagepng($img,$fichier) {include_spip('inc/filtres_images_lib'); return _image_imagepng($img,$fichier);}
function image_imagegif($img,$fichier) {include_spip('inc/filtres_images_lib'); return _image_imagegif($img,$fichier);}
function image_imagejpg($img,$fichier,$qualite=_IMG_GD_QUALITE) {include_spip('inc/filtres_images_lib'); return image_imagejpg($img,$fichier,$qualite=_IMG_GD_QUALITE);}
function image_imageico($img, $fichier) {include_spip('inc/filtres_images_lib'); return _image_imageico($img, $fichier);}
function image_ratio ($srcWidth, $srcHeight, $maxWidth, $maxHeight) {include_spip('inc/filtres_images_lib');return _image_ratio ($srcWidth, $srcHeight, $maxWidth, $maxHeight);}
function image_tag_changer_taille($tag,$width,$height,$style=false){include_spip('inc/filtres_images_lib');return _image_tag_changer_taille($tag,$width,$height,$style);}
function image_valeurs_trans($img, $effet, $forcer_format = false, $fonction_creation = NULL) {include_spip('inc/filtres_images_lib'); return _image_valeurs_trans($img, $effet, $forcer_format, $fonction_creation);}

// Pour assurer la compatibilite avec les anciens nom des filtres image_xxx
// commencent par "image_"
// http://code.spip.net/@reduire_image
function reduire_image($texte, $taille = -1, $taille_y = -1) {
	return filtrer('image_graver',
		filtrer('image_reduire',$texte, $taille, $taille_y)
	);
}
// http://code.spip.net/@valeurs_image_trans
function valeurs_image_trans($img, $effet, $forcer_format = false) {
	include_spip('inc/filtres_images_lib_mini');
	return _image_valeurs_trans($img, $effet, $forcer_format = false);
}

?>