diff --git a/program/include/rcmail_sendmail.php b/program/include/rcmail_sendmail.php index e2ac45b9f..e6f0e3659 100644 --- a/program/include/rcmail_sendmail.php +++ b/program/include/rcmail_sendmail.php @@ -680,6 +680,15 @@ class rcmail_sendmail */ public function email_input_format($mailto, $count = false, $check = true) { + // convert to UTF-8 to preserve \x2c(,) and \x3b(;) used in ISO-2022-JP; + $charset = $this->options['charset']; + if ($charset != RCUBE_CHARSET) { + $mailto = rcube_charset::convert($mailto, $charset, RCUBE_CHARSET); + } + if (preg_match('/ISO-2022/i', $charset)) { + $use_base64 = true; + } + // simplified email regexp, supporting quoted local part $email_regexp = '(\S+|("[^"]+"))@\S+'; @@ -711,7 +720,16 @@ class rcmail_sendmail if ($name[0] == '"' && $name[strlen($name)-1] == '"') { $name = substr($name, 1, -1); } - $name = stripcslashes($name); + + // encode "name" field + if (!empty($use_base64)) { + $name = rcube_charset::convert($name, RCUBE_CHARSET, $charset); + $name = Mail_mimePart::encodeMB($name, $charset, 'base64'); + } + else { + $name = stripcslashes($name); + } + $address = rcube_utils::idn_to_ascii(trim($address, '<>')); $result[] = format_email_recipient($address, $name); $item = $address; diff --git a/program/lib/Roundcube/rcube_charset.php b/program/lib/Roundcube/rcube_charset.php index 6e59334c1..48341538b 100644 --- a/program/lib/Roundcube/rcube_charset.php +++ b/program/lib/Roundcube/rcube_charset.php @@ -63,6 +63,8 @@ class rcube_charset '238' => 'WINDOWS-1250', 'MS950' => 'CP950', 'WINDOWS949' => 'UHC', + 'WINDOWS1257' => 'ISO-8859-13', + 'ISO2022JP' => 'ISO-2022-JP-MS', ); /** @@ -270,7 +272,7 @@ class rcube_charset */ public static function convert($str, $from, $to = null) { - $to = empty($to) ? RCUBE_CHARSET : strtoupper($to); + $to = empty($to) ? RCUBE_CHARSET : self::parse_charset($to); $from = self::parse_charset($from); // It is a common case when UTF-16 charset is used with US-ASCII content (#1488654) @@ -283,15 +285,6 @@ class rcube_charset return $str; } - $aliases = array( - 'WINDOWS-1257' => 'ISO-8859-13', - 'US-ASCII' => 'ASCII', - 'ISO-2022-JP' => 'ISO-2022-JP-MS', - ); - - $mb_from = $aliases[$from] ?: $from; - $mb_to = $aliases[$to] ?: $to; - // Ignore invalid characters $mbstring_sc = mb_substitute_character(); mb_substitute_character('none'); @@ -300,7 +293,7 @@ class rcube_charset // using mb_check_encoding() is much slower set_error_handler(array('rcube_charset', 'error_handler'), E_WARNING); try { - $out = mb_convert_encoding($str, $mb_to, $mb_from); + $out = mb_convert_encoding($str, $to, $from); } catch (ErrorException $e) { $out = false; diff --git a/program/lib/Roundcube/rcube_mime.php b/program/lib/Roundcube/rcube_mime.php index e0204d545..b22407f3e 100644 --- a/program/lib/Roundcube/rcube_mime.php +++ b/program/lib/Roundcube/rcube_mime.php @@ -592,6 +592,7 @@ class rcube_mime // Iconv's substr/strlen are 100x slower (#1489113) if ($charset && $charset != RCUBE_CHARSET) { + $charset = rcube_charset::parse_charset($charset); mb_internal_encoding($charset); } diff --git a/tests/Framework/Charset.php b/tests/Framework/Charset.php index f984a6e30..da3bff890 100644 --- a/tests/Framework/Charset.php +++ b/tests/Framework/Charset.php @@ -65,11 +65,12 @@ class Framework_Charset extends PHPUnit\Framework\TestCase { return array( array('ö', 'ö', 'UTF-8', 'UTF-8'), - array('ö', '', 'UTF-8', 'US-ASCII'), + array('ö', '', 'UTF-8', 'ASCII'), array('aż', 'a', 'UTF-8', 'US-ASCII'), array('&BCAEMARBBEEESwQ7BDoEOA-', 'Рассылки', 'UTF7-IMAP', 'UTF-8'), array('Рассылки', '&BCAEMARBBEEESwQ7BDoEOA-', 'UTF-8', 'UTF7-IMAP'), array(base64_decode('GyRCLWo7M3l1OSk2SBsoQg=='), '㈱山﨑工業', 'ISO-2022-JP', 'UTF-8'), + array('㈱山﨑工業', base64_decode('GyRCLWo7M3l1OSk2SBsoQg=='), 'UTF-8', 'ISO-2022-JP'), ); } diff --git a/tests/Framework/Mime.php b/tests/Framework/Mime.php index 6768531fe..c15134395 100644 --- a/tests/Framework/Mime.php +++ b/tests/Framework/Mime.php @@ -238,6 +238,10 @@ class Framework_Mime extends PHPUnit\Framework\TestCase array("this-is-just-some-blabla-to-make-this-more-than-seventy-five-characters-in-a-row -- this line should be wrapped", 20, "\n"), "this-is-just-some-blabla-to-make-this-more-than-seventy-five-characters-in-a-row\n-- this line should\nbe wrapped", ), + array( + array(rcube_charset::convert("㈱山﨑工業", 'UTF-8', 'ISO-2022-JP'), 1, "\n", true, 'ISO-2022-JP'), + rcube_charset::convert("㈱\n山\n﨑\n工\n業", 'UTF-8', 'ISO-2022-JP'), + ), ); foreach ($samples as $sample) { diff --git a/tests/Rcmail/Sendmail.php b/tests/Rcmail/Sendmail.php index 9e99f70b4..a946f8902 100644 --- a/tests/Rcmail/Sendmail.php +++ b/tests/Rcmail/Sendmail.php @@ -7,6 +7,62 @@ */ class Rcmail_RcmailSendmail extends PHPUnit\Framework\TestCase { + + /** + * Data for test_convert() + */ + function data_email_input_format() + { + return array( + array( + 'name ', + 'name ', + 'UTF-8' + ), + array( + '"first last" ', + 'first last ', + 'UTF-8' + ), + array( + '"first last" , test2@domain.tld,', + 'first last , test2@domain.tld', + 'UTF-8' + ), + array( + '', + 'test@domain.tld', + 'UTF-8' + ), + array( + 'test@domain.tld', + 'test@domain.tld', + 'UTF-8' + ), + array( + 'ö ', + 'ö ', + 'UTF-8' + ), + array( + base64_decode('GyRCLWo7M3l1OSk2SBsoQg==') . ' ', + '=?ISO-2022-JP?B?GyRCLWo7M3l1OSk2SBsoQg==?= ', + 'ISO-2022-JP' + ), + ); + } + + /** + * @dataProvider data_email_input_format + */ + function test_email_input_format($input, $output, $charset) + { + $sendmail = new rcmail_sendmail(); + $sendmail->options['charset'] = $charset; + + $this->assertEquals($output, $sendmail->email_input_format($input)); + } + /** * Test rcmail_sendmail::identity_select() */