summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG5
-rw-r--r--SQL/postgres.initial.sql4
-rw-r--r--SQL/postgres.update.sql2
-rw-r--r--index.php10
-rw-r--r--program/include/rcube_imap_generic.php57
-rw-r--r--program/include/rcube_session.php23
-rw-r--r--program/lib/Net/SMTP.php88
7 files changed, 115 insertions, 74 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 4882f4eb7..fad555c8f 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 089cae036..d6f4db71f 100644
--- a/SQL/postgres.initial.sql
+++ b/SQL/postgres.initial.sql
@@ -25,7 +25,7 @@ CREATE TABLE users (
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 @@ CREATE TABLE messages (
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 2f4498d36..0ae8d3f7f 100644
--- a/SQL/postgres.update.sql
+++ b/SQL/postgres.update.sql
@@ -85,7 +85,7 @@ ALTER TABLE users ALTER last_login SET DEFAULT NULL;
-- 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 f4e2a55a1..cf3369376 100644
--- a/index.php
+++ b/index.php
@@ -95,10 +95,12 @@ if ($RCMAIL->task == 'login' && $RCMAIL->action == 'login') {
}
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 @@ if ($RCMAIL->task == 'login' && $RCMAIL->action == 'login') {
$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 f3855892a..29159c734 100644
--- a/program/include/rcube_imap_generic.php
+++ b/program/include/rcube_imap_generic.php
@@ -213,31 +213,26 @@ class rcube_imap_generic
{
$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 @@ class rcube_imap_generic
{
$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 @@ class rcube_imap_generic
} 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 @@ class rcube_imap_generic
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 @@ class rcube_imap_generic
}
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 @@ class rcube_imap_generic
$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 @@ class rcube_imap_generic
$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 59ce42379..0bae4a711 100644
--- a/program/include/rcube_session.php
+++ b/program/include/rcube_session.php
@@ -183,27 +183,12 @@ class rcube_session
}
- public function regenerate_id()
+ public function regenerate_id($destroy=true)
{
- $randval = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
-
- 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);
+ session_regenerate_id($destroy);
+ $this->vars = false;
+ $this->key = session_id();
return true;
}
diff --git a/program/lib/Net/SMTP.php b/program/lib/Net/SMTP.php
index 31307f03b..fef8076ce 100644
--- a/program/lib/Net/SMTP.php
+++ b/program/lib/Net/SMTP.php
@@ -106,6 +106,13 @@ class Net_SMTP
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 @@ class Net_SMTP
* @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 @@ class Net_SMTP
$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
@@ -179,6 +189,19 @@ class Net_SMTP
}
/**
+ * 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);
+ }
+
+ /**
* Set the value of the debugging flag.
*
* @param boolean $debug New value for the debugging flag.
@@ -369,7 +392,7 @@ class Net_SMTP
* 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?
*
@@ -388,6 +411,16 @@ class Net_SMTP
$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))) {
return $error;
}
@@ -617,7 +650,8 @@ class Net_SMTP
$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 @@ class Net_SMTP
} elseif (trim($params['verp'])) {
$args .= ' XVERP=' . $params['verp'];
}
- } elseif (is_string($params)) {
+ } elseif (is_string($params) && !empty($params)) {
$args .= ' ' . $params;
}
@@ -919,31 +953,29 @@ class Net_SMTP
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;
-
- 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);
- }
+ /* 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 ($size >= $this->_esmtp['SIZE']) {
- $this->disconnect();
- return PEAR::raiseError('Message size exceeds server limit');
+ 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);
+ }
+
+ /* 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 @@ class Net_SMTP
}
}
} 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.