| | |
| | | <?php |
| | | |
| | | /** |
| | | /* |
| | | +-----------------------------------------------------------------------+ |
| | | | This file is part of the Roundcube Webmail client | |
| | | | Copyright (C) 2005-2012, The Roundcube Dev Team | |
| | |
| | | 'MDNSENT' => '$MDNSent', |
| | | '*' => '\\*', |
| | | ); |
| | | |
| | | public static $mupdate; |
| | | |
| | | protected $fp; |
| | | protected $host; |
| | |
| | | return false; |
| | | } |
| | | |
| | | if (!stream_socket_enable_crypto($this->fp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) { |
| | | if (isset($this->prefs['socket_options']['ssl']['crypto_method'])) { |
| | | $crypto_method = $this->prefs['socket_options']['ssl']['crypto_method']; |
| | | } |
| | | else { |
| | | // There is no flag to enable all TLS methods. Net_SMTP |
| | | // handles enabling TLS similarly. |
| | | $crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT |
| | | | @STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT |
| | | | @STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT; |
| | | } |
| | | |
| | | if (!stream_socket_enable_crypto($this->fp, true, $crypto_method)) { |
| | | $this->setError(self::ERROR_BAD, "Unable to negotiate TLS"); |
| | | $this->closeConnection(); |
| | | return false; |
| | |
| | | // folder name with spaces. Let's try to handle this situation |
| | | if (!is_array($items) && ($pos = strpos($response, '(')) !== false) { |
| | | $response = substr($response, $pos); |
| | | $items = $this->tokenizeResponse($response, 1); |
| | | $items = $this->tokenizeResponse($response, 1); |
| | | |
| | | if (!is_array($items)) { |
| | | return $result; |
| | | } |
| | |
| | | } |
| | | |
| | | // Clear internal status cache |
| | | unset($this->data['STATUS:'.$mailbox]); |
| | | $this->clear_status_cache($mailbox); |
| | | |
| | | if (!empty($messages) && $messages != '*' && $this->hasCapability('UIDPLUS')) { |
| | | $messages = self::compressMessageSet($messages); |
| | |
| | | * |
| | | * @return int Number of messages, False on error |
| | | */ |
| | | function countMessages($mailbox, $refresh = false) |
| | | function countMessages($mailbox) |
| | | { |
| | | if ($refresh) { |
| | | $this->selected = null; |
| | | } |
| | | |
| | | if ($this->selected === $mailbox) { |
| | | if ($this->selected === $mailbox && isset($this->data['EXISTS'])) { |
| | | return $this->data['EXISTS']; |
| | | } |
| | | |
| | |
| | | */ |
| | | function countRecent($mailbox) |
| | | { |
| | | if (!strlen($mailbox)) { |
| | | $mailbox = 'INBOX'; |
| | | if ($this->selected === $mailbox && isset($this->data['RECENT'])) { |
| | | return $this->data['RECENT']; |
| | | } |
| | | |
| | | $this->select($mailbox); |
| | | // Check internal cache |
| | | $cache = $this->data['STATUS:'.$mailbox]; |
| | | if (!empty($cache) && isset($cache['RECENT'])) { |
| | | return (int) $cache['RECENT']; |
| | | } |
| | | |
| | | if ($this->selected === $mailbox) { |
| | | return $this->data['RECENT']; |
| | | // Try STATUS (should be faster than SELECT) |
| | | $counts = $this->status($mailbox, array('RECENT')); |
| | | if (is_array($counts)) { |
| | | return (int) $counts['RECENT']; |
| | | } |
| | | |
| | | return false; |
| | |
| | | $encoding = $encoding ? trim($encoding) : 'US-ASCII'; |
| | | $algorithm = $algorithm ? trim($algorithm) : 'REFERENCES'; |
| | | $criteria = $criteria ? 'ALL '.trim($criteria) : 'ALL'; |
| | | $data = ''; |
| | | |
| | | list($code, $response) = $this->execute($return_uid ? 'UID THREAD' : 'THREAD', |
| | | array($algorithm, $encoding, $criteria)); |
| | |
| | | $flag = $this->flags[strtoupper($flag)]; |
| | | } |
| | | |
| | | if (!$flag || (!in_array($flag, (array) $this->data['PERMANENTFLAGS']) |
| | | && !in_array('\\*', (array) $this->data['PERMANENTFLAGS'])) |
| | | if (!$flag) { |
| | | return false; |
| | | } |
| | | |
| | | // if PERMANENTFLAGS is not specified all flags are allowed |
| | | if (!empty($this->data['PERMANENTFLAGS']) |
| | | && !in_array($flag, (array) $this->data['PERMANENTFLAGS']) |
| | | && !in_array('\\*', (array) $this->data['PERMANENTFLAGS']) |
| | | ) { |
| | | return false; |
| | | } |
| | |
| | | |
| | | // Clear internal status cache |
| | | unset($this->data['STATUS:'.$to]); |
| | | unset($this->data['STATUS:'.$from]); |
| | | $this->clear_status_cache($from); |
| | | |
| | | $result = $this->execute('UID MOVE', array( |
| | | $this->compressMessageSet($messages), $this->escape($to)), |
| | |
| | | return false; |
| | | } |
| | | |
| | | function sortHeaders($a, $field, $flag) |
| | | /** |
| | | * Sort messages by specified header field |
| | | * |
| | | * @param array $messages Array of rcube_message_header objects |
| | | * @param string $field Name of the property to sort by |
| | | * @param string $flag Sorting order (ASC|DESC) |
| | | * |
| | | * @return array Sorted input array |
| | | */ |
| | | public static function sortHeaders($messages, $field, $flag) |
| | | { |
| | | if (empty($field)) { |
| | | $field = 'uid'; |
| | |
| | | $field = strtolower($field); |
| | | } |
| | | |
| | | if ($field == 'date' || $field == 'internaldate') { |
| | | $field = 'timestamp'; |
| | | } |
| | | |
| | | if (empty($flag)) { |
| | | $flag = 'ASC'; |
| | | } else { |
| | | } |
| | | else { |
| | | $flag = strtoupper($flag); |
| | | } |
| | | |
| | | $c = count($a); |
| | | if ($c > 0) { |
| | | // Strategy: |
| | | // First, we'll create an "index" array. |
| | | // Then, we'll use sort() on that array, |
| | | // and use that to sort the main array. |
| | | // Strategy: First, we'll create an "index" array. |
| | | // Then, we'll use sort() on that array, and use that to sort the main array. |
| | | |
| | | // create "index" array |
| | | $index = array(); |
| | | reset($a); |
| | | while (list($key, $val) = each($a)) { |
| | | if ($field == 'timestamp') { |
| | | $data = $this->strToTime($val->date); |
| | | if (!$data) { |
| | | $data = $val->timestamp; |
| | | } |
| | | } else { |
| | | $data = $val->$field; |
| | | if (is_string($data)) { |
| | | $data = str_replace('"', '', $data); |
| | | if ($field == 'subject') { |
| | | $data = preg_replace('/^(Re: \s*|Fwd:\s*|Fw:\s*)+/i', '', $data); |
| | | } |
| | | $data = strtoupper($data); |
| | | } |
| | | $index = array(); |
| | | $result = array(); |
| | | |
| | | reset($messages); |
| | | |
| | | while (list($key, $headers) = each($messages)) { |
| | | $value = null; |
| | | |
| | | switch ($field) { |
| | | case 'arrival': |
| | | $field = 'internaldate'; |
| | | case 'date': |
| | | case 'internaldate': |
| | | case 'timestamp': |
| | | $value = self::strToTime($headers->$field); |
| | | if (!$value && $field != 'timestamp') { |
| | | $value = $headers->timestamp; |
| | | } |
| | | $index[$key] = $data; |
| | | |
| | | break; |
| | | |
| | | default: |
| | | // @TODO: decode header value, convert to UTF-8 |
| | | $value = $headers->$field; |
| | | if (is_string($value)) { |
| | | $value = str_replace('"', '', $value); |
| | | if ($field == 'subject') { |
| | | $value = preg_replace('/^(Re:\s*|Fwd:\s*|Fw:\s*)+/i', '', $value); |
| | | } |
| | | |
| | | $data = strtoupper($value); |
| | | } |
| | | } |
| | | |
| | | $index[$key] = $value; |
| | | } |
| | | |
| | | if (!empty($index)) { |
| | | // sort index |
| | | if ($flag == 'ASC') { |
| | | asort($index); |
| | | } else { |
| | | } |
| | | else { |
| | | arsort($index); |
| | | } |
| | | |
| | | // form new array based on index |
| | | $result = array(); |
| | | reset($index); |
| | | while (list($key, $val) = each($index)) { |
| | | $result[$key] = $a[$key]; |
| | | $result[$key] = $messages[$key]; |
| | | } |
| | | } |
| | | |
| | |
| | | return false; |
| | | } |
| | | |
| | | switch ($encoding) { |
| | | case 'base64': |
| | | $mode = 1; |
| | | break; |
| | | case 'quoted-printable': |
| | | $mode = 2; |
| | | break; |
| | | case 'x-uuencode': |
| | | case 'x-uue': |
| | | case 'uue': |
| | | case 'uuencode': |
| | | $mode = 3; |
| | | break; |
| | | default: |
| | | $mode = 0; |
| | | } |
| | | |
| | | // Use BINARY extension when possible (and safe) |
| | | $binary = $mode && preg_match('/^[0-9.]+$/', $part) && $this->hasCapability('BINARY'); |
| | | $fetch_mode = $binary ? 'BINARY' : 'BODY'; |
| | | $partial = $max_bytes ? sprintf('<0.%d>', $max_bytes) : ''; |
| | | |
| | | // format request |
| | | $key = $this->nextTag(); |
| | | $request = $key . ($is_uid ? ' UID' : '') . " FETCH $id ($fetch_mode.PEEK[$part]$partial)"; |
| | | $result = false; |
| | | $found = false; |
| | | |
| | | // send request |
| | | if (!$this->putLine($request)) { |
| | | $this->setError(self::ERROR_COMMAND, "Unable to send command: $request"); |
| | | return false; |
| | | } |
| | | |
| | | if ($binary) { |
| | | // WARNING: Use $formatted argument with care, this may break binary data stream |
| | | $mode = -1; |
| | | } |
| | | $binary = true; |
| | | |
| | | do { |
| | | if (!$initiated) { |
| | | switch ($encoding) { |
| | | case 'base64': |
| | | $mode = 1; |
| | | break; |
| | | case 'quoted-printable': |
| | | $mode = 2; |
| | | break; |
| | | case 'x-uuencode': |
| | | case 'x-uue': |
| | | case 'uue': |
| | | case 'uuencode': |
| | | $mode = 3; |
| | | break; |
| | | default: |
| | | $mode = 0; |
| | | } |
| | | |
| | | // Use BINARY extension when possible (and safe) |
| | | $binary = $binary && $mode && preg_match('/^[0-9.]+$/', $part) && $this->hasCapability('BINARY'); |
| | | $fetch_mode = $binary ? 'BINARY' : 'BODY'; |
| | | $partial = $max_bytes ? sprintf('<0.%d>', $max_bytes) : ''; |
| | | |
| | | // format request |
| | | $key = $this->nextTag(); |
| | | $request = $key . ($is_uid ? ' UID' : '') . " FETCH $id ($fetch_mode.PEEK[$part]$partial)"; |
| | | $result = false; |
| | | $found = false; |
| | | $initiated = true; |
| | | |
| | | // send request |
| | | if (!$this->putLine($request)) { |
| | | $this->setError(self::ERROR_COMMAND, "Unable to send command: $request"); |
| | | return false; |
| | | } |
| | | |
| | | if ($binary) { |
| | | // WARNING: Use $formatted argument with care, this may break binary data stream |
| | | $mode = -1; |
| | | } |
| | | } |
| | | |
| | | $line = trim($this->readLine(1024)); |
| | | |
| | | if (!$line) { |
| | | break; |
| | | } |
| | | |
| | | // handle UNKNOWN-CTE response - RFC 3516, try again with standard BODY request |
| | | if ($binary && !$found && preg_match('/^' . $key . ' NO \[UNKNOWN-CTE\]/i', $line)) { |
| | | $binary = $initiated = false; |
| | | continue; |
| | | } |
| | | |
| | | // skip irrelevant untagged responses (we have a result already) |
| | |
| | | |
| | | // BASE64 |
| | | if ($mode == 1) { |
| | | $line = rtrim($line, "\t\r\n\0\x0B"); |
| | | $line = preg_replace('|[^a-zA-Z0-9+=/]|', '', $line); |
| | | // create chunks with proper length for base64 decoding |
| | | $line = $prev.$line; |
| | | $length = strlen($line); |
| | |
| | | } |
| | | } |
| | | } |
| | | } while (!$this->startsWith($line, $key, true)); |
| | | } while (!$this->startsWith($line, $key, true) || !$initiated); |
| | | |
| | | if ($result !== false) { |
| | | if ($file) { |
| | |
| | | } |
| | | |
| | | 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)); |
| | |
| | | } |
| | | |
| | | /** |
| | | * Clear internal status cache |
| | | */ |
| | | protected function clear_status_cache($mailbox) |
| | | { |
| | | unset($this->data['STATUS:' . $mailbox]); |
| | | unset($this->data['EXISTS']); |
| | | unset($this->data['RECENT']); |
| | | unset($this->data['UNSEEN']); |
| | | } |
| | | |
| | | /** |
| | | * Converts flags array into string for inclusion in IMAP command |
| | | * |
| | | * @param array $flags Flags (see self::flags) |
| | |
| | | |
| | | if (!isset($this->prefs['literal+']) && in_array('LITERAL+', $this->capability)) { |
| | | $this->prefs['literal+'] = true; |
| | | } |
| | | |
| | | if (preg_match('/(\[| )MUPDATE=.*/', $str)) { |
| | | self::$mupdate = true; |
| | | } |
| | | |
| | | if ($trusted) { |