MIMEによるEメールの構造
2020/09/20更新
目次
メールの構造
基本
最も基本的なメールは以下の通りとなる。From
とTo
で差出人と送信先のメールアドレスを指定し、Subject
に件名を記述する。そして1行空けて本文を書く。
From: from@example.com To: to@example.com Subject: Sample subject Hello. This is sample text.
日本語のメール
メールは本来7ビットしか扱えないため、メールアドレスも件名も本文も本来は上記のようにASCII文字(半角記号英数字)しか使うことができない。日本語の文字コードの一つであるISO-2022-JP(いわゆる「JISコード」)は、7ビットの範囲で日本語を表現するものなので、昔からメールで日本語といえばISO-2022-JPが使われていた。しかし、より一般的で自由度も高いShift_JISやUTF-8など他の文字コードは8ビットのコードなので、メールでの使用は禁忌とされていた。
しかし、MIME(Multipurpose Internet Mail Extensions)という規格が導入され、7ビットの範囲でマルチバイト文字やバイナリファイルの送受信を可能とする方法が作られた。MIMEでは、データの種類やエンコードを指定することができ、さらに一つのメールの中を本文のデータと添付ファイルのデータといったように分割するしくみも備えている。
MIMEのしくみに従うと、UTF-8の日本語のメールは以下のようになる。
From: from@example.com To: to@example.com Subject: Sample subject MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: base64 ※UTF-8の本文をBase64にエンコードしたもの※
このようにContent-Type
ヘッダでMIMEタイプと文字コードを指定し、Content-Transfer-Encoding
ヘッダでそれをBase64エンコードしたことを明記し、本文には「UTF-8の本文をBase64エンコードしたもの」を記述する。
なお、From
、To
、Subject
で日本語を使う方法は後述する。
添付ファイル付きメール
添付ファイルを実現するには、MIMEのマルチパートというしくみを使う。マルチパートは、一つのメールを複数の部分に分割する方法である。
From: from@example.com To: to@example.com Subject: Sample subject MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="aaaaaaaaaa" --aaaaaaaaaa Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: base64 ※UTF-8の本文をBase64エンコードしたもの※ --aaaaaaaaaa Content-Type: ※添付ファイルのMIMEタイプ※ Content-Disposition: attachment; filename="※添付ファイルのファイル名※" Content-Transfer-Encoding: base64 ※添付ファイルをBase64エンコードしたもの※ --aaaaaaaaaa--
マルチパートメールでは、まずMIMEタイプにmultipart/○○
を指定してマルチパートであることを宣言し、boundary="~"
で境界文字列を定義する。multipart/mixed
は複数のデータ(ここでは本文と添付ファイル)が混在していることを表す。境界文字列はマルチパートの各パートの境目を表す文字列で、本文や添付ファイルのデータの中に出現しない任意の文字列を指定する(ここではaaaaaaaaaa
としている)。そして、--境界文字列
で各パートの開始を、--境界文字列--
で全てのパートの終わりを示す。
各パートには、冒頭でそのパートのContent-Type
などを指定し、続けてデータを記述する。パートのContent-Type
を再びmultipart
にして別の境界文字列を指定すれば、マルチパートを入れ子にすることもできる。
なお、添付ファイルのファイル名で日本語を使う方法は後述する。
HTMLメール
HTMLメールは通常HTMLの本文データの他に、HTML未対応のクライアントのために同じ内容をプレーンテキストで表したデータも同時に送っている。これもマルチパートによって実現される。
From: from@example.com To: to@example.com Subject: Sample subject MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="aaaaaaaaaa" --aaaaaaaaaa Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: base64 ※UTF-8の本文(プレーンテキスト)をBase64エンコードしたもの※ --aaaaaaaaaa Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: base64 ※UTF-8の本文(HTML)をBase64エンコードしたもの※ --aaaaaaaaaa--
multipart/alternative
は同じ内容を別の形式で表していることを表す。上記のようなメールを送信すると、HTML対応のクライアントではHTMLメールが表示される。
添付ファイル付きのHTMLメール
添付ファイルとHTMLメールを同時に実現するには、マルチパートを入れ子にすればよい。
From: from@example.com To: to@example.com Subject: Sample subject MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="aaaaaaaaaa" --aaaaaaaaaa Content-Type: multipart/alternative; boundary="bbbbbbbbbb" --bbbbbbbbbb ※プレーンテキストの本文のパート※ --bbbbbbbbbb ※HTMLの本文のパート※ --bbbbbbbbbb-- --aaaaaaaaaa ※添付ファイルのパート※ --aaaaaaaaaa--
上記例では、境界文字列がaaaaaaaaaa
のマルチパートで本文と添付ファイルを実現し、本文のパートをさらに境界文字列がbbbbbbbbbb
のマルチパートでプレーンテキストとHTMLに分割している。
ヘッダで日本語を使う方法
ヘッダのFrom
、To
、Subject
などで日本語を使う場合は、RFC2047で定義された以下のような形式のエンコードを行なう。
=?(文字コード)?(エンコード方式)?(エンコードされた文字列)?=
文字コードは現代では「UTF-8」を指定することが多いと思われるが、互換性などを考慮して昔ながらの「ISO-2022-JP」も使用される。エンコード方式は、日本語の場合はBase64エンコードを表す「B
」を指定する。そして、指定した文字コードの文字列をBase64エンコードして、最後の部分に記述する。このようにして、例えばSubject
をUTF-8で「日本語の件名」としたい場合は以下のようになる。
From: from@example.com To: to@example.com Subject: =?UTF-8?B?5pel5pys6Kqe44Gu5Lu25ZCN?= …
From
やTo
の名前に使う場合は以下のようになる。
From: =?UTF-8?B?5pel5pys6Kqe5aSq6YOO?= <nihongo.taro@example.com> To: =?UTF-8?B?5pel5pys6Kqe6Iqx5a2Q?= <nihongo.hanako@example.com> …
日本語の文字列が短い場合は上記の通りで問題ないが、長くなってくると問題が発生する。メールヘッダでは、1行あたりの文字数に制限があり、78文字以下にすべきとされている。そこで、長くなる場合は2行以上に分割して書くことができるのだが、分割する際に以下の注意が必要となる。
分割した2行目以降は、先頭にスペースを入れる。英単語の間のスペースなどで分割した場合はこのスペースは残されるが、
=? ~ ?=
の形式でエンコードした文字列が続く場合はスペースは残らない。Subject: This is a mail. → 「This is a mail.」という件名になる(isとaの間にスペースが残る) Subject: =?UTF-8?B?5pel5pys6Kqe?= =?UTF-8?B?44Gu5Lu25ZCN?= → それぞれをデコードすると「日本語」「の件名」であるが、間にスペースは残らず「日本語の件名」となる。
原則として、全体をエンコードする。日本語の部分だけをエンコードして半角英数字はエンコードせずに残すようなことはしない。
エンコードしたBase64文字列や、マルチバイト文字のバイト列を任意の場所で分割することはできない。まず、文字単位で分割してから、それぞれをBase64エンコードする。
Subject: =?UTF-8?B?5pel5pys6Kqe?= =?UTF-8?B?44Gu5Lu25ZCN?= → 「日本語」と「の件名」で分割してそれぞれをエンコードしているのでOK。 Subject: =?UTF-8?B?5pel5pys6Kqe44?= =?UTF-8?B?Gu5Lu25ZCN?= → Base64文字列を適当なところで分割することはできない。 Subject: =?UTF-8?B?5pel5pys6Kqe44E=?= =?UTF-8?B?ruS7tuWQjQ==?= → Base64文字列としては正しいが、バイト列を文字境界以外で分割することはできない。
ISO-2022-JPを文字コードとする場合は、各行のエンコードが終わるごとにローマ字状態に戻っている(最後がローマ字INコード
\x1B(B
となっている)必要がある。
添付ファイルのファイル名で日本語を使う方法
添付ファイルのファイル名で日本語を使うのは、ヘッダで日本語を使うよりもさらに厄介である。どうしても必要でないのなら、ASCIIだけのファイル名にしておいた方が無難である。
現状、以下のような方法が乱立しており、メールソフトによっても対応状況が異なる。
1つ目は、RFC2231に基づいた最も正しい方法。
filename
パラメーターの代わりにfilename*
パラメーターを使い、先頭に文字コードを記述した後、URLエンコードによって日本語ファイル名を指定する。また、長くなる場合は、filename
と*
の間に*0
、*1
、*2
…と連番を付けて複数のパラメーターに分割する。パラメーター間の;
を忘れないように注意する。ちなみに、*0
、*1
、*2
…と分割する方法は、日本語を使わない場合の通常のfilename
パラメーターに対しても使用できる。Content-Disposition: attachment; filename*=utf-8''※UTF-8のファイル名をURLエンコードしたもの※ ファイル名が長い場合: Content-Disposition: attachment; filename*0*=utf-8''※UTF-8のファイル名をURLエンコードしたもの1※; filename*1*=※UTF-8のファイル名をURLエンコードしたもの2※; filename*2*=※UTF-8のファイル名をURLエンコードしたもの3※
2つ目は、エンコードの方式をヘッダと同じ
=? ~ ?=
の形式にしたもの。RFC2047で明確に
=? ~ ?=
の形式をここで使ってはいけないとされているため、本来は正しくない方法であるが、慣例的に多くの場面で使われている。Content-Disposition: attachment; filename="=? ~ ?=" ファイル名が長い場合: Content-Disposition: attachment; filename="=? ~ ?= =? ~ ?= =? ~ ?="
3つ目は、
Content-Disposition
ヘッダのfilename
パラメーターではなく、Content-Type
ヘッダのname
パラメーターに=? ~ ?=
の方式で指定するもの。こちらも本来正しい方法ではないが、できるだけ多くのメールソフトへ対応させることを狙って、1つ目の正しい方法や2つ目の方法と併用するケースがある。Content-Type: image/png; name="=? ~ ?=" ファイル名が長い場合: Content-Type: image/png; name="=? ~ ?= =? ~ ?= =? ~ ?="
メールの送信
sendmailコマンド
sendmail
コマンドは、サーバー上の自前のプログラムからアラートメールなどを送信したい場合に手軽に使えるメール送信コマンドである。
sendmail
コマンドでメール送信するには、以下のようにする。
cat mail.txt | /usr/sbin/sendmail -i -t
sendmail
コマンドは、.
だけの行があるとメールの終端と見なしてしまうため、それを回避するために通常-i
オプションをつける。また、-t
オプションは標準入力から送信先アドレスなどの情報を取得することを意味する。
標準入力(上記の場合mail.txt
の内容)には、ここまで説明してきたような形式でメールの内容を与える。
From: from@example.com To: to@example.com Subject: Sample subject Hello. This is sample text.
これだけでメールを送信することができる。