メールのふりわけを行うためのメモ。 プラットフォームは Debian を想定。
rdeliver は RubyMail と RubyFilter を使った メールの振り分けを行うためのプログラムで、 procmail などと同じように ~/.forward から呼出して使うことができます。
.procmailrc のようなわかりにくいスクリプトでなく、 Ruby スクリプトでわかりやすく振り分けルールを記述することができます。 またメールを表現する RMail クラスはかなり強力なのでいろいろなことができます。
最近 rubyfilter が Debian unstable に入ったので、 apt-get install rdeliver とするだけで使えるようになります。
rdeliver の使い方は /usr/bin/rdeliver に書かれた rd ドキュメントを参照。 ここ でオンラインでも読めます。
フィルタスクリプト .rdeliver を書くには RMail::Message や RFilter::DeliverAgent といったクラスの使い方を理解する必要があります。 以下のオンラインドキュメントを参照するのが便利。
具体的には、渡された RFilter::DeliverAgent オブジェクト (agent メソッドで参照できます) から ヘッダなどの各種情報を取り出して判断したあと、 どのようにそのメールを処理するか指示します。 フォルダに保存 (save)、捨てる (reject)、遅らす (defer)、コマンドにパイプ入力 (pipe) などのメソッドが用意されています。
~/Maildir の下でメールの振り分けを行うことにして適当に準備
~/.forward:
"|/usr/bin/rdeliver --home /home/yaegashi/Maildir --log /home/yaegashi/Maildir/.rlog"
~/Maildir/.rdeliver:
def main
agent.save("./") # /を最後につけるとそのディレクトリに Maildir 配送
end
配送のログは ~/Maildir/.rlog に残る。 ~/Maildir/.rdeliver に書かれたスクリプトの中で エラーが起きた場合はエラーをログに記録し、メールの配送は遅延される。
2003/08/29 01:27:15 24451: Action: save to "./"
2003/08/29 01:46:48 24529: Action: save to "./"
2003/08/29 03:00:03 24970: uncaught exception: #<NameError: undefined local variable or method `hoge' for #<Deliver:0x402a9f40>>
2003/08/29 03:00:03 24970: uncaught exception backtrace:
/home/yaegashi/Maildir/.rdeliver:2:in `main'
/usr/bin/rdeliver:172
/usr/bin/rdeliver:168:in `process'
/usr/bin/rdeliver:168
2003/08/29 03:00:03 24970: Action: defer: uncaught exception
~/.forward での rdeliver のオプションの書き間違いなど、 なにかやばいエラーが起きた場合、 ~/CATASTROPHIC_DELIVERY_FAILURE というファイルに エラーメッセージが書きこまれる。この場合もメールの配送は遅延される。
Exception:
#<Errno::EACCES: Permission denied - "/.rlog">
Backtrace:
/usr/lib/ruby/1.6/rfilter/delivery_agent.rb:310:in `process'
/usr/bin/rdeliver:168
最初に bogofilter による spam 判定を行い、 ヘッダを見て適当な Maildir フォルダに分類する .rdeliver の例。 メーリングリストのアドレスをヘッダから類推して対応する Maildir フォルダに保存する。 X-Mailing-List: など、 メーリングリストの名前が書かれたヘッダを見つけて自動的に分類してくれるので便利。
| アドレス | フォルダ |
| ML (ex. linux-users@linux.or.jp) | ML フォルダ (ex. .ml.jp.or.linux.linux-users/) |
| ウイルス | .virus/ |
| スパム | .spam/ |
| その他 | inbox (./) |
def main
# ウイルススキャン
IO.popen("clamdscan --quiet -", "w") do |io|
io.write agent.message
end
agent.save(".virus/") if $? == 1<<8
agent.log(1, "clamdscan: I/O error") if $? == 2<<8
# スパムフィルタ
IO.popen("bogofilter", "w") do |io|
io.write agent.message
end
agent.save(".spam/") if $? == 0<<8
agent.log(1, "bogofilter: I/O error") if $? == 2<<8
# X-ML-Name: debian-users
a = case agent.header["x-ml-name"]
when /linux-users/
"linux-users@linux.or.jp"
when /debian-users/
"debian-users@debian.or.jp"
else
nil
end
# List-Post: <mailto:debian-devel@lists.debian.org>
a ||= if x = agent.header["list-post"]
x =~ /<mailto:([^>]+)>/ ? $1.downcase : nil
end
# X-ML-Address: linux-zaurus@quickml.com
a ||= header["x-ml-address"]
# ML でないようなら inbox (./) へ
folder = a ? ".ml.#{address_to_folder(a)}/" : "./"
# フォルダに保存
agent.save(folder)
end
# "linux-users@linux.or.jp" -> "jp.or.linux.linux-users"
def address_to_folder(address)
address.split(/[\.@]/).reverse.join(".")
end
# Local Variables:
# mode: ruby
# End: