TMail パーサのデバッグ方法

あれ!? TMail のメール解析がおかしいぞ!? というときは、パーサまで手を入れないといけないかも知れない。そのデバッグの仕方を書いておく。

パーサのデバッグフラグをONにする

TMailを使うスクリプトの中で、

require "tmail"

TMail::Parser.const_set(:MAILP_DEBUG, true)    # <== ココ
mail = TMail::Mail.parse(IO.read(mailfile))

こうするとデバッグ情報が出力される。また通常は、ヘッダの中身を解析していてエラーが起こった場合は、通常何も出力されずに無視されて、エンコード時にそのヘッダが捨てられてしまう。デバッグをONにすると、中断こそしないがそのエラーも出力される。

まずこれで実行して、構文解析エラーが原因なのかどうかが調べてみるとよい。エラーメッセージだけで、対応方法が分かる場合がある。

パーサの中の、どの解析ルールが原因なのかを調べないといけない場合は、次のステップに進もう。

raccのインストール

gem install racc

デバッグ情報付きパーサの生成

TMailのパーサは、 lib/tmail/parser.rb である。これは、ルール定義ファイル parser.y から、racc によって自動生成される。

cd tmail-1.2.3.1
racc -t -o lib/tmail/parser.rb lib/tmail/parser.y

オプション"-t"はデバッグ情報付きのパーサが生成される。

ふたたび実行

デバッグ情報入りのパーサだと、豊富な情報がたくさん出てくる。

(例)解析部分:

Content-Type: multipart/mixed; 

(例)出力:

read    :CTYPE(CTYPE) :CTYPE
shift   CTYPE
[ (CTYPE :CTYPE) ]
goto    12
[ 0 12 ]
read    :TOKEN(TOKEN) "multipart"
shift   TOKEN
[ (CTYPE :CTYPE) (TOKEN "multipart") ]
goto    62
[ 0 12 62 ]
read    "/"("/") "/"
shift   "/"
[ (CTYPE :CTYPE) (TOKEN "multipart") ("/" "/") ]
goto    99
[ 0 12 62 99 ]
read    :TOKEN(TOKEN) "mixed"
shift   TOKEN
[ (CTYPE :CTYPE) (TOKEN "multipart") ("/" "/") (TOKEN "mixed") ]
goto    126
[ 0 12 62 99 126 ]
reduce  <none> --> params
[ (CTYPE :CTYPE) (TOKEN "multipart") ("/" "/") (TOKEN "mixed") (params {}) ]
goto    136
[ 0 12 62 99 126 136 ]
read    ";"(";") ";"
shift   ";"
[ (CTYPE :CTYPE) (TOKEN "multipart") ("/" "/") (TOKEN "mixed") (params {}) (";" ";") ]
  • read TOKEN_CONTENT … スキャナがトークンとその中身 TOKEN_CONTENT を読み取った。(パーサは、スキャナによって文字列をトークンに分解したものを処理する)
  • shift TOKEN … パーサが次のトークン TOKEN を引っ張ってきた。
  • goto RULE … 他のルール RULE に飛んだ
  • reduce FROM --> TO … ルール FROM からルール TO に戻ってきた。
  • [ (CTYPE :CTYPE) ... ] … 解析している階層のトレース(うまく説明できない)

パーサを修正する

原因となるルールが特定できたら、 lib/tmail/parser.y を修正する。(またはデバッグ出力を入れる)そして再び上記のraccを実行して、再度実行してみる…を繰り返す。

修正が完了したら、

cd tmail-1.2.3.1
racc -E -o lib/tmail/parser.rb lib/tmail/parser.y

デバッグ情報は取り除こう。ちなみに -E はraccライブラリを parser.rb に埋め込むスイッチ。