PageRenderTime 26ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/upload/includes/class_session.php

https://github.com/Smokki/PsychoStats-extended
PHP | 734 lines | 505 code | 77 blank | 152 comment | 79 complexity | 887c7af8f6b3d7ea376b72341d628be8 MD5 | raw file
  1. <?php
  2. /**
  3. * This file is part of PsychoStats.
  4. *
  5. * Written by Jason Morriss <stormtrooper@psychostats.com>
  6. * Copyright 2008 Jason Morriss
  7. *
  8. * PsychoStats is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * PsychoStats is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with PsychoStats. If not, see <http://www.gnu.org/licenses/>.
  20. *
  21. * Version: $Id: class_session.php 569 2008-11-05 19:03:02Z lifo $
  22. */
  23. /***
  24. PsychoStats Session class
  25. This session class is for use with the PsychoCMS framework. It provides a basic
  26. framework for dealing with user sessions. This version of the session class only
  27. works with the PsychoCMS object.
  28. Plugins may wish to inherit this class in order to allow for other types of sessions,
  29. for example, integrating a session handler from 3rd party forum software.
  30. ***/
  31. // MYSQL session table for this session class is at the bottom of this file.
  32. if (defined("CLASS_PSYCHO_SESSION_PHP")) return 1;
  33. define('CLASS_PSYCHO_SESSION_PHP', 1);
  34. class PsychoSession {
  35. var $config = array();
  36. var $_is_bot = NULL;
  37. var $_is_new = 0;
  38. var $db = 0;
  39. var $ended = 1;
  40. var $sidmethod = 'get';
  41. var $SESSION_BOTS = array();
  42. var $sid = '';
  43. var $options = array();
  44. var $sessdata = array( // stores all the session data (stored in the database, not the cookie)
  45. 'session_id' => '',
  46. 'session_userid' => 0,
  47. 'session_start' => 0,
  48. 'session_last' => 0,
  49. 'session_ip' => 0,
  50. 'session_logged_in' => 0,
  51. 'session_key' => null,
  52. 'session_key_time' => null,
  53. 'session_is_admin' => 0,
  54. 'session_is_bot' => 0,
  55. );
  56. // session constructor. Sets defaults and will automatically start a new session or load an existing one
  57. function PsychoSession($_config = array()) {
  58. $this->config = array(
  59. 'cms' => null, // a CMS object MUST be passed in
  60. 'dbhandle' => 0,
  61. 'delaystart' => 0,
  62. 'cookielife' => 60 * 60, // 1 hour
  63. 'cookielifeoptions' => 60 * 60 * 24 * 30, // ~30 days
  64. 'cookiedomain' => '',
  65. 'cookiepath' => '/',
  66. 'cookiename' => 'sess',
  67. 'cookiesecure' => 0,
  68. 'cookiesalt' => '', // mcrypt module must be installed if this is !empty
  69. 'cookiecompress' => TRUE,
  70. 'cookieencode' => TRUE,
  71. 'login_callback_func' => '',
  72. 'match_agent_ip' => FALSE, // i haven't kept up-to-date on bot IPs
  73. 'db_session_table' => 'sessions',
  74. 'db_user_table' => '', // if blank, extra user features are ignored...
  75. 'db_user_session_last' => 'session_last', // table field name to update the users last session request
  76. 'db_user_login_key' => 'session_login_key', // table field name to hold auto-login token
  77. 'db_user_last_visit' => 'lastvisit', // table field name to update the users last (previous) visit
  78. 'db_user_id' => 'userid', // table field name for the users "user id"
  79. // these aren't used in this version of the session class; pass a dbhandle above instead
  80. 'dbuser' => '',
  81. 'dbpass' => '',
  82. 'dbhost' => 'localhost',
  83. 'dbname' => 'sessions',
  84. );
  85. // *** This list is way out of date; It's too tedious to try and maintain it ***
  86. // user agent, name, ip substrs
  87. $this->SESSION_BOTS = array();
  88. $this->SESSION_BOTS[] = NULL; // we don't want index 0 to be used
  89. $this->SESSION_BOTS[] = array('Google', 'Googlebot', '216.239.46.|64.68.8|64.68.9|164.71.1.|192.51.44.|66.249.71.|66.249.64.|66.249.65.|66.249.66.');
  90. $this->SESSION_BOTS[] = array('ia_archiver', 'Alexa', '66.28.250.|209.237.238.');
  91. $this->SESSION_BOTS[] = array('Slurp/', 'Inktomi', '216.35.116.|66.196.|66.94.230.|202.212.5.');
  92. $this->SESSION_BOTS[] = array('Infoseek', 'Infoseek', '204.162.9|205.226.203|206.3.30.|210.236.233.');
  93. $this->SESSION_BOTS[] = array('Scooter', 'Alta Vista', '194.221.84.|204.123.28.|208.221.35|212.187.226.|66.17.148.');
  94. $this->SESSION_BOTS[] = array('Lycos', 'Lycos', '208.146.27.|209.202.19|209.67.22|202.232.118.');
  95. $this->SESSION_BOTS[] = array('alltheweb', 'FAST', '146.101.142.2|216.35.112.|64.41.254.2|213.188.8.');
  96. $this->SESSION_BOTS[] = array('WISEnut', 'WiseNut', '64.241.243.|209.249.67.1|216.34.42.|66.35.208.');
  97. $this->SESSION_BOTS[] = array('msnbot/', 'MSN', '131.107.3.|204.95.98.|131.107.1|65.54.164.95|65.54.164.3|65.54.164.4|65.54.164.5|65.54.164.6|207.46.98.');
  98. $this->SESSION_BOTS[] = array('MARTINI', 'Looksmart', '64.241.242.|207.138.42.212');
  99. $this->SESSION_BOTS[] = array('teoma', 'Ask Jeeves', '216.200.130.|216.34.121.|63.236.92.1|64.55.148.|65.192.195.|65.214.36.');
  100. $this->config = array_merge($this->config, $_config);
  101. // A CMS object must be passed to us. We primarily need it for inputs
  102. if (empty($this->config['cms'])) {
  103. trigger_error("Session class instantiated without a CMS object!", E_ERROR);
  104. }
  105. $this->cms =& $this->config['cms'];
  106. // a database object must be passed to us
  107. $this->db =& $this->config['dbhandle'];
  108. $this->is_bot();
  109. if (!$this->config['delaystart']) $this->start(); // start session if its not 'delayed'
  110. }
  111. // gets/sets the admin flag of the session
  112. function is_admin($toggle = null) {
  113. $old = $this->sessdata['session_is_admin'];
  114. if ($toggle !== null) $this->sessdata['session_is_admin'] = $toggle;
  115. return $old;
  116. }
  117. function is_bot() {
  118. if (!is_null($this->_is_bot)) return $this->_is_bot;
  119. $ip = $_SERVER['REMOTE_ADDR'];
  120. $agent = $_SERVER['HTTP_USER_AGENT'];
  121. $ip_match = $this->config['match_agent_ip'] ? 0 : 1;
  122. $agent_match = 0;
  123. foreach ($this->SESSION_BOTS as $idx => $row) {
  124. # print "$idx => $value<br>";
  125. if (!$row) continue;
  126. foreach (explode('|', $row[0]) as $bot_agent) {
  127. # print "$agent == $bot_agent<BR>";
  128. if ($bot_agent != '' && preg_match('/' . preg_quote($bot_agent, '/') . '/i', $agent)) {
  129. # print "AGENT MATCH!!!<BR>\n";
  130. $agent_match = $idx;
  131. break;
  132. }
  133. }
  134. if ($agent_match and !$ip_match) {
  135. foreach (explode('|', $row[2]) as $bot_ip) {
  136. # print "$ip == $bot_ip<BR>";
  137. if ($bot_ip != '' && strpos($ip, $bot_ip) === 0) {
  138. # print "IPADDR MATCH!!!<BR>\n";
  139. $ip_match = $idx;
  140. break;
  141. }
  142. }
  143. }
  144. if ($agent_match and $ip_match) break;
  145. }
  146. // agent_match and ip_match will always be the same bot index
  147. $this->_is_bot = ($agent_match and $ip_match) ? $agent_match : 0;
  148. return $this->_is_bot;
  149. }
  150. function bot_name($idx) {
  151. if (array_key_exists($idx, $this->SESSION_BOTS) and is_array($this->SESSION_BOTS[$idx])) {
  152. return $this->SESSION_BOTS[$idx][1];
  153. } else {
  154. return '';
  155. }
  156. }
  157. // generates a new random SID. If you provide the $random string it will be used to help generate the md5 hash.
  158. function generate_sid($random="") {
  159. if ($this->is_bot()) {
  160. return sprintf("%032d", $this->is_bot());
  161. } else {
  162. // return md5(time() . mt_rand() . $random);
  163. // the UNIQUE_ID may or may not actually be present. If it is, all the better.
  164. return md5($_SERVER["UNIQUE_ID"] . uniqid(mt_rand(), true) . $random);
  165. }
  166. }
  167. // delete expired sessions
  168. function garbage_collect() {
  169. $now = time();
  170. $cmd = "DELETE FROM {$this->config['db_session_table']} WHERE ($now - session_last > {$this->config['cookielife']})";
  171. $res = $this->db->query($cmd);
  172. }
  173. // returns the current session SID from a COOKIE or GET data. Returns FALSE if there is none
  174. function _find_user_sid() {
  175. $this->garbage_collect();
  176. $name = $this->sid_name();
  177. $sid = FALSE;
  178. if ($this->cms->cookie[$name] != '') {
  179. $this->sidmethod = 'cookie';
  180. $sid = $this->cms->cookie[$name];
  181. } elseif ($this->cms->input[$name] != '') {
  182. $this->sidmethod = 'get';
  183. $sid = $this->cms->input[$name];
  184. $this->cms->cookie[$name] = $sid;
  185. } else {
  186. $this->sidmethod = 'none';
  187. }
  188. // if ($sid != FALSE and get_magic_quotes_gpc()) stripslashes($sid);
  189. return $sid;
  190. }
  191. function is_new() {
  192. return $this->_is_new;
  193. }
  194. function is_sid($sid) {
  195. return ereg('^[a-f0-9]{32}$', strtolower($sid));
  196. }
  197. // sets a cookie for the user based on the cookie settings we have. $suffix is the trailing part of the SID name.
  198. // '_id' or '_login'
  199. function send_cookie($data, $time=0, $suffix='_id') {
  200. return setcookie(
  201. $this->sid_name($suffix),
  202. $data,
  203. $time,
  204. $this->config['cookiepath'],
  205. $this->config['cookiedomain'],
  206. $this->config['cookiesecure']
  207. );
  208. }
  209. // returns the contents of a session cookie or false if not found
  210. function get_cookie($suffix = '_id') {
  211. $name = $this->sid_name($suffix);
  212. if (array_key_exists($name, $this->cms->cookie)) {
  213. return $this->cms->cookie[$name];
  214. }
  215. return false;
  216. }
  217. // short-cut method for deleting a users cookie.
  218. function delete_cookie($suffix='_id') {
  219. if ($this->cms->cookie[ $this->sid_name($suffix) ]) {
  220. unset($this->cms->cookie[ $this->sid_name($suffix) ]);
  221. return $this->send_cookie("", time()-100000, $suffix);
  222. }
  223. return 0;
  224. }
  225. function _read_session($sid) {
  226. $res = $this->db->query("SELECT * FROM " . $this->db->qi($this->config['db_session_table']) . " WHERE session_id=" . $this->db->escape($sid, true));
  227. if (!$res) die("Fatal Session Error at line " . __LINE__ . ": " . $this->db->lasterr());
  228. $this->sessdata = $this->db->num_rows() > 0 ? $this->db->fetch_row() : $this->_init_new_session();
  229. }
  230. function _save_session() {
  231. if ($this->ended) return 1; // do not save anything if the session was end()'ed
  232. $res = $this->db->query("SELECT session_id FROM " . $this->db->qi($this->config['db_session_table']) . " WHERE session_id=" . $this->db->escape($this->sessdata['session_id'], true));
  233. list($exists) = $this->db->fetch_row(0);
  234. if (!$res) die("Fatal Session Error at line " . __LINE__ . ": " . $this->db->lasterr());
  235. // don't allow a blank key, set it to null instead
  236. if (empty($this->sessdata['session_key'])) $this->sessdata['session_key'] = null;
  237. if ($exists) {
  238. $this->db->update($this->config['db_session_table'], $this->sessdata, 'session_id', $this->sessdata['session_id']);
  239. } else {
  240. $this->db->insert($this->config['db_session_table'], $this->sessdata);
  241. }
  242. /*
  243. $prefix = ($exists) ? "UPDATE" : "INSERT INTO";
  244. $cmd = "$prefix " . $this->db->qi($this->config['db_session_table']) . " SET ";
  245. foreach ($this->sessdata as $k => $v) {
  246. $cmd .= sprintf("%s=%s, ", $this->db->qi($k), $this->db->escape($v, true));
  247. }
  248. $cmd = substr($cmd, 0, -2); // strip off trailing ', '
  249. if ($exists) $cmd .= " WHERE session_id=" . $this->db->escape($this->sessdata['session_id'], true);
  250. // print "SAVE SESSION: $cmd<br>";
  251. $res = $this->db->query($cmd);
  252. */
  253. if (!$res) die("Fatal Session Error at line " . __LINE__ . ": " . $this->db->lasterr());
  254. }
  255. function _init_new_session() {
  256. // print "INIT SESSION ... <br>";
  257. $this->_is_new = 1;
  258. $this->sid($this->generate_sid());
  259. $this->sessdata = array(
  260. 'session_id' => $this->sid(),
  261. 'session_userid' => 0,
  262. 'session_start' => time(),
  263. 'session_last' => time(),
  264. 'session_ip' => sprintf("%u", ip2long($_SERVER['REMOTE_ADDR'])),
  265. 'session_logged_in' => 0,
  266. 'session_is_bot' => $this->is_bot(),
  267. );
  268. }
  269. // private method to get or set the users current SID cookie
  270. function _session_start() {
  271. $sid = $this->_find_user_sid();
  272. if (!$sid or !$this->is_sid($sid)) {
  273. # print "NEW SESSION STARTING ... <BR>";
  274. $this->_init_new_session();
  275. # $this->_save_session(); // always SAVE when we create a new session
  276. $this->send_cookie($this->sid());
  277. } else {
  278. # print "PREVIOUS SESSION STARTING ... <BR>";
  279. $this->_read_session($sid);
  280. $this->sid($sid);
  281. if ($this->_expired()) {
  282. $this->delete_session($this->sid()); // deletes old session from database
  283. $this->_init_new_session(); // generate a new dataset
  284. # $this->_save_session();
  285. $this->delete_cookie(); // delete old sess_id cookie
  286. $this->send_cookie($this->sid()); // send a new cookie
  287. }
  288. }
  289. }
  290. // starts the session (no need to call this unless delaystart is true) ----------------------------
  291. function start() {
  292. $this->ended = 0;
  293. $this->_session_start();
  294. $this->_initkey();
  295. $now = time();
  296. $this->sessdata['session_last'] = $now;
  297. // If the user is NOT logged in and there is a 'login' cookie set, try to verify and log the user in automatically
  298. // print "START SESSION<br/>\n";
  299. if ($this->online_status()==0 and !empty($this->cms->cookie[ $this->sid_name('_login') ])) {
  300. $auto = $this->load_login();
  301. if ($auto['userid'] and $auto['password']) {
  302. $func = $this->config['login_callback_func'];
  303. $userid = is_callable($func) ? call_user_func($func, $auto['userid'], $auto['password']) : false;
  304. if ($userid) {
  305. $this->online_status(1, $userid); // user is now magically online!
  306. } else {
  307. $this->delete_cookie('_login'); // login cookie was invalid, so delete it
  308. }
  309. } else {
  310. $this->delete_cookie('_login'); // login cookie was invalid, so delete it
  311. }
  312. }
  313. // Update the users LAST event timestamp (in the users database, not the session database).
  314. // When the cookie expires this will remain in the user data to keep track of the last time the user did anything.
  315. // Used for keeping track of NEW messages, etc.
  316. // This must be done AFTER the autlogin block above! otherwise users that are auto logged in will never have the correct
  317. // 'last visit' timestamp.
  318. if (!empty($this->config['db_user_table'])) {
  319. if ($this->sessdata['session_userid'] > 0 and $this->sessdata['session_logged_in']) {
  320. $cmd = sprintf("UPDATE %s SET %s=$now WHERE %s='%s'",
  321. $this->db->qi($this->config['db_user_table']),
  322. $this->db->qi($this->config['db_user_session_last']),
  323. $this->db->qi($this->config['db_user_id']),
  324. $this->db->escape($this->sessdata['session_userid'])
  325. );
  326. // print "UPDATE USER: $cmd<br>";
  327. $res = $this->db->query($cmd);
  328. }
  329. }
  330. // session data will be saved before the script exits
  331. register_shutdown_function(array(&$this, '_save_session'));
  332. } // end function start()
  333. // Initializes the encryption engine for encrypting user cookies
  334. function _initkey() {
  335. $this->session_encrypted = false;
  336. if ($this->config['cookiesalt'] and function_exists('mcrypt_module_open')) {
  337. $salt = $this->config['cookiesalt'] == -1 ? $this->sid() : $this->config['cookiesalt'];
  338. $this->td = mcrypt_module_open('tripledes', '', 'ecb', '');
  339. $this->iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($this->td), MCRYPT_RAND);
  340. $this->key = substr($salt, 0, mcrypt_enc_get_key_size($this->td));
  341. $this->session_encrypted = true;
  342. }
  343. return $this->session_encrypted;
  344. }
  345. function encrypt($str) {
  346. if (!$this->session_encrypted) return $str;
  347. mcrypt_generic_init($this->td, $this->key, $this->iv);
  348. $encrypted = mcrypt_generic($this->td, $str); // Encrypt data
  349. mcrypt_generic_deinit($this->td);
  350. return $encrypted;
  351. }
  352. function decrypt($str) {
  353. if (!$this->session_encrypted) return $str;
  354. mcrypt_generic_init($this->td, $this->key, $this->iv);
  355. $decrypted = trim(mdecrypt_generic($this->td, $str));
  356. mcrypt_generic_deinit($this->td);
  357. return $decrypted;
  358. }
  359. // sets or gets the current online status for the session. If the online status is changed, the previous value is returned.
  360. function online_status($online=-1, $userid=0) {
  361. $status = $this->sessdata['session_logged_in']; // get original status by default
  362. if ($online >= 1) { // LOGIN THE USER
  363. $this->sessdata['session_logged_in'] = 1;
  364. $this->sessdata['session_userid'] = $userid;
  365. # $this->_save_session();
  366. if (!empty($this->config['db_user_table'])) {
  367. $res = $this->db->query(sprintf("SELECT %s FROM %s WHERE %s='%s' LIMIT 1",
  368. $this->db->qi($this->config['db_user_session_last']),
  369. $this->db->qi($this->config['db_user_table']),
  370. $this->db->qi($this->config['db_user_id']),
  371. $this->db->escape($userid)
  372. ));
  373. list($last) = $res ? $this->db->fetch_row(0) : time();
  374. $res = $this->db->query(sprintf("UPDATE %s SET %s=$last WHERE %s='%s'", // update the USER table
  375. $this->db->qi($this->config['db_user_table']),
  376. $this->db->qi($this->config['db_user_last_visit']),
  377. $this->db->qi($this->config['db_user_id']),
  378. $this->db->escape($this->sessdata['session_userid'])
  379. ));
  380. }
  381. } elseif ($online == 0) {
  382. $this->sessdata['session_logged_in'] = 0;
  383. $this->sessdata['session_is_admin'] = 0;
  384. $this->sessdata['session_userid'] = 0;
  385. $this->delete_login();
  386. }
  387. return $status;
  388. }
  389. // returns the total seconds 'online' for the session ------------------------------------------------------------
  390. function seconds_online() {
  391. $diff = $this->sessdata['session_last'] - $this->sessdata['session_start'];
  392. return ($diff > 0) ? $diff : 0;
  393. }
  394. function onlinetime() {
  395. return $this->seconds_online();
  396. }
  397. function userid() {
  398. return $this->sessdata['session_userid'];
  399. }
  400. // returns the total number of active sessions -------------------------------------------------------------------
  401. // 5 minutes is generally a reasonable amount of time to wait before a session is 'inactive'
  402. // if $wantarray is true, a 2 element array is is returned with the total 'members' and 'guests' online, respectively
  403. function total_online($timeframe=300, $wantarray=0) {
  404. $memebers = 0;
  405. $guests = 0;
  406. $now = time();
  407. $res = $this->db->query(sprintf("SELECT count(DISTINCT session_userid) FROM %s WHERE session_userid != 0 AND session_last + $timeframe > $now",
  408. $this->db->qi($this->config['db_session_table'])
  409. ));
  410. list($members) = $this->db->fetch_row(0);
  411. $res = $this->db->query(sprintf("SELECT count(*) FROM %s WHERE session_userid=0 AND session_last + $timeframe > $now",
  412. $this->db->qi($this->config['db_session_table'])
  413. ));
  414. list($guests) = $this->db->fetch_row(0);
  415. if ($wantarray) {
  416. return array( $members > 0 ? $members : 0, $guests > 0 ? $guests : 0);
  417. } else {
  418. $total = $members + $guests;
  419. return ($total > 0) ? $total : 1;
  420. }
  421. }
  422. // End/remove the session (including the user's SID cookie)
  423. function end() {
  424. $this->delete_cookie('_id');
  425. $this->delete_cookie('_login');
  426. $this->delete_session($this->sid());
  427. $this->sid('');
  428. $this->sessdata = array();
  429. $this->_init_new_session(); // generate a new dataset, but it's not saved yet ...
  430. $this->ended = 1; // don't save the new session at exit
  431. }
  432. // closes the session. There's no need to call this unless you want to make sure the session is updated before
  433. // redirecting to another page. Other session sub-classes might want to use this.
  434. function close() {
  435. # $this->_save_session();
  436. }
  437. // internal function, returns true if the session has expired
  438. function _expired() {
  439. return (time() - $this->sessdata['session_last'] > $this->config['cookielife']);
  440. }
  441. // returns the time the session started
  442. function session_start() {
  443. return $this->sessdata['session_start'];
  444. }
  445. // returns the user ID if the session is logged in. Returns 0 otherwise.
  446. function logged_in() {
  447. return $this->online_status() ? $this->userid() : 0;
  448. }
  449. // returns the cookie name (w/o any suffix; '_id', etc)
  450. function sid_prefix() {
  451. return $this->config['cookiename'];
  452. }
  453. // returns the name of the SID cookie
  454. function sid_name($suffix='_id') {
  455. return $this->config['cookiename'] . $suffix;
  456. }
  457. // returns the current session ID
  458. function sid($new = null) {
  459. if ($new === null) {
  460. return $this->sid;
  461. } else {
  462. $old = $this->sid;
  463. $this->sid = $new;
  464. $this->sessdata['session_id'] = $new;
  465. return $old;
  466. }
  467. }
  468. // deletes the session specified, or the current session if no $sid is given.
  469. function delete_session($sid = null) {
  470. if ($sid === null) $sid = $this->sid();
  471. return $this->db->delete($this->config['db_session_table'], 'session_id', $sid);
  472. }
  473. // loads extra options for the session (separate cookie)
  474. // the options cookie stores session related settings for the session.
  475. // the settings are not tied to the user so even if the user is not logged in
  476. // the session options are still present.
  477. function load_session_options() {
  478. $sidname = $this->sid_name('_opts');
  479. $o = array();
  480. if (array_key_exists($sidname, $this->cms->cookie)) {
  481. $str = $this->cms->cookie[$sidname];
  482. // decode -> decrypt -> inflate -> unserialize
  483. $str = $this->config['cookieencode'] ? base64_decode($str) : $str;
  484. $decoded = $this->decrypt($str);
  485. if ($this->config['cookiecompress'] and function_exists('gzinflate')) $decoded = @gzinflate($decoded);
  486. if ($decoded === FALSE) $this->delete_cookie('_opts');
  487. $o = unserialize($decoded);
  488. # print "COOKIE: "; print_r($o); print "<br/>\n"; // DEBUG
  489. }
  490. if (!is_array($o)) $o = array();
  491. $this->options = $o;
  492. return $o;
  493. }
  494. // saves the session options.
  495. // deflate reduces the cookie size by about 1/2 (plus it obfuscates it)
  496. function save_session_options($opts = null) {
  497. if ($opts === null) {
  498. if ($this->options === null) {
  499. $this->load_session_options();
  500. }
  501. $opts = $this->options;
  502. }
  503. if (!is_array($opts)) $opts = array();
  504. // serialize -> deflate -> encrypt -> encode
  505. $str = serialize($opts);
  506. if ($this->config['cookiecompress'] and function_exists('gzdeflate')) $str = gzdeflate($str);
  507. $str = $this->encrypt($str);
  508. $encoded = $this->config['cookieencode'] ? base64_encode($str) : $str;
  509. $this->send_cookie($encoded, $this->config['cookielifeoptions'] ? time() + $this->config['cookielifeoptions'] : 0, '_opts');
  510. // $this->send_cookie(strlen($encoded), 0, '_opts_size'); // debug
  511. // add the modified cookie to memory incase we re-read the options before we exit
  512. $this->cms->cookie[ $this->sid_name('_opts') ] = $encoded;
  513. }
  514. // deletes the options cookie
  515. function delete_session_options() {
  516. $this->delete_cookie('_opts');
  517. }
  518. // sets/gets an option; but does not save the cookie, use save_session_options().
  519. // returns false if getting an option doesn't exist.
  520. function opt($key, $value = null) {
  521. if ($this->options === null) {
  522. $this->options = $this->load_session_options();
  523. }
  524. if (is_array($this->options)) {
  525. if ($value === null) {
  526. if (array_key_exists($key, $this->options)) {
  527. return $this->options[$key];
  528. }
  529. } else {
  530. $old = $this->options[$key];
  531. $this->options[$key] = $value;
  532. return $old;
  533. }
  534. }
  535. return false;
  536. }
  537. // sets several session options.
  538. function set_opts($values = array(), $exclusive = false) {
  539. if ($exclusive) {
  540. $this->options = array();
  541. }
  542. if ($this->options === null) {
  543. $this->options = $this->load_session_options();
  544. }
  545. if (is_array($values)) {
  546. foreach ($values as $key => $val) {
  547. $this->options[$key] = $val;
  548. }
  549. }
  550. }
  551. // deletes a session option, but not the entire cookie
  552. function del_opt($key) {
  553. if ($this->options === null) {
  554. $this->options = $this->load_session_options();
  555. }
  556. $list = is_array($key) ? $key : array( $key );
  557. foreach ($list as $k) {
  558. unset($this->options[$k]);
  559. }
  560. }
  561. // sets/gets the session key (this is not the session_id).
  562. // this is for CSRF security.
  563. function key($value = null) {
  564. $old = $this->sessdata['session_key'];
  565. if ($value !== null) {
  566. $this->sessdata['session_key'] = empty($value) ? null : $value;
  567. $this->sessdata['session_key_time'] = time();
  568. }
  569. return $old;
  570. }
  571. // returns the time the session key was generated
  572. function key_time() {
  573. return $this->sessdata['session_key_time'];
  574. }
  575. // returns true if the key given matches the current session and is valid.
  576. // max_age is the maximum seconds a key is allowed to be alive before being invalid.
  577. function verify_key($form_key, $max_age = 900) {
  578. $time = $this->key_time();
  579. if (empty($time)) $time = time();
  580. $valid = (
  581. !is_null($form_key) and
  582. $this->key() == $form_key and
  583. time() - $time <= $max_age
  584. );
  585. return $valid;
  586. }
  587. // returns either 'cookie' or 'get' depending how the session was restored/created.
  588. function sid_method() {
  589. return $this->sidmethod;
  590. }
  591. // saves an auto_login cookie.
  592. // $password is already a hash (from $user->hash())
  593. // Saves the autologin cookie to the users browser so the next time they view the page they will be logged on automatically.
  594. // The user's cookie must have the proper login_key or the auto-login will fail. This prevents another user from attempting
  595. // to forge an auto-login since the login_key is private to the original user.
  596. function save_login($userid, $password) {
  597. $token = substr(md5(md5($_SERVER["UNIQUE_ID"] . uniqid(mt_rand(), true)) . $userid . $password), mt_rand(0,24), 8);
  598. $ary = array('userid' => $userid, 'password' => $password, 'token' => $token);
  599. $data = $this->encrypt(base64_encode(serialize($ary)));
  600. // save the auto-login key to the user table so we can verify it later when the user tries to auto-login again
  601. if (!empty($this->config['db_user_table'])) {
  602. $cmd = $this->db->update($this->config['db_user_table'], array( $this->config['db_user_login_key'] => $token ),
  603. $this->config['db_user_id'], $this->sessdata['session_userid']
  604. );
  605. }
  606. return $this->send_cookie($data, time()+60*60*24*30, '_login'); // autologin cookie is saved for 30 days
  607. }
  608. // returns the auto login cookie or an empty array if not found or not valid.
  609. function load_login() {
  610. $data = null;
  611. $enc = $this->cms->cookie[ $this->sid_name('_login') ];
  612. if (!empty($enc)) {
  613. $data = unserialize(base64_decode($this->decrypt($enc)));
  614. }
  615. // verify the key in the cookie matches the key in the user table
  616. if (is_array($data) and !empty($this->config['db_user_table'])) {
  617. $usertoken = $this->db->fetch_item(sprintf("SELECT %s FROM %s WHERE %s='%s' LIMIT 1",
  618. $this->db->qi($this->config['db_user_login_key']),
  619. $this->db->qi($this->config['db_user_table']),
  620. $this->db->qi($this->config['db_user_id']),
  621. $this->db->escape($data['userid'])
  622. ));
  623. if (empty($data['token']) or $usertoken != $data['token']) {
  624. $data = array();
  625. }
  626. }
  627. return $data;
  628. // return is_array($data) ? $data : array();
  629. }
  630. // delete the auto_login cookie
  631. function delete_login() {
  632. $this->delete_cookie('_login');
  633. return 1;
  634. }
  635. } // end of session class
  636. /**
  637. CREATE TABLE `ps_sessions` (
  638. `session_id` char(32) NOT NULL default '',
  639. `session_userid` int(10) unsigned NOT NULL default '0',
  640. `session_start` int(10) unsigned NOT NULL default '0',
  641. `session_last` int(10) unsigned NOT NULL default '0',
  642. `session_ip` int(10) unsigned NOT NULL default '0',
  643. `session_logged_in` tinyint(1) NOT NULL default '0',
  644. `session_is_admin` tinyint(1) NOT NULL default '0',
  645. `session_is_bot` tinyint(1) NOT NULL default '0',
  646. `session_key` char(32) default NULL,
  647. `session_key_time` int(10) unsigned default NULL,
  648. PRIMARY KEY (`session_id`),
  649. KEY `session_userid` (`session_userid`)
  650. );
  651. **/
  652. ?>