メール取ってきて振り分けるならfdm

大量のメールを効率よく処理するには、メールの自動振り分け処理は欠かせません。以前はこの振り分けを、クライアントのMUA (Mail.app) で実行してました。やっぱり設定が簡単だからね。

でもこのやりかただと、Mail.appが稼働しているマシンが動いていないとメール振り分けも行われません。そもそもメール振り分け自体がメール到着と別のタイミングで行われてしまいます。別のホストやメールソフトからメールを読んでいると、いまいちキモいです。

(私は自分用マシンにimap4サーバをたてて、メールはそこに届き、そこに格納されるようにしています。この場合メールソフトの役割は「imap4サーバに整理蓄積されているメール群を読み書きする道具」です)

やっぱりサーバ側で振り分けを実行するか… と思ったのだが、どのツールを使って振り分けしようかなと調べました。

そもそも親メールサーバから俺メールサーバまでメールをPOP3で取得する部分

  • もともとfdmを使っている
  • この手のソフトはfetchmailが有名だが、ソ連が崩壊したいま、fetchmailは無いでしょう。戦後ですよもう

俺メールサーバのなかでメールを振り分ける処理

  • この手のソフトはfetchmailが有名だが、東西ドイツが統一されたいま、procmailは無いでしょう。21世紀ですよもう.
  • Sieveという抽象化されたメール振り分けルール記述言語があり、Dovecot imapd用の実装もある。ThunderbirdみたいなMUAからSieve設定をいじるためのプラグインもあるようだ。
    筋が良さそうだけど、手元の環境 (squeezeなDebian GNU/Linux) にリーズナブルなコストで導入するのは面倒そうだったので、試すのは20分で引き返してやめた. 僕がやりたいのは忙しい業務の最適化であって、ホストいじり遊びではないのだ。
  • ほかにも、高林哲さんが書いたlispっぽい振り分け記述のやつとかあったと思うけど、fdmがそもそも振り分け機能を含んでいることに今さら気づいたので、これを使ってみる。fdm自体がまだまだ知られていないようなのでもったいないこともあるし。

いろいろ試し、fdmで振り分け生活するのも悪くなかったのでご紹介。結果として下の図のような構成になりました。

fdm

(OmniGraffle file)

  • 会社のメールサーバがあります。ここからはpop3でしかメールを取得できません。21世(ry
  • 僕個人のメールサーバもあります(社内に)。ここでfdmを動かし、会社のメールサーバからメールを取得し、振り分ける処理をします。
  • メールの振り分け先は、僕のメールサーバにあるMaildirです。具体的には ~/Maildir 以下にあるただの>UNIXディレクトリ構造です。
  • 僕のMaildir以下にある、振り分けられたメー>ルボックス群へのメール読み書きアクセスはdovecot imapdが提供しています。
  • 僕はノートPCやデスクトップPCで、好きなメールソフトを使って、imap4経由でこれらメールボックスを好きなように読み書きします。

fdmって何

fdmは、いろんな方法でメールを取ってきて、ルールに従ってあれこれ処理するもの。つまりfetchmailとprocmailを合体させてモダンに(マシに?)したようなツール。

pop3 (pop3s), imap4 (imap4s) からメールを取ってこれる。これらはssh tunnelをかまして取って来いという指定もできる。

あるいは、fdmのstdinに新規メールを流し込む運用もできる。

既存のMaildirやmboxを入力として処理することもできるので、溜まった大量のメールに対してルール処理をバッチで流すといったこともできる。

nntpでnetnewsも取ってこれるけど、さすがに年号が平成にかわったいま、コレは要らないよね…

fdmの設定

いろいろこまごまできるけど、詳しくはhttp://fdm.sourceforge.net/を見てください。

設定ファイルは、ホスト全体に施すなら/etc/fdm.confに書く。ユーザごとの設定なら~/.fdm.confが標準の場所だ。何にせよfdm -f [config_file]で好きな設定ファイルを参照できるのはUNIX流儀のとおり。

メールを取ってくる設定

例えば定礎株式会社の社員が社内メールサーバからpop3で取ってくる設定はこんな感じ

account "teiso"
pop3 server "mail.intra.example.co.jp"
user "andy.smith"
pass "FooBarMoge"

キーワード間に空白や改行入れるのは好きにやっていい。
インデントしてるのは単に見やすさのためだけです。
なので上の設定は下のように書いても同じ。

account "teiso" pop3 server "mail.intra.example.co.jp" user "andy.smith" pass "FooBarMoge"

文ごとの区切りは特にない。単にパーサが満足するまでトークンを食べていってるタイプみたい。設定ファイルの中にパスワードをべた書きする設計は、いかにもイケてないですね。危なくてダサいです。少なくともchmod 600 ~/.fdm.confは必ずやっておいてください。

このようなアカウント設定は複数書いても大丈夫。特定のアカウント設定だけ実行したいならfdm -a teisoとして指定。複数あったら、頭から順に実行されます。逆に、勝手に実行されたくないもの(陽に示したときだけ実行してほしいもの)は、account "名前"の後にdisabledを付けてください。

メールを振り分ける設定

簡単な実例

match "^From: .*@itunes\\.com" in headers
or "^From: .*rakuten-bank\\.co\\.jp" in headers
or "^From: au-ryokin-monthly" in headers
or "^From: .*atu@mail\\.uccard\\.co\\.jp" in headers
maildir "%h/Maildir/.dealings"

matchで、これから振り分けルールを書くよという宣言. 特定のアカウント処理のときだけ動いてほしいルールの場合は、match account "アカウント名"としてください。

マッチングルールはregexpで書けます。PCRE (Perl Compatible Regular Expressions, 要するにPerlの正規表現を切りだしたライブラリ) を利用するようビルドされてたらPCREを使えます。リテラルエスケープはバックスラッシュ2つです。

in headers はメールヘッダだけナメる指定。お察しの通り、in body もあります。どっちも無ければメール全体をなめます。

orじゃなくてandも書けます。ルールは左から順に評価。
orandの直後にnotと書けば論理反転。
で、(一連の)条件判断のあとに出てくるmaildirという部分が、具体的にどうせよというアクションです。この場合は指定のMaildirにメールを格納せよってことですね。

ここではホームディレクトリの Maildir/.dealings って Maildir に格納せよと指定してます。ここは実際にあなたのサーバのMaildir以下に降りていって、振り分けたいIMAP4フォルダの実際のディレクトリ名を確認してください。imapdの種別と設定によりますが、フォルダ名に日本語を使ってる場合、実際のpathは、あの糞なutf7エンコーディングになってるかもしれません。

アクションには他にも mbox (mboxに格納せよ), drop (ステ), keep (何もせずcontinue的), pipe (次に指定するshell commandsにパイプ), exec などあります。rewrite, add-header, remove-header みたいに、メール内容を料理して次のアクションにつなげていくためのアクションもあります。

いろいろとmatchルールを書いていったら、最後はmatch allで締めるのが一般的でしょう。あらゆる場合にマッチする、つまりdefaultルールです。例えばこんなふうに:

match all
action maildir "%h/Maildir"

定数

上の例に

maildir "%h/Maildir/.dealings"

とありましたが、この%hには、あなたの home directoryが入ってます。
他にも必要そうなものは一通り定義されてます。http://fdm.sourceforge.net/参照。

変数

上の振り分けルールは、実際には以下のように変数も使って書いています。

$maildir = "%h/Maildir"

action "dealings" maildir "${maildir}/.dealings"

match "^From: .*@itunes\\.com" in headers
or "^From: .*rakuten-bank\\.co\\.jp" in headers
or "^From: au-ryokin-monthly" in headers
or "^From: .*atu@mail\\.uccard\\.co\\.jp" in headers
action "dealings"

perlみたいに、$をシジルに使って変数つかえます。

変数参照は "${variable_name}" のように。

で、アクション自体の定義を変数に入れちゃって、それをaction "action_name"として呼び出すこともできます。

日本語サブジェクトへのマッチ

電子メールの日本語Subjectは何らかのMIMEエンコードがなされて届くのが正しく、fdmはエンコードされたママの状態でmatch処理を行うので、そのままでは日本語題名の一部をルールに書いても無意味です。
たとえば「【日報】」を題名の一部に含んでいるメールをdaily-reportフォルダに振り分けたい場合は、以下のようにしてください。nkf 1.9以降が必要です。

match pipe "grep '^Subject: ' | nkf -w | grep '【日報】' " returns ( 0, )
action maildir "${maildir}/.daily-report"

メールの転送

メール振り分けルールでは、他のメールアドレスへの転送を書きたいこともあるでしょう。こんな感じで。

match all
action >smtp server "localhost" to "andy@example.com"

ただしfdmが稼働しているホスト (localhost) で、インターネットにきちんとメールが出て行くようMTAが設定されているのが条件です。さもなくば、きちんとsmtpを受け取って処理してくれるホストを指定してください。

上の例では、とにかくなんだろうが転送しちゃってますが、さすがにこれだとエラーメールのループとかがおっかないので、せめて以下みたいにMAILER-DAEMONからのメールは転送しないとか書いてあげましょう。X-Loopとか見るのもいいですね。

actionの最後にcontinueと書いてあるのは、さらに以下の処理にも移るってことですね。つまり上の例だと「andy@example.comに転送」、下の例なら「andy@example.comに『も』転送」です。.procmailrcでいう「:0 c」ですね。

match not "^From: .*MAILER-DAEMON" in headers
action smtp server "localhost" to "andy@example.com" continue

fdmの起動

fdmはパラメータなしで起動しても何もしません。通常はfdm fetchとして起動します。

まずは-nで、実際の動作はさせずdry-runで様子を見たり、-vでverboseな動作を見たりするのがおすすめです。-v-vvとか-vvvなど、v大盛りほどverboseになるのもUNIX流儀どおりです。

実際にはfdmは手で動かさず、crontabに入れたり~/.forwardに入れたりしてのバッチ処理になるでしょう。このような場合、エラーが出ても闇に消えてしまうとせつないので、-l オプションを付けましょう。エラーはsyslogに投げられます。

.forwardから起動する場合

~/.forwardからfdmを起動する場合、つまりメール取得機能は使わず振り分け機能だけ使う場合は以下のようにします。

結局パイプ経由で食わせることになるので、~/.forwardはこんなしときます。-l はログをsyslogに投げる, -a は明示的に「stdinという名称のアカウント処理」のみ実行, fetchはfetch処理モードとして実行(やや直感と違いますが), です。

"| fdm -l -a stdin fetch"

~/.fdm.conf は以下の感じで。

まずstdinという名称で, stdinからの処理を受けるアカウント定義をしておきます。ただのstdinなので、ユーザ名とかパスワードは要りません。他のアカウント定義と混ざって実行されても意味ないので、disabledも付けておきましょう。

account "stdin" disabled stdin

あとはルール定義を好きなだけ並べていきます。これであの、そびえたつクソみたいな.procmailrcを捨てられます。おめでとう!

さらに

ざっくりした全体の流れはこんなところです。詳しくはhttp://fdm.sourceforge.net/を見てください。さらに進むなら、毛深くガッツリと設定された例が http://ft.bewatermyfriend.org/comp/fdm/fdm.conf.html にあります。というかこの2例ぐらいしか説明見たことないんで、みなさんも知見があれば書いて僕におしえてください。