Find this useful? Enter your email to receive occasional updates for securing PHP code.
Signing you up...
Thank you for signing up!
PHP Decode
<?php /* +-----------------------------------------------------------------------+ | Th..
Decoded Output download
<?php
/*
+-----------------------------------------------------------------------+
| This file is part of the Roundcube Webmail client |
| |
| Copyright (C) The Roundcube Dev Team |
| Copyright (C) Kolab Systems AG |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
| PURPOSE: |
| Provide database supported session management |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <[email protected]> |
| Author: Aleksander Machniak <[email protected]> |
| Author: Cor Bosman <[email protected]> |
+-----------------------------------------------------------------------+
*/
/**
* Abstract class to provide database supported session storage
*/
abstract class rcube_session implements SessionHandlerInterface
{
protected $config;
protected $key;
protected $ip;
protected $cookie;
protected $changed;
protected $start;
protected $vars;
protected $now;
protected $lifetime;
protected $reloaded = false;
protected $appends = [];
protected $unsets = [];
protected $gc_enabled = 0;
protected $gc_handlers = [];
protected $cookiename = 'roundcube_sessauth';
protected $ip_check = false;
protected $logging = false;
protected $ignore_write = false;
/**
* Blocks session data from being written to database.
* Can be used if write-race conditions are to be expected
*
* @var bool
*/
public $nowrite = false;
/**
* Factory, returns driver-specific instance of the class
*
* @param rcube_config $config
*
* @return rcube_session Session object
*/
public static function factory($config)
{
// get session storage driver
$storage = $config->get('session_storage', 'db');
// class name for this storage
$class = "rcube_session_{$storage}";
if (!class_exists($class)) {
rcube::raise_error([
'code' => 604,
'type' => 'session',
'message' => 'Failed to find session driver. Check session_storage config option',
], true, true);
}
return new $class($config);
}
/**
* Object constructor
*
* @param rcube_config $config
*/
public function __construct($config)
{
$this->config = $config;
// set ip check
$this->set_ip_check($this->config->get('ip_check'));
// set cookie name
if ($name = $this->config->get('session_auth_name')) {
$this->set_cookiename($name);
}
}
/**
* Register session handler
*/
public function register_session_handler()
{
if (session_id()) {
// Session already exists, skip
return;
}
ini_set('session.serialize_handler', 'php');
// set custom functions for PHP session management
session_set_save_handler($this);
}
/**
* Wrapper for session_start()
*/
public function start()
{
$this->start = microtime(true);
$this->ip = rcube_utils::remote_addr();
$this->logging = $this->config->get('session_debug', false);
$lifetime = $this->config->get('session_lifetime', 1) * 60;
$this->set_lifetime($lifetime);
session_start();
}
/**
* Abstract methods should be implemented by driver classes
*/
#[Override]
#[ReturnTypeWillChange]
abstract public function open($save_path, $session_name);
#[Override]
#[ReturnTypeWillChange]
abstract public function close();
#[Override]
#[ReturnTypeWillChange]
abstract public function destroy($key);
#[Override]
#[ReturnTypeWillChange]
abstract public function read($key);
/**
* Write data to the session store
*
* @param string $key Session identifier
* @param string $vars Session data
*
* @return bool True on success, False on failure
*/
abstract protected function save($key, $vars);
/**
* Write data to the session store
*
* @param string $key Session identifier
* @param string $newvars New session data string
* @param string $oldvars Old session data string
*
* @return bool True on success, False on failure
*/
abstract protected function update($key, $newvars, $oldvars);
/**
* Session write handler. This calls the implementation methods for write/update after some initial checks.
*
* @param string $key Session identifier
* @param string $vars Serialized data string
*
* @return bool True on success, False on failure
*/
#[Override]
#[ReturnTypeWillChange]
public function write($key, $vars)
{
if ($this->nowrite) {
return true;
}
// check cache
$oldvars = $this->get_cache($key);
// if there are cached vars, update store, else insert new data
if ($oldvars) {
$newvars = $this->fixvars($vars, $oldvars);
return $this->update($key, $newvars, $oldvars);
}
return $this->save($key, $vars);
}
/**
* Execute registered garbage collector routines
*
* @param int $maxlifetime Maximum session lifetime
*
* @return int|false Number of deleted sessions on success, False on failure
*/
#[Override]
#[ReturnTypeWillChange]
public function gc($maxlifetime)
{
// move gc execution to the script shutdown function
// see rcube::shutdown() and rcube_session::write_close()
$this->gc_enabled = $maxlifetime;
return 0;
}
/**
* Wrapper for session_write_close()
*/
public function write_close()
{
session_write_close();
// write_close() is called on script shutdown, see rcube::shutdown()
// execute cleanup functionality if enabled by session gc handler
// we do this after closing the session for better performance
$this->gc_shutdown();
}
/**
* Creates a new (separate) session
*
* @param array $data Session data
*
* @return ?string Session identifier (on success)
*/
public function create($data)
{
$length = strlen(session_id());
$key = rcube_utils::random_bytes($length);
// create new session
if ($this->save($key, $this->serialize($data))) {
return $key;
}
return null;
}
/**
* Merge vars with old vars and apply unsets
*/
protected function fixvars($vars, $oldvars)
{
$newvars = '';
if ($oldvars !== null) {
$a_oldvars = $this->unserialize($oldvars);
if (is_array($a_oldvars)) {
// remove unset keys on oldvars
foreach ((array) $this->unsets as $var) {
if (isset($a_oldvars[$var])) {
unset($a_oldvars[$var]);
} else {
$path = explode('.', $var);
$k = array_pop($path);
$node = &$this->get_node($path, $a_oldvars);
unset($node[$k]);
}
}
$newvars = $this->serialize(array_merge(
(array) $a_oldvars, (array) $this->unserialize($vars)));
} else {
$newvars = $vars;
}
}
$this->unsets = [];
return $newvars;
}
/**
* Register additional garbage collector functions
*
* @param mixed $func Callback function
*/
public function register_gc_handler($func)
{
foreach ($this->gc_handlers as $handler) {
if ($handler == $func) {
return;
}
}
$this->gc_handlers[] = $func;
}
/**
* Garbage collector handler to run on script shutdown
*/
protected function gc_shutdown()
{
if ($this->gc_enabled) {
foreach ($this->gc_handlers as $fct) {
call_user_func($fct);
}
}
}
/**
* Generate and set new session id
*
* @param bool $destroy If enabled the current session will be destroyed
*
* @return bool True on success, False on failure
*/
public function regenerate_id($destroy = true)
{
$old_id = session_id();
// Since PHP 7.0 session_regenerate_id() will cause the old
// session data update, we don't need this
$this->ignore_write = true;
session_regenerate_id($destroy);
$this->ignore_write = false;
$this->vars = null;
$this->key = session_id();
$this->log("Session regenerate: {$old_id} -> {$this->key}");
return true;
}
/**
* See if we have vars of this key already cached, and if so, return them.
*
* @param string $key Session identifier
*
* @return string Serialized data string
*/
protected function get_cache($key)
{
// no session data in cache (read() returns false)
if (!$this->key) {
$cache = null;
}
// use internal data for fast requests (up to 0.5 sec.)
elseif ($key == $this->key && (!$this->vars || microtime(true) - $this->start < 0.5)) {
$cache = $this->vars;
} else { // else read data again
$cache = $this->read($key);
}
return $cache;
}
/**
* Append the given value to the certain node in the session data array
*
* Warning: Do not use if you already modified $_SESSION in the same request (#1490608)
*
* @param string $path Path denoting the session variable where to append the value
* @param ?string $key Key name under which to append the new value (use null for appending to an indexed list)
* @param mixed $value Value to append to the session data array
*/
public function append($path, $key, $value)
{
// re-read session data from DB because it might be outdated
if (!$this->reloaded && microtime(true) - $this->start > 0.5) {
$this->reload();
$this->reloaded = true;
$this->start = microtime(true);
}
$node = &$this->get_node(explode('.', $path), $_SESSION);
if ($key !== null) {
$node[$key] = $value;
$path .= '.' . $key;
} else {
$node[] = $value;
}
$this->appends[] = $path;
// when overwriting a previously unset variable
if (array_key_exists($path, $this->unsets)) {
unset($this->unsets[$path]);
}
}
/**
* Unset a session variable
*
* @param string $var Variable name (can be a path denoting a certain node
* in the session array, e.g. compose.attachments.5)
*
* @return bool True on success, False on failure
*/
public function remove($var = null)
{
if (empty($var)) {
return $this->destroy(session_id());
}
$this->unsets[] = $var;
if (isset($_SESSION[$var])) {
unset($_SESSION[$var]);
} else {
$path = explode('.', $var);
$key = array_pop($path);
$node = &$this->get_node($path, $_SESSION);
unset($node[$key]);
}
return true;
}
/**
* Kill this session
*/
public function kill()
{
$this->log('Session destroy: ' . session_id());
$this->vars = null;
$this->ip = rcube_utils::remote_addr(); // update IP (might have changed)
$this->destroy(session_id());
rcube_utils::setcookie($this->cookiename, '-del-', time() - 60);
}
/**
* Re-read session data from storage backend
*/
public function reload()
{
// collect updated data from previous appends
$merge_data = [];
foreach ((array) $this->appends as $var) {
$path = explode('.', $var);
$value = $this->get_node($path, $_SESSION);
$k = array_pop($path);
$node = &$this->get_node($path, $merge_data);
$node[$k] = $value;
}
if ($this->key) {
$data = $this->read($this->key);
}
if (!empty($data)) {
session_decode($data);
// apply appends and unsets to reloaded data
$_SESSION = array_merge_recursive($_SESSION, $merge_data);
foreach ((array) $this->unsets as $var) {
if (isset($_SESSION[$var])) {
unset($_SESSION[$var]);
} else {
$path = explode('.', $var);
$k = array_pop($path);
$node = &$this->get_node($path, $_SESSION);
unset($node[$k]);
}
}
}
}
/**
* Returns a reference to the node in data array referenced by the given path.
* e.g. ['compose','attachments'] will return $_SESSION['compose']['attachments']
*/
protected function &get_node($path, &$data_arr)
{
$node = &$data_arr;
if (!empty($path)) {
foreach ((array) $path as $key) {
if (!isset($node[$key])) {
$node[$key] = [];
}
$node = &$node[$key];
}
}
return $node;
}
/**
* Serialize session data
*/
protected function serialize($vars)
{
$data = '';
if (is_array($vars)) {
foreach ($vars as $var => $value) {
$data .= $var . '|' . serialize($value);
}
} else {
$data = 'b:0;';
}
return $data;
}
/**
* Unserialize session data
* http://www.php.net/manual/en/function.session-decode.php#56106
*
* @param string $str Serialized data string
*
* @return array|false Unserialized data
*/
public static function unserialize($str)
{
$str = (string) $str;
$endptr = strlen($str);
$p = 0;
$serialized = '';
$items = 0;
$level = 0;
while ($p < $endptr) {
$q = $p;
while ($str[$q] != '|') {
if (++$q >= $endptr) {
break 2;
}
}
if ($str[$p] == '!') {
$p++;
$has_value = false;
} else {
$has_value = true;
}
$name = substr($str, $p, $q - $p);
$q++;
$serialized .= 's:' . strlen($name) . ':"' . $name . '";';
if ($has_value) {
while (true) {
$p = $q;
switch (strtolower($str[$q])) {
case 'n': // null
case 'b': // boolean
case 'i': // integer
case 'd': // decimal
do {
$q++;
} while (($q < $endptr) && ($str[$q] != ';'));
$q++;
$serialized .= substr($str, $p, $q - $p);
if ($level == 0) {
break 2;
}
break;
case 'r': // reference
$q += 2;
$id = '';
for (; ($q < $endptr) && ($str[$q] != ';'); $q++) {
$id .= $str[$q];
}
$q++;
// increment pointer because of outer array
$serialized .= 'R:' . (intval($id) + 1) . ';';
if ($level == 0) {
break 2;
}
break;
case 's': // string
$q += 2;
$length = '';
for (; ($q < $endptr) && ($str[$q] != ':'); $q++) {
$length .= $str[$q];
}
$q += 2;
$q += (int) $length + 2;
$serialized .= substr($str, $p, $q - $p);
if ($level == 0) {
break 2;
}
break;
case 'a': // array
case 'o': // object
do {
$q++;
} while ($q < $endptr && $str[$q] != '{');
$q++;
$level++;
$serialized .= substr($str, $p, $q - $p);
break;
case '}': // end of array|object
$q++;
$serialized .= substr($str, $p, $q - $p);
if (--$level == 0) {
break 2;
}
break;
default:
return false;
}
}
} else {
$serialized .= 'N;';
$q += 2;
}
$items++;
$p = $q;
}
return unserialize('a:' . $items . ':{' . $serialized . '}');
}
/**
* Setter for session lifetime
*
* @param int $lifetime Session lifetime (in seconds)
*/
public function set_lifetime($lifetime)
{
$this->lifetime = max(120, $lifetime);
// valid time range is now - 1/2 lifetime to now + 1/2 lifetime
$now = time();
$this->now = $now - ($now % ($this->lifetime / 2));
}
/**
* Getter for remote IP saved with this session
*
* @return string Client IP address
*/
public function get_ip()
{
return $this->ip;
}
/**
* Setter for cookie encryption secret
*
* @param string $secret Authentication secret string
*/
public function set_secret($secret = null)
{
// generate random hash and store in session
if (!$secret) {
if (!empty($_SESSION['auth_secret'])) {
$secret = $_SESSION['auth_secret'];
} else {
$secret = rcube_utils::random_bytes(strlen($this->key));
}
}
$_SESSION['auth_secret'] = $secret;
}
/**
* Enable/disable IP check
*
* @param bool $check IP address checking state
*/
public function set_ip_check($check)
{
$this->ip_check = $check;
}
/**
* Setter for the cookie name used for session cookie
*
* @param string $name Authentication cookie name
*/
public function set_cookiename($name)
{
if ($name) {
$this->cookiename = $name;
}
}
/**
* Check session authentication cookie
*
* @return bool True if valid, False if not
*/
public function check_auth()
{
$this->cookie = $_COOKIE[$this->cookiename] ?? null;
$result = $this->ip_check ? rcube_utils::remote_addr() == $this->ip : true;
$prev = null;
if (!$result) {
$this->log('IP check failed for ' . $this->key . '; expected ' . $this->ip . '; got ' . rcube_utils::remote_addr());
}
if ($result && $this->mkcookie($this->now) != $this->cookie) {
$this->log('Session auth check failed for ' . $this->key . '; timeslot = ' . date('Y-m-d H:i:s', $this->now));
$result = false;
// Check if using id from a previous time slot
for ($i = 1; $i <= 2; $i++) {
$prev = $this->now - ($this->lifetime / 2) * $i;
if ($this->mkcookie($prev) == $this->cookie) {
$this->log('Send new auth cookie for ' . $this->key . ': ' . $this->cookie);
$this->set_auth_cookie();
$result = true;
}
}
}
if (!$result) {
$this->log('Session authentication failed for ' . $this->key
. '; invalid auth cookie sent; timeslot = ' . date('Y-m-d H:i:s', $prev));
}
return $result;
}
/**
* Set session authentication cookie
*/
public function set_auth_cookie()
{
$this->cookie = $this->mkcookie($this->now);
rcube_utils::setcookie($this->cookiename, $this->cookie, 0);
$_COOKIE[$this->cookiename] = $this->cookie;
}
/**
* Create session cookie for specified time slot.
*
* @param int $timeslot Time slot to use
*
* @return string Cookie value
*/
protected function mkcookie($timeslot)
{
// make sure the secret key exists
$this->set_secret();
// no need to hash this, it's just a random string
return $_SESSION['auth_secret'] . '-' . $timeslot;
}
/**
* Writes debug information to the log
*
* @param string $line Log line
*/
public function log($line)
{
if ($this->logging) {
rcube::write_log('session', $line);
}
}
}
?>
Did this file decode correctly?
Original Code
<?php
/*
+-----------------------------------------------------------------------+
| This file is part of the Roundcube Webmail client |
| |
| Copyright (C) The Roundcube Dev Team |
| Copyright (C) Kolab Systems AG |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
| PURPOSE: |
| Provide database supported session management |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <[email protected]> |
| Author: Aleksander Machniak <[email protected]> |
| Author: Cor Bosman <[email protected]> |
+-----------------------------------------------------------------------+
*/
/**
* Abstract class to provide database supported session storage
*/
abstract class rcube_session implements SessionHandlerInterface
{
protected $config;
protected $key;
protected $ip;
protected $cookie;
protected $changed;
protected $start;
protected $vars;
protected $now;
protected $lifetime;
protected $reloaded = false;
protected $appends = [];
protected $unsets = [];
protected $gc_enabled = 0;
protected $gc_handlers = [];
protected $cookiename = 'roundcube_sessauth';
protected $ip_check = false;
protected $logging = false;
protected $ignore_write = false;
/**
* Blocks session data from being written to database.
* Can be used if write-race conditions are to be expected
*
* @var bool
*/
public $nowrite = false;
/**
* Factory, returns driver-specific instance of the class
*
* @param rcube_config $config
*
* @return rcube_session Session object
*/
public static function factory($config)
{
// get session storage driver
$storage = $config->get('session_storage', 'db');
// class name for this storage
$class = "rcube_session_{$storage}";
if (!class_exists($class)) {
rcube::raise_error([
'code' => 604,
'type' => 'session',
'message' => 'Failed to find session driver. Check session_storage config option',
], true, true);
}
return new $class($config);
}
/**
* Object constructor
*
* @param rcube_config $config
*/
public function __construct($config)
{
$this->config = $config;
// set ip check
$this->set_ip_check($this->config->get('ip_check'));
// set cookie name
if ($name = $this->config->get('session_auth_name')) {
$this->set_cookiename($name);
}
}
/**
* Register session handler
*/
public function register_session_handler()
{
if (session_id()) {
// Session already exists, skip
return;
}
ini_set('session.serialize_handler', 'php');
// set custom functions for PHP session management
session_set_save_handler($this);
}
/**
* Wrapper for session_start()
*/
public function start()
{
$this->start = microtime(true);
$this->ip = rcube_utils::remote_addr();
$this->logging = $this->config->get('session_debug', false);
$lifetime = $this->config->get('session_lifetime', 1) * 60;
$this->set_lifetime($lifetime);
session_start();
}
/**
* Abstract methods should be implemented by driver classes
*/
#[Override]
#[ReturnTypeWillChange]
abstract public function open($save_path, $session_name);
#[Override]
#[ReturnTypeWillChange]
abstract public function close();
#[Override]
#[ReturnTypeWillChange]
abstract public function destroy($key);
#[Override]
#[ReturnTypeWillChange]
abstract public function read($key);
/**
* Write data to the session store
*
* @param string $key Session identifier
* @param string $vars Session data
*
* @return bool True on success, False on failure
*/
abstract protected function save($key, $vars);
/**
* Write data to the session store
*
* @param string $key Session identifier
* @param string $newvars New session data string
* @param string $oldvars Old session data string
*
* @return bool True on success, False on failure
*/
abstract protected function update($key, $newvars, $oldvars);
/**
* Session write handler. This calls the implementation methods for write/update after some initial checks.
*
* @param string $key Session identifier
* @param string $vars Serialized data string
*
* @return bool True on success, False on failure
*/
#[Override]
#[ReturnTypeWillChange]
public function write($key, $vars)
{
if ($this->nowrite) {
return true;
}
// check cache
$oldvars = $this->get_cache($key);
// if there are cached vars, update store, else insert new data
if ($oldvars) {
$newvars = $this->fixvars($vars, $oldvars);
return $this->update($key, $newvars, $oldvars);
}
return $this->save($key, $vars);
}
/**
* Execute registered garbage collector routines
*
* @param int $maxlifetime Maximum session lifetime
*
* @return int|false Number of deleted sessions on success, False on failure
*/
#[Override]
#[ReturnTypeWillChange]
public function gc($maxlifetime)
{
// move gc execution to the script shutdown function
// see rcube::shutdown() and rcube_session::write_close()
$this->gc_enabled = $maxlifetime;
return 0;
}
/**
* Wrapper for session_write_close()
*/
public function write_close()
{
session_write_close();
// write_close() is called on script shutdown, see rcube::shutdown()
// execute cleanup functionality if enabled by session gc handler
// we do this after closing the session for better performance
$this->gc_shutdown();
}
/**
* Creates a new (separate) session
*
* @param array $data Session data
*
* @return ?string Session identifier (on success)
*/
public function create($data)
{
$length = strlen(session_id());
$key = rcube_utils::random_bytes($length);
// create new session
if ($this->save($key, $this->serialize($data))) {
return $key;
}
return null;
}
/**
* Merge vars with old vars and apply unsets
*/
protected function fixvars($vars, $oldvars)
{
$newvars = '';
if ($oldvars !== null) {
$a_oldvars = $this->unserialize($oldvars);
if (is_array($a_oldvars)) {
// remove unset keys on oldvars
foreach ((array) $this->unsets as $var) {
if (isset($a_oldvars[$var])) {
unset($a_oldvars[$var]);
} else {
$path = explode('.', $var);
$k = array_pop($path);
$node = &$this->get_node($path, $a_oldvars);
unset($node[$k]);
}
}
$newvars = $this->serialize(array_merge(
(array) $a_oldvars, (array) $this->unserialize($vars)));
} else {
$newvars = $vars;
}
}
$this->unsets = [];
return $newvars;
}
/**
* Register additional garbage collector functions
*
* @param mixed $func Callback function
*/
public function register_gc_handler($func)
{
foreach ($this->gc_handlers as $handler) {
if ($handler == $func) {
return;
}
}
$this->gc_handlers[] = $func;
}
/**
* Garbage collector handler to run on script shutdown
*/
protected function gc_shutdown()
{
if ($this->gc_enabled) {
foreach ($this->gc_handlers as $fct) {
call_user_func($fct);
}
}
}
/**
* Generate and set new session id
*
* @param bool $destroy If enabled the current session will be destroyed
*
* @return bool True on success, False on failure
*/
public function regenerate_id($destroy = true)
{
$old_id = session_id();
// Since PHP 7.0 session_regenerate_id() will cause the old
// session data update, we don't need this
$this->ignore_write = true;
session_regenerate_id($destroy);
$this->ignore_write = false;
$this->vars = null;
$this->key = session_id();
$this->log("Session regenerate: {$old_id} -> {$this->key}");
return true;
}
/**
* See if we have vars of this key already cached, and if so, return them.
*
* @param string $key Session identifier
*
* @return string Serialized data string
*/
protected function get_cache($key)
{
// no session data in cache (read() returns false)
if (!$this->key) {
$cache = null;
}
// use internal data for fast requests (up to 0.5 sec.)
elseif ($key == $this->key && (!$this->vars || microtime(true) - $this->start < 0.5)) {
$cache = $this->vars;
} else { // else read data again
$cache = $this->read($key);
}
return $cache;
}
/**
* Append the given value to the certain node in the session data array
*
* Warning: Do not use if you already modified $_SESSION in the same request (#1490608)
*
* @param string $path Path denoting the session variable where to append the value
* @param ?string $key Key name under which to append the new value (use null for appending to an indexed list)
* @param mixed $value Value to append to the session data array
*/
public function append($path, $key, $value)
{
// re-read session data from DB because it might be outdated
if (!$this->reloaded && microtime(true) - $this->start > 0.5) {
$this->reload();
$this->reloaded = true;
$this->start = microtime(true);
}
$node = &$this->get_node(explode('.', $path), $_SESSION);
if ($key !== null) {
$node[$key] = $value;
$path .= '.' . $key;
} else {
$node[] = $value;
}
$this->appends[] = $path;
// when overwriting a previously unset variable
if (array_key_exists($path, $this->unsets)) {
unset($this->unsets[$path]);
}
}
/**
* Unset a session variable
*
* @param string $var Variable name (can be a path denoting a certain node
* in the session array, e.g. compose.attachments.5)
*
* @return bool True on success, False on failure
*/
public function remove($var = null)
{
if (empty($var)) {
return $this->destroy(session_id());
}
$this->unsets[] = $var;
if (isset($_SESSION[$var])) {
unset($_SESSION[$var]);
} else {
$path = explode('.', $var);
$key = array_pop($path);
$node = &$this->get_node($path, $_SESSION);
unset($node[$key]);
}
return true;
}
/**
* Kill this session
*/
public function kill()
{
$this->log('Session destroy: ' . session_id());
$this->vars = null;
$this->ip = rcube_utils::remote_addr(); // update IP (might have changed)
$this->destroy(session_id());
rcube_utils::setcookie($this->cookiename, '-del-', time() - 60);
}
/**
* Re-read session data from storage backend
*/
public function reload()
{
// collect updated data from previous appends
$merge_data = [];
foreach ((array) $this->appends as $var) {
$path = explode('.', $var);
$value = $this->get_node($path, $_SESSION);
$k = array_pop($path);
$node = &$this->get_node($path, $merge_data);
$node[$k] = $value;
}
if ($this->key) {
$data = $this->read($this->key);
}
if (!empty($data)) {
session_decode($data);
// apply appends and unsets to reloaded data
$_SESSION = array_merge_recursive($_SESSION, $merge_data);
foreach ((array) $this->unsets as $var) {
if (isset($_SESSION[$var])) {
unset($_SESSION[$var]);
} else {
$path = explode('.', $var);
$k = array_pop($path);
$node = &$this->get_node($path, $_SESSION);
unset($node[$k]);
}
}
}
}
/**
* Returns a reference to the node in data array referenced by the given path.
* e.g. ['compose','attachments'] will return $_SESSION['compose']['attachments']
*/
protected function &get_node($path, &$data_arr)
{
$node = &$data_arr;
if (!empty($path)) {
foreach ((array) $path as $key) {
if (!isset($node[$key])) {
$node[$key] = [];
}
$node = &$node[$key];
}
}
return $node;
}
/**
* Serialize session data
*/
protected function serialize($vars)
{
$data = '';
if (is_array($vars)) {
foreach ($vars as $var => $value) {
$data .= $var . '|' . serialize($value);
}
} else {
$data = 'b:0;';
}
return $data;
}
/**
* Unserialize session data
* http://www.php.net/manual/en/function.session-decode.php#56106
*
* @param string $str Serialized data string
*
* @return array|false Unserialized data
*/
public static function unserialize($str)
{
$str = (string) $str;
$endptr = strlen($str);
$p = 0;
$serialized = '';
$items = 0;
$level = 0;
while ($p < $endptr) {
$q = $p;
while ($str[$q] != '|') {
if (++$q >= $endptr) {
break 2;
}
}
if ($str[$p] == '!') {
$p++;
$has_value = false;
} else {
$has_value = true;
}
$name = substr($str, $p, $q - $p);
$q++;
$serialized .= 's:' . strlen($name) . ':"' . $name . '";';
if ($has_value) {
while (true) {
$p = $q;
switch (strtolower($str[$q])) {
case 'n': // null
case 'b': // boolean
case 'i': // integer
case 'd': // decimal
do {
$q++;
} while (($q < $endptr) && ($str[$q] != ';'));
$q++;
$serialized .= substr($str, $p, $q - $p);
if ($level == 0) {
break 2;
}
break;
case 'r': // reference
$q += 2;
$id = '';
for (; ($q < $endptr) && ($str[$q] != ';'); $q++) {
$id .= $str[$q];
}
$q++;
// increment pointer because of outer array
$serialized .= 'R:' . (intval($id) + 1) . ';';
if ($level == 0) {
break 2;
}
break;
case 's': // string
$q += 2;
$length = '';
for (; ($q < $endptr) && ($str[$q] != ':'); $q++) {
$length .= $str[$q];
}
$q += 2;
$q += (int) $length + 2;
$serialized .= substr($str, $p, $q - $p);
if ($level == 0) {
break 2;
}
break;
case 'a': // array
case 'o': // object
do {
$q++;
} while ($q < $endptr && $str[$q] != '{');
$q++;
$level++;
$serialized .= substr($str, $p, $q - $p);
break;
case '}': // end of array|object
$q++;
$serialized .= substr($str, $p, $q - $p);
if (--$level == 0) {
break 2;
}
break;
default:
return false;
}
}
} else {
$serialized .= 'N;';
$q += 2;
}
$items++;
$p = $q;
}
return unserialize('a:' . $items . ':{' . $serialized . '}');
}
/**
* Setter for session lifetime
*
* @param int $lifetime Session lifetime (in seconds)
*/
public function set_lifetime($lifetime)
{
$this->lifetime = max(120, $lifetime);
// valid time range is now - 1/2 lifetime to now + 1/2 lifetime
$now = time();
$this->now = $now - ($now % ($this->lifetime / 2));
}
/**
* Getter for remote IP saved with this session
*
* @return string Client IP address
*/
public function get_ip()
{
return $this->ip;
}
/**
* Setter for cookie encryption secret
*
* @param string $secret Authentication secret string
*/
public function set_secret($secret = null)
{
// generate random hash and store in session
if (!$secret) {
if (!empty($_SESSION['auth_secret'])) {
$secret = $_SESSION['auth_secret'];
} else {
$secret = rcube_utils::random_bytes(strlen($this->key));
}
}
$_SESSION['auth_secret'] = $secret;
}
/**
* Enable/disable IP check
*
* @param bool $check IP address checking state
*/
public function set_ip_check($check)
{
$this->ip_check = $check;
}
/**
* Setter for the cookie name used for session cookie
*
* @param string $name Authentication cookie name
*/
public function set_cookiename($name)
{
if ($name) {
$this->cookiename = $name;
}
}
/**
* Check session authentication cookie
*
* @return bool True if valid, False if not
*/
public function check_auth()
{
$this->cookie = $_COOKIE[$this->cookiename] ?? null;
$result = $this->ip_check ? rcube_utils::remote_addr() == $this->ip : true;
$prev = null;
if (!$result) {
$this->log('IP check failed for ' . $this->key . '; expected ' . $this->ip . '; got ' . rcube_utils::remote_addr());
}
if ($result && $this->mkcookie($this->now) != $this->cookie) {
$this->log('Session auth check failed for ' . $this->key . '; timeslot = ' . date('Y-m-d H:i:s', $this->now));
$result = false;
// Check if using id from a previous time slot
for ($i = 1; $i <= 2; $i++) {
$prev = $this->now - ($this->lifetime / 2) * $i;
if ($this->mkcookie($prev) == $this->cookie) {
$this->log('Send new auth cookie for ' . $this->key . ': ' . $this->cookie);
$this->set_auth_cookie();
$result = true;
}
}
}
if (!$result) {
$this->log('Session authentication failed for ' . $this->key
. '; invalid auth cookie sent; timeslot = ' . date('Y-m-d H:i:s', $prev));
}
return $result;
}
/**
* Set session authentication cookie
*/
public function set_auth_cookie()
{
$this->cookie = $this->mkcookie($this->now);
rcube_utils::setcookie($this->cookiename, $this->cookie, 0);
$_COOKIE[$this->cookiename] = $this->cookie;
}
/**
* Create session cookie for specified time slot.
*
* @param int $timeslot Time slot to use
*
* @return string Cookie value
*/
protected function mkcookie($timeslot)
{
// make sure the secret key exists
$this->set_secret();
// no need to hash this, it's just a random string
return $_SESSION['auth_secret'] . '-' . $timeslot;
}
/**
* Writes debug information to the log
*
* @param string $line Log line
*/
public function log($line)
{
if ($this->logging) {
rcube::write_log('session', $line);
}
}
}
Function Calls
None |
Stats
MD5 | 023bd7c882fa01559bd97f94c8fbb05d |
Eval Count | 0 |
Decode Time | 116 ms |