Core  3.2
PHP API documentation
 All Data Structures Namespaces Files Functions Variables Pages
Class.Session.php
Go to the documentation of this file.
1 <?php
2 /*
3  * @author Anakeen
4  * @package FDL
5  * * Syntaxe :
6  * ---------
7  * $session = new Session();
8 */
9 
10 $CLASS_SESSION_PHP = '$Id: Class.Session.php,v 1.38 2009/01/12 15:15:31 jerome Exp $';
11 include_once ('Class.QueryDb.php');
12 include_once ('Class.DbObj.php');
13 include_once ('Class.Log.php');
14 include_once ('Class.User.php');
15 include_once ("Class.SessionCache.php");
16 
17 class Session extends DbObj
18 {
19  const SESSION_CT_CLOSE = 2;
20  const SESSION_CT_ARGS = 3;
21  const SESSION_MIN_BYTE_LENGTH = 16; /* 16 bytes = 128 bits */
22  var $fields = array(
23  "id",
24  "userid",
25  "name",
26  "last_seen"
27  );
28 
29  var $id_fields = array(
30  "id"
31  );
32  public $id;
33  public $userid;
34  public $name;
35  public $last_seen;
36  public $status;
37  private $sendCookie = true;
38  var $dbtable = "sessions";
39 
40  var $sqlcreate = "create table sessions ( id text,
41  userid int,
42  name text not null,
43  last_seen timestamp not null DEFAULT now() );
44  create unique index sessions_idx on sessions(id);
45  create index sessions_idx_userid on sessions(userid);";
46 
48 
49  const PARAMNAME = 'dcpsession';
50  var $session_name = self::PARAMNAME;
51  function __construct($session_name = self::PARAMNAME, $sendCookie = true)
52  {
53  if (!empty($_SERVER['HTTP_HOST'])) {
54  include_once ("config/sessionHandler.php");
55  }
56  parent::__construct();
57  if ($session_name != '') $this->session_name = $session_name;
58  $this->last_seen = strftime('%Y-%m-%d %H:%M:%S', time());
59  $this->sendCookie = ($sendCookie === true);
60  }
61 
62  function set($id = "")
63  {
64  global $_SERVER;
65 
66  if (!$this->sessionDirExistsAndIsWritable()) {
67  return false;
68  }
69 
70  $this->gcSessions();
71  $createNewSession = true;
72 
73  if ($id) {
74  $query = new QueryDb($this->dbaccess, "Session");
75  $query->addQuery("id = '" . pg_escape_string($id) . "'");
76  $list = $query->Query(0, 0, "TABLE");
77  if ($query->nb != 0) {
78  $this->Affect($list[0]);
79  if (!$this->hasExpired()) {
80  $createNewSession = false;
81  $this->touch();
82  session_name($this->session_name);
83  session_id($id);
84  @session_start();
85  @session_write_close(); // avoid block
86 
87  }
88  }
89  }
90 
91  if ($createNewSession) {
92  $u = new Account();
93  if ((!empty($_SERVER['PHP_AUTH_USER'])) && $u->SetLoginName($_SERVER['PHP_AUTH_USER'])) {
94  $this->open($u->id);
95  } else {
96  $this->open(Account::ANONYMOUS_ID); //anonymous session
97 
98  }
99  }
100  // set cookie session
101  if (!empty($_SERVER['HTTP_HOST'])) {
102 
103  $this->setCookieSession($this->id, $this->SetTTL());
104  }
105  return true;
106  }
107 
108  protected static function getWebRootPath_Apache()
109  {
110  if (!isset($_SERVER['SCRIPT_FILENAME'])) {
111  return false;
112  }
113  if (!isset($_SERVER['SCRIPT_NAME'])) {
114  return false;
115  }
116  /*
117  * Get absolute context's pathname (with trailing '/'):
118  *
119  * "/var/www/foo/"
120  *
121  */
122  $contextRoot = realpath(DEFAULT_PUBDIR);
123  if ($contextRoot === false) {
124  return false;
125  }
126  $contextRoot.= '/';
127  /*
128  * Get absolute script's filename:
129  *
130  * "/var/www/foo/bar/baz.php"
131  *s
132  */
133  $scriptFilename = realpath($_SERVER['SCRIPT_FILENAME']);
134  if ($scriptFilename === false) {
135  return false;
136  }
137  /*
138  * Remove leading context's pathname from script's filename:
139  *
140  * "/var/www/foo/bar/baz.php" - "^/var/www/foo/" => "bar/baz.php"
141  *
142  * This gives us the script's filename relative to the context's root.
143  */
144  $pos = strpos($scriptFilename, $contextRoot);
145  if ($pos !== 0) {
146  return false;
147  }
148  $relativeScriptFilename = substr($scriptFilename, strlen($contextRoot));
149  /*
150  * Remove trailing relative script's filename from script's name by finding the
151  * relative script's filename by the right :
152  *
153  * "/x/y/z/bar/baz.php" - "bar/baz.php$" => "/x/y/z/"
154  *
155  * This gives us the Web root directory.
156  */
157  $scriptName = $_SERVER['SCRIPT_NAME'];
158  $pos = strrpos($scriptName, $relativeScriptFilename);
159  $webRootLen = (strlen($scriptName) - strlen($relativeScriptFilename));
160  if ($pos !== $webRootLen) {
161  return false;
162  }
163  $webRoot = substr($scriptName, 0, $webRootLen);
164  return $webRoot;
165  }
166 
167  protected static function getWebRootPath_CoreUrlIndex()
168  {
169  $coreUrlIndex = \ApplicationParameterManager::getParameterValue('CORE', 'CORE_URLINDEX');
170  if (is_string($coreUrlIndex)) {
171  $tokens = parse_url($coreUrlIndex);
172  if (!isset($tokens['path'])) {
173  return '/';
174  }
175  $webRoot = $tokens['path'];
176  /* Append trailing '/' if not already present */
177  if (substr($webRoot, -1, 1) !== '/') {
178  $webRoot.= '/';
179  }
180  return $webRoot;
181  }
182  return false;
183  }
184 
185  /**
186  * Try to autodetect « web root path » (i.e. the URL path as seen from the user stand point to reach the root of
187  * the Dynacase context).
188  *
189  * @param mixed $default The default value to return is all auto-detection fails.
190  * @return string|mixed Returns the « web root path » or the $default value if the path could not be detected with
191  * enough confidence
192  */
193  public static function getWebRootPath($default = false)
194  {
195  /*
196  * 1) Try to detect web root from CORE_URLINDEX
197  */
198  if (($webRoot = self::getWebRootPath_CoreUrlIndex()) !== false) {
199  return $webRoot;
200  }
201  /*
202  * 2) Try to detect web root from Apache environment variables
203  */
204  if (($webRoot = self::getWebRootPath_Apache()) !== false) {
205  return $webRoot;
206  }
207  return $default;
208  }
209 
210  function setCookieSession($id, $ttl = 0)
211  {
212  $webRootPath = self::getWebRootPath('/');
213  $cookiePath = preg_replace(':/+:', '/', $webRootPath);
214  $this->setcookie($this->name, $id, $ttl, $cookiePath, null, null, true);
215  }
216  /**
217  * Closes session and removes all datas
218  */
219  function close()
220  {
221  global $_SERVER; // use only cache with HTTP
222  if (!empty($_SERVER['HTTP_HOST'])) {
223  session_name($this->name);
224  session_id($this->id);
225  @session_start();
226  @session_destroy();
227  // delete session cookie
228  $this->setcookie($this->name, false, time() - 3600, null, null, null, true);
229  $this->Delete();
230  }
231  $this->status = self::SESSION_CT_CLOSE;
232  return $this->status;
233  }
234  /**
235  * Closes all session
236  */
237  function closeAll($uid = null)
238  {
239  if ($uid === null) {
240  $this->exec_query(sprintf("delete from sessions where name = '%s';", pg_escape_string($this->session_name)));
241  } else {
242  $this->exec_query(sprintf("delete from sessions where name = '%s' and userid=%d;", pg_escape_string($this->session_name) , $uid));
243  }
244  $this->status = self::SESSION_CT_CLOSE;
245  return $this->status;
246  }
247  /**
248  * Closes all user's sessions
249  */
250  function closeUsers($uid = - 1)
251  {
252  if (!$uid > 0) return '';
253  $this->exec_query("delete from sessions where userid= '" . pg_escape_string($uid) . "'");
254  $this->status = self::SESSION_CT_CLOSE;
255  return $this->status;
256  }
257 
258  function open($uid = Account::ANONYMOUS_ID)
259  {
260  $idsess = $this->newId();
261  global $_SERVER; // use only cache with HTTP
262  if (!empty($_SERVER['HTTP_HOST'])) {
263  session_name($this->session_name);
264  session_id($idsess);
265  @session_start();
266  @session_write_close(); // avoid block
267  // $this->initCache();
268 
269  }
270  $this->name = $this->session_name;
271  $this->id = $idsess;
272  $this->userid = $uid;
273  $this->last_seen = strftime('%Y-%m-%d %H:%M:%S', time());
274  $this->Add();
275  $this->log->debug("Nouvelle Session : {$this->id}");
276  }
277  // --------------------------------
278  // Stocke une variable de session args
279  // $v est une chaine !
280  // --------------------------------
281  function register($k = "", $v = "")
282  {
283  if ($k == "") {
284  $this->status = self::SESSION_CT_ARGS;
285  return $this->status;
286  }
287  global $_SERVER; // use only cache with HTTP
288  if (!empty($_SERVER['HTTP_HOST'])) {
289  session_name($this->name);
290  session_id($this->id);
291  @session_start();
292  $_SESSION[$k] = $v;
293  @session_write_close(); // avoid block
294 
295  }
296 
297  return true;
298  }
299  // --------------------------------
300  // Récupère une variable de session
301  // $v est une chaine !
302  // --------------------------------
303  function read($k = "", $d = "")
304  {
305  if (empty($_SERVER['HTTP_HOST'])) {
306  return ($d);
307  }
308  /* Load session's data only once as requested by #4825 */
309  $sessionOpened = false;
310  if (!isset($_SESSION)) {
311  session_name($this->name);
312  session_id($this->id);
313  @session_start();
314  $sessionOpened = true;
315  }
316  if (isset($_SESSION[$k])) {
317  $val = $_SESSION[$k];
318  } else {
319  $val = $d;
320  }
321  if ($sessionOpened) {
322  @session_write_close();
323  }
324  return $val;
325  }
326  // --------------------------------
327  // Détruit une variable de session
328  // $v est une chaine !
329  // --------------------------------
330  function unregister($k = "")
331  {
332  global $_SERVER; // use only cache with HTTP
333  if ($this->name && !empty($_SERVER['HTTP_HOST'])) {
334  session_name($this->name);
335  session_id($this->id);
336  @session_start();
337  unset($_SESSION[$k]);
338  @session_write_close(); // avoid block
339 
340  }
341  return true;
342  }
343  /**
344  * Get, or generate, a "cache busting" key
345  *
346  * @param string $prefix
347  * @return string
348  */
349  public function getUKey($prefix = '')
350  {
351  $uKey = $this->read('_uKey_', false);
352  if ($uKey === false) {
353  $uKey = uniqid($prefix);
354  $this->register('_uKey_', $uKey);
355  }
356  return $uKey;
357  }
358  // ------------------------------------------------------------------------
359  // utilities functions (private)
360  // ------------------------------------------------------------------------
361  function newId()
362  {
363  $this->log->debug("newId");
364  $byteLength = (int)getParam('CORE_SESSION_BYTE_LENGTH');
365  if ($byteLength < self::SESSION_MIN_BYTE_LENGTH) {
366  $byteLength = self::SESSION_MIN_BYTE_LENGTH;
367  }
368  return self::randomId($byteLength);
369  }
370  /**
371  * Get a new cryptographically strong random id
372  *
373  * Throws an exception if no cryptographically strong random bytes could be
374  * obtained from openssl: this might occurs on broken or old system.
375  *
376  * @param int $byteLength The number of bytes to get from the CSPRNG
377  * @return string The random bytes in hexadecimal representation (e.g. "a7d1f43b")
378  * @throws \Dcp\Exception
379  */
380  private static function randomId($byteLength)
381  {
382  $strong = false;
383  $bytes = openssl_random_pseudo_bytes($byteLength, $strong);
384  if ($bytes === false || $strong === false) {
385  throw new \Dcp\Exception(sprintf("Unable to get cryptographically strong random bytes from openssl: your system might be broken or too old."));
386  }
387  return bin2hex($bytes);
388  }
389  /**
390  * replace value of global parameter in session cache
391  * @param string $paramName
392  * @param string $paramValue
393  * @return bool
394  */
395  function replaceGlobalParam($paramName, $paramValue)
396  {
397  global $_SERVER; // use only cache with HTTP
398  if (!empty($_SERVER['HTTP_HOST'])) {
399  session_name($this->name);
400  session_id($this->id);
401  @session_start();
402  foreach ($_SESSION as $k => $v) {
403  if (preg_match("/^sessparam[0-9]+$/", $k)) {
404  if (isset($v[$paramName])) {
405  $_SESSION[$k][$paramName] = $paramValue;
406  }
407  }
408  }
409  @session_write_close(); // avoid block
410 
411  }
412  return true;
413  }
414  function setTTL()
415  {
416  $ttliv = $this->getSessionTTL(0);
417  if ($ttliv > 0) {
418  //$ttli->CloseConnect();
419  return (time() + $ttliv);
420  }
421  return 0;
422  }
423 
424  function getSessionTTL($default = 0, $ttlParamName = '')
425  {
426  if ($ttlParamName == '') {
427  if ($this->userid == Account::ANONYMOUS_ID) {
428  $ttlParamName = 'CORE_GUEST_SESSIONTTL';
429  } else {
430  $ttlParamName = 'CORE_SESSIONTTL';
431  }
432  }
433  return intval(getParam($ttlParamName, $default));
434  }
435 
436  function getSessionGcProbability($default = "0.01")
437  {
438  return getParam("CORE_SESSIONGCPROBABILITY", $default);
439  }
440 
441  function touch()
442  {
443  $this->last_seen = strftime('%Y-%m-%d %H:%M:%S', time());
444  $err = $this->modify();
445  return $err;
446  }
447 
449  {
450  $ttl = $this->getSessionTTL(0, 'CORE_SESSIONTTL');
451  if ($ttl > 0) {
452  return $this->exec_query(sprintf("DELETE FROM sessions WHERE userid != %s AND last_seen < timestamp 'now()' - interval '%s seconds'", Account::ANONYMOUS_ID, pg_escape_string($ttl)));
453  }
454  return '';
455  }
456 
458  {
459  $ttl = $this->getSessionTTL(0, 'CORE_GUEST_SESSIONTTL');
460  if ($ttl > 0) {
461  return $this->exec_query(sprintf("DELETE FROM sessions WHERE userid = %s AND last_seen < timestamp 'now()' - interval '%s seconds'", Account::ANONYMOUS_ID, pg_escape_string($ttl)));
462  }
463  return '';
464  }
465 
467  {
468  $maxage = getParam('CORE_SESSIONMAXAGE', '');
469  if ($maxage != '') {
470  return $this->exec_query(sprintf("DELETE FROM sessions WHERE last_seen < timestamp 'now()' - interval '%s'", pg_escape_string($maxage)));
471  }
472  return '';
473  }
474 
475  function gcSessions()
476  {
477  $gcP = $this->getSessionGcProbability();
478  if ($gcP <= 0) {
479  return "";
480  }
481  $p = rand() / getrandmax();
482  if ($p <= $gcP) {
483  $err = $this->deleteUserExpiredSessions();
484  if ($err != "") {
485  error_log(__CLASS__ . "::" . __FUNCTION__ . " " . "Error cleaning up user sessions: " . $err);
486  }
487  $err = $this->deleteGuestExpiredSessions();
488  if ($err != "") {
489  error_log(__CLASS__ . "::" . __FUNCTION__ . " " . "Error cleaning up guest sessions: " . $err);
490  }
491  $err = $this->deleteMaxAgedSessions();
492  if ($err != "") {
493  error_log(__CLASS__ . "::" . __FUNCTION__ . " " . "Error cleaning up max-aged sessions: " . $err);
494  }
495  }
496  return "";
497  }
498 
499  function setuid($uid)
500  {
501  if (!is_int($uid)) {
502  $u = new Account();
503  if ($u->SetLoginName($uid)) {
504  $uid = $u->id;
505  } else {
506  $err = "Could not resolve login name '" . $uid . "' to uid";
507  error_log(__CLASS__ . "::" . __FUNCTION__ . " " . $err);
508  return $err;
509  }
510  }
511  if ($this->userid != $uid) {
512  if (isset($_SESSION)) {
513  $sessionCopy = $_SESSION;
514  // Reset session id when user id does not match regitered session user id
515  $this->close();
516  $this->set();
517  session_id($this->id);
518  session_start();
519  // Copy session values from old to new session
520  foreach ($sessionCopy as $k => $v) {
521  $_SESSION[$k] = $v;
522  }
523  session_write_close(); // avoid block
524 
525  }
526  }
527 
528  $this->userid = $uid;
529  return $this->modify();
530  }
531 
533  {
534  include_once ('WHAT/Lib.Prefix.php');
535 
536  global $pubdir;
537 
538  $sessionDir = sprintf("%s/var/session", $pubdir);
539  if (!is_dir($sessionDir)) {
540  trigger_error(sprintf("Session directory '%s' does not exists.", $sessionDir));
541  return false;
542  }
543 
544  if (!is_writable($sessionDir)) {
545  trigger_error(sprintf("Session directory '%s' is not writable.", $sessionDir));
546  return false;
547  }
548 
549  return true;
550  }
551 
552  function hasExpired()
553  {
554  include_once ('FDL/Lib.Util.php');
555  $ttl = $this->getSessionTTL(0);
556  if ($ttl > 0) {
557  $now = time();
558  $last_seen = stringDateToUnixTs($this->last_seen);
559  if ($now > $last_seen + $ttl) {
560  return true;
561  }
562  }
563  return false;
564  }
565 
566  function removeSessionFile($sessid = null)
567  {
568  include_once ('WHAT/Lib.Prefix.php');
569  global $pubdir;
570  if ($sessid === null) {
571  $sessid = $this->id;
572  }
573  $sessionFile = sprintf("%s/var/session/sess_%s", $pubdir, $sessid);
574  if (file_exists($sessionFile)) {
575  unlink($sessionFile);
576  }
577  }
578  /**
579  * Delete all user's sessions except the current session.
580  *
581  * @param string $userId The user id (default is $this->userid)
582  * @param string $exceptSessionId The session id to keep (default is $this->id)
583  * @return string empty string on success, or the SQL error message
584  */
585  function deleteUserSessionsExcept($userId = '', $exceptSessionId = '')
586  {
587  if ($userId == '') {
588  $userId = $this->userid;
589  }
590  if ($exceptSessionId == '') {
591  $exceptSessionId = $this->id;
592  }
593  return $this->exec_query(sprintf("DELETE FROM sessions WHERE userid = %d AND id != '%s'", $userId, pg_escape_string($exceptSessionId)));
594  }
595  private function setcookie($name, $value = null, $expire = null, $path = null, $domain = null, $secure = null, $httponly = null)
596  {
597  if ($this->sendCookie) {
598  if ($path === null) {
599  $webRootPath = self::getWebRootPath('/');
600  $path = preg_replace(':/+:', '/', $webRootPath);
601  }
602  return setcookie($name, $value, $expire, $path, $domain, $secure, $httponly);
603  }
604  return false;
605  }
606 } // Class Session
const SESSION_MIN_BYTE_LENGTH
open($uid=Account::ANONYMOUS_ID)
getUKey($prefix= '')
deleteGuestExpiredSessions()
global $pubdir
Definition: vault_init.php:18
deleteUserExpiredSessions()
exec_query($sql, $lvl=0, $prepare=false)
closeUsers($uid=-1)
print $fam getTitle() $fam name
Add($nopost=false, $nopre=false)
deleteUserSessionsExcept($userId= '', $exceptSessionId= '')
const ANONYMOUS_ID
static getParameterValue($application, $parameterName)
getSessionGcProbability($default="0.01")
deleteMaxAgedSessions()
setuid($uid)
replaceGlobalParam($paramName, $paramValue)
$d
Definition: dav.php:77
getSessionTTL($default=0, $ttlParamName= '')
sessionDirExistsAndIsWritable()
const DEFAULT_PUBDIR
Definition: Lib.Prefix.php:28
modify($nopost=false, $sfields="", $nopre=false)
static getWebRootPath($default=false)
const SESSION_CT_ARGS
static getWebRootPath_Apache()
$path
Definition: dav.php:39
set($id="")
getParam($name, $def="")
must be in core or global type
Definition: Lib.Common.php:193
closeAll($uid=null)
static getWebRootPath_CoreUrlIndex()
global $_SERVER
$CLASS_SESSION_PHP
read($k="", $d="")
stringDateToUnixTs($isodate, $utc=false)
Definition: Lib.Util.php:217
__construct($session_name=self::PARAMNAME, $sendCookie=true)
if(($docid!==0)&&(!is_numeric($docid))) $query
setCookieSession($id, $ttl=0)
const PARAMNAME
if($file) if($subject==""&&$file) if($subject=="") $err
$sessid
Definition: pack.php:51
removeSessionFile($sessid=null)
unregister($k="")
const SESSION_CT_CLOSE
$value
← centre documentaire © anakeen