7bit と 8bit の狭間で

これは Postfix Advent Calendar 2014 の6日目の記事です。

その昔、電子メールは 7bit データでした。

日本語は ASCII の範囲におさまらないのですが、ISO-2022-JP*1にエンコードすることで 7bit になるので、日本語でメールする人たちはそのようにしてました。今でも日本語を扱うメールアプリのデフォルトのエンコーディングは ISO-2022-JP になってることが多いと思います。

ただしヘッダの From や To フィールドには規格上 ISO-2022-JP は書けないので、メールアドレスの表示名には日本語は使えませんでした。

余談ですが、メール本文の冒頭で自分の名前を名乗る日本の風習は From に日本語で名前が書けなかったためじゃないかと、個人的に妄想してます。

バイナリデータは uuencode 等でテキストに変換して、メール本文に貼り付けて送ったものでした。

その後 MIME という規格ができて、メールヘッダにも ASCII 以外の文字列を記述できたり、メールにファイルを添付することができるようになり、便利に使えるようになりました。*2

MIME では本文中の 8bit 文字を ISO-2022-JP のような 7bit に変換しなくてもそのまま記述できます。ただし、ヘッダに Content-Transfer-Encoding: 8bit の記述が必要です。*3

UTF-8 のテキストをそのまま埋め込んだメールデータは次のようになります。あいかわらずヘッダ中では 8bit 文字は書けないので、Subject はエンコーディングしています(この例では「テスト」)。

Subject: =?utf-8?b?44OG44K544OI?=
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit

本日は晴天なり

8bit メールは新しい規格なので(と言っても20年くらい前ですが)、相手がちゃんと処理できるかどうかわかりません。

SMTP の EHLO 命令に対する応答に 8BITMIME が含まれてれば対応しています。Postfix は対応しています。

EHLO hoge
250-x220
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-STARTTLS
250-ENHANCEDSTATUSCODES
250-8BITMIME              ★
250 DSN

Postfix が 8bit メールを配送する時に相手が対応していない場合はどのようになるのか試してみます。

master.cf を次のようにします。

smtp      inet  n       -       -       -       -       smtpd
 -o content_filter=smtp:localhost:10025
10025     inet  n       -       -       -       -       smtpd
 -o smtpd_discard_ehlo_keywords=8BITMIME

25番ポートでうけつけたメールを 10025番ポートに中継しますが、10025番ポートの Postfix は EHLO に 8BITMIME を返しません。

この状態で 25番ポートに先ほどのメールを送信すると、最終的に届いた内容は次のようになりました。

Subject: =?utf-8?b?44OG44K544OI?=
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable                         ★

=E6=9C=AC=E6=97=A5=E3=81=AF=E6=99=B4=E5=A4=A9=E3=81=AA=E3=82=8A     ★

10025番ポートで動いている MTA が 8BITMIME を返さなかったので、25番ポートの Postfix が本文部のエンコーディングを 8bit から quoted-printable に変換して送信しました。

テキストを添付した次のメールを送ると、

Subject: txt attached
Content-Type: multipart/mixed; boundary=123456789

--123456789
Content-Type: text/plain
Content-Transfer-Encoding: 7bit

ASCII BODY
--123456789
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit

本日は晴天なり
--123456789--

該当パートだけ quoted-printable になります。

Subject: txt attached
Content-Type: multipart/mixed; boundary=123456789

--123456789
Content-Type: text/plain
Content-Transfer-Encoding: 7bit

ASCII BODY
--123456789
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable                         ★

=E6=9C=AC=E6=97=A5=E3=81=AF=E6=99=B4=E5=A4=A9=E3=81=AA=E3=82=8A     ★
--123456789--

メールを添付した次のメールを送ると、

Subject: eml attached
Content-Type: multipart/mixed; boundary=123456789

--123456789
Content-Type: text/plain
Content-Transfer-Encoding: 7bit

ASCII BODY
--123456789
Content-Type: message/rfc822
Content-Transfer-Encoding: 8bit

Subject: =?utf-8?b?44OG44K544OI?=
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit

本日は晴天なり
--123456789--

添付パートの Content-Transport-Type と、添付されたメールの本文が変換されます。

Subject: eml attached
Content-Type: multipart/mixed; boundary=123456789

--123456789
Content-Type: text/plain
Content-Transfer-Encoding: 7bit

ASCII BODY
--123456789
Content-Type: message/rfc822
Content-Transfer-Encoding: 7bit                                       ★

Subject: =?utf-8?b?44OG44K544OI?=
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable                           ★

=E6=9C=AC=E6=97=A5=E3=81=AF=E6=99=B4=E5=A4=A9=E3=81=AA=E3=82=8A       ★
--123456789--

添付されたメールを丸ごと quoted-printable にしないのは、Content-Type: message/rfc822 に指定できる Content-Transfer-Encoding は 7bit, 8bit,binary のいずれかだと決められているためです。

このように Postfix は配送先が 8bit メールを受け取れるかどうかによってメールの中身を変換することがありますが、main.cf に disable_mime_output_conversion = yes を設定するとこの 8bit → quoted-printable 変換を行いません。 相手が EHLO に 8BITMIME を返さなくても無理やり 8bit のまま送りつけます。

また、メールを受け取るときに Content-Transfer-Encoding: 8bit がないのに 8bit 文字が入っていた時にエラーにすることもできます。mian.cf に strict_8bitmime_body = yes を設定します。

この場合、DATA 命令の応答として次のようなエラーを返します。

550 5.6.0 improper use of 8-bit data in message body

この時ログには次のように記録されます。

postfix/cleanup[5085]: 7F3813A6: reject: mime-error improper use of 8-bit data in message body: ????????????????????? from localhost[127.0.0.1]; from=<sender@example.com> to=<rcpt@example.com>

*1:昔は「いわゆるJISコード」とか言われてました。

*2:便利になった分複雑になり、SMTP(Simple Mail Transfer Protocol)のどこがシンプルやねん!と言いたくなることもしばしばですが…。

*3:8bit をそのまま書けると言ってもテキストである必要があります。SMTP は行指向なので、行の区切りがないバイナリデータはやはりそのまま書くことはできません。