開発しているシステムの.NET バージョンを4.5にしたらメール件名が文字化けしたので、エンコードを2回するコードを書いた全世界100万のソフトウェアエンジニアの皆さん、こんにちは。 MNTSQ株式会社でバックエンドエンジニアをしている沼井です。
弊社が提供しているSaaSである「MNTSQ CLM」は、多数のエンタープライズ企業へ導入されています。その結果、契約書はもちろん、それに付随する多くのドキュメントをデータとして扱う必要があります。 そのドキュメントにはメールも含まれており、メールは企業の(契約をはじめとする)業務ナレッジを含む重要な資産となります。 そのため、MNTSQではこれから送られるメールはもちろん、過去のメールについてもナレッジとして取り込み、活用できる機能を提供しています。
崩れたメールを語る
ここまで読んで鋭い方はお気づきと思いますが、過去から長年蓄積されてきたメールをきちんと取り扱えるようにするには、様々な配慮が必要です。
- 標準的なメールファイル(emlファイル)だけでなく、Microsoft Outlookからエクスポートされたmsg形式等のメールも取り扱う
- 多種多様な文字コードが使用されたメールを取り扱う
- RFCに沿ってないフォーマットのメールを取り扱う
とくに「RFCに沿ってないメール」については、メーラー(MUA)と送信サーバ(MTA)のRFC非準拠の実装、不具合その他によって引き起こされているのが実情かと思います。
もちろん完全に崩れて読み取れないもの(それはメールといえるのか?)を救出することはできないのですが、ヘッダーのごく一部だけがRFC非準拠のフォーマットである、かつそれが頻発しているようなケースについては、 可能な範囲で救う必要があります。
ところで私はこうしたデータに向き合うとき、最新のRFCや最新のメール処理ライブラリの挙動だけをもとに、過去のメールファイルの扱いづらさを安易に断じるような姿勢をとるべきではないと考えています。長年の歴史のなかで複数のソフトウェアが介在して作成・転送・蓄積されたデータというのは、本質的にこうした問題をはらみがちで、それはソフトウェアの利用者の責任に帰すべきではないと思っています (一方で、未来においてこうした問題が起きにくいプロトコルやデータフォーマットを検討・提案していくことは、ITエンジニアの重要な責務としてあると思っています)。
閑話休題で、以下では、RFC準拠のメールパーサーライブラリを使っているだけではエラーがおきたり、テキストを正しく表示できないようなメールを「崩れたメール」と呼びつつ、私達がどのようなメールに直面し、対処してきた(あるいは対処できなかった)かというナレッジを、一部ですが共有したいと思います。
文字化け
メールデータで問題が起きるというと、やはり文字化けのことが思い浮かぶ方が多いと思います。
文字化けといっても実情は多様です。 件名、本文、添付ファイル名のそれぞれについて個別に文字化けが起き得るし、また、原因としても以下のような種類があります:
Content-Typeヘッダーのcharset名指定(どの文字エンコーディング方式を使ったか) が、実際に使われたエンコーディングと異なる
- 例1: 実際はcp1250 (windows-1250)でエンコーディングされているが、charset としてはiso-1250 という名前が指定されている
- 例2: 実際はcp932でエンコーディングされているが、charsetとしては shift_jis という名前が指定されている (ようするに、機種依存文字がまざっちゃってる、ということですね)
- 対処としては、エンコードエラーがおきたときに、正しい(かもしれない)charset名指定でリトライするという方法があります
使用しているプログラミング言語処理系/ライブラリでそもそも対応していない文字エンコーディング方式が使われている
- 例: ISO-2022-KRがRubyで取り扱えない
メールも含めた文字コードおよび文字化けの理解のためには、「プログラマのための文字コード技術入門」が非常に有用なため、一読をおすすめします。
Referencesヘッダーが壊れている
これはかなり見かけるケースです。 メールの送信・返信を繰り返すたびにデータ(メッセージID)が追記されていくため、「複数のメーラーが介在し、どこかのメーラーが1つでも正しくないフォーマットで追記するとエラーになる」「ヘッダー行がどんどん長くなっていった結果、一定長をすぎたときに問題が発生してしまう」という少なくとも2つの要因がありそうだと考えています。 以下、例です:
References: <ABCDEF@aaa.example.com> <BCDEFG@bbb.example.com> (中略。1000文字くらい) <CDEFGH@c cc.example>
- ヘッダー行が1000文字を超えたあたりで、余計なスペースが入り込んでいます
- これは、メールの1行998文字制限を超えたことでおきてしまう問題と思われます (RFC5322 2.1.1)
- 対処としては、メッセージIDのなかには通常スペースが入りえないので、それが見つかったらカットする、という方法があります
- 参考: https://sendgrid.kke.co.jp/docs/API_Reference/SMTP_API/building_an_smtp_email.html
ヘッダは1行あたり最大1,000文字が上限となります。この制限に従わないと、中間のMTAがヘッダをスペース以外で分割してしまう可能性があります。これにより、最終的なメールにスペースが挿入されてしまう可能性があります。メールがSendGridに到達する前に別のMTAを通過する場合、最大ヘッダ長の制限がさらに厳しくなり、ヘッダが切り捨てられる可能性があります。
References: <ABCDEF@aaa.example.com> <BCDEFG@bbb.example.com>,<CDEFGH@ccc.example>
- メッセージIDの区切りの一部にカンマが混入しています
- 実際はカンマ区切りでなく、空白で区切られるのが正しいフォーマットです
- 対処としては、メッセージID外に見つかったカンマをカットする、という方法があります
実際にはこれ以外の壊れ方もいくつか見られるのですが、今回の記事はそれを書くには余白が狭すぎるため、省略します。
メールアドレスのフォーマットがRFC準拠でない
<hoge@example.co.>
や <hoge..@example.co.jp>
のような、RFC準拠でないメールアドレスが From や To ヘッダーなどに一定含まれるケースがあります (頻出ネタですね)。
対処としては、メールアドレス部分はフォーマットの検証をしないというのがあります。(このメールアドレスを使って後段でメール送信するのでなければ、文字列データとして扱えればよく、不正フォーマットであってもよい)
当たり前にあると思ったヘッダーが存在しない
From, Date などの、最低限存在すべき (RFC 5322 3.6) と定められたヘッダーが存在しないケースも存在します。 体験したことがある例としては、MIMEのマルチパートメッセージだけが残っていて、その他のヘッダーが存在しないというものです。 これについては、一般のMUAやMTAの不具合によって起きたものか? というと怪しい部分はあると思っていますが、原因追求は本題ではないので深堀りしないでおきます。
対処としては、RFC的に必須のヘッダーだとしても、無いことはありえると想定した処理をするという方法があります。
改行コード
メールメッセージの行はCRLFで区切られるはずなのですが、CRCRLFで区切られているケースが存在します。 一部のMTAを経由したときにこのような事象が起き得る、という情報を見たことはありますが、やはり今回の記事の本題ではないので深堀りしないでおきます。
対処としては、 CRCRLF を CRLF へ変換する方法があります。
最後に
本記事では、崩れたメールについて見てきました。 読んでいただいた方の参考となれば幸いです。
今回は標準的なメールファイル(emlファイル)についてのみ語りましたが、実際には Outlookのmsgファイルについても語り尽くせないほどの話題があります。 ですが、今回はここまで(腹八分目)にとどめておき、今後の宿題としたいと思います。
MNTSQでは、メールを含む歴史的経緯のあるドキュメントをリスペクトし、それに向き合うことに価値を感じられるエンジニアを募集しています。