summaryrefslogtreecommitdiff
path: root/program/lib/Roundcube/rcube_imap_generic.php
diff options
context:
space:
mode:
Diffstat (limited to 'program/lib/Roundcube/rcube_imap_generic.php')
-rw-r--r--program/lib/Roundcube/rcube_imap_generic.php282
1 files changed, 129 insertions, 153 deletions
diff --git a/program/lib/Roundcube/rcube_imap_generic.php b/program/lib/Roundcube/rcube_imap_generic.php
index bce4cd4e2..1b28c3bd7 100644
--- a/program/lib/Roundcube/rcube_imap_generic.php
+++ b/program/lib/Roundcube/rcube_imap_generic.php
@@ -72,8 +72,6 @@ class rcube_imap_generic
const COMMAND_CAPABILITY = 2;
const COMMAND_LASTLINE = 4;
- const DEBUG_LINE_LENGTH = 4098; // 4KB + 2B for \r\n
-
/**
* Object constructor
*/
@@ -789,21 +787,23 @@ class rcube_imap_generic
// TLS connection
if ($this->prefs['ssl_mode'] == 'tls' && $this->getCapability('STARTTLS')) {
- $res = $this->execute('STARTTLS');
+ if (version_compare(PHP_VERSION, '5.1.0', '>=')) {
+ $res = $this->execute('STARTTLS');
- if ($res[0] != self::ERROR_OK) {
- $this->closeConnection();
- return false;
- }
+ if ($res[0] != self::ERROR_OK) {
+ $this->closeConnection();
+ return false;
+ }
- if (!stream_socket_enable_crypto($this->fp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
- $this->setError(self::ERROR_BAD, "Unable to negotiate TLS");
- $this->closeConnection();
- return false;
- }
+ if (!stream_socket_enable_crypto($this->fp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
+ $this->setError(self::ERROR_BAD, "Unable to negotiate TLS");
+ $this->closeConnection();
+ return false;
+ }
- // Now we're secure, capabilities need to be reread
- $this->clearCapability();
+ // Now we're secure, capabilities need to be reread
+ $this->clearCapability();
+ }
}
// Send ID info
@@ -902,11 +902,6 @@ class rcube_imap_generic
$this->prefs['auth_type'] = 'CHECK';
}
- // disabled capabilities
- if (!empty($this->prefs['disabled_caps'])) {
- $this->prefs['disabled_caps'] = array_map('strtoupper', (array)$this->prefs['disabled_caps']);
- }
-
// additional message flags
if (!empty($this->prefs['message_flags'])) {
$this->flags = array_merge($this->flags, $this->prefs['message_flags']);
@@ -1088,8 +1083,8 @@ class rcube_imap_generic
/**
* Executes EXPUNGE command
*
- * @param string $mailbox Mailbox name
- * @param string|array $messages Message UIDs to expunge
+ * @param string $mailbox Mailbox name
+ * @param string $messages Message UIDs to expunge
*
* @return boolean True on success, False on error
*/
@@ -1107,13 +1102,10 @@ class rcube_imap_generic
// Clear internal status cache
unset($this->data['STATUS:'.$mailbox]);
- if (!empty($messages) && $messages != '*' && $this->hasCapability('UIDPLUS')) {
- $messages = self::compressMessageSet($messages);
- $result = $this->execute('UID EXPUNGE', array($messages), self::COMMAND_NORESPONSE);
- }
- else {
+ if ($messages)
+ $result = $this->execute('UID EXPUNGE', array($messages), self::COMMAND_NORESPONSE);
+ else
$result = $this->execute('EXPUNGE', null, self::COMMAND_NORESPONSE);
- }
if ($result == self::ERROR_OK) {
$this->selected = null; // state has changed, need to reselect
@@ -1350,8 +1342,9 @@ class rcube_imap_generic
$folders[$mailbox] = array();
}
- // store folder options
- if ($cmd == 'LIST') {
+ // store LSUB options only if not empty, this way
+ // we can detect a situation when LIST doesn't return specified folder
+ if (!empty($opts) || $cmd == 'LIST') {
// Add to options array
if (empty($this->data['LIST'][$mailbox]))
$this->data['LIST'][$mailbox] = $opts;
@@ -1583,12 +1576,11 @@ class rcube_imap_generic
}
// message IDs
- if (!empty($add)) {
+ if (!empty($add))
$add = $this->compressMessageSet($add);
- }
list($code, $response) = $this->execute($return_uid ? 'UID SORT' : 'SORT',
- array("($field)", $encoding, !empty($add) ? $add : 'ALL'));
+ array("($field)", $encoding, 'ALL' . (!empty($add) ? ' '.$add : '')));
if ($code != self::ERROR_OK) {
$response = null;
@@ -1675,6 +1667,7 @@ class rcube_imap_generic
}
if (!empty($criteria)) {
+ $modseq = stripos($criteria, 'MODSEQ') !== false;
$params .= ($params ? ' ' : '') . $criteria;
}
else {
@@ -1813,6 +1806,7 @@ class rcube_imap_generic
if ($skip_deleted && preg_match('/FLAGS \(([^)]+)\)/', $line, $matches)) {
$flags = explode(' ', strtoupper($matches[1]));
if (in_array('\\DELETED', $flags)) {
+ $deleted[$id] = $id;
continue;
}
}
@@ -2004,6 +1998,7 @@ class rcube_imap_generic
/**
* Moves message(s) from one folder to another.
+ * Original message(s) will be marked as deleted.
*
* @param string|array $messages Message UID(s)
* @param string $from Mailbox name
@@ -2022,41 +2017,15 @@ class rcube_imap_generic
return false;
}
- // use MOVE command (RFC 6851)
- if ($this->hasCapability('MOVE')) {
- // Clear last COPYUID data
- unset($this->data['COPYUID']);
+ $r = $this->copy($messages, $from, $to);
+ if ($r) {
// Clear internal status cache
- unset($this->data['STATUS:'.$to]);
unset($this->data['STATUS:'.$from]);
- $result = $this->execute('UID MOVE', array(
- $this->compressMessageSet($messages), $this->escape($to)),
- self::COMMAND_NORESPONSE);
-
- return ($result == self::ERROR_OK);
- }
-
- // use COPY + STORE +FLAGS.SILENT \Deleted + EXPUNGE
- $result = $this->copy($messages, $from, $to);
-
- if ($result) {
- // Clear internal status cache
- unset($this->data['STATUS:'.$from]);
-
- $result = $this->flag($from, $messages, 'DELETED');
-
- if ($messages == '*') {
- // CLOSE+SELECT should be faster than EXPUNGE
- $this->close();
- }
- else {
- $this->expunge($from, $messages);
- }
+ return $this->flag($from, $messages, 'DELETED');
}
-
- return $result;
+ return $r;
}
/**
@@ -2197,7 +2166,7 @@ class rcube_imap_generic
// create array with header field:data
if (!empty($headers)) {
$headers = explode("\n", trim($headers));
- foreach ($headers as $resln) {
+ foreach ($headers as $hid => $resln) {
if (ord($resln[0]) <= 32) {
$lines[$ln] .= (empty($lines[$ln]) ? '' : "\n") . trim($resln);
} else {
@@ -2205,7 +2174,7 @@ class rcube_imap_generic
}
}
- foreach ($lines as $str) {
+ while (list($lines_key, $str) = each($lines)) {
list($field, $string) = explode(':', $str, 2);
$field = strtolower($field);
@@ -2509,7 +2478,7 @@ class rcube_imap_generic
}
if ($binary) {
- // WARNING: Use $formatted argument with care, this may break binary data stream
+ // WARNING: Use $formatting argument with care, this may break binary data stream
$mode = -1;
}
@@ -2530,7 +2499,6 @@ class rcube_imap_generic
// handle one line response
if ($line[0] == '(' && substr($line, -1) == ')') {
// tokenize content inside brackets
- // the content can be e.g.: (UID 9844 BODY[2.4] NIL)
$tokens = $this->tokenizeResponse(preg_replace('/(^\(|\)$)/', '', $line));
for ($i=0; $i<count($tokens); $i+=2) {
@@ -2645,11 +2613,11 @@ class rcube_imap_generic
/**
* Handler for IMAP APPEND command
*
- * @param string $mailbox Mailbox name
- * @param string|array $message The message source string or array (of strings and file pointers)
- * @param array $flags Message flags
- * @param string $date Message internal date
- * @param bool $binary Enable BINARY append (RFC3516)
+ * @param string $mailbox Mailbox name
+ * @param string $message Message content
+ * @param array $flags Message flags
+ * @param string $date Message internal date
+ * @param bool $binary Enable BINARY append (RFC3516)
*
* @return string|bool On success APPENDUID response (if available) or True, False on failure
*/
@@ -2663,28 +2631,13 @@ class rcube_imap_generic
$binary = $binary && $this->getCapability('BINARY');
$literal_plus = !$binary && $this->prefs['literal+'];
- $len = 0;
- $msg = is_array($message) ? $message : array(&$message);
- $chunk_size = 512000;
-
- for ($i=0, $cnt=count($msg); $i<$cnt; $i++) {
- if (is_resource($msg[$i])) {
- $stat = fstat($msg[$i]);
- if ($stat === false) {
- return false;
- }
- $len += $stat['size'];
- }
- else {
- if (!$binary) {
- $msg[$i] = str_replace("\r", '', $msg[$i]);
- $msg[$i] = str_replace("\n", "\r\n", $msg[$i]);
- }
- $len += strlen($msg[$i]);
- }
+ if (!$binary) {
+ $message = str_replace("\r", '', $message);
+ $message = str_replace("\n", "\r\n", $message);
}
+ $len = strlen($message);
if (!$len) {
return false;
}
@@ -2709,32 +2662,7 @@ class rcube_imap_generic
}
}
- foreach ($msg as $msg_part) {
- // file pointer
- if (is_resource($msg_part)) {
- rewind($msg_part);
- while (!feof($msg_part) && $this->fp) {
- $buffer = fread($msg_part, $chunk_size);
- $this->putLine($buffer, false);
- }
- fclose($msg_part);
- }
- // string
- else {
- $size = strlen($msg_part);
-
- // Break up the data by sending one chunk (up to 512k) at a time.
- // This approach reduces our peak memory usage
- for ($offset = 0; $offset < $size; $offset += $chunk_size) {
- $chunk = substr($msg_part, $offset, $chunk_size);
- if (!$this->putLine($chunk, false)) {
- return false;
- }
- }
- }
- }
-
- if (!$this->putLine('')) { // \r\n
+ if (!$this->putLine($message)) {
return false;
}
@@ -2773,23 +2701,94 @@ class rcube_imap_generic
*/
function appendFromFile($mailbox, $path, $headers=null, $flags = array(), $date = null, $binary = false)
{
+ unset($this->data['APPENDUID']);
+
+ if ($mailbox === null || $mailbox === '') {
+ return false;
+ }
+
// open message file
+ $in_fp = false;
if (file_exists(realpath($path))) {
- $fp = fopen($path, 'r');
+ $in_fp = fopen($path, 'r');
}
- if (!$fp) {
+ if (!$in_fp) {
$this->setError(self::ERROR_UNKNOWN, "Couldn't open $path for reading");
return false;
}
- $message = array();
+ $body_separator = "\r\n\r\n";
+ $len = filesize($path);
+
+ if (!$len) {
+ return false;
+ }
+
if ($headers) {
- $message[] = trim($headers, "\r\n") . "\r\n\r\n";
+ $headers = preg_replace('/[\r\n]+$/', '', $headers);
+ $len += strlen($headers) + strlen($body_separator);
+ }
+
+ $binary = $binary && $this->getCapability('BINARY');
+ $literal_plus = !$binary && $this->prefs['literal+'];
+
+ // build APPEND command
+ $key = $this->nextTag();
+ $request = "$key APPEND " . $this->escape($mailbox) . ' (' . $this->flagsToStr($flags) . ')';
+ if (!empty($date)) {
+ $request .= ' ' . $this->escape($date);
}
- $message[] = $fp;
+ $request .= ' ' . ($binary ? '~' : '') . '{' . $len . ($literal_plus ? '+' : '') . '}';
+
+ // send APPEND command
+ if ($this->putLine($request)) {
+ // Don't wait when LITERAL+ is supported
+ if (!$literal_plus) {
+ $line = $this->readReply();
- return $this->append($mailbox, $message, $flags, $date, $binary);
+ if ($line[0] != '+') {
+ $this->parseResult($line, 'APPEND: ');
+ return false;
+ }
+ }
+
+ // send headers with body separator
+ if ($headers) {
+ $this->putLine($headers . $body_separator, false);
+ }
+
+ // send file
+ while (!feof($in_fp) && $this->fp) {
+ $buffer = fgets($in_fp, 4096);
+ $this->putLine($buffer, false);
+ }
+ fclose($in_fp);
+
+ if (!$this->putLine('')) { // \r\n
+ return false;
+ }
+
+ // read response
+ do {
+ $line = $this->readLine();
+ } while (!$this->startsWith($line, $key, true, true));
+
+ // Clear internal status cache
+ unset($this->data['STATUS:'.$mailbox]);
+
+ if ($this->parseResult($line, 'APPEND: ') != self::ERROR_OK)
+ return false;
+ else if (!empty($this->data['APPENDUID']))
+ return $this->data['APPENDUID'];
+ else
+ return true;
+ }
+ else {
+ $this->setError(self::ERROR_COMMAND, "Unable to send command: $request");
+ }
+
+ return false;
}
/**
@@ -3538,7 +3537,7 @@ class rcube_imap_generic
if (is_array($element)) {
reset($element);
- foreach ($element as $value) {
+ while (list($key, $value) = each($element)) {
$string .= ' ' . self::r_implode($value);
}
}
@@ -3566,7 +3565,7 @@ class rcube_imap_generic
// if less than 255 bytes long, let's not bother
if (!$force && strlen($messages)<255) {
return $messages;
- }
+ }
// see if it's already been compressed
if (strpos($messages, ':') !== false) {
@@ -3674,20 +3673,8 @@ class rcube_imap_generic
*/
static function strToTime($date)
{
- // Clean malformed data
- $date = preg_replace(
- array(
- '/GMT\s*([+-][0-9]+)/', // support non-standard "GMTXXXX" literal
- '/[^a-z0-9\x20\x09:+-]/i', // remove any invalid characters
- '/\s*(Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s*/i', // remove weekday names
- ),
- array(
- '\\1',
- '',
- '',
- ), $date);
-
- $date = trim($date);
+ // support non-standard "GMTXXXX" literal
+ $date = preg_replace('/GMT\s*([+-][0-9]+)/', '\\1', $date);
// if date parsing fails, we have a date in non-rfc format
// remove token from the end and try again
@@ -3712,10 +3699,6 @@ class rcube_imap_generic
$this->capability = explode(' ', strtoupper($str));
- if (!empty($this->prefs['disabled_caps'])) {
- $this->capability = array_diff($this->capability, $this->prefs['disabled_caps']);
- }
-
if (!isset($this->prefs['literal+']) && in_array('LITERAL+', $this->capability)) {
$this->prefs['literal+'] = true;
}
@@ -3761,10 +3744,9 @@ class rcube_imap_generic
/**
* Set the value of the debugging flag.
*
- * @param boolean $debug New value for the debugging flag.
- * @param callback $handler Logging handler function
+ * @param boolean $debug New value for the debugging flag.
*
- * @since 0.5-stable
+ * @since 0.5-stable
*/
function setDebug($debug, $handler = null)
{
@@ -3775,18 +3757,12 @@ class rcube_imap_generic
/**
* Write the given debug text to the current debug output handler.
*
- * @param string $message Debug mesage text.
+ * @param string $message Debug mesage text.
*
- * @since 0.5-stable
+ * @since 0.5-stable
*/
private function debug($message)
{
- if (($len = strlen($message)) > self::DEBUG_LINE_LENGTH) {
- $diff = $len - self::DEBUG_LINE_LENGTH;
- $message = substr($message, 0, self::DEBUG_LINE_LENGTH)
- . "... [truncated $diff bytes]";
- }
-
if ($this->resourceid) {
$message = sprintf('[%s] %s', $this->resourceid, $message);
}