bogofilter を正しく使おう – 2007年 6月版

さて、bogofilter 使用環境での spam 登録/ham 登録/判定のためのスクリプトの改訂版です。 このスクリプトは元々 Mew のマーク付けに呼び出して使うために書いています。 何故このスクリプトが必要かというと、nkf では本文が base64 等でエンコードされたメールをきちんとデコードできないからです。 Ruby で書かれた前処理のフィルタ (その1その2) は見つかったのですが、perl のものが見当たらないので自分で書いてみたのです。外部コマンド実行のオーバーヘッド削減を目的として Text::Kakasi や NKF モジュールを用いています。

前回から何が変わったかというと、MIME の階層をきちんと辿るようにしました。前回のものはこれが不十分でした。 また、コンテンツタイプがテキスト以外のものは Content-Type フィールドを表示して、学習に生かせるようにしました。実行時の各種オプションも新設しました。

#!/usr/bin/perl
use Email::MIME;
use NKF;
use Text::Kakasi;
use Getopt::Std;
my $nkfopt  = "-e";
my $bogoopt = "";
my $printmode = 0;
sub mime_body {
my $o = shift;
my @p = $o->subparts;
my $m = "";
if (@p > 0) {
foreach my $so (@p) {
$m = $m . mime_body($so);
}
}
elsif (!defined($o->content_type) || $o->content_type =~ /text/i) {
$m = $m . nkf($nkfopt, $o->body);
}
else {
$m = $m . $o->content_type . "\n";
}
return $m;
}
local $/;
my $k = Text::Kakasi->new;
$k->set("-w");
getopt('snp');
if (defined($opt_s)) { $bogoopt = "-s"; }
if (defined($opt_n)) { $bogoopt = "-n"; }
if (defined($opt_p)) { $printmode = 1;  }
LOOP: foreach $filename (@ARGV)  {
open(I, $filename) || next LOOP;
my $message = <I>;
my $parsed = Email::MIME->new($message);
$message = nkf($nkfopt, $parsed->header_obj->as_string . "\n") . mime_body($parsed);
my $tokenized = $k->get($message);
if ($printmode) {
print $tokenized;
}
else {
open(O, "| bogofilter $bogoopt") || die("can't execute bogofilter");
print O $tokenized;
close(O) && print "$filename\n";
}
close(I);
}

このスクリプトを bogo として、使い方を簡単に説明します。 そのフォルダのメールをスパムとしてデータベースに登録するには次のように呼び出します。

bogo -s -- *

そのフォルダのメールをハムとしてデータベースに登録するには次のように呼び出します。

bogo -n -- *

"-p" オプションは bogofilter に喰わせる前のメールデータを標準出力に出力します。 kakai による分かち書きの結果も確認できます。

bogo -p -- *

引数でオプションをつけずファイル名のみ渡すと、指定したファイルのうち spam と判定されたメールのファイル名のみ標準出力に出力します。 これを Mew のマークに使います。

bogo *

利用している perl のメール関連パッケージは以下の通りです。この順で "perl Makefile.PL; make; make install" を実行しインストールしました。

  • MIME-Types-1.20
  • MIME-Base64-3.07
  • Email-MIME-ContentType-1.014
  • Email-MIME-Encoding-1.311
  • Email-Simple-1.999
  • Email-MIME-1.859

この他に Text::Kakasi と NKF を用いています。

メール関連のこの辺りのモジュールは使いやすいと思います。 日本語のサイトで取り上げている例は少ないようですが、このコードが処理の参考になればと思います。