ブログが続かないわけ

この日記のはてなブックマーク数
Webエンジニアが思うこと by junichiro on Facebook

Return-Pathをセットする(MIME::Lite)

このエントリーを含むはてなブックマーク hateb
メール配信の仕組みなどを作ると、どうしても面倒になってくるのがエラーの処理。アプリケーションの開発を行っているとエラーの処理というのは日常的なものだ。何かを実行してエラーならまた別の何かを、例えばログを出力して終了する、ってことをしてやればよい。これは何かを実行すればすぐに成功か失敗かがわかるから単純だ。

ところがメールってやつはやっかいで、処理(送信)された瞬間には成功したか失敗したかわからない。一見成功したように見えても、しばらくしてから、「やっぱりだめだったよ...」ってな感じでエラーメールが返ってくる。つまり、このエラーメールが返ってきてから初めて失敗したことがわかり、そこでそれなりの処理をしなければならないのだ。

わかっているならそれをやればいいんだけど、いくつか考えなければならないことがある。まずは、エラーメールがどこに返ってくるかということだ。ものすごく話を単純にすれば、これは送信者に返ってくる。システムから送る場合もFromアドレスに返ってくるのだ。

このFromアドレスというのが実はさらにクセモノなんだ。メールで「Fromアドレス」と言った場合、2通りの解釈がある。「envelope FROM(1)」と「ヘッダーに書かれているFrom(2)」だ。

リアルのメールを例に説明すると、(1)は封筒に書かれている差出人の住所、(2)は念のため中の便せんに書いた差出人の住所ということになる。

ここでFromの話はいったん置いておいて、次にReturn-Pathの話をしよう。賢明な読者はご存知かと思うが、メールのヘッダーにはReturn-Pathという項目があり、そこに「エラーのときの返信先」が書けるようになっているのだ。

では、エラーメールを処理するためには、メールを送るときにヘッダーにReturn-Pathをつければいいということになるがこれがそう簡単にはいかない。Return-Pathはメールがメールサーバを経由するときに、そのメールサーバに勝手に書き換えられてしまうのだ。だから単純にヘッダーに「Return-Path: error@example.com」と書いてもだめなのだ。

では、どんなアドレスに書き換えられてしまうのだろうか?
それこそが「envelope FROM」なのだ。(2)のヘッダーのFromと勘違いしないように注意してほしい。こちらをいくら書き変えてもReturn-Pathは変わらないので、そこではまらないように。

ようやく本題であるPerlのMIME::Liteの説明に入れます。
このモジュールはメール送信の定番ですが、オブジェクトを生成するときにヘッダーなどがセットできる。
    $msg = MIME::Lite->new(
From =>'me@myhost.com',
To =>'you@yourhost.com',
Cc =>'some@other.com, some@more.com',
Subject =>'A message with 2 parts...',
'Return-Path' =>'error@example.com',
Type =>'multipart/mixed'
);
しかし、先ほどの説明でお分かりのとおり、ここでReturn-Pathをセットしても意味がない。Fromにセットされたアドレスがenvelope FROM にもヘッダーのFromにもセットされてしまうので、そのアドレスに書き換えられてしまうのだ。

やりたいことを実現するためには、FromにセットされたアドレスはヘッダーのFromに、Return-Pathにセットされたアドレスはenvelope FROMにセットされるようにしなければならない。

そのためにはMIME::Liteの挙動を見ていく必要がある。
MIME::Lite はsendmailモードで動く場合と、smtpモードで動く場合があります。呼び出し方にもよると思うが、今回はCatalystでC::P::Email::Japanese を使うことを想定する。この場合Unixのsendmail を使える環境であればsendmailモードで動作し、それ以外の場合はsmtpモードで動作するようだ。

私の環境ではsendmailモードで動作している。
sendmailモードでは最終的に
/usr/sbin/sendmail -t -oi -oem
という形でsendmailコマンドが呼び出されている。しかし、sendmail のマニュアルを見てわかったのだが、Return-Pathを有効にするためにはReturn-Path(=envelope FROM)を明示するための-f オプションが必要になる。例えば、'error@example.com' をenvelope FROMに指定したければ、
/usr/sbin/sendmail -t -oi -oem -ferror@example.com
という呼び出しが必要になる。

そのため、Return-Pathが指定されていた場合はsendmailコマンドに-fオプションが付加されるようにMIME::Liteを書き変えてみた。
--- Lite.pm.orig        2006-12-26 19:54:30.000000000 +0900
+++ Lite.pm 2006-12-27 16:59:42.000000000 +0900
@@ -2446,6 +2446,7 @@
else { ### no args; use defaults
$method = "send_by_$Sender";
@args = @{$SenderArgs{$Sender} || []};
+ $args[0] = $args[0]." -f". $self->get('Return-Path') if($self->get('Return-Path'));
}
$self->verify_data if $AUTO_VERIFY; ### prevents missing parts!
return $self->$method(@args);

はたしてこんな書き換えが許されるのかわからないが、とりあえずこれで動くようになった。

バンザイ!

[追記 2007/01/14]
いろいろな方からもっと良い方法を指摘していただいたので、新しいエントリーで補足しました。
Return-Pathをセットする(Catalyst::Plugin::Email::Japanese)

この記事のトラックバックURL
トラックバック機能は終了しました。
トラックバック
Return-Pathをセットする(Catalyst::Plugin::Email::Japanese)
先日MIME::LiteをカスタマイズしないとReturn-Pathをセットできないというような内容のエントリを書きましたが、適切な突っ込みを頂き、私が間違っていたことがわかりました。 [参考]下記のコメント欄 Return-Pathをセットする(MIME::Lite) MIME::LiteのFromSend
| ブログが続かないわけ | 2007/01/12 3:28 PM |
コメント
send メソッド発行時に FromSender をセットすれば envelope FROM がセットされると思います。

$msg->send(
'sendmail',
FromSender => 'error@example.com',
);
| 海賊たろ | 2007/01/11 2:11 PM |









関連情報