メールサーバやメールリーダが行うメール本文の書き換えについて

#はじめに
メールの文字化け対策について調査を行っていました。それで副次的に気がついたのですが、メールサーバやメールリーダーが「ヘッダ」だけでなくメールの「本文」の修正を行う事があるようです。

マジか！

修正内容について調査してみました。

初めにメールサーバによる書き換えを調査します．
（結論を先に述べると、Office365(クラウド)はHTMLのタグの改行空白の修正が入ります。）

続いてOffice2019(Win)による書き換えを調査します．
 (結論を先に述べると、Plain/HTMLのAlternativeメールは、Office2019(Win+IMAP)でメールのコピー作業を行うと，IMAPサーバ上の元メールとコピーしたメールの内容が書き換わってます．HTMLの内容に合わせてPlainの内容が書き換わります。)

※注意: 無料版のOutlookのメールアカウントは単純な内容のメール(例えば本文が「テスト」一行のメールなど)を多数送受信するとアカウントロックされる事があるようです。ご注意ください。以下の調査は職場で契約しているOffice365を用いてます。(Office365は職場の管理者が色々設定している可能性があるため，無料版Outlookで試したかったのですが，断念しました．)

#事前準備

職場のメールアドレス(CentOS7/Postfix)を用意しました．続いてOffice365のメールアドレスとgmail(クラウド)のメールアドレスを準備しました。

- 職場のメール: 職場のアドレス@example.com (CentOS7/Postfix)
- Gmailメール: テストアドレス@gmail.com
- Office365メール: テストアドレス@outlook.com

Mewで上記の３つのアドレスを読み書きできるようにしてください．

Gmailは「安全性の低いアプリの許可」をオンにしないと読めないと思います．Office365は特になにもしないでもimapで読めましたが(職場で契約したものなので)無料版のOutlookでも同様かはわかりません．

#サンプルメール1(PLAIN+HTML)

以下のようなファイルを用意しました．JISコードで作成したPLAINテキストおよび，ほぼ同じ内容のHTMLファイルです．

```:書換テストjis.txt
転送時にファイルが書き換えられるかのテスト(20210629)
*送信元(Mew6.8/JIS)*
```

```:書換テストjis.html
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-2022-jp">
  </head>
  <body>
    転送時にファイルが書き換えられるかのテスト(20210629)<BR>
    <b>送信元(Mew6.8/JIS)</b>
  </body>
</html>
```
#サンプルメール2(内容差分)

以下のようなファイルを用意しました．JISコードで作成したPLAINテキストおよび，内容が異なるHTMLファイルです．Plainテキストの「差分txt」がHTMLでは「差分html」に修正しています．


```:書換テストdiff.txt
転送時にファイルが書き換えられるかのテスト(20210629)
*送信元(Mew6.8/JIS)/差分txt*
```

```:書換テストdiff.html
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-2022-jp">
  </head>
  <body>
    転送時にファイルが書き換えられるかのテスト(20210629)<BR>
    <b>送信元(Mew6.8/JIS)/差分html</b>
  </body>
</html>
```
#Mewのマルチパートで送信
テストメールをMewで作成します．テストメールをOutlookやThunderbirdで作成すると，メールリーダーがどのようなメールを作成するか確認し辛いので，Mewで無理やり同等のマルチパートメールを作成します．

そのため，少々めんどくさい操作しています．

次の組み合わせでMewのマルチパートで送信してみます．

-サンプルメール1(PLAIN+HTML): 書換テストjis.txt / 書換テストjis.html
-サンプルメール2(内容差分): 書換テストdiff.txt / 書換テストdiff.html

ここでMewで送信時の注意点ですが、

- Mewの本文にはなにも書かない．
- Multipart/MixedからAlternativeしてください(Tで修正)．
- ファイル名を無しにしてください(Pで修正). ファイル名を消すと
  「xxxx.txt」から「*xxxx.txt」に修正されます．

こんな感じです．

```:Mew送信例サンプルメール1(PLAIN+HTML)
To: テストアドレス@gmail.com, テストアドレス@outlook.com
Subject: 添付ファイルが書き換えられるかのテスト(20210629)/JIS
From: 職場のアドレス@example.com
Dcc: 職場のアドレス@example.com
X-Mailer: Mew version 6.8 on Emacs 24.3
----
(本文にはなにも書かない．)
------------------------------ attachments ------------------------------
      Multipart/Alternative   (←MixedからAlternativeに修正)     3/
     1  Text/Plain(guess)                                         *Cover.txt
     2  Text/Plain(guess)                                         *書換テストji.
     3  Text/Html(guess)                                          *書換テストji..
     4                                                            .
--------0-1-2-3-4-5-6-7-8-9----------------------------------------------

```

Mew6.8(CentOS7.9)から職場のメールサーバ経由でOffice365/gmailのメールサーバに送信します。

> Mew6.8 → SMTP → 職場のメールサーバ(CentOS7) → SMTP → (クラウド)Office365/Gmail

サンプルメール1(PLAIN+HTML)を送信して，続いてサンプルメール2(内容差分)を送信してください．

```:Mew送信例サンプルメール2(内容差分)
To: テストアドレス@gmail.com, テストアドレス@outlook.com
Subject: 添付ファイルが書き換えられるかのテスト(20210629)/diff
From: 職場のアドレス@example.com
Dcc: 職場のアドレス@example.com
X-Mailer: Mew version 6.8 on Emacs 24.3
----
(本文にはなにも書かない．)
------------------------------ attachments ------------------------------
      Multipart/Alternative                                      3/
     1  Text/Plain(guess)                                         *Cover.txt
     2  Text/Plain(guess)                                         *書換テストdi..
     3  Text/Html(guess)                                          *書換テストdi..
     4                                                            .
--------0-1-2-3-4-5-6-7-8-9----------------------------------------------
```

#メールサーバによる修正

##サンプルメール1(PLAIN+HTML)

GmailとOffice365に送ったメールをMewで取り込んでください．

オリジナルのメールとdiffを取ってみます．

- Gmailは本文修正なしです．

- Office365は本文のHTMLパートに以下の修正が入ります．HTMLの細かな修正がはいります．

```
$ diff -uwi jis_org.eml jis_office365.eml
(diffの出力は一部割愛)
 ----Next_Part(Tue_Jun_29_10_03_27_2021_939)--
 Content-Type: Text/Plain; charset=iso-2022-jp
@@ -31,8 +131,7 @@
 Content-Type: Text/Html; charset=iso-2022-jp
 Content-Transfer-Encoding: 7bit

-＜html＞
-  ＜head＞
+＜html＞＜head＞
     ＜meta http-equiv="Content-Type" content="text/html; charset=iso-2022-jp"＞
   ＜/head＞
   ＜body＞
```



##サンプルメール2(内容差分)の差分
Gmailは修正なし．Office365はサンプルメール1(JIS)とほぼ同じです．

#メールリーダーによる修正
先ほど確認した書換はメールサーバによるものですが、次にメールリーダでの修正について確認します。

職場のメールサーバにOffice2019(Win10+IMAP)で接続します。Office2019での操作でサンプルメール1と2を別のフォルダにコピー作業を行ってみます．(移動では確認してません．)

こんな形になります．

> Office2019(Win) → IMAP → 職場のメールサーバ(CentOS7)

IMAPのため，Office2019上でコピー作業すると，IMAPサーバ上のファイルのコピーが発生します．

コピー後，コピーしたメールを再びMewで取り込みます．


##サンプルメール1(PLAIN+HTML)の差分
コピーしただけでは何も起きないと思いますよね．そんな事はなかったりします．

ヘッダ部分は読みにくいので文章で説明しますが，

- Subjectの改行位置の修正

```
-Subject: =?iso-2022-jp?B?GyRCRTpJVSVVJSElJCVrJCw9cRsoQg==?=
- =?iso-2022-jp?B?GyRCJC00OSQoJGkkbCRrJCskThsoQg==?=
- =?iso-2022-jp?B?GyRCJUYlOSVIGyhCKDIwMjEwNjI5KS9KSVM=?=
+Subject: =?iso-2022-jp?B?GyRCRTpJVSVVJSElJCVrJCw9cSQtNDkkKCRpJGwkayQrJE4lRiU5GyhC?=
+       =?iso-2022-jp?B?GyRCJUgbKEIoMjAyMTA2MjkpL0pJUw==?=
```

- From/Toなどのアドレス表記の修正。アドレスは多数あるので抜粋です。

```
修正前:
- 職場のメール ＜職場のアドレス@example.com＞
- テストアドレス@outlook.com

修正後:
- "職場のメール" ＜職場のアドレス@example.com＞
- ＜テストアドレス@outlook.com＞
```

- MIMEのboundaryの変更

```
 Content-Type: Multipart/Alternative;
- boundary="--Next_Part(Tue_Jun_29_10_03_27_2021_939)--"
+       boundary="----=_NextPart_000_002D_01D76CD0.721A1BB0"
```

- ヘッダ部からの追加削除

```
削除:
Content-Transfer-Encoding: 7bit

追加:
(省略)
```

続いて本文への修正ですが、

- (前述しましたが)boundary修正
- 「This is a multipart message in MIME format.」追加
- Plainテキストの修正
 「*」除去．← <b>ちょっとこれなに</b>

- Encodeが7bit からquoted-printable修正．

```
$ diff -uwi jis_org.eml jis_office2019.eml
(diffの出力は一部割愛)

+This is a multipart message in MIME format.

-----Next_Part(Tue_Jun_29_10_03_27_2021_939)--
-Content-Type: Text/Plain; charset=iso-2022-jp
+------=_NextPart_000_002D_01D76CD0.721A1BB0
+Content-Type: text/plain;
+       charset="iso-2022-jp"
 Content-Transfer-Encoding: 7bit

 転送時にファイルが書き換えられるかのテスト(20210629)
-*送信元(Mew6.8/JIS)*
+送信元(Mew6.8/JIS)

-----Next_Part(Tue_Jun_29_10_03_27_2021_939)--
-Content-Type: Text/Html; charset=iso-2022-jp
-Content-Transfer-Encoding: 7bit
+------=_NextPart_000_002D_01D76CD0.721A1BB0
+Content-Type: text/html;
+       boundary="--Next_Part(Tue_Jun_29_10_03_27_2021_939)--";
+       charset="iso-2022-jp"
+Content-Transfer-Encoding: quoted-printable

 ＜html＞
   ＜head＞
-    ＜meta http-equiv="Content-Type" content="text/html; charset=iso-2022-jp"＞
+    ＜meta http-equiv=3D"Content-Type" content=3D"text/html; =
+charset=3Diso-2022-jp"＞
   ＜/head＞
   ＜body＞
-    転送時にファイルが書き換えられるかのテスト(20210629)＜BR＞
-    ＜b＞送信元(Mew6.8/JIS)＜/b＞
+    =1B$BE＞Aw;~$K%U%!%$%k$,=3Dq$-49$($i$l$k$+$N%F%9%H=1B(B(20210629)＜BR＞
+    ＜b＞=1B$BAw?.85=1B(B(Mew6.8/JIS)＜/b＞
   ＜/body＞
 ＜/html＞

-----Next_Part(Tue_Jun_29_10_03_27_2021_939)----
+------=_NextPart_000_002D_01D76CD0.721A1BB0--
```

##サンプルメール2(内容差分)の差分
このサンプルはPlainテキストとHTMLで文章の内容が異なります．Plainテキストの「差分txt」がHTMLでは「差分html」に修正しています．

サンプルメール1と同じのは割愛しますが，

- Plainテキストの修正。
「差分txt」が「差分html」に差し替え．← <b>ちょっとこれなに</b>


```
$ diff -uwi jis_org.eml jis_office2019.eml
(diffの出力は一部割愛)

+This is a multipart message in MIME format.

-----Next_Part(Tue_Jun_29_10_08_49_2021_742)--
-Content-Type: Text/Plain; charset=iso-2022-jp
+------=_NextPart_000_0026_01D76CD0.71D76D60
+Content-Type: text/plain;
+       charset="iso-2022-jp"
 Content-Transfer-Encoding: 7bit

 転送時にファイルが書き換えられるかのテスト(20210629)
-*送信元(Mew6.8/JIS)/差分txt*
+送信元(Mew6.8/JIS)/差分html

-----Next_Part(Tue_Jun_29_10_08_49_2021_742)--
-Content-Type: Text/Html; charset=iso-2022-jp
-Content-Transfer-Encoding: 7bit
+------=_NextPart_000_0026_01D76CD0.71D76D60
+Content-Type: text/html;
+       boundary="--Next_Part(Tue_Jun_29_10_08_49_2021_742)--";
+       charset="iso-2022-jp"
+Content-Transfer-Encoding: quoted-printable

 ＜html＞
   ＜head＞
-    ＜meta http-equiv="Content-Type" content="text/html; charset=iso-2022-jp"＞
+    ＜meta http-equiv=3D"Content-Type" content=3D"text/html; =
+charset=3Diso-2022-jp"＞
   ＜/head＞
   ＜body＞
-    転送時にファイルが書き換えられるかのテスト(20210629)＜BR＞
-    ＜b＞送信元(Mew6.8/JIS)/差分html＜/b＞
+    =1B$BE＞Aw;~$K%U%!%$%k$,=3Dq$-49$($i$l$k$+$N%F%9%H=1B(B(20210629)＜BR＞
+    ＜b＞=1B$BAw?.85=1B(B(Mew6.8/JIS)/=1B$B:9J,=1B(Bhtml＜/b＞
   ＜/body＞
 ＜/html＞

-----Next_Part(Tue_Jun_29_10_08_49_2021_742)----
+------=_NextPart_000_0026_01D76CD0.71D76D60--
```

というようにPLAINテキストとHTMLのAlternative属性のメールはHTMLに準じてPlainテキスト部が修正されます．
#その他の修正
上記以外にOutlook2019(Win+IMAP)でファイルのコピー作業をすると，以下の修正が入るようです．詳細は省略します．

- (常に発生)添付ファイルのファイル名(Content-Disposition: filename)が
  RFC2231の場合は，Base64形式(=?charset?B?.....?=)に修正．


  Mewで添付ファイル「一二三四五六七八.txt」を添付し，職場メールサーバに送信後Outlook2019(Win+IMAP)でファイルのコピー操作を実施した例です．

追記：よく見たら添付ファイルの「charset=iso-2022-jp」指令が無くなってるので確認したらBASE64をdecodeしたらこれShift-JISじゃないか。。

```
$ diff -uwi jp-filename-mew.eml jp-filename-office2019.eml
(diffの出力は一部割愛)

-----Next_Part(Tue_Jun_29_14_58_42_2021_099)--
-Content-Type: Text/Plain; charset=iso-2022-jp
+This is a multipart message in MIME format.
+
+------=_NextPart_000_000C_01D76CF7.585CFD10
+Content-Type: text/plain;
+       boundary="--Next_Part(Tue_Jun_29_14_58_42_2021_099)--";
+       charset="iso-2022-jp"
 Content-Transfer-Encoding: 7bit

 ファイル名命名規則の修正

-----Next_Part(Tue_Jun_29_14_58_42_2021_099)--
-Content-Type: Text/Plain; charset=iso-2022-jp;
- name="=?iso-2022-jp?B?GyRCMGxGczswO004Xk87PDdILBsoQi50eHQ=?="
-Content-Transfer-Encoding: 7bit
-Content-Disposition: inline;
- filename*=iso-2022-jp''%1B%24B0lFs%3B0%3BM8%5EO%3B%3C7H%2C%1B%28B%2Etxt
+------=_NextPart_000_000C_01D76CF7.585CFD10
+Content-Type: Text/Plain;
+       name="=?iso-2022-jp?B?GyRCMGxGczswO004Xk87PDdILBsoQi50eHQ=?="
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment;
+       filename="=?iso-2022-jp?B?GyRCMGxGczswO004Xk87PDdILBsoQi50eHQ=?="

-ファイル名: 一二三四五六七八.txt
+g3SDQINDg4uWvDogiOqT8Y5PjmyM3JhajrWUqi50eHQNCg==

-----Next_Part(Tue_Jun_29_14_58_42_2021_099)----
+------=_NextPart_000_000C_01D76CF7.585CFD10--
```


- (まれに発生)添付ファイルのファイル名やSubjectがBase64(=?charset?B?.....?=)
  からQuoted Printable形式 (=?charset?Q?=16進数...?=)に修正．

- base64の幅が72文字の場合は76文字に修正．(これがあるのでdiff取る時は注意)

補足:diffとる時は次のスクリプトが役にたつかも．BASE64の添付ファイル部分を削ります。ファイルを書換えるのでバックアップ後実施ください．

```perl:strip-base64.pl
#!/usr/bin/perl
#
# 頭が悪い実装．

#72文字-76文字の行が5行続いたらBASE64と判断

for my $i (@ARGV){
    next unless(-f $i);
    my $bakfile = $i . ".bak";
    rename ($i , $bakfile) or die;
    open(FILE, "< ", $bakfile) or die;
    my $tmp=join("",<FILE>);
    close(FILE);
    open(FILE, ">", $i) or die;
    #print $tmp;
    $tmp =~ s/\n[\da-zA-Z\+\/]{72,76}\n[\da-zA-Z\+\/]{72,76}\n[\da-zA-Z\+\/]{72,76}\n[\da-zA-Z\+\/]{72,76}\n[\da-zA-Z\+\/]{72,76}\n[\da-zA-Z\+\/\=\n]+/\nStripBASE64\n/g;

    print FILE $tmp;
    close(FILE);
}
```

以上です。