メールヘッダ覚え書き


はてなのこの時間のメンテナンス率は異常。



全般的なこと

改行は CRLF
1 行は (CRLF を除いて) 998 文字を超えてはならず、 78文字を超えないようにすべき

2.2.1 Line Length Limits
Each line of characters MUST be no more than 998 characters, and SHOULD be no more than 78 characters, excluding the CRLF.
(RFC 2822 in April 2001)

http://www.ietf.org/rfc/rfc2822.txt

ただ、過去の RFC を見る限り、このへんは "ネチケット" くさい (今じゃこの言葉全然聞かないね) 。
文字数制限も、 UI を考慮して、って感じになってるし。
しかも RFC ごとに 64 文字だとか 75 文字だとか数字が違う。
というわけで、そんな厳格なもんだと考えなくてもいいようだ。
まあ適当に改行してればいい。 70 文字とかで。

ヘッダとボディ
空行で区切られてる。空行前までがヘッダ、後がボディ
ヘッダ全般
エスケープコードとか、そういう何か特殊っぽいのは使えない。
ヘッダ形式
長すぎる場合は、次の行の先頭に空白(もしくはタブ)を置いて続ける
         1         2         3         4         5         6         7         8
12345678901234567890123456789012345678901234567890123456789012345678901234567890
Subject: 000000000000000000000000000000000000000000000000000000000000000000000
 00000000000000000000000000000000000000000000000000000000000000000000000000000
 00000000000000000000000000000000000000000000000000000000000000000000000000000
	000000000000000000000000000000000000000000000000000000000000000000000000000
(最後の行の先頭はタブ)
エンコードしてた場合
Subject: =?ISO-2022-JP?B?000000000000000000000000000000000000000000000000000?=
 =?ISO-2022-JP?B?00000000000000000000000000000000000000000000000000000000000?=
 =?ISO-2022-JP?B?00000000000000000000000000000000000000000000000000000000000?=

ちなみに、行頭の空白の数とかは規定にないっぽい。元データの空白の前後で改行が入ったとき、どう連結されるのか不明くさい。
http://jp.php.net/manual/ja/function.mb-encode-mimeheader.php の注意にある

注意: この関数は、行を分割する際に特別な配慮(単語の区切りなど)を行いません。 このせいで、もとの文字列に予期せぬ空白が入ってしまう可能性があります。

http://jp.php.net/manual/ja/function.mb-encode-mimeheader.php

は、その辺りが原因かな。

件名

ASCII 以外なら =?文字エンコード名?エンコード名?データ部?= という形式でエンコードされている。
よくあるのが =?ISO-2022-JP?B?データ部?= のような形。これはデータ部のデータは、 ISO-2022-JP の文字データが Base64エンコードされているものだっていうこと。

ボディ

ヘッダの Content-Type Content-Transfer-Encoding にエンコード方式がある。
Content-Type: text/plain;charset=UTF-8
Content-Transfer-Encoding: 8bit
なら、ボディは UTF-8 そのまま載ってる。
Content-Type: text/plain;charset=UTF-8
Content-Transfer-Encoding: Base64
ならボディは Base64エンコードされた UTF-8 文字列
Content-Type: text/plain;charset=ISO-2022-JP
Content-Transfer-Encoding: 7bit
なら ISO-2022-JP そのまま。日本じゃこれが一番多い。

エンコードの意味

古い MTA は 7bit 文字しか扱えない。

7bit の日本語エンコーディングあるよ (ISO-2022-JP)

ボディはそれでもいいけど、ヘッダにはエスケープコードいれちゃいけないよ

ISO-2022-JP ってエスケープコード使ってんじゃん。仕方ねーからエンコードしなきゃな。

こうしてヘッダ (Subject や From 等) は Base64(ISO-2022-JP) 、ボディは ISO-2022-JP なメール習慣ができあがった。

UTF-8 で送れるの?

Subject: =?utf-8?B?データ?=
Content-Type: text/plain;charset=UTF-8
Content-Transfer-Encoding: 8bit

(UTF-8 そのまんま)

とかなら送れるようだ。実際そういうメールを受け取ったことがある。
でも、 MTA によっては、中継途中にボディのデータを 8bit > 7bit に加工するかもしれない。ようするにボディが壊れる。
特に必要ないなら避けるべき

Subject: =?utf-8?B?データ?=
Content-Type: text/plain;charset=UTF-8
Content-Transfer-Encoding: Base64

(Base64エンコードされた UTF-8)

これも大丈夫。でも Base64 は元データの 3, 4 割増になる。

Base64

データ部は正規表現で ^[a-zA-Z0-9\+\/]+={0,3}$
よって

<?php
// PHP の preg 用
$charset = '[-_a-zA-Z0-9]+'; // 適当。 [^\?] + のがいいかもしれない。
$encoding = '[BQ]'; // Base64 もしくは Quotet-printable (っていうのがある)
$data = '[a-zA-Z0-9+\/]+={0,3}'; // 行末の = はパディングに使われる。詳しくは Base64 を勉強すること。
$encoded = '=\?' . $charset . '\?' . $encoding . '\?' . $data . '\?=';
$pattern = '/' . $encoded . '/';
?>

こうなる。

最新の RFC

多分 ftp://ftp.rfc-editor.org/in-notes/rfc3862.txt in August 2004

  • メーラは、 1 行の長さを制限してはならない。
  • 全体的に UTF-8 を使う
  • でもヘッダ名は US-ASCII の文字以外使ってはならない

など、ぱっと見た限りでも色々変わってそう