Completed
Push — master ( 8d3210...15390a )
by cam
16:23 queued 09:26
created

session.php ➔ is_php_session_started()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 4
nc 3
nop 0
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
/***************************************************************************\
4
 *  SPIP, Systeme de publication pour l'internet                           *
5
 *                                                                         *
6
 *  Copyright (c) 2001-2017                                                *
7
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
8
 *                                                                         *
9
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
10
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
11
\***************************************************************************/
12
13
/**
14
 * Gestion de l'authentification par sessions
15
 * à utiliser pour valider l'accès (bloquant)
16
 * ou pour reconnaître un utilisateur (non bloquant)
17
 *
18
 * @package SPIP\Core\Session
19
 */
20
21
if (!defined('_ECRIRE_INC_VERSION')) {
22
	return;
23
}
24
25
26
$GLOBALS['visiteur_session'] = array(); # globale decrivant l'auteur
27
28
/**
29
 * 3 actions sur les sessions, selon le type de l'argument:
30
 *
31
 * - numerique: efface toutes les sessions de l'auteur (retour quelconque)
32
 * - tableau: cree une session pour l'auteur decrit et retourne l'identifiant
33
 * - bool: predicat de validite de la session indiquee par le cookie
34
 *
35
 * @uses supprimer_sessions()
36
 * @uses ajouter_session()
37
 * @uses verifier_session()
38
 *
39
 * @param int|array|bool $auteur
40
 * @return bool|null|void
0 ignored issues
show
Documentation introduced by
Should the return type not be null|string|boolean|integer?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
41
 */
42
function inc_session_dist($auteur = false) {
43
	if (is_numeric($auteur)) {
44
		return supprimer_sessions($auteur, $auteur > 0);
45
	} else {
46
		if (is_array($auteur)) {
47
			return ajouter_session($auteur);
48
		} else {
49
			return verifier_session($auteur);
50
		}
51
	}
52
}
53
54
55
/**
56
 * Supprimer toutes les vieilles sessions d'un auteur
57
 *
58
 * Cette fonction efface toutes les sessions appartenant a l'auteur
59
 * On en profite pour effacer toutes les sessions
60
 * creees il y a plus de 4*_RENOUVELLE_ALEA
61
 * Tenir compte de l'ancien format ou les noms commencaient par "session_"
62
 * et du meme coup des repertoires plats
63
 *
64
 * Attention : id_auteur peut etre negatif (cas des auteurs temporaires pendant le dump)
65
 *
66
 * @uses verifier_session()
67
 * @uses fichier_session()
68
 * @uses spip_session()
69
 *
70
 * @param int $id_auteur
71
 *     Identifiant d'auteur dont on veut supprimer les sessions
72
 * @param bool $toutes
73
 *     Supprimer aussi les vieilles sessions des autres auteurs ?
74
 * @param bool $actives
75
 *     false pour ne pas supprimer les sessions valides de $id_auteur.
76
 *     false revient donc a uniquement supprimer les vieilles sessions !
77
 */
78
function supprimer_sessions($id_auteur, $toutes = true, $actives = true) {
79
80
	spip_log("supprimer sessions auteur $id_auteur", "session");
81
	if ($toutes or $id_auteur !== $GLOBALS['visiteur_session']['id_auteur']) {
82
		if ($dir = opendir(_DIR_SESSIONS)) {
83
			$t = time() - (4 * _RENOUVELLE_ALEA);
84
			while (($f = readdir($dir)) !== false) {
85
				if (preg_match(",^[^\d-]*(-?\d+)_\w{32}\.php[3]?$,", $f, $regs)) {
86
					$f = _DIR_SESSIONS . $f;
87
					if (($actives and $regs[1] == $id_auteur) or ($t > filemtime($f))) {
88
						spip_unlink($f);
89
					}
90
				}
91
			}
92
		}
93
	} else {
94
		verifier_session();
95
		spip_unlink(fichier_session('alea_ephemere', true));
96
	}
97
98
	// forcer le recalcul de la session courante
99
	spip_session(true);
100
}
101
102
/**
103
 * Ajoute une session pour l'auteur décrit par un tableau issu d'un SELECT-SQL
104
 *
105
 * @uses spip_php_session_start() Lorsque session anonyme
106
 * @uses hash_env()
107
 * @uses preparer_ecriture_session()
108
 * @uses fichier_session()
109
 * @uses ecrire_fichier_session()
110
 *
111
 * @param array $auteur
112
 *     Description de la session de l'auteur. Peut contenir (par exemple)
113
 *     les clés : id_auteur, nom, login, email, statut, lang, ...
114
 * @return bool|string
0 ignored issues
show
Documentation introduced by
Should the return type not be false|null|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
115
 */
116
function ajouter_session($auteur) {
117
	// Si le client a deja une session valide pour son id_auteur
118
	// on conserve le meme fichier
119
120
	// Attention un visiteur peut avoir une session et un id=0,
121
	// => ne pas melanger les sessions des differents visiteurs
122
	$id_auteur = isset($auteur['id_auteur']) ? intval($auteur['id_auteur']) : 0;
123
124
	// Si ce n'est pas un inscrit (les inscrits ont toujours des choses en session)
125
	// on va vérifier s'il y a vraiment des choses à écrire
126
	if (!$id_auteur) {
127
		// On supprime les données de base pour voir le contenu réel de la session
128
		$auteur_verif = $auteur;
129
		if (isset($auteur_verif['id_auteur'])) {
130
			unset($auteur_verif['id_auteur']);
131
		}
132
		if (isset($auteur_verif['hash_env'])) {
133
			unset($auteur_verif['hash_env']);
134
		}
135
		if (isset($auteur_verif['ip_change'])) {
136
			unset($auteur_verif['ip_change']);
137
		}
138
		if (isset($auteur_verif['date_session'])) {
139
			unset($auteur_verif['date_session']);
140
		}
141
142
		// Les variables vraiment nulle ne sont pas à prendre en compte non plus
143
		foreach ($auteur_verif as $variable => $valeur) {
144
			if ($valeur === null) {
145
				unset($auteur_verif[$variable]);
146
			}
147
		}
148
149
		// Si après ça la session est vide alors on supprime l'éventuel fichier et on arrête là
150
		if (!$auteur_verif) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $auteur_verif of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
151
			if (isset($_COOKIE['spip_session']) and isset($_SESSION[$_COOKIE['spip_session']])) {
152
				unset($_SESSION[$_COOKIE['spip_session']]);
153
			}
154
			if (isset($_COOKIE['spip_session'])) {
155
				unset($_COOKIE['spip_session']);
156
			}
157
158
			return false;
159
		}
160
	}
161
162
	if (!isset($_COOKIE['spip_session'])
163
		or !preg_match(',^' . $id_auteur . '_,', $_COOKIE['spip_session'])
164
	) {
165
		$_COOKIE['spip_session'] = $id_auteur . '_' . md5(uniqid(rand(), true));
166
	}
167
168
	// Maintenant on sait qu'on a des choses à écrire
169
	// On s'assure d'avoir au moins ces valeurs
170
	$auteur['id_auteur'] = $id_auteur;
171
	if (!isset($auteur['hash_env'])) {
172
		$auteur['hash_env'] = hash_env();
173
	}
174
	if (!isset($auteur['ip_change'])) {
175
		$auteur['ip_change'] = false;
176
	}
177
178
	if (!isset($auteur['date_session'])) {
179
		$auteur['date_session'] = time();
180
	}
181 View Code Duplication
	if (isset($auteur['prefs']) and is_string($auteur['prefs'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
182
		$auteur['prefs'] = unserialize($auteur['prefs']);
183
	}
184
185
	$fichier_session = "";
186
187
	// les sessions anonymes sont stockees dans $_SESSION
188
	if (!$id_auteur) {
189
		spip_php_session_start();
190
		$_SESSION[$_COOKIE['spip_session']] = preparer_ecriture_session($auteur);
191
	} else {
192
		$fichier_session = fichier_session('alea_ephemere');
193
		if (!ecrire_fichier_session($fichier_session, $auteur)) {
194
			spip_log('Echec ecriture fichier session ' . $fichier_session, "session" . _LOG_HS);
195
			include_spip('inc/minipres');
196
			echo minipres();
197
			exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The function ajouter_session() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
198
		}
199
		// verifier et limiter le nombre maxi de sessions
200
		// https://core.spip.net/issues/3807
201
		lister_sessions_auteur($id_auteur);
202
	}
203
204
	// poser le cookie de session SPIP
205
	include_spip('inc/cookie');
206
	$duree = definir_duree_cookie_session($auteur);
207
	spip_setcookie( 'spip_session', $_COOKIE['spip_session'], time() + $duree);
208
	spip_log("ajoute session $fichier_session cookie $duree", "session");
209
210
	// Si on est admin, poser le cookie de correspondance
211
	if (!function_exists('autoriser')) {
212
		include_spip('inc/autoriser');
213
	}
214
	if (autoriser('ecrire','','',$auteur) and _DUREE_COOKIE_ADMIN) {
215
		spip_setcookie('spip_admin', '@' . $auteur['login'], time() + max(_DUREE_COOKIE_ADMIN, $duree));
216
	} // sinon le supprimer ...
217
	else {
218
		spip_setcookie('spip_admin', '', 1);
219
	}
220
221
	# on en profite pour purger les vieilles sessions anonymes abandonnees
222
	# supprimer_sessions(0, true, false);
223
224
	return $_COOKIE['spip_session'];
225
}
226
227
/**
228
 * Calcule le temps de validité en seconde du cookie de session
229
 *
230
 * Applique un coefficient multiplicateur à la durée de renouvellement de l'alea 
231
 * (noté ensuite `dR`, valant 12h par défaut) pour déterminer la durée du cookie.
232
 * 
233
 * - `2 * dR`, par défaut 
234
 * - `20 * dR` si le visiteur a indiqué vouloir rester connecté quelques jours 
235
 *    sur le formulaire de login (la clé `cookie` vaut alors `oui`) 
236
 * - `c * dR`, un coeficient défini manuellement si la clé `cookie` est numérique
237
 * 
238
 * @param array $auteur
239
 *     Description de l'auteur
240
 * @return int
241
 *     Durée en secondes
242
**/
243
function definir_duree_cookie_session($auteur) {
244
	$coef = 2;
245
	if (isset($auteur['cookie'])) {
246
		if (is_numeric($auteur['cookie'])) {
247
			$coef = $auteur['cookie'];
248
		} else {
249
			$coef = 20;
250
		}
251
	}
252
	return (int)(_RENOUVELLE_ALEA * $coef);
253
}
254
255
/**
256
 * Vérifie si le cookie spip_session indique une session valide
257
 *
258
 * Si oui, la decrit dans le tableau $visiteur_session et retourne id_auteur
259
 * La rejoue si IP change puis accepte le changement si $change=true
260
 *
261
 * Retourne false en cas d'echec, l'id_auteur de la session si defini, null sinon
262
 *
263
 * @uses spip_php_session_start() Si session anonyme
264
 * @uses fichier_session()
265
 * @uses ajouter_session()
266
 * @uses hash_env()
267
 *
268
 * @param bool $change
269
 * @return bool|int|null
270
 */
271
function verifier_session($change = false) {
272
	// si pas de cookie, c'est fichu
273
	if (!isset($_COOKIE['spip_session'])) {
274
		return false;
275
	}
276
277
	$fichier_session = "";
278
279
	// est-ce une session anonyme ?
280
	if (!intval($_COOKIE['spip_session'])) {
281
		spip_php_session_start();
282
		if (!isset($_SESSION[$_COOKIE['spip_session']]) or !is_array($_SESSION[$_COOKIE['spip_session']])) {
283
			return false;
284
		}
285
		$GLOBALS['visiteur_session'] = $_SESSION[$_COOKIE['spip_session']];
286
	} else {
287
		// Tester avec alea courant
288
		$fichier_session = fichier_session('alea_ephemere', true);
289
		if ($fichier_session and @file_exists($fichier_session)) {
290
			include($fichier_session);
291
		} else {
292
			// Sinon, tester avec alea precedent
293
			$fichier_session = fichier_session('alea_ephemere_ancien', true);
294
			if (!$fichier_session or !@file_exists($fichier_session)) {
295
				return false;
296
			}
297
298
			// Renouveler la session avec l'alea courant
299
			include($fichier_session);
300
			spip_log('renouvelle session ' . $GLOBALS['visiteur_session']['id_auteur'], "session");
301
			spip_unlink($fichier_session);
302
			ajouter_session($GLOBALS['visiteur_session']);
303
		}
304
	}
305
306
	// Compatibilite ascendante : auteur_session est visiteur_session si
307
	// c'est un auteur SPIP authentifie (tandis qu'un visiteur_session peut
308
	// n'etre qu'identifie, sans aucune authentification).
309
310
	if (isset($GLOBALS['visiteur_session']['id_auteur']) and $GLOBALS['visiteur_session']['id_auteur']) {
311
		$GLOBALS['auteur_session'] = &$GLOBALS['visiteur_session'];
312
	}
313
314
315
	// Si l'adresse IP change, inc/presentation mettra une balise image
316
	// avec un URL de rappel demandant a changer le nom de la session.
317
	// Seul celui qui a l'IP d'origine est rejoue
318
	// ainsi un eventuel voleur de cookie ne pourrait pas deconnecter
319
	// sa victime, mais se ferait deconnecter par elle.
320
	if (hash_env() != $GLOBALS['visiteur_session']['hash_env']) {
321
		if (!$GLOBALS['visiteur_session']['ip_change']) {
322
			define('_SESSION_REJOUER', true);
323
			$GLOBALS['visiteur_session']['ip_change'] = true;
324
			ajouter_session($GLOBALS['visiteur_session']);
325
		} else {
326
			if ($change) {
327
				spip_log("session non rejouee, vol de cookie ?", "session");
328
			}
329
		}
330
	} else {
331
		if ($change) {
332
			spip_log("rejoue session $fichier_session " . $_COOKIE['spip_session'], "session");
333
			if ($fichier_session) {
334
				spip_unlink($fichier_session);
335
			}
336
			$GLOBALS['visiteur_session']['ip_change'] = false;
337
			unset($_COOKIE['spip_session']);
338
			ajouter_session($GLOBALS['visiteur_session']);
339
		}
340
	}
341
342
	// Si la session a ete initiee il y a trop longtemps, elle est annulee
343
	if (isset($GLOBALS['visiteur_session'])
344
		and defined('_AGE_SESSION_MAX')
345
		and _AGE_SESSION_MAX > 0
346
		and time() - @$GLOBALS['visiteur_session']['date_session'] > _AGE_SESSION_MAX
347
	) {
348
		unset($GLOBALS['visiteur_session']);
349
350
		return false;
351
	}
352
353
	return is_numeric($GLOBALS['visiteur_session']['id_auteur'])
354
		? $GLOBALS['visiteur_session']['id_auteur']
355
		: null;
356
}
357
358
/**
359
 * Lire une valeur dans la session SPIP
360
 *
361
 * @api
362
 * @example `$login = session_get('login');`
363
 *
364
 * @param string $nom
365
 *    Clé dont on souhaite la valeur
366
 * @return mixed|null
367
 *     Valeur, si trouvée, `null` sinon.
368
 */
369
function session_get($nom) {
370
	return isset($GLOBALS['visiteur_session'][$nom]) ? $GLOBALS['visiteur_session'][$nom] : null;
371
}
372
373
374
/**
375
 * Ajouter une donnée dans la session SPIP
376
 *
377
 * @api
378
 * @uses ajouter_session()
379
 * @uses terminer_actualiser_sessions() Ajoute la fonction en fin de hit.
380
 *
381
 * @param string $nom
382
 * @param null $val
383
 * @return void|array
384
 */
385
function session_set($nom, $val = null) {
386
	static $remove = array();
387
	static $actualiser_sessions = false;
388
	if ($nom === false) {
389
		return $remove;
390
	}
391
	if (is_null($val)) {
392
		// rien a faire
393
		if (!isset($GLOBALS['visiteur_session'][$nom])) {
394
			return;
395
		}
396
		unset($GLOBALS['visiteur_session'][$nom]);
397
		$remove[] = $nom;
398
	} else {
399
		// On ajoute la valeur dans la globale
400
		$GLOBALS['visiteur_session'][$nom] = $val;
401
		if ($remove) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $remove of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
402
			$remove = array_diff($remove, array($nom));
403
		}
404
	}
405
	if (!$actualiser_sessions) {
406
		// il faut creer la session si on en a pas, la premiere fois
407
		ajouter_session($GLOBALS['visiteur_session']);
408
		// in register la fonction qui mettra a jour toutes les sessions en fin de hit
409
		register_shutdown_function('terminer_actualiser_sessions');
410
		$actualiser_sessions = true;
411
	}
412
}
413
414
/**
415
 * En fin de hit, synchroniser toutes les sessions
416
 *
417
 * @uses actualiser_sessions()
418
 */
419
function terminer_actualiser_sessions() {
420
	// se remettre dans le dossier de travail au cas ou Apache a change
421
	chdir(_ROOT_CWD);
422
	// recuperer les variables a effacer
423
	$remove = session_set(false);
424
	// mettre a jour toutes les sessions
425
	actualiser_sessions($GLOBALS['visiteur_session'], $remove);
0 ignored issues
show
Bug introduced by
It seems like $remove defined by session_set(false) on line 423 can also be of type null; however, actualiser_sessions() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
426
}
427
428
429
/**
430
 * Mettre à jour les sessions existantes pour un auteur
431
 *
432
 * Quand on modifie une fiche auteur on appelle cette fonction qui va
433
 * mettre à jour les fichiers de session de l'auteur en question.
434
 * (auteurs identifiés seulement)
435
 *
436
 * Ne concerne que les sessions des auteurs loges (id_auteur connu)
437
 *
438
 * @uses ajouter_session()
439
 * @uses fichier_session()
440
 * @uses preg_files()
441
 * @uses preparer_ecriture_session()
442
 * @uses ecrire_fichier_session()
443
 *
444
 * @param array $auteur
445
 * @param array $supprimer_cles
446
 *     Liste des clés à supprimer des tableaux de sessions
447
 */
448
function actualiser_sessions($auteur, $supprimer_cles = array()) {
449
450
	$id_auteur = isset($auteur['id_auteur']) ? intval($auteur['id_auteur']) : 0;
451
	$id_auteur_courant = isset($GLOBALS['visiteur_session']['id_auteur']) ? intval($GLOBALS['visiteur_session']['id_auteur']) : 0;
452
453
	// si l'auteur est celui de la session courante, verifier/creer la session si besoin
454
	$fichier_session_courante = "";
455
	if ($id_auteur == $id_auteur_courant) {
456
		$auteur = array_merge($GLOBALS['visiteur_session'], $auteur);
457
		ajouter_session($auteur);
458
		if ($id_auteur) {
459
			$fichier_session_courante = fichier_session('alea_ephemere');
460
		}
461
	}
462
463
	// si session anonyme on ne fait rien d'autre ici : les sessions anonymes sont non partagees
464
	if (!$id_auteur) {
465
		return;
466
	}
467
468
	// les préférences sont désérialisées, toujours.
469 View Code Duplication
	if (isset($auteur['prefs']) and is_string($auteur['prefs'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
470
		$auteur['prefs'] = unserialize($auteur['prefs']);
471
	}
472
473
	// memoriser l'auteur courant (celui qui modifie la fiche)
474
	$sauve = $GLOBALS['visiteur_session'];
475
476
	// .. mettre a jour les sessions de l'auteur cible
477
	// attention au $ final pour ne pas risquer d'embarquer un .php.jeton temporaire
478
	// cree par une ecriture concurente d'une session (fichier atomique temporaire)
479
	$sessions = lister_sessions_auteur($id_auteur);
480
481
	// 1ere passe : lire et fusionner les sessions
482 View Code Duplication
	foreach ($sessions as $session) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
483
		$GLOBALS['visiteur_session'] = array();
484
		// a pu etre supprime entre le preg initial et le moment ou l'on arrive la (concurrence)
485
		if ($session !== $fichier_session_courante
486
			and @file_exists($session)
487
		) {
488
			include $session; # $GLOBALS['visiteur_session'] est alors l'auteur cible
489
490
			$auteur = array_merge($GLOBALS['visiteur_session'], $auteur);
491
		}
492
	}
493
494
	// supprimer les eventuelles cles dont on ne veut plus
495
	foreach ($supprimer_cles as $cle) {
496
		unset($auteur[$cle]);
497
	}
498
499
	$auteur_session = preparer_ecriture_session($auteur);
500
501
	// seconde passe : ecrire les sessions qui ne sont pas a jour
502 View Code Duplication
	foreach ($sessions as $session) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
503
		$GLOBALS['visiteur_session'] = array();
504
		// a pu etre supprime entre le preg initial et le moment ou l'on arrive la (concurrence)
505
		if (@file_exists($session)) {
506
			include $session; # $GLOBALS['visiteur_session'] est alors l'auteur cible
507
508
			// est-ce que cette session est a mettre a jour ?
509
			if ($auteur_session != $GLOBALS['visiteur_session']) {
510
				ecrire_fichier_session($session, $auteur);
511
			}
512
		}
513
	}
514
515
	if ($id_auteur == $id_auteur_courant) {
516
		$GLOBALS['visiteur_session'] = $auteur;
517
		$GLOBALS['auteur_session'] = &$GLOBALS['visiteur_session'];
518
	} else {
519
		// restaurer l'auteur courant
520
		$GLOBALS['visiteur_session'] = $sauve;
521
	}
522
523
}
524
525
/**
526
 * lister les sessions et en verifier le nombre maxi
527
 * en supprimant les plus anciennes si besoin
528
 * https://core.spip.net/issues/3807
529
 *
530
 * @param int $id_auteur
531
 * @param int $nb_max
0 ignored issues
show
Documentation introduced by
Should the type for parameter $nb_max not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
532
 * @return array
533
 */
534
function lister_sessions_auteur($id_auteur, $nb_max = null) {
535
536
	if (is_null($nb_max)) {
537
		if (!defined('_NB_SESSIONS_MAX')) {
538
			define('_NB_SESSIONS_MAX', 100);
539
		}
540
		$nb_max = _NB_SESSIONS_MAX;
541
	}
542
543
	// liste des sessions
544
	$sessions = preg_files(_DIR_SESSIONS, '/' . $id_auteur . '_.*\.php$');
545
546
	// si on en a plus que la limite, supprimer les plus vieilles
547
	// si ce ne sont pas des sessions anonymes car elles sont alors chacune differentes
548
	if ($id_auteur
549
		and count($sessions) > $nb_max) {
550
551
		// limiter le nombre de sessions ouvertes par un auteur
552
		// filemtime sur les sessions
553
		$sessions = array_flip($sessions);
554
555
		// 1ere passe : lire les filemtime
556
		foreach ($sessions as $session => $z) {
557
			if ($d = @filemtime($session)
558
			) {
559
				$sessions[$session] = $d;
560
			} else {
561
				$sessions[$session] = 0;
562
			}
563
		}
564
565
		// les plus anciennes en premier
566
		asort($sessions);
567
568
		$sessions = array_keys($sessions);
569
		while (count($sessions) > $nb_max) {
570
			$session = array_shift($sessions);
571
			@unlink($session);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
572
		}
573
	}
574
575
	return $sessions;
576
}
577
578
579
/**
580
 * Préparer le tableau de session avant écriture
581
 *
582
 * Nettoyage de quelques variables sensibles, et appel d'un pipeline
583
 *
584
 * @pipeline preparer_fichier_session
585
 * @param array $auteur
586
 * @return array
587
 */
588
function preparer_ecriture_session($auteur) {
589
	$row = $auteur;
590
591
	// ne pas enregistrer ces elements de securite
592
	// dans le fichier de session
593
	unset($auteur['pass']);
594
	unset($auteur['htpass']);
595
	unset($auteur['low_sec']);
596
	unset($auteur['alea_actuel']);
597
	unset($auteur['alea_futur']);
598
599
	$auteur = pipeline('preparer_fichier_session', array('args' => array('row' => $row), 'data' => $auteur));
600
601
	// ne pas enregistrer les valeurs vraiment nulle dans le fichier
602
	foreach ($auteur as $variable => $valeur) {
603
		if ($valeur === null) {
604
			unset($auteur[$variable]);
605
		}
606
	}
607
608
	return $auteur;
609
}
610
611
/**
612
 * Ecrire le fichier d'une session
613
 *
614
 * @param string $fichier
615
 * @param array $auteur
616
 * @return bool
617
 */
618
function ecrire_fichier_session($fichier, $auteur) {
619
620
	$auteur = preparer_ecriture_session($auteur);
621
622
	// enregistrer les autres donnees du visiteur
623
	$texte = "<" . "?php\n";
624
	foreach ($auteur as $var => $val) {
625
		$texte .= '$GLOBALS[\'visiteur_session\'][\'' . $var . '\'] = '
626
			. var_export($val, true) . ";\n";
627
	}
628
	$texte .= "?" . ">\n";
629
630
	return ecrire_fichier($fichier, $texte);
631
}
632
633
634
/**
635
 * Calculer le nom du fichier session
636
 *
637
 * @param string $alea
638
 * @param bool $tantpis
639
 * @return string
640
 */
641
function fichier_session($alea, $tantpis = false) {
642
643
	include_spip('inc/acces');
644
	charger_aleas();
645
646
	if (empty($GLOBALS['meta'][$alea])) {
647
		if (!$tantpis) {
648
			spip_log("fichier session ($tantpis): $alea indisponible", "session");
649
			include_spip('inc/minipres');
650
			echo minipres();
651
		}
652
653
		return ''; // echec mais $tanpis
654
	} else {
655
		$repertoire = sous_repertoire(_DIR_SESSIONS, '', false, $tantpis);
656
		$c = $_COOKIE['spip_session'];
657
658
		return $repertoire . intval($c) . '_' . md5($c . ' ' . $GLOBALS['meta'][$alea]) . '.php';
659
	}
660
}
661
662
663
/**
664
 * Code à insérer par `inc/presentation` pour rejouer la session
665
 *
666
 * @note
667
 *   Pourquoi insère-t-on le src par js et non directement en statique dans le HTML ?
668
 *   Historiquement, inséré par une balise `<script>` en r424
669
 *   puis modifié par `<img>` statique + js en r427
670
 *
671
 * @see action_cookie_dist() qui sera appelé
672
 *
673
 * @return string
674
 */
675
function rejouer_session() {
676
	return '<img src="' . generer_url_action('cookie', 'change_session=oui', true) . '" width="0" height="0" alt="" />';
677
}
678
679
680
/**
681
 * On verifie l'IP et le nom du navigateur
682
 *
683
 * @return string
684
 */
685
function hash_env() {
686
	static $res = '';
687
	if ($res) {
688
		return $res;
689
	}
690
691
	return $res = md5($GLOBALS['ip'] . (isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : ''));
692
}
693
694
695
/**
696
 * Démarre une session PHP si ce n'est pas déjà fait.
697
 *
698
 * @link http://php.net/manual/fr/function.session-start.php
699
 * @uses is_php_session_started()
700
 * @return bool True si une session PHP est ouverte.
701
 **/
702
function spip_php_session_start() {
703
	if (!is_php_session_started()) {
704
		return session_start();
705
	}
706
707
	return true;
708
}
709
710
/**
711
 * Indique si une sesssion PHP est active
712
 *
713
 * @link http://php.net/manual/fr/function.session-status.php#113468
714
 * @return bool true si une session PHP est active
715
 **/
716
function is_php_session_started() {
717
	if (php_sapi_name() !== 'cli') {
718
		return session_status() === PHP_SESSION_ACTIVE ? true : false;
719
	}
720
721
	return false;
722
}
723