From 617b4f699f2e47991c50e05528b1f9ecbc3c3d9c Mon Sep 17 00:00:00 2001
From: svncommit <devs@roundcube.net>
Date: Tue, 12 May 2009 09:26:07 -0400
Subject: [PATCH] Minimize chance of race condition in session handling (#1485659, #1484678)

---
 CHANGELOG                   |    1 
 program/include/session.inc |  143 +++++++++++++++++++++++++++++++++++++++++++++--
 program/include/rcmail.php  |    2 
 program/steps/mail/func.inc |    2 
 4 files changed, 139 insertions(+), 9 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 1bc633d..31efef1 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,7 @@
 CHANGELOG RoundCube Webmail
 ===========================
 
+- Minimize chance of race condition in session handling (#1485659, #1484678)
 - Fix session handling on non-session SQL query error (#1485734)
 - Fix html editor mode setting when reopening draft message (#1485834)
 - Added quick search box menu (#1484304)
diff --git a/program/include/rcmail.php b/program/include/rcmail.php
index e660e52..45b59ae 100644
--- a/program/include/rcmail.php
+++ b/program/include/rcmail.php
@@ -124,7 +124,7 @@
 
     // 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 d75b303..2d537ed 100644
--- a/program/include/session.inc
+++ b/program/include/session.inc
@@ -62,19 +62,14 @@
 {
   $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 @@
 }
 
 
+// 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)
 {
diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc
index 537199c..5e9bc8a 100644
--- a/program/steps/mail/func.inc
+++ b/program/steps/mail/func.inc
@@ -1278,7 +1278,7 @@
 
   rcmail::get_instance()->plugins->exec_hook('cleanup_attachments',array());
   
-  unset($_SESSION['compose']);
+  rcube_sess_unset('compose');
   }
   
 

--
Gitblit v1.9.1