メールリーダーThunderbirdの添付ファイル名にゴミが混ざる

#はじめに
メールリーダーThunderbird(78.11.0/Windows10)でメールを受信したときに、ときどき添付ファイル名にゴミが混ざります。他のメールリーダーでは正常に表示されるので、どうやらThunderbirdの問題のようです。

具体的には、こんな感じで、ゴミ(&#xfffd;)が混ざります

「20210702_福岡県福岡市博多駅&#xfffd;地下.xlsx」

> ![ThunderbirdSubjectファイル名文字化け.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61370/a99a7c4b-ac70-9fae-f5ee-8bfd44ffde17.jpeg)


同じファイル名でもゴミが混ざらない場合があります。それでメールのソースをみて差分を調べてみました。

補足: 本ブログの実験ですが、最後でメールのソースの直接書換をやっています。手順が非常に煩雑なため、省略しました。メールのファイル配置をご存じでMewにお詳しい方なら説明しなくても可能かと思います。

#ゴミが混ざる条件(調査1)
添付ファイルのファイル名の文字コードは多くの場合、JIS(iso-2022-jp)もしくはUTF-8です。

そして、RFC2231エンコーディングされています。時々RFCに準じてないBエンコーディングの場合があります。

ゴミが混ざるのはJIS+Bエンコーディングの時によく起きるようです。少なくとも私の確認した範囲では。

#(余談)Bエンコーディング
Bエンコーディングについて、すでにご存じの方は読み飛ばしてください。

例えばファイル名が「ABあいUEO.txt」のメールの場合、JISでBエンコーディングで記入されたメールのソースをみるとfilenameは次のようになってます。

```
Content-Disposition: attachment;
 filename="?iso-2022-jp?B?QUIbJEIkIiQkGyhCVUVPLnR4dA==?="
```

ここで「 =?iso-2022-jp?B?」がBエンコーディングの始まり指定で、「?=」が終了指定です。

なのでファイル名の本文は

> QUIbJEIkIiQkGyhCVUVPLnR4dA==

です。上記はBASE64エンコーディングといいます。3バイトの8bitデータを4個のアスキーコードに置き換えるエンコーディングです。詳細はこちらをご覧ください。

https://ja.wikipedia.org/wiki/Base64

このBASE64をdecodeしてみますと以下のようになります。ESCはASCIIの27番です。＄は全角になってますが本当は半角です。空白は読みやすいように加筆したものです。

> A B ESC ＄ B ＄ " ＄ ＄ ESC ( B U E O . t x t

ここで「ESC ＄ B」という並びはJISコードの始まりという意味です。 「ESC ( B」という並びはJISコードの終わり(ASCIIコードの始まり)という意味です。読みやすくするとこうなります。

> A B  (JIS開始) ＄ " ＄ ＄ (JIS終了) U E O . t x t

次に「＄ "」はJISで「あ」です。「＄ ＄」はJISで「い」です。なのでまとめると

> A B あ い U E O . t x t

になります。

#ゴミが混ざる条件(調査2)

他の文字化けしている場合と文字化けしてない場合の比較調査を行ってみました。

ファイル名が文字化けする場合は、

- Bエンコーディングでfilenameが2行に分割されている。改行したところにゴミが混ざります。
- Bエンコーディングは各行が4の倍数になります。4の倍数に足りない場合は最後に「=」を挿入します。文字化けしている場合は、filenameの1行目が最初から4の倍数で、「=」の挿入が無かったです。

先ほどの文字化けしたファイル名「20210702_福岡県福岡市博多駅&#xfffd;地下.xlsx」は以下のようにBエンコーディングでした。

```
Content-Disposition: attachment;
 filename="=?ISO-2022-JP?B?MjAyMTA3MDJfGyRCSiEyLDgpSiEyLDtUR25CPzFYGyhC?=
 =?ISO-2022-JP?B?GyRCQ08yPBsoQi54bHN4?="
```

上記例では「=?ISO-2022-JP?B?」がBエンコーディング開始。「?=」がBエンコーディング終了です。そのためBエンコーディング本体は

> MjAyMTA3MDJfGyRCSiEyLDgpSiEyLDtUR25CPzFYGyhC
> GyRCQ08yPBsoQi54bHN4

です。filenameの1行目は4の倍数であり、最後に「=」がないです。

補足: 余談で挙げたファイル名「ABあいUEO.txt」は、4の倍数ではないため

> QUIbJEIkIiQkGyhCVUVPLnR4dA==

最後に「=」が二つ挿入されています。

#ゴミが混ざる条件(実験)
まず先ほどの仮定を確認してみます。文字数調整のため頭にアルファベットを挿入していきます。WindowsのメールリーダーSylpeed3.7.0では、設定でファイル名の生成方法をRFC2231エンコーディングからRFCに準じてないBエンコーディングに変更できます。

>全般の設定→送信→エンコーディング→MIMEファイル名エンコーディング
標準のRFC2231からMIMEヘッダに変更してください。

その後以下のファイル名で送信してみます。

「非常にとても長い日本語ファイル名です.txt」
「A非常にとても長い日本語ファイル名です.txt」
「AA非常にとても長い日本語ファイル名です.txt」
「AAA非常にとても長い日本語ファイル名です.txt」

結果は以下のとおりです。

正常表示: 「非常にとても長い日本語ファイル名です.txt」(4の倍数ではない)

```
Content-Disposition: attachment;
 filename="=?ISO-2022-JP?B?GyRCSHM+byRLJEgkRiRiRDkkJEZ8S1w4bCVVJSEbKEI=?=
 =?ISO-2022-JP?B?GyRCJSQla0w+JEckORsoQi50eHQ=?="
```


ゴミ混入：「A非常にとても長い日本語ファ&#xfffd;イル名です.txt」(4の倍数)

```
Content-Disposition: attachment;
 filename="=?ISO-2022-JP?B?QRskQkhzPm8kSyRIJEYkYkQ5JCRGfEtcOGwlVSUhGyhC?=
 =?ISO-2022-JP?B?GyRCJSQla0w+JEckORsoQi50eHQ=?="
```

正常表示：「AA非常にとても長い日本語ファイル名です.txt」(4の倍数ではない)

```
Content-Disposition: attachment;
 filename="=?ISO-2022-JP?B?QUEbJEJIcz5vJEskSCRGJGJEOSQkRnxLXDhsJVUbKEI=?=
 =?ISO-2022-JP?B?GyRCJSElJCVrTD4kRyQ5GyhCLnR4dA==?="
```

ゴミ混入：「AAA非常にとても長い日本語フ&#xfffd;ァイル名です.txt」(4の倍数)

```
Content-Disposition: attachment;
 filename="=?ISO-2022-JP?B?QUFBGyRCSHM+byRLJEgkRiRiRDkkJEZ8S1w4bCVVGyhC?=
 =?ISO-2022-JP?B?GyRCJSElJCVrTD4kRyQ5GyhCLnR4dA==?="
```

概ね推測通りです。

#ゴミが混ざる条件(実験2)

もうちょっと短いファイル名で試してみます。filenameを２行に分割する必要がありますが、これはメールリーダーの標準設定ではできません。メールのソースのfilenameを直接編集する必要があります。ですがfilenameを直接修正するのは少々手間です。結果だけ記載しておきますので、手順がわかる方のみ試してみてください。

(ヒント: 適当なメールを作成しMewでローカルフォルダのinboxにファイルをコピーしてごにょごにょして、Message-IDとDateを書き換えて、sでフォルダ更新して、rでresentすれば。。)

まず、以下のファイル名を生成してみます。(改行)の所でfilenameのBエンコーディングを改行します。

「あ(改行)いうえ1-3.txt」
「あい(改行)うえ2-2.txt」
「あいう(改行)え3-1.txt」

```
$ echo -n "あ" | nkf -j | perl -MMIME::Base64 -0777 -ne 'print encode_base64($_)'
GyRCJCIbKEI=

$ echo -n "あい" | nkf -j | perl -MMIME::Base64 -0777 -ne 'print encode_base64($_)'
GyRCJCIkJBsoQg==

$ echo -n "あいう" | nkf -j | perl -MMIME::Base64 -0777 -ne 'print encode_base64($_)'
GyRCJCIkJCQmGyhC

$ echo -n "いうえ1-3.txt" | nkf -j | perl -MMIME::Base64 -0777 -ne 'print encode_base64($_)'
GyRCJCQkJiQoGyhCMS0zLnR4dA==

$ echo -n "うえ2-2.txt" | nkf -j | perl -MMIME::Base64 -0777 -ne 'print encode_base64($_)'
GyRCJCYkKBsoQjItMi50eHQ=

$ echo -n "え3-1.txt" | nkf -j | perl -MMIME::Base64 -0777 -ne 'print encode_base64($_)'
GyRCJCgbKEIzLTEudHh0
```

上記結果をもとにメールのソースを直接書き換えてfilenameを生成します。

結果は以下のとおりです。

正常表示: 「あ(改行)いうえ1-3.txt」→「あいうえ1-3.txt」(4の倍数ではない)

```
Content-Disposition: attachment;
 filename="=?ISO-2022-JP?B?GyRCJCIbKEI=?=
 =?ISO-2022-JP?B?GyRCJCQkJiQoGyhCMS0zLnR4dA==?="
```

正常表示:「あい(改行)うえ2-2.txt」→「あいうえ2-2.txt」(4の倍数ではない)

```
Content-Disposition: attachment;
 filename="=?ISO-2022-JP?B?GyRCJCIkJBsoQg==?=
 =?ISO-2022-JP?B?GyRCJCYkKBsoQjItMi50eHQ=?="
```

ゴミ混入:「あいう(改行)え3-1.txt」→「あいう&#xfffd;え3-1.txt」(4の倍数)

```
Content-Disposition: attachment;
 filename="=?ISO-2022-JP?B?GyRCJCIkJCQmGyhC?=
 =?ISO-2022-JP?B?GyRCJCgbKEIzLTEudHh0?="
```

結果として推測通りでした。

#補足
ゴミ(&#xfffd;)に割り当てられているコードですが、添付ファイルを開こうとすると、割り当てられている文字コードが(U+FFFD)であることがわかります。
> ![文字化け.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61370/0b6e79b8-bc04-f3da-8a62-f0345a9f5ea2.jpeg)

これはUNICODEの「replacement character」のようです。 
https://ja.wikipedia.org/wiki/Specials_(Unicode_block)
以上です。