From 5228a5558f0ee9af785f1b4cdcef4d97b17b33f6 Mon Sep 17 00:00:00 2001
From: alecpl <alec@alec.pl>
Date: Fri, 11 Mar 2011 03:55:20 -0500
Subject: [PATCH] - Applied fixes from trunk

---
 CHANGELOG                              |    5 +
 index.php                              |   10 +-
 program/lib/Net/SMTP.php               |   86 ++++++++++++++-------
 SQL/postgres.update.sql                |    2 
 program/include/rcube_session.php      |   23 +----
 SQL/postgres.initial.sql               |    4 
 program/include/rcube_imap_generic.php |   57 +++++++++----
 7 files changed, 114 insertions(+), 73 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 4882f4e..fad555c 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,11 @@
 CHANGELOG Roundcube Webmail
 ===========================
 
+- PEAR::Net_SMTP 1.5.1
+- Force names of unique constraints in PostgreSQL DDL
+- Add code for prevention from IMAP connection hangs when server closes socket unexpectedly
+- Remove redundant DELETE query (for old session deletion) on login
+- Get around unreliable rand() and mt_rand() in session ID generation (#1486281)
 - Fix some emails are not shown using Cyrus IMAP (#1487820)
 - Fix handling of mime-encoded words with non-integral number of octets in a word (#1487801)
 - Fix parsing links with non-printable characters inside (#1487805)
diff --git a/SQL/postgres.initial.sql b/SQL/postgres.initial.sql
index 089cae0..d6f4db7 100644
--- a/SQL/postgres.initial.sql
+++ b/SQL/postgres.initial.sql
@@ -25,7 +25,7 @@
     last_login timestamp with time zone DEFAULT NULL,
     "language" varchar(5),
     preferences text DEFAULT ''::text NOT NULL,
-    UNIQUE (username, mail_host)
+    CONSTRAINT users_username_key UNIQUE (username, mail_host)
 );
 
 CREATE INDEX users_alias_id_idx ON users (alias);
@@ -217,7 +217,7 @@
     size integer DEFAULT 0 NOT NULL,
     headers text NOT NULL,
     structure text,
-    UNIQUE (user_id, cache_key, uid)
+    CONSTRAINT messages_user_id_key UNIQUE (user_id, cache_key, uid)
 );
 
 CREATE INDEX messages_index_idx ON messages (user_id, cache_key, idx);
diff --git a/SQL/postgres.update.sql b/SQL/postgres.update.sql
index 2f4498d..0ae8d3f 100644
--- a/SQL/postgres.update.sql
+++ b/SQL/postgres.update.sql
@@ -85,7 +85,7 @@
 -- Updates from version 0.4.2
 
 DROP INDEX users_username_id_idx;
-ALTER TABLE users ADD UNIQUE (username, mail_host);
+ALTER TABLE users ADD CONSTRAINT users_username_key UNIQUE (username, mail_host);
 ALTER TABLE contacts ALTER email TYPE varchar(255);
 
 TRUNCATE messages;
diff --git a/index.php b/index.php
index f4e2a55..cf33693 100644
--- a/index.php
+++ b/index.php
@@ -95,10 +95,12 @@
   }
   else if ($auth['valid'] && !$auth['abort'] &&
         !empty($auth['host']) && !empty($auth['user']) &&
-        $RCMAIL->login($auth['user'], $auth['pass'], $auth['host'])) {
-    // create new session ID
+        $RCMAIL->login($auth['user'], $auth['pass'], $auth['host'])
+  ) {
+    // create new session ID, don't destroy the current session
+    // it was destroyed already by $RCMAIL->kill_session() above
     $RCMAIL->session->remove('temp');
-    $RCMAIL->session->regenerate_id();
+    $RCMAIL->session->regenerate_id(false);
 
     // send auth cookie if necessary
     $RCMAIL->authenticate_session();
@@ -110,7 +112,7 @@
     $query = array();
     if ($url = get_input_value('_url', RCUBE_INPUT_POST)) {
       parse_str($url, $query);
-      
+
       // prevent endless looping on login page
       if ($query['_task'] == 'login')
         unset($query['_task']);
diff --git a/program/include/rcube_imap_generic.php b/program/include/rcube_imap_generic.php
index f385589..29159c7 100644
--- a/program/include/rcube_imap_generic.php
+++ b/program/include/rcube_imap_generic.php
@@ -213,31 +213,26 @@
     {
         $line = '';
 
-        if (!$this->fp) {
-            return NULL;
-        }
-
         if (!$size) {
             $size = 1024;
         }
 
         do {
-            if (feof($this->fp)) {
+            if ($this->eof()) {
                 return $line ? $line : NULL;
             }
 
             $buffer = fgets($this->fp, $size);
 
             if ($buffer === false) {
-                @fclose($this->fp);
-                $this->fp = null;
+                $this->closeSocket();
                 break;
             }
             if ($this->_debug) {
                 $this->debug('S: '. rtrim($buffer));
             }
             $line .= $buffer;
-        } while ($buffer[strlen($buffer)-1] != "\n");
+        } while (substr($buffer, -1) != "\n");
 
         return $line;
     }
@@ -267,7 +262,7 @@
     {
         $data = '';
         $len  = 0;
-        while ($len < $bytes && !feof($this->fp))
+        while ($len < $bytes && !$this->eof())
         {
             $d = fread($this->fp, $bytes-$len);
             if ($this->_debug) {
@@ -312,8 +307,7 @@
             } else if ($res == 'BAD') {
                 $this->errornum = self::ERROR_BAD;
             } else if ($res == 'BYE') {
-                @fclose($this->fp);
-                $this->fp = null;
+                $this->closeSocket();
                 $this->errornum = self::ERROR_BYE;
             }
 
@@ -339,6 +333,32 @@
         return self::ERROR_UNKNOWN;
     }
 
+    private function eof()
+    {
+        if (!is_resource($this->fp)) {
+            return true;
+        }
+
+        // If a connection opened by fsockopen() wasn't closed
+        // by the server, feof() will hang.
+        $start = microtime(true);
+
+        if (feof($this->fp) || 
+            ($this->prefs['timeout'] && (microtime(true) - $start > $this->prefs['timeout']))
+        ) {
+            $this->closeSocket();
+            return true;
+        }
+
+        return false;
+    }
+
+    private function closeSocket()
+    {
+        @fclose($this->fp);
+        $this->fp = null;
+    }
+
     function setError($code, $msg='')
     {
         $this->errornum = $code;
@@ -360,8 +380,7 @@
         }
         if ($error && preg_match('/^\* (BYE|BAD) /i', $string, $m)) {
             if (strtoupper($m[1]) == 'BYE') {
-                @fclose($this->fp);
-                $this->fp = null;
+                $this->closeSocket();
             }
             return true;
         }
@@ -701,11 +720,12 @@
             $host = $this->prefs['ssl_mode'] . '://' . $host;
         }
 
+        if ($this->prefs['timeout'] <= 0) {
+            $this->prefs['timeout'] = ini_get('default_socket_timeout');
+        }
+
         // Connect
-        if ($this->prefs['timeout'] > 0)
-            $this->fp = @fsockopen($host, $this->prefs['port'], $errno, $errstr, $this->prefs['timeout']);
-        else
-            $this->fp = @fsockopen($host, $this->prefs['port'], $errno, $errstr);
+        $this->fp = @fsockopen($host, $this->prefs['port'], $errno, $errstr, $this->prefs['timeout']);
 
         if (!$this->fp) {
             $this->setError(self::ERROR_BAD, sprintf("Could not connect to %s:%d: %s", $host, $this->prefs['port'], $errstr));
@@ -855,8 +875,7 @@
             $this->readReply();
         }
 
-        @fclose($this->fp);
-        $this->fp = false;
+        $this->closeSocket();
     }
 
     /**
diff --git a/program/include/rcube_session.php b/program/include/rcube_session.php
index 59ce423..0bae4a7 100644
--- a/program/include/rcube_session.php
+++ b/program/include/rcube_session.php
@@ -183,27 +183,12 @@
   }
 
 
-  public function regenerate_id()
+  public function regenerate_id($destroy=true)
   {
-    $randval = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+    session_regenerate_id($destroy);
 
-    for ($random = '', $i=1; $i <= 32; $i++) {
-      $random .= substr($randval, mt_rand(0,(strlen($randval) - 1)), 1);
-    }
-
-    // use md5 value for id or remove capitals from string $randval
-    $random = md5($random);
-
-    // delete old session record
-    $this->destroy(session_id());
-
-    session_id($random);
-
-    $cookie   = session_get_cookie_params();
-    $lifetime = $cookie['lifetime'] ? time() + $cookie['lifetime'] : 0;
-
-    rcmail::setcookie(session_name(), $random, $lifetime);
-
+    $this->vars = false;
+    $this->key  = session_id();
     return true;
   }
 
diff --git a/program/lib/Net/SMTP.php b/program/lib/Net/SMTP.php
index 31307f0..fef8076 100644
--- a/program/lib/Net/SMTP.php
+++ b/program/lib/Net/SMTP.php
@@ -106,6 +106,13 @@
     var $_socket = null;
 
     /**
+     * The socket I/O timeout value in seconds.
+     * @var int
+     * @access private
+     */
+    var $_timeout = 0;
+
+    /**
      * The most recent server response code.
      * @var int
      * @access private
@@ -148,11 +155,13 @@
      * @param integer $port       The port to connect to.
      * @param string  $localhost  The value to give when sending EHLO or HELO.
      * @param boolean $pipeling   Use SMTP command pipelining
+     * @param integer $timeout    Socket I/O timeout in seconds.
      *
      * @access  public
      * @since   1.0
      */
-    function Net_SMTP($host = null, $port = null, $localhost = null, $pipelining = false)
+    function Net_SMTP($host = null, $port = null, $localhost = null,
+        $pipelining = false, $timeout = 0)
     {
         if (isset($host)) {
             $this->host = $host;
@@ -166,6 +175,7 @@
         $this->pipelining = $pipelining;
 
         $this->_socket = new Net_Socket();
+        $this->_timeout = $timeout;
 
         /* Include the Auth_SASL package.  If the package is not
          * available, we disable the authentication methods that
@@ -176,6 +186,19 @@
             $pos = array_search('CRAM-MD5', $this->auth_methods);
             unset($this->auth_methods[$pos]);
         }
+    }
+
+    /**
+     * Set the socket I/O timeout value in seconds plus microseconds.
+     *
+     * @param   integer $seconds        Timeout value in seconds.
+     * @param   integer $microseconds   Additional value in microseconds.
+     *
+     * @access  public
+     * @since   1.5.0
+     */
+    function setTimeout($seconds, $microseconds = 0) {
+        return $this->_socket->setTimeout($seconds, $microseconds);
     }
 
     /**
@@ -369,7 +392,7 @@
      * Attempt to connect to the SMTP server.
      *
      * @param   int     $timeout    The timeout value (in seconds) for the
-     *                              socket connection.
+     *                              socket connection attempt.
      * @param   bool    $persistent Should a persistent socket connection
      *                              be used?
      *
@@ -386,6 +409,16 @@
         if (PEAR::isError($result)) {
             return PEAR::raiseError('Failed to connect socket: ' .
                                     $result->getMessage());
+        }
+
+        /*
+         * Now that we're connected, reset the socket's timeout value for 
+         * future I/O operations.  This allows us to have different socket 
+         * timeout values for the initial connection (our $timeout parameter) 
+         * and all other socket operations.
+         */
+        if (PEAR::isError($error = $this->setTimeout($this->_timeout))) {
+            return $error;
         }
 
         if (PEAR::isError($error = $this->_parseResponse(220))) {
@@ -617,7 +650,8 @@
         $challenge = base64_decode($this->_arguments[0]);
         $digest = &Auth_SASL::factory('digestmd5');
         $auth_str = base64_encode($digest->getResponse($uid, $pwd, $challenge,
-                                                       $this->host, "smtp", $authz));
+                                                       $this->host, "smtp",
+                                                       $authz));
 
         if (PEAR::isError($error = $this->_put($auth_str))) {
             return $error;
@@ -830,7 +864,7 @@
             } elseif (trim($params['verp'])) {
                 $args .= ' XVERP=' . $params['verp'];
             }
-        } elseif (is_string($params)) {
+        } elseif (is_string($params) && !empty($params)) {
             $args .= ' ' . $params;
         }
 
@@ -919,31 +953,29 @@
             return PEAR::raiseError('Expected a string or file resource');
         }
 
-        /* RFC 1870, section 3, subsection 3 states "a value of zero
-         * indicates that no fixed maximum message size is in force".
-         * Furthermore, it says that if "the parameter is omitted no
-         * information is conveyed about the server's fixed maximum
-         * message size". */
-        if (isset($this->_esmtp['SIZE']) && ($this->_esmtp['SIZE'] > 0)) {
-            /* Start by considering the size of the optional headers string.  
-             * We also account for the addition 4 character "\r\n\r\n"
-             * separator sequence. */
-            $size = (is_null($headers)) ? 0 : strlen($headers) + 4;
+        /* Start by considering the size of the optional headers string.  We
+         * also account for the addition 4 character "\r\n\r\n" separator
+         * sequence. */
+        $size = (is_null($headers)) ? 0 : strlen($headers) + 4;
 
-            if (is_resource($data)) {
-                $stat = fstat($data);
-                if ($stat === false) {
-                    return PEAR::raiseError('Failed to get file size');
-                }
-                $size += $stat['size'];
-            } else {
-                $size += strlen($data);
+        if (is_resource($data)) {
+            $stat = fstat($data);
+            if ($stat === false) {
+                return PEAR::raiseError('Failed to get file size');
             }
+            $size += $stat['size'];
+        } else {
+            $size += strlen($data);
+        }
 
-            if ($size >= $this->_esmtp['SIZE']) {
-                $this->disconnect();
-                return PEAR::raiseError('Message size exceeds server limit');
-            }
+        /* RFC 1870, section 3, subsection 3 states "a value of zero indicates
+         * that no fixed maximum message size is in force".  Furthermore, it
+         * says that if "the parameter is omitted no information is conveyed
+         * about the server's fixed maximum message size". */
+        $limit = (isset($this->_esmtp['SIZE'])) ? $this->_esmtp['SIZE'] : 0;
+        if ($limit > 0 && $size >= $limit) {
+            $this->disconnect();
+            return PEAR::raiseError('Message size exceeds server limit');
         }
 
         /* Initiate the DATA command. */
@@ -974,8 +1006,6 @@
                 }
             }
         } else {
-            if (!isset($size))
-                $size = strlen($data);
             /*
              * Break up the data by sending one chunk (up to 512k) at a time.  
              * This approach reduces our peak memory usage.

--
Gitblit v1.9.1