MIMEとメールのダイエット
(2022.8.8-2023.3.22)
メールはテキスト(印字可能文字の)ファイルです。メールのテキストをメールのソースといいます。メールのソースは,MIME(Multipurpose Internet Mail Extensions)という規格(規則)に従って書かれています。
メーラー
メーラー(メールソフト)は,MIMEに従ってメールのソースを解釈して表示します。MIMEを理解すれば,大きなメールのソースから,無駄な部分を圧縮したり削除したりしてファイルサイズをダイエットする事ができます。「メールのダイエット」というと,普通,書くメールの本数や本文の量を減らすことを指すようですが,ここでは,メールボックスを圧迫しないように,保存してあるメールの無駄な容量を削減するという意味で使います。
thunderbird
最も歴史があり信用できる,標準メーラーはthunderbirdです。例えば,他のメーラーでは文字化けしないのに,thunderbirdでは文字化けする,ということが起こります。これは,thunderbirdがMIMEに忠実で,他のメーラーでは文字化けを避けるために,MIMEを甘く解釈して処理を行っているからです。メールのダイエット処理もMIMEに厳格なthunderbirdを使って行うのが安全です。
emlファイル
thunderbirdでは,Ctrl+Sでメールのソースを拡張子がemlのテキストファイルとして保存できます。逆に,emlファイルは,thunderbirdのメールのフォルダにドラッグ&ドロップすれば,メールとして取り込めます。
それゆえ,メールのダイエットは,メールをemlファイルで保存し,ソースを編集して,再びメールフォルダに戻して行います。
emlファイルの編集は,Visual Studio Code(VSCode)にEmail extensionを入れて行うと,MIME文法が色分けされて表示されるので便利です。
文字コード
メールソースで使われる文字コードは,英語圏ではASCII(Unicodeの127番目まで)文字です。
1バイトでASCII文字を表現すると,最上位の1ビットは使われないことから,メールの通信経路では,この最上位1ビットが送られないことがあります。
日本語では,MIMEに従って,JIS(iso-2022-jp)か UTF-8が使われます。
JISコード
JISコード(iso-2022-jp)では,ASCII文字で次のように日本語の文字を表します:ESC(1B)に続く2バイトで文字集合を指示し,文字はASCIIのGL文字n個で表します。指示が(BならASCII(n=1),(JならJIS C 6220(n=1,カタカナなど),$@ならJIS C 6226(n=2),$BならJIS X 0208(n=2)です。
JISコードも1バイト中7ビットしか使わないので,最上位1ビットが送られない可能性のあるメールで使われるようになりました。しかし、最近はそのような経路は無くなったので、メールのコードもUTF-8に統一されつつあります。
改行コード
テキストデータの1行の区切りを表すコードはシステムによって異なり,CR(0x0D)またはLF(0x0A)が使われます。
LF: UNIXやUnix系のシステム。
CR+LF: Microsoft Windows,MS-DOS。
CR: Mac OS(バージョン9まで)。
nkfにオプションを指定して,これらを変換できます:
nkf -オプション 入力ファイル > 出力ファイル
オプション
d:改行をLFにする。
c:改行をCR+LFにする。
Lm:改行をCRにする。
しかし,メールダイエットの作業では改行コードの変換は必要ありません。
文字エンコード
テキストデータに限らない,一般のデータをテキストデータに変換することを文字エンコード,逆に,元のデータに戻すことを文字デコードといいます。メールはテキストデータであるため,画像などの一般のデータを添付する際は文字エンコードが使われます。電子メールで使われる文字エンコードは,quoted-printable, base64, uuencode, binhexなどです。
base64
データを6ビットに区切り,64通りある6ビットの値を1バイトのASCII文字に置き換えます(以下ではこれを8/6変換と呼びます)。base64エンコード/デコードはbase64コマンドで行えます。
base64 [-d] 入力ファイル > 出力ファイル
オプション-dがなければエンコード,-dがあればデコード。base64はWSLにapt install base64でインストールできます。
テキストデータのデコードはなぜかbase64ではエラーでできない事が多いので,以下のようにnkfで行います:
nkf -mB 入力ファイル > 出力ファイル
quoted-printable
=に続く2桁の16進数で文字コードを表します(=3Dなど)。ただし,=を除く文字コードが0x21~7EのASCII文字はそのままでもかまいません。行末の=は行の継続を表します。テキストデータのquoted-printableのエンコード/デコードはnkfで行うことができます:
エンコード
nkf -MQ 入力ファイル > 出力ファイル
デコード
nkf -mQ 入力ファイル > 出力ファイル
(QをBにすると,base64のエンコード/デコードができます)
uuencode
8/6変換後,データの先頭にヘッダー行,末尾にフッター行(end)が付加されます。
binhex
8/6変換後,圧縮と誤り検出が加えられ,ヘッダー行 (This file must be converted with BinHex 4.0) が付加されます。
pythonでbinhexエンコード/デコードを行えます。 Google ColaboratoryでGoogle Driveをマウント(画面左側の📁を選択し、[ドライブをマウント][GOOGLEドライブに接続])し,/content/drive/My Drive/ に処理したいファイルをアップロードします。
デコード:
import binhex
binhex.hexbin('/content/drive/My Drive/入力ファイル', '/content/drive/My Drive/出力ファイル')
エンコード:
import binhex
binhex.binhex('/content/drive/My Drive/入力ファイル', '/content/drive/My Drive/出力ファイル')
MIMEヘッダ
MIMEでは,次のようなMIMEヘッダという形式で,文字コードやデータの種類を指定します。
Content-Type: Multipart
Content-Type: multipart/mixed; boundary="文字列"
"文字列"で区切られるパートからなる,マルチパートメールであることを示します。Content-Typeに続くMIME Typeはここに一覧があります。
Content-Type: multipart/alternative; boundary="文字列"
個々のパートが,プレーンテキストとHTMLのように,同じ内容であることを示します。
Content-Type: multipart/signed; protocol="application/pkcs7-signature"; micalg=sha1; boundary="文字列"
署名されていることを示します。
Content-Type: text
Content-Type: text/html; charset="文字コード"
HTML文書。文字コードはUTF-8,Shift_JIS,EUC-JP,ISO-2022-JP。ISO-2022-JPがJISコード。
Content-Type: text/plain; charset="文字コード"
テキスト文書。
Content-Transfer-Encoding:
Content-Transfer-Encoding: 文字エンコード
文字エンコードを指定します。base64,quoted-printable,あるいは7bit,8bit。Nbitとは,バイト並びの下位Nビットを文字コードとして解釈し,上位8-Nビットは無視するという意味です。
ファイル
Content-Type: 種類; name="ファイル名"
ファイル。種類は application/msword,application/pdf,application/vnd.ms-excel;image/bmp,image/gif,image/jpeg,image/png,image/svg+xml,image/tiff など。
Content-Disposition: attachment; filename="ファイル名"
添付ファイル。
Content-Disposition: inline; filename="ファイル名"
Content-Id: <コンテンツID>
内部ファイル。コンテンツIDで参照します。
Content-Type: application/pkcs7-signature; name="smime.p7s"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="smime.p7s"
Content-Description: S/MIME Cryptographic Signature
署名ファイル。
件名や送信者名などの文字コード
件名や送信者名などメールヘッダに日本語を含めるには,次の形式で文字コード,文字エンコードを指定します:
=?文字コード?文字エンコード方式?文字列?=
文字コードは,UTF-8かISO-2022-JP,文字エンコード方式は,Base64の場合B,Quoted-Printableの場合Q,いずれかの値が入ります(本文の文字エンコード方式で利用できる7bit,8bitに相当するものはありません)。
メールのダイエット例
thunderbirdでは,添付ファイルの分離と暗号化メールの復号はできますが,その他のダイエットはできないので,以下のように手動で行う必要があります。頻繁にあり得るものは,スクリプトeml-minifierを作りましたので,ご利用ください。
インライン添付ファイルの分離
インライン添付ファイルを右クリックして保存し,
Content-Disposition: inline; パートの本文を削除する。Content-Id: ヘッダも削除すると,添付ファイル名をメーラーで見ることができます。
署名付きメールの添付ファイル分離
thunderbirdでは,署名付きメールの添付ファイルは分離できないので,以下のように手動で分離します。
添付ファイルをすべて保存してから,
Content-Type: application/pkcs7-signature; パートを削除し,
Content-Disposition: attachment; パートの本文を削除します。
返信の引用を削除
返信メールから,長々と引用された返信元のメールを削除するには,以下のようにします。
Content-Type: text/html; パートがあれば,Content-Type: text/plain; パートは削除し,
Content-Type: text パートの本文が,読めない場合,
本文を別のファイルにコピーして,Content-Transfer-Encoding: に応じてデコードします。base64なら,nkf -mB 入力ファイル > 出力ファイル。
本文をデコードした本文に置き換え,Content-Transfer-Encoding: ヘッダを削除し,文字コードを適宜変更します。
必要に応じて引用を削除します。
winmail.datで添付されたファイルの分離
thunderbirdに拡張機能look outを入れて,winmail.datで添付されたファイルを保存し,
Content-Type: application/ms-tnef; name="winmail.dat" パートの本文を削除します。
そのパートのヘッダを,添付されていたファイルのパートに書き換えます。(例)
Content-Type: 種類; name="ファイル名"
Content-Disposition: attachment; filename="ファイル名"
添付ファイルの個数分そのようなパートを作ります。
Content-Type: textパートの文字コードをUTF-8にし,Content-Transfer-Encoding: ヘッダを削除します。
ファイルを文字コードをUTF-8にして保存します。
画像ファイルのダイエット
(インライン)添付画像ファイルを右クリックして保存します。
保存した画像ファイルの容量を削減処理をします。
適切な圧縮形式の選択:pngファイルは線画,jpgファイルは写真を保存するのに適した圧縮形式です。この選択が適切でない場合は,適切な圧縮形式に変更してください。
画素数の削減:インラインファイルで,本文で画像サイズが指定されている場合,画像ファイルの縦横の画素数を小さくしても,画質の劣化が気にならないことがあります。irfanviewで画像を開いて,[image][resize/resample]で[resample]を選択しておこなうと,きれいに処理できます。
jpgファイルのquality:irfanviewで画像を開いて,名前を付けてjpgで保存します。デフォルトquality 80で容量も元のファイルより削減される場合もあります。qualityを60まで下げると,より容量が削減されます。画像の種類によっては劣化が目立ちません。
pngファイルの圧縮レベル:irfanviewで画像を開いて,名前を付けてpngでCompression levelを9にして保存します。画質の劣化はありません。たいていのpngファイルは容量が小さくなります。
pngファイルの色数の削減:irfanviewで画像を開いて,[image][decrease color depth]で16 colorsにすると,画質があまり劣化せず容量が劇的に減ることがあります。[Use Floyd-Steinberg dithering]と[Use best color quality]にチェックを付けるか付けないかで,4通りの方法を試し,最良の結果が得られる方法を選択してください。何もチェックしないか,[Use best color quality]だけチェックすると良い結果が得られるようです。例
グレースケール画像であれば,上記処理に先立って,irfanviewのconvert to grayscaleを行ったほうがよいです。
処理した画像ファイルをContent-Transfer-Encoding:で指定された方法で文字エンコードします。base64の場合は次のようにします。
base64 処理済み画像ファイル名 > テキストファイル名
Content-Disposition: attachment(またはinline); パートの本文をテキストファイルの内容に置き換え,Content-Typeを容量削減画像の形式(image/png, image/jpeg, image/gifなど),Content-Typeのnameとfilenameをファイル名に置き換えます。
ファイルの分離先
添付ファイルやインラインファイルの分離先として,ローカルマシンのフォルダを指定しておけば,ファイルが分離された事を意識することなく利用できます。
添付ファイルの分離先URL
file:///C:/.../ファイル名 のようにフォルダを/で区切って指定するか,http://localhost/.../ファイル名 とします。
Content-Type: 種類; name="ファイル名"
Content-Disposition: attachment; filename="ファイル名"
X-Mozilla-External-Attachment-URL: 分離先URL
X-Mozilla-Altered: AttachmentDetached; date="Tue Mar 31 11:13:55 2020"
インラインファイルの分離先URL
text/htmlのimgタグのsrcような参照先を,分離先URLに置き換えます。この場合,file://でローカルファイルをしても上手くいかないようです。http://なら上手くいきます。
そこで,Windowsの場合,Webサーバ,インターネットインフォメーションサービス(IIS)を起動しておけば,ローカルファイルをhttp://localhostで参照できます。
IISの起動法
⚙→[アプリ]→[プログラムと機能]→[Windows機能の有効化または無効化]→[インターネットインフォメーションサービス]をチェック。
IISの設定
[スタート]を右クリックして[コンピュータの管理]→[サービスとアプリケーション]→[インターネットインフォメーションサービス]で[ディレクトリの参照]でディレクトリを開いて,[Default Web Site]を右クリックして[仮想ディレクトリを作る]でファイルを置きたいディレクトリを指定する。http://仮想ディレクトリ名/でファイルを参照できます。