用意されているMatcher

概要

まあ、開発自体に入る前に、それでも Matcher と Mailet にどんなものが用意されているか見てみようか。かなりいろいろなものが用意されているので、それこそ Sieve みたいなことをしよう、というくらいだったら、特に開発しなくてもイケてしまうかもしれないぞ。

Matcher は受け取ったメールに何か処理をさせるために、そもそもそのメールが「処理対象であるか?」を判定するためのものである。言い替えると次のようなメール「であるか」を、判定するクラスだということだ。

実際にはこれらの Matcher は config.xml の中で、

<mailet match="RelayLimit=30" class="Null"/>

のようなかたちで使われる。要するに、

<mailet match="Matcher種別=オプション" class="Mailet種別"/>

ということだ。オプションは具体的なMatcherによって違う。

Matcherのリスト

で、具体的に用意されているのは、org.apache.james.transport.matchers にあって、次の36種類だ。残念なことに、メールの本文内容に特定の文字列があるか?とかいう Matcher はないな。

SMTP 情報に対してマッチする Matcher

実際にはメールには、SMTP の接続から取得される情報と、メールヘッダから取得される情報とがある。このうち、メールヘッダの情報はいくらでも偽造が可能だが、SMTP から取得される情報は、仮に偽造されても「SMTP で発行された接続自体の情報」としてはあくまでも正しいものであり続ける(信頼性は別として...)

なので、MTA は基本的に「SMTP の情報」をベースに動作するのが普通である。だから、James の場合も、これらを使ってメールサーバとして動作しているわけで、そのような「SMTP情報」に対する Matcher は、Mailet の一番有益な Matcher である、ということになる。

では、この「SMTP情報」にどういうものがあるか、というと、

RemoteAddr
接続してきたメールサーバのIPアドレス
RemoteHost
接続してきたメールサーバのIPアドレスから、DNSを逆引きして得られたホスト名
Sender
送信者。SMTP の MAIL FROM: ヘッダから取得。
Recipient
受取人。SMTP の RCPT TO: ヘッダから取得。

ということになる。これらに関する Matcher は多数用意されている。

ここで注意すべきなのは、受取人(Recipients)に関する Matcher が少し特殊な働き方をすることである。それ以外の Matcher は「すべてか無か」の結果を返す。言い換えれば「一致するか、しないか」のどっちかである。が、受取人(Recipients)については、これが「複数ありうる」ということを前提として、James は作られている。だから、Matcher の match() メソッドの戻り値は「受取人の Collection」なのであり、フツーの Matcher は「受け取ったメールのRecipients をそのまま返す」か「nullを返す」かどっちかなのである。が、受取人に関する Matcher は、「メールの受取人を加工して、条件に合った受取人だけにして返す」という処理をすることになり、後々の振る舞いも異なる。だから、これについては別立てでもう一度触れることにする。

RemoteAddrInNetwork

メールの送信元(RemoteAddress)が、オプションで列挙されたアドレス表記に一致するかどうかで Match を行う。このオプションに書ける形式はカンマか空白区切りで、

フォーマット
IPアドレス127.0.0.1
ワイルドカード共のIPアドレス127.0.0.*
ドメイン名myHost.com
プレフィックス長付きドメイン名myHost.com/24
ネットマスク付きドメイン名myHost.com/255.255.255.0
プレフィックス付きIPアドレス127.0.0.0/8
ネットマスク付きIPアドレス127.0.0.0/255.0.0.0

のいずれでもよい。

RemoteAddrNotInNetwork

RemoteAddrInNetwork の否定形の Matcher である。

SenderHostIs

メールの送信者(Sender:要するに SMTP の MAIL FROM:)のドメイン名部分が、オプションで列挙するリストに含まれているかどうかをチェックする。オプションの列挙はカンマ or 空白で区切る。

SenderInFakeDomain

メールの送信元(Sender:要するに SMTP の MAIL FROM:)のドメインについて、DNS をルックアップして、「本当にその送信者ドメインが存在するか」と、「そのドメインのメールサーバ(対応する MX レコード)」をDNSを介してチェックする。送信者ドメインのメールサーバの IPアドレスが引けないか、引けたIPアドレスがオプションの列挙に載っていれば、マッチする。まあ要するにスパマが「架空の送信者」を名乗るのを排除するためにある。

SenderIs

メールの送信元(Sender:要するに SMTP の MAIL FROM:)について、オプションの列挙リストに一致するかどうか。

SenderIsNull

メールの送信元(Sender:要するに SMTP の MAIL FROM:)がない場合に一致する。オプションはなし。

SenderIsRegex

メールの送信元(Sender:要するに SMTP の MAIL FROM:)がオプションの正規表現に一致するか。オプションの正規表現はPerl5風で、RecipientIsRegex と同様に冗長な (.*) を必要とする。

IsSingleRecipient

受取人(Recipient)が1件のみの場合にマッチする。

受取人に関する Matcher

先にも述べたように、ここに集めたもの以外は、「すべてか無か」で結果を返す。言い換えると結果は「条件に一致しているか」「一致しないか」のどっちかだけである。がここに集めた、「受取人(Recipient)に関するMatcher」だけは、「条件に一致する受取人だけにして結果を返す」というロジックで動作する。

つまり、James は「複数の受取人がある」という前提で作られている。だから、Matcher#match() メソッドの戻り値は Collection になっており、ここで示す Matcher は、「条件に一致する受取人だけにして結果を返す」という特別な振る舞いをする。そのため、ここで示す Matcher の多くは、org.apache.mailet.GenericMatcher ではなく、org.apache.mailet.GenericRecipientMatcher を継承して作られている。

この結果(一致する受取人だけになった)をどうパイプラインが解釈するのか...は少々難しいので後回しにするが、簡単に言えば、

メールが「一致する受取人宛メール」と「一致しない受取人宛メール」に「分身」し、それぞれ別なメールとして以降のパイプラインを辿る

ということである。勿論、通常のケースでは受取人は1件のみ、というケースが多かろう。その場合はフツーの Matcher と振る舞いは異ならない。

あと、この Recipient 関連 Matcher は便利さを追求し過ぎて、いろいろ種類があり過ぎる。ここで少しまとめておく。要するに、

  someone  @ hogehoge.com
 --------    ------------
   UserIs      HostIs
 |                       |
 +-----------------------+
       RecipientIs

という命名規則になっているので、そのように見て欲しい。

RecipientIs

受取人(Recipient)全体がオプションで列挙するメールアドレスと一致すればマッチする。オプションの列挙は「カンマ,空白,タブ」のいずれで区切ってもよい。

RecipientIsLocal

宛先が Local なホストに属し(<servername>タグ内部で定義されたホストである)、アカウントを James が管理している場合にマッチする。

RecipientIsRegex

受取人(Recipient)全体をPerl5風正規表現で照合する。機能は明解だが、少し注意点がある。この正規表現一致は Jakarta ORO の Perl5Matcher.matches() メソッドを使っているので、「頭から最後までの完全一致」でパターンを書かなくてはならない、ということである。部分一致ではないから、

<mailet match="RecipientIsRegex=@some" class="****">

では、「sugiura@somedomain.jp」に一致せず、それをするためにはやや冗長に「(.*)」を使って、

<mailet match="RecipientIsRegex=(.*)@some(.*)" class="***">

と書かなくてはならない。

HostIs

宛先(Recipient)のドメイン部分が、オプションで与えたホスト名と一致する。オプションはカンマ区切りでホスト名をいくつでも指定できる。

<mailet match="HostIs=hogehoge.com,localhost" class="***" />

なら、someone@hogehoge.com, someone@localhost 宛のメールに一致するわけである。

HostIsLocal

宛先(Recipient)が Local なホストに属している。これは HostIs Matcher の特殊ケースで、一致すべきホスト名は、config.xml の <servername>タグ内部で定義されたホストだ、ということである。だからオプションはない。

UserIs

受取人(Recipient)のアカウント部分が、オプションの列挙リストと一致すればマッチする。オプションはカンマor空白を区切り文字とする。

RecipientIsOverFixedQuota

さて、以降は「受取人についての Matcher」だが、ちょっと特殊な役割のものになる。

RecipientIsOverFixedQuota Matcher は受取人について貯まっているメールの総量について規制する役割りを果たす Matcher である。言い替えると受取人ごとに、UserRepository( var/mail/inboxes/受取人アカウント)に貯まっているメールの総サイズを計算し、それがオプションで指定するサイズを越えていればマッチし、その受取人だけを Matcher の戻り値として返す...というロジックである(なぜか今送ったメールのサイズは反映しないが..)。

ということは、パイプラインをうまく組み立てれば、「inbox が満杯のユーザについては『満杯なので受け取れない』旨の返信を出し、そうでないユーザについては処理を継続する」ということもできてしまうわけである。

オプションでは「K(キロバイト)」「M(メガバイト)」の単位(大文字・小文字は無視)が使える。だから次の設定だと貯まっているメールが 40メガバイトを越えれば、新たにメールを受けつけない設定になる。

 <mailet match=match="RecipientIsOverFixedQuota=40M" class="ToProcessor">
    <processor> error </processor>
    <notice>The recipient has exceeded maximum allowed size quota</notice>
</mailet>

CommandForListserv

CommandListservMatcher

この2つは、Mailet のところで見る メーリングリスト機能と関連する Matcher である。要するにメーリングリストを「購読(subscribe)」したい時には「メーリングリスト名-on@ホスト名」宛にメールを送り、「購読中止(unsubscribe)」したい時には「メーリングリスト名-off@ホスト名」宛にメールを送る、という仕様で動くものである。だからそういう特殊な Recipient名に対するマッチング処理をこの2つの Matcher が担当する。言い替えると、CommandForListserv は「オプションで指定するアカウント名-on@オプションで指定するホスト名」かオプションで指定するアカウント名-off@オプションで指定するホスト名」に一致する。CommandListservMatcher はより汎用的にオプションで指定するアカウント名-コマンド名@オプションで指定するホスト名」にマッチする。

メールヘッダに関する Matcher

メールヘッダは偽造可能であるし、MUA 仕様によってもいろいろと違う可能性もある。そこらへんをうまく考えて利用して欲しい。

CompareNumericHeaderValue

これは何か任意のメールヘッダについて、値が数値であるものを判定対象とする。言い替えると、適当な(追加)ヘッダに値として数値を取るようにしてやり、それが指定の値と大小判定をして、処理すべきか否かを決める。たとえばそのヘッダが「X-Priority」だとして、それが「3以上」のものを処理するのなら、

<mailet match="CompareNumericHeaderValue=X-Priority >= 3" class="Mailet種別"/>

のようなかたちで指定する。言い替えると、
<mailet match="CompareNumericHeaderValue=ヘッダ名 演算子 閾値" class="Mailet種別"/>

というかたちである。それぞれ空白orタブで区切る。値はdouble値を取りうる。演算子は LT(<), LE(<=,=<), EQ(==,=), GE(>=,=>), GT(>) が使える。ちょいと残念なのは、値が正しくない場合に例外を投げちゃうところかな。

FileRegexMatcher

こいつは重装備だ。ファイルでヘッダのマッチパターンを指定するというものだ。だから、

<mailet match="FileRegexMatcher=ファイル名" class="Mailet種別"/>

のようなかたちで使い、そのファイルは「:」で区切って、
ヘッダ名:Perl風マッチパターン

を記述する。勿論複数可である。空白などはそのまま処理されるので、勝手に入れちゃいけないよ。

HasHabeasWarrantMark

「Habeas Warrant Mark」という copyright の一種を検出する。何かよう判らんが、関心のある方は http://www.hebeas.com でも見てくれ。要するに「X-Habes-SWE-[1-9]」というようなヘッダがあって、その値が決まっているようである。で、その値であればマッチする。

HasHeader

オプションで与えたヘッダ名のヘッダがあればマッチする。要するに、

<mailet match="HasHeader=X-Priority" class="***" />

なら、もし「X-Priority:」ヘッダがあればマッチする。使いでがありそうだ。

RelayLimit

転送回数を制限する。言い替えると Received: ヘッダの数を勘定して、それがオプションでの指定回数を越えていれば一致する。

SubjectIs

Subject: ヘッダがオプションで指定するものと一致すればマッチする。

SubjectStartsWith

Subject: ヘッダがオプションで指定したもので始まればマッチする。

メール内容に関する Matcher

メールは当然添付ファイル付きなど、MIME メールである可能性がある。それらも含めてここにまとめよう。

AttachmentFileNameIs

添付ファイルがある時、そのファイル名を特定できる。たとえば、

<mailet match="AttatchmentFileNameIs=*.gif" class="Mailet種別"/>

というように、添付ファイルのファイル名が .gif で終るものだけにマッチさせることができる。大変残念なことだが、このワイルドカードのロジックはいい加減だ。先頭文字しか「*」であることができない。また、カンマ区切りで複数のファイル名を並べることも可能だ。

HasAttachment

要するに「添付ファイル付きメール」ならマッチする。オプションはない。

SizeGreaterThan

メールのサイズがオプションの指定サイズを越えていればマッチする。オプションでは単位として「K(キロバイト)」「M(メガバイト)」を使うこともできる。

Jamesが設定する属性によるMatcher

パイプラインを通じて、James 自体がメールに対して、いろいろな属性を与えることをする。それらに対する Matcher がいろいろある。一番重要なのは Mail Attribute と呼ばれるハッシュテーブルであり、名前→値でさまざまな属性情報を保存できる。

HasMailAttribute

オプションで与えた「Mail Attribute」がメールにあればマッチする。「Mail Attribute」は、James が処理するメールに勝手に与える属性である(org.apache.james.core.MailImpl 参照)。なので、Mailet などで適当に「Mail Attribute」を与えたものを、ここで検出できる。実際に使っているのは、「配信エラー時」に "delivery-error" に起きた例外を(RemoteDelivery)にセットするなどである。本当はどんな型でもセット可能だが、通常の Mailet → Matcher 検出では文字列での利用がほとんどだろう(まあ、比較する場合は toString() してるけどね...)。

HasMailAttributeWithValue

HasMailAttribute と同じく「Mail Attribute」を検出する。ただし、値がオプションで指定のものと一致するかどうかの検査である。たとえば、

<mailet match="HasMailAttributeWithValue=MyAttribute, value" class="Mailet種別"/>

のように「アトリビュート名,値」でコンマ区切りで指定する。

HasMailAttributeWithValueRegex

HashMailAttributeWithValue と同様で、値の指定が Perl5 風正規表現マッチをする。

FetchedFrom

ホントはこれはヘッダに関する Matcher だが、振舞いが「属性」風なので、ここに入れておこう。FetchPop機能がメールをPOPサーバから引き出した時に、「X-fetched-from:」という特殊ヘッダをメールに追加する。それがあるかどうかを判定して、そのメールが FetchPop されたものかどうかを判定する。副作用があって、これを呼ぶとその「X-fetched-from:」ヘッダは消滅する。オプションはない。

SMTPAuthSuccessful

SMTP Auth(認証付きSMTP)で送られたメールならばマッチする。まあ、最近のモバイル環境でのメール送信に対応した仕様である。現実的には SMTP Auth で送られたメールは、キー「org.apache.james.SMTPAuthUser」でその認証アカウントを Mail Attribute として保存している。

SMTPAuthUserIs

SMTP Auth(認証付きSMTP)の認証アカウントが、オプションで列挙するアカウント名のリストにあればマッチする。オプションの列挙は例によってカンマor空白orタブ区切りである。

特殊目的

まあ、その他項目だな。重要なものも多いので、「その他」だって軽く見ないように。

All

いうまでもなく、すべてのメールとマッチする。オプションなぞあるわけない。結構悩ましいことだが、「match="All"」で何か別なメールを発送する処理をすると、生成したメールが更にこの Matcher に一致して、ループになる...というちょいと注意すべきことが起きる可能性があるので、これは後でしっかり説明する。また後でこのソースを検討する

InSpammerBlacklist

「スパマか?」と推測される接続元(RemoteIP)からのメールに一致する。このやり方は面白く、引数として「スパマのIPアドレスをリストアップしているサービスのホスト名」を指定する。で、

  1. メールからその接続元(RemoteIP:例えば 154.32.88.132)を取得する。
  2. そのサービス(例えば relays.ordb.org)に対して、「RemoteIP を逆転したもの+サービスホスト名」(この例だと「132.88.32.154.relays.ordb.org」というようなホスト名)で DNS ルックアップを行う。
  3. ブラックリストサービス(dnsbl.njabl.orgrelays.ordb.org←日本語充実!)の場合、もしスパマならば、この疑似ホスト名が登録されている。
  4. ホワイトリストサービス(query.bondedsender.org)の場合、この擬似ホスト名が登録されていれば、スパマでないことを保証する。

という具合のものだ。config.xml ではデフォルトで上記の3つのサービスを利用している。それぞれチェックしてみると面白いぞ。

ここらへん後でソースを見てみる

NESSpamCheck

これもスパマ排除の目的の Matcher だが、メールヘッダの「スパムっぽい」パターンを照合して、「スパムメール?」と判定する。名前の由来は NEtscape Mail Server のようで、要するに Netscape Mail Server の「スパム推測ルール」を頂いたものであるようだ。特にオプションはない。



copyright by K.Sugiura, 1996-2006