summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG1
-rw-r--r--program/include/rcube_imap.php82
-rw-r--r--tests/maildecode.php45
3 files changed, 112 insertions, 16 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 5054fd969..2773dafe4 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,7 @@
CHANGELOG Roundcube Webmail
===========================
+- Fix handling of comments inside an email address spec. (#1487673)
- Fix randomly disappearing folders list in IE (#1487704)
- Fix list column add/removal in IE (#1487703)
- Fix login redirect issues (#1487686)
diff --git a/program/include/rcube_imap.php b/program/include/rcube_imap.php
index fa7998338..cab232da4 100644
--- a/program/include/rcube_imap.php
+++ b/program/include/rcube_imap.php
@@ -4696,10 +4696,13 @@ class rcube_imap
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);
@@ -4741,6 +4744,81 @@ class rcube_imap
/**
+ * 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
*/
diff --git a/tests/maildecode.php b/tests/maildecode.php
index cfd7eda2f..8d359a5b6 100644
--- a/tests/maildecode.php
+++ b/tests/maildecode.php
@@ -35,28 +35,45 @@ class rcube_test_maildecode extends UnitTestCase
8 => '"Test<Test" <test@domain.tld>',
9 => '=?ISO-8859-1?B?VGVzdAo=?= <test@domain.tld>',
10 => '=?ISO-8859-1?B?VGVzdAo=?=<test@domain.tld>', // #1487068
+ // comments in address (#1487673)
+ 11 => 'Test (comment) <test@domain.tld>',
+ 12 => '"Test" (comment) <test@domain.tld>',
+ 13 => '"Test (comment)" (comment) <test@domain.tld>',
+ 14 => '(comment) <test@domain.tld>',
+ 15 => 'Test <test@(comment)domain.tld>',
+ 16 => 'Test Test ((comment)) <test@domain.tld>',
+ 17 => 'test@domain.tld (comment)',
+ 18 => '"Test,Test" <test@domain.tld>',
);
$results = array(
- 0 => array('', 'test@domain.tld'),
- 1 => array('', 'test@domain.tld'),
- 2 => array('Test', 'test@domain.tld'),
- 3 => array('Test Test', 'test@domain.tld'),
- 4 => array('Test Test', 'test@domain.tld'),
- 5 => array('Test Test', 'test@domain.tld'),
- 6 => array('Test Test', 'test@domain.tld'),
- 7 => array('Test " Test', 'test@domain.tld'),
- 8 => array('Test<Test', 'test@domain.tld'),
- 9 => array('Test', 'test@domain.tld'),
- 10 => array('Test', 'test@domain.tld'),
+ 0 => array(1, '', 'test@domain.tld'),
+ 1 => array(1, '', 'test@domain.tld'),
+ 2 => array(1, 'Test', 'test@domain.tld'),
+ 3 => array(1, 'Test Test', 'test@domain.tld'),
+ 4 => array(1, 'Test Test', 'test@domain.tld'),
+ 5 => array(1, 'Test Test', 'test@domain.tld'),
+ 6 => array(1, 'Test Test', 'test@domain.tld'),
+ 7 => array(1, 'Test " Test', 'test@domain.tld'),
+ 8 => array(1, 'Test<Test', 'test@domain.tld'),
+ 9 => array(1, 'Test', 'test@domain.tld'),
+ 10 => array(1, 'Test', 'test@domain.tld'),
+ 11 => array(1, 'Test', 'test@domain.tld'),
+ 12 => array(1, 'Test', 'test@domain.tld'),
+ 13 => array(1, 'Test (comment)', 'test@domain.tld'),
+ 14 => array(1, '', 'test@domain.tld'),
+ 15 => array(1, 'Test', 'test@domain.tld'),
+ 16 => array(1, 'Test Test', 'test@domain.tld'),
+ 17 => array(1, '', 'test@domain.tld'),
+ 18 => array(1, 'Test,Test', 'test@domain.tld'),
);
foreach ($headers as $idx => $header) {
$res = $this->app->imap->decode_address_list($header);
- $this->assertEqual(1, count($res), "Rows number in result for header: " . $header);
- $this->assertEqual($results[$idx][0], $res[1]['name'], "Name part decoding for header: " . $header);
- $this->assertEqual($results[$idx][1], $res[1]['mailto'], "Name part decoding for header: " . $header);
+ $this->assertEqual($results[$idx][0], count($res), "Rows number in result for header: " . $header);
+ $this->assertEqual($results[$idx][1], $res[1]['name'], "Name part decoding for header: " . $header);
+ $this->assertEqual($results[$idx][2], $res[1]['mailto'], "Name part decoding for header: " . $header);
}
}