<!--
   $FML: filter.sgml,v 1.12 2010/03/18 20:28:04 fukachan Exp $
-->


<!--
   XXX-TODO: フィルタの説明を書き直せ
   XXX-TODO: MTI の説明？
-->


<chapter id="internals.filter">
	<title>
	フィルタ
	</title>

<caution>

<para>
本章でいうフィルタとは、
特にこだわらない限り「投稿された記事に対するフィルタ」を指します。
</para>

<para>
この「記事に対するフィルタ」システムは 
FML::Filter クラスに実装されています。
</para>

<para>
もちろん「記事」ではなく
「コマンドメール」を対象としたフィルタシステムなども考えられます。
この場合、その特殊なフィルタシステムが 
FML::Filter のサブクラスにあるとはかぎりませんので注意して下さい。
コマンドメールが対象であれば、
コマンド処理のクラス中に実装されたフィルタシステムを呼び出しています。
たとえばコマンドメールのコマンドの長さを制限する機能(フィルタ)は
FML::Command::Filter クラスが担当しています。
</para>

</caution>


<sect1  id="internals.filter.overview">
	<title>
	フィルタの概要
	</title>

<para>
コンテンツを検査するフィルタには大きく分けて三種類あります。それは、
「ヘッダに対するフィルタ」
「メール本文に対するフィルタ」
「MIME 構造を元にしたフィルタ(mime component filter)」の３つです。
</para>

<para>
もっとも「メール本文に対するフィルタ」にも、
「 MIME でないメールに対するフィルタ」
と
「text/plain に対するフィルタ」
の二種類があるので、
大きく分けると四種類が正しい説明かもしれません。
<screen>
大まかな区分け

  header

  body
	non-mime
		permit / reject (fml 4.0 の content filter の !MIME)

	mime component filter
		(fml 4.0 でいうところ content filter 相当)

	text/plain filter
			ja, en, ... (言語依存フィルタ)
			非言語依存パート (M$ GUID scanner のたぐい)
		先頭部分の text/plain パートに対するフィルタ
			(シンタックスチェック)

	外部フィルタ (ウィルス/SPAM チェッカを呼び出す)
		clamav
		spamassassin
		bogofilter
</screen>
本当はこれ以外に、SMTP もしくは LMTP を使って、外部の virus scanner な
りに飛ばす迂回路(postfix の content_filter 相当)が必要だと思うのですが、
(それは Postfix でやればいいとおもいますので)実装していません。
</para>

<!-- XXX-TODO トラフィックベースのフィルタ -->
<para>
また、これら以外にトラフィックベースのフィルタがあります
(ほとんど未実装なので解説は省略 _o_)。
</para>

</sect1>


<sect1 id="internals.filter.config">
	<title>
	フィルタに関する設定
	</title>

<para>
配送やコマンドメールといった機能別に
「フィルタを使うか使わないか？」
「使うフィルタルールは？」
などの変数が定義されています。
</para>

<para>
変数名体系の基本は以下の通りです。
<screen>
use_サービス_filter       = yes か no
サービス_filter_functions = 機能A 機能B

use_機能A   = yes か no
機能A_rules = ルール1 ルール2
</screen>
</para>

&var.filter.rules;

</sect1>


<sect1 id="internals.filter.size">
	<title>
	フィルタ: サイズ制限
	</title>

<para>
ヘッダ部分およびメール本文の部分のサイズに制限がかけられます。
また、変数は記事とコマンドメールについてそれぞれ別に存在します。
必要なフィルタのみを有効にするとか、
○○の場合だけサイズ制限の機能を切るといった使い方も可能です(
もっとも、あまり複雑な条件下でのみフィルタといったことをしようとすると
HOOK による対応になるとおもいますが…)。
</para>

<para>
余談: Mail::Message クラスがメールを解析したさいに、
メールの各部分のサイズも分析済みなので、このフィルタの実装は簡単です:)
</para>

&var.filter.size;

</sect1>


<sect1 id="internals.filter.command">
	<title>
	フィルタ: コマンドメールへの制限
	</title>

<para>
コマンドメールには
「一通当たりのコマンド数」
や、
コマンド依存の制限、
たとえば「一回のコマンドメールで get できるファイルは 100 個までとする」
がありえます。
</para>

<!-- &table.list.filter.commandmail; -->

</sect1>


<sect1 id="internals.filter.mimecomponent">
	<title>
	フィルタ: mime component filter のルール構成法
	</title>

<para>
&fml4; の content filter とは異なり、
&fml8; の mime component filter のルールは
<screen>
text/plain 	permit
text/html	reject
*		permit
</screen>
のような空白区切りのフォーマットで書いてあります。
</para>

<para>
MIME が前提なので、!MIME (MIME 以外を対象とする)という命令はありませんが、
text/plain と multipart/mixed 中の text/plain を区別するために、
こういう書き方である必要があるとおもうわけです。
<screen>
全体			部分		アクション
----------------------------------------------
text/plain 		*		permit
multipart/mixed		text/plain	permit
multipart/mixed		text/html	reject
multipart/mixed		image/*		cutoff
*			*		permit
</screen>
さらに、将来はこういうのもありえるでしょうか？(未実装)
<screen>
text/plain 		:uuencoded:	cutoff
text/plain 		:size>500k	cutoff
</screen>
</para>

<para>
ただし、このルールに関しても構成上の問題点がいくつかあります。
</para>

<sect2>
	<title>
	first match vs last match ?
	</title>

<para>
アクションには first match のものとそうでないものがあります。reject は 
たいてい first match ですが、
cutoff は first match ではないとおもわれます。
さて？
</para>

</sect2>


<sect2>
	<title>
	permit の意味
	</title>

<para>
では、permit はどうでしょう？実のところ文脈依存と考えられますが、
何が正しいのかよくわかりません。
たとえば、multipart のメールの中身が
<screen>
text/plain + image/jpeg + text/html
</screen>
のように３つの異なるタイプのパートからなる場合、
どういうルールなら「あいまいさ」がないでしょうか？
</para>

<para>
結論をいえば、cutoff や reject を指定するタイプのルールしかうまく機能
しない、つまり「特定の○○を削除ないしは拒否する」ことならうまくできる
と言えそうです。ゆえにデフォルトは permit にするしかありません。
</para>

<para>
『permit は「個別に許す」という意味である』説と、『permit は「メール全
体を許す」という意味である』説の両方がありえます。たとえば、
<screen>
text/plain	*	permit
*		*	reject
</screen>
は text/plain は許す、それ以外のいかなる型も許さない。
これは text/plain に曖昧さがないので OK でしょう。
</para>

<para>
一方で『permit は「メール全体を許す」という意味である』説がありえます。
たとえば text/plain のメールだけを許したいとしましょう。 
直観的には、次のようなルールを書くとおもいます。
<screen>
text/plain	*	permit
*		*	reject
</screen>
しかし、これは permit が即 OK の意味でないとすると
<screen>		
*	*	reject
</screen>
と一緒になってしまうわけです。だから permit は「メール全体を OK として
ルールとの照らし合わせ処理をそこで終りにする」という意味にとらないとい
けません。よって、次のようなルールはありえないことになります。
<screen>
text/plain 		*		permit
multipart/mixed		text/plain	permit
multipart/mixed		text/html	reject
multipart/mixed		image/*		cutoff
*			*		permit
</screen>
ありえないというのは、このルールは次のように書いても同じだからです。
<screen>
text/plain 		*		permit
multipart/mixed		text/html	reject
multipart/mixed		image/*		cutoff
*			*		permit
</screen>
つまり permit 命令で処理が終ってしまうとすれば、multipart に対しては事
実上使ってはいけないことになる。『デフォルトの処理』か『text/plain * 』
のようなものに対してのみ permit 命令は意味があるわけです。
</para>

<para>
以下、first match を前提に、事例を考えてみましょう。
</para>

</sect2>


<sect2>
	<title>
	ケーススタディ: デフォルトの挙動
	</title>

<para>
暗黙のデフォルトルールは、他の header や text フィルタとの整合性を考え
ると「とりあえず通す」ですかね？
<screen>
*		*	permit
</screen>
これは content filter の「ルールをうまく書けない」という別の理由によっ
ても支持されるでしょう。
</para>

<para>
なお、デフォルトの挙動を reject に変更するには * * reject を最後に付け
加えてください。
</para>

</sect2>


<sect2>
	<title>
	ケーススタディ: text/plain (全体)のみをゆるす
	</title>

<para>
<screen>
text/plain	*	permit
*		*	reject
</screen>
</para>

</sect2>


<sect2>
	<title>
	ケーススタディ: text/plain があれば何でも許す
	</title>

<para>
text/plain があれば何でも許す。それ以外の型は拒否する。
これは簡単なルールが書けない例です。
<screen>
text/plain	*		permit
multipart/*	text/plain	permit
*		*		reject
</screen>
しかし、このルールでは
<screen>
text/plain + text/plain + text/plain
</screen>
でも、
<screen>
text/plain + text/plain + image/jpeg
</screen>
でもどっちも OK になってしまうのね。だめじゃん。
もっとも not オペレータ(!)があれば、解決は可能でしょう。
<screen>
text/plain	*		permit
multipart/*	!text/plain	reject
multipart/*	text/plain	permit
*		*		reject
</screen>
たぶん、これが期待される plain/text のみを通すルールだとおもいます。
</para>

</sect2>


<sect2>
	<title>
	ケーススタディ: 
	text/html (全体) および text/html を含む multipart だけを拒否
	</title>

<para>
<screen>
text/html	*		reject
multipart/*	text/html	reject
*		*		permit
</screen>
</para>

</sect2>


<sect2>
	<title>
	ケーススタディ: むずかしい例？
	</title>

<para>
これはどうでしょう？これは簡単なルールが書けない例です。
<screen>
text/plain	*		permit
multipart/*	text/plain	permit
multipart/*	*		reject
*		*		reject
</screen>
multipart の中身が text/plain からのみなるメールなら許す。つまり、
<screen>
text/plain + text/plain + text/plain
</screen>
は、OK。でも、
<screen>
text/plain + text/plain + image/jpeg
text/plain + image/jpeg + text/html
</screen>
これらも二番目のルールで permit されてしまいます。
うまく書けない例です。
</para>

</sect2>


<sect2>
	<title>
	ケーススタディ: 前例のバリエーションで reject ではなく cutoff
	</title>

<para> 
前例のバリエーションで reject ではなく cutoff の場合。
<screen>   
text/plain	*		permit
multipart/*	image/*		cutoff
multipart/*	text/plain	permit
*		*		reject
</screen>
つまり
<screen>
text/plain + text/plain + text/plain
</screen>
は、OK。一方、
<screen>
text/plain + text/plain + image/jpeg
</screen>
のメールは image/jpeg 部分を削って、3番めのルールで permit される。
もっとも、これだけだと
<screen>
text/plain + image/jpeg + text/html
</screen>
が通過可能ですが…
</para>

</sect2>


<sect2>
	<title>
	ケーススタディ: 前の例で cutoff + permit にすると？
	</title>

<para>
<screen>
text/plain	*		permit
multipart/*	image/*		cutoff
multipart/*	image/*		permit
multipart/*	text/plain	permit
*		*		reject
</screen>
なら、
<screen>
text/plain + text/plain + text/plain
text/plain + text/plain + image/jpeg
text/plain + text/plain + text/html
</screen>
どれも OK ですが、ルールのマッチする場所が異なります。
</para>

</sect2>

</sect1>


<sect1 id="internals.filter.mimecomponent.functions">
	<title>
	議論: mime component filter に必要な機能とは？
	</title>

<para>
以下は、fml-help ＭＬによるアンケートのまとめです。
</para>

<para>
結論を先に書くと、いまの機能でいいようで、content filter の起源となっ
た cutoff の機能はもう不要みたいです。
</para>

<para>
まずは content filter の使い方をいくつか考えてみます。
<screen>
(1) text/plain と multipart の text/plain だけは通す
	text/plain じゃないメールなんて○○ ML では不要
	だいたい virus つきかもしれないじゃん

(2) multipart で text/plain 以外があるようなメールは拒否
	virus つきかもしれないじゃん

(3) multipart の text/html 部分を削る
	いわゆる html メール
	＃ついでに text/plain に戻す(やりすぎかも…)？

(4) multipart の image/* 部分だけを削る
	画像なんてＭＬに送るなよぉ

(5) text/html のメールなんて spam に決まってるから捨てる
</screen>
で、アンケートの結果をまとめると、通常は (1) を使う。これは決まりみた
いです。規模とポリシー(きびしくできるか否かなど)によって(2)もアリかも
しれない。
たとえば社内ＭＬのようにメールの型を厳しくできないＭＬは、
そもそも mime component filter を on にしないというわけです。
</para>

</sect1>


</chapter>
