発見した TMail-1.2.3.1 のバグ各種
TMail-1.2.3.1 のバグを色々直した。それぞれ詳しいことは以下。各修正を適用したTMailのgemをダウンロードに置いてあるので、手っ取り早く使いたい方は、これをダウンロードして、 gem install tmail-1.2.3.1.gem でインストールしてほしい。
ついでにこの gem には、leave a note [message] behind on Rails: RailsのMailer(TMail)のメールアドレスドット問題の修正も取り込ませていただいた。
正しく動作する保証は無いですが、不具合があればコメントしていただけると助かります。
Content-Typeで値がクォートを含んだまま取り込まれる
TMailプロジェクトに送信したパッチ: RubyForge: TMail: Modify: 23165 - ContentTypeHeader should unquote parameter's value on encode
例えば
Content-Type: image/jpeg; name="\e$B4A;z\e(B.jpg"
という生JISのファイル名がクォートされて指定されていると、なぜかエンコードした後に、
Content-Type: image/jpeg; name*=iso-2022-jp'ja'%22%1b$B4A%3bz%1b%28B.jpg%22
%22(")が前後にくっついてしまう。本当は、
Content-Type: image/jpeg; name*=iso-2022-jp'ja'%1b$B4A%3bz%1b%28B.jpg
でないといけない。
AppleMail 添付ファイルパートの Content-Type が解析エラー
TMailプロジェクトに送信したパッチ: RubyForge: TMail: Modify: 23681 - Fix parse error on AppleMail's bad Content-Type parameter value - unquoted but bencoded
AppleMailに日本語ファイル名を添付させてみよう。するとこんなパートが生成される。
--Apple-Mail-1-993553537 Content-Transfer-Encoding: base64 Content-Type: application/pdf; x-mac-type=50444620; x-unix-mode=0644; x-mac-creator=4341524F; name==?ISO-2022-JP?B?GyRCQXdFRTdPRX0/XhsoQi5wZGY=?= Content-Disposition: inline; filename*=ISO-2022-JP''%1B%24BAwEE7OE%7D%3F%5E%1B%28B.pdf
これをパースし、このパートのcontent_typeを得ようとすると
part.content_type => nil
nilが帰ってくる。さらに、encodedでエンコードすると、
--Apple-Mail-1-993553537 Content-Transfer-Encoding: base64 Content-Disposition: inline; filename*=ISO-2022-JP''%1B%24BAwEE7OE%7D%3F%5E%1B%28B.pdf
Content-Type そのものが消えてしまう。(メーラ上の動作では、本文として展開されてしまう)
実は、Content-Type をパースする際に構文エラーが発生しているのだ。 name==?ISO- の2つ目の = に出会った時点で、これは有効なトークンではないとエラーになってしまう。確かに、RFC 2045(対訳)多目的インターネットメール拡張 パート1 Content-Type ヘッダフィールドの文法 によれば、これはクォートしないといけない。 AppleMail が間違っているのだ。
Content-Type をパースする前に、このようなパターンを見つけてクォートさせてやることで解決した。
ちなみに、AppleMailに長いファイル名で添付をさせると
Content-Type: application/pdf; x-unix-mode=0644; name="=?ISO-2022-JP?B?GyRCRDkhPCQkRDkhPCQkRDkhPCQkRDkhPCQkGyhC?= =?ISO-2022-JP?B?GyRCRDkhPCQkRDkhPCQkRDkhPCQkRDkhPCQkGyhC?= =?ISO-2022-JP?B?GyRCRDkhPCQkRDkhPCQkRDkhPCQkRDkhPCQkGyhC?= =?ISO-2022-JP?B?GyRCRDkhPCQkRDkhPCQkRDkhPCQkGyhCLnBkZg==?="
ちゃんとクォートされる。1行で収まる場合のみ、クォートしてくれないらしい。
TMailで作成した添付ファイル名の文字化け
TMailで添付ファイル付きのメールを1から作成しようとすると、このようなスクリプトになるが、
filename = "任意の日本語ファイル名" # JISにすること filedata = "ファイルのデータ" require "base64" require "tmail" mail = TMail::Mail.new() part = TMail::Mail.new() part.transfer_encoding = "7bit" part.set_content_type('text', 'plain', 'charset'=>'iso-2022-jp') part.body = "honbun." mail.parts << part part = TMail::Mail.new() part.set_content_type('application', 'octet-stream', 'name' => filename) part.set_disposition("attachment") part.transfer_encoding = "base64" part.body = Base64.encode64(filedata) mail.parts << part mail.encoded
filename によっては、途中でファイル名が化けたりする。
例えば、「何かチラシ.pdf」をこのスクリプトで作成すると、メーラで開いたときにはファイル名が「何か汁初酬.pdf」になってしまう。まあ笑えるからいいか、ってんなわけない。
このヘッダを見ると
Content-Type: application/octet-stream; name*=iso-2022-jp'ja'%1b$B2%3f$+%A%i%7%1b%28B.pdf
となっている。原因は、エンコード時にJIS文字列中の % をエスケープしてくれてないからである。正しくは、
Content-Type: application/octet-stream; name*=iso-2022-jp'ja'%1b$B2%3f$+%25A%25i%257%1b%28B.pdf
でないといけない。
encode.rb の encode_value の中で、 TOKEN_UNSAFE という正規表現を %xx に置き換えている。TOKEN_UNSAFEの宣言は utils.rb にある。それが使っている値である、 aspecial/tspecial に % を追加して修正した。
しかし、これだと他のメソッドにも影響が出そうだが…。ちょっと不安である。それに aspecial/tspecial の定義が、 RFC と違ってきてしまう。
このパッチはトラッカーにsubmitしていない。パッチは以下の通りである。
Index: lib/tmail/utils.rb =================================================================== --- lib/tmail/utils.rb (リビジョン 261) +++ lib/tmail/utils.rb (作業コピー) @@ -109,8 +109,8 @@ # It also provides methods you can call to determine if a string is safe module TextUtils - aspecial = %Q|()<>[]:;.\\,"| - tspecial = %Q|()<>[];:\\,"/?=| + aspecial = %Q|()<>[]:;.\\,"%| + tspecial = %Q|()<>[];:\\,"/?=%| lwsp = %Q| \t\r\n| control = %Q|\x00-\x1f\x7f-\xff|