このイコール記号(=)はいったい何?
(lars.ingebrigtsen.no)- 最近Twitterで古いメールの引用文が広まり、文末の イコール記号(=) がなぜ現れるのかという疑問が提起された
- この記号は
quoted-printableエンコーディング の過程で生じるもので、長い行を強制的に分割するときに行が続くことを示すために使われる - メール送信時には CRLF(キャリッジリターン+ラインフィード) を改行として使うが、これを Unix の NL に変換する際にデコードアルゴリズムが誤作動すると、イコール記号が残ったり文字が失われたりする
- イコール記号は改行以外にも 非ASCII文字(例: =C2=A0) を表すために使われ、誤ったデコーダがこれを単純置換するとエラーを引き起こす
- 問題の原因は バグのあるデコードロジックと不適切な変換処理 にあり、メールを加工した人が技術的に未熟だったことを示している
メール引用文中のイコール記号(=)の正体
-
ここ数日、Twitterで 古いメールの引用文 が多数共有され、文末のイコール記号が目立つ現象が発生
- 筆者は、これを コードやOCR(光学文字認識)の誤り だと誤解する主張に反論
- 実際には、メールを読みやすく変換する過程で生じた エンコーディング処理のエラー である
-
メールは以前は単純なテキストだったが、長い行や特殊文字を扱うために
quoted-printableエンコーディング が導入された- 長い行を分割するとき、行末にイコール記号(=)を付けて「この行は続く」という意味を示す
- このとき、イコール記号の後には CRLF(キャリッジリターン+ラインフィード) が付く
改行エンコーディングとデコードエラー
-
メールサーバーは CRLF 改行を標準として使うが、Unix 系システムは NL だけを使う
- 変換過程で1バイト減り、デコーダがこれを誤って処理するとイコール記号が残ったり文字が抜け落ちたりする
- 例として、「non- =CRLF cloven」が誤って処理されると「non- loven」のように
cが消える
-
一部の実装は、行末のイコール記号を見つけると2文字を削除する方式で処理する
- このアルゴリズムが Unix 形式のファイルでは誤作動し、イコール記号がそのまま残る現象が起きる
イコール記号のもう一つの用途: 非ASCII文字エンコーディング
-
イコール記号は改行以外にも 非ASCII文字エンコーディング に使われる
- 例:
=C2=A0は non-breaking space(改行しない空白) を意味する - メール本文でインデントや特殊文字の表現時によく現れる
- 例:
-
筆者は、一部の変換処理が
=C2,=A0などを単純な置換(search-replace)だけで処理し、正しいデコーダを使わなかった と推測している
技術的背景と標準
-
RFC 2045 標準は quoted-printable エンコーディングを 転送用(transport) と定義している
- 受信後はデコードされて きれいなテキスト として保存されるのが原則
- しかし実際の実装ではこの工程が省略され、改行処理のエラーが頻発する
-
例示コードの
(quoted-printable-decode-string "he=\nllo")は"hello"に正常復元される- これは SMTP サーバーの文脈で CRLF を前提としたアルゴリズムを再利用しているため
- Windows ベースのファイルでは正常に動作するが、Unix ベースでは失敗する
結論
- メール引用文中のイコール記号は quoted-printable エンコーディングの残骸 であり、
改行処理と非ASCII文字デコードの欠陥 が組み合わさった結果である - 問題の根本原因は 不正確なデコーダ実装とエンコーディング変換ミス にある
- 筆者はこれを「技術的な問題であり、誤った処理の結果」だと要約し、
メール変換過程で標準を細かく順守する必要性を強調している
1件のコメント
Hacker Newsのコメント
この記事の中心人物は Lars Ingebrigtsen で、Emacs のメール/Usenet リーダーパッケージである Gnus のマニュアルを書いた人物とのこと
彼のマニュアルは機知に富み有益で、メールのパースについて大半の人よりはるかに深い理解を持っている
マニュアルはこちらで読め、別バージョンはこのリンクにある
UiO(オスロ大学)で彼が最初に Gnus を作っていた頃を覚えているという話
情報学科の学生たちの間ではちょっとした スター開発者 で、みんな Emacs と Gnus を使っていた
これは「危険なほど知っている人」の典型例だという意見
メールが単なるプレーンテキストではないことは知っていたが、quoted-printable のデコードを単純な置換で処理してはいけないことまでは知らなかった、という話
正規表現で HTML を直接パースするバグと同類で、最初はうまくいっているように見えるが、あとで 議会の証拠資料に ‘=’ 記号が大量に残る 事態になる
「メールサーバはなぜ長い行を嫌うのか」という質問があった
サーバはヘッダをパースする必要があるため、単純なバイナリ blob として扱えない
IMAP はサーバ側で完全なパースが必要で、POP3 は単一デバイス向けなので今では合わない
RFC 821 は行長を最大 1000 バイトに制限しており、互換性のため 80 文字以下で区切るのが一般的だった
そのため Base64 エンコードにも 76 文字ごとに改行 が入る
たとえば PDP-11 は 512KB、VAX-11 は 2MB 程度で、プログラマたちは バイト単位でメモリを計算 していた
HELO,MAIL FROM,RCPT TO,DATAなどで通信する構造を説明している関連文書は IBM 公式ドキュメント と Wikipedia で見られる
最初はこの記事が
= == === .=. <== ==> <<== ==>> (==) => =~=のような 演算子の意味 の話かと思った、というコメント個人的にメールアーカイブ用ソフトウェアを自作したことがあるという人もいた
20年以上蓄積された .eml ファイルの エッジケース処理 がいちばん難しかった
概念は単純でも、メールは驚くほど複雑だという
メールアドレスの妥当性検証も、実質的には不可能に近い
数年間少数のユーザーはいたものの、MIME 処理 が最大の苦痛だった
興味深かったのは ‘=’ 記号そのものより、その周辺の 文字が消える現象 だったという意見
off-by-one エラーのように、‘=’ を消す代わりに実際のテキストの一部が失われたように見える
おそらく CRLF/LF 変換が関係しているのではないかという
なぜこうした問題が 今になって 表面化しているのか気になった、というコメント
ここ数日、人々が古いメールを Twitter に投稿しているが、何が理由なのかと思っていたという
原因は Gmail ではなく 中間サーバでの変換 の可能性がある、という指摘もあった
CRLF→LF 変換に加え、quoted-printable を二重適用すると ‘=’ が残る現象が起こるため、2台のメールサーバが関与していた可能性がある
実際には 非専門のインターン が単純なツールでデータを集め、何度も変換されるうちにフォーマットが壊れる
元データはすでに破棄され、残るのは 形だけ残った断片データ だけになる
archive.today の記事にも同じ quoted-printable の破損現象 が見られる
関連リンクは pastes.io/correspond と HN スレッド
Outlook でダウンロードしたメールを見る際、quoted-printable を自動でデコードしてくれる .eml ビューア があるとよい、とのこと