[ Index ]
 

Code source de b2evolution 2.1.0-beta

Accédez au Source d'autres logiciels libres

Classes | Fonctions | Variables | Constantes | Tables

title

Body

[fermer]

/blogs/inc/sessions/model/ -> _session.class.php (source)

   1  <?php
   2  /**

   3   * This file implements the Session class and holds the

   4   * {@link session_unserialize_callback()} function used by it.

   5   *

   6   * A session can be bound to a user and provides functions to store data in its

   7   * context.

   8   * All Hitlogs are also bound to a Session.

   9   *

  10   * This file is part of the evoCore framework - {@link http://evocore.net/}

  11   * See also {@link http://sourceforge.net/projects/evocms/}.

  12   *

  13   * @copyright (c)2003-2007 by Francois PLANQUE - {@link http://fplanque.net/}

  14   * Parts of this file are copyright (c)2004-2006 by Daniel HAHLER - {@link http://thequod.de/contact}.

  15   *

  16   * {@internal License choice

  17   * - If you have received this file as part of a package, please find the license.txt file in

  18   *   the same folder or the closest folder above for complete license terms.

  19   * - If you have received this file individually (e-g: from http://evocms.cvs.sourceforge.net/)

  20   *   then you must choose one of the following licenses before using the file:

  21   *   - GNU General Public License 2 (GPL) - http://www.opensource.org/licenses/gpl-license.php

  22   *   - Mozilla Public License 1.1 (MPL) - http://www.opensource.org/licenses/mozilla1.1.php

  23   * }}

  24   *

  25   * {@internal Open Source relicensing agreement:

  26   * Daniel HAHLER grants Francois PLANQUE the right to license

  27   * Daniel HAHLER's contributions to this file and the b2evolution project

  28   * under any OSI approved OSS license (http://www.opensource.org/licenses/).

  29   *

  30   * Matt FOLLETT grants Francois PLANQUE the right to license

  31   * Matt FOLLETT's contributions to this file and the b2evolution project

  32   * under any OSI approved OSS license (http://www.opensource.org/licenses/).

  33   * }}

  34   *

  35   * @package evocore

  36   *

  37   * {@internal Below is a list of authors who have contributed to design/coding of this file: }}

  38   * @author blueyed: Daniel HAHLER.

  39   * @author fplanque: Francois PLANQUE.

  40   * @author jeffbearer: Jeff BEARER - {@link http://www.jeffbearer.com/}.

  41   * @author mfollett:  Matt FOLLETT - {@link http://www.mfollett.com/}.

  42   *

  43   * @version $Id: _session.class.php,v 1.1 2007/06/25 11:01:00 fplanque Exp $

  44   */
  45  if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );
  46  
  47  
  48  /**

  49   * A session tracks a given user (not necessarily logged in) while he's navigating the site.

  50   * A sessions also stores data for the length of the session.

  51   *

  52   * Sessions are tracked with a cookie containing the session ID.

  53   * The cookie also contains a random key to prevent sessions hacking.

  54   *

  55   * @package evocore

  56   */
  57  class Session
  58  {
  59      /**

  60       * The ID of the session.

  61       * @var integer

  62       */
  63      var $ID;
  64  
  65      /**

  66       * The session key (to be used in URLs).

  67       * @var string

  68       */
  69      var $key;
  70  
  71      /**

  72       * The user ID for the user of the session (NULL for anonymous (not logged in) user).

  73       *

  74       * @var integer

  75       */
  76      var $user_ID;
  77  
  78      /**

  79       * Is the session validated?

  80       * This means that it was created from a received cookie.

  81       * @var boolean

  82       */
  83      var $is_validated = false;
  84  
  85      /**

  86       * Data stored for the session.

  87       *

  88       * This holds an array( expire, value ) for each data item key.

  89       *

  90       * @access protected

  91       * @var array

  92       */
  93      var $_data;
  94  
  95      var $_session_needs_save = false;
  96  
  97  
  98      /**

  99       * Constructor

 100       */
 101  	function Session()
 102      {
 103          global $DB, $Debuglog, $current_User, $localtimenow, $Messages, $Settings;
 104          global $Hit;
 105          global $cookie_session, $cookie_expires, $cookie_path, $cookie_domain;
 106  
 107          $Debuglog->add( 'cookie_domain='.$cookie_domain, 'session' );
 108          $Debuglog->add( 'cookie_path='.$cookie_path, 'session' );
 109  
 110          $session_cookie = param_cookie( $cookie_session, 'string', '' );
 111          if( empty( $session_cookie ) )
 112          {
 113              $Debuglog->add( 'No session cookie received.', 'session' );
 114          }
 115          else
 116          { // session ID sent by cookie
 117              if( ! preg_match( '~^(\d+)_(\w+)$~', $session_cookie, $match ) )
 118              {
 119                  $Debuglog->add( 'Invalid session cookie format!', 'session' );
 120              }
 121              else
 122              {    // We have a valid session cookie:
 123                  $session_id_by_cookie = $match[1];
 124                  $session_key_by_cookie = $match[2];
 125  
 126                  $Debuglog->add( 'Session ID received from cookie: '.$session_id_by_cookie, 'session' );
 127  
 128                  $row = $DB->get_row( '
 129                      SELECT sess_ID, sess_key, sess_data, sess_user_ID
 130                        FROM T_sessions
 131                       WHERE sess_ID  = '.$DB->quote($session_id_by_cookie).'
 132                         AND sess_key = '.$DB->quote($session_key_by_cookie).'
 133                         AND UNIX_TIMESTAMP(sess_lastseen) > '.($localtimenow - $Settings->get('timeout_sessions')) );
 134                  if( empty( $row ) )
 135                  {
 136                      $Debuglog->add( 'Session ID/key combination is invalid!', 'session' );
 137                  }
 138                  else
 139                  { // ID + key are valid: load data
 140                      $Debuglog->add( 'Session ID is valid.', 'session' );
 141                      $this->ID = $row->sess_ID;
 142                      $this->key = $row->sess_key;
 143                      $this->user_ID = $row->sess_user_ID;
 144                      $this->is_validated = true;
 145  
 146                      $Debuglog->add( 'Session user_ID: '.var_export($this->user_ID, true), 'session' );
 147  
 148                      if( empty( $row->sess_data ) )
 149                      {
 150                          $Debuglog->add( 'No session data available.', 'session' );
 151                          $this->_data = array();
 152                      }
 153                      else
 154                      { // Some session data has been previsouly stored:
 155  
 156                          // Unserialize session data (using an own callback that should provide class definitions):

 157                          $old_callback = ini_set( 'unserialize_callback_func', 'session_unserialize_callback' );
 158                          if( $old_callback === false )
 159                          { // this can fail, if "ini_set" has been disabled for security reasons.. :/
 160                              // Brutally load add classes that we might need:

 161                               session_unserialize_load_all_classes();
 162                          }
 163                          // TODO: dh> This can fail, if there are special chars in sess_data:

 164                          //       It will be encoded in $evo_charset _after_ "SET NAMES", but

 165                          //       get retrieved here, _before_ any "SET NAMES" (if $db_config['connection_charset'] is not set (default))!

 166                          $this->_data = @unserialize($row->sess_data);
 167  
 168                          if( $old_callback !== false )
 169                          {    // Restore the old callback if we changed it:
 170                              ini_set( 'unserialize_callback_func', $old_callback );
 171                          }
 172  
 173                          if( ! is_array($this->_data) )
 174                          {
 175                              $Debuglog->add( 'Session data corrupted!<br />
 176                                  connection_charset: '.var_export($DB->connection_charset, true).'<br />
 177                                  Serialized data was: --['.var_export($row->sess_data, true).']--', array('session','error') );
 178                              $this->_data = array();
 179                          }
 180                          else
 181                          {
 182                              $Debuglog->add( 'Session data loaded.', 'session' );
 183  
 184                              // Load a Messages object from session data, if available:

 185                              if( ($sess_Messages = $this->get('Messages')) && is_a( $sess_Messages, 'log' ) )
 186                              {
 187                                  // dh> TODO: "old" messages should rather get prepended to any existing ones from the current request, rather than appended

 188                                  $Messages->add_messages( $sess_Messages->messages );
 189                                  $Debuglog->add( 'Added Messages from session data.', 'session' );
 190                                  $this->delete( 'Messages' );
 191                              }
 192                          }
 193                      }
 194                  }
 195              }
 196          }
 197  
 198  
 199          if( $this->ID )
 200          { // there was a valid session before; update data (lastseen)
 201              $this->_session_needs_save = true;
 202          }
 203          else
 204          { // create a new session
 205              $this->key = generate_random_key(32);
 206  
 207              // We need to INSERT now because we need an ID now! (for the cookie)

 208              $DB->query( "
 209                  INSERT INTO T_sessions( sess_key, sess_lastseen, sess_ipaddress )
 210                  VALUES (
 211                      '".$this->key."',
 212                      '".date( 'Y-m-d H:i:s', $localtimenow )."',
 213                      '".$Hit->IP."'
 214                  )" );
 215  
 216              $this->ID = $DB->insert_id;
 217  
 218              // Set a cookie valid for ~ 10 years:

 219              setcookie( $cookie_session, $this->ID.'_'.$this->key, time()+315360000, $cookie_path, $cookie_domain );
 220  
 221              $Debuglog->add( 'ID (generated): '.$this->ID, 'session' );
 222              $Debuglog->add( 'Cookie sent.', 'session' );
 223          }
 224  
 225          register_shutdown_function( array( & $this, 'dbsave' ) );
 226      }
 227  
 228  
 229      /**

 230       * Attach a User object to the session.

 231       *

 232       * @param User The user to attach

 233       */
 234  	function set_User( $User )
 235      {
 236          return $this->set_user_ID( $User->ID );
 237      }
 238  
 239  
 240      /**

 241       * Attach a user ID to the session.

 242       *

 243       * NOTE: ID gets saved to DB on shutdown. This may be a "problem" when querying T_sessions for sess_user_ID.

 244       *

 245       * @param integer The ID of the user to attach

 246       */
 247  	function set_user_ID( $user_ID )
 248      {
 249          if( $user_ID != $this->user_ID )
 250          {
 251              global $UserSettings, $DB;
 252  
 253              if( ! $UserSettings->get('login_multiple_sessions', $user_ID) )
 254              { // The user does not want to have multiple sessions open at the same time:
 255                  // Invalidate previous sessions:

 256                  global $Debuglog;
 257                  $Debuglog->add( 'Invalidating all previous user sessions, because login_multiple_sessions=0', 'session' );
 258                  $DB->query( '
 259                      UPDATE T_sessions
 260                           SET sess_key = NULL
 261                       WHERE sess_user_ID = '.$DB->quote($user_ID).'
 262                           AND sess_ID != '.$this->ID );
 263              }
 264  
 265              $this->user_ID = $user_ID;
 266              $this->_session_needs_save = true;
 267          }
 268      }
 269  
 270  
 271      /**

 272       * Logout the user, by invalidating the session key and unsetting {@link $user_ID}.

 273       *

 274       * We want to keep the user in the session log, but we're unsetting {@link $user_ID}, which refers

 275       * to the current session.

 276       *

 277       * Because the session key is invalid/broken, on the next request a new session will be started.

 278       *

 279       * NOTE: we MIGHT want to link subsequent sessions together if we want to keep track...

 280       */
 281  	function logout()
 282      {
 283          global $Debuglog, $cookie_session, $cookie_path, $cookie_domain;
 284  
 285          // Invalidate the session key (no one will be able to use this session again)

 286          $this->key = NULL;
 287          $this->_data = array(); // We don't need to keep old data

 288          $this->_session_needs_save = true;
 289          $this->dbsave();
 290  
 291          $this->user_ID = NULL; // Unset user_ID after invalidating/saving the session above, to keep the user info attached to the old session.

 292  
 293          // clean up the session cookie:

 294          setcookie( $cookie_session, '', 200000000, $cookie_path, $cookie_domain );
 295      }
 296  
 297  
 298      /**

 299       * Check if session has a user attached.

 300       *

 301       * @return boolean

 302       */
 303  	function has_User()
 304      {
 305          return !empty( $this->user_ID );
 306      }
 307  
 308  
 309      /**

 310       * Get the attached User.

 311       *

 312       * @return false|User

 313       */
 314      function & get_User()
 315      {
 316          if( !empty($this->user_ID) )
 317          {
 318              $UserCache = & get_Cache( 'UserCache' );
 319              return $UserCache->get_by_ID( $this->user_ID );
 320          }
 321  
 322          $r = false;
 323          return $r;
 324      }
 325  
 326  
 327      /**

 328       * Get a data value for the session. This checks for the data to be expired and unsets it then.

 329       *

 330       * @param string Name of the data's key.

 331       * @param mixed Default value to use if key is not set or has expired. (since 1.10.0)

 332       * @return mixed The value, if set; otherwise $default

 333       */
 334  	function get( $param, $default = NULL )
 335      {
 336          global $Debuglog, $localtimenow;
 337  
 338          if( isset( $this->_data[$param] ) )
 339          {
 340              if( array_key_exists(1, $this->_data[$param]) // can be NULL!
 341                && ( is_null( $this->_data[$param][0] ) || $this->_data[$param][0] > $localtimenow ) ) // check for expired data
 342              {
 343                  return $this->_data[$param][1];
 344              }
 345              else
 346              { // expired or old format (without 'value' key)
 347                  unset( $this->_data[$param] );
 348                  $this->_session_needs_save = true;
 349                  $Debuglog->add( 'Session data['.$param.'] expired.', 'session' );
 350              }
 351          }
 352  
 353          return $default;
 354      }
 355  
 356  
 357      /**

 358       * Set a data value for the session.

 359       *

 360       * @param string Name of the data's key.

 361       * @param mixed The value

 362       * @param integer Time in seconds for data to expire (0 to disable).

 363       */
 364  	function set( $param, $value, $expire = 0 )
 365      {
 366          global $Debuglog, $localtimenow;
 367  
 368          if( ! isset($this->_data[$param])
 369           || ! is_array($this->_data[$param]) // deprecated: check to transform 1.6 session data to 1.7
 370           || $this->_data[$param][1] != $value
 371           || $expire != 0 )
 372          {    // There is something to update:
 373              $this->_data[$param] = array( ( $expire ? ($localtimenow + $expire) : NULL ), $value );
 374  
 375              if( $param == 'Messages' )
 376              { // also set boolean to not call CachePageContent plugin event on next request:
 377                  $this->set( 'core.no_CachePageContent', 1 );
 378              }
 379  
 380              $Debuglog->add( 'Session data['.$param.'] updated. Expire in: '.( $expire ? $expire.'s' : '-' ).'.', 'session' );
 381  
 382              $this->_session_needs_save = true;
 383          }
 384      }
 385  
 386  
 387      /**

 388       * Delete a value from the session data.

 389       *

 390       * @param string Name of the data's key.

 391       */
 392  	function delete( $param )
 393      {
 394          global $Debuglog;
 395  
 396          if( isset($this->_data[$param]) )
 397          {
 398              unset( $this->_data[$param] );
 399  
 400              $Debuglog->add( 'Session data['.$param.'] deleted!', 'session' );
 401  
 402              $this->_session_needs_save = true;
 403          }
 404      }
 405  
 406  
 407      /**

 408       * Updates session data in database.

 409       *

 410       * Note: The key actually only needs to be updated on a logout.

 411       */
 412  	function dbsave()
 413      {
 414          global $DB, $Debuglog, $Hit, $localtimenow;
 415  
 416          if( ! $this->_session_needs_save )
 417          {    // There have been no changes since the last save.
 418              return false;
 419          }
 420  
 421          $sess_data = empty($this->_data) ? NULL : serialize($this->_data);
 422          $DB->query( "
 423              UPDATE T_sessions SET
 424                  sess_data = ".$DB->quote( $sess_data ).",
 425                  sess_ipaddress = '".$Hit->IP."',
 426                  sess_key = ".$DB->quote( $this->key ).",
 427                  sess_lastseen = '".date( 'Y-m-d H:i:s', $localtimenow )."',
 428                  sess_user_ID = ".$DB->null( $this->user_ID )."
 429              WHERE sess_ID = ".$this->ID, 'Session::dbsave()' );
 430  
 431          $Debuglog->add( 'Session data saved!', 'session' );
 432  
 433          $this->_session_needs_save = false;
 434      }
 435  
 436  
 437      /**

 438       * Reload session data.

 439       *

 440       * This is needed if the running process waits for a child process to write data

 441       * into the Session, e.g. the captcha plugin in test mode waiting for the Debuglog

 442       * output from the process that created the image (included through an IMG tag).

 443       */
 444  	function reload_data()
 445      {
 446          global $Debuglog, $DB;
 447  
 448          if( empty($this->ID) )
 449          {
 450              return false;
 451          }
 452  
 453          $sess_data = $DB->get_var( '
 454              SELECT sess_data FROM T_sessions
 455               WHERE sess_ID = '.$this->ID );
 456  
 457          $sess_data = @unserialize( $sess_data );
 458          if( $sess_data === false )
 459          {
 460              $this->_data = array();
 461          }
 462          else
 463          {
 464              $this->_data = $sess_data;
 465          }
 466  
 467          $Debuglog->add( 'Reloaded session data.' );
 468      }
 469  }
 470  
 471  
 472  /**

 473   * This gets used as a {@link unserialize()} callback function, which is

 474   * responsible for loading the requested class.

 475   *

 476   * IMPORTANT: when modifying this, modify the following also:

 477   * @see session_unserialize_load_all_classes()

 478   *

 479   * @todo Once we require PHP5, we should think about using this as __autoload function.

 480   *

 481   * @return boolean True, if the required class could be loaded; false, if not

 482   */
 483  function session_unserialize_callback( $classname )
 484  {
 485      switch( strtolower($classname) )
 486      {
 487          case 'blog':
 488              load_class('collections/model/_blog.class.php');
 489              return true;
 490  
 491          case 'collectionsettings':
 492              load_class('collections/model/_collsettings.class.php');
 493              return true;
 494  
 495          case 'comment':
 496              load_class('comments/model/_comment.class.php');
 497              return true;
 498  
 499          case 'item':
 500              load_class('items/model/_item.class.php');
 501              return true;
 502  
 503          case 'group':
 504              load_class('users/model/_group.class.php');
 505              return true;
 506  
 507          case 'user':
 508              load_class('users/model/_user.class.php');
 509              return true;
 510      }
 511  
 512      return false;
 513  }
 514  
 515  
 516  /**

 517   * When session_unserialize_callback() cannot be registered to do some smart loading,

 518   * then we fall back to this function and load everything with brute force...

 519   *

 520   * IMPORTANT: when modifying this, modify the following also:

 521   * @see session_unserialize_callback()

 522   */
 523  function session_unserialize_load_all_classes()
 524  {
 525      load_class('collections/model/_blog.class.php');
 526      load_class('collections/model/_collsettings.class.php');
 527      load_class('comments/model/_comment.class.php');
 528      load_class('items/model/_item.class.php');
 529      load_class('users/model/_group.class.php');
 530      load_class('users/model/_user.class.php');
 531  }
 532  
 533  
 534  /*

 535   * $Log: _session.class.php,v $

 536   * Revision 1.1  2007/06/25 11:01:00  fplanque

 537   * MODULES (refactored MVC)

 538   *

 539   * Revision 1.43  2007/06/19 22:50:15  blueyed

 540   * cleanup

 541   *

 542   * Revision 1.42  2007/05/13 22:02:09  fplanque

 543   * removed bloated $object_def

 544   *

 545   * Revision 1.41  2007/04/26 00:11:11  fplanque

 546   * (c) 2007

 547   *

 548   * Revision 1.40  2007/04/23 15:08:39  blueyed

 549   * TODO

 550   *

 551   * Revision 1.39  2007/04/05 21:53:51  fplanque

 552   * fix for OVH

 553   *

 554   * Revision 1.38  2007/03/11 18:29:50  blueyed

 555   * Use is_array for session data check

 556   *

 557   * Revision 1.37  2007/02/25 01:39:05  fplanque

 558   * wording

 559   *

 560   * Revision 1.36  2007/02/21 22:21:30  blueyed

 561   * "Multiple sessions" user setting

 562   *

 563   * Revision 1.35  2007/02/15 16:37:53  waltercruz

 564   * Changing double quotes to single quotes

 565   *

 566   * Revision 1.34  2007/02/14 14:38:04  waltercruz

 567   * Changing double quotes to single quotes

 568   *

 569   * Revision 1.33  2007/01/27 15:18:23  blueyed

 570   * doc

 571   *

 572   * Revision 1.32  2007/01/27 01:02:49  blueyed

 573   * debug_die() if ini_set() fails on Session data restore

 574   *

 575   * Revision 1.31  2007/01/16 00:08:44  blueyed

 576   * Implemented $default param for Session::get()

 577   *

 578   * Revision 1.30  2006/12/28 15:43:31  fplanque

 579   * minor

 580   *

 581   * Revision 1.29  2006/12/17 23:44:35  fplanque

 582   * minor cleanup

 583   *

 584   * Revision 1.28  2006/12/07 23:13:11  fplanque

 585   * @var needs to have only one argument: the variable type

 586   * Otherwise, I can't code!

 587   *

 588   * Revision 1.27  2006/11/24 18:27:24  blueyed

 589   * Fixed link to b2evo CVS browsing interface in file docblocks

 590   *

 591   * Revision 1.26  2006/11/14 21:13:58  blueyed

 592   * I've spent > 2 hours debugging this charset nightmare and all I've got are those lousy TODOs..

 593   */
 594  ?>


Généré le : Thu Nov 29 23:58:50 2007 par Balluche grâce à PHPXref 0.7
  Clicky Web Analytics