summaryrefslogtreecommitdiff
path: root/program/include
diff options
context:
space:
mode:
authorsvncommit <devs@roundcube.net>2009-05-12 13:26:07 +0000
committersvncommit <devs@roundcube.net>2009-05-12 13:26:07 +0000
commit617b4f699f2e47991c50e05528b1f9ecbc3c3d9c (patch)
tree83b540541b969d913ff42b0a0d3ad760a312e8f3 /program/include
parent78cdeba1a82dd744f59ebfe625a7d7dd8d23ff41 (diff)
Minimize chance of race condition in session handling (#1485659, #1484678)
Diffstat (limited to 'program/include')
-rw-r--r--program/include/rcmail.php2
-rw-r--r--program/include/session.inc143
2 files changed, 137 insertions, 8 deletions
diff --git a/program/include/rcmail.php b/program/include/rcmail.php
index e660e52c5..45b59ae49 100644
--- a/program/include/rcmail.php
+++ b/program/include/rcmail.php
@@ -124,7 +124,7 @@ class rcmail
// reset some session parameters when changing task
if ($_SESSION['task'] != $this->task)
- unset($_SESSION['page']);
+ rcube_sess_unset('page');
// set current task to session
$_SESSION['task'] = $this->task;
diff --git a/program/include/session.inc b/program/include/session.inc
index d75b303b3..2d537eda6 100644
--- a/program/include/session.inc
+++ b/program/include/session.inc
@@ -62,19 +62,14 @@ function rcube_sess_write($key, $vars)
{
$DB = rcmail::get_instance()->get_dbh();
- $sql_result = $DB->query(
- "SELECT 1 FROM " . get_table_name('session') . "
- WHERE sess_id=?",
- $key);
-
$now = $DB->fromunixtime(time());
- if ($DB->num_rows($sql_result)) {
+ if ($oldvars = rcube_sess_read($key)) {
$DB->query(
"UPDATE " . get_table_name('session') . "
SET vars=?, changed= " . $now . "
WHERE sess_id=?",
- $vars,
+ rcube_sess_serialize(array_merge(rcube_sess_unserialize($oldvars), rcube_sess_unserialize($vars))),
$key);
}
else {
@@ -91,6 +86,140 @@ function rcube_sess_write($key, $vars)
}
+// unset session variable
+function rcube_sess_unset($var)
+{
+ $DB = rcmail::get_instance()->get_dbh();
+
+ if ($DB->is_error()) {
+ return false;
+ }
+
+ $now = $DB->fromunixtime(time());
+
+ $sql_result = $DB->query(
+ "SELECT vars
+ FROM " . get_table_name('session') . "
+ WHERE sess_id=?",
+ session_id());
+
+ if ($sql_arr = $DB->fetch_assoc($sql_result)) {
+ $vars = rcube_sess_unserialize($sql_arr['vars']);
+ if (isset($vars[$var])) {
+ unset($vars[$var]);
+ $DB->query(
+ "UPDATE " . get_table_name('session') . "
+ SET vars=?, changed= " . $now . "
+ WHERE sess_id=?",
+ rcube_sess_serialize($vars),
+ session_id());
+ }
+ }
+
+ return true;
+}
+
+
+// serialize session data
+function rcube_sess_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
+function rcube_sess_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) {
+ for (;;) {
+ $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;
+ for ($id = ''; ($q < $endptr) && ($str[$q] != ';'); $q++) $id .= $str[$q];
+ $q++;
+ $serialized .= 'R:' . ($id + 1) . ';'; /* increment pointer because of outer array */
+ if ($level == 0) break 2;
+ break;
+ case 's': /* string */
+ $q+=2;
+ for ($length=''; ($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 . '}' );
+}
+
+
// handler for session_destroy()
function rcube_sess_destroy($key)
{