Dépôt officiel du core SPIP * Anciennement présent sur svn://trac.rezo.net/spip * Les plugins-dist faisant partie de la distribution SPIP sont présents 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.

session.php 22 KiB

15 years ago
15 years ago
15 years ago
15 years ago
18 years ago
18 years ago
18 years ago
18 years ago
10 years ago
10 years ago
10 years ago
10 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744
  1. <?php
  2. /***************************************************************************\
  3. * SPIP, Système de publication pour l'internet *
  4. * *
  5. * Copyright © avec tendresse depuis 2001 *
  6. * Arnaud Martin, Antoine Pitrou, Philippe Rivière, Emmanuel Saint-James *
  7. * *
  8. * Ce programme est un logiciel libre distribué sous licence GNU/GPL. *
  9. * Pour plus de détails voir le fichier COPYING.txt ou l'aide en ligne. *
  10. \***************************************************************************/
  11. /**
  12. * Gestion de l'authentification par sessions
  13. * à utiliser pour valider l'accès (bloquant)
  14. * ou pour reconnaître un utilisateur (non bloquant)
  15. *
  16. * @package SPIP\Core\Session
  17. */
  18. if (!defined('_ECRIRE_INC_VERSION')) {
  19. return;
  20. }
  21. $GLOBALS['visiteur_session'] = array(); # globale decrivant l'auteur
  22. /**
  23. * 3 actions sur les sessions, selon le type de l'argument:
  24. *
  25. * - numerique: efface toutes les sessions de l'auteur (retour quelconque)
  26. * - tableau: cree une session pour l'auteur decrit et retourne l'identifiant
  27. * - bool: predicat de validite de la session indiquee par le cookie
  28. *
  29. * @uses supprimer_sessions()
  30. * @uses ajouter_session()
  31. * @uses verifier_session()
  32. *
  33. * @param int|array|bool $auteur
  34. * @return bool|null|void
  35. */
  36. function inc_session_dist($auteur = false) {
  37. if (is_numeric($auteur)) {
  38. return supprimer_sessions($auteur, $auteur > 0);
  39. } else {
  40. if (is_array($auteur)) {
  41. return ajouter_session($auteur);
  42. } else {
  43. return verifier_session($auteur);
  44. }
  45. }
  46. }
  47. /**
  48. * Supprimer toutes les vieilles sessions d'un auteur
  49. *
  50. * Cette fonction efface toutes les sessions appartenant a l'auteur
  51. * On en profite pour effacer toutes les sessions
  52. * creees il y a plus de 4*_RENOUVELLE_ALEA
  53. * Tenir compte de l'ancien format ou les noms commencaient par "session_"
  54. * et du meme coup des repertoires plats
  55. *
  56. * Attention : id_auteur peut etre negatif (cas des auteurs temporaires pendant le dump)
  57. *
  58. * @uses verifier_session()
  59. * @uses fichier_session()
  60. * @uses spip_session()
  61. *
  62. * @param int $id_auteur
  63. * Identifiant d'auteur dont on veut supprimer les sessions
  64. * @param bool $toutes
  65. * Supprimer aussi les vieilles sessions des autres auteurs ?
  66. * @param bool $actives
  67. * false pour ne pas supprimer les sessions valides de $id_auteur.
  68. * false revient donc a uniquement supprimer les vieilles sessions !
  69. */
  70. function supprimer_sessions($id_auteur, $toutes = true, $actives = true) {
  71. $nb_files = 0;
  72. $nb_max_files = (defined('_MAX_NB_SESSIONS_OUVERTES') ? _MAX_NB_SESSIONS_OUVERTES : 1000);
  73. spip_log("supprimer sessions auteur $id_auteur", "session");
  74. if ($toutes or $id_auteur !== $GLOBALS['visiteur_session']['id_auteur']) {
  75. if ($dir = opendir(_DIR_SESSIONS)) {
  76. $t = $_SERVER['REQUEST_TIME'] - (4*_RENOUVELLE_ALEA); // 48h par defaut
  77. $t_short = $_SERVER['REQUEST_TIME'] - max(_RENOUVELLE_ALEA/4,3*3600); // 3h par defaut
  78. $t = time() - (4 * _RENOUVELLE_ALEA);
  79. while (($f = readdir($dir)) !== false) {
  80. $nb_files++;
  81. if (preg_match(",^[^\d-]*(-?\d+)_\w{32}\.php[3]?$,", $f, $regs)) {
  82. $f = _DIR_SESSIONS . $f;
  83. if (($actives and $regs[1] == $id_auteur) or ($t > filemtime($f))) {
  84. spip_unlink($f);
  85. }
  86. // si il y a trop de sessions ouvertes, on purge les sessions anonymes de plus de 3H
  87. // cf http://core.spip.org/issues/3276
  88. elseif ($nb_files>$nb_max_files and !intval($regs[1]) and ($t_short > filemtime($f))) {
  89. spip_unlink($f);
  90. }
  91. }
  92. }
  93. }
  94. } else {
  95. verifier_session();
  96. spip_unlink(fichier_session('alea_ephemere', true));
  97. }
  98. // forcer le recalcul de la session courante
  99. spip_session(true);
  100. }
  101. /**
  102. * Ajoute une session pour l'auteur décrit par un tableau issu d'un SELECT-SQL
  103. *
  104. * @uses spip_php_session_start() Lorsque session anonyme
  105. * @uses hash_env()
  106. * @uses preparer_ecriture_session()
  107. * @uses fichier_session()
  108. * @uses ecrire_fichier_session()
  109. *
  110. * @param array $auteur
  111. * Description de la session de l'auteur. Peut contenir (par exemple)
  112. * les clés : id_auteur, nom, login, email, statut, lang, ...
  113. * @return bool|string
  114. */
  115. function ajouter_session($auteur) {
  116. // Si le client a deja une session valide pour son id_auteur
  117. // on conserve le meme fichier
  118. // Attention un visiteur peut avoir une session et un id=0,
  119. // => ne pas melanger les sessions des differents visiteurs
  120. $id_auteur = isset($auteur['id_auteur']) ? intval($auteur['id_auteur']) : 0;
  121. // Si ce n'est pas un inscrit (les inscrits ont toujours des choses en session)
  122. // on va vérifier s'il y a vraiment des choses à écrire
  123. if (!$id_auteur) {
  124. // On supprime les données de base pour voir le contenu réel de la session
  125. $auteur_verif = $auteur;
  126. if (isset($auteur_verif['id_auteur'])) {
  127. unset($auteur_verif['id_auteur']);
  128. }
  129. if (isset($auteur_verif['hash_env'])) {
  130. unset($auteur_verif['hash_env']);
  131. }
  132. if (isset($auteur_verif['ip_change'])) {
  133. unset($auteur_verif['ip_change']);
  134. }
  135. if (isset($auteur_verif['date_session'])) {
  136. unset($auteur_verif['date_session']);
  137. }
  138. // Les variables vraiment nulle ne sont pas à prendre en compte non plus
  139. foreach ($auteur_verif as $variable => $valeur) {
  140. if ($valeur === null) {
  141. unset($auteur_verif[$variable]);
  142. }
  143. }
  144. // Si après ça la session est vide alors on supprime l'éventuel fichier et on arrête là
  145. if (!$auteur_verif) {
  146. if (isset($_COOKIE['spip_session']) and isset($_SESSION[$_COOKIE['spip_session']])) {
  147. unset($_SESSION[$_COOKIE['spip_session']]);
  148. }
  149. if (isset($_COOKIE['spip_session'])) {
  150. unset($_COOKIE['spip_session']);
  151. }
  152. return false;
  153. }
  154. }
  155. if (!isset($_COOKIE['spip_session'])
  156. or !preg_match(',^' . $id_auteur . '_,', $_COOKIE['spip_session'])
  157. ) {
  158. $_COOKIE['spip_session'] = $id_auteur . '_' . md5(uniqid(rand(), true));
  159. }
  160. // Maintenant on sait qu'on a des choses à écrire
  161. // On s'assure d'avoir au moins ces valeurs
  162. $auteur['id_auteur'] = $id_auteur;
  163. if (!isset($auteur['hash_env'])) {
  164. $auteur['hash_env'] = hash_env();
  165. }
  166. if (!isset($auteur['ip_change'])) {
  167. $auteur['ip_change'] = false;
  168. }
  169. if (!isset($auteur['date_session'])) {
  170. $auteur['date_session'] = time();
  171. }
  172. if (isset($auteur['prefs'])
  173. and is_string($auteur['prefs'])
  174. and $prefs = @unserialize($auteur['prefs'])) {
  175. $auteur['prefs'] = $prefs;
  176. }
  177. $fichier_session = "";
  178. // les sessions anonymes sont stockees dans $_SESSION
  179. if (!$id_auteur) {
  180. spip_php_session_start();
  181. $_SESSION[$_COOKIE['spip_session']] = preparer_ecriture_session($auteur);
  182. } else {
  183. $fichier_session = fichier_session('alea_ephemere');
  184. if (!ecrire_fichier_session($fichier_session, $auteur)) {
  185. spip_log('Echec ecriture fichier session ' . $fichier_session, "session" . _LOG_HS);
  186. include_spip('inc/minipres');
  187. echo minipres();
  188. exit;
  189. }
  190. // verifier et limiter le nombre maxi de sessions
  191. // https://core.spip.net/issues/3807
  192. lister_sessions_auteur($id_auteur);
  193. }
  194. // poser le cookie de session SPIP
  195. include_spip('inc/cookie');
  196. $duree = definir_duree_cookie_session($auteur);
  197. spip_setcookie( 'spip_session', $_COOKIE['spip_session'], [
  198. 'expires' => time() + $duree
  199. ]);
  200. spip_log("ajoute session $fichier_session cookie $duree", "session");
  201. // Si on est admin, poser le cookie de correspondance
  202. if (!function_exists('autoriser')) {
  203. include_spip('inc/autoriser');
  204. }
  205. if (autoriser('ecrire','','',$auteur) and _DUREE_COOKIE_ADMIN) {
  206. spip_setcookie('spip_admin', '@' . ($auteur['email'] ?: $auteur['login']), [
  207. 'expires' => time() + max(_DUREE_COOKIE_ADMIN, $duree)
  208. ]);
  209. } // sinon le supprimer ...
  210. else {
  211. spip_setcookie('spip_admin', '', [
  212. 'expires' => 1
  213. ]);
  214. }
  215. # on en profite pour purger les vieilles sessions anonymes abandonnees
  216. # supprimer_sessions(0, true, false);
  217. return $_COOKIE['spip_session'];
  218. }
  219. /**
  220. * Calcule le temps de validité en seconde du cookie de session
  221. *
  222. * Applique un coefficient multiplicateur à la durée de renouvellement de l'alea
  223. * (noté ensuite `dR`, valant 12h par défaut) pour déterminer la durée du cookie.
  224. *
  225. * - `2 * dR`, par défaut
  226. * - `20 * dR` si le visiteur a indiqué vouloir rester connecté quelques jours
  227. * sur le formulaire de login (la clé `cookie` vaut alors `oui`)
  228. * - `c * dR`, un coeficient défini manuellement si la clé `cookie` est numérique
  229. *
  230. * @param array $auteur
  231. * Description de l'auteur
  232. * @return int
  233. * Durée en secondes
  234. **/
  235. function definir_duree_cookie_session($auteur) {
  236. $coef = 2;
  237. if (isset($auteur['cookie'])) {
  238. if (is_numeric($auteur['cookie'])) {
  239. $coef = $auteur['cookie'];
  240. } else {
  241. $coef = 20;
  242. }
  243. }
  244. return (int)(_RENOUVELLE_ALEA * $coef);
  245. }
  246. /**
  247. * Vérifie si le cookie spip_session indique une session valide
  248. *
  249. * Si oui, la decrit dans le tableau $visiteur_session et retourne id_auteur
  250. * La rejoue si IP change puis accepte le changement si $change=true
  251. *
  252. * Retourne false en cas d'echec, l'id_auteur de la session si defini, null sinon
  253. *
  254. * @uses spip_php_session_start() Si session anonyme
  255. * @uses fichier_session()
  256. * @uses ajouter_session()
  257. * @uses hash_env()
  258. *
  259. * @param bool $change
  260. * @return bool|int|null
  261. */
  262. function verifier_session($change = false) {
  263. // si pas de cookie, c'est fichu
  264. if (!isset($_COOKIE['spip_session'])) {
  265. return false;
  266. }
  267. $fichier_session = "";
  268. // est-ce une session anonyme ?
  269. if (!intval($_COOKIE['spip_session'])) {
  270. spip_php_session_start();
  271. if (!isset($_SESSION[$_COOKIE['spip_session']]) or !is_array($_SESSION[$_COOKIE['spip_session']])) {
  272. return false;
  273. }
  274. $GLOBALS['visiteur_session'] = $_SESSION[$_COOKIE['spip_session']];
  275. } else {
  276. // Tester avec alea courant
  277. $fichier_session = fichier_session('alea_ephemere', true);
  278. if ($fichier_session and @file_exists($fichier_session)) {
  279. include($fichier_session);
  280. } else {
  281. // Sinon, tester avec alea precedent
  282. $fichier_session = fichier_session('alea_ephemere_ancien', true);
  283. if (!$fichier_session or !@file_exists($fichier_session)) {
  284. return false;
  285. }
  286. // Renouveler la session avec l'alea courant
  287. include($fichier_session);
  288. spip_log('renouvelle session ' . $GLOBALS['visiteur_session']['id_auteur'], "session");
  289. spip_unlink($fichier_session);
  290. ajouter_session($GLOBALS['visiteur_session']);
  291. }
  292. }
  293. // Compatibilite ascendante : auteur_session est visiteur_session si
  294. // c'est un auteur SPIP authentifie (tandis qu'un visiteur_session peut
  295. // n'etre qu'identifie, sans aucune authentification).
  296. if (isset($GLOBALS['visiteur_session']['id_auteur']) and $GLOBALS['visiteur_session']['id_auteur']) {
  297. $GLOBALS['auteur_session'] = &$GLOBALS['visiteur_session'];
  298. }
  299. // Si l'adresse IP change, inc/presentation mettra une balise image
  300. // avec un URL de rappel demandant a changer le nom de la session.
  301. // Seul celui qui a l'IP d'origine est rejoue
  302. // ainsi un eventuel voleur de cookie ne pourrait pas deconnecter
  303. // sa victime, mais se ferait deconnecter par elle.
  304. if (hash_env() != $GLOBALS['visiteur_session']['hash_env']) {
  305. if (!$GLOBALS['visiteur_session']['ip_change']) {
  306. define('_SESSION_REJOUER', true);
  307. $GLOBALS['visiteur_session']['ip_change'] = true;
  308. ajouter_session($GLOBALS['visiteur_session']);
  309. } else {
  310. if ($change) {
  311. spip_log("session non rejouee, vol de cookie ?", "session");
  312. }
  313. }
  314. } else {
  315. if ($change) {
  316. spip_log("rejoue session $fichier_session " . $_COOKIE['spip_session'], "session");
  317. if ($fichier_session) {
  318. spip_unlink($fichier_session);
  319. }
  320. $GLOBALS['visiteur_session']['ip_change'] = false;
  321. unset($_COOKIE['spip_session']);
  322. ajouter_session($GLOBALS['visiteur_session']);
  323. }
  324. }
  325. // Si la session a ete initiee il y a trop longtemps, elle est annulee
  326. if (isset($GLOBALS['visiteur_session'])
  327. and defined('_AGE_SESSION_MAX')
  328. and _AGE_SESSION_MAX > 0
  329. and time() - @$GLOBALS['visiteur_session']['date_session'] > _AGE_SESSION_MAX
  330. ) {
  331. unset($GLOBALS['visiteur_session']);
  332. return false;
  333. }
  334. return is_numeric($GLOBALS['visiteur_session']['id_auteur'])
  335. ? $GLOBALS['visiteur_session']['id_auteur']
  336. : null;
  337. }
  338. /**
  339. * Lire une valeur dans la session SPIP
  340. *
  341. * @api
  342. * @example `$login = session_get('login');`
  343. *
  344. * @param string $nom
  345. * Clé dont on souhaite la valeur
  346. * @return mixed|null
  347. * Valeur, si trouvée, `null` sinon.
  348. */
  349. function session_get($nom) {
  350. return isset($GLOBALS['visiteur_session'][$nom]) ? $GLOBALS['visiteur_session'][$nom] : null;
  351. }
  352. /**
  353. * Ajouter une donnée dans la session SPIP
  354. *
  355. * @api
  356. * @uses ajouter_session()
  357. * @uses terminer_actualiser_sessions() Ajoute la fonction en fin de hit.
  358. *
  359. * @param string $nom
  360. * @param null $val
  361. * @return void|array
  362. */
  363. function session_set($nom, $val = null) {
  364. static $remove = array();
  365. static $actualiser_sessions = false;
  366. if ($nom === false) {
  367. return $remove;
  368. }
  369. if (is_null($val)) {
  370. // rien a faire
  371. if (!isset($GLOBALS['visiteur_session'][$nom])) {
  372. return;
  373. }
  374. unset($GLOBALS['visiteur_session'][$nom]);
  375. $remove[] = $nom;
  376. } else {
  377. // On ajoute la valeur dans la globale
  378. $GLOBALS['visiteur_session'][$nom] = $val;
  379. if ($remove) {
  380. $remove = array_diff($remove, array($nom));
  381. }
  382. }
  383. if (!$actualiser_sessions) {
  384. // il faut creer la session si on en a pas, la premiere fois
  385. ajouter_session($GLOBALS['visiteur_session']);
  386. // in register la fonction qui mettra a jour toutes les sessions en fin de hit
  387. register_shutdown_function('terminer_actualiser_sessions');
  388. $actualiser_sessions = true;
  389. }
  390. }
  391. /**
  392. * En fin de hit, synchroniser toutes les sessions
  393. *
  394. * @uses actualiser_sessions()
  395. */
  396. function terminer_actualiser_sessions() {
  397. // se remettre dans le dossier de travail au cas ou Apache a change
  398. chdir(_ROOT_CWD);
  399. // recuperer les variables a effacer
  400. $remove = session_set(false);
  401. // mettre a jour toutes les sessions
  402. actualiser_sessions($GLOBALS['visiteur_session'], $remove);
  403. }
  404. /**
  405. * Mettre à jour les sessions existantes pour un auteur
  406. *
  407. * Quand on modifie une fiche auteur on appelle cette fonction qui va
  408. * mettre à jour les fichiers de session de l'auteur en question.
  409. * (auteurs identifiés seulement)
  410. *
  411. * Ne concerne que les sessions des auteurs loges (id_auteur connu)
  412. *
  413. * @uses ajouter_session()
  414. * @uses fichier_session()
  415. * @uses preg_files()
  416. * @uses preparer_ecriture_session()
  417. * @uses ecrire_fichier_session()
  418. *
  419. * @param array $auteur
  420. * @param array $supprimer_cles
  421. * Liste des clés à supprimer des tableaux de sessions
  422. */
  423. function actualiser_sessions($auteur, $supprimer_cles = array()) {
  424. $id_auteur = isset($auteur['id_auteur']) ? intval($auteur['id_auteur']) : 0;
  425. $id_auteur_courant = isset($GLOBALS['visiteur_session']['id_auteur']) ? intval($GLOBALS['visiteur_session']['id_auteur']) : 0;
  426. // si l'auteur est celui de la session courante, verifier/creer la session si besoin
  427. $fichier_session_courante = "";
  428. if ($id_auteur == $id_auteur_courant) {
  429. $auteur = array_merge($GLOBALS['visiteur_session'], $auteur);
  430. ajouter_session($auteur);
  431. if ($id_auteur) {
  432. $fichier_session_courante = fichier_session('alea_ephemere');
  433. }
  434. }
  435. // si session anonyme on ne fait rien d'autre ici : les sessions anonymes sont non partagees
  436. if (!$id_auteur) {
  437. return;
  438. }
  439. // les préférences sont désérialisées, toujours.
  440. // [fixme] Le champ 'prefs' sert aussi à l’inscription
  441. if (isset($auteur['prefs']) and is_string($auteur['prefs'])) {
  442. $auteur['prefs'] = @unserialize($auteur['prefs']);
  443. if (!is_array($auteur['prefs'])) {
  444. $auteur['prefs'] = [];
  445. }
  446. }
  447. // memoriser l'auteur courant (celui qui modifie la fiche)
  448. $sauve = $GLOBALS['visiteur_session'];
  449. // .. mettre a jour les sessions de l'auteur cible
  450. // attention au $ final pour ne pas risquer d'embarquer un .php.jeton temporaire
  451. // cree par une ecriture concurente d'une session (fichier atomique temporaire)
  452. $sessions = lister_sessions_auteur($id_auteur);
  453. // 1ere passe : lire et fusionner les sessions
  454. foreach ($sessions as $session) {
  455. $GLOBALS['visiteur_session'] = array();
  456. // a pu etre supprime entre le preg initial et le moment ou l'on arrive la (concurrence)
  457. if ($session !== $fichier_session_courante
  458. and @file_exists($session)
  459. ) {
  460. include $session; # $GLOBALS['visiteur_session'] est alors l'auteur cible
  461. $auteur = array_merge($GLOBALS['visiteur_session'], $auteur);
  462. }
  463. }
  464. // supprimer les eventuelles cles dont on ne veut plus
  465. foreach ($supprimer_cles as $cle) {
  466. unset($auteur[$cle]);
  467. }
  468. $auteur_session = preparer_ecriture_session($auteur);
  469. // seconde passe : ecrire les sessions qui ne sont pas a jour
  470. foreach ($sessions as $session) {
  471. $GLOBALS['visiteur_session'] = array();
  472. // a pu etre supprime entre le preg initial et le moment ou l'on arrive la (concurrence)
  473. if (@file_exists($session)) {
  474. include $session; # $GLOBALS['visiteur_session'] est alors l'auteur cible
  475. // est-ce que cette session est a mettre a jour ?
  476. if ($auteur_session != $GLOBALS['visiteur_session']) {
  477. ecrire_fichier_session($session, $auteur);
  478. }
  479. }
  480. }
  481. if ($id_auteur == $id_auteur_courant) {
  482. $GLOBALS['visiteur_session'] = $auteur;
  483. $GLOBALS['auteur_session'] = &$GLOBALS['visiteur_session'];
  484. } else {
  485. // restaurer l'auteur courant
  486. $GLOBALS['visiteur_session'] = $sauve;
  487. }
  488. }
  489. /**
  490. * lister les sessions et en verifier le nombre maxi
  491. * en supprimant les plus anciennes si besoin
  492. * https://core.spip.net/issues/3807
  493. *
  494. * @param int $id_auteur
  495. * @param int $nb_max
  496. * @return array
  497. */
  498. function lister_sessions_auteur($id_auteur, $nb_max = null) {
  499. if (is_null($nb_max)) {
  500. if (!defined('_NB_SESSIONS_MAX')) {
  501. define('_NB_SESSIONS_MAX', 100);
  502. }
  503. $nb_max = _NB_SESSIONS_MAX;
  504. }
  505. // liste des sessions
  506. $sessions = preg_files(_DIR_SESSIONS, '/' . $id_auteur . '_.*\.php$');
  507. // si on en a plus que la limite, supprimer les plus vieilles
  508. // si ce ne sont pas des sessions anonymes car elles sont alors chacune differentes
  509. if ($id_auteur
  510. and count($sessions) > $nb_max) {
  511. // limiter le nombre de sessions ouvertes par un auteur
  512. // filemtime sur les sessions
  513. $sessions = array_flip($sessions);
  514. // 1ere passe : lire les filemtime
  515. foreach ($sessions as $session => $z) {
  516. if ($d = @filemtime($session)
  517. ) {
  518. $sessions[$session] = $d;
  519. } else {
  520. $sessions[$session] = 0;
  521. }
  522. }
  523. // les plus anciennes en premier
  524. asort($sessions);
  525. $sessions = array_keys($sessions);
  526. while (count($sessions) > $nb_max) {
  527. $session = array_shift($sessions);
  528. @unlink($session);
  529. }
  530. }
  531. return $sessions;
  532. }
  533. /**
  534. * Préparer le tableau de session avant écriture
  535. *
  536. * Nettoyage de quelques variables sensibles, et appel d'un pipeline
  537. *
  538. * @pipeline preparer_fichier_session
  539. * @param array $auteur
  540. * @return array
  541. */
  542. function preparer_ecriture_session($auteur) {
  543. $row = $auteur;
  544. // ne pas enregistrer ces elements de securite
  545. // dans le fichier de session
  546. unset($auteur['pass']);
  547. unset($auteur['htpass']);
  548. unset($auteur['low_sec']);
  549. unset($auteur['alea_actuel']);
  550. unset($auteur['alea_futur']);
  551. $auteur = pipeline('preparer_fichier_session', array('args' => array('row' => $row), 'data' => $auteur));
  552. // ne pas enregistrer les valeurs vraiment nulle dans le fichier
  553. foreach ($auteur as $variable => $valeur) {
  554. if ($valeur === null) {
  555. unset($auteur[$variable]);
  556. }
  557. }
  558. return $auteur;
  559. }
  560. /**
  561. * Ecrire le fichier d'une session
  562. *
  563. * @param string $fichier
  564. * @param array $auteur
  565. * @return bool
  566. */
  567. function ecrire_fichier_session($fichier, $auteur) {
  568. $auteur = preparer_ecriture_session($auteur);
  569. // enregistrer les autres donnees du visiteur
  570. $texte = "<" . "?php\n";
  571. foreach ($auteur as $var => $val) {
  572. $texte .= '$GLOBALS[\'visiteur_session\'][' . var_export($var, true) . '] = '
  573. . var_export($val, true) . ";\n";
  574. }
  575. $texte .= "?" . ">\n";
  576. return ecrire_fichier($fichier, $texte);
  577. }
  578. /**
  579. * Calculer le nom du fichier session
  580. *
  581. * @param string $alea
  582. * @param bool $tantpis
  583. * @return string
  584. */
  585. function fichier_session($alea, $tantpis = false) {
  586. include_spip('inc/acces');
  587. charger_aleas();
  588. if (empty($GLOBALS['meta'][$alea])) {
  589. if (!$tantpis) {
  590. spip_log("fichier session ($tantpis): $alea indisponible", "session");
  591. include_spip('inc/minipres');
  592. echo minipres();
  593. }
  594. return ''; // echec mais $tanpis
  595. } else {
  596. $repertoire = sous_repertoire(_DIR_SESSIONS, '', false, $tantpis);
  597. $c = $_COOKIE['spip_session'];
  598. return $repertoire . intval($c) . '_' . md5($c . ' ' . $GLOBALS['meta'][$alea]) . '.php';
  599. }
  600. }
  601. /**
  602. * Code à insérer par `inc/presentation` pour rejouer la session
  603. *
  604. * @note
  605. * Pourquoi insère-t-on le src par js et non directement en statique dans le HTML ?
  606. * Historiquement, inséré par une balise `<script>` en r424
  607. * puis modifié par `<img>` statique + js en r427
  608. *
  609. * @see action_cookie_dist() qui sera appelé
  610. *
  611. * @return string
  612. */
  613. function rejouer_session() {
  614. return '<img src="' . generer_url_action('cookie', 'change_session=oui', true) . '" width="0" height="0" alt="" />';
  615. }
  616. /**
  617. * On verifie l'IP et le nom du navigateur
  618. *
  619. * @return string
  620. */
  621. function hash_env() {
  622. static $res = '';
  623. if ($res) {
  624. return $res;
  625. }
  626. return $res = md5($GLOBALS['ip'] . (isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : ''));
  627. }
  628. /**
  629. * Démarre une session PHP si ce n'est pas déjà fait.
  630. *
  631. * @link http://php.net/manual/fr/function.session-start.php
  632. * @uses is_php_session_started()
  633. * @return bool True si une session PHP est ouverte.
  634. **/
  635. function spip_php_session_start() {
  636. if (!is_php_session_started()) {
  637. return session_start();
  638. }
  639. return true;
  640. }
  641. /**
  642. * Indique si une sesssion PHP est active
  643. *
  644. * @link http://php.net/manual/fr/function.session-status.php#113468
  645. * @return bool true si une session PHP est active
  646. **/
  647. function is_php_session_started() {
  648. if (php_sapi_name() !== 'cli') {
  649. return session_status() === PHP_SESSION_ACTIVE ? true : false;
  650. }
  651. return false;
  652. }