Permalink
Browse files

merged branch ganchiku/fix-for-iso-2022-jp-without-dependency-maps (PR

…#199)

Commits
-------

8de4792 cleaned a bit
b4f6fc9 added japanese iso-2022-jp support. Still need to configure dependency_maps and charset

Discussion
----------

added japanese iso-2022-jp support.

Bug fix: yes
Feature addition: yes
Backwards compatibility break: no
Swiftmailer tests pass: yes
Fixes the following tickets: #122

Still need to fix dependency maps, and default charset, as I have on my branch as follows:
https://github.com/ganchiku/swiftmailer/tree/iso-2022-jp-with-dependency_maps

---------------------------------------------------------------------------

by fabpot at 2012-05-06T10:58:15Z

A test is broken after applying this PR:

    php test-suite/run.php Swift_Mime_HeaderEncoder_Base64HeaderEncoderAcceptanceTest
    Swift_Mime_HeaderEncoder_Base64HeaderEncoderAcceptanceTest
    1) Encoded string should decode back to original string for sample  at [/Users/fabien/work/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/HeaderEncoder/Base64HeaderEncoderAcceptanceTest.php line 35]
    	in testEncodingJIS
    FAILURES!!!
    Test cases run: 1/1, Passes: 0, Failures: 1, Exceptions: 0

---------------------------------------------------------------------------

by ganchiku at 2012-05-06T12:20:44Z

Well, the test passes in my environment.
Ubuntu10.04, PHP 5.3.2-1ubuntu4.14 with Suhosin-Patch (cli) (built: Feb 11 2012 06:50:46)

```text
$ php test-suite/run.php Swift_Mime_HeaderEncoder_Base64HeaderEncoderAcceptanceTest
Swift_Mime_HeaderEncoder_Base64HeaderEncoderAcceptanceTest
OK
Test cases run: 1/1, Passes: 1, Failures: 0, Exceptions: 0
```

The test is basically doing the same thing, and should pass.
```php
public function encodeString($string, $firstLineOffset = 0,
    $maxLineLength = 0, $charset = 'utf-8')
  {
      $charset = strtolower($charset);
    if ($charset == 'iso-2022-jp')
    {
        $old = mb_internal_encoding();
        mb_internal_encoding('utf-8');
        $newstring = mb_encode_mimeheader($string, $charset, $this->getName(), "\r\n");
        mb_internal_encoding($old);
        return $newstring;
    }
    return parent::encodeString($string, $firstLineOffset, $maxLineLength);
  }
```

```php
  public function testEncodingJIS()
  {
    if (function_exists('mb_convert_encoding'))
    {
      // base64_encode and split cannot handle long JIS text to fold
        $subject = "長い長い長い長い長い長い長い長い長い長い長い長い長い長い長い長い長い長い長い長い件名";

      $encodedWrapperLength = strlen('=?iso-2022-jp?' . $this->_encoder->getName() . '??=');

      $old = mb_internal_encoding();
      mb_internal_encoding('utf-8');
      $newstring = mb_encode_mimeheader($subject, 'iso-2022-jp', 'B', "\r\n");
       mb_internal_encoding($old);

      $encoded = $this->_encoder->encodeString($subject, 0, 75 - $encodedWrapperLength, 'iso-2022-jp');
      $this->assertEqual(
        $encoded, $newstring,
        'Encoded string should decode back to original string for sample '
      );
    }
  }
```

Or am I missing something?

---------------------------------------------------------------------------

by fabpot at 2012-05-07T06:23:01Z

Here is the values of `$encoded`:

    6ZW344GE6ZW344GE6ZW344GE6ZW344GE6ZW344GE6ZW344GE6ZW344GE6
    ZW344GE6ZW344GE6ZW344GE6ZW344GE6ZW344GE6ZW344GE6ZW344GE6Z
    W344GE6ZW344GE6ZW344GE6ZW344GE6ZW344GE6ZW344GE5Lu25ZCN

And the value of `$newstring`:

    =?ISO-2022-JP?B?GyRCRDkkJEQ5JCREOSQkRDkkJEQ5JCREOSQkRDkkJEQ5JCREOSQkGyhC?=
     =?ISO-2022-JP?B?GyRCRDkkJEQ5JCREOSQkRDkkJEQ5JCREOSQkRDkkJEQ5JCREOSQkGyhC?=
     =?ISO-2022-JP?B?GyRCRDkkJEQ5JCQ3b0w+GyhC?=

---------------------------------------------------------------------------

by xdecock at 2012-05-07T09:04:01Z

It's more a transcoding to utf-8 than adding charset support if i read
correctly.

On Sat, May 5, 2012 at 10:04 AM, Shin Ohno <
reply@reply.github.com
> wrote:

> Bug fix: yes
> Feature addition: yes
> Backwards compatibility break: no
> Swiftmailer tests pass: yes
> Fixes the following tickets: #122
>
> Still need to fix dependency maps, and default charset, as I have on my
> branch as follows:
>
> https://github.com/ganchiku/swiftmailer/tree/iso-2022-jp-with-dependency_maps
>
>
> You can merge this Pull Request by running:
>
>  git pull https://github.com/ganchiku/swiftmailerfix-for-iso-2022-jp-without-dependency-maps
>
> Or you can view, comment on it, or merge it online at:
>
>  #199
>
> -- Commit Summary --
>
> * added japanese iso-2022-jp support.
>
> -- File Changes --
>
> M lib/classes/Swift/Mime/HeaderEncoder/Base64HeaderEncoder.php (29)
> M lib/classes/Swift/Mime/HeaderEncoder/QpHeaderEncoder.php (2)
> M lib/classes/Swift/Mime/Headers/AbstractHeader.php (16)
> M lib/classes/Swift/Mime/Headers/ParameterizedHeader.php (2)
> M lib/classes/Swift/Mime/MimePart.php (29)
> A tests/_samples/charsets/iso-2022-jp/one.txt (11)
> A
> tests/acceptance/Swift/Mime/HeaderEncoder/Base64HeaderEncoderAcceptanceTest.php
> (39)
>
> -- Patch Links --
>
>  https://github.com/swiftmailer/swiftmailer/pull/199.patch
>  https://github.com/swiftmailer/swiftmailer/pull/199.diff
>
> ---
> Reply to this email directly or view it on GitHub:
> #199
>

--
Xavier De Cock
GPG Fingerprint: 93CA EE3F 9F57 5BE1 AE4A  794D 3C74 CA9E E7A5 0C1B
GPG Id: 0xE7A50C1B

---------------------------------------------------------------------------

by ganchiku at 2012-05-08T01:15:29Z

@fabpot , hmm, the $encoded is wrong.. Let me look into the code again.

@xdecock , well I thought so at first, but the you cannot split 75 chars to fold header string without checking each byte of the characters in the header, such as subject field.
The splitting fails because it cannot choose the right place to split. That is the reason why we are using mb_encode_mimeheader, or write the same function in native PHP, which was the one I PRed last time. But closed the one, and instead PRed using mb_encode_mimeheader.

---------------------------------------------------------------------------

by ganchiku at 2012-05-08T02:09:10Z

I cleaned the code a bit, but I don't think it is related the problem.

The $encoded characters,
```text
6ZW344GE6ZW344GE6ZW344GE6ZW344GE6ZW344GE6ZW344GE6ZW344GE6
ZW344GE6ZW344GE6ZW344GE6ZW344GE6ZW344GE6ZW344GE6ZW344GE6Z
W344GE6ZW344GE6ZW344GE6ZW344GE6ZW344GE6ZW344GE5Lu25ZCN
```
is utf-8 base64_encoded characters. And it seems the "if statement" below is returning false.

```php
    if (strtolower($charset) === 'iso-2022-jp')
```

I cleand that part to add strtolower, anyway.
As the test code,  the string, "iso-2022-jp" is passed as fourth parameter, "if statement" should pass.

---------------------------------------------------------------------------

by ttsuru at 2012-05-30T12:35:58Z

+1

---------------------------------------------------------------------------

by fabpot at 2012-06-15T19:42:21Z

I still have the same failing test. Can I do something to help you figure out the problem?
  • Loading branch information...
2 parents e8aaed4 + 8de4792 commit adade891a80ddb77d474d5e065769fffc3251364 @fabpot fabpot committed Jun 17, 2012
@@ -32,5 +32,30 @@ public function getName()
{
return 'B';
}
-
+
+
+ /**
+ * Takes an unencoded string and produces a Base64 encoded string from it.
+ * If the charset is iso-2022-jp, it uses mb_encode_mimeheader instead of
+ * default encodeString, otherwise pass to the parent method.
+ * @param string $string to encode
+ * @param int $firstLineOffset
+ * @param int $maxLineLength, optional, 0 indicates the default of 76 bytes
+ * @param string $charset
+ * @return string
+ */
+ public function encodeString($string, $firstLineOffset = 0,
+ $maxLineLength = 0, $charset = 'utf-8')
+ {
+ if (strtolower($charset) === 'iso-2022-jp')
+ {
+ $old = mb_internal_encoding();
+ mb_internal_encoding('utf-8');
+ $newstring = mb_encode_mimeheader($string, $charset, $this->getName(), "\r\n");
+ mb_internal_encoding($old);
+ return $newstring;
+ }
+ return parent::encodeString($string, $firstLineOffset, $maxLineLength);
+ }
+
}
@@ -60,7 +60,7 @@ public function getName()
* @return string
*/
public function encodeString($string, $firstLineOffset = 0,
- $maxLineLength = 0)
+ $maxLineLength = 0, $charst = 'utf-8')
{
return str_replace(array(' ', '=20', "=\r\n"), array('_', '_', "\r\n"),
parent::encodeString($string, $firstLineOffset, $maxLineLength)
@@ -377,15 +377,18 @@ protected function getTokenAsEncodedWord($token, $firstLineOffset = 0)
$encodedTextLines = explode("\r\n",
$this->_encoder->encodeString(
- $token, $firstLineOffset, 75 - $encodingWrapperLength
+ $token, $firstLineOffset, 75 - $encodingWrapperLength, $this->_charset
)
- );
-
- foreach ($encodedTextLines as $lineNum => $line)
+ );
+
+ if (strtolower($this->_charset) !== 'iso-2022-jp') // special encoding for iso-2022-jp using mb_encode_mimeheader
{
- $encodedTextLines[$lineNum] = '=?' . $charsetDecl .
- '?' . $this->_encoder->getName() .
- '?' . $line . '?=';
+ foreach ($encodedTextLines as $lineNum => $line)
+ {
+ $encodedTextLines[$lineNum] = '=?' . $charsetDecl .
+ '?' . $this->_encoder->getName() .
+ '?' . $line . '?=';
+ }
}
return implode("\r\n ", $encodedTextLines);
@@ -207,7 +207,7 @@ private function _createParameter($name, $value)
if (isset($this->_paramEncoder))
{
$value = $this->_paramEncoder->encodeString(
- $origValue, $firstLineOffset, $maxValueLength
+ $origValue, $firstLineOffset, $maxValueLength, $this->getCharset()
);
}
else //We have to go against RFC 2183/2231 in some areas for interoperability
@@ -62,11 +62,14 @@ public function __construct(Swift_Mime_HeaderSet $headers,
*/
public function setBody($body, $contentType = null, $charset = null)
{
- parent::setBody($body, $contentType);
if (isset($charset))
{
$this->setCharset($charset);
}
+ $body = $this->_convertString($body);
+
+ parent::setBody($body, $contentType);
+
return $this;
}
@@ -194,4 +197,28 @@ protected function _setNestingLevel($level)
$this->_nestingLevel = $level;
}
+ /** Encode charset when charset is not utf-8 */
+ protected function _convertString($string)
+ {
+ $charset = strtolower($this->getCharset());
+ if (!in_array($charset, array('utf-8', 'iso-8859-1', "")))
+ {
+ // mb_convert_encoding must be the first one to check, since iconv cannot convert some words.
+ if (function_exists('mb_convert_encoding'))
+ {
+ $string = mb_convert_encoding($string, $charset, 'utf-8');
+ }
+ else if (function_exists('iconv'))
+ {
+ $string = iconv($charset, 'utf-8//TRANSLIT//IGNORE', $string);
+ }
+ else
+ {
+ throw new Swift_SwiftException('No suitable convert encoding function (use UTF-8 as your harset or install the mbstring or iconv extension).');
+ }
+ return $string;
+ }
+ return $string;
+ }
+
}
@@ -0,0 +1,11 @@
+ISO-2022-JPは、インターネット上(特に電子メール)などで使われる日本の文字用の文字符号化方式。ISO/IEC 2022のエスケープシーケンスを利用して文字集合を切り替える7ビットのコードであることを特徴とする (アナウンス機能のエスケープシーケンスは省略される)。俗に「JISコード」と呼ばれることもある。
+
+概要
+日本語表記への利用が想定されている文字コードであり、日本語の利用されるネットワークにおいて、日本の規格を応用したものである。また文字集合としては、日本語で用いられる漢字、ひらがな、カタカナはもちろん、ラテン文字、ギリシア文字、キリル文字なども含んでおり、学術や産業の分野での利用も考慮たものとなっている。規格名に、ISOの日本語の言語コードであるjaではなく、国・地域名コードのJPが示されているゆえんである。
+文字集合としてJIS X 0201のC0集合(制御文字)、JIS X 0201のラテン文字集合、ISO 646の国際基準版図形文字、JIS X 0208の1978年版(JIS C 6226-1978)と1983年および1990年版が利用できる。JIS X 0201の片仮名文字集合は利用できない。1986年以降、日本の電子メールで用いられてきたJUNETコードを、村井純・Mark Crispin・Erik van der Poelが1993年にRFC化したもの(RFC 1468)。後にJIS X 0208:1997の附属書2としてJISに規定された。MIMEにおける文字符号化方式の識別用の名前として IANA に登録されている。
+なお、符号化の仕様についてはISO/IEC 2022#ISO-2022-JPも参照。
+
+ISO-2022-JPと非標準的拡張使用
+「JISコード」(または「ISO-2022-JP」)というコード名の規定下では、その仕様通りの使用が求められる。しかし、Windows OS上では、実際にはCP932コード (MicrosoftによるShift JISを拡張した亜種。ISO-2022-JP規定外文字が追加されている。)による独自拡張(の文字)を断りなく使うアプリケーションが多い。この例としてInternet ExplorerやOutlook Expressがある。また、EmEditor、秀丸エディタやThunderbirdのようなMicrosoft社以外のWindowsアプリケーションでも同様の場合がある。この場合、ISO-2022-JPの範囲外の文字を使ってしまうと、異なる製品間では未定義不明文字として認識されるか、もしくは文字化けを起こす原因となる。そのため、Windows用の電子メールクライアントであっても独自拡張の文字を使用すると警告を出したり、あえて使えないように制限しているものも存在する。さらにはISO-2022-JPの範囲内であってもCP932は非標準文字(FULLWIDTH TILDE等)を持つので文字化けの原因になり得る。
+また、符号化方式名をISO-2022-JPとしているのに、文字集合としてはJIS X 0212 (いわゆる補助漢字) やJIS X 0201の片仮名文字集合 (いわゆる半角カナ) をも符号化している例があるが、ISO-2022-JPではこれらの文字を許容していない。これらの符号化は独自拡張の実装であり、中にはISO/IEC 2022の仕様に準拠すらしていないものもある[2]。従って受信側の電子メールクライアントがこれらの独自拡張に対応していない場合、その文字あるいはその文字を含む行、時にはテキスト全体が文字化けすることがある。
+
@@ -0,0 +1,39 @@
+<?php
+
+require_once 'Swift/Mime/HeaderEncoder/Base64HeaderEncoder.php';
+require_once 'Swift/ByteStream/ArrayByteStream.php';
+
+class Swift_Mime_HeaderEncoder_Base64HeaderEncoderAcceptanceTest
+ extends UnitTestCase
+{
+
+ private $_encoder;
+
+ public function setUp()
+ {
+ $this->_encoder = new Swift_Mime_HeaderEncoder_Base64HeaderEncoder();
+ }
+
+ public function testEncodingJIS()
+ {
+ if (function_exists('mb_convert_encoding'))
+ {
+ // base64_encode and split cannot handle long JIS text to fold
+ $subject = "長い長い長い長い長い長い長い長い長い長い長い長い長い長い長い長い長い長い長い長い件名";
+
+ $encodedWrapperLength = strlen('=?iso-2022-jp?' . $this->_encoder->getName() . '??=');
+
+ $old = mb_internal_encoding();
+ mb_internal_encoding('utf-8');
+ $newstring = mb_encode_mimeheader($subject, 'iso-2022-jp', 'B', "\r\n");
+ mb_internal_encoding($old);
+
+ $encoded = $this->_encoder->encodeString($subject, 0, 75 - $encodedWrapperLength, 'iso-2022-jp');
+ $this->assertEqual(
+ $encoded, $newstring,
+ 'Encoded string should decode back to original string for sample '
+ );
+ }
+ }
+
+}

0 comments on commit adade89

Please sign in to comment.