cb.session.php
上传用户:stephen_wu
上传日期:2008-07-05
资源大小:1757k
文件大小:17k
源码类别:

网络

开发平台:

Unix_Linux

  1. <?php
  2. /**
  3. * @version $Id: cb.session.php 444 2008-04-24 18:25:39Z beat $
  4. * @package Community Builder
  5. * @subpackage cb.session.php
  6. * @author Beat
  7. * @copyright (C) 2008 Beat, www.joomlapolis.com
  8. * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html GNU/GPL version 2
  9. */
  10. // no direct access
  11. if ( ! ( defined( '_VALID_CB' ) || defined( '_JEXEC' ) || defined( '_VALID_MOS' ) ) ) { die( 'Direct Access to this location is not allowed.' ); }
  12. /**
  13.  * CB 1.x SESSIONS functions:
  14.  */
  15. /**
  16.  * Checks that all globals are safe to known PHP and Zend bugs:
  17.  */
  18. function cbCheckSafeGlobals() {
  19. static $banned = array( '_files', '_env', '_get', '_post', '_cookie', '_server', '_session', 'globals' );
  20. $check = array( &$_FILES, &$_ENV, &$_GET, &$_POST, &$_COOKIE, &$_SERVER );
  21. for ( $i = 0, $n = count( $check ) ; $i < $n ; $i++ ) {
  22. foreach ( $check[$i] as $k => $v ) {
  23. // check for PHP globals injection bug and for PHP Zend_Hash_Del_Key_Or_Index bug:
  24. if ( in_array( strtolower( $k ), $banned ) || is_numeric( $k ) ) {
  25. die( sprintf( 'Illegal variable %s.', addslashes( htmlspecialchars( $k ) ) ) );
  26. }
  27. }
  28. }
  29. }
  30. /**
  31.  * Unregister super-globals if register_globals is set
  32.  */
  33. function cbUnregisterGlobals() {
  34. if ( ini_get( 'register_globals' ) ) {
  35. $check = array( &$_SERVER, &$_ENV, &$_FILES, &$_COOKIE, &$_POST, &$_GET );
  36. if ( isset( $_SESSION ) ) {
  37. array_unshift ( $check, $_SESSION );
  38. }
  39. for ( $i = 0, $n = count( $check ) ; $i < $n ; $i++ ) {
  40. foreach ($check[$i] as $key => $value) {
  41. if ( $key != 'GLOBALS' ) {
  42. unset( $GLOBALS[$key] );
  43. }
  44. }
  45. }
  46. }
  47. }
  48. /**
  49.  * CLASS implements a minimal database connection working with CB database when present
  50.  * and with MySql directly if not.
  51.  */
  52. class CBSessionStorage {
  53. var $_db;
  54. var $_table_prefix;
  55. var $_resource;
  56. var $_cursor;
  57. var $_sql;
  58. /**
  59.  * Constructor
  60.  *
  61.  * @return CBSessionStorage
  62.  */
  63. function CBSessionStorage( ) {
  64. }
  65. /**
  66.  * Connects to database layer or to mysql database
  67.  *
  68.  * @return CBSessionStorage   or $_CB_database
  69.  */
  70. function & connect() {
  71. if ( ! $this->_db ) {
  72. global $_CB_database;
  73. if ( $_CB_database ) {
  74. $this->_db =& $_CB_database;
  75. } else {
  76. $config = file_get_contents( dirname( __FILE__ ) . '/../../../../../configuration.php' );
  77. $db_host = $this->_parseConfig( $config, 'host' );
  78. $db_user = $this->_parseConfig( $config, 'user' );
  79. $db_password = $this->_parseConfig( $config, 'password' );
  80. $db_db = $this->_parseConfig( $config, 'db' );
  81. $this->_db = new CBSessionStorage();
  82. $this->_db->_resource = mysql_connect( $db_host, $db_user, $db_password ) or die( 'Session connect error!' );
  83. mysql_select_db( $db_db, $this->_db->_resource ) or die( 'Session database error!' );
  84. $this->_db->_table_prefix = $this->_parseConfig( $config, 'dbprefix' );
  85. }
  86. }
  87. return $this->_db;
  88. }
  89. /**
  90.  * Parses a mambo/joomla/compatibles configuration file content
  91.  * @access private
  92.  *
  93.  * @param  string  $config   Content of config file
  94.  * @param  string  $varName  Name of variable to fetch
  95.  * @return string            Content of variable or NULL
  96.  */
  97. function _parseConfig( $config, $varName ) {
  98. preg_match( '/$(?:mosConfig_)?' . $varName . 's*=s*'([^']*)'/', $config, $matches );
  99.         if ( isset($matches[1]) ) {
  100.          return $matches[1];
  101.         } else {
  102.          return null;
  103.         }
  104. }
  105. /**
  106. * Sets the SQL query string for later execution.
  107. *
  108. * This function replaces a string identifier $prefix with the
  109. * string held is the _table_prefix class variable.
  110. *
  111. * @param string $sql     The SQL query
  112. * @param int    $offset  The offset to start selection
  113. * @param int    $limit   The number of results to return
  114. * @param string $prefix  The common table prefix search for replacement string
  115. */
  116. function setQuery( $sql, $offset = 0, $limit = 0, $prefix='#__' ) {
  117. if ( $offset || $limit ) {
  118. $sql .= " LIMIT ";
  119. if ( $offset ) {
  120. $sql .= ( (int) $offset ) . ', ';
  121. }
  122. $sql .= ( (int) $limit );
  123. }
  124. $this->_sql = $this->replacePrefix( $sql, $prefix );
  125. }
  126. /**
  127.  * Replace $prefix with $this->_table_prefix (simplified method)
  128.  * @access private
  129.  *
  130.  * @param  string  $sql      SQL query
  131.  * @param  string  $prefix   Common table prefix
  132.  */
  133. function replacePrefix( $sql, $prefix = '#__' ) {
  134. return str_replace( $prefix, $this->_table_prefix, $sql );
  135. }
  136. /**
  137. * Execute the query
  138. * @param  string  the query (optional, it will use the setQuery one otherwise)
  139. * @return mixed A database resource if successful, FALSE if not.
  140. */
  141. function query( $sql = null ) {
  142. $this->_cursor = mysql_query( $this->_sql, $this->_resource );
  143. return $this->_cursor;
  144. }
  145. /**
  146. * Fetch a result row as an associative array
  147. *
  148. * @return array
  149. */
  150. function loadAssoc( ) {
  151. if ( ! ( $cur = $this->query() ) ) {
  152. $result = null;
  153. } else {
  154. $result = mysql_fetch_assoc( $cur );
  155. if ( ! $result ) {
  156. $result = null;
  157. }
  158. mysql_free_result( $cur );
  159. }
  160. return $result;
  161. }
  162. /**
  163. * Get a database escaped string
  164. *
  165. * @param  string  $text
  166. * @return string
  167. */
  168. function getEscaped( $text ) {
  169. return mysql_real_escape_string( $text, $this->_resource );
  170. }
  171. /**
  172. * Get a quoted database escaped string
  173. *
  174. * @param  string  $text
  175. * @return string
  176. */
  177. function Quote( $text ) {
  178. return ''' . $this->getEscaped( $text ) . ''';
  179. }
  180. /**
  181. * Quote an identifier name (field, table, etc)
  182. *
  183. * @param  string  $s  The name
  184. * @return string      The quoted name
  185. */
  186. function NameQuote( $s ) {
  187. return '`' . $s . '`';
  188. }
  189. }
  190. /**
  191.  * This class implements CB independant database-based scalable cookies-only-based secure sessions.
  192.  *
  193.  */
  194. class CBSession {
  195. var $_session_id = null;
  196. var $_session_var = null;
  197. var $_cookie_name = 'cb_web_session';
  198. var $_cookie_verify = 'cb_web_session_verify';
  199. var $_life_time = null;
  200. var $_sessionRecord = null;
  201. var $_mode;
  202. /**
  203.  * Mini-db
  204.  * @var CBSessionStorage
  205.  */
  206. var $_db = null;
  207. /**
  208.  * Constructor
  209.  * @access private
  210.  * 
  211.  * @param  string     $mode  'cookie' for most secure way (requires cookies enabled), 'sessionid': set id by session
  212.  * @return CBSession
  213.  */
  214. function CBSession( $mode = 'cookie' ) {
  215. $this->_mode = $mode;
  216. // Read the maxlifetime setting from PHP:
  217. $this->_life_time = max( get_cfg_var( 'session.gc_maxlifetime' ), 43200 ); // 12 hours minimum
  218. $dbConnect = new CBSessionStorage();
  219. $this->_db = $dbConnect->connect();
  220. }
  221. /**
  222.  * Gets singleton
  223.  *
  224.  * @param  string     $mode  'cookie' for most secure way (requires cookies enabled), 'sessionid': set id by session
  225.  * @return CBSession
  226.  */
  227. function & getInstance( $mode = 'cookie' ) {
  228. static $session = array();
  229. if ( ! isset( $session[$mode] ) ) {
  230. $session[$mode] = new CBSession( $mode );
  231. }
  232. return $session[$mode];
  233. }
  234. /**
  235.  * session_start() creates a session or resumes the current one based on the current session id
  236.  * that's being passed via a cookie.
  237.  *
  238.  * If you want to use a named session, you must call session_name() before calling session_start().
  239.  *
  240.  * session_start() will register internal output handler for URL rewriting when trans-sid is enabled.
  241.  * If a user uses ob_gzhandler or like with ob_start(), the order of output handler is important for proper output.
  242.  * For example, user must register ob_gzhandler before session start.
  243.  *
  244.  * @param  string  $mode   'cookie' for most secure way (requires cookies enabled), 'sessionid': set id by session
  245.  * @return bool      True: ok, False: already started
  246.  */
  247. function session_start( $_noNewSession = false ) {
  248. if ( $this->_session_var !== null ) {
  249. // session already started:
  250. return false;
  251. }
  252. if ( $this->_mode == 'cookie' ) {
  253. global $_COOKIE;
  254. if ( isset( $_COOKIE[$this->_cookie_name] ) ) {
  255. // session existing in browser:
  256. $session_id = substr( $_COOKIE[$this->_cookie_name], 0, 32 );
  257. } else {
  258. $session_id = null;
  259. }
  260. } elseif ( $this->_mode == 'sessionid' ) {
  261. $session_id = $this->_session_id;
  262. } else {
  263. return false;
  264. }
  265. if ( $session_id ) {
  266. $session_data = $this->read( $session_id );
  267. if ( $session_data ) {
  268. // session found in database:
  269. $session_var = unserialize( $session_data );
  270. if ( ( $session_var !== false ) && ( $this->_validateSession( $session_id, $session_data ) ) ) {
  271. // valid session has been retrieved:
  272. $this->_session_id = $session_id;
  273. $this->_session_var = $session_var;
  274. return true;
  275. }
  276. }
  277. }
  278. if ( $_noNewSession ) {
  279. return false;
  280. }
  281. // no valid session has been found: create a new one:
  282. $this->_session_id = $this->generateRandSessionid( 32 );
  283. $this->_session_var = array( 'cbsessions.verify' => $this->generateRandSessionid( 32 ) );
  284. $this->_validateSession(); // set the session
  285. if ( $this->_mode == 'cookie' ) {
  286. $this->_sendSessionCookies();
  287. }
  288. return true;
  289. }
  290. /**
  291.  * Sends out the session cookies
  292.  * @access private
  293.  */
  294. function _sendSessionCookies() {
  295. header('P3P: CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"'); // needed for IE6 to accept this cookie in higher security setting.
  296. $sp = session_get_cookie_params();
  297. if ( isset( $sp['secure'] ) ) {
  298. if ( isset( $sp['httponly'] ) ) {
  299. // php >= 5.2.0:
  300. setcookie( $this->_cookie_name, $this->_session_id, false, $sp['path'], $sp['domain'], $sp['secure'], true );
  301. } else {
  302. // php < 5.2.0, but > 4.0.4:
  303. setcookie( $this->_cookie_name, $this->_session_id, false, $sp['path'], $sp['domain'], $sp['secure'] );
  304. }
  305. } else {
  306. setcookie( $this->_cookie_name, $this->_session_id, false, $sp['path'], $sp['domain'] );
  307. }
  308. // setcookie( $this->_cookie_name, $this->_session_id, false, '/' );
  309. }
  310. /**
  311.  * Regenerates a new session id, keeping session data
  312.  *
  313.  */
  314. function session_regenerate( ) {
  315. if ( ! $this->_session_id ) {
  316. // tries to load existing session:
  317. $this->session_start( true );
  318. }
  319. if ( $this->_session_id ) {
  320. $this->destroy( $this->_session_id );
  321. }
  322. $this->_session_id = $this->generateRandSessionid( 32 );
  323. $this->_sendSessionCookies();
  324. }
  325. /**
  326.  * End the current session and store session data.
  327.  *
  328.  * @return bool
  329.  */
  330. function session_write_close( ) {
  331. // store:
  332. if ( ! $this->write( $this->_session_id, serialize( $this->_session_var ) ) ) {
  333. die( 'Session write error!' );
  334. }
  335. // timeout old sessions:
  336. $this->gc();
  337. return true;
  338. }
  339. /**
  340.  * Gets value of the session variable, change it with $this->set()
  341.  * (not a reference, use $this->get_reference() for reference)
  342.  *
  343.  * @param  string  $name
  344.  * @return mixed          NULL if not set
  345.  */
  346. function get( $name ) {
  347. if ( isset( $this->_session_var[$name] ) ) {
  348. return $this->_session_var[$name];
  349. } else {
  350. $null = null;
  351. return $null;
  352. }
  353. }
  354. /**
  355.  * Sets a value to the session variable $name (Not a reference)
  356.  *
  357.  * @param  string  $name
  358.  * @param  mixed   $value
  359.  */
  360. function set( $name, $value ) {
  361. $this->_session_var[$name] = $value;
  362. }
  363. /**
  364.  * Gets a reference to the session variable (which can be changed)
  365.  *
  366.  * @param  string  $name
  367.  * @return mixed          If empty/new: NULL
  368.  */
  369. function & getReference( $name ) {
  370. if ( ! isset( $this->_session_var[$name] ) ) {
  371. $this->_session_var[$name] = null;
  372. }
  373. return $this->_session_var[$name];
  374. }
  375. /**
  376.  * Unset current session
  377.  *
  378.  * @return bool           True success, False failed
  379.  */
  380. function session_unset() {
  381. if ( $this->_session_id ) {
  382. $this->destroy( $this->_session_id );
  383. $this->_session_id = null;
  384. $this->_session_var = null;
  385. }
  386. }
  387. /**
  388.  * Sets/Gets current session id for get (warning: lower security)
  389.  *
  390.  * @param  string  $id  new     if change
  391.  * @return string       current if no change ( $id = null ) if session started already
  392.  */
  393. function session_id( $id = null ) {
  394. if ( $id == null ) {
  395. if ( $this->_session_var !== null ) {
  396. // session started, can return id:
  397. return $this->_session_id;
  398. } else {
  399. return '';
  400. }
  401. } else {
  402. $current = $this->_session_id;
  403. if ( $id ) {
  404. $this->_session_id = $id;
  405. }
  406. return $current;
  407. }
  408. }
  409. /**
  410.  * Generates a random session_id of chars and numbers
  411.  * (Similar to cbMakeRandomString)
  412.  * @access private
  413.  *
  414.  * @param  int    $stringLength
  415.  * @return string
  416.  */
  417. function generateRandSessionid( $stringLength = 32 ) {
  418. $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
  419. $len = strlen( $chars );
  420. $rndString = '';
  421. mt_srand( 10000000 * (double) microtime() );
  422. for ( $i = 0; $i < $stringLength; $i++ ) {
  423. $rndString .= $chars[mt_rand( 0, $len - 1 )];
  424. }
  425. return $rndString;
  426. }
  427. /**
  428.  * Validate the session id with internal records of the browser and check values.
  429.  * @access private
  430.  *
  431.  * @return bool
  432.  */
  433. function _validateSession( ) {
  434. global $_COOKIE;
  435. // check if browser user agent has changed:
  436. if ( isset( $_SERVER['HTTP_USER_AGENT'] ) ) {
  437. $browser = $this->get( 'cbsession.agent' );
  438. if ( $browser === null ) {
  439. $this->set( 'cbsession.agent', $_SERVER['HTTP_USER_AGENT']);
  440. } elseif ( $_SERVER['HTTP_USER_AGENT'] !== $browser ) {
  441. return false;
  442. }
  443. }
  444. /* PROBLEM: COMMENTED OUT FOR NOW:
  445. // check if client IP received (could be fake through proxy) matches:
  446. if ( $this->_getClientIp() != $this->_sessionRecord['client_ip'] ) {
  447. return false;
  448. }
  449. // check if initial session connection had no proxy and now suddenly we have one:
  450. $incoming_ip = $this->_getIcomingIp();
  451. if ( ( $incoming_ip != $this->_sessionRecord['client_ip'] )
  452. && ( $this->_sessionRecord['incoming_ip'] == $this->_sessionRecord['client_ip'] ) ) 
  453. {
  454. return false;
  455. }
  456. */
  457. // Things seem to match, check the validation cookie: //TBD later
  458. return true;
  459. }
  460. function _getIcomingIp() {
  461. global $_SERVER;
  462. return $_SERVER['REMOTE_ADDR'];
  463. }
  464. function _getClientIp() {
  465. global $_SERVER;
  466. if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
  467. $forwarded_ip_array = explode(',',$_SERVER['HTTP_X_FORWARDED_FOR']);
  468. $client_ip = $forwarded_ip_array[count($forwarded_ip_array) - 1];
  469. } else {
  470. $client_ip = $_SERVER['REMOTE_ADDR'];
  471. }
  472. return $client_ip;
  473. }
  474. /**
  475.  * Reads session record from database
  476.  *
  477.  * @param  string  $id
  478.  * @return string       or NULL if record innexistant or expired
  479.  */
  480. function read( $id ) {
  481. // Fetch session data from the selected database
  482. $sql = 'SELECT * FROM `#__comprofiler_sessions`'
  483. . ' WHERE `session_id` = ' . $this->_db->Quote( $id )
  484. . ' AND `expiry_time` >= UNIX_TIMESTAMP()'
  485. ;
  486. $this->_db->setQuery( $sql );
  487. $this->_sessionRecord = $this->_db->loadAssoc();
  488. if ( $this->_sessionRecord !== null ) {
  489. return $this->_sessionRecord['session_data'];
  490. }
  491. return null;
  492. }
  493. /**
  494.  * Writes session record to database
  495.  *
  496.  * @param  string  $id
  497.  * @param  string  $data
  498.  * @return bool
  499.  */
  500. function write( $id, $data ) {
  501. global $_CB_framework;
  502. // Prepare new values:
  503. $v = array();
  504. $v['session_id'] = $this->_db->Quote( $id );
  505. $v['session_data'] = $this->_db->Quote( $data );
  506. $v['expiry_time'] = 'UNIX_TIMESTAMP()+' . (int) $this->_life_time;
  507. if ( $_CB_framework ) {
  508. $v['ui'] = (int) $_CB_framework->getUi();
  509. if ( $_CB_framework->myId() ) {
  510. $v['username'] = $this->_db->Quote( $_CB_framework->myUsername() );
  511. $v['userid'] = (int) $_CB_framework->myId();
  512. }
  513. }
  514. if ( $this->_sessionRecord !== null ) {
  515. // UPDATE existing:
  516. $sets = array();
  517. foreach ( $v as $col => $escapedVal ) {
  518. $sets[] = $this->_db->NameQuote( $col ) . ' = ' . $escapedVal;
  519. }
  520. $where = array_shift( $sets );
  521. $sql = 'UPDATE `#__comprofiler_sessions` SET ' . implode( ', ', $sets )
  522. . ' WHERE ' . $where;
  523. $this->_db->setQuery( $sql );
  524. $okUpdate = $this->_db->query();
  525. if ( $okUpdate ) {
  526. return $okUpdate;
  527. }
  528. }
  529. // INSERT new: add IP address for first record:
  530. $v['client_ip'] = $this->_db->Quote( $this->_getClientIp() );
  531. $v['incoming_ip'] = $this->_db->Quote( $this->_getIcomingIp() );
  532. $columns = array();
  533. $escValues = array();
  534. foreach ( $v as $col => $escapedVal ) {
  535. $columns[] = $this->_db->NameQuote( $col );
  536. $escValues[] = $escapedVal;
  537. }
  538. $sql = 'INSERT INTO `#__comprofiler_sessions`'
  539. . ' (' . implode( ',', $columns ) . ')'
  540. . ' VALUES(' . implode( ',', $escValues ) . ')'
  541. ;
  542. $this->_db->setQuery( $sql );
  543. return $this->_db->query();
  544. }
  545. /**
  546.  * Removes session $id from database
  547.  *
  548.  * @param  string $id
  549.  * @return bool
  550.  */
  551. function destroy( $id ) {
  552. $sql = 'DELETE FROM `#__comprofiler_sessions`'
  553. . ' WHERE `session_id` = ' . $this->_db->Quote( $id )
  554. ;
  555. $this->_db->setQuery( $sql );
  556. return $this->_db->query();
  557. }
  558. /**
  559.  * Garbage Collection
  560.  * Delete all records who have passed the expiration time
  561.  *
  562.  * @return bool
  563.  */
  564. function gc() {
  565. $sql = 'DELETE FROM `#__comprofiler_sessions` WHERE `expiry_time` < UNIX_TIMESTAMP();';
  566. $this->_db->setQuery( $sql );
  567. return $this->_db->query();
  568. }
  569. }
  570. // ----- NO MORE CLASSES OR FUNCTIONS PASSED THIS POINT -----
  571. // Post class declaration initialisations
  572. // some version of PHP don't allow the instantiation of classes
  573. // before they are defined
  574. ?>