Dépôt officiel du core SPIP * Copie possible par svn sur svn://trac.rezo.net/spip * Les svn:externals sont présent dans https://git.spip.net/SPIP/[nom du plugin dist] https://www.spip.net
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

3647 lines
105 KiB

  1. <?php
  2. /***************************************************************************\
  3. * SPIP, Systeme de publication pour l'internet *
  4. * *
  5. * Copyright (c) 2001-2019 *
  6. * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
  7. * *
  8. * Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
  9. * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
  10. \***************************************************************************/
  11. /**
  12. * Utilitaires indispensables autour du serveur Http.
  13. *
  14. * @package SPIP\Core\Utilitaires
  15. **/
  16. if (!defined('_ECRIRE_INC_VERSION')) {
  17. return;
  18. }
  19. /**
  20. * Cherche une fonction surchargeable et en retourne le nom exact,
  21. * après avoir chargé le fichier la contenant si nécessaire.
  22. *
  23. * Charge un fichier (suivant les chemins connus) et retourne si elle existe
  24. * le nom de la fonction homonyme `$dir_$nom`, ou suffixé `$dir_$nom_dist`
  25. *
  26. * Peut être appelé plusieurs fois, donc optimisé.
  27. *
  28. * @api
  29. * @uses include_spip() Pour charger le fichier
  30. * @example
  31. * ```
  32. * $envoyer_mail = charger_fonction('envoyer_mail', 'inc');
  33. * $envoyer_mail($email, $sujet, $texte);
  34. * ```
  35. *
  36. * @param string $nom
  37. * Nom de la fonction (et du fichier)
  38. * @param string $dossier
  39. * Nom du dossier conteneur
  40. * @param bool $continue
  41. * true pour ne pas râler si la fonction n'est pas trouvée
  42. * @return string
  43. * Nom de la fonction, ou false.
  44. */
  45. function charger_fonction($nom, $dossier = 'exec', $continue = false) {
  46. static $echecs = array();
  47. if (strlen($dossier) and substr($dossier, -1) != '/') {
  48. $dossier .= '/';
  49. }
  50. $f = str_replace('/', '_', $dossier) . $nom;
  51. if (function_exists($f)) {
  52. return $f;
  53. }
  54. if (function_exists($g = $f . '_dist')) {
  55. return $g;
  56. }
  57. if (isset($echecs[$f])) {
  58. return $echecs[$f];
  59. }
  60. // Sinon charger le fichier de declaration si plausible
  61. if (!preg_match(',^\w+$,', $f)) {
  62. if ($continue) {
  63. return false;
  64. } //appel interne, on passe
  65. include_spip('inc/minipres');
  66. echo minipres();
  67. exit;
  68. }
  69. // passer en minuscules (cf les balises de formulaires)
  70. // et inclure le fichier
  71. if (!$inc = include_spip($dossier . ($d = strtolower($nom)))
  72. // si le fichier truc/machin/nom.php n'existe pas,
  73. // la fonction peut etre definie dans truc/machin.php qui regroupe plusieurs petites fonctions
  74. and strlen(dirname($dossier)) and dirname($dossier) != '.'
  75. ) {
  76. include_spip(substr($dossier, 0, -1));
  77. }
  78. if (function_exists($f)) {
  79. return $f;
  80. }
  81. if (function_exists($g)) {
  82. return $g;
  83. }
  84. if ($continue) {
  85. return $echecs[$f] = false;
  86. }
  87. // Echec : message d'erreur
  88. spip_log("fonction $nom ($f ou $g) indisponible" .
  89. ($inc ? "" : " (fichier $d absent de $dossier)"));
  90. include_spip('inc/minipres');
  91. echo minipres(_T('forum_titre_erreur'),
  92. _T('fichier_introuvable', array('fichier' => '<b>' . spip_htmlentities($d) . '</b>')),
  93. array('all_inline'=>true,'status'=>404));
  94. exit;
  95. }
  96. /**
  97. * Inclusion unique avec verification d'existence du fichier + log en crash sinon
  98. *
  99. * @param string $file
  100. * @return bool
  101. */
  102. function include_once_check($file) {
  103. if (file_exists($file)) {
  104. include_once $file;
  105. return true;
  106. }
  107. $crash = (isset($GLOBALS['meta']['message_crash_plugins']) ? unserialize($GLOBALS['meta']['message_crash_plugins']) : '');
  108. $crash = ($crash ? $crash : array());
  109. $crash[$file] = true;
  110. ecrire_meta('message_crash_plugins', serialize($crash));
  111. return false;
  112. }
  113. /**
  114. * Inclut un fichier PHP (en le cherchant dans les chemins)
  115. *
  116. * @api
  117. * @uses find_in_path()
  118. * @example
  119. * ```
  120. * include_spip('inc/texte');
  121. * ```
  122. *
  123. * @param string $f
  124. * Nom du fichier (sans l'extension)
  125. * @param bool $include
  126. * - true pour inclure le fichier,
  127. * - false ne fait que le chercher
  128. * @return string|bool
  129. * - false : fichier introuvable
  130. * - string : chemin du fichier trouvé
  131. **/
  132. function include_spip($f, $include = true) {
  133. return find_in_path($f . '.php', '', $include);
  134. }
  135. /**
  136. * Requiert un fichier PHP (en le cherchant dans les chemins)
  137. *
  138. * @uses find_in_path()
  139. * @see include_spip()
  140. * @example
  141. * ```
  142. * require_spip('inc/texte');
  143. * ```
  144. *
  145. * @param string $f
  146. * Nom du fichier (sans l'extension)
  147. * @return string|bool
  148. * - false : fichier introuvable
  149. * - string : chemin du fichier trouvé
  150. **/
  151. function require_spip($f) {
  152. return find_in_path($f . '.php', '', 'required');
  153. }
  154. /**
  155. * Exécute une fonction (appellée par un pipeline) avec la donnée transmise.
  156. *
  157. * Un pipeline est lie a une action et une valeur
  158. * chaque element du pipeline est autorise a modifier la valeur
  159. * le pipeline execute les elements disponibles pour cette action,
  160. * les uns apres les autres, et retourne la valeur finale
  161. *
  162. * Cf. compose_filtres dans references.php, qui est la
  163. * version compilee de cette fonctionnalite
  164. * appel unitaire d'une fonction du pipeline
  165. * utilisee dans le script pipeline precompile
  166. *
  167. * on passe $val par reference pour limiter les allocations memoire
  168. *
  169. * @param string $fonc
  170. * Nom de la fonction appelée par le pipeline
  171. * @param string|array $val
  172. * Les paramètres du pipeline, son environnement
  173. * @return string|array $val
  174. * Les paramètres du pipeline modifiés
  175. **/
  176. function minipipe($fonc, &$val) {
  177. // fonction
  178. if (function_exists($fonc)) {
  179. $val = call_user_func($fonc, $val);
  180. } // Class::Methode
  181. else {
  182. if (preg_match("/^(\w*)::(\w*)$/S", $fonc, $regs)
  183. and $methode = array($regs[1], $regs[2])
  184. and is_callable($methode)
  185. ) {
  186. $val = call_user_func($methode, $val);
  187. } else {
  188. spip_log("Erreur - '$fonc' non definie !");
  189. }
  190. }
  191. return $val;
  192. }
  193. /**
  194. * Appel d’un pipeline
  195. *
  196. * Exécute le pipeline souhaité, éventuellement avec des données initiales.
  197. * Chaque plugin qui a demandé à voir ce pipeline vera sa fonction spécifique appelée.
  198. * Les fonctions (des plugins) appelées peuvent modifier à leur guise le contenu.
  199. *
  200. * Deux types de retours. Si `$val` est un tableau de 2 éléments, avec une clé `data`
  201. * on retourne uniquement ce contenu (`$val['data']`) sinon on retourne tout `$val`.
  202. *
  203. *
  204. * @example
  205. * Appel du pipeline `pre_insertion`
  206. * ```
  207. * $champs = pipeline('pre_insertion', array(
  208. * 'args' => array('table' => 'spip_articles'),
  209. * 'data' => $champs
  210. * ));
  211. * ```
  212. *
  213. * @param string $action
  214. * Nom du pipeline
  215. * @param null|string|array $val
  216. * Données à l’entrée du pipeline
  217. * @return mixed|null
  218. * Résultat
  219. */
  220. function pipeline($action, $val = null) {
  221. static $charger;
  222. // chargement initial des fonctions mises en cache, ou generation du cache
  223. if (!$charger) {
  224. if (!($ok = @is_readable($charger = _CACHE_PIPELINES))) {
  225. include_spip('inc/plugin');
  226. // generer les fichiers php precompiles
  227. // de chargement des plugins et des pipelines
  228. actualise_plugins_actifs();
  229. if (!($ok = @is_readable($charger))) {
  230. spip_log("fichier $charger pas cree");
  231. }
  232. }
  233. if ($ok) {
  234. include_once $charger;
  235. }
  236. }
  237. // appliquer notre fonction si elle existe
  238. $fonc = 'execute_pipeline_' . strtolower($action);
  239. if (function_exists($fonc)) {
  240. $val = $fonc($val);
  241. } // plantage ?
  242. else {
  243. spip_log("fonction $fonc absente : pipeline desactive", _LOG_ERREUR);
  244. }
  245. // si le flux est une table avec 2 cle args&data
  246. // on ne ressort du pipe que les donnees dans 'data'
  247. // array_key_exists pour php 4.1.0
  248. if (is_array($val)
  249. and count($val) == 2
  250. and (array_key_exists('data', $val))
  251. ) {
  252. $val = $val['data'];
  253. }
  254. return $val;
  255. }
  256. /**
  257. * Enregistrement des événements
  258. *
  259. * Signature : `spip_log(message[,niveau|type|type.niveau])`
  260. *
  261. * Le niveau de log par défaut est la valeur de la constante `_LOG_INFO`
  262. *
  263. * Les différents niveaux possibles sont :
  264. *
  265. * - `_LOG_HS` : écrira 'HS' au début de la ligne logguée
  266. * - `_LOG_ALERTE_ROUGE` : 'ALERTE'
  267. * - `_LOG_CRITIQUE` : 'CRITIQUE'
  268. * - `_LOG_ERREUR` : 'ERREUR'
  269. * - `_LOG_AVERTISSEMENT` : 'WARNING'
  270. * - `_LOG_INFO_IMPORTANTE` : '!INFO'
  271. * - `_LOG_INFO` : 'info'
  272. * - `_LOG_DEBUG` : 'debug'
  273. *
  274. * @example
  275. * ```
  276. * spip_log($message)
  277. * spip_log($message, 'recherche')
  278. * spip_log($message, _LOG_DEBUG)
  279. * spip_log($message, 'recherche.'._LOG_DEBUG)
  280. * ```
  281. *
  282. * @api
  283. * @link http://programmer.spip.net/spip_log
  284. * @uses inc_log_dist()
  285. *
  286. * @param string $message
  287. * Message à loger
  288. * @param string|int $name
  289. *
  290. * - int indique le niveau de log, tel que `_LOG_DEBUG`
  291. * - string indique le type de log
  292. * - `string.int` indique les 2 éléments.
  293. * Cette dernière notation est controversée mais le 3ème
  294. * paramètre est planté pour cause de compatibilité ascendante.
  295. */
  296. function spip_log($message = null, $name = null) {
  297. static $pre = array();
  298. static $log;
  299. preg_match('/^([a-z_]*)\.?(\d)?$/iS', (string)$name, $regs);
  300. if (!isset($regs[1]) or !$logname = $regs[1]) {
  301. $logname = null;
  302. }
  303. if (!isset($regs[2]) or !$niveau = $regs[2]) {
  304. $niveau = _LOG_INFO;
  305. }
  306. if ($niveau <= (defined('_LOG_FILTRE_GRAVITE') ? _LOG_FILTRE_GRAVITE : _LOG_INFO_IMPORTANTE)) {
  307. if (!$pre) {
  308. $pre = array(
  309. _LOG_HS => 'HS:',
  310. _LOG_ALERTE_ROUGE => 'ALERTE:',
  311. _LOG_CRITIQUE => 'CRITIQUE:',
  312. _LOG_ERREUR => 'ERREUR:',
  313. _LOG_AVERTISSEMENT => 'WARNING:',
  314. _LOG_INFO_IMPORTANTE => '!INFO:',
  315. _LOG_INFO => 'info:',
  316. _LOG_DEBUG => 'debug:'
  317. );
  318. $log = charger_fonction('log', 'inc');
  319. }
  320. if (!is_string($message)) {
  321. $message = print_r($message, true);
  322. }
  323. $log($pre[$niveau] . ' ' . $message, $logname);
  324. }
  325. }
  326. /**
  327. * Enregistrement des journaux
  328. *
  329. * @uses inc_journal_dist()
  330. * @param string $phrase Texte du journal
  331. * @param array $opt Tableau d'options
  332. **/
  333. function journal($phrase, $opt = array()) {
  334. $journal = charger_fonction('journal', 'inc');
  335. $journal($phrase, $opt);
  336. }
  337. /**
  338. * Renvoie le `$_GET` ou le `$_POST` émis par l'utilisateur
  339. * ou pioché dans un tableau transmis
  340. *
  341. * @api
  342. * @param string $var
  343. * Clé souhaitée
  344. * @param bool|array $c
  345. * Tableau transmis (sinon cherche dans GET ou POST)
  346. * @return mixed|null
  347. * - null si la clé n'a pas été trouvée
  348. * - la valeur de la clé sinon.
  349. **/
  350. function _request($var, $c = false) {
  351. if (is_array($c)) {
  352. return isset($c[$var]) ? $c[$var] : null;
  353. }
  354. if (isset($_GET[$var])) {
  355. $a = $_GET[$var];
  356. } elseif (isset($_POST[$var])) {
  357. $a = $_POST[$var];
  358. } else {
  359. return null;
  360. }
  361. // Si on est en ajax et en POST tout a ete encode
  362. // via encodeURIComponent, il faut donc repasser
  363. // dans le charset local...
  364. if (defined('_AJAX')
  365. and _AJAX
  366. and isset($GLOBALS['meta']['charset'])
  367. and $GLOBALS['meta']['charset'] != 'utf-8'
  368. and is_string($a)
  369. // check rapide mais pas fiable
  370. and preg_match(',[\x80-\xFF],', $a)
  371. // check fiable
  372. and include_spip('inc/charsets')
  373. and is_utf8($a)
  374. ) {
  375. return importer_charset($a, 'utf-8');
  376. }
  377. return $a;
  378. }
  379. /**
  380. * Affecte une valeur à une clé (pour usage avec `_request()`)
  381. *
  382. * @see _request() Pour obtenir la valeur
  383. * @note Attention au cas ou l'on fait `set_request('truc', NULL);`
  384. *
  385. * @param string $var Nom de la clé
  386. * @param string $val Valeur à affecter
  387. * @param bool|array $c Tableau de données (sinon utilise `$_GET` et `$_POST`)
  388. * @return array|bool
  389. * - array $c complété si un $c est transmis,
  390. * - false sinon
  391. **/
  392. function set_request($var, $val = null, $c = false) {
  393. if (is_array($c)) {
  394. unset($c[$var]);
  395. if ($val !== null) {
  396. $c[$var] = $val;
  397. }
  398. return $c;
  399. }
  400. unset($_GET[$var]);
  401. unset($_POST[$var]);
  402. if ($val !== null) {
  403. $_GET[$var] = $val;
  404. }
  405. return false; # n'affecte pas $c
  406. }
  407. /**
  408. * Sanitizer une valeur *SI* elle provient du GET ou POST
  409. * Utile dans les squelettes pour les valeurs qu'on attrape dans le env,
  410. * dont on veut permettre à un squelette de confiance appelant de fournir une valeur complexe
  411. * mais qui doit etre nettoyee si elle provient de l'URL
  412. *
  413. * On peut sanitizer
  414. * - une valeur simple : `$where = spip_sanitize_from_request($value, 'where')`
  415. * - un tableau en partie : `$env = spip_sanitize_from_request($env, ['key1','key2'])`
  416. * - un tableau complet : `$env = spip_sanitize_from_request($env, '*')`
  417. *
  418. * @param string|array $value
  419. * @param string|array $key
  420. * @param string $sanitize_function
  421. * @return array|mixed|string
  422. */
  423. function spip_sanitize_from_request($value, $key, $sanitize_function='entites_html') {
  424. if (is_array($value)) {
  425. if ($key=='*') {
  426. $key = array_keys($value);
  427. }
  428. if (!is_array($key)) {
  429. $key = [$key];
  430. }
  431. foreach ($key as $k) {
  432. if (!empty($value[$k])) {
  433. $value[$k] = spip_sanitize_from_request($value[$k], $k, $sanitize_function);
  434. }
  435. }
  436. return $value;
  437. }
  438. // si la valeur vient des GET ou POST on la sanitize
  439. if (!empty($value) and $value == _request($key)) {
  440. $value = $sanitize_function($value);
  441. }
  442. return $value;
  443. }
  444. /**
  445. * Tester si une URL est absolue
  446. *
  447. * On est sur le web, on exclut certains protocoles,
  448. * notamment 'file://', 'php://' et d'autres…
  449. * @param string $url
  450. * @return bool
  451. */
  452. function tester_url_absolue($url) {
  453. $url = trim($url);
  454. if (preg_match(";^([a-z]{3,7}:)?//;Uims", $url, $m)) {
  455. if (
  456. isset($m[1])
  457. and $p = strtolower(rtrim($m[1], ':'))
  458. and in_array($p, array('file', 'php', 'zlib', 'glob', 'phar', 'ssh2', 'rar', 'ogg', 'expect', 'zip'))
  459. ) {
  460. return false;
  461. }
  462. return true;
  463. }
  464. return false;
  465. }
  466. /**
  467. * Prend une URL et lui ajoute/retire un paramètre
  468. *
  469. * @filtre
  470. * @link http://www.spip.net/4255
  471. * @example
  472. * ```
  473. * [(#SELF|parametre_url{suite,18})] (ajout)
  474. * [(#SELF|parametre_url{suite,''})] (supprime)
  475. * [(#SELF|parametre_url{suite[],1})] (tableaux valeurs multiples)
  476. * ```
  477. *
  478. * @param string $url URL
  479. * @param string $c Nom du paramètre
  480. * @param string|array|null $v Valeur du paramètre
  481. * @param string $sep Séparateur entre les paramètres
  482. * @return string URL
  483. */
  484. function parametre_url($url, $c, $v = null, $sep = '&amp;') {
  485. // requete erronnee : plusieurs variable dans $c et aucun $v
  486. if (strpos($c, "|") !== false and is_null($v)) {
  487. return null;
  488. }
  489. // lever l'#ancre
  490. if (preg_match(',^([^#]*)(#.*)$,', $url, $r)) {
  491. $url = $r[1];
  492. $ancre = $r[2];
  493. } else {
  494. $ancre = '';
  495. }
  496. // eclater
  497. $url = preg_split(',[?]|&amp;|&,', $url);
  498. // recuperer la base
  499. $a = array_shift($url);
  500. if (!$a) {
  501. $a = './';
  502. }
  503. $regexp = ',^(' . str_replace('[]', '\[\]', $c) . '[[]?[]]?)(=.*)?$,';
  504. $ajouts = array_flip(explode('|', $c));
  505. $u = is_array($v) ? $v : rawurlencode($v);
  506. $testv = (is_array($v) ? count($v) : strlen($v));
  507. $v_read = null;
  508. // lire les variables et agir
  509. foreach ($url as $n => $val) {
  510. if (preg_match($regexp, urldecode($val), $r)) {
  511. $r = array_pad($r, 3, null);
  512. if ($v === null) {
  513. // c'est un tableau, on memorise les valeurs
  514. if (substr($r[1], -2) == "[]") {
  515. if (!$v_read) {
  516. $v_read = array();
  517. }
  518. $v_read[] = $r[2] ? substr($r[2], 1) : '';
  519. } // c'est un scalaire, on retourne direct
  520. else {
  521. return $r[2] ? substr($r[2], 1) : '';
  522. }
  523. } // suppression
  524. elseif (!$testv) {
  525. unset($url[$n]);
  526. }
  527. // Ajout. Pour une variable, remplacer au meme endroit,
  528. // pour un tableau ce sera fait dans la prochaine boucle
  529. elseif (substr($r[1], -2) != '[]') {
  530. $url[$n] = $r[1] . '=' . $u;
  531. unset($ajouts[$r[1]]);
  532. }
  533. // Pour les tableaux on laisse tomber les valeurs de
  534. // départ, on remplira à l'étape suivante
  535. else {
  536. unset($url[$n]);
  537. }
  538. }
  539. }
  540. // traiter les parametres pas encore trouves
  541. if ($v === null
  542. and $args = func_get_args()
  543. and count($args) == 2
  544. ) {
  545. return $v_read; // rien trouve ou un tableau
  546. } elseif ($testv) {
  547. foreach ($ajouts as $k => $n) {
  548. if (!is_array($v)) {
  549. $url[] = $k . '=' . $u;
  550. } else {
  551. $id = (substr($k, -2) == '[]') ? $k : ($k . "[]");
  552. foreach ($v as $w) {
  553. $url[] = $id . '=' . (is_array($w) ? 'Array' : $w);
  554. }
  555. }
  556. }
  557. }
  558. // eliminer les vides
  559. $url = array_filter($url);
  560. // recomposer l'adresse
  561. if ($url) {
  562. $a .= '?' . join($sep, $url);
  563. }
  564. return $a . $ancre;
  565. }
  566. /**
  567. * Ajoute (ou retire) une ancre sur une URL
  568. *
  569. * L’ancre est nettoyée : on translitère, vire les non alphanum du début,
  570. * et on remplace ceux à l'interieur ou au bout par `-`
  571. *
  572. * @example
  573. * - `$url = ancre_url($url, 'navigation'); // => mettra l’ancre #navigation
  574. * - `$url = ancre_url($url, ''); // => enlèvera une éventuelle ancre
  575. * @uses translitteration()
  576. * @param string $url
  577. * @param string $ancre
  578. * @return string
  579. */
  580. function ancre_url($url, $ancre) {
  581. // lever l'#ancre
  582. if (preg_match(',^([^#]*)(#.*)$,', $url, $r)) {
  583. $url = $r[1];
  584. }
  585. if (preg_match('/[^-_a-zA-Z0-9]+/S', $ancre)) {
  586. if (!function_exists('translitteration')) {
  587. include_spip('inc/charsets');
  588. }
  589. $ancre = preg_replace(
  590. array('/^[^-_a-zA-Z0-9]+/', '/[^-_a-zA-Z0-9]/'),
  591. array('', '-'),
  592. translitteration($ancre)
  593. );
  594. }
  595. return $url . (strlen($ancre) ? '#' . $ancre : '');
  596. }
  597. /**
  598. * Pour le nom du cache, les `types_urls` et `self`
  599. *
  600. * @param string|null $reset
  601. * @return string
  602. */
  603. function nettoyer_uri($reset = null) {
  604. static $done = false;
  605. static $propre = '';
  606. if (!is_null($reset)) {
  607. return $propre = $reset;
  608. }
  609. if ($done) {
  610. return $propre;
  611. }
  612. $done = true;
  613. return $propre = nettoyer_uri_var($GLOBALS['REQUEST_URI']);
  614. }
  615. /**
  616. * Nettoie une request_uri des paramètres var_xxx
  617. *
  618. * Attention, la regexp doit suivre _CONTEXTE_IGNORE_VARIABLES défini au début de public/assembler.php
  619. *
  620. * @param $request_uri
  621. * @return string
  622. */
  623. function nettoyer_uri_var($request_uri) {
  624. $uri1 = $request_uri;
  625. do {
  626. $uri = $uri1;
  627. $uri1 = preg_replace(',([?&])(var_[^=&]*|PHPSESSID|fbclid|utm_[^=&]*)=[^&]*(&|$),i',
  628. '\1', $uri);
  629. } while ($uri <> $uri1);
  630. return preg_replace(',[?&]$,', '', $uri1);
  631. }
  632. /**
  633. * Donner l'URL de base d'un lien vers "soi-meme", modulo les trucs inutiles
  634. *
  635. * @param string $amp
  636. * Style des esperluettes
  637. * @param bool $root
  638. * @return string
  639. * URL vers soi-même
  640. **/
  641. function self($amp = '&amp;', $root = false) {
  642. $url = nettoyer_uri();
  643. if (!$root
  644. and (
  645. // si pas de profondeur on peut tronquer
  646. $GLOBALS['profondeur_url'] < (_DIR_RESTREINT ? 1 : 2)
  647. // sinon c'est OK si _SET_HTML_BASE a ete force a false
  648. or (defined('_SET_HTML_BASE') and !_SET_HTML_BASE))
  649. ) {
  650. $url = preg_replace(',^[^?]*/,', '', $url);
  651. }
  652. // ajouter le cas echeant les variables _POST['id_...']
  653. foreach ($_POST as $v => $c) {
  654. if (substr($v, 0, 3) == 'id_') {
  655. $url = parametre_url($url, $v, $c, '&');
  656. }
  657. }
  658. // supprimer les variables sans interet
  659. if (test_espace_prive()) {
  660. $url = preg_replace(',([?&])('
  661. . 'lang|show_docs|'
  662. . 'changer_lang|var_lang|action)=[^&]*,i', '\1', $url);
  663. $url = preg_replace(',([?&])[&]+,', '\1', $url);
  664. $url = preg_replace(',[&]$,', '\1', $url);
  665. }
  666. // eviter les hacks
  667. include_spip('inc/filtres_mini');
  668. $url = spip_htmlspecialchars($url);
  669. $url = str_replace(array("'", '"', '<', '[', ']', ':'), array('%27', '%22', '%3C', '%5B', '%5D', '%3A'), $url);
  670. // &amp; ?
  671. if ($amp != '&amp;') {
  672. $url = str_replace('&amp;', $amp, $url);
  673. }
  674. // Si ca demarre par ? ou vide, donner './'
  675. $url = preg_replace(',^([?].*)?$,', './\1', $url);
  676. return $url;
  677. }
  678. /**
  679. * Indique si on est dans l'espace prive
  680. *
  681. * @return bool
  682. * true si c'est le cas, false sinon.
  683. */
  684. function test_espace_prive() {
  685. return defined('_ESPACE_PRIVE') ? _ESPACE_PRIVE : false;
  686. }
  687. /**
  688. * Vérifie la présence d'un plugin actif, identifié par son préfixe
  689. *
  690. * @param string $plugin
  691. * @return bool
  692. */
  693. function test_plugin_actif($plugin) {
  694. return ($plugin and defined('_DIR_PLUGIN_' . strtoupper($plugin))) ? true : false;
  695. }
  696. /**
  697. * Traduction des textes de SPIP
  698. *
  699. * Traduit une clé de traduction en l'obtenant dans les fichiers de langues.
  700. *
  701. * @api
  702. * @uses inc_traduire_dist()
  703. * @uses _L()
  704. * @example
  705. * ```
  706. * _T('bouton_enregistrer')
  707. * _T('medias:image_tourner_droite')
  708. * _T('medias:erreurs', array('nb'=>3))
  709. * _T("email_sujet", array('spip_lang'=>$lang_usager))
  710. * ```
  711. *
  712. * @param string $texte
  713. * Clé de traduction
  714. * @param array $args
  715. * Couples (variable => valeur) pour passer des variables à la chaîne traduite. la variable spip_lang permet de forcer la langue
  716. * @param array $options
  717. * - string class : nom d'une classe a ajouter sur un span pour encapsuler la chaine
  718. * - bool force : forcer un retour meme si la chaine n'a pas de traduction
  719. * - bool sanitize : nettoyer le html suspect dans les arguments
  720. * @return string
  721. * Texte
  722. */
  723. function _T($texte, $args = array(), $options = array()) {
  724. static $traduire = false;
  725. $o = array('class' => '', 'force' => true, 'sanitize' => true);
  726. if ($options) {
  727. // support de l'ancien argument $class
  728. if (is_string($options)) {
  729. $options = array('class' => $options);
  730. }
  731. $o = array_merge($o, $options);
  732. }
  733. if (!$traduire) {
  734. $traduire = charger_fonction('traduire', 'inc');
  735. include_spip('inc/lang');
  736. }
  737. // On peut passer explicitement la langue dans le tableau
  738. // On utilise le même nom de variable que la globale
  739. if (isset($args['spip_lang'])) {
  740. $lang = $args['spip_lang'];
  741. // On l'enleve pour ne pas le passer au remplacement
  742. unset($args['spip_lang']);
  743. } // Sinon on prend la langue du contexte
  744. else {
  745. $lang = $GLOBALS['spip_lang'];
  746. }
  747. $text = $traduire($texte, $lang);
  748. if (!strlen($text)) {
  749. if (!$o['force']) {
  750. return '';
  751. }
  752. $text = $texte;
  753. // pour les chaines non traduites, assurer un service minimum
  754. if (!$GLOBALS['test_i18n'] and (_request('var_mode') != 'traduction')) {
  755. $text = str_replace('_', ' ',
  756. (($n = strpos($text, ':')) === false ? $texte :
  757. substr($texte, $n + 1)));
  758. }
  759. $o['class'] = null;
  760. }
  761. return _L($text, $args, $o);
  762. }
  763. /**
  764. * Remplace les variables `@...@` par leur valeur dans une chaîne de langue.
  765. *
  766. * Cette fonction est également appelée dans le code source de SPIP quand une
  767. * chaîne n'est pas encore dans les fichiers de langue.
  768. *
  769. * @see _T()
  770. * @example
  771. * ```
  772. * _L('Texte avec @nb@ ...', array('nb'=>3)
  773. * ```
  774. *
  775. * @param string $text
  776. * Texte
  777. * @param array $args
  778. * Couples (variable => valeur) à transformer dans le texte
  779. * @param array $options
  780. * - string class : nom d'une classe a ajouter sur un span pour encapsuler la chaine
  781. * - bool sanitize : nettoyer le html suspect dans les arguments
  782. * @return string
  783. * Texte
  784. */
  785. function _L($text, $args = array(), $options = array()) {
  786. $f = $text;
  787. $defaut_options = array(
  788. 'class' => null,
  789. 'sanitize' => true,
  790. );
  791. // support de l'ancien argument $class
  792. if ($options and is_string($options)) {
  793. $options = array('class' => $options);
  794. }
  795. if (is_array($options)) {
  796. $options += $defaut_options;
  797. } else {
  798. $options = $defaut_options;
  799. }
  800. if (is_array($args) and count($args)) {
  801. if (!function_exists('interdire_scripts')) {
  802. include_spip('inc/texte');
  803. }
  804. if (!function_exists('echapper_html_suspect')) {
  805. include_spip('inc/texte_mini');
  806. }
  807. foreach ($args as $name => $value) {
  808. if (strpos($text, "@$name@") !== false) {
  809. if ($options['sanitize']) {
  810. $value = echapper_html_suspect($value);
  811. $value = interdire_scripts($value, -1);
  812. }
  813. if (!empty($options['class'])) {
  814. $value = "<span class='".$options['class']."'>$value</span>";
  815. }
  816. $text = str_replace("@$name@", $value, $text);
  817. unset($args[$name]);
  818. }
  819. }
  820. // Si des variables n'ont pas ete inserees, le signaler
  821. // (chaines de langues pas a jour)
  822. if ($args) {
  823. spip_log("$f: variables inutilisees " . join(', ', array_keys($args)), _LOG_DEBUG);
  824. }
  825. }
  826. if (($GLOBALS['test_i18n'] or (_request('var_mode') == 'traduction')) and is_null($options['class'])) {
  827. return "<span class=debug-traduction-erreur>$text</span>";
  828. } else {
  829. return $text;
  830. }
  831. }
  832. /**
  833. * Retourne un joli chemin de répertoire
  834. *
  835. * Pour afficher `ecrire/action/` au lieu de `action/` dans les messages
  836. * ou `tmp/` au lieu de `../tmp/`
  837. *
  838. * @param stirng $rep Chemin d’un répertoire
  839. * @return string
  840. */
  841. function joli_repertoire($rep) {
  842. $a = substr($rep, 0, 1);
  843. if ($a <> '.' and $a <> '/') {
  844. $rep = (_DIR_RESTREINT ? '' : _DIR_RESTREINT_ABS) . $rep;
  845. }
  846. $rep = preg_replace(',(^\.\.\/),', '', $rep);
  847. return $rep;
  848. }
  849. /**
  850. * Débute ou arrête un chronomètre et retourne sa valeur
  851. *
  852. * On exécute 2 fois la fonction, la première fois pour démarrer le chrono,
  853. * la seconde fois pour l’arrêter et récupérer la valeur
  854. *
  855. * @example
  856. * ```
  857. * spip_timer('papoter');
  858. * // actions
  859. * $duree = spip_timer('papoter');
  860. * ```
  861. *
  862. * @param string $t
  863. * Nom du chronomètre
  864. * @param bool $raw
  865. * - false : retour en texte humainement lisible
  866. * - true : retour en millisecondes
  867. * @return float|int|string|void
  868. */
  869. function spip_timer($t = 'rien', $raw = false) {
  870. static $time;
  871. $a = time();
  872. $b = microtime();
  873. // microtime peut contenir les microsecondes et le temps
  874. $b = explode(' ', $b);
  875. if (count($b) == 2) {
  876. $a = end($b);
  877. } // plus precis !
  878. $b = reset($b);
  879. if (!isset($time[$t])) {
  880. $time[$t] = $a + $b;
  881. } else {
  882. $p = ($a + $b - $time[$t]) * 1000;
  883. unset($time[$t]);
  884. # echo "'$p'";exit;
  885. if ($raw) {
  886. return $p;
  887. }
  888. if ($p < 1000) {
  889. $s = '';
  890. } else {
  891. $s = sprintf("%d ", $x = floor($p / 1000));
  892. $p -= ($x * 1000);
  893. }
  894. return $s . sprintf($s ? "%07.3f ms" : "%.3f ms", $p);
  895. }
  896. }
  897. // Renvoie False si un fichier n'est pas plus vieux que $duree secondes,
  898. // sinon renvoie True et le date sauf si ca n'est pas souhaite
  899. // http://code.spip.net/@spip_touch
  900. function spip_touch($fichier, $duree = 0, $touch = true) {
  901. if ($duree) {
  902. clearstatcache();
  903. if ((@$f = filemtime($fichier)) and ($f >= time() - $duree)) {
  904. return false;
  905. }
  906. }
  907. if ($touch !== false) {
  908. if (!@touch($fichier)) {
  909. spip_unlink($fichier);
  910. @touch($fichier);
  911. };
  912. @chmod($fichier, _SPIP_CHMOD & ~0111);
  913. }
  914. return true;
  915. }
  916. /**
  917. * Action qui déclenche une tache de fond
  918. *
  919. * @see queue_affichage_cron()
  920. * @see action_super_cron_dist()
  921. * @uses cron()
  922. **/
  923. function action_cron() {
  924. include_spip('inc/headers');
  925. http_status(204); // No Content
  926. header("Connection: close");
  927. define('_DIRECT_CRON_FORCE', true);
  928. cron();
  929. }
  930. /**
  931. * Exécution des tâches de fond
  932. *
  933. * @uses inc_genie_dist()
  934. *
  935. * @param array $taches
  936. * Tâches forcées
  937. * @param array $taches_old
  938. * Tâches forcées, pour compat avec ancienne syntaxe
  939. * @return bool
  940. * True si la tache a pu être effectuée
  941. */
  942. function cron($taches = array(), $taches_old = array()) {
  943. // si pas en mode cron force, laisser tomber.
  944. if (!defined('_DIRECT_CRON_FORCE')) {
  945. return false;
  946. }
  947. if (!is_array($taches)) {
  948. $taches = $taches_old;
  949. } // compat anciens appels
  950. // si taches a inserer en base et base inaccessible, laisser tomber
  951. // sinon on ne verifie pas la connexion tout de suite, car si ca se trouve
  952. // queue_sleep_time_to_next_job() dira qu'il n'y a rien a faire
  953. // et on evite d'ouvrir une connexion pour rien (utilisation de _DIRECT_CRON_FORCE dans mes_options.php)
  954. if ($taches and count($taches) and !spip_connect()) {
  955. return false;
  956. }
  957. spip_log("cron !", 'jq' . _LOG_DEBUG);
  958. if ($genie = charger_fonction('genie', 'inc', true)) {
  959. return $genie($taches);
  960. }
  961. return false;
  962. }
  963. /**
  964. * Ajout d'une tache dans la file d'attente
  965. *
  966. * @param string $function
  967. * Le nom de la fonction PHP qui doit être appelée.
  968. * @param string $description
  969. * Une description humainement compréhensible de ce que fait la tâche
  970. * (essentiellement pour l’affichage dans la page de suivi de l’espace privé)
  971. * @param array $arguments
  972. * Facultatif, vide par défaut : les arguments qui seront passés à la fonction, sous forme de tableau PHP
  973. * @param string $file
  974. * Facultatif, vide par défaut : nom du fichier à inclure, via `include_spip($file)`
  975. * exemple : `'inc/mail'` : il ne faut pas indiquer .php
  976. * Si le nom finit par un '/' alors on considère que c’est un répertoire et SPIP fera un `charger_fonction($function, $file)`
  977. * @param bool $no_duplicate
  978. * Facultatif, `false` par défaut
  979. *
  980. * - si `true` la tâche ne sera pas ajoutée si elle existe déjà en file d’attente avec la même fonction et les mêmes arguments.
  981. * - si `function_only` la tâche ne sera pas ajoutée si elle existe déjà en file d’attente avec la même fonction indépendamment de ses arguments
  982. * @param int $time
  983. * Facultatif, `0` par défaut : indique la date sous forme de timestamp à laquelle la tâche doit être programmée.
  984. * Si `0` ou une date passée, la tâche sera exécutée aussitôt que possible (en général en fin hit, en asynchrone).
  985. * @param int $priority
  986. * Facultatif, `0` par défaut : indique un niveau de priorité entre -10 et +10.
  987. * Les tâches sont exécutées par ordre de priorité décroissante, une fois leur date d’exécution passée. La priorité est surtout utilisée quand une tâche cron indique qu’elle n’a pas fini et doit être relancée : dans ce cas SPIP réduit sa priorité pour être sûr que celle tâche ne monopolise pas la file d’attente.
  988. * @return int
  989. * Le numéro de travail ajouté ou `0` si aucun travail n’a été ajouté.
  990. */
  991. function job_queue_add(
  992. $function,
  993. $description,
  994. $arguments = array(),
  995. $file = '',
  996. $no_duplicate = false,
  997. $time = 0,
  998. $priority = 0
  999. ) {
  1000. include_spip('inc/queue');
  1001. return queue_add_job($function, $description, $arguments, $file, $no_duplicate, $time, $priority);
  1002. }
  1003. /**
  1004. * Supprimer une tache de la file d'attente
  1005. *
  1006. * @param int $id_job
  1007. * id of jonb to delete
  1008. * @return bool
  1009. */
  1010. function job_queue_remove($id_job) {
  1011. include_spip('inc/queue');
  1012. return queue_remove_job($id_job);
  1013. }
  1014. /**
  1015. * Associer une tache a un/des objets de SPIP
  1016. *
  1017. * @param int $id_job
  1018. * id of job to link
  1019. * @param array $objets
  1020. * can be a simple array('objet'=>'article', 'id_objet'=>23)
  1021. * or an array of simple array to link multiples objet in one time
  1022. */
  1023. function job_queue_link($id_job, $objets) {
  1024. include_spip('inc/queue');
  1025. return queue_link_job($id_job, $objets);
  1026. }
  1027. /**
  1028. * Renvoyer le temps de repos restant jusqu'au prochain job
  1029. *
  1030. * @staticvar int $queue_next_job_time
  1031. * @see queue_set_next_job_time()
  1032. * @param int|bool $force
  1033. * Utilisée par `queue_set_next_job_time()` pour mettre à jour la valeur :
  1034. *
  1035. * - si `true`, force la relecture depuis le fichier
  1036. * - si int, affecte la static directement avec la valeur
  1037. * @return int
  1038. *
  1039. * - `0` si un job est à traiter
  1040. * - `null` si la queue n'est pas encore initialisée
  1041. */
  1042. function queue_sleep_time_to_next_job($force = null) {
  1043. static $queue_next_job_time = -1;
  1044. if ($force === true) {
  1045. $queue_next_job_time = -1;
  1046. } elseif ($force) {
  1047. $queue_next_job_time = $force;
  1048. }
  1049. if ($queue_next_job_time == -1) {
  1050. if (!defined('_JQ_NEXT_JOB_TIME_FILENAME')) {
  1051. define('_JQ_NEXT_JOB_TIME_FILENAME', _DIR_TMP . "job_queue_next.txt");
  1052. }
  1053. // utiliser un cache memoire si dispo
  1054. if (function_exists("cache_get") and defined('_MEMOIZE_MEMORY') and _MEMOIZE_MEMORY) {
  1055. $queue_next_job_time = cache_get(_JQ_NEXT_JOB_TIME_FILENAME);
  1056. } else {
  1057. $queue_next_job_time = null;
  1058. if (lire_fichier(_JQ_NEXT_JOB_TIME_FILENAME, $contenu)) {
  1059. $queue_next_job_time = intval($contenu);
  1060. }
  1061. }
  1062. }
  1063. if (is_null($queue_next_job_time)) {
  1064. return null;
  1065. }
  1066. if (!$_SERVER['REQUEST_TIME']) {
  1067. $_SERVER['REQUEST_TIME'] = time();
  1068. }
  1069. return $queue_next_job_time - $_SERVER['REQUEST_TIME'];
  1070. }
  1071. /**
  1072. * Transformation XML des `&` en `&amp;`
  1073. *
  1074. * @pipeline post_typo
  1075. * @param string $u
  1076. * @return string
  1077. */
  1078. function quote_amp($u) {
  1079. return preg_replace(
  1080. "/&(?![a-z]{0,4}\w{2,3};|#x?[0-9a-f]{2,6};)/i",
  1081. "&amp;", $u);
  1082. }
  1083. /**
  1084. * Produit une balise `<script>` valide
  1085. *
  1086. * @example
  1087. * ```
  1088. * echo http_script('alert("ok");');
  1089. * echo http_script('','js/jquery.js');
  1090. * ```
  1091. *
  1092. * @param string $script
  1093. * Code source du script
  1094. * @param string $src
  1095. * Permet de faire appel à un fichier javascript distant
  1096. * @param string $noscript
  1097. * Contenu de la balise `<noscript>`
  1098. * @return string
  1099. * Balise HTML `<script>` et son contenu
  1100. **/
  1101. function http_script($script, $src = '', $noscript = '') {
  1102. static $done = array();
  1103. if ($src && !isset($done[$src])) {
  1104. $done[$src] = true;
  1105. $src = find_in_path($src, _JAVASCRIPT);
  1106. $src = " src='$src'";
  1107. } else {
  1108. $src = '';
  1109. }
  1110. if ($script) {
  1111. $script = ("/*<![CDATA[*/\n" .
  1112. preg_replace(',</([^>]*)>,', '<\/\1>', $script) .
  1113. "/*]]>*/");
  1114. }
  1115. if ($noscript) {
  1116. $noscript = "<noscript>\n\t$noscript\n</noscript>\n";
  1117. }
  1118. return ($src or $script or $noscript)
  1119. ? "<script type='text/javascript'$src>$script</script>$noscript"
  1120. : '';
  1121. }
  1122. /**
  1123. * Sécurise du texte à écrire dans du PHP ou du Javascript.
  1124. *
  1125. * Transforme n'importe quel texte en une chaîne utilisable
  1126. * en PHP ou Javascript en toute sécurité, à l'intérieur d'apostrophes
  1127. * simples (`'` uniquement ; pas `"`)
  1128. *
  1129. * Utile particulièrement en filtre dans un squelettes
  1130. * pour écrire un contenu dans une variable JS ou PHP.
  1131. *
  1132. * Échappe les apostrophes (') du contenu transmis.
  1133. *
  1134. * @link http://www.spip.net/4281
  1135. * @example
  1136. * PHP dans un squelette
  1137. * ```
  1138. * $x = '[(#TEXTE|texte_script)]';
  1139. * ```
  1140. *
  1141. * JS dans un squelette (transmettre une chaîne de langue)
  1142. * ```
  1143. * $x = '<:afficher_calendrier|texte_script:>';
  1144. * ```
  1145. *
  1146. * @filtre
  1147. * @param string $texte
  1148. * Texte à échapper
  1149. * @return string
  1150. * Texte échappé
  1151. **/
  1152. function texte_script($texte) {
  1153. return str_replace('\'', '\\\'', str_replace('\\', '\\\\', $texte));
  1154. }
  1155. /**
  1156. * Gestion des chemins (ou path) de recherche de fichiers par SPIP
  1157. *
  1158. * Empile de nouveaux chemins (à la suite de ceux déjà présents, mais avant
  1159. * le répertoire `squelettes` ou les dossiers squelettes), si un répertoire
  1160. * (ou liste de répertoires séparés par `:`) lui est passé en paramètre.
  1161. *
  1162. * Ainsi, si l'argument est de la forme `dir1:dir2:dir3`, ces 3 chemins sont placés
  1163. * en tête du path, dans cet ordre (hormis `squelettes` & la globale
  1164. * `$dossier_squelette` si définie qui resteront devant)
  1165. *
  1166. * Retourne dans tous les cas la liste des chemins.
  1167. *
  1168. * @note
  1169. * Cette fonction est appelée à plusieurs endroits et crée une liste
  1170. * de chemins finale à peu près de la sorte :
  1171. *
  1172. * - dossiers squelettes (si globale précisée)
  1173. * - squelettes/
  1174. * - plugins (en fonction de leurs dépendances) : ceux qui dépendent
  1175. * d'un plugin sont devant eux (ils peuvent surcharger leurs fichiers)
  1176. * - racine du site
  1177. * - squelettes-dist/
  1178. * - prive/
  1179. * - ecrire/
  1180. *
  1181. * @param string $dir_path
  1182. * - Répertoire(s) à empiler au path
  1183. * - '' provoque un recalcul des chemins.
  1184. * @return array
  1185. * Liste des chemins, par ordre de priorité.
  1186. **/
  1187. function _chemin($dir_path = null) {
  1188. static $path_base = null;
  1189. static $path_full = null;
  1190. if ($path_base == null) {
  1191. // Chemin standard depuis l'espace public
  1192. $path = defined('_SPIP_PATH') ? _SPIP_PATH :
  1193. _DIR_RACINE . ':' .
  1194. _DIR_RACINE . 'squelettes-dist/:' .
  1195. _DIR_RACINE . 'prive/:' .
  1196. _DIR_RESTREINT;
  1197. // Ajouter squelettes/
  1198. if (@is_dir(_DIR_RACINE . 'squelettes')) {
  1199. $path = _DIR_RACINE . 'squelettes/:' . $path;
  1200. }
  1201. foreach (explode(':', $path) as $dir) {
  1202. if (strlen($dir) and substr($dir, -1) != '/') {
  1203. $dir .= "/";
  1204. }
  1205. $path_base[] = $dir;
  1206. }
  1207. $path_full = $path_base;
  1208. // Et le(s) dossier(s) des squelettes nommes
  1209. if (strlen($GLOBALS['dossier_squelettes'])) {
  1210. foreach (array_reverse(explode(':', $GLOBALS['dossier_squelettes'])) as $d) {
  1211. array_unshift($path_full, ($d[0] == '/' ? '' : _DIR_RACINE) . $d . '/');
  1212. }
  1213. }
  1214. $GLOBALS['path_sig'] = md5(serialize($path_full));
  1215. }
  1216. if ($dir_path === null) {
  1217. return $path_full;
  1218. }
  1219. if (strlen($dir_path)) {
  1220. $tete = "";
  1221. if (reset($path_base) == _DIR_RACINE . 'squelettes/') {
  1222. $tete = array_shift($path_base);
  1223. }
  1224. $dirs = array_reverse(explode(':', $dir_path));
  1225. foreach ($dirs as $dir_path) {
  1226. #if ($dir_path{0}!='/')
  1227. # $dir_path = $dir_path;
  1228. if (substr($dir_path, -1) != '/') {
  1229. $dir_path .= "/";
  1230. }
  1231. if (!in_array($dir_path, $path_base)) {
  1232. array_unshift($path_base, $dir_path);
  1233. }
  1234. }
  1235. if (strlen($tete)) {
  1236. array_unshift($path_base, $tete);
  1237. }
  1238. }
  1239. $path_full = $path_base;
  1240. // Et le(s) dossier(s) des squelettes nommes
  1241. if (strlen($GLOBALS['dossier_squelettes'])) {
  1242. foreach (array_reverse(explode(':', $GLOBALS['dossier_squelettes'])) as $d) {
  1243. array_unshift($path_full, ((isset($d[0]) and $d[0] == '/') ? '' : _DIR_RACINE) . $d . '/');
  1244. }
  1245. }
  1246. $GLOBALS['path_sig'] = md5(serialize($path_full));
  1247. return $path_full;
  1248. }
  1249. /**
  1250. * Retourne la liste des chemins connus de SPIP, dans l'ordre de priorité
  1251. *
  1252. * Recalcule la liste si le nom ou liste de dossier squelettes a changé.
  1253. *
  1254. * @uses _chemin()
  1255. *
  1256. * @return array Liste de chemins
  1257. **/
  1258. function creer_chemin() {
  1259. $path_a = _chemin();
  1260. static $c = '';
  1261. // on calcule le chemin si le dossier skel a change
  1262. if ($c != $GLOBALS['dossier_squelettes']) {
  1263. // assurer le non plantage lors de la montee de version :
  1264. $c = $GLOBALS['dossier_squelettes'];
  1265. $path_a = _chemin(''); // forcer un recalcul du chemin
  1266. }
  1267. return $path_a;
  1268. }
  1269. function lister_themes_prives() {
  1270. static $themes = null;
  1271. if (is_null($themes)) {
  1272. // si pas encore definie
  1273. if (!defined('_SPIP_THEME_PRIVE')) {
  1274. define('_SPIP_THEME_PRIVE', 'spip');
  1275. }
  1276. $themes = array(_SPIP_THEME_PRIVE);
  1277. // lors d'une installation neuve, prefs n'est pas definie.
  1278. if (isset($GLOBALS['visiteur_session']['prefs'])) {
  1279. $prefs = $GLOBALS['visiteur_session']['prefs'];
  1280. } else {
  1281. $prefs = array();
  1282. }
  1283. if (is_string($prefs)) {
  1284. $prefs = unserialize($GLOBALS['visiteur_session']['prefs']);
  1285. }
  1286. if (
  1287. ((isset($prefs['theme']) and $theme = $prefs['theme'])
  1288. or (isset($GLOBALS['theme_prive_defaut']) and $theme = $GLOBALS['theme_prive_defaut']))
  1289. and $theme != _SPIP_THEME_PRIVE
  1290. ) {
  1291. array_unshift($themes, $theme);
  1292. } // placer le theme choisi en tete
  1293. }
  1294. return $themes;
  1295. }
  1296. function find_in_theme($file, $subdir = '', $include = false) {
  1297. static $themefiles = array();
  1298. if (isset($themefiles["$subdir$file"])) {
  1299. return $themefiles["$subdir$file"];
  1300. }
  1301. // on peut fournir une icone generique -xx.svg qui fera le job dans toutes les tailles, et qui est prioritaire sur le png
  1302. // si il y a un .svg a la bonne taille (-16.svg) a cote, on l'utilise en remplacement du -16.png
  1303. if (preg_match(',-(\d+)[.](png|gif|svg)$,', $file, $m)
  1304. and $file_svg_generique = substr($file,0, -strlen($m[0])) . "-xx.svg"
  1305. and $f = find_in_theme("$file_svg_generique")) {
  1306. if ($fsize = substr($f,0,-6) . $m[1] . ".svg" and file_exists($fsize)) {
  1307. return $themefiles["$subdir$file"] = $fsize;
  1308. }
  1309. else {
  1310. return $themefiles["$subdir$file"] = "$f?".$m[1]."px";
  1311. }
  1312. }
  1313. $themes = lister_themes_prives();
  1314. foreach ($themes as $theme) {
  1315. if ($f = find_in_path($file, "prive/themes/$theme/$subdir", $include)) {
  1316. return $themefiles["$subdir$file"] = $f;
  1317. }
  1318. }
  1319. spip_log("$file introuvable dans le theme prive " . reset($themes), 'theme');
  1320. return $themefiles["$subdir$file"] = "";
  1321. }
  1322. /**
  1323. * Cherche une image dans les dossiers d'images
  1324. *
  1325. * Cherche en priorité dans les thèmes d'image (prive/themes/X/images)
  1326. * et si la fonction n'en trouve pas, gère le renommage des icones (ex: 'supprimer' => 'del')
  1327. * de facon temporaire le temps de la migration, et cherche de nouveau.
  1328. *
  1329. * Si l'image n'est toujours pas trouvée, on la cherche dans les chemins,
  1330. * dans le répertoire défini par la constante `_NOM_IMG_PACK`
  1331. *
  1332. * @see find_in_theme()
  1333. * @see inc_icone_renommer_dist()
  1334. *
  1335. * @param string $icone
  1336. * Nom de l'icone cherchée
  1337. * @return string
  1338. * Chemin complet de l'icone depuis la racine si l'icone est trouée,
  1339. * sinon chaîne vide.
  1340. **/
  1341. function chemin_image($icone) {
  1342. static $icone_renommer;
  1343. if ($p = strpos($icone, '?')) {
  1344. $icone = substr($icone,0, $p);
  1345. }
  1346. // gerer le cas d'un double appel en evitant de refaire le travail inutilement
  1347. if (strpos($icone, "/") !== false and file_exists($icone)) {
  1348. return $icone;
  1349. }
  1350. // si c'est un nom d'image complet (article-24.png) essayer de le renvoyer direct
  1351. if (preg_match(',[.](png|gif|jpg|svg)$,', $icone) and $f = find_in_theme("images/$icone")) {
  1352. return $f;
  1353. }
  1354. // sinon passer par le module de renommage
  1355. if (is_null($icone_renommer)) {
  1356. $icone_renommer = charger_fonction('icone_renommer', 'inc', true);
  1357. }
  1358. if ($icone_renommer) {
  1359. list($icone, $fonction) = $icone_renommer($icone, "");
  1360. if (file_exists($icone)) {
  1361. return $icone;
  1362. }
  1363. }
  1364. return find_in_path($icone, _NOM_IMG_PACK);
  1365. }
  1366. //
  1367. // chercher un fichier $file dans le SPIP_PATH
  1368. // si on donne un sous-repertoire en 2e arg optionnel, il FAUT le / final
  1369. // si 3e arg vrai, on inclut si ce n'est fait.
  1370. $GLOBALS['path_sig'] = '';
  1371. $GLOBALS['path_files'] = null;
  1372. /**
  1373. * Recherche un fichier dans les chemins de SPIP (squelettes, plugins, core)
  1374. *
  1375. * Retournera le premier fichier trouvé (ayant la plus haute priorité donc),
  1376. * suivant l'ordre des chemins connus de SPIP.
  1377. *
  1378. * @api
  1379. * @see charger_fonction()
  1380. * @uses creer_chemin() Pour la liste des chemins.
  1381. * @example
  1382. * ```
  1383. * $f = find_in_path('css/perso.css');
  1384. * $f = find_in_path('perso.css', 'css');
  1385. * ```
  1386. *
  1387. * @param string $file
  1388. * Fichier recherché
  1389. * @param string $dirname
  1390. * Répertoire éventuel de recherche (est aussi extrait automatiquement de $file)
  1391. * @param bool|string $include
  1392. * - false : ne fait rien de plus
  1393. * - true : inclut le fichier (include_once)
  1394. * - 'require' : idem, mais tue le script avec une erreur si le fichier n'est pas trouvé.
  1395. * @return string|bool
  1396. * - string : chemin du fichier trouvé
  1397. * - false : fichier introuvable
  1398. **/
  1399. function find_in_path($file, $dirname = '', $include = false) {
  1400. static $dirs = array();
  1401. static $inc = array(); # cf http://trac.rezo.net/trac/spip/changeset/14743
  1402. static $c = '';
  1403. if (!$file and !strlen($file)) {
  1404. return false;
  1405. }
  1406. // on calcule le chemin si le dossier skel a change
  1407. if ($c != $GLOBALS['dossier_squelettes']) {
  1408. // assurer le non plantage lors de la montee de version :
  1409. $c = $GLOBALS['dossier_squelettes'];
  1410. creer_chemin(); // forcer un recalcul du chemin et la mise a jour de path_sig
  1411. }
  1412. if (isset($GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file])) {
  1413. if (!$GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file]) {
  1414. return false;
  1415. }
  1416. if ($include and !isset($inc[$dirname][$file])) {
  1417. include_once _ROOT_CWD . $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file];
  1418. $inc[$dirname][$file] = $inc[''][$dirname . $file] = true;
  1419. }
  1420. return $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file];
  1421. }
  1422. $a = strrpos($file, '/');
  1423. if ($a !== false) {
  1424. $dirname .= substr($file, 0, ++$a);
  1425. $file = substr($file, $a);
  1426. }
  1427. foreach (creer_chemin() as $dir) {
  1428. if (!isset($dirs[$a = $dir . $dirname])) {
  1429. $dirs[$a] = (is_dir(_ROOT_CWD . $a) || !$a);
  1430. }
  1431. if ($dirs[$a]) {
  1432. if (file_exists(_ROOT_CWD . ($a .= $file))) {
  1433. if ($include and !isset($inc[$dirname][$file])) {
  1434. include_once _ROOT_CWD . $a;
  1435. $inc[$dirname][$file] = $inc[''][$dirname . $file] = true;
  1436. }
  1437. if (!defined('_SAUVER_CHEMIN')) {
  1438. // si le chemin n'a pas encore ete charge, ne pas lever le flag, ne pas cacher
  1439. if (is_null($GLOBALS['path_files'])) {
  1440. return $a;
  1441. }
  1442. define('_SAUVER_CHEMIN', true);
  1443. }
  1444. return $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file] = $GLOBALS['path_files'][$GLOBALS['path_sig']][''][$dirname . $file] = $a;
  1445. }
  1446. }
  1447. }
  1448. if ($include) {
  1449. spip_log("include_spip $dirname$file non trouve");
  1450. if ($include === 'required') {
  1451. echo '<pre>',
  1452. "<strong>Erreur Fatale</strong><br />";
  1453. if (function_exists('debug_print_backtrace')) {
  1454. echo debug_print_backtrace();
  1455. }
  1456. echo '</pre>';
  1457. die("Erreur interne: ne peut inclure $dirname$file");
  1458. }
  1459. }
  1460. if (!defined('_SAUVER_CHEMIN')) {
  1461. // si le chemin n'a pas encore ete charge, ne pas lever le flag, ne pas cacher
  1462. if (is_null($GLOBALS['path_files'])) {
  1463. return false;
  1464. }
  1465. define('_SAUVER_CHEMIN', true);
  1466. }
  1467. return $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file] = $GLOBALS['path_files'][$GLOBALS['path_sig']][''][$dirname . $file] = false;
  1468. }
  1469. function clear_path_cache() {
  1470. $GLOBALS['path_files'] = array();
  1471. spip_unlink(_CACHE_CHEMIN);
  1472. }
  1473. function load_path_cache() {
  1474. // charger le path des plugins
  1475. if (@is_readable(_CACHE_PLUGINS_PATH)) {
  1476. include_once(_CACHE_PLUGINS_PATH);
  1477. }
  1478. $GLOBALS['path_files'] = array();
  1479. // si le visiteur est admin,
  1480. // on ne recharge pas le cache pour forcer sa mise a jour
  1481. if (
  1482. // la session n'est pas encore chargee a ce moment, on ne peut donc pas s'y fier
  1483. //AND (!isset($GLOBALS['visiteur_session']['statut']) OR $GLOBALS['visiteur_session']['statut']!='0minirezo')
  1484. // utiliser le cookie est un pis aller qui marche 'en general'
  1485. // on blinde par un second test au moment de la lecture de la session
  1486. // !isset($_COOKIE[$GLOBALS['cookie_prefix'].'_admin'])
  1487. // et en ignorant ce cache en cas de recalcul explicite
  1488. !_request('var_mode')
  1489. ) {
  1490. // on essaye de lire directement sans verrou pour aller plus vite
  1491. if ($contenu = spip_file_get_contents(_CACHE_CHEMIN)) {
  1492. // mais si semble corrompu on relit avec un verrou
  1493. if (!$GLOBALS['path_files'] = unserialize($contenu)) {
  1494. lire_fichier(_CACHE_CHEMIN, $contenu);
  1495. if (!$GLOBALS['path_files'] = unserialize($contenu)) {
  1496. $GLOBALS['path_files'] = array();
  1497. }
  1498. }
  1499. }
  1500. }
  1501. }
  1502. function save_path_cache() {
  1503. if (defined('_SAUVER_CHEMIN')
  1504. and _SAUVER_CHEMIN
  1505. ) {
  1506. ecrire_fichier(_CACHE_CHEMIN, serialize($GLOBALS['path_files']));
  1507. }
  1508. }
  1509. /**
  1510. * Trouve tous les fichiers du path correspondants à un pattern
  1511. *
  1512. * Pour un nom de fichier donné, ne retourne que le premier qui sera trouvé
  1513. * par un `find_in_path()`
  1514. *
  1515. * @api
  1516. * @uses creer_chemin()
  1517. * @uses preg_files()
  1518. *
  1519. * @param string $dir
  1520. * @param string $pattern
  1521. * @param bool $recurs
  1522. * @return array
  1523. */
  1524. function find_all_in_path($dir, $pattern, $recurs = false) {
  1525. $liste_fichiers = array();
  1526. $maxfiles = 10000;
  1527. // cas borderline si dans mes_options on appelle redirige_par_entete qui utilise _T et charge un fichier de langue
  1528. // on a pas encore inclus flock.php
  1529. if (!function_exists('preg_files')) {
  1530. include_once _ROOT_RESTREINT . 'inc/flock.php';
  1531. }
  1532. // Parcourir le chemin
  1533. foreach (creer_chemin() as $d) {
  1534. $f = $d . $dir;
  1535. if (@is_dir($f)) {
  1536. $liste = preg_files($f, $pattern, $maxfiles - count($liste_fichiers), $recurs === true ? array() : $recurs);
  1537. foreach ($liste as $chemin) {
  1538. $nom = basename($chemin);
  1539. // ne prendre que les fichiers pas deja trouves
  1540. // car find_in_path prend le premier qu'il trouve,
  1541. // les autres sont donc masques
  1542. if (!isset($liste_fichiers[$nom])) {
  1543. $liste_fichiers[$nom] = $chemin;
  1544. }
  1545. }
  1546. }
  1547. }
  1548. return $liste_fichiers;
  1549. }
  1550. /**
  1551. * Prédicat sur les scripts de ecrire qui n'authentifient pas par cookie
  1552. * et beneficient d'une exception
  1553. *
  1554. * @param string $nom
  1555. * @param bool $strict
  1556. * @return bool
  1557. */
  1558. function autoriser_sans_cookie($nom, $strict = false) {
  1559. static $autsanscookie = array('install', 'base_repair');
  1560. if (in_array($nom, $autsanscookie)) {
  1561. if (test_espace_prive()){
  1562. include_spip('base/connect_sql');
  1563. if (!$strict or !spip_connect()){
  1564. return true;
  1565. }
  1566. }
  1567. }
  1568. return false;
  1569. }
  1570. /**
  1571. * Fonction codant et décodant les URLs des objets SQL mis en page par SPIP
  1572. *
  1573. * @api
  1574. * @param string $id
  1575. * numero de la cle primaire si nombre, URL a decoder si pas numerique
  1576. * @param string $entite
  1577. * surnom de la table SQL (donne acces au nom de cle primaire)
  1578. * @param string $args
  1579. * query_string a placer apres cle=$id&....
  1580. * @param string $ancre
  1581. * ancre a mettre a la fin de l'URL a produire
  1582. * @param bool|string $public
  1583. * produire l'URL publique ou privee (par defaut: selon espace)
  1584. * si string : serveur de base de donnee (nom du connect)
  1585. * @param string $type
  1586. * fichier dans le repertoire ecrire/urls determinant l'apparence
  1587. * @return string|array
  1588. * url codee ou fonction de decodage
  1589. * array : derogatoire, la fonction d'url retourne (objet,id_objet) utilises par nettoyer_raccourcis_typo() pour generer un lien titre
  1590. * (cas des raccourcis personalises [->spip20] : il faut implementer une fonction generer_url_spip et une fonction generer_url_ecrire_spip)
  1591. */
  1592. function generer_url_entite($id = '', $entite = '', $args = '', $ancre = '', $public = null, $type = null) {
  1593. if ($public === null) {
  1594. $public = !test_espace_prive();
  1595. }
  1596. $entite = objet_type($entite); // cas particulier d'appels sur objet/id_objet...
  1597. if (!$public) {
  1598. if (!$entite) {
  1599. return '';
  1600. }
  1601. if (!function_exists('generer_url_ecrire_objet')) {
  1602. include_spip('inc/urls');
  1603. }
  1604. $res = generer_url_ecrire_objet($entite, $id, $args, $ancre, false);
  1605. } else {
  1606. if ($type === null) {
  1607. $type = (isset($GLOBALS['type_urls']))
  1608. ? $GLOBALS['type_urls'] // pour surcharge via fichier d'options
  1609. : ((isset($GLOBALS['meta']['type_urls'])) // sinon la config url_etendues
  1610. ? ($GLOBALS['meta']['type_urls']) : "page"); // sinon type "page" par défaut
  1611. }
  1612. $f = charger_fonction($type, 'urls', true);
  1613. // se rabattre sur les urls page si les urls perso non dispo
  1614. if (!$f) {
  1615. $f = charger_fonction('page', 'urls', true);
  1616. }
  1617. // si $entite='', on veut la fonction de passage URL ==> id
  1618. // sinon on veut effectuer le passage id ==> URL
  1619. if (!$entite) {
  1620. return $f;
  1621. }
  1622. // mais d'abord il faut tester le cas des urls sur une
  1623. // base distante
  1624. if (is_string($public)
  1625. and $g = charger_fonction('connect', 'urls', true)
  1626. ) {
  1627. $f = $g;
  1628. }
  1629. $res = $f(intval($id), $entite, $args, $ancre, $public);
  1630. }
  1631. if ($res) {
  1632. return $res;
  1633. }
  1634. // Sinon c'est un raccourci ou compat SPIP < 2
  1635. if (!function_exists($f = 'generer_url_' . $entite)) {
  1636. if (!function_exists($f .= '_dist')) {
  1637. $f = '';
  1638. }
  1639. }
  1640. if ($f) {
  1641. $url = $f($id, $args, $ancre);
  1642. if (strlen($args)) {
  1643. $url .= strstr($url, '?')
  1644. ? '&amp;' . $args
  1645. : '?' . $args;
  1646. }
  1647. return $url;
  1648. }
  1649. // On a ete gentil mais la ....
  1650. spip_log("generer_url_entite: entite $entite ($f) inconnue $type $public");
  1651. return '';
  1652. }
  1653. function generer_url_ecrire_entite_edit($id, $entite, $args = '', $ancre = '') {
  1654. $exec = objet_info($entite, 'url_edit');
  1655. $url = generer_url_ecrire($exec, $args);
  1656. if (intval($id)) {
  1657. $url = parametre_url($url, id_table_objet($entite), $id);
  1658. } else {
  1659. $url = parametre_url($url, 'new', 'oui');
  1660. }
  1661. if ($ancre) {
  1662. $url = ancre_url($url, $ancre);
  1663. }
  1664. return $url;
  1665. }
  1666. // http://code.spip.net/@urls_connect_dist
  1667. function urls_connect_dist($i, &$entite, $args = '', $ancre = '', $public = null) {
  1668. include_spip('base/connect_sql');
  1669. $id_type = id_table_objet($entite, $public);
  1670. return _DIR_RACINE . get_spip_script('./')
  1671. . "?" . _SPIP_PAGE . "=$entite&$id_type=$i&connect=$public"
  1672. . (!$args ? '' : "&$args")
  1673. . (!$ancre ? '' : "#$ancre");
  1674. }
  1675. /**
  1676. * Transformer les caractères utf8 d'une URL (farsi par exemple) selon la RFC 1738
  1677. *
  1678. * @param string $url
  1679. * @return string
  1680. */
  1681. function urlencode_1738($url) {
  1682. if (preg_match(',[^\x00-\x7E],sS', $url)) {
  1683. $uri = '';
  1684. for ($i = 0; $i < strlen($url); $i++) {
  1685. if (ord($a = $url[$i]) > 127) {
  1686. $a = rawurlencode($a);
  1687. }
  1688. $uri .= $a;
  1689. }
  1690. $url = $uri;
  1691. }
  1692. return quote_amp($url);
  1693. }
  1694. // http://code.spip.net/@generer_url_entite_absolue
  1695. function generer_url_entite_absolue($id = '', $entite = '', $args = '', $ancre = '', $connect = null) {
  1696. if (!$connect) {
  1697. $connect = true;
  1698. }
  1699. $h = generer_url_entite($id, $entite, $args, $ancre, $connect);
  1700. if (!preg_match(',^\w+:,', $h)) {
  1701. include_spip('inc/filtres_mini');
  1702. $h = url_absolue($h);
  1703. }
  1704. return $h;
  1705. }
  1706. /**
  1707. * Tester qu'une variable d'environnement est active
  1708. *
  1709. * Sur certains serveurs, la valeur 'Off' tient lieu de false dans certaines
  1710. * variables d'environnement comme `$_SERVER['HTTPS']` ou `ini_get('display_errors')`
  1711. *
  1712. * @param string|bool $truc
  1713. * La valeur de la variable d'environnement
  1714. * @return bool
  1715. * true si la valeur est considérée active ; false sinon.
  1716. **/
  1717. function test_valeur_serveur($truc) {
  1718. if (!$truc) {
  1719. return false;
  1720. }
  1721. return (strtolower($truc) !== 'off');
  1722. }
  1723. //
  1724. // Fonctions de fabrication des URL des scripts de Spip
  1725. //
  1726. /**
  1727. * Calcule l'url de base du site
  1728. *
  1729. * Calcule l'URL de base du site, en priorité sans se fier à la méta (adresse_site) qui
  1730. * peut être fausse (sites avec plusieurs noms d’hôtes, déplacements, erreurs).
  1731. * En dernier recours, lorsqu'on ne trouve rien, on utilise adresse_site comme fallback.
  1732. *
  1733. * @note
  1734. * La globale `$profondeur_url` doit être initialisée de manière à
  1735. * indiquer le nombre de sous-répertoires de l'url courante par rapport à la
  1736. * racine de SPIP : par exemple, sur ecrire/ elle vaut 1, sur sedna/ 1, et à
  1737. * la racine 0. Sur url/perso/ elle vaut 2
  1738. *
  1739. * @param int|boo|array $profondeur
  1740. * - si non renseignée : retourne l'url pour la profondeur $GLOBALS['profondeur_url']
  1741. * - si int : indique que l'on veut l'url pour la profondeur indiquée
  1742. * - si bool : retourne le tableau static complet
  1743. * - si array : réinitialise le tableau static complet avec la valeur fournie
  1744. * @return string|array
  1745. */
  1746. function url_de_base($profondeur = null) {
  1747. static $url = array();
  1748. if (is_array($profondeur)) {
  1749. return $url = $profondeur;
  1750. }
  1751. if ($profondeur === false) {
  1752. return $url;
  1753. }
  1754. if (is_null($profondeur)) {
  1755. $profondeur = $GLOBALS['profondeur_url'];
  1756. }
  1757. if (isset($url[$profondeur])) {
  1758. return $url[$profondeur];
  1759. }
  1760. $http = 'http';
  1761. if (
  1762. isset($_SERVER["SCRIPT_URI"])
  1763. and substr($_SERVER["SCRIPT_URI"], 0, 5) == 'https'
  1764. ) {
  1765. $http = 'https';
  1766. } elseif (
  1767. isset($_SERVER['HTTPS'])
  1768. and test_valeur_serveur($_SERVER['HTTPS'])
  1769. ) {
  1770. $http = 'https';
  1771. }
  1772. // note : HTTP_HOST contient le :port si necessaire
  1773. $host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : null;
  1774. // si on n'a pas trouvé d'hôte du tout, en dernier recours on utilise adresse_site comme fallback
  1775. if (is_null($host) and isset($GLOBALS['meta']['adresse_site'])) {
  1776. $host = $GLOBALS['meta']['adresse_site'];
  1777. if ($scheme = parse_url($host, PHP_URL_SCHEME)) {
  1778. $http = $scheme;
  1779. $host = str_replace("{$scheme}://", '', $host);
  1780. }
  1781. }
  1782. if (isset($_SERVER['SERVER_PORT'])
  1783. and $port = $_SERVER['SERVER_PORT']
  1784. and strpos($host, ":") == false
  1785. ) {
  1786. if (!defined('_PORT_HTTP_STANDARD')) {
  1787. define('_PORT_HTTP_STANDARD', '80');
  1788. }
  1789. if (!defined('_PORT_HTTPS_STANDARD')) {
  1790. define('_PORT_HTTPS_STANDARD', '443');
  1791. }
  1792. if ($http == "http" and !in_array($port, explode(',', _PORT_HTTP_STANDARD))) {
  1793. $host .= ":$port";
  1794. }
  1795. if ($http == "https" and !in_array($port, explode(',', _PORT_HTTPS_STANDARD))) {
  1796. $host .= ":$port";
  1797. }
  1798. }
  1799. if (!$GLOBALS['REQUEST_URI']) {
  1800. if (isset($_SERVER['REQUEST_URI'])) {
  1801. $GLOBALS['REQUEST_URI'] = $_SERVER['REQUEST_URI'];
  1802. } else {
  1803. $GLOBALS['REQUEST_URI'] = (php_sapi_name() !== 'cli') ? $_SERVER['PHP_SELF'] : '';
  1804. if (!empty($_SERVER['QUERY_STRING'])
  1805. and !strpos($_SERVER['REQUEST_URI'], '?')
  1806. ) {
  1807. $GLOBALS['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING'];
  1808. }
  1809. }
  1810. }
  1811. $url[$profondeur] = url_de_($http, $host, $GLOBALS['REQUEST_URI'], $profondeur);
  1812. return $url[$profondeur];
  1813. }
  1814. /**
  1815. * fonction testable de construction d'une url appelee par url_de_base()
  1816. *
  1817. * @param string $http
  1818. * @param string $host
  1819. * @param string $request
  1820. * @param int $prof
  1821. * @return string
  1822. */
  1823. function url_de_($http, $host, $request, $prof = 0) {
  1824. $prof = max($prof, 0);
  1825. $myself = ltrim($request, '/');
  1826. # supprimer la chaine de GET
  1827. list($myself) = explode('?', $myself);
  1828. // vieux mode HTTP qui envoie après le nom de la methode l'URL compléte
  1829. // protocole, "://", nom du serveur avant le path dans _SERVER["REQUEST_URI"]
  1830. if (strpos($myself,'://') !== false) {
  1831. $myself = explode('://',$myself);
  1832. array_shift($myself);
  1833. $myself = implode('://',$myself);
  1834. $myself = explode('/',$myself);
  1835. array_shift($myself);
  1836. $myself = implode('/',$myself);
  1837. }
  1838. $url = join('/', array_slice(explode('/', $myself), 0, -1 - $prof)) . '/';
  1839. $url = $http . '://' . rtrim($host, '/') . '/' . ltrim($url, '/');
  1840. return $url;
  1841. }
  1842. // Pour une redirection, la liste des arguments doit etre separee par "&"
  1843. // Pour du code XHTML, ca doit etre &amp;
  1844. // Bravo au W3C qui n'a pas ete capable de nous eviter ca
  1845. // faute de separer proprement langage et meta-langage
  1846. // Attention, X?y=z et "X/?y=z" sont completement differents!
  1847. // http://httpd.apache.org/docs/2.0/mod/mod_dir.html
  1848. /**
  1849. * Crée une URL vers un script de l'espace privé
  1850. *
  1851. * @example
  1852. * ```
  1853. * generer_url_ecrire('admin_plugin')
  1854. * ```
  1855. *
  1856. * @param string $script
  1857. * Nom de la page privée (xx dans exec=xx)
  1858. * @param string $args
  1859. * Arguments à transmettre, tel que `arg1=yy&arg2=zz`
  1860. * @param bool $no_entities
  1861. * Si false : transforme les `&` en `&amp;`
  1862. * @param bool|string $rel
  1863. * URL relative ?
  1864. *
  1865. * - false : l’URL sera complète et contiendra l’URL du site
  1866. * - true : l’URL sera relavive.
  1867. * - string : on transmet l'url à la fonction
  1868. * @return string URL
  1869. **/
  1870. function generer_url_ecrire($script = '', $args = "", $no_entities = false, $rel = false) {
  1871. if (!$rel) {
  1872. $rel = url_de_base() . _DIR_RESTREINT_ABS . _SPIP_ECRIRE_SCRIPT;
  1873. } else {
  1874. if (!is_string($rel)) {
  1875. $rel = _DIR_RESTREINT ? _DIR_RESTREINT :
  1876. ('./' . _SPIP_ECRIRE_SCRIPT);
  1877. }
  1878. }
  1879. list($script, $ancre) = array_pad(explode('#', $script), 2, null);
  1880. if ($script and ($script <> 'accueil' or $rel)) {
  1881. $args = "?exec=$script" . (!$args ? '' : "&$args");
  1882. } elseif ($args) {
  1883. $args = "?$args";
  1884. }
  1885. if ($ancre) {
  1886. $args .= "#$ancre";
  1887. }
  1888. return $rel . ($no_entities ? $args : str_replace('&', '&amp;', $args));
  1889. }
  1890. //
  1891. // Adresse des scripts publics (a passer dans inc-urls...)
  1892. //
  1893. /**
  1894. * Retourne le nom du fichier d'exécution de SPIP
  1895. *
  1896. * @see _SPIP_SCRIPT
  1897. * @note
  1898. * Detecter le fichier de base, a la racine, comme etant spip.php ou ''
  1899. * dans le cas de '', un $default = './' peut servir (comme dans urls/page.php)
  1900. *
  1901. * @param string $default
  1902. * Script par défaut
  1903. * @return string
  1904. * Nom du fichier (constante _SPIP_SCRIPT), sinon nom par défaut
  1905. **/
  1906. function get_spip_script($default = '') {
  1907. # cas define('_SPIP_SCRIPT', '');
  1908. if (_SPIP_SCRIPT) {
  1909. return _SPIP_SCRIPT;
  1910. } else {
  1911. return $default;
  1912. }
  1913. }
  1914. /**
  1915. * Crée une URL vers une page publique de SPIP
  1916. *
  1917. * @example
  1918. * ```
  1919. * generer_url_public("rubrique","id_rubrique=$id_rubrique")
  1920. * ```
  1921. *
  1922. * @param string $script
  1923. * Nom de la page
  1924. * @param string|array $args
  1925. * Arguments à transmettre a l'URL,
  1926. * soit sous la forme d'un string tel que `arg1=yy&arg2=zz`
  1927. * soit sous la forme d'un array tel que array( `arg1` => `yy`, `arg2` => `zz` )
  1928. * @param bool $no_entities
  1929. * Si false : transforme les `&` en `&amp;`
  1930. * @param bool $rel
  1931. * URL relative ?
  1932. *
  1933. * - false : l’URL sera complète et contiendra l’URL du site
  1934. * - true : l’URL sera relavive.
  1935. * @param string $action
  1936. * - Fichier d'exécution public (spip.php par défaut)
  1937. * @return string URL
  1938. **/
  1939. function generer_url_public($script = '', $args = "", $no_entities = false, $rel = true, $action = '') {
  1940. // si le script est une action (spip_pass, spip_inscription),
  1941. // standardiser vers la nouvelle API
  1942. if (!$action) {
  1943. $action = get_spip_script();
  1944. }
  1945. if ($script) {
  1946. $action = parametre_url($action, _SPIP_PAGE, $script, '&');
  1947. }
  1948. if ($args) {
  1949. if (is_array($args)) {
  1950. $r = '';
  1951. foreach ($args as $k => $v) {
  1952. $r .= '&' . $k . '=' . $v;
  1953. }
  1954. $args = substr($r, 1);
  1955. }
  1956. $action .=
  1957. (strpos($action, '?') !== false ? '&' : '?') . $args;
  1958. }
  1959. if (!$no_entities) {
  1960. $action = quote_amp($action);
  1961. }
  1962. // ne pas generer une url avec /./?page= en cas d'url absolue et de _SPIP_SCRIPT vide
  1963. return ($rel ? _DIR_RACINE . $action : rtrim(url_de_base(), '/') . preg_replace(",^/[.]/,", "/", "/$action"));
  1964. }
  1965. // http://code.spip.net/@generer_url_prive
  1966. function generer_url_prive($script, $args = "", $no_entities = false) {
  1967. return generer_url_public($script, $args, $no_entities, false, _DIR_RESTREINT_ABS . 'prive.php');
  1968. }
  1969. // Pour les formulaires en methode POST,
  1970. // mettre le nom du script a la fois en input-hidden et dans le champ action:
  1971. // 1) on peut ainsi memoriser le signet comme si c'etait un GET
  1972. // 2) ca suit http://en.wikipedia.org/wiki/Representational_State_Transfer
  1973. /**
  1974. * Retourne un formulaire (POST par défaut) vers un script exec
  1975. * de l’interface privée
  1976. *
  1977. * @param string $script
  1978. * Nom de la page exec
  1979. * @param string $corps
  1980. * Contenu du formulaire
  1981. * @param string $atts
  1982. * Si présent, remplace les arguments par défaut (method=post) par ceux indiqués
  1983. * @param string $submit
  1984. * Si indiqué, un bouton de soumission est créé avec texte sa valeur.
  1985. * @return string
  1986. * Code HTML du formulaire
  1987. **/
  1988. function generer_form_ecrire($script, $corps, $atts = '', $submit = '') {
  1989. $script1 = explode('&', $script);
  1990. $script1 = reset($script1);
  1991. return "<form action='"
  1992. . ($script ? generer_url_ecrire($script) : '')
  1993. . "' "
  1994. . ($atts ? $atts : " method='post'")
  1995. . "><div>\n"
  1996. . "<input type='hidden' name='exec' value='$script1' />"
  1997. . $corps
  1998. . (!$submit ? '' :
  1999. ("<div style='text-align: " . $GLOBALS['spip_lang_right'] . "'><input class='fondo' type='submit' value=\"" . entites_html($submit) . "\" /></div>"))
  2000. . "</div></form>\n";
  2001. }
  2002. /**
  2003. * Générer un formulaire pour lancer une action vers $script
  2004. *
  2005. * Attention, JS/Ajax n'aime pas le melange de param GET/POST
  2006. * On n'applique pas la recommandation ci-dessus pour les scripts publics
  2007. * qui ne sont pas destines a etre mis en signets
  2008. *
  2009. * @param string $script
  2010. * @param string $corps
  2011. * @param string $atts
  2012. * @param bool $public
  2013. * @return string
  2014. */
  2015. function generer_form_action($script, $corps, $atts = '', $public = false) {
  2016. // si l'on est dans l'espace prive, on garde dans l'url
  2017. // l'exec a l'origine de l'action, qui permet de savoir si il est necessaire
  2018. // ou non de proceder a l'authentification (cas typique de l'install par exemple)
  2019. $h = (_DIR_RACINE and !$public)
  2020. ? generer_url_ecrire(_request('exec'))
  2021. : generer_url_public();
  2022. return "\n<form action='" .
  2023. $h .
  2024. "'" .
  2025. $atts .
  2026. ">\n" .
  2027. "<div>" .
  2028. "\n<input type='hidden' name='action' value='$script' />" .
  2029. $corps .
  2030. "</div></form>";
  2031. }
  2032. /**
  2033. * Créer une URL
  2034. *
  2035. * @param string $script
  2036. * Nom du script à exécuter
  2037. * @param string $args
  2038. * Arguments à transmettre a l'URL sous la forme `arg1=yy&arg2=zz`
  2039. * @param bool $no_entities
  2040. * Si false : transforme les & en &amp;
  2041. * @param boolean $public
  2042. * URL relative ? false : l’URL sera complète et contiendra l’URL du site.
  2043. * true : l’URL sera relative.
  2044. * @return string
  2045. * URL
  2046. */
  2047. function generer_url_action($script, $args = "", $no_entities = false, $public = false) {
  2048. // si l'on est dans l'espace prive, on garde dans l'url
  2049. // l'exec a l'origine de l'action, qui permet de savoir si il est necessaire
  2050. // ou non de proceder a l'authentification (cas typique de l'install par exemple)
  2051. $url = (_DIR_RACINE and !$public)
  2052. ? generer_url_ecrire(_request('exec'))
  2053. : generer_url_public('', '', false, false);
  2054. $url = parametre_url($url, 'action', $script);
  2055. if ($args) {
  2056. $url .= quote_amp('&' . $args);
  2057. }
  2058. if ($no_entities) {
  2059. $url = str_replace('&amp;', '&', $url);
  2060. }
  2061. return $url;
  2062. }
  2063. /**
  2064. * Fonction d'initialisation groupée pour compatibilité ascendante
  2065. *
  2066. * @param string $pi Répertoire permanent inaccessible
  2067. * @param string $pa Répertoire permanent accessible
  2068. * @param string $ti Répertoire temporaire inaccessible
  2069. * @param string $ta Répertoire temporaire accessible
  2070. */
  2071. function spip_initialisation($pi = null, $pa = null, $ti = null, $ta = null) {
  2072. spip_initialisation_core($pi, $pa, $ti, $ta);
  2073. spip_initialisation_suite();
  2074. }
  2075. /**
  2076. * Fonction d'initialisation, appellée dans inc_version ou mes_options
  2077. *
  2078. * Elle définit les répertoires et fichiers non partageables
  2079. * et indique dans $test_dirs ceux devant être accessibles en écriture
  2080. * mais ne touche pas à cette variable si elle est déjà définie
  2081. * afin que mes_options.php puisse en spécifier d'autres.
  2082. *
  2083. * Elle définit ensuite les noms des fichiers et les droits.
  2084. * Puis simule un register_global=on sécurisé.
  2085. *
  2086. * @param string $pi Répertoire permanent inaccessible
  2087. * @param string $pa Répertoire permanent accessible
  2088. * @param string $ti Répertoire temporaire inaccessible
  2089. * @param string $ta Répertoire temporaire accessible
  2090. */
  2091. function spip_initialisation_core($pi = null, $pa = null, $ti = null, $ta = null) {
  2092. static $too_late = 0;
  2093. if ($too_late++) {
  2094. return;
  2095. }
  2096. // Declaration des repertoires
  2097. // le nom du repertoire plugins/ activables/desactivables
  2098. if (!defined('_DIR_PLUGINS')) {
  2099. define('_DIR_PLUGINS', _DIR_RACINE . "plugins/");
  2100. }
  2101. // le nom du repertoire des extensions/ permanentes du core, toujours actives
  2102. if (!defined('_DIR_PLUGINS_DIST')) {
  2103. define('_DIR_PLUGINS_DIST', _DIR_RACINE . "plugins-dist/");
  2104. }
  2105. // le nom du repertoire des librairies
  2106. if (!defined('_DIR_LIB')) {
  2107. define('_DIR_LIB', _DIR_RACINE . "lib/");
  2108. }
  2109. if (!defined('_DIR_IMG')) {
  2110. define('_DIR_IMG', $pa);
  2111. }
  2112. if (!defined('_DIR_LOGOS')) {
  2113. define('_DIR_LOGOS', $pa);
  2114. }
  2115. if (!defined('_DIR_IMG_ICONES')) {
  2116. define('_DIR_IMG_ICONES', _DIR_LOGOS . "icones/");
  2117. }
  2118. if (!defined('_DIR_DUMP')) {
  2119. define('_DIR_DUMP', $ti . "dump/");
  2120. }
  2121. if (!defined('_DIR_SESSIONS')) {
  2122. define('_DIR_SESSIONS', $ti . "sessions/");
  2123. }
  2124. if (!defined('_DIR_TRANSFERT')) {
  2125. define('_DIR_TRANSFERT', $ti . "upload/");
  2126. }
  2127. if (!defined('_DIR_CACHE')) {
  2128. define('_DIR_CACHE', $ti . "cache/");
  2129. }
  2130. if (!defined('_DIR_CACHE_XML')) {
  2131. define('_DIR_CACHE_XML', _DIR_CACHE . "xml/");
  2132. }
  2133. if (!defined('_DIR_SKELS')) {
  2134. define('_DIR_SKELS', _DIR_CACHE . "skel/");
  2135. }
  2136. if (!defined('_DIR_AIDE')) {
  2137. define('_DIR_AIDE', _DIR_CACHE . "aide/");
  2138. }
  2139. if (!defined('_DIR_TMP')) {
  2140. define('_DIR_TMP', $ti);
  2141. }
  2142. if (!defined('_DIR_VAR')) {
  2143. define('_DIR_VAR', $ta);
  2144. }
  2145. if (!defined('_DIR_ETC')) {
  2146. define('_DIR_ETC', $pi);
  2147. }
  2148. if (!defined('_DIR_CONNECT')) {
  2149. define('_DIR_CONNECT', $pi);
  2150. }
  2151. if (!defined('_DIR_CHMOD')) {
  2152. define('_DIR_CHMOD', $pi);
  2153. }
  2154. if (!isset($GLOBALS['test_dirs']))
  2155. // Pas $pi car il est bon de le mettre hors ecriture apres intstall
  2156. // il sera rajoute automatiquement si besoin a l'etape 2 de l'install
  2157. {
  2158. $GLOBALS['test_dirs'] = array($pa, $ti, $ta);
  2159. }
  2160. // Declaration des fichiers
  2161. if (!defined('_CACHE_PLUGINS_PATH')) {
  2162. define('_CACHE_PLUGINS_PATH', _DIR_CACHE . "charger_plugins_chemins.php");
  2163. }
  2164. if (!defined('_CACHE_PLUGINS_OPT')) {
  2165. define('_CACHE_PLUGINS_OPT', _DIR_CACHE . "charger_plugins_options.php");
  2166. }
  2167. if (!defined('_CACHE_PLUGINS_FCT')) {
  2168. define('_CACHE_PLUGINS_FCT', _DIR_CACHE . "charger_plugins_fonctions.php");
  2169. }
  2170. if (!defined('_CACHE_PIPELINES')) {
  2171. define('_CACHE_PIPELINES', _DIR_CACHE . "charger_pipelines.php");
  2172. }
  2173. if (!defined('_CACHE_CHEMIN')) {
  2174. define('_CACHE_CHEMIN', _DIR_CACHE . "chemin.txt");
  2175. }
  2176. # attention .php obligatoire pour ecrire_fichier_securise
  2177. if (!defined('_FILE_META')) {
  2178. define('_FILE_META', $ti . 'meta_cache.php');
  2179. }
  2180. if (!defined('_DIR_LOG')) {
  2181. define('_DIR_LOG', _DIR_TMP . 'log/');
  2182. }
  2183. if (!defined('_FILE_LOG')) {
  2184. define('_FILE_LOG', 'spip');
  2185. }
  2186. if (!defined('_FILE_LOG_SUFFIX')) {
  2187. define('_FILE_LOG_SUFFIX', '.log');
  2188. }
  2189. // Le fichier de connexion a la base de donnees
  2190. // tient compte des anciennes versions (inc_connect...)
  2191. if (!defined('_FILE_CONNECT_INS')) {
  2192. define('_FILE_CONNECT_INS', 'connect');
  2193. }
  2194. if (!defined('_FILE_CONNECT')) {
  2195. define('_FILE_CONNECT',
  2196. (@is_readable($f = _DIR_CONNECT . _FILE_CONNECT_INS . '.php') ? $f
  2197. : (@is_readable($f = _DIR_RESTREINT . 'inc_connect.php') ? $f
  2198. : false)));
  2199. }
  2200. // Le fichier de reglages des droits
  2201. if (!defined('_FILE_CHMOD_INS')) {
  2202. define('_FILE_CHMOD_INS', 'chmod');
  2203. }
  2204. if (!defined('_FILE_CHMOD')) {
  2205. define('_FILE_CHMOD',
  2206. (@is_readable($f = _DIR_CHMOD . _FILE_CHMOD_INS . '.php') ? $f
  2207. : false));
  2208. }
  2209. if (!defined('_FILE_LDAP')) {
  2210. define('_FILE_LDAP', 'ldap.php');
  2211. }
  2212. if (!defined('_FILE_TMP_SUFFIX')) {
  2213. define('_FILE_TMP_SUFFIX', '.tmp.php');
  2214. }
  2215. if (!defined('_FILE_CONNECT_TMP')) {
  2216. define('_FILE_CONNECT_TMP', _DIR_CONNECT . _FILE_CONNECT_INS . _FILE_TMP_SUFFIX);
  2217. }
  2218. if (!defined('_FILE_CHMOD_TMP')) {
  2219. define('_FILE_CHMOD_TMP', _DIR_CHMOD . _FILE_CHMOD_INS . _FILE_TMP_SUFFIX);
  2220. }
  2221. // Definition des droits d'acces en ecriture
  2222. if (!defined('_SPIP_CHMOD') and _FILE_CHMOD) {
  2223. include_once _FILE_CHMOD;
  2224. }
  2225. // Se mefier des fichiers mal remplis!
  2226. if (!defined('_SPIP_CHMOD')) {
  2227. define('_SPIP_CHMOD', 0777);
  2228. }
  2229. if (!defined('_DEFAULT_CHARSET')) {
  2230. /** Le charset par défaut lors de l'installation */
  2231. define('_DEFAULT_CHARSET', 'utf-8');
  2232. }
  2233. if (!defined('_ROOT_PLUGINS')) {
  2234. define('_ROOT_PLUGINS', _ROOT_RACINE . "plugins/");
  2235. }
  2236. if (!defined('_ROOT_PLUGINS_DIST')) {
  2237. define('_ROOT_PLUGINS_DIST', _ROOT_RACINE . "plugins-dist/");
  2238. }
  2239. if (!defined('_ROOT_PLUGINS_SUPPL') && defined('_DIR_PLUGINS_SUPPL') && _DIR_PLUGINS_SUPPL) {
  2240. define('_ROOT_PLUGINS_SUPPL', _ROOT_RACINE . str_replace(_DIR_RACINE, '', _DIR_PLUGINS_SUPPL));
  2241. }
  2242. // La taille des Log
  2243. if (!defined('_MAX_LOG')) {
  2244. define('_MAX_LOG', 100);
  2245. }
  2246. // Sommes-nous dans l'empire du Mal ?
  2247. // (ou sous le signe du Pingouin, ascendant GNU ?)
  2248. if (isset($_SERVER['SERVER_SOFTWARE']) and strpos($_SERVER['SERVER_SOFTWARE'], '(Win') !== false) {
  2249. if (!defined('_OS_SERVEUR')) {
  2250. define('_OS_SERVEUR', 'windows');
  2251. }
  2252. if (!defined('_SPIP_LOCK_MODE')) {
  2253. define('_SPIP_LOCK_MODE', 1);
  2254. } // utiliser le flock php
  2255. } else {
  2256. if (!defined('_OS_SERVEUR')) {
  2257. define('_OS_SERVEUR', '');
  2258. }
  2259. if (!defined('_SPIP_LOCK_MODE')) {
  2260. define('_SPIP_LOCK_MODE', 1);
  2261. } // utiliser le flock php
  2262. #if (!defined('_SPIP_LOCK_MODE')) define('_SPIP_LOCK_MODE',2); // utiliser le nfslock de spip mais link() est tres souvent interdite
  2263. }
  2264. // Langue par defaut
  2265. if (!defined('_LANGUE_PAR_DEFAUT')) {
  2266. define('_LANGUE_PAR_DEFAUT', 'fr');
  2267. }
  2268. //
  2269. // Module de lecture/ecriture/suppression de fichiers utilisant flock()
  2270. // (non surchargeable en l'etat ; attention si on utilise include_spip()
  2271. // pour le rendre surchargeable, on va provoquer un reecriture
  2272. // systematique du noyau ou une baisse de perfs => a etudier)
  2273. include_once _ROOT_RESTREINT . 'inc/flock.php';
  2274. // charger tout de suite le path et son cache
  2275. load_path_cache();
  2276. // *********** traiter les variables ************
  2277. //
  2278. // Securite
  2279. //
  2280. // Ne pas se faire manger par un bug php qui accepte ?GLOBALS[truc]=toto
  2281. if (isset($_REQUEST['GLOBALS'])) {
  2282. die();
  2283. }
  2284. // nettoyer les magic quotes \' et les caracteres nuls %00
  2285. spip_desinfecte($_GET);
  2286. spip_desinfecte($_POST);
  2287. spip_desinfecte($_COOKIE);
  2288. spip_desinfecte($_REQUEST);
  2289. // appliquer le cookie_prefix
  2290. if ($GLOBALS['cookie_prefix'] != 'spip') {
  2291. include_spip('inc/cookie');
  2292. recuperer_cookies_spip($GLOBALS['cookie_prefix']);
  2293. }
  2294. //
  2295. // Capacites php (en fonction de la version)
  2296. //
  2297. $GLOBALS['flag_ob'] = (function_exists("ob_start")
  2298. && function_exists("ini_get")
  2299. && !strstr(@ini_get('disable_functions'), 'ob_'));
  2300. $GLOBALS['flag_sapi_name'] = function_exists("php_sapi_name");
  2301. $GLOBALS['flag_get_cfg_var'] = (@get_cfg_var('error_reporting') != "");
  2302. $GLOBALS['flag_upload'] = (!$GLOBALS['flag_get_cfg_var'] ||
  2303. (get_cfg_var('upload_max_filesize') > 0));
  2304. // Compatibilite avec serveurs ne fournissant pas $REQUEST_URI
  2305. if (isset($_SERVER['REQUEST_URI'])) {
  2306. $GLOBALS['REQUEST_URI'] = $_SERVER['REQUEST_URI'];
  2307. } else {
  2308. $GLOBALS['REQUEST_URI'] = (php_sapi_name() !== 'cli') ? $_SERVER['PHP_SELF'] : '';
  2309. if (!empty($_SERVER['QUERY_STRING'])
  2310. and !strpos($_SERVER['REQUEST_URI'], '?')
  2311. ) {
  2312. $GLOBALS['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING'];
  2313. }
  2314. }
  2315. // Duree de validite de l'alea pour les cookies et ce qui s'ensuit.
  2316. if (!defined('_RENOUVELLE_ALEA')) {
  2317. define('_RENOUVELLE_ALEA', 12 * 3600);
  2318. }
  2319. if (!defined('_DUREE_COOKIE_ADMIN')) {
  2320. define('_DUREE_COOKIE_ADMIN', 14 * 24 * 3600);
  2321. }
  2322. // charger les meta si possible et renouveller l'alea au besoin
  2323. // charge aussi effacer_meta et ecrire_meta
  2324. $inc_meta = charger_fonction('meta', 'inc');
  2325. $inc_meta();
  2326. // nombre de repertoires depuis la racine
  2327. // on compare a l'adresse de spip.php : $_SERVER["SCRIPT_NAME"]
  2328. // ou a defaut celle donnee en meta ; (mais si celle-ci est fausse
  2329. // le calcul est faux)
  2330. if (!_DIR_RESTREINT) {
  2331. $GLOBALS['profondeur_url'] = 1;
  2332. } else {
  2333. $uri = isset($_SERVER['REQUEST_URI']) ? explode('?', $_SERVER['REQUEST_URI']) : '';
  2334. $uri_ref = $_SERVER["SCRIPT_NAME"];
  2335. if (!$uri_ref
  2336. // si on est appele avec un autre ti, on est sans doute en mutu
  2337. // si jamais c'est de la mutu avec sous rep, on est perdu si on se fie
  2338. // a spip.php qui est a la racine du spip, et vue qu'on sait pas se reperer
  2339. // s'en remettre a l'adresse du site. alea jacta est.
  2340. or $ti !== _NOM_TEMPORAIRES_INACCESSIBLES
  2341. ) {
  2342. if (isset($GLOBALS['meta']['adresse_site'])) {
  2343. $uri_ref = parse_url($GLOBALS['meta']['adresse_site']);
  2344. $uri_ref = (isset($uri_ref['path']) ? $uri_ref['path'] : '') . '/';
  2345. } else {
  2346. $uri_ref = "";
  2347. }
  2348. }
  2349. if (!$uri or !$uri_ref) {
  2350. $GLOBALS['profondeur_url'] = 0;
  2351. } else {
  2352. $GLOBALS['profondeur_url'] = max(0,
  2353. substr_count($uri[0], '/')
  2354. - substr_count($uri_ref, '/'));
  2355. }
  2356. }
  2357. // s'il y a un cookie ou PHP_AUTH, initialiser visiteur_session
  2358. if (_FILE_CONNECT) {
  2359. if (verifier_visiteur() == '0minirezo'
  2360. // si c'est un admin sans cookie admin, il faut ignorer le cache chemin !
  2361. and !isset($_COOKIE['spip_admin'])
  2362. ) {
  2363. clear_path_cache();
  2364. }
  2365. }
  2366. }
  2367. /**
  2368. * Complements d'initialisation non critiques pouvant etre realises
  2369. * par les plugins
  2370. *
  2371. */
  2372. function spip_initialisation_suite() {
  2373. static $too_late = 0;
  2374. if ($too_late++) {
  2375. return;
  2376. }
  2377. // taille mini des login
  2378. if (!defined('_LOGIN_TROP_COURT')) {
  2379. define('_LOGIN_TROP_COURT', 4);
  2380. }
  2381. // la taille maxi des logos (0 : pas de limite) (pas de define par defaut, ce n'est pas utile)
  2382. #if (!defined('_LOGO_MAX_SIZE')) define('_LOGO_MAX_SIZE', 0); # poids en ko
  2383. #if (!defined('_LOGO_MAX_WIDTH')) define('_LOGO_MAX_WIDTH', 0); # largeur en pixels
  2384. #if (!defined('_LOGO_MAX_HEIGHT')) define('_LOGO_MAX_HEIGHT', 0); # hauteur en pixels
  2385. // la taille maxi des images (0 : pas de limite) (pas de define par defaut, ce n'est pas utile)
  2386. #if (!defined('_DOC_MAX_SIZE')) define('_DOC_MAX_SIZE', 0); # poids en ko
  2387. #if (!defined('_IMG_MAX_SIZE')) define('_IMG_MAX_SIZE', 0); # poids en ko
  2388. #if (!defined('_IMG_MAX_WIDTH')) define('_IMG_MAX_WIDTH', 0); # largeur en pixels
  2389. #if (!defined('_IMG_MAX_HEIGHT')) define('_IMG_MAX_HEIGHT', 0); # hauteur en pixels
  2390. if (!defined('_PASS_LONGUEUR_MINI')) {
  2391. define('_PASS_LONGUEUR_MINI', 6);
  2392. }
  2393. // Qualite des images calculees automatiquement. C'est un nombre entre 0 et 100, meme pour imagick (on ramene a 0..1 par la suite)
  2394. if (!defined('_IMG_QUALITE')) {
  2395. define('_IMG_QUALITE', 85);
  2396. } # valeur par defaut
  2397. if (!defined('_IMG_GD_QUALITE')) {
  2398. define('_IMG_GD_QUALITE', _IMG_QUALITE);
  2399. } # surcharge pour la lib GD
  2400. if (!defined('_IMG_CONVERT_QUALITE')) {
  2401. define('_IMG_CONVERT_QUALITE', _IMG_QUALITE);
  2402. } # surcharge pour imagick en ligne de commande
  2403. // Historiquement la valeur pour imagick semble differente. Si ca n'est pas necessaire, il serait preferable de garder _IMG_QUALITE
  2404. if (!defined('_IMG_IMAGICK_QUALITE')) {
  2405. define('_IMG_IMAGICK_QUALITE', 75);
  2406. } # surcharge pour imagick en PHP
  2407. if (!defined('_COPIE_LOCALE_MAX_SIZE')) {
  2408. define('_COPIE_LOCALE_MAX_SIZE', 33554432);
  2409. } // poids en octet
  2410. // qq chaines standard
  2411. if (!defined('_ACCESS_FILE_NAME')) {
  2412. define('_ACCESS_FILE_NAME', '.htaccess');
  2413. }
  2414. if (!defined('_AUTH_USER_FILE')) {
  2415. define('_AUTH_USER_FILE', '.htpasswd');
  2416. }
  2417. if (!defined('_SPIP_DUMP')) {
  2418. define('_SPIP_DUMP', 'dump@nom_site@@stamp@.xml');
  2419. }
  2420. if (!defined('_CACHE_RUBRIQUES')) {
  2421. /** Fichier cache pour le navigateur de rubrique du bandeau */
  2422. define('_CACHE_RUBRIQUES', _DIR_TMP . 'menu-rubriques-cache.txt');
  2423. }
  2424. if (!defined('_CACHE_RUBRIQUES_MAX')) {
  2425. /** Nombre maxi de rubriques enfants affichées pour chaque rubrique du navigateur de rubrique du bandeau */
  2426. define('_CACHE_RUBRIQUES_MAX', 500);
  2427. }
  2428. if (!defined('_EXTENSION_SQUELETTES')) {
  2429. define('_EXTENSION_SQUELETTES', 'html');
  2430. }
  2431. if (!defined('_DOCTYPE_ECRIRE')) {
  2432. /** Définit le doctype de l’espace privé */
  2433. define('_DOCTYPE_ECRIRE', "<!DOCTYPE html>\n");
  2434. }
  2435. if (!defined('_DOCTYPE_AIDE')) {
  2436. /** Définit le doctype de l’aide en ligne */
  2437. define('_DOCTYPE_AIDE',
  2438. "<!DOCTYPE html PUBLIC '-//W3C//DTD HTML 4.01 Frameset//EN' 'http://www.w3.org/TR/1999/REC-html401-19991224/frameset.dtd'>");
  2439. }
  2440. if (!defined('_SPIP_SCRIPT')) {
  2441. /** L'adresse de base du site ; on peut mettre '' si la racine est gerée par
  2442. * le script de l'espace public, alias index.php */
  2443. define('_SPIP_SCRIPT', 'spip.php');
  2444. }
  2445. if (!defined('_SPIP_PAGE')) {
  2446. /** Argument page, personalisable en cas de conflit avec un autre script */
  2447. define('_SPIP_PAGE', 'page');
  2448. }
  2449. // le script de l'espace prive
  2450. // Mettre a "index.php" si DirectoryIndex ne le fait pas ou pb connexes:
  2451. // les anciens IIS n'acceptent pas les POST sur ecrire/ (#419)
  2452. // meme pb sur thttpd cf. http://forum.spip.net/fr_184153.html
  2453. if (!defined('_SPIP_ECRIRE_SCRIPT')) {
  2454. if (!empty($_SERVER['SERVER_SOFTWARE']) and preg_match(',IIS|thttpd,', $_SERVER['SERVER_SOFTWARE'])) {
  2455. define('_SPIP_ECRIRE_SCRIPT', 'index.php');
  2456. } else {
  2457. define('_SPIP_ECRIRE_SCRIPT', '');
  2458. }
  2459. }
  2460. if (!defined('_SPIP_AJAX')) {
  2461. define('_SPIP_AJAX', ((!isset($_COOKIE['spip_accepte_ajax']))
  2462. ? 1
  2463. : (($_COOKIE['spip_accepte_ajax'] != -1) ? 1 : 0)));
  2464. }
  2465. // La requete est-elle en ajax ?
  2466. if (!defined('_AJAX')) {
  2467. define('_AJAX',
  2468. (isset($_SERVER['HTTP_X_REQUESTED_WITH']) # ajax jQuery
  2469. or !empty($_REQUEST['var_ajax_redir']) # redirection 302 apres ajax jQuery
  2470. or !empty($_REQUEST['var_ajaxcharset']) # compat ascendante pour plugins
  2471. or !empty($_REQUEST['var_ajax']) # forms ajax & inclure ajax de spip
  2472. )
  2473. and empty($_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
  2474. );
  2475. }
  2476. # nombre de pixels maxi pour calcul de la vignette avec gd
  2477. # au dela de 5500000 on considere que php n'est pas limite en memoire pour cette operation
  2478. # les configurations limitees en memoire ont un seuil plutot vers 1MPixel
  2479. if (!defined('_IMG_GD_MAX_PIXELS')) {
  2480. define('_IMG_GD_MAX_PIXELS',
  2481. (isset($GLOBALS['meta']['max_taille_vignettes']) and $GLOBALS['meta']['max_taille_vignettes'])
  2482. ? $GLOBALS['meta']['max_taille_vignettes']
  2483. : 0);
  2484. }
  2485. if (!defined('_MEMORY_LIMIT_MIN')) {
  2486. define('_MEMORY_LIMIT_MIN', 16);
  2487. } // en Mo
  2488. // si on est dans l'espace prive et si le besoin est superieur a 8Mo (qui est vraiment le standard)
  2489. // on verifie que la memoire est suffisante pour le compactage css+js pour eviter la page blanche
  2490. // il y aura d'autres problemes et l'utilisateur n'ira pas tres loin, mais ce sera plus comprehensible qu'une page blanche
  2491. if (test_espace_prive() and _MEMORY_LIMIT_MIN > 8) {
  2492. if ($memory = trim(ini_get('memory_limit')) and $memory != -1) {
  2493. $unit = strtolower(substr($memory, -1));
  2494. $memory = substr($memory, 0, -1);
  2495. switch ($unit) {
  2496. // Le modifieur 'G' est disponible depuis PHP 5.1.0
  2497. case 'g':
  2498. $memory *= 1024;
  2499. case 'm':
  2500. $memory *= 1024;
  2501. case 'k':
  2502. $memory *= 1024;
  2503. }
  2504. if ($memory < _MEMORY_LIMIT_MIN * 1024 * 1024) {
  2505. @ini_set('memory_limit', $m = _MEMORY_LIMIT_MIN . 'M');
  2506. if (trim(ini_get('memory_limit')) != $m) {
  2507. if (!defined('_INTERDIRE_COMPACTE_HEAD_ECRIRE')) {
  2508. define('_INTERDIRE_COMPACTE_HEAD_ECRIRE', true);
  2509. } // evite une page blanche car on ne saura pas calculer la css dans ce hit
  2510. }
  2511. }
  2512. } else {
  2513. if (!defined('_INTERDIRE_COMPACTE_HEAD_ECRIRE')) {
  2514. define('_INTERDIRE_COMPACTE_HEAD_ECRIRE', true);
  2515. }
  2516. } // evite une page blanche car on ne saura pas calculer la css dans ce hit
  2517. }
  2518. // Protocoles a normaliser dans les chaines de langues
  2519. if (!defined('_PROTOCOLES_STD')) {
  2520. define('_PROTOCOLES_STD', 'http|https|ftp|mailto|webcal');
  2521. }
  2522. init_var_mode();
  2523. }
  2524. /**
  2525. * Repérer les variables d'URL spéciales `var_mode` qui conditionnent
  2526. * la validité du cache ou certains affichages spéciaux.
  2527. *
  2528. * Le paramètre d'URL `var_mode` permet de
  2529. * modifier la pérennité du cache, recalculer des urls
  2530. * ou d'autres petit caches (trouver_table, css et js compactes ...),
  2531. * d'afficher un écran de débug ou des traductions non réalisées.
  2532. *
  2533. * En fonction de ces paramètres dans l'URL appelante, on définit
  2534. * da constante `_VAR_MODE` qui servira ensuite à SPIP.
  2535. *
  2536. * Le paramètre `var_mode` accepte ces valeurs :
  2537. *
  2538. * - `calcul` : force un calcul du cache de la page (sans forcément recompiler les squelettes)
  2539. * - `recalcul` : force un calcul du cache de la page en recompilant au préabable les squelettes
  2540. * - `inclure` : modifie l'affichage en ajoutant visuellement le nom de toutes les inclusions qu'elle contient
  2541. * - `debug` : modifie l'affichage activant le mode "debug"
  2542. * - `preview` : modifie l'affichage en ajoutant aux boucles les éléments prévisualisables
  2543. * - `traduction` : modifie l'affichage en affichant des informations sur les chaînes de langues utilisées
  2544. * - `urls` : permet de recalculer les URLs des objets appelés dans la page par les balises `#URL_xx`
  2545. * - `images` : permet de recalculer les filtres d'images utilisés dans la page
  2546. *
  2547. * En dehors des modes `calcul` et `recalcul`, une autorisation 'previsualiser' ou 'debug' est testée.
  2548. *
  2549. * @note
  2550. * Il éxiste également le paramètre `var_profile` qui modifie l'affichage pour incruster
  2551. * le nombre de requêtes SQL utilisées dans la page, qui peut se compléter avec le paramètre
  2552. * ` var_mode` (calcul ou recalcul).
  2553. */
  2554. function init_var_mode() {
  2555. static $done = false;
  2556. if (!$done) {
  2557. if (isset($_GET['var_mode'])) {
  2558. $var_mode = explode(',', $_GET['var_mode']);
  2559. // tout le monde peut calcul/recalcul
  2560. if (!defined('_VAR_MODE')) {
  2561. if (in_array('recalcul', $var_mode)) {
  2562. define('_VAR_MODE', 'recalcul');
  2563. } elseif (in_array('calcul', $var_mode)) {
  2564. define('_VAR_MODE', 'calcul');
  2565. }
  2566. }
  2567. $var_mode = array_diff($var_mode, array('calcul', 'recalcul'));
  2568. if ($var_mode) {
  2569. include_spip('inc/autoriser');
  2570. // autoriser preview si preview seulement, et sinon autoriser debug
  2571. if (autoriser(
  2572. ($_GET['var_mode'] == 'preview')
  2573. ? 'previsualiser'
  2574. : 'debug'
  2575. )) {
  2576. if (in_array('traduction', $var_mode)) {
  2577. // forcer le calcul pour passer dans traduire
  2578. if (!defined('_VAR_MODE')) {
  2579. define('_VAR_MODE', 'calcul');
  2580. }
  2581. // et ne pas enregistrer de cache pour ne pas trainer les surlignages sur d'autres pages
  2582. if (!defined('_VAR_NOCACHE')) {
  2583. define('_VAR_NOCACHE', true);
  2584. }
  2585. $var_mode = array_diff($var_mode, array('traduction'));
  2586. }
  2587. if (in_array('preview', $var_mode)) {
  2588. // basculer sur les criteres de preview dans les boucles
  2589. if (!defined('_VAR_PREVIEW')) {
  2590. define('_VAR_PREVIEW', true);
  2591. }
  2592. // forcer le calcul
  2593. if (!defined('_VAR_MODE')) {
  2594. define('_VAR_MODE', 'calcul');
  2595. }
  2596. // et ne pas enregistrer de cache
  2597. if (!defined('_VAR_NOCACHE')) {
  2598. define('_VAR_NOCACHE', true);
  2599. }
  2600. $var_mode = array_diff($var_mode, array('preview'));
  2601. }
  2602. if (in_array('inclure', $var_mode)) {
  2603. // forcer le compilo et ignorer les caches existants
  2604. if (!defined('_VAR_MODE')) {
  2605. define('_VAR_MODE', 'calcul');
  2606. }
  2607. if (!defined('_VAR_INCLURE')) {
  2608. define('_VAR_INCLURE', true);
  2609. }
  2610. // et ne pas enregistrer de cache
  2611. if (!defined('_VAR_NOCACHE')) {
  2612. define('_VAR_NOCACHE', true);
  2613. }
  2614. $var_mode = array_diff($var_mode, array('inclure'));
  2615. }
  2616. if (in_array('urls', $var_mode)) {
  2617. // forcer le compilo et ignorer les caches existants
  2618. if (!defined('_VAR_MODE')) {
  2619. define('_VAR_MODE', 'calcul');
  2620. }
  2621. if (!defined('_VAR_URLS')) {
  2622. define('_VAR_URLS', true);
  2623. }
  2624. $var_mode = array_diff($var_mode, array('urls'));
  2625. }
  2626. if (in_array('images', $var_mode)) {
  2627. // forcer le compilo et ignorer les caches existants
  2628. if (!defined('_VAR_MODE')) {
  2629. define('_VAR_MODE', 'calcul');
  2630. }
  2631. // indiquer qu'on doit recalculer les images
  2632. if (!defined('_VAR_IMAGES')) {
  2633. define('_VAR_IMAGES', true);
  2634. }
  2635. $var_mode = array_diff($var_mode, array('images'));
  2636. }
  2637. if (in_array('debug', $var_mode)) {
  2638. if (!defined('_VAR_MODE')) {
  2639. define('_VAR_MODE', 'debug');
  2640. }
  2641. // et ne pas enregistrer de cache
  2642. if (!defined('_VAR_NOCACHE')) {
  2643. define('_VAR_NOCACHE', true);
  2644. }
  2645. $var_mode = array_diff($var_mode, array('debug'));
  2646. }
  2647. if (count($var_mode) and !defined('_VAR_MODE')) {
  2648. define('_VAR_MODE', reset($var_mode));
  2649. }
  2650. if (isset($GLOBALS['visiteur_session']['nom'])) {
  2651. spip_log($GLOBALS['visiteur_session']['nom']
  2652. . " " . _VAR_MODE);
  2653. }
  2654. } // pas autorise ?
  2655. else {
  2656. // si on n'est pas connecte on se redirige
  2657. if (!$GLOBALS['visiteur_session']) {
  2658. include_spip('inc/headers');
  2659. redirige_par_entete(generer_url_public('login',
  2660. 'url=' . rawurlencode(
  2661. parametre_url(self(), 'var_mode', $_GET['var_mode'], '&')
  2662. ), true));
  2663. }
  2664. // sinon tant pis
  2665. }
  2666. }
  2667. }
  2668. if (!defined('_VAR_MODE')) {
  2669. /**
  2670. * Indique le mode de calcul ou d'affichage de la page.
  2671. * @see init_var_mode()
  2672. */
  2673. define('_VAR_MODE', false);
  2674. }
  2675. $done = true;
  2676. }
  2677. }
  2678. // Annuler les magic quotes \' sur GET POST COOKIE et GLOBALS ;
  2679. // supprimer aussi les eventuels caracteres nuls %00, qui peuvent tromper
  2680. // la commande is_readable('chemin/vers/fichier/interdit%00truc_normal')
  2681. // http://code.spip.net/@spip_desinfecte
  2682. function spip_desinfecte(&$t, $deep = true) {
  2683. foreach ($t as $key => $val) {
  2684. if (is_string($t[$key])) {
  2685. $t[$key] = str_replace(chr(0), '-', $t[$key]);
  2686. } // traiter aussi les "texte_plus" de article_edit
  2687. else {
  2688. if ($deep and is_array($t[$key]) and $key !== 'GLOBALS') {
  2689. spip_desinfecte($t[$key], $deep);
  2690. }
  2691. }
  2692. }
  2693. }
  2694. // retourne le statut du visiteur s'il s'annonce
  2695. // http://code.spip.net/@verifier_visiteur
  2696. function verifier_visiteur() {
  2697. // Rq: pour que cette fonction marche depuis mes_options
  2698. // il faut forcer l'init si ce n'est fait
  2699. // mais on risque de perturber des plugins en initialisant trop tot
  2700. // certaines constantes
  2701. @spip_initialisation_core(
  2702. (_DIR_RACINE . _NOM_PERMANENTS_INACCESSIBLES),
  2703. (_DIR_RACINE . _NOM_PERMANENTS_ACCESSIBLES),
  2704. (_DIR_RACINE . _NOM_TEMPORAIRES_INACCESSIBLES),
  2705. (_DIR_RACINE . _NOM_TEMPORAIRES_ACCESSIBLES)
  2706. );
  2707. // Demarrer une session NON AUTHENTIFIEE si on donne son nom
  2708. // dans un formulaire sans login (ex: #FORMULAIRE_FORUM)
  2709. // Attention on separe bien session_nom et nom, pour eviter
  2710. // les melanges entre donnees SQL et variables plus aleatoires
  2711. $variables_session = array('session_nom', 'session_email');
  2712. foreach ($variables_session as $var) {
  2713. if (_request($var) !== null) {
  2714. $init = true;
  2715. break;
  2716. }
  2717. }
  2718. if (isset($init)) {
  2719. #@spip_initialisation_suite();
  2720. $session = charger_fonction('session', 'inc');
  2721. $session();
  2722. include_spip('inc/texte');
  2723. foreach ($variables_session as $var) {
  2724. if (($a = _request($var)) !== null) {
  2725. $GLOBALS['visiteur_session'][$var] = safehtml($a);
  2726. }
  2727. }
  2728. if (!isset($GLOBALS['visiteur_session']['id_auteur'])) {
  2729. $GLOBALS['visiteur_session']['id_auteur'] = 0;
  2730. }
  2731. $session($GLOBALS['visiteur_session']);
  2732. return 0;
  2733. }
  2734. $h = (isset($_SERVER['PHP_AUTH_USER']) and !$GLOBALS['ignore_auth_http']);
  2735. if ($h or isset($_COOKIE['spip_session']) or isset($_COOKIE[$GLOBALS['cookie_prefix'] . '_session'])) {
  2736. $session = charger_fonction('session', 'inc');
  2737. if ($session()) {
  2738. return $GLOBALS['visiteur_session']['statut'];
  2739. }
  2740. if ($h and isset($_SERVER['PHP_AUTH_PW'])) {
  2741. include_spip('inc/auth');
  2742. $h = lire_php_auth($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
  2743. }
  2744. if ($h) {
  2745. $GLOBALS['visiteur_session'] = $h;
  2746. return $GLOBALS['visiteur_session']['statut'];
  2747. }
  2748. }
  2749. // au moins son navigateur nous dit la langue preferee de cet inconnu
  2750. include_spip('inc/lang');
  2751. utiliser_langue_visiteur();
  2752. return false;
  2753. }
  2754. /**
  2755. * Sélectionne la langue donnée en argument et mémorise la courante
  2756. *
  2757. * Restaure l'ancienne langue si appellée sans argument.
  2758. *
  2759. * @note
  2760. * On pourrait économiser l'empilement en cas de non changemnt
  2761. * et lui faire retourner `False` pour prevenir l'appelant
  2762. * Le noyau de Spip sait le faire, mais pour assurer la compatibilité
  2763. * cette fonction retourne toujours non `False`
  2764. *
  2765. * @uses changer_langue()
  2766. * @param null|string $lang
  2767. * - string : Langue à appliquer,
  2768. * - null : Pour restituer la dernière langue mémorisée.
  2769. * @return string
  2770. * - string Langue utilisée.
  2771. **/
  2772. function lang_select($lang = null) {
  2773. static $pile_langues = array();
  2774. if (!function_exists('changer_langue')) {
  2775. include_spip('inc/lang');
  2776. }
  2777. if ($lang === null) {
  2778. $lang = array_pop($pile_langues);
  2779. } else {
  2780. array_push($pile_langues, $GLOBALS['spip_lang']);
  2781. }
  2782. if (isset($GLOBALS['spip_lang']) and $lang == $GLOBALS['spip_lang']) {
  2783. return $lang;
  2784. }
  2785. changer_langue($lang);
  2786. return $lang;
  2787. }
  2788. /**
  2789. * Renvoie une chaîne qui identifie la session courante
  2790. *
  2791. * Permet de savoir si on peut utiliser un cache enregistré pour cette session.
  2792. * Cette chaîne est courte (8 cars) pour pouvoir être utilisée dans un nom
  2793. * de fichier cache.
  2794. *
  2795. * @pipeline_appel definir_session
  2796. *
  2797. * @param bool $force
  2798. * @return string
  2799. * Identifiant de la session
  2800. **/
  2801. function spip_session($force = false) {
  2802. static $session;
  2803. if ($force or !isset($session)) {
  2804. $s = pipeline('definir_session',
  2805. $GLOBALS['visiteur_session']
  2806. ? serialize($GLOBALS['visiteur_session'])
  2807. . '_' . @$_COOKIE['spip_session']
  2808. : ''
  2809. );
  2810. $session = $s ? substr(md5($s), 0, 8) : '';
  2811. }
  2812. #spip_log('session: '.$session);
  2813. return $session;
  2814. }
  2815. /**
  2816. * Retourne un lien vers une aide
  2817. *
  2818. * Aide, aussi depuis l'espace privé à présent.
  2819. * Surchargeable mais pas d'erreur fatale si indisponible.
  2820. *
  2821. * @param string $aide
  2822. * Cle d'identification de l'aide desiree
  2823. * @param bool $distante
  2824. * Generer une url locale (par defaut)
  2825. * ou une url distante [directement sur spip.net]
  2826. * @return
  2827. * Lien sur une icone d'aide
  2828. **/
  2829. function aider($aide = '', $distante = false) {
  2830. $aider = charger_fonction('aide', 'inc', true);
  2831. return $aider ? $aider($aide, '', array(), $distante) : '';
  2832. }
  2833. /**
  2834. * Page `exec=info` : retourne le contenu de la fonction php `phpinfo()`
  2835. *
  2836. * Si l’utiliseur est un webmestre.
  2837. */
  2838. function exec_info_dist() {
  2839. include_spip('inc/autoriser');
  2840. if (autoriser('webmestre')) {
  2841. $cookies_masques = ['spip_session', 'PHPSESSID'];
  2842. $cookies_backup = [];
  2843. foreach ($cookies_masques as $k) {
  2844. if (!empty($_COOKIE[$k])) {
  2845. $cookies_backup[$k] = $_COOKIE[$k];
  2846. $_COOKIE[$k] = '******************************';
  2847. }
  2848. }
  2849. phpinfo();
  2850. foreach ($cookies_backup as $k => $v) {
  2851. $_COOKIE[$k] = $v;
  2852. }
  2853. } else {
  2854. include_spip('inc/filtres');
  2855. sinon_interdire_acces();
  2856. }
  2857. }
  2858. /**
  2859. * Génère une erreur de squelette
  2860. *
  2861. * Génère une erreur de squelette qui sera bien visible par un
  2862. * administrateur authentifié lors d'une visite de la page en erreur
  2863. *
  2864. * @param bool|string|array $message
  2865. * - Message d'erreur (string|array)
  2866. * - false pour retourner le texte des messages d'erreurs
  2867. * - vide pour afficher les messages d'erreurs
  2868. * @param string|array|object $lieu
  2869. * Lieu d'origine de l'erreur
  2870. * @return null|string
  2871. * - Rien dans la plupart des cas
  2872. * - string si $message à false.
  2873. **/
  2874. function erreur_squelette($message = '', $lieu = '') {
  2875. $debusquer = charger_fonction('debusquer', 'public');
  2876. if (is_array($lieu)) {
  2877. include_spip('public/compiler');
  2878. $lieu = reconstruire_contexte_compil($lieu);
  2879. }
  2880. return $debusquer($message, $lieu);
  2881. }
  2882. /**
  2883. * Calcule un squelette avec un contexte et retourne son contenu
  2884. *
  2885. * La fonction de base de SPIP : un squelette + un contexte => une page.
  2886. * $fond peut etre un nom de squelette, ou une liste de squelette au format array.
  2887. * Dans ce dernier cas, les squelettes sont tous evalues et mis bout a bout
  2888. * $options permet de selectionner les options suivantes :
  2889. *
  2890. * - trim => true (valeur par defaut) permet de ne rien renvoyer si le fond ne produit que des espaces ;
  2891. * - raw => true permet de recuperer la strucure $page complete avec entetes et invalideurs
  2892. * pour chaque $fond fourni.
  2893. *
  2894. * @api
  2895. * @param string /array $fond
  2896. * - Le ou les squelettes à utiliser, sans l'extension, {@example prive/liste/auteurs}
  2897. * - Le fichier sera retrouvé dans la liste des chemins connus de SPIP (squelettes, plugins, spip)
  2898. * @param array $contexte
  2899. * - Informations de contexte envoyées au squelette, {@example array('id_rubrique' => 8)}
  2900. * - La langue est transmise automatiquement (sauf option étoile).
  2901. * @param array $options
  2902. * Options complémentaires :
  2903. *
  2904. * - trim : applique un trim sur le résultat (true par défaut)
  2905. * - raw : retourne un tableau d'information sur le squelette (false par défaut)
  2906. * - etoile : ne pas transmettre la langue au contexte automatiquement (false par défaut),
  2907. * équivalent de INCLURE*
  2908. * - ajax : gere les liens internes du squelette en ajax (équivalent du paramètre {ajax})
  2909. * @param string $connect
  2910. * Non du connecteur de bdd a utiliser
  2911. * @return string|array
  2912. * - Contenu du squelette calculé
  2913. * - ou tableau d'information sur le squelette.
  2914. */
  2915. function recuperer_fond($fond, $contexte = array(), $options = array(), $connect = '') {
  2916. if (!function_exists('evaluer_fond')) {
  2917. include_spip('public/assembler');
  2918. }
  2919. // assurer la compat avec l'ancienne syntaxe
  2920. // (trim etait le 3eme argument, par defaut a true)
  2921. if (!is_array($options)) {
  2922. $options = array('trim' => $options);
  2923. }
  2924. if (!isset($options['trim'])) {
  2925. $options['trim'] = true;
  2926. }
  2927. if (isset($contexte['connect'])) {
  2928. $connect = $contexte['connect'];
  2929. unset($contexte['connect']);
  2930. }
  2931. $texte = "";
  2932. $pages = array();
  2933. $lang_select = '';
  2934. if (!isset($options['etoile']) or !$options['etoile']) {
  2935. // Si on a inclus sans fixer le critere de lang, on prend la langue courante
  2936. if (!isset($contexte['lang'])) {
  2937. $contexte['lang'] = $GLOBALS['spip_lang'];
  2938. }
  2939. if ($contexte['lang'] != $GLOBALS['meta']['langue_site']) {
  2940. $lang_select = lang_select($contexte['lang']);
  2941. }
  2942. }
  2943. if (!isset($GLOBALS['_INC_PUBLIC'])) {
  2944. $GLOBALS['_INC_PUBLIC'] = 0;
  2945. }
  2946. $GLOBALS['_INC_PUBLIC']++;
  2947. // fix #4235
  2948. $cache_utilise_session_appelant = (isset($GLOBALS['cache_utilise_session']) ? $GLOBALS['cache_utilise_session'] : null);
  2949. foreach (is_array($fond) ? $fond : array($fond) as $f) {
  2950. unset($GLOBALS['cache_utilise_session']); // fix #4235
  2951. $page = evaluer_fond($f, $contexte, $connect);
  2952. if ($page === '') {
  2953. $c = isset($options['compil']) ? $options['compil'] : '';
  2954. $a = array('fichier' => $f);
  2955. $erreur = _T('info_erreur_squelette2', $a); // squelette introuvable
  2956. erreur_squelette($erreur, $c);
  2957. // eviter des erreurs strictes ensuite sur $page['cle'] en PHP >= 5.4
  2958. $page = array('texte' => '', 'erreur' => $erreur);
  2959. }
  2960. $page = pipeline('recuperer_fond', array(
  2961. 'args' => array('fond' => $f, 'contexte' => $contexte, 'options' => $options, 'connect' => $connect),
  2962. 'data' => $page
  2963. ));
  2964. if (isset($options['ajax']) and $options['ajax']) {
  2965. if (!function_exists('encoder_contexte_ajax')) {
  2966. include_spip('inc/filtres');
  2967. }
  2968. $page['texte'] = encoder_contexte_ajax(
  2969. array_merge(
  2970. $contexte,
  2971. array('fond' => $f),
  2972. ($connect ? array('connect' => $connect) : array())
  2973. ),
  2974. '',
  2975. $page['texte'],
  2976. $options['ajax']
  2977. );
  2978. }
  2979. if (isset($options['raw']) and $options['raw']) {
  2980. $pages[] = $page;
  2981. } else {
  2982. $texte .= $options['trim'] ? rtrim($page['texte']) : $page['texte'];
  2983. }
  2984. // contamination de la session appelante, pour les inclusions statiques
  2985. if (isset($page['invalideurs']['session'])){
  2986. $cache_utilise_session_appelant = $page['invalideurs']['session'];
  2987. }
  2988. }
  2989. // restaurer le sessionnement du contexte appelant,
  2990. // éventuellement contaminé si on vient de récupérer une inclusion statique sessionnée
  2991. if (isset($cache_utilise_session_appelant)) {
  2992. $GLOBALS['cache_utilise_session'] = $cache_utilise_session_appelant;
  2993. }
  2994. $GLOBALS['_INC_PUBLIC']--;
  2995. if ($lang_select) {
  2996. lang_select();
  2997. }
  2998. if (isset($options['raw']) and $options['raw']) {
  2999. return is_array($fond) ? $pages : reset($pages);
  3000. } else {
  3001. return $options['trim'] ? ltrim($texte) : $texte;
  3002. }
  3003. }
  3004. /**
  3005. * Trouve un squelette dans le repertoire modeles/
  3006. *
  3007. * @param $nom
  3008. * @return string
  3009. */
  3010. function trouve_modele($nom) {
  3011. return trouver_fond($nom, 'modeles/');
  3012. }
  3013. /**
  3014. * Trouver un squelette dans le chemin
  3015. * on peut specifier un sous-dossier dans $dir
  3016. * si $pathinfo est a true, retourne un tableau avec
  3017. * les composantes du fichier trouve
  3018. * + le chemin complet sans son extension dans fond
  3019. *
  3020. * @param string $nom
  3021. * @param string $dir
  3022. * @param bool $pathinfo
  3023. * @return array|string
  3024. */
  3025. function trouver_fond($nom, $dir = '', $pathinfo = false) {
  3026. $f = find_in_path($nom . '.' . _EXTENSION_SQUELETTES, $dir ? rtrim($dir, '/') . '/' : '');
  3027. if (!$pathinfo) {
  3028. return $f;
  3029. }
  3030. // renvoyer un tableau detaille si $pathinfo==true
  3031. $p = pathinfo($f);
  3032. if (!isset($p['extension']) or !$p['extension']) {
  3033. $p['extension'] = _EXTENSION_SQUELETTES;
  3034. }
  3035. if (!isset($p['extension']) or !$p['filename']) {
  3036. $p['filename'] = ($p['basename'] ? substr($p['basename'], 0, -strlen($p['extension']) - 1) : '');
  3037. }
  3038. $p['fond'] = ($f ? substr($f, 0, -strlen($p['extension']) - 1) : '');
  3039. return $p;
  3040. }
  3041. /**
  3042. * Teste, pour un nom de page de l'espace privé, s'il est possible
  3043. * de générer son contenu.
  3044. *
  3045. * Dans ce cas, on retourne la fonction d'exécution correspondante à utiliser
  3046. * (du répertoire `ecrire/exec`). Deux cas particuliers et prioritaires :
  3047. * `fond` ou `fond_monobloc` sont retournés si des squelettes existent.
  3048. *
  3049. * - `fond` : pour des squelettes de `prive/squelettes/contenu`
  3050. * ou pour des objets éditoriaux dont les suqelettes seront échaffaudés
  3051. * - `fond_monobloc` (compatibilité avec SPIP 2.1) : pour des squelettes de `prive/exec`
  3052. *
  3053. * @param string $nom
  3054. * Nom de la page
  3055. * @return string
  3056. * Nom de l'exec, sinon chaîne vide.
  3057. **/
  3058. function tester_url_ecrire($nom) {
  3059. static $exec = array();
  3060. if (isset($exec[$nom])) {
  3061. return $exec[$nom];
  3062. }
  3063. // tester si c'est une page en squelette
  3064. if (trouver_fond($nom, 'prive/squelettes/contenu/')) {
  3065. return $exec[$nom] = 'fond';
  3066. } // compat skels orthogonaux version precedente
  3067. elseif (trouver_fond($nom, 'prive/exec/')) {
  3068. return $exec[$nom] = 'fond_monobloc';
  3069. } // echafaudage d'un fond !
  3070. elseif (include_spip('public/styliser_par_z') and z_echafaudable($nom)) {
  3071. return $exec[$nom] = 'fond';
  3072. }
  3073. // attention, il ne faut pas inclure l'exec ici
  3074. // car sinon #URL_ECRIRE provoque des inclusions
  3075. // et des define intrusifs potentiels
  3076. return $exec[$nom] = ((find_in_path("{$nom}.php", 'exec/') or charger_fonction($nom, 'exec', true)) ? $nom : '');
  3077. }
  3078. /**
  3079. * Teste la présence d’une extension PHP
  3080. *
  3081. * @deprected Utiliser directement la fonction native `extension_loaded($module)`
  3082. * @example
  3083. * ```
  3084. * $ok = charger_php_extension('sqlite');
  3085. * ```
  3086. * @param string $module Nom du module à charger
  3087. * @return bool true si le module est chargé
  3088. **/
  3089. function charger_php_extension($module) {
  3090. if (extension_loaded($module)) {
  3091. return true;
  3092. }
  3093. return false;
  3094. }
  3095. /**
  3096. * Indique si le code HTML5 est permis sur le site public
  3097. *
  3098. * @return bool
  3099. * true si et seulement si la configuration autorise le code HTML5 sur le site public
  3100. **/
  3101. function html5_permis() {
  3102. return (isset($GLOBALS['meta']['version_html_max'])
  3103. and ('html5' == $GLOBALS['meta']['version_html_max']));
  3104. }
  3105. /**
  3106. * Lister les formats image acceptes par les lib et fonctions images
  3107. * @param bool $gd
  3108. * @param bool $svg_allowed
  3109. * @return array
  3110. */
  3111. function formats_image_acceptables($gd = false, $svg_allowed = true) {
  3112. $config = ($gd ? "gd_formats" : "formats_graphiques");
  3113. $formats = (isset($GLOBALS['meta'][$config]) ? $GLOBALS['meta'][$config] : 'png,gif,jpg');
  3114. $formats = explode(',', $formats);
  3115. $formats = array_filter($formats);
  3116. $formats = array_map('trim', $formats);
  3117. if ($svg_allowed) {
  3118. $formats[] = 'svg';
  3119. }
  3120. return $formats;
  3121. }
  3122. /**
  3123. * Extension de la fonction getimagesize pour supporter aussi les images SVG
  3124. * @param string $fichier
  3125. * @return array|bool
  3126. */
  3127. function spip_getimagesize($fichier) {
  3128. if (!$imagesize = @getimagesize($fichier)) {
  3129. include_spip("inc/svg");
  3130. if ($attrs = svg_lire_attributs($fichier)) {
  3131. list($width, $height, $viewbox) = svg_getimagesize_from_attr($attrs);
  3132. $imagesize = [
  3133. $width,
  3134. $height,
  3135. IMAGETYPE_SVG,
  3136. "width=\"{$width}\" height=\"{$height}\"",
  3137. "mime" => "image/svg+xml"
  3138. ];
  3139. }
  3140. }
  3141. return $imagesize;
  3142. }
  3143. /*
  3144. * Bloc de compatibilite : quasiment tous les plugins utilisent ces fonctions
  3145. * desormais depreciees ; plutot que d'obliger tout le monde a charger
  3146. * vieilles_defs, on va assumer l'histoire de ces 3 fonctions ubiquitaires
  3147. */
  3148. /**
  3149. * lire_meta : fonction dépréciée
  3150. *
  3151. * @deprecated Utiliser `$GLOBALS['meta'][$nom]` ou `lire_config('nom')`
  3152. * @see lire_config()
  3153. * @param string $nom Clé de meta à lire
  3154. * @return mixed Valeur de la meta.
  3155. **/
  3156. function lire_meta($nom) {
  3157. return isset($GLOBALS['meta'][$nom]) ? $GLOBALS['meta'][$nom] : null;
  3158. }
  3159. /**
  3160. * ecrire_metas : fonction dépréciée
  3161. *
  3162. * @deprecated
  3163. **/
  3164. function ecrire_metas() { }
  3165. /**
  3166. * Poser une alerte qui sera affiche aux auteurs de bon statut ('' = tous)
  3167. * au prochain passage dans l'espace prive
  3168. * chaque alerte doit avoir un nom pour eviter duplication a chaque hit
  3169. * les alertes affichees une fois sont effacees
  3170. *
  3171. * @param string $nom
  3172. * @param string $message
  3173. * @param string $statut
  3174. */
  3175. function avertir_auteurs($nom, $message, $statut = '') {
  3176. $alertes = $GLOBALS['meta']['message_alertes_auteurs'];
  3177. if (!$alertes
  3178. or !is_array($alertes = unserialize($alertes))
  3179. ) {
  3180. $alertes = array();
  3181. }
  3182. if (!isset($alertes[$statut])) {
  3183. $alertes[$statut] = array();
  3184. }
  3185. $alertes[$statut][$nom] = $message;
  3186. ecrire_meta("message_alertes_auteurs", serialize($alertes));
  3187. }
  3188. if (PHP_VERSION_ID < 50500) {
  3189. if (!function_exists('array_column')) {
  3190. /**
  3191. * Returns the values from a single column of the input array, identified by
  3192. * the $columnKey.
  3193. *
  3194. * Optionally, you may provide an $indexKey to index the values in the returned
  3195. * array by the values from the $indexKey column in the input array.
  3196. *
  3197. * @link http://php.net/manual/fr/function.array-column.php
  3198. * @link https://github.com/ramsey/array_column/blob/master/src/array_column.php
  3199. * @copyright Copyright (c) Ben Ramsey (http://benramsey.com)
  3200. * @license http://opensource.org/licenses/MIT MIT
  3201. *
  3202. * @param array $input A multi-dimensional array (record set) from which to pull
  3203. * a column of values.
  3204. * @param mixed $columnKey The column of values to return. This value may be the
  3205. * integer key of the column you wish to retrieve, or it
  3206. * may be the string key name for an associative array.
  3207. * @param mixed $indexKey (Optional.) The column to use as the index/keys for
  3208. * the returned array. This value may be the integer key
  3209. * of the column, or it may be the string key name.
  3210. * @return array
  3211. */
  3212. function array_column($input = null, $columnKey = null, $indexKey = null)
  3213. {
  3214. // Using func_get_args() in order to check for proper number of
  3215. // parameters and trigger errors exactly as the built-in array_column()
  3216. // does in PHP 5.5.
  3217. $argc = func_num_args();
  3218. $params = func_get_args();
  3219. if ($argc < 2) {
  3220. trigger_error("array_column() expects at least 2 parameters, {$argc} given", E_USER_WARNING);
  3221. return null;
  3222. }
  3223. if (!is_array($params[0])) {
  3224. trigger_error(
  3225. 'array_column() expects parameter 1 to be array, ' . gettype($params[0]) . ' given',
  3226. E_USER_WARNING
  3227. );
  3228. return null;
  3229. }
  3230. if (!is_int($params[1])
  3231. && !is_float($params[1])
  3232. && !is_string($params[1])
  3233. && $params[1] !== null
  3234. && !(is_object($params[1]) && method_exists($params[1], '__toString'))
  3235. ) {
  3236. trigger_error('array_column(): The column key should be either a string or an integer', E_USER_WARNING);
  3237. return false;
  3238. }
  3239. if (isset($params[2])
  3240. && !is_int($params[2])
  3241. && !is_float($params[2])
  3242. && !is_string($params[2])
  3243. && !(is_object($params[2]) && method_exists($params[2], '__toString'))
  3244. ) {
  3245. trigger_error('array_column(): The index key should be either a string or an integer', E_USER_WARNING);
  3246. return false;
  3247. }
  3248. $paramsInput = $params[0];
  3249. $paramsColumnKey = ($params[1] !== null) ? (string) $params[1] : null;
  3250. $paramsIndexKey = null;
  3251. if (isset($params[2])) {
  3252. if (is_float($params[2]) || is_int($params[2])) {
  3253. $paramsIndexKey = (int) $params[2];
  3254. } else {
  3255. $paramsIndexKey = (string) $params[2];
  3256. }
  3257. }
  3258. $resultArray = array();
  3259. foreach ($paramsInput as $row) {
  3260. $key = $value = null;
  3261. $keySet = $valueSet = false;
  3262. if ($paramsIndexKey !== null && array_key_exists($paramsIndexKey, $row)) {
  3263. $keySet = true;
  3264. $key = (string) $row[$paramsIndexKey];
  3265. }
  3266. if ($paramsColumnKey === null) {
  3267. $valueSet = true;
  3268. $value = $row;
  3269. } elseif (is_array($row) && array_key_exists($paramsColumnKey, $row)) {
  3270. $valueSet = true;
  3271. $value = $row[$paramsColumnKey];
  3272. }
  3273. if ($valueSet) {
  3274. if ($keySet) {
  3275. $resultArray[$key] = $value;
  3276. } else {
  3277. $resultArray[] = $value;
  3278. }
  3279. }
  3280. }
  3281. return $resultArray;
  3282. }
  3283. }
  3284. }
  3285. /**
  3286. * Nettoie une chaine pour servir comme classes CSS.
  3287. *
  3288. * @note
  3289. * les classes CSS acceptent théoriquement tous les caractères sauf NUL.
  3290. * Ici, on limite (enlève) les caractères autres qu’alphanumérique, espace, - + _ @
  3291. *
  3292. * @param string|string[] $classes
  3293. * @return string|string[]
  3294. */
  3295. function spip_sanitize_classname($classes) {
  3296. if (is_array($classes)) {
  3297. return array_map('spip_sanitize_classname', $classes);
  3298. }
  3299. return preg_replace("/[^ 0-9a-z_\-+@]/i", "", $classes);
  3300. }