alecpl
2011-08-09 6d0ada30d7847a509db10d819020ac653597d073
- Fix handling of email addresses with quoted local part (#1487939)


6 files modified
41 ■■■■■ changed files
CHANGELOG 1 ●●●● patch | view | raw | blame | history
program/include/rcube_imap.php 7 ●●●● patch | view | raw | blame | history
program/include/rcube_smtp.php 5 ●●●●● patch | view | raw | blame | history
program/lib/Mail/mimePart.php 10 ●●●● patch | view | raw | blame | history
program/steps/mail/sendmail.inc 13 ●●●●● patch | view | raw | blame | history
tests/maildecode.php 5 ●●●● patch | view | raw | blame | history
CHANGELOG
@@ -1,6 +1,7 @@
CHANGELOG Roundcube Webmail
===========================
- Fix handling of email addresses with quoted local part (#1487939)
- Fix EOL character in vCard exports (#1487873)
- Added optional "multithreading" autocomplete feature
- Plugin API: Added 'config_get' hook
program/include/rcube_imap.php
@@ -4760,12 +4760,15 @@
        $str = self::explode_header_string(',;', $str, true);
        $result = array();
        // simplified regexp, supporting quoted local part
        $email_rx = '(\S+|("\s*(?:[^"\f\n\r\t\v\b\s]+\s*)+"))@\S+';
        foreach ($str as $key => $val) {
            $name    = '';
            $address = '';
            $val     = trim($val);
            if (preg_match('/(.*)<(\S+@\S+)>$/', $val, $m)) {
            if (preg_match('/(.*)<('.$email_rx.')>$/', $val, $m)) {
                $address = $m[2];
                $name    = trim($m[1]);
            }
@@ -4779,7 +4782,7 @@
            // dequote and/or decode name
            if ($name) {
                if ($name[0] == '"') {
                if ($name[0] == '"' && $name[strlen($name)-1] == '"') {
                    $name = substr($name, 1, -1);
                    $name = stripslashes($name);
                }
program/include/rcube_smtp.php
@@ -439,14 +439,14 @@
    // if we're passed an array, assume addresses are valid and implode them before parsing.
    if (is_array($recipients))
      $recipients = implode(', ', $recipients);
    $addresses = array();
    $recipients = rcube_explode_quoted_string(',', $recipients);
    reset($recipients);
    while (list($k, $recipient) = each($recipients))
    {
      $a = explode(" ", $recipient);
      $a = rcube_explode_quoted_string(' ', $recipient);
      while (list($k2, $word) = each($a))
      {
        if (strpos($word, "@") > 0 && $word[strlen($word)-1] != '"')
@@ -457,6 +457,7 @@
        }
      }
    }
    return $addresses;
  }
program/lib/Mail/mimePart.php
@@ -131,6 +131,7 @@
    */
    var $_eol = "\r\n";
    /**
    * Constructor.
    *
@@ -800,6 +801,9 @@
        // Structured header (make sure addr-spec inside is not encoded)
        if (!empty($separator)) {
            // Simple e-mail address regexp
            $email_regexp = '(\S+|("\s*(?:[^"\f\n\r\t\v\b\s]+\s*)+"))@\S+';
            $parts = Mail_mimePart::_explodeQuotedString($separator, $value);
            $value = '';
@@ -817,12 +821,12 @@
                }
                // let's find phrase (name) and/or addr-spec
                if (preg_match('/^<\S+@\S+>$/', $part)) {
                if (preg_match('/^<' . $email_regexp . '>$/', $part)) {
                    $value .= $part;
                } else if (preg_match('/^\S+@\S+$/', $part)) {
                } else if (preg_match('/^' . $email_regexp . '$/', $part)) {
                    // address without brackets and without name
                    $value .= $part;
                } else if (preg_match('/<*\S+@\S+>*$/', $part, $matches)) {
                } else if (preg_match('/<*' . $email_regexp . '>*$/', $part, $matches)) {
                    // address with name (handle name)
                    $address = $matches[0];
                    $word = str_replace($address, '', $part);
program/steps/mail/sendmail.inc
@@ -143,7 +143,10 @@
{
  global $EMAIL_FORMAT_ERROR, $RECIPIENT_COUNT;
  $regexp = array('/[,;]\s*[\r\n]+/', '/[\r\n]+/', '/[,;]\s*$/m', '/;/', '/(\S{1})(<\S+@\S+>)/U');
  // simplified email regexp, supporting quoted local part
  $email_regexp = '(\S+|("\s*(?:[^"\f\n\r\t\v\b\s]+\s*)+"))@\S+';
  $regexp  = array('/[,;]\s*[\r\n]+/', '/[\r\n]+/', '/[,;]\s*$/m', '/;/', '/(\S{1})(<'.$email_regexp.'>)/U');
  $replace = array(', ', ', ', '', ',', '\\1 \\2');
  // replace new lines and strip ending ', ', make address input more valid
@@ -155,15 +158,15 @@
  foreach($items as $item) {
    $item = trim($item);
    // address in brackets without name (do nothing)
    if (preg_match('/^<\S+@\S+>$/', $item)) {
    if (preg_match('/^<'.$email_regexp.'>$/', $item)) {
      $item = rcube_idn_to_ascii($item);
      $result[] = $item;
    // address without brackets and without name (add brackets)
    } else if (preg_match('/^\S+@\S+$/', $item)) {
    } else if (preg_match('/^'.$email_regexp.'$/', $item)) {
      $item = rcube_idn_to_ascii($item);
      $result[] = '<'.$item.'>';
    // address with name (handle name)
    } else if (preg_match('/\S+@\S+>*$/', $item, $matches)) {
    } else if (preg_match('/'.$email_regexp.'>*$/', $item, $matches)) {
      $address = $matches[0];
      $name = str_replace($address, '', $item);
      $name = trim($name);
@@ -172,7 +175,7 @@
            $name = '"'.addcslashes($name, '"').'"';
      }
      $address = rcube_idn_to_ascii($address);
      if (!preg_match('/^<\S+@\S+>$/', $address))
      if (!preg_match('/^<'.$email_regexp.'>$/', $address))
        $address = '<'.$address.'>';
      $result[] = $name.' '.$address;
tests/maildecode.php
@@ -44,6 +44,8 @@
        16 => 'Test Test ((comment)) <test@domain.tld>',
        17 => 'test@domain.tld (comment)',
        18 => '"Test,Test" <test@domain.tld>',
        // 1487939
        19 => 'Test <"test test"@domain.tld>',
    );
    $results = array(
@@ -66,6 +68,7 @@
        16 => array(1, 'Test Test', 'test@domain.tld'),
        17 => array(1, '', 'test@domain.tld'),
        18 => array(1, 'Test,Test', 'test@domain.tld'),
        19 => array(1, 'Test', '"test test"@domain.tld'),
    );
    foreach ($headers as $idx => $header) {
@@ -73,7 +76,7 @@
      $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);
      $this->assertEqual($results[$idx][2], $res[1]['mailto'], "Email part decoding for header: " . $header);
    }
  }