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)

---
 program/include/session.inc |  161 +++++++++++++++++++++++++++++++++++++++++++++--------
 1 files changed, 137 insertions(+), 24 deletions(-)

diff --git a/program/include/session.inc b/program/include/session.inc
index f91a51a..2d537ed 100644
--- a/program/include/session.inc
+++ b/program/include/session.inc
@@ -5,7 +5,7 @@
  | program/include/session.inc                                           |
  |                                                                       |
  | This file is part of the RoundCube Webmail client                     |
- | Copyright (C) 2005-2008, RoundCube Dev. - Switzerland                 |
+ | Copyright (C) 2005-2009, RoundCube Dev. - Switzerland                 |
  | Licensed under the GNU GPL                                            |
  |                                                                       |
  | PURPOSE:                                                              |
@@ -39,10 +39,6 @@
   
   $DB = rcmail::get_instance()->get_dbh();
   
-  if ($DB->is_error()) {
-    return false;
-  }
-  
   $sql_result = $DB->query(
     "SELECT vars, ip, " . $DB->unixtimestamp('changed') . " AS changed
      FROM " . get_table_name('session') . "
@@ -66,23 +62,14 @@
 {
   $DB = rcmail::get_instance()->get_dbh();
   
-  if ($DB->is_error()) {
-    return false;
-  }
-
-  $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 {
@@ -99,16 +86,146 @@
 }
 
 
+// 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)
 {
   $rcmail = rcmail::get_instance();
   $DB = $rcmail->get_dbh();
   
-  if ($DB->is_error()) {
-    return false;
-  }
-
   $DB->query("DELETE FROM " . get_table_name('session') . " WHERE sess_id=?", $key);
 
   return true;
@@ -120,10 +237,6 @@
 {
   $rcmail = rcmail::get_instance();
   $DB = $rcmail->get_dbh();
-
-  if ($DB->is_error()) {
-    return false;
-  }
 
   // just delete all expired sessions
   $DB->query("DELETE FROM " . get_table_name('session') . "

--
Gitblit v1.9.1