メールの符号化について
受信したメールをprocmail経由でphpに渡してゴニョゴニョする時に失敗した件のまとめ。
まずメール送信の仕組みから分かっていなかった。
初期のメールではASCIIの7bitデータのみが想定されていたため、現在でも添付のバイナリやマルチバイトである日本語は7bitに変換(MIMEエンコード)してやる必要があるわけ。MIMEエンコードには主にBase64とQuoted-printableの変換方法がある(Base64とQuoted-printableはともに可逆)。
で、今回失敗したのは、通常procmail経由でphpに渡ってくるメールは7bit(今回はQuoted-printable)で符号化されたデータなのに、日本語であることを前提としていたためメールの解析に失敗したというわけ。
ちなみにメールがどの方法でエンコードされているかはメールのソースを見れば分かる。日本語のメールは最初から日本語で送られてくるわけでは決してない!gmail等のメーラーがメールヘッダーの以下の部分を見てよしなに変換してくれていたってわけ!
Content-Transfer-Encoding: quoted-printable
ここには通常「7bit」が指定されることが多い。7bitだと一概にこれでエンコードされている!とは言えないぽい(少し調べてみたけどよく分からんかった)。
またphpのmb_send_mailとか使うと「7bit」で送信されてその際にmb_languageの結果に基づいてエンコードされるんだけど、languageがJapaneseの場合は、「ISO-2022-JP/Base64」の組み合わせでエンコードされるぽい。
で、今回の件をよくよく調べたところ、メールの本文はMail_mimeDecode::decodeで取得しており、このクラスはContent-Transfer-Encodingを考慮するため、問題が無いはずであることが分かった。
なのでMail_mimeDecodeを要調査。
調査結果
結果的に問題はなかった。phpの解析部分に問題があっただけでMail_mimeDecodeは正しくquoted-printableの本文をデコードしていた。
以降メモ。
Mail_mimeDecodeはヘッダのContent-Transfer-Encodingを元にbodyをデコードする(オプションのdecode_bodies=>trueとすること)。今までbashのmailコマンドでテストしていたんだけどmailだとContent-Transfer-Encodingが指定できない?ぽいのでphpのmail関数を使うこと。以下のような感じ。
<?php $subject = "サブジェクト"; $body = <<<EOF 本文 EOF; $to = "to"; $from = "from"; mb_language('japanese'); mb_internal_encoding('utf-8'); $subject = mb_encode_mimeheader($subject); $body = mb_convert_encoding($body,"Quoted-Printable","UTF-8"); $header = "From: ".$from."\n". "Mime-Version: 1.0\n". "Content-Type: text/plain; charset=utf-8\n". "Content-Transfer-Encoding: quoted-printable"; mail($to, $subject, $body, $header, "-f".$from);
参考サイト
エンコード・コレクション (メール、テキスト関連)
quoted-printable文字列の変換 - [サンプルコード/PHP] ぺんたん info
PHPでメールを送る – ぱんぴーまっしぐら