PHPのmb_encode_mimeheaderの実装について本気出して考えてみた(大袈裟)

PHPのmb_send_mail(?)を使ってメールを送るとOutlook Expressだときちんと表示されるが、ThunderbirdBecky!gmailとかだとSubjectが中途半端に化けるんだとか。

とりあえずmb_encode_mimeheader*1が悪さしていることはわかった。

<?php
$str = 'この文字列はMB_ENCODE_MIMEHEADERされます。されるといいな。よーわからへんけどな。';
$str = mb_convert_encoding($str,'ISO-2022-JP','UTF8,ISO-2022-JP,EUCJP,SJIS');

print mb_encode_mimeheader($str);
?>

↑の実行結果は

=?ISO-2022-JP?B?GyRCJDMkTko4O3pOcyRPGyhCTUJfRU5DT0RFX01JTUVIRUFERVIbJEIk?=
 =?ISO-2022-JP?B?NSRsJF4kOSEjJDUkbCRrJEgkJCQkJEohIyRoITwkbyQrJGkkWCRzJDEk?=
 =?ISO-2022-JP?B?SSRKISMbKEI=?=

となる。

ちなみにThunderbirdで「この文字列はMB_ENCODE_MIMEHEADERされます。されるといいな。よーわからへんけどな。」とSubjectにして送ると

=?ISO-2022-JP?B?GyRCJDMkTko4O3pOcyRPGyhCTUJfRU5DT0RFX01JTUVIRUFE?=
 =?ISO-2022-JP?B?RVIbJEIkNSRsJF4kOSEjJDUkbCRrJEgkJCQkJEohIyRoITwkbyQrGyhC?=
 =?ISO-2022-JP?B?GyRCJGkkWCRzJDEkSSRKISMbKEI=?=

のようなエンコードのされ方になります。

余計な文字を抜いて両者を比較してみると。。。

GyRCJDMkTko4O3pOcyRPGyhCTUJfRU5DT0RFX01JTUVIRUFERVIbJEIkNSRsJF4kOSEjJDUkbCRrJEgkJCQkJEohIyRoITwkbyQrJGkkWCRzJDEkSSRKISMbKEI
GyRCJDMkTko4O3pOcyRPGyhCTUJfRU5DT0RFX01JTUVIRUFERVIbJEIkNSRsJF4kOSEjJDUkbCRrJEgkJCQkJEohIyRoITwkbyQrGyhCGyRCJGkkWCRzJDEkSSRKISMbKEI

上がmb_encode_mimeheader、下がThunderbird。同じ文字列を変換しているはずなのに。。。

それを

GyRCJDMkTko4O3pOcyRPGyhCTUJfRU5DT0RFX01JTUVIRUFERVIbJEIkNSRsJF4kOSEjJDUkbCRrJEgkJCQkJEohIyRoITwkbyQr        JGkkWCRzJDEkSSRKISMbKEI
GyRCJDMkTko4O3pOcyRPGyhCTUJfRU5DT0RFX01JTUVIRUFERVIbJEIkNSRsJF4kOSEjJDUkbCRrJEgkJCQkJEohIyRoITwkbyQrGyhCGyRCJGkkWCRzJDEkSSRKISMbKEI

としてみると、mb_encode_mimeheaderのものは中途半端に欠けてるデータがある。

その欠けてる「GyhCGyRC」をBase64デコード*2して、バイナリエディタで開いてみると。。。

1B 28 42 1B 24 42

これで気付く人は気付くかと思うのだが、とりあえず最初の3バイトをぐぐってみよう。
google:1B 28 42
なにやら文字コードネタが多いが、適当に「WikipediaISO-2022-JP-2004*3」でも見てもらえれば多分大丈夫だが、ISO-2022-JPエスケープシーケンスになっているのだ。

ISO-2022-JP(以下めんどいのでJISコード)って文字コードはASCIIな文字とマルチバイト文字の境目にエスケープシーケンスを入れることでASCIIな文字とマルチバイト文字を区別している仕様になっていたと思うのだが、RFC 2822の関係で1行を78文字に制限するからみで長い文字列を変換する時にぶった切るのだが、Thunderbirdの場合は

[ESC]マルチバイト文字[ESC]ASCII
[ESC]マルチバイト文字その二[ESC]
[ESC]マルチバイト文字その三[ESC]

という風にある程度の文字数で元の文字列を分割して、各行ごとにエスケープシーケンスを付与してMIME変換しているのだがmb_encode_mimeheaderの場合

[ESC]マルチバイト文字[ESC]ASCII
[ESC]マルチバイト文字その二
マルチバイト文字その三[ESC]

というようにぶった切ったあとにマルチバイト文字が続いていたらエスケープシーケンスを付与せずにMIME変換を行なっている。

Outlook Expressはその辺をうまく解決してくれるのだが、ThunderbirdとかBecky!とかgmailは解釈してくれないようだ。

もっともRFC2822の「2.2.3. Long Header Fields」を見ている限りでは

Subject: This is a test
   can be represented as:

Subject: This
 is a test

というのを見るとThunderbirdBecky!gmailの解釈で正しいような気がする。

というかこれはmb_encode_mimeheaderの怠慢(?)な気もするなぁ。
パッチ書け?('A`)そんなスキルと時間があったら書いてみてもいいかも。。。

#PHP-DEV MLにでも投稿してみるか?