From f91f8533678c388b879d9e999a6bcc9e22ad7e19 Mon Sep 17 00:00:00 2001 From: Hugues Hiegel Date: Tue, 5 Aug 2014 16:44:07 +0200 Subject: lot of stuff --- program/lib/Roundcube/rcube_imap_generic.php | 293 ++++++++++++--------------- 1 file changed, 129 insertions(+), 164 deletions(-) (limited to 'program/lib/Roundcube/rcube_imap_generic.php') diff --git a/program/lib/Roundcube/rcube_imap_generic.php b/program/lib/Roundcube/rcube_imap_generic.php index f9a62f010..1b28c3bd7 100644 --- a/program/lib/Roundcube/rcube_imap_generic.php +++ b/program/lib/Roundcube/rcube_imap_generic.php @@ -48,8 +48,6 @@ class rcube_imap_generic '*' => '\\*', ); - public static $mupdate; - private $fp; private $host; private $logged = false; @@ -74,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 */ @@ -791,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 @@ -904,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']); @@ -1090,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 */ @@ -1109,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 @@ -1352,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; @@ -1585,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; @@ -1677,6 +1667,7 @@ class rcube_imap_generic } if (!empty($criteria)) { + $modseq = stripos($criteria, 'MODSEQ') !== false; $params .= ($params ? ' ' : '') . $criteria; } else { @@ -1815,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; } } @@ -2006,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 @@ -2024,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; } /** @@ -2199,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 { @@ -2207,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); @@ -2511,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; } @@ -2532,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; $igetCapability('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; } @@ -2711,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; } @@ -2775,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); + } + $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(); + + 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"); } - $message[] = $fp; - return $this->append($mailbox, $message, $flags, $date, $binary); + return false; } /** @@ -3158,11 +3155,6 @@ class rcube_imap_generic } foreach ($data as $entry) { - // Workaround cyrus-murder bug, the entry[2] string needs to be escaped - if (self::$mupdate) { - $entry[2] = addcslashes($entry[2], '\\"'); - } - // ANNOTATEMORE drafts before version 08 require quoted parameters $entries[] = sprintf('%s (%s %s)', $this->escape($entry[0], true), $this->escape($entry[1], true), $this->escape($entry[2], true)); @@ -3545,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); } } @@ -3573,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) { @@ -3681,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 @@ -3719,18 +3699,10 @@ 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; } - if (preg_match('/(\[| )MUPDATE=.*/', $str)) { - self::$mupdate = true; - } - if ($trusted) { $this->capability_readed = true; } @@ -3772,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) { @@ -3786,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); } -- cgit v1.2.3