From f66f5f02e30d9fc2d5c06eeefb4377b8ff36fce2 Mon Sep 17 00:00:00 2001
From: alecpl <alec@alec.pl>
Date: Fri, 08 Apr 2011 02:15:37 -0400
Subject: [PATCH] - Removed dependency on rcube_* functions, small improvements
---
program/include/rcube_imap.php | 578 +++++++++++++++++++++++++++++++++++++++------------------
1 files changed, 393 insertions(+), 185 deletions(-)
diff --git a/program/include/rcube_imap.php b/program/include/rcube_imap.php
index bf88c29..b998c22 100644
--- a/program/include/rcube_imap.php
+++ b/program/include/rcube_imap.php
@@ -5,7 +5,7 @@
| program/include/rcube_imap.php |
| |
| This file is part of the Roundcube Webmail client |
- | Copyright (C) 2005-2010, Roundcube Dev. - Switzerland |
+ | Copyright (C) 2005-2010, The Roundcube Dev Team |
| Licensed under the GNU GPL |
| |
| PURPOSE: |
@@ -100,6 +100,16 @@
'RETURN-PATH',
);
+ const UNKNOWN = 0;
+ const NOPERM = 1;
+ const READONLY = 2;
+ const TRYCREATE = 3;
+ const INUSE = 4;
+ const OVERQUOTA = 5;
+ const ALREADYEXISTS = 6;
+ const NONEXISTENT = 7;
+ const CONTACTADMIN = 8;
+
/**
* Object constructor
@@ -138,6 +148,18 @@
$this->options['port'] = $port;
+ if ($this->options['debug']) {
+ $this->conn->setDebug(true, array($this, 'debug_handler'));
+
+ $this->options['ident'] = array(
+ 'name' => 'Roundcube Webmail',
+ 'version' => RCMAIL_VERSION,
+ 'php' => PHP_VERSION,
+ 'os' => PHP_OS,
+ 'command' => $_SERVER['REQUEST_URI'],
+ );
+ }
+
$attempt = 0;
do {
$data = rcmail::get_instance()->plugins->exec_hook('imap_connect',
@@ -156,21 +178,20 @@
$this->ssl = $use_ssl;
if ($this->conn->connected()) {
- // print trace messages
- if ($this->conn->message && ($this->debug_level & 8)) {
- console($this->conn->message);
- }
// get namespace and delimiter
$this->set_env();
-
return true;
}
// write error log
else if ($this->conn->error) {
- if ($pass && $user)
+ if ($pass && $user) {
+ $message = sprintf("Login failed for %s from %s. %s",
+ $user, rcmail_remote_ip(), $this->conn->error);
+
raise_error(array('code' => 403, 'type' => 'imap',
'file' => __FILE__, 'line' => __LINE__,
- 'message' => $this->conn->error), true, false);
+ 'message' => $message), true, false);
+ }
}
return false;
@@ -198,11 +219,11 @@
*/
function reconnect()
{
- $this->closeConnection();
- $this->connect($this->host, $this->user, $this->pass, $this->port, $this->ssl);
+ $this->conn->closeConnection();
+ $connected = $this->connect($this->host, $this->user, $this->pass, $this->port, $this->ssl);
// issue SELECT command to restore connection status
- if ($this->mailbox)
+ if ($connected && strlen($this->mailbox))
$this->conn->select($this->mailbox);
}
@@ -214,7 +235,7 @@
*/
function get_error_code()
{
- return ($this->conn) ? $this->conn->errornum : 0;
+ return $this->conn->errornum;
}
@@ -225,7 +246,48 @@
*/
function get_error_str()
{
- return ($this->conn) ? $this->conn->error : '';
+ return $this->conn->error;
+ }
+
+
+ /**
+ * Returns code of last command response
+ *
+ * @return int Response code
+ */
+ function get_response_code()
+ {
+ switch ($this->conn->resultcode) {
+ case 'NOPERM':
+ return self::NOPERM;
+ case 'READ-ONLY':
+ return self::READONLY;
+ case 'TRYCREATE':
+ return self::TRYCREATE;
+ case 'INUSE':
+ return self::INUSE;
+ case 'OVERQUOTA':
+ return self::OVERQUOTA;
+ case 'ALREADYEXISTS':
+ return self::ALREADYEXISTS;
+ case 'NONEXISTENT':
+ return self::NONEXISTENT;
+ case 'CONTACTADMIN':
+ return self::CONTACTADMIN;
+ default:
+ return self::UNKNOWN;
+ }
+ }
+
+
+ /**
+ * Returns last command response
+ *
+ * @return string Response
+ */
+ function get_response_str()
+ {
+ return $this->conn->result;
}
@@ -300,9 +362,9 @@
* @param string $mailbox Mailbox/Folder name
* @access public
*/
- function select_mailbox($mailbox)
+ function select_mailbox($mailbox=null)
{
- $mailbox = $this->mod_mailbox($mailbox);
+ $mailbox = strlen($mailbox) ? $this->mod_mailbox($mailbox) : $this->mailbox;
$selected = $this->conn->select($mailbox);
@@ -417,12 +479,12 @@
{
$this->threading = false;
- if ($enable) {
- if ($this->get_capability('THREAD=REFS'))
+ if ($enable && ($caps = $this->get_capability('THREAD'))) {
+ if (in_array('REFS', $caps))
$this->threading = 'REFS';
- else if ($this->get_capability('THREAD=REFERENCES'))
+ else if (in_array('REFERENCES', $caps))
$this->threading = 'REFERENCES';
- else if ($this->get_capability('THREAD=ORDEREDSUBJECT'))
+ else if (in_array('ORDEREDSUBJECT', $caps))
$this->threading = 'ORDEREDSUBJECT';
}
@@ -493,56 +555,56 @@
$imap_shared = $config->get('imap_ns_shared');
$imap_delimiter = $config->get('imap_delimiter');
- if ($imap_delimiter) {
- $this->delimiter = $imap_delimiter;
- }
-
- if (!$this->conn)
+ if (!$this->conn->connected())
return;
$ns = $this->conn->getNamespace();
- // NAMESPACE supported
+ // Set namespaces (NAMESPACE supported)
if (is_array($ns)) {
$this->namespace = $ns;
-
- if (empty($this->delimiter))
- $this->delimiter = $ns['personal'][0][1];
- if (empty($this->delimiter))
- $this->delimiter = $this->conn->getHierarchyDelimiter();
- if (empty($this->delimiter))
- $this->delimiter = '/';
}
- // not supported, get namespace from config
- else if ($imap_personal !== null || $imap_shared !== null || $imap_other !== null) {
- if (empty($this->delimiter))
- $this->delimiter = $this->conn->getHierarchyDelimiter();
- if (empty($this->delimiter))
- $this->delimiter = '/';
-
+ else {
$this->namespace = array(
'personal' => NULL,
'other' => NULL,
'shared' => NULL,
);
+ }
- if ($imap_personal !== null) {
- foreach ((array)$imap_personal as $dir) {
- $this->namespace['personal'][] = array($dir, $this->delimiter);
+ if ($imap_delimiter) {
+ $this->delimiter = $imap_delimiter;
+ }
+ if (empty($this->delimiter)) {
+ $this->delimiter = $this->namespace['personal'][0][1];
+ }
+ if (empty($this->delimiter)) {
+ $this->delimiter = $this->conn->getHierarchyDelimiter();
+ }
+ if (empty($this->delimiter)) {
+ $this->delimiter = '/';
+ }
+
+ // Overwrite namespaces
+ if ($imap_personal !== null) {
+ $this->namespace['personal'] = NULL;
+ foreach ((array)$imap_personal as $dir) {
+ $this->namespace['personal'][] = array($dir, $this->delimiter);
+ }
+ }
+ if ($imap_other !== null) {
+ $this->namespace['other'] = NULL;
+ foreach ((array)$imap_other as $dir) {
+ if ($dir) {
+ $this->namespace['other'][] = array($dir, $this->delimiter);
}
}
- if ($imap_other !== null) {
- foreach ((array)$imap_other as $dir) {
- if ($dir) {
- $this->namespace['other'][] = array($dir, $this->delimiter);
- }
- }
- }
- if ($imap_shared !== null) {
- foreach ((array)$imap_shared as $dir) {
- if ($dir) {
- $this->namespace['shared'][] = array($dir, $this->delimiter);
- }
+ }
+ if ($imap_shared !== null) {
+ $this->namespace['shared'] = NULL;
+ foreach ((array)$imap_shared as $dir) {
+ if ($dir) {
+ $this->namespace['shared'][] = array($dir, $this->delimiter);
}
}
}
@@ -2000,7 +2062,7 @@
return false;
}
- $struct = &$this->_structure_part($structure);
+ $struct = &$this->_structure_part($structure, 0, '', $headers);
$struct->headers = get_object_vars($headers);
// don't trust given content-type
@@ -2132,6 +2194,11 @@
$struct->charset = $struct->ctype_parameters['charset'];
}
+ // #1487700: workaround for lack of charset in malformed structure
+ if (empty($struct->charset) && !empty($mime_headers) && $mime_headers->charset) {
+ $struct->charset = $mime_headers->charset;
+ }
+
// read content encoding
if (!empty($part[5]) && $part[5]!='NIL') {
$struct->encoding = strtolower($part[5]);
@@ -2180,7 +2247,11 @@
$mime_headers = $this->conn->fetchPartHeader(
$this->mailbox, $this->_msg_id, false, $struct->mime_id);
}
- $struct->headers = $this->_parse_headers($mime_headers) + $struct->headers;
+
+ if (is_string($mime_headers))
+ $struct->headers = $this->_parse_headers($mime_headers) + $struct->headers;
+ else if (is_object($mime_headers))
+ $struct->headers = get_object_vars($mime_headers) + $struct->headers;
// get real content-type of message/rfc822
if ($struct->mimetype == 'message/rfc822') {
@@ -2376,20 +2447,21 @@
// TODO: Add caching for message parts
- if (!$part) $part = 'TEXT';
+ if (!$part) {
+ $part = 'TEXT';
+ }
$body = $this->conn->handlePartBody($this->mailbox, $uid, true, $part,
$o_part->encoding, $print, $fp);
- if ($fp || $print)
+ if ($fp || $print) {
return true;
+ }
- // convert charset (if text or message part)
- if ($body && ($o_part->ctype_primary == 'text' || $o_part->ctype_primary == 'message')) {
- // assume default if no charset specified
- if (empty($o_part->charset) || strtolower($o_part->charset) == 'us-ascii')
- $o_part->charset = $this->default_charset;
-
+ // convert charset (if text or message part) and part's charset is specified
+ if ($body && $o_part->charset
+ && preg_match('/^(text|message)$/', $o_part->ctype_primary)
+ ) {
$body = rcube_charset_convert($body, $o_part->charset);
}
@@ -2582,7 +2654,6 @@
// send expunge command in order to have the moved message
// really deleted from the source mailbox
-$aa = rcube_timer();
if ($moved) {
$this->_expunge($from_mbox, false, $uids);
$this->_clear_messagecount($from_mbox);
@@ -2592,7 +2663,7 @@
else if ($config->get('delete_always', false) && $tbox == $config->get('trash_mbox')) {
$moved = $this->delete_message($uids, $fbox);
}
-rcube_print_time($aa);
+
if ($moved) {
// unset threads internal cache
unset($this->icache['threads']);
@@ -2774,6 +2845,18 @@
$a_uids = is_array($uids) ? join(',', $uids) : $uids;
else
$a_uids = NULL;
+
+ // force mailbox selection and check if mailbox is writeable
+ // to prevent a situation when CLOSE is executed on closed
+ // or EXPUNGE on read-only mailbox
+ $result = $this->conn->select($mailbox);
+ if (!$result) {
+ return false;
+ }
+ if (!$this->conn->data['READ-WRITE']) {
+ $this->conn->setError(rcube_imap_generic::ERROR_READONLY, "Mailbox is read-only");
+ return false;
+ }
// CLOSE(+SELECT) should be faster than EXPUNGE
if (empty($a_uids) || $a_uids == '1:*')
@@ -3019,7 +3102,7 @@
/**
* Get mailbox size (size of all messages in a mailbox)
*
- * @param string $name Mailbox name
+ * @param string $name Mailbox name
* @return int Mailbox size in bytes, False on error
*/
function get_mailbox_size($name)
@@ -3094,6 +3177,7 @@
*
* @param string $mbox_name Mailbox to rename
* @param string $new_name New mailbox name
+ *
* @return boolean True on success
*/
function rename_mailbox($mbox_name, $new_name)
@@ -3103,92 +3187,87 @@
// make absolute path
$mailbox = $this->mod_mailbox($mbox_name);
$abs_name = $this->mod_mailbox($new_name);
+ $delm = $this->get_hierarchy_delimiter();
- // check if mailbox is subscribed
- $a_subscribed = $this->_list_mailboxes();
- $subscribed = in_array($mailbox, $a_subscribed);
-
- // unsubscribe folder
- if ($subscribed)
- $this->conn->unsubscribe($mailbox);
+ // get list of subscribed folders
+ if ((strpos($mailbox, '%') === false) && (strpos($mailbox, '*') === false)) {
+ $a_subscribed = $this->_list_mailboxes('', $mbox_name . $delm . '*');
+ $subscribed = $this->mailbox_exists($mbox_name, true);
+ }
+ else {
+ $a_subscribed = $this->_list_mailboxes();
+ $subscribed = in_array($mailbox, $a_subscribed);
+ }
if (strlen($abs_name))
$result = $this->conn->renameFolder($mailbox, $abs_name);
if ($result) {
- $delm = $this->get_hierarchy_delimiter();
+ // unsubscribe the old folder, subscribe the new one
+ if ($subscribed) {
+ $this->conn->unsubscribe($mailbox);
+ $this->conn->subscribe($abs_name);
+ }
// check if mailbox children are subscribed
- foreach ($a_subscribed as $c_subscribed)
+ foreach ($a_subscribed as $c_subscribed) {
if (preg_match('/^'.preg_quote($mailbox.$delm, '/').'/', $c_subscribed)) {
$this->conn->unsubscribe($c_subscribed);
$this->conn->subscribe(preg_replace('/^'.preg_quote($mailbox, '/').'/',
$abs_name, $c_subscribed));
}
+ }
// clear cache
$this->clear_message_cache($mailbox.'.msg');
$this->clear_cache('mailboxes');
}
- // try to subscribe it
- if ($result && $subscribed)
- $this->conn->subscribe($abs_name);
-
return $result;
}
/**
- * Remove mailboxes from server
+ * Remove mailbox from server
*
- * @param string|array $mbox_name Mailbox name(s) string/array
+ * @param string $mbox_name Mailbox name
+ *
* @return boolean True on success
*/
function delete_mailbox($mbox_name)
{
- $deleted = false;
+ $result = false;
+ $mailbox = $this->mod_mailbox($mbox_name);
+ $delm = $this->get_hierarchy_delimiter();
- if (is_array($mbox_name))
- $a_mboxes = $mbox_name;
- else if (is_string($mbox_name) && strlen($mbox_name))
- $a_mboxes = explode(',', $mbox_name);
+ // get list of folders
+ if ((strpos($mailbox, '%') === false) && (strpos($mailbox, '*') === false))
+ $sub_mboxes = $this->list_unsubscribed('', $mailbox . $delm . '*');
+ else
+ $sub_mboxes = $this->list_unsubscribed();
- if (is_array($a_mboxes)) {
- $delimiter = $this->get_hierarchy_delimiter();
-
- foreach ($a_mboxes as $mbox_name) {
- $mailbox = $this->mod_mailbox($mbox_name);
- $sub_mboxes = $this->conn->listMailboxes('', $mbox_name . $delimiter . '*');
+ // send delete command to server
+ $result = $this->conn->deleteFolder($mailbox);
- // unsubscribe mailbox before deleting
- $this->conn->unsubscribe($mailbox);
+ if ($result) {
+ // unsubscribe mailbox
+ $this->conn->unsubscribe($mailbox);
- // send delete command to server
- $result = $this->conn->deleteFolder($mailbox);
- if ($result) {
- $deleted = true;
- $this->clear_message_cache($mailbox.'.msg');
- }
-
- foreach ($sub_mboxes as $c_mbox) {
- if ($c_mbox != 'INBOX') {
- $this->conn->unsubscribe($c_mbox);
- $result = $this->conn->deleteFolder($c_mbox);
- if ($result) {
- $deleted = true;
- $this->clear_message_cache($c_mbox.'.msg');
- }
+ foreach ($sub_mboxes as $c_mbox) {
+ if (preg_match('/^'.preg_quote($mailbox.$delm, '/').'/', $c_mbox)) {
+ $this->conn->unsubscribe($c_mbox);
+ if ($this->conn->deleteFolder($c_mbox)) {
+ $this->clear_message_cache($c_mbox.'.msg');
}
}
}
+
+ // clear mailbox-related cache
+ $this->clear_message_cache($mailbox.'.msg');
+ $this->clear_cache('mailboxes');
}
- // clear mailboxlist cache
- if ($deleted)
- $this->clear_cache('mailboxes');
-
- return $deleted;
+ return $result;
}
@@ -3242,6 +3321,38 @@
/**
+ * Returns the namespace where the folder is in
+ *
+ * @param string $mbox_name Folder name
+ *
+ * @return string One of 'personal', 'other' or 'shared'
+ * @access public
+ */
+ function mailbox_namespace($mbox_name)
+ {
+ if ($mbox_name == 'INBOX') {
+ return 'personal';
+ }
+
+ foreach ($this->namespace as $type => $namespace) {
+ if (is_array($namespace)) {
+ foreach ($namespace as $ns) {
+ if (strlen($ns[0])) {
+ if ((strlen($ns[0])>1 && $mbox_name == substr($ns[0], 0, -1))
+ || strpos($mbox_name, $ns[0]) === 0
+ ) {
+ return $type;
+ }
+ }
+ }
+ }
+ }
+
+ return 'personal';
+ }
+
+
+ /**
* Modify folder name for input/output according to root dir and namespace
*
* @param string $mbox_name Folder name
@@ -3257,32 +3368,26 @@
// If folder contains namespace prefix, don't modify it
if (is_array($this->namespace['shared'])) {
foreach ($this->namespace['shared'] as $ns) {
- foreach ((array)$ns as $root) {
- if ($root[0] && strpos($mbox_name, $root[0]) === 0) {
- return $mbox_name;
- }
+ if ($ns[0] && strpos($mbox_name, $ns[0]) === 0) {
+ return $mbox_name;
}
}
}
if (is_array($this->namespace['other'])) {
foreach ($this->namespace['other'] as $ns) {
- foreach ((array)$ns as $root) {
- if ($root[0] && strpos($mbox_name, $root[0]) === 0) {
- return $mbox_name;
- }
+ if ($ns[0] && strpos($mbox_name, $ns[0]) === 0) {
+ return $mbox_name;
}
}
}
if (is_array($this->namespace['personal'])) {
foreach ($this->namespace['personal'] as $ns) {
- foreach ((array)$ns as $root) {
- if ($root[0] && strpos($mbox_name, $root[0]) === 0) {
- return $mbox_name;
- }
+ if ($ns[0] && strpos($mbox_name, $ns[0]) === 0) {
+ return $mbox_name;
}
}
// Add prefix if first personal namespace is non-empty
- if ($this->namespace['personal'][0][0]) {
+ if ($mbox_name != 'INBOX' && $this->namespace['personal'][0][0]) {
return $this->namespace['personal'][0][0].$mbox_name;
}
}
@@ -4271,80 +4376,95 @@
*/
public static function decode_mime_string($input, $fallback=null)
{
- // Initialize variable
- $out = '';
+ if (!empty($fallback)) {
+ $default_charset = $fallback;
+ }
+ else {
+ $default_charset = rcmail::get_instance()->config->get('default_charset', 'ISO-8859-1');
+ }
- // Iterate instead of recursing, this way if there are too many values we don't have stack overflows
// rfc: all line breaks or other characters not found
// in the Base64 Alphabet must be ignored by decoding software
// delete all blanks between MIME-lines, differently we can
// receive unnecessary blanks and broken utf-8 symbols
$input = preg_replace("/\?=\s+=\?/", '?==?', $input);
- // Check if there is stuff to decode
- if (strpos($input, '=?') !== false) {
- // Loop through the string to decode all occurences of =? ?= into the variable $out
- while(($pos = strpos($input, '=?')) !== false) {
+ // encoded-word regexp
+ $re = '/=\?([^?]+)\?([BbQq])\?([^?\n]*)\?=/';
+
+ // Find all RFC2047's encoded words
+ if (preg_match_all($re, $input, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) {
+ // Initialize variables
+ $tmp = array();
+ $out = '';
+ $start = 0;
+
+ foreach ($matches as $idx => $m) {
+ $pos = $m[0][1];
+ $charset = $m[1][0];
+ $encoding = $m[2][0];
+ $text = $m[3][0];
+ $length = strlen($m[0][0]);
+
// Append everything that is before the text to be decoded
- $out .= substr($input, 0, $pos);
+ if ($start != $pos) {
+ $substr = substr($input, $start, $pos-$start);
+ $out .= rcube_charset_convert($substr, $default_charset);
+ $start = $pos;
+ }
+ $start += $length;
- // Get the location of the text to decode
- $end_cs_pos = strpos($input, "?", $pos+2);
- $end_en_pos = strpos($input, "?", $end_cs_pos+1);
- $end_pos = strpos($input, "?=", $end_en_pos+1);
+ // Per RFC2047, each string part "MUST represent an integral number
+ // of characters . A multi-octet character may not be split across
+ // adjacent encoded-words." However, some mailers break this, so we
+ // try to handle characters spanned across parts anyway by iterating
+ // through and aggregating sequential encoded parts with the same
+ // character set and encoding, then perform the decoding on the
+ // aggregation as a whole.
- // Extract the encoded string
- $encstr = substr($input, $pos+2, ($end_pos-$pos-2));
- // Extract the remaining string
- $input = substr($input, $end_pos+2);
+ $tmp[] = $text;
+ if ($next_match = $matches[$idx+1]) {
+ if ($next_match[0][1] == $start
+ && $next_match[1][0] == $charset
+ && $next_match[2][0] == $encoding
+ ) {
+ continue;
+ }
+ }
- // Decode the string fragement
- $out .= rcube_imap::_decode_mime_string_part($encstr);
+ $count = count($tmp);
+ $text = '';
+
+ // Decode and join encoded-word's chunks
+ if ($encoding == 'B' || $encoding == 'b') {
+ // base64 must be decoded a segment at a time
+ for ($i=0; $i<$count; $i++)
+ $text .= base64_decode($tmp[$i]);
+ }
+ else { //if ($encoding == 'Q' || $encoding == 'q') {
+ // quoted printable can be combined and processed at once
+ for ($i=0; $i<$count; $i++)
+ $text .= $tmp[$i];
+
+ $text = str_replace('_', ' ', $text);
+ $text = quoted_printable_decode($text);
+ }
+
+ $out .= rcube_charset_convert($text, $charset);
+ $tmp = array();
}
- // Deocde the rest (if any)
- if (strlen($input) != 0)
- $out .= rcube_imap::decode_mime_string($input, $fallback);
+ // add the last part of the input string
+ if ($start != strlen($input)) {
+ $out .= rcube_charset_convert(substr($input, $start), $default_charset);
+ }
// return the results
return $out;
}
// no encoding information, use fallback
- return rcube_charset_convert($input,
- !empty($fallback) ? $fallback : rcmail::get_instance()->config->get('default_charset', 'ISO-8859-1'));
- }
-
-
- /**
- * Decode a part of a mime-encoded string
- *
- * @param string $str String to decode
- * @return string Decoded string
- * @access private
- */
- private function _decode_mime_string_part($str)
- {
- $a = explode('?', $str);
- $count = count($a);
-
- // should be in format "charset?encoding?base64_string"
- if ($count >= 3) {
- for ($i=2; $i<$count; $i++)
- $rest .= $a[$i];
-
- if (($a[1]=='B') || ($a[1]=='b'))
- $rest = base64_decode($rest);
- else if (($a[1]=='Q') || ($a[1]=='q')) {
- $rest = str_replace('_', ' ', $rest);
- $rest = quoted_printable_decode($rest);
- }
-
- return rcube_charset_convert($rest, $a[0]);
- }
-
- // we dont' know what to do with this
- return $str;
+ return rcube_charset_convert($input, $default_charset);
}
@@ -4636,10 +4756,13 @@
private function _parse_address_list($str, $decode=true)
{
// remove any newlines and carriage returns before
- $a = rcube_explode_quoted_string('[,;]', preg_replace( "/[\r\n]/", " ", $str));
+ $str = preg_replace('/\r?\n(\s|\t)?/', ' ', $str);
+
+ // extract list items, remove comments
+ $str = self::explode_header_string(',;', $str, true);
$result = array();
- foreach ($a as $key => $val) {
+ foreach ($str as $key => $val) {
$name = '';
$address = '';
$val = trim($val);
@@ -4679,6 +4802,91 @@
return $result;
}
+
+ /**
+ * Explodes header (e.g. address-list) string into array of strings
+ * using specified separator characters with proper handling
+ * of quoted-strings and comments (RFC2822)
+ *
+ * @param string $separator String containing separator characters
+ * @param string $str Header string
+ * @param bool $remove_comments Enable to remove comments
+ *
+ * @return array Header items
+ */
+ static function explode_header_string($separator, $str, $remove_comments=false)
+ {
+ $length = strlen($str);
+ $result = array();
+ $quoted = false;
+ $comment = 0;
+ $out = '';
+
+ for ($i=0; $i<$length; $i++) {
+ // we're inside a quoted string
+ if ($quoted) {
+ if ($str[$i] == '"') {
+ $quoted = false;
+ }
+ else if ($str[$i] == '\\') {
+ if ($comment <= 0) {
+ $out .= '\\';
+ }
+ $i++;
+ }
+ }
+ // we're inside a comment string
+ else if ($comment > 0) {
+ if ($str[$i] == ')') {
+ $comment--;
+ }
+ else if ($str[$i] == '(') {
+ $comment++;
+ }
+ else if ($str[$i] == '\\') {
+ $i++;
+ }
+ continue;
+ }
+ // separator, add to result array
+ else if (strpos($separator, $str[$i]) !== false) {
+ if ($out) {
+ $result[] = $out;
+ }
+ $out = '';
+ continue;
+ }
+ // start of quoted string
+ else if ($str[$i] == '"') {
+ $quoted = true;
+ }
+ // start of comment
+ else if ($remove_comments && $str[$i] == '(') {
+ $comment++;
+ }
+
+ if ($comment <= 0) {
+ $out .= $str[$i];
+ }
+ }
+
+ if ($out && $comment <= 0) {
+ $result[] = $out;
+ }
+
+ return $result;
+ }
+
+
+ /**
+ * This is our own debug handler for the IMAP connection
+ * @access public
+ */
+ public function debug_handler(&$imap, $message)
+ {
+ write_log('imap', $message);
+ }
+
} // end class rcube_imap
--
Gitblit v1.9.1