From 6c68cbde375cd425ed98c037e2fa964aca552744 Mon Sep 17 00:00:00 2001
From: alecpl <alec@alec.pl>
Date: Thu, 20 Jan 2011 02:39:26 -0500
Subject: [PATCH] - Fix handling of comments inside an email address spec. (#1487673)

---
 tests/maildecode.php           |   45 ++++++++++----
 CHANGELOG                      |    1 
 program/include/rcube_imap.php |   82 ++++++++++++++++++++++++++
 3 files changed, 112 insertions(+), 16 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 5054fd9..2773daf 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 fa79983..cab232d 100644
--- a/program/include/rcube_imap.php
+++ b/program/include/rcube_imap.php
@@ -4696,10 +4696,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);
@@ -4741,6 +4744,81 @@
 
 
     /**
+     * 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 cfd7eda..8d359a5 100644
--- a/tests/maildecode.php
+++ b/tests/maildecode.php
@@ -35,28 +35,45 @@
         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);
     }
   }
 

--
Gitblit v1.9.1