MHonArc で電子メールを HTML 化 するとき、日本語メールを扱う際には多少コツがいります。注意点はすでに先達 によって
のような形でまとめられており(私も大変お世話になりま した。ありがとうございます)、変換後の HTML 内での日本語の表現方法で、次 の4つに大別されます。
これらによって、実用上ほとんど問題なく扱えるようになっているのですが、 あと少しだけ、付け足しのような形で気づいたことを補足しておこうと思います (と思っていたら、かなり長くなってしまいました…)。
なお、以下の記述は MHonArc 2.6.16 に基づきます。また、添付ファ イルのことは何も考えていないので、それに関する問題があるかもし れません。ご注意ください。
以下述べるほとんどの問題は、MIME 形式でない日 本語メールを扱う際のものです。最近のメーラーを使って日本語のメールを 書くと、ほとんどの場合メーラーが自動的に MIME 形式にのっとった形にしてく れるはずなので、今や MIME 形式でない日本語メールというのはほとんど流通し ていないでしょう。ですから、MHonArc を使う場合も、それが大きい問題になる ことはほとんどなく、上述の文書で考慮から漏れてしまったりしているのだと思 います。
非 MIME 形式の日本語メールを処理する際、 「生の JIS コードで書か れた文字列」の扱いが問題になるわけですが、「メール本文に書かれていた 場合」と「ヘッダに書かれていた場合」で話がちょっと違ってきます。
ここでは非 MIME 形式のメールを考えていますから、ここで述べている「本
文の生 JIS」とは、Content-Type
ヘッダによる
charset
指定がない JIS コード文字列のことです。
これに対処するオプションは MHonArc にちゃんとあって、
<DEFCHARSET>
リソースを iso-2022-jp
に指
定すれば OK です。上記 MHonArc の
日本語化 や
MHonArcで日本語のページを生成 (ゴミ箱の中の技術メモ)で既に指南されて
いる通りなので、その通りに設定すれば文字化けは起きません
(ただし、前者で述べられている手法のうち、
MHonArc の日本語化 [UTF-8
編] ではこの 修正されたようです。。<DEFCHARSET>
リソースの指定が欠けて
いますので、それを補ってください)
また、今「文字化けは起きません」と書きましたが、JIS コードを使う MHonArcの日本語化 [ISO-2022-JP 編] だけは、条件 によっては無視できない量の文字化けが発生します(後述)。
ヘッダ部に入る生 JIS が問題になるのは、主として Subject
ヘッダになるでしょう。以下でも、主に Subject
ヘッダに限定し
た話をします。
この場合、上で紹介されている 4 つの手法のうち、Subject
が文字化けせずに済むのは
MHonArcの日本語化
[ISO-2022-JP 編]のみです。私の場合この手法は、
後述する「ヘッダ・本文での文字化け
が、特に HTML パートで顕著で回避困難」という問題から見送り、いろいろ
試行錯誤した結果、別の方法で「Subject の生 JIS」問題を(ほぼ)解決しまし
た。確認したのは
MHonArcで日本語のページを生成 (ゴミ箱の中の技術メモ)に準じた EUC-JP
にする方法なので、以下ではそれを前提に述べますが、おそらく
MHonArc の日本語化
[Unicode Character Entity References 編]や
MHonArc の日本語化
[UTF-8 編]でも同様の手法が使えると思います(が、未確認です)。
選択肢は以下の 2 つです。順に説明していきます。
メールヘッダでは、MHonArc は、地の部分(MIME encoded 部以外)の文字列
は「plain
」という仮想 charset
を持つとして扱い
ます。そして、
今のように
<TextEncode>
リソースで EUC-JP コードへの変換が指定さ
れているときは、この仮想 charset
「plain
」
が指し示す「実体」の charset
を「元々の文字列の持っている
charset
」として文字コード変換ルーチンに渡し、文字列の
EUC-JP コードへの変換を行います。
仮想 charset「plain」 ↓ 実体 charset(普通は us-ascii)→ <TextEncode> → EUC-JP
標準では、この実体 charset
は us-ascii
なの
で、生 JIS Subject に対しては何も変換が起きません。が、今述べた通り、変
換処理自体はちゃんと仕込まれていますので、仮想 charset
「plain
」の指し示す実体 charset
が、
iso-2022-jp
になるようにできれば問題は解決です。それには、
<CharsetAliases>
リソースを用いれば OK です。具体的に
は、
MHonArcで日本語のページを生成 (ゴミ箱の中の技術メモ)の設定に加えて、
リソースファイルに次の指定を追加します(これだと、補助漢字や第3・4水準
の漢字が入ってるとダメ?)。
<-- 仮想 charset plain の実体は iso-2022-jp だとして扱う -->
<CharsetAliases>
iso-2022-jp; plain
</CharsetAliases>
(ちなみに、デフォルトの charset
は
charset
「plain
」us-ascii
と微妙に違っているため、
<CharsetAliases>
リソースで対処し、<DEFCHARSET>
リソースで対処するという2度手間が発生しているわけですが、これは一発
で指定できた方が合理的、というのは素人考えなのでしょうか…。個人的には、
<DEFCHARSET>
リソースを指定したら、メールヘッダでの
仮想 charset
「plain
」の実体も連動してそれにな
る、というのがスッキリしている気がします。)
さて、この方法の問題点は、Subject
以外も含めたすべて
のヘッダを、一律に JIS コードと見なして EUC-JP コードへの変換をか
けるので、メール数が多いときは処理効率に響くかもしれない(どれくらいの差
異があるのかは全然測定してません。ひょっとしたら、全然問題にならない水準
なのかも)といった点です。この点が気になるなら、次の方法をとるのも1つの
手です。
MHonArc には、元々の電子メールについていた Subject
を、
HTML 化に当たって加工できるよう、
<SUBJECTSTRIPCODE>
というリソースが設けられていま
す。上述の <CharsetAliases>
リソースの代わりに、これ
を利用して JIS コードから EUC コードへの変換を行うことができます。
例えば、上述の <CharsetAliases>
リソースの代わりに、
以下のようなリソース指定を行ってください(ここでも、
MHonArcで日
本語のページを生成 (ゴミ箱の中の技術メモ)の設定は前提とした上で、そ
れに追加で指定することに注意)。
<SUBJECTSTRIPCODE>
unless (index($_, "\033") < $[) {
require MHonArc::Char::JP;
MHonArc::Char::JP::jp_2022_to_euc(\$_) ;}
</SUBJECTSTRIPCODE>
※ 以前は Encode モジュールの from_to 関数を使う例 を挙げていましたが、この関数って単に内部で一旦 utf-8 に変換して、変換先 の文字コードに再変換するだけだったんですね…。JIS→EUC-JP 変換としては、 何て無駄なことをしているんだ(笑)。いろいろあさってみると、MHonArc 自身 にシンプルな JIS→EUC-JP 変換ルーチンがついていることが解ったので、それ を使うように記述を変更してみました。
元々この <SUBJECTSTRIPCODE>
リソースは、ML で取り
交わされるメールの Subject
の先頭につく表題部分を取り除くた
めに s/^\[someML:\s*(\d)+\]//;
みたいな感じで使うことを想定
して用意されたもののようで、それがリソース名の「STRIP
」とい
う所に反映しているのでしょうね。
残念ながら、前項で述べた 2 つのいずれの方法をとった場合も、
もし、生 JIS Subject の中に、たまたま
=?HOGE?B?FUGA?=
のように、MIME encoded 部であるかのように見えてしまうバイト列(実際は JIS コードの日本語の一部)が含まれていると、MHonArc はそこを MIME decode 処理しようとしてしまうので文字化けする
という問題が残ります(これは、 MHonArcの日本語化 [ISO-2022-JP 編]の手法を採用していた場合でさえ生じる問題です)。
電子メールの Subject に使われるような日本語の中で、たまたま そういうバイト列が発生する確率は極めて低いでしょうから、これは恐らく無 視してもよい問題なのでしょう。この問題もどうしても解決したい、というこ とだと、以下のような手法が考えられるでしょうか。
このような「悪質な」生 JIS Subject については、とにかくどんなことがあ
ろうと、MHonArc の MIME decode 処理より前に、無害な形に変換し
なければなりません。一番自然なのは EUC-JP に変換してしまうことなので、
以下では出力される HTML は EUC-JP に統一して考えます。まず、以下で説明
するように、<TextEncode>
リソースの使用は必須となり
ます。
仮に、<TextEncode>
リソースを使わなかった場合は、
MIME encode された日本語 Subject は、MHonArc 内部では必ず JIS コードの
まま扱われます。ですから、MHonArc 内部では「JIS コードの Subject」と
「EUC-JP コードの Subject(生 JIS 起源のもの)」が混在することになりま
す。このとき、次の2つの問題が生じます。
iso2022jp.pl
に含まれているものeucjp.pl
に含まれているものcharset
に対してしか正し
く動作しません(未確認)。ですから、clip 処理を正しく行うには、
MHonArc 内部での Subject の文字コードが統一されていなければならな
いのです(JIS と EUC-JP が混在していても正しく働くような clip 関数
を自作すればそれでも問題ないかもしれませんが、ここでは関数を自作す
るなど、MHonArc に手を加えるような話までは立ち入らないことにしま
す)。<NOMAILTO>
リソースを指定したり、
<SPAMMODE>
リソースを諦めるなど、HTML 書
き換え処理関連のリソースはすべて廃棄しなければいけない。そし
て、その場合でさえ、自動リンク機能に起因する僅かな文字化けの
危険性を、完全に排除できるわけではない。したがって、一般性のある解としては <TextEncode>
リ
ソースを使うしかないわけです(※注))。
さて <TextEncode>
リ
ソースを使う場合、MHonArc はヘッダを読み込むときに「一番最初に MIME
decode 処理を行う」ように作られています。よって、この場合は、
MHonArc で処理する前に、外部のコード変換ツールで生 JIS Subject
を EUC-JP 化して無害化するしか手がありません。
以上の条件をみたす手法ですが、一応次のようにすれば達成できるようです。
(1) まず、MHonArc
で日本語のページを生成 (ゴミ箱の中の技術メモ)の
euc-jp.pl
を準備します。(2) 次に、MHonArc とは独立に、何ら
かの日本語コード変換ツールを使って、対象のメール全部を JIS→EUC-JP とコー
ド変換しておきます。(3) その EUC-JP に変換したファイルに対して、MHonArc
で以下のリソースを指定して HTML に変換します。
<!-- メールヘッダで MIME encode されている日本語と、 -->
<!-- メール本体部はすべて EUC-JP にしてしまう -->
<TextEncode>
euc-jp; MHonArc::Encode::from_to; MHonArc/Encode.pm
</TextEncode>
<!-- メールヘッダの非 MIME 部分は、事前に EUC-JP に変換ずみなので、 -->
<!-- plain の実体は EUC-JP -->
<CharsetAliases>
euc-jp; plain
</CharsetAliases>
<CharsetConverters>
euc-jp; mhonarc::htmlize
</CharsetConverters>
<!-- 非 MIME のメッセージ用指定。コード変換ツールによって -->
<!-- EUC-JP に変換ずみ -->
<DEFCHARSET>
euc-jp
</DEFCHARSET>
<TextClipFunc>
euc_jp::clip; eucjp.pl
</TextClipFunc>
(<CharsetConverters>
で、
euc_jp::str2html
を指定しない理由は後述
)
この場合、(2) の段階でMIME 形式のメール本文は「charset
が
ISO-2022-JP と宣言されているのに実際は EUC-JP」という状態になり、それが
(3) で MHonArc に読み込まれ <TextEncode> リソースによっ
て JIS→EUC-JP 変換を強制されるのですが、幸い、これによる文字化けは発生しないようです。
※注
<TextEncode>
リソースを使うと、どうしても外部ツールに
頼らざるを得なかったり、後述のような問題が
あるのですが、それが嫌だ、という場合は、次のようにするとうまくいくか
もしれません(未確認)。
<DECODEHEADS>
リソースは
指定しない(でないと、<SUBJECTSTRIPCODE> リソースが働く前に、
「悪質な」生 JIS Subject 中では、ニセ MIME encoded 部で大文字が小
文字に変わってしまう可能性があるから)。iso-2022-jp
charset
に対し、「JIS コードを EUC-JP コードに変換した後
HTML 特殊文字をエスケープする」という処理を行う関数を(自作して)
設定する(plain charset
に対しては何もしない)。iso-2022-jp
を指定。上では、<TextEncode>
使用を推奨する路線で書きまし
たが、実はこのリソースを使った場合、
ある問題が発生します。これが
どうしても嫌だ、という場合は
<NOMAILTO>
リソースを強制されたり、
SPAMMODE
リソースのような「HTML 書き換えタイプ」のリ
ソースが使えなくても困らないという条件の下でなら、未確認ですが以下の手順で何と かなるでしょうか。
<TextEncode>
と
<TextClipFunc>
の設定部分を削り、
<CharsetConverters> リソース
で追加で
charset iso-2022-jp
に対し
MHonArcの日本
語化 [ISO-2022-JP 編] と同じものを設定して HTML に変換最後の EUC-JP 変換では、「JIS と EUC-JP が混在していても正しく変換で きるツール」が必要。私の使っている ack は大丈夫みたいです。最も広く使わ れている nkf が適するかどうかは知りません。
なお、最初に全部コード変換を行う関係上、clip 処理や HTML 書き換え処理 で文字化けが発生する可能性があるのは MIME encode されたヘッダ部だけ で、本文での文字化けの心配は不要です。
MHonArc の日本語化 [ISO-2022-JP 編]で述べられている方法だと、以下のような HTML 書き換え処理による文字化けを完全に防ぐことはどうしてもできませ ん。
特に、3番目については、リソースの設定でオフにすることはどうしてもで きないようです。これについては、JIS コードの日本語の文字列中に、たまたま http://〜 や ftp://〜 のように URL に見えてしまうバイト列が含まれる可能 性は極めて低いでしょうから、実害はないのでしょうが、1・2番目の方は「@」 の前後が英数字になっているだけで発生するので、害は大きいです。 本家 ML archive ページでさえ、これが原因と見られる文字化けを多数被っているく らいです。
これは、HTML 書き換えは、単純な正規表現ベースの書き換えなので、JIS コー ドの日本語文字列中の「たまたま、書き換え対象の ASCII 文字列に見えてしま うバイト列の部分」は片っ端から書き換えに逢ってしまうからです。HTML の文 字コードを JIS コードにしている限り、MHonArc での完全解決はおそらく不可 能でしょう。
なお、4番目の HTML メールについては、たとえ本文を EUC-JP などにして 文字化けを回避する場合であっても セキュリティー上の観点から避け、 HTML メールをすべて無効にした方がよいでしょう。
(ただ、私の場合、以下の理由で HTML メールの無効化は見送りました。ま
ず、ほんの数人の知り合いの間でやりとりした、内容のよくわかっているメール
を、自分達の間でだけ公開する目的だったので、セキュリティー上の考慮が不要
でした。それでも plain text alternative part を優先する設定は試してみた
のですが、1行を改行せずに長ーーーーーーーく書く癖のある人がいたため、
pre
タグで挟んでしまうと非常に読みにくくなってしまったので
す。
一見、それは <MIMEARGS> リソースで text/plain
に対
し maxwidth
引数や nonfixed
引数を与えればよさ
そうにも見えます。しかしまず、前者を試したところ、まったく改行されません
でした。どうやらこの引数は「行分割に適した空白部を見つけて改行する」とい
う動作をさせるものらしく、
NOTE: A line that contains no whitespace and is longer than
maxwidth, will not be wrapped.
と述べられています。ということは、
日本語のように空白が入らずズラズラ続く言語では、改行は行われないというこ
となのでしょう。
また、HTML メール以外の普通のメールも含む archive だったため、後者
の nonfixed
引数を与える方法はとりたくありませんでした。そ
うなると、結局 HTML パートをそのまま使うより方法がありません)
MHonArc は、mhonarc -scan -outdir /path/to/msghtmls のよう に -scan オプションを与えて「scan モード」で呼ぶことができま すが、このときは日本語 Subject がうまく表示されないことが多いです。
上述の 4 つの手法のいずれの場合も、日本語 Subject は
のいずれかの問題が発生します。まあ、scan モードを使 うことはほとんどないでしょうし、特に気にする必要もないのかもしれませんが、 前者はともかく、後者については、せっかく clipping function の指定が可能になっているのですから、それを使って文 字化けの起きないようにして欲しいところです。
公式には、
MHonArc に必要な perl のバージョンは Perl 5 ということになっているの
ですが、バージョン 5.005_03 では warnings.pm
に関するエラー
が出て動作しませんでした(詳しくはどんなエラーだったかは記録を残してなかっ
たのでわかりません)。perl 5.8.8 を入れることによって動作するようになっ
たのですが、MHonArc の開発のある段階で、単なる Perl 5 では動かないように
なってしまったのだと思います(Perl versions 5.6.1 and later are
recommended.
と書いてあるところを見ると、現行の最低必要バージョンは
これ?)。
MHonArc の標準配布物に含まれる iso2022jp.pl
ですが、よく
見るとちょっと処理が怪しげな部分があります。
jp2022_to_html
この問題は
公式側で把握さ
れ、対処が行われました。が、以下の関数 clip に対する問題は、引き続
き残っています。
複数行を含む文字列が処理対象なのですが、
という具合に、ASCII 文字で始まるすべての行の先頭に一旦
foreach (@lines) {
# a trick to process preceding ASCII text
$_ = "\033(B" . $_ unless /^\033/;
...
$ret にいろいろ付加
...
# remove a `trick'
$ret =~ s/^\033\(B//;
# add back eol
$ret .= "\n" unless (++$i >= $cnt);
...
}
ESC
( B
を付加するという処理をしています。それを必要な処理が
終わった段階で取り除こうという意図は読み取れるのですが、そこは意
図通りは動作していません。途中で処理結果を変数 $ret
に後からどんどん継ぎ足しているため、上の処理だと、先頭の
ESC ( B
が取り除かれるのは最初の行だけで(ループ中
何度も繰り返し実行されるのだけど、それが意味を持つのは最初の1回
だけ)、2行目以降の行頭は、余分な ESC ( B
がついた
ままになってしまいます(ちなみに、「
行頭に変な
文字が入ります (□(B とか)。どうして?
」という疑問の直接
の答はこれでしょう。そこにもある通り、一旦別のツールでメールを全
部 EUC-JP などに変換してから MHonArc にかけた場合、
<CharsetConverters>
リソースに
iso-2022-jp; iso_2022_jp::str2html; iso2022jp.pl
な
んて指定があると「EUC-JP のはずなのに、行頭に ESC (
B
がついている」なんて状態になってしまうわけです)。
それが規格上誤りなのかどうかはよくわかりませんし、どちらにせよ
JIS コードを正しく扱うブラウザならこれで誤動作することはまずない
はずなので別によいのですが、わざわざremove a `trick'
と意識し
たはずの意図は結局ほとんど生かされていません。
(ということは開発者はとっくに気がついていて、無害だから放置され ていた、というだけのことかもしれませんが)
clip
その1引数 $is_html, $has_tags がともに真の状態 で呼ばれたとき実行される、
if (($1 eq '<') && $has_tags) {
s/^[^>\033]*>//;
} else {
って部分は HTML タグを全部潰す意図で入れてあるはず です。しかし、もし入力に <SOME_TAG ATTR_NAME="ここは JIS コード の日本語"> ってな文字列が含まれていた場合、この正規表現には合致 しないので、結果としてその形のタグは丸通しになってしまうのではないでしょ うか?(未確認)
現時点では、この部分は使われていないとは書かれている(NOTE
The $has_tags argument is currently not used within MHonArc, but it
will likely be used in a future release.
)のですが、将来は使う予定
ということなので、そのときに問題が顕在化しそうな気がします。
clip
その2この処理だと、切り出す最後の1文字が「全角文字」(「半角文字」2 文字分の幅)だった場合、指定された長さを半角1文字分はみ出す可能性があ るように思います(未確認)。
これは、「どうせ HTML 上の表示なので、clip と言っても 1 文字分くらい のずれは細かく気にしても意味がない」と大らかな考え方で意図的に放置して あるだけかもしれないですけど(ひょっとしたら utf-8 版の clip 関数もそん なもんなのかもしれません(未確認))。
後述の eucjp.pl
にも同じ問題があるように見えますが(未
確認)、これは iso2022jp.pl
を忠実に真似て作った、というこ
となので、直すとしたらまず iso2022jp.pl
の方を直すのが筋な
のでしょう。
MHonArcで
日本語のページを生成 (ゴミ箱の中の技術メモ)で配布してくださっている
eucjp.pl
を見てみて、ちょっと気づいたことがあります。
eucjp_to_html
基本的に、&, <, > の 3 文字は &, <,
> に変換、他の文字は素通し、という動作なので、このルーチンは
mhonarc::htmlize
で代用可能なような気がします。
実際、リソースで例として挙がっている
<CharsetConverters>
euc-jp; euc_jp::str2html; eucjp.pl
</CharsetConverters>
という部分を、
<CharsetConverters>
euc-jp; mhonarc::htmlize
</CharsetConverters>
と変更した上で用いても、ほとんど変わりのない結果が
生成されました(違うのは、私が試した例では「" も " に変換
される」ことと、「</pre>直前の本文末尾の改行が削られずに残る」と
いう点、そして .mhonarc.db
に記録される「デフォルトと異な
るオプション指定内容」のみで、出来上がった HTML を普通に読む上では何ら
問題はありませんでした)。
そもそも、元になった iso2022jp.pl
で「各行に分けて個別
にループ」という処理をしているのは、JIS コードには「行の先頭は暗黙のう
ちに ASCII 状態と仮定する」という約束があるからでした。EUC-JP コードは
そういうことは必要としないので、「行ごとに分ける」処理は元々いらないよ
うに思います(ということを承知の上で、「何か見落としがあるとまずいから、
安全側に振って忠実に真似た実装をした」のかもしれませんが)。
mhonarc::htmlize
との重要な違いというと、「日本語 EUC
にはならない 8 ビットバイト列が現れた場合、そこで処理を打ち切る(その結
果、残りの文字列は丸々カットされる)」という動作なので、外部から変なデー
タを送られる攻撃に対しては euc_jp::str2html
の方がより安全、
とは言えるかもしれません。
clip
上で指摘した iso2022jp.pl
のような「タグを潰し損ねる」という問題はないと思いますが、やっぱり
ちょっと変です。
引数 $is_html が真の場合の処理
} elsif ($1 eq '&') {
my $s = $1;
$s =~ s/^([^\;]*\;)//;
$ret .= "&$s";
ですが、これだと「単に & 記号がダブって、それ
が 1 文字とカウントされるだけ」で、the length of '&' will be
1 if $is_html
ってことにはならないんじゃないでしょうか?
また、その少し後の $length--;
というのも、タグを潰した
場合まで含めて実行されるので、「タグを 1 文字分として数えている」ことに
なってしまっているようです(なので、指定した文字数より短い文字列しか切
り出されない。タグを潰す場合、文字数のカウントに関しては「なかったもの
として扱う」のが正しいはず)。
<DECODEHEADS>
は必要なのか?
MHonArcの日本語化
[ISO-2022-JP 編] や
MHonArc の日本語化
[Unicode Character Entity References 編] では
<DECODEHEADS>
リソースを指定するように指示されている
のですが、これって必要なのでしょうか?前者では
まず
MIME encode された Subject: を decode するために、<DECODEHEADS>
というリソースを設定します。
のように、「これがないと MIME
encode された日本語 Subject は読めない」という趣旨の説明があるのですが、
実際はこれがあってもなくても、<CharsetConverters> リソースの設定だ
けで日本語 Subject は読めます。
リファレンスによると、このリソースは
<CharsetConverters>
で「-decode-
」指定が
なされた charset
にしか影響を及ぼさないようですし、実際にこ
のリソースありの場合となしの場合で ISO-2022-JP 設定で結果を比べてみても、
違いが現れたのは
.mhonarc.db
に格納される SubjectX-Subject
コメント内の MIME encoded 部に限定され、その違いも
charset, encoding
の大文字小文字の違いのみでした。
可能性としては、MHonArc のあるバージョンでは
<DECODEHEADS>
指定が意味を持つことがあったけど、現在
はそうではなくなっている、というようなことがあるのかもしれません。