Rails(TMail)で携帯のメールアドレスをパースするとエラーになる件の対応方法について
Ruby(Rails)で携帯の写メールを受け取るプログラムを作っていたら、ドット(.)が連続するメールアドレスでエラーになった。本来はRFC違反だから使ってはいけないんだけど、DoCoMoとかauでは使えてしまうので対応する必要がある。
対応方法をまとめてみた。
※ただし、この方法で対応できるのは"example...@ezweb.ne.jp"などの先頭がドットで始まらないメールアドレスのみ。先頭がドットで始まっているメールアドレスの場合は、対応出来ていない事に注意。それでもこの方法は実行する価値があると思う。
何が問題なのか
まず、これで何が問題になるのかについて軽く説明する。
以下のようにドットが3回連続するなどRFCに違反しているメールアドレスをTMailで処理しようとすると
メールアドレスがnilになってしまう。FromだけでなくToなどメールアドレスであればおそらく全て同様。
TMail(ActionMailer)を使ってメールを送信したり、メールを受信してFromアドレスへメールを返信する
などの処理が出来ない。
$ irb irb> require 'rubygems' irb> require 'action_mailer' irb> mail = TMail::Mail.new irb> mail.from = 'example...@exampl.com' irb> mail.from => nil ← 設定したメールアドレスがnilになってしまう!
対応方法は?
対応方法は2通りある。
- パターン1
- 既存のparser.rbを新しいparser.rbで上書きする
- パターン2
- アプリケーションのディレクトリ内にparser.rbを別ファイル名で置く(tmail_parser.rbなど)
- TMailを使用するソース内でParserを新しいparser.rbと置き換える
ここでは、既存の環境を壊したくないのでパターン2の方法を使う。
1. Raccをインストールする
parser.yをraccコマンドでコンパイル?してTMailが使用しているparser.rbを作成する事になるため、raccをダウンロードしてインストールしておく。
# wget http://i.loveruby.net/archive/racc/racc-1.4.5-all.tar.gz # tar xvzf racc-1.4.5-all.tar.gz # cd racc-1.4.5-all # ruby setup.rb
2. TMailをrubygemsのサイトからダウンロードする
"rubygemsのサイト"から自分が使用しているバージョンのTMailをDLする。Railsを使っている場合、TMailを個別にgem installしていないはずなので、ActionMailerのディレクトリ内を確認する。
Rails2.1.0しか入っていない環境の場合 $RUBY_HOME/lib/ruby/gems/1.8/gems/actionmailer-2.1.0/lib/action_mailer/vendor/tmail-1.2.3
3. perser.yを編集してドットが連続しても対応できるようにする
ダウンロードしてきたtmail-{version}.tgzを展開して、tmail-{version}/lib/tmail/parser.yを編集する。
以下はtmail-1.2.3のparser.yと編集後のファイルのdiff。
他のバージョンでも同様に編集すれば良いはず。
- | local_head '.' { val[0].push ''; val[0] } + | local_head dots { (val[1] + 1).times { val[0].push '' }; val[0] } - dots : '.' { 0 } - | '.' '.' { 1 } + dot_repeat : '.' + | dot_repeat '.' { val[0] + val[1] } + + dots : dot_repeat { val[0].size - 1 }
4. Raccでparser.yからparser.rbを作成する
racc ./parser.y -E -o ./tmail_parser.rb
5. 作成したtmail_parser.rbを{RAILS_ROOT}/lib/tmail_parser.rbに置く
6. TMailを使用するソース内でparserをtmail_parserに置き換える
TMail.instance_eval{remove_const 'Parser'} require 'tmail_parser' mail = TMail::Mail.parse(mail_data) ※mail_dataは受信したメールの内容