メールでルー語変換 powered by qpsmtpd

かなり前になりますが、miyagawa さんが Plagger の開発始めたときのプラグイン機構の話で例にあげていた qpsmtpd、ずっと気になっていたのですがやっとさわってみました。

これを使って、メールでルー語変換というのを作ってみました。 m@lou5.jp にメールを送ると、件名や本文がルー語変換されて戻ります。自分で楽しむなり、戻ってきたルー語メールを転送するなり、ご自由にお試しください。ルー語変換は携帯から使いたいという声が多かったのですが、これで携帯でもいけるかな、と。

携帯アドレスの場合絵文字を混ぜて返すとか、PC の場合 HTML メールで返したりとか、いろいろ夢はひろがりますが、それはまた時間取れたときやります。。

qpsmtpd

日本語の言及が少ないのでちょっと書いておくと、全部がプラグインでできた Perl による SMTP サーバー。スパム対策に強い smtpd として普通に知られてるようですが、デーモンとして立ち上がっていてくれるので、Perl 書きには mod_perl 的に使えるという代物です。この前の YAPC::Asia で、VOX の自動返信メール部分でこれを使ってるって話していました。

自動返信メールだと、alias 駆動みたいなのでもいいのですが、やはり毎回プロセスが立ち上がるというのはアクセス(送信)が集中したときのサーバーリソースが怖い。

インストール。ソースから入れる方法 も Wiki に細かく書いてあるんですが、パッケージから。OS Fedora なのですが、yum で見つからなかったので、RPM とその依存を解決するために必要なのをこことかから落っことしてきて rpm -i。すんなり入りました。

仕組みは、blosxom ぽい。plugins/ ディレクトリにスクリプトの断片が置かれてて、config/plugins ファイルに、使うプラグインを1行1個で列挙。同じフックポイントにかかるプラグインは、そこに書かれた順で起動。プラグインによってもっと設定が必要な場合は、config/ 以下にプラグインと同じ名前でファイルを置き、そこに設定を書くことで使える。config/ 以下には loglevel みたいな本体が使う設定ファイルも混じっている。

さっそく telnet localhost 25 で試してみる。実装が Perl なのとソースがきれいなので勉強になります。check_earlytalker プラグインとか面白いです。plugins/ ディレクトリのスクリプト断片は、Filter::EntryFullText で .pl な asset の呼ばれ方に似てる(Qpsmtpd::Plugin の compile() 参照)。

qpsmtpd はやたらノリノリな MTA です。QUIT のあと Have a wonderful day. とか言われて開発者のキャラが見えます。

実は Qmail や postfix を完全にリプレースするものかと思ってたのですが、そうじゃなく smtpd 部分のみを置き換えるものだということをここで知る(笑)。それ以外の、キューを配送するところとかは 何がしかの MTA が必要。もともと postfix だったので、postfix/master.cf の smtpd のところをコメントアウトして起動。Fedora(Redhat)の場合 /etc/init.d/qpsmtpd-forkserver が使う設定は ` /etc/sysconfig/qpsmtpd-forkserver` にありますが、そこのローカルホストしばりを外し終了。というわけで、今 lou5.jp:25 は qpsmtpd が受けています。

Pluggable なアプリの利点はいろいろですが、既存のプラグインの中からやりたいことと似ているものを探し、パクるとこから始められるという入りやすさもありますよね。今回は自動返信メールということになるので、Plagger でいう Publish のようなフェーズである hook_queue にかかるモジュールを参考につくり始めました。

プラグインの書き方は簡単。この辺に使える hook の一覧と受け取る引数、何を返すとどうなるかが。最初に書いたテストプラグイン。

sub hook_queue {
    my ($self, $transaction) = @_;
    
    $self->log(LOGINFO, "## sender:" . $transaction->sender->address);
    $self->log(LOGINFO, "## from:"   . $transaction->header->get('from') );
    
    return (DECLINED, "pass to next plugin.");
}

これを plugins/hoge として保存して、config/plugins に hoge って書くと、ログに sender と from を出します。$transaction オブジェクトについては perldoc lib/Qpsmtpd/Transaction.pm

書いてると、処理部分をサーバーサイドの Catalyst アプリの Lou5::Site と一緒に、Lou5::Mail にまとめたくなります。そんな人向けに? qpsmtpd は config/plugins に plugins/ 以下のファイル名ではなく、Foo::Bar みたいなモジュール名を書けます。

その場合 Foo::Bar が呼ばれるのですが、Lou5::Mail はシステムライブラリ領域には置いていないんでモジュールが見つからないエラーになります。taint モードで動いている qpsmtpd に PERL5LIB とかで勝手パスを渡すのはむずかしく、結局 plugins/lou5_mail というファイルを用意して、

use lib '/home/tomi/Lou5/lib';
use Lou5::Mail;

と書き、Lou5::Mail 側ではいろいろ export するという方法をとりました。レポジトリはこのへんです。config/plugins は queue/postfix-queue の前に lou5_mail と入れてます。