<!--
 $FML: recipes.filter.sgml,v 1.2 2006/01/15 13:47:38 fukachan Exp $
-->

<qandaset>


<qandaentry>

<question>
<para>
フィルタシステムを使わない独自のフィルタを設定したい
</para>
</question>

<answer>
<para>
&fml8; のフィルタシステムで利用可能なフィルタは 
<screen>
/usr/local/etc/fml//defaults/$VERSION/default_config.cf.ja
</screen>
を読むと分かります。
</para>

<para>
この内蔵フィルタで実装されていない内容のフィルタは
article_post_verify_request_end_hook
article_filter_start_hook  
article_filter_end_hook  
article_post_run_start_hook  
のいづれかの HOOK でおこなえばよいでしょう。
<screen>
$article_filter_start_hook = q{
	my $header = $curproc->incoming_message_header();
	my $body   = $curproc->incoming_message_body();

	if (条件) {
		$curproc->policy_reject_this_message();
	}
};
</screen>
のように書けば良いでしょう。
例については以下のレシピも参照して下さい。
</para>

</answer>

</qandaentry>


<qandaentry>

<question>
<para>
特定のメールアドレスを拒否したい
</para>
</question>

<answer>
<para>
たとえば特定のドメイン(\S+\@example.co.jp)は全部はじきたいといった
フィルタのことです。
内蔵フィルタの一部として実装しようと考えていますが、
残念ながら未実装です _o_
</para>
</answer>

</qandaentry>


<qandaentry>

<question>
<para>
message/partial をフィルタしたい。
</para>
</question>

<answer>
<para>
mime component filter で「message/partial * reject」を設定して下さい。
<screen>
[/usr/local/etc/fml/mime_component_filter]

# allow only text/plain messages.
message/partial *		reject
text/plain	*		permit
text/html	*		reject
multipart/*	!text/plain	reject
</screen>
</para>

<para>
注意: このフィルタは全ＭＬに反映されます。
</para>
</answer>

</qandaentry>


<qandaentry>

<question>
<para>
危ない添付ファイルがついてきたメールは拒否する HOOK
</para>
</question>

<answer>

<para>
fml8 は、あらかじめ入力されたメッセージを解析し、鎖状の Mail::Message 
オブジェクトの形で持っています。そこで、MIME/Multipart の各部分のヘッ
ダを取り出して、正規表現で順に確認すればよいだけです。
</para>

<para>
HOOK でなんとかする/ごまかす例は次の通りです。
どちらの例でも、マッチした場合、stop_this_process() 命令で、
これ以上の処理を止めています。
また、この例では何もエラーメッセージを返そうとしていないため、
結果として、このメッセージは無視されて終りになっています。
</para>

<para>
返事をしてあげたいなら、 reply_message() で、何か返事を返してあげてく
ださい…が、たぶんウィルスとかなので、返事してあげなくてもいいとおもふ。
</para>

<para>
.exe など特定のファイル拡張子にマッチするか？を調べる例。
に昔の fml8 (2004/12/08 以前)なら、こんな感じ。
<screen>
$distribute_verify_request_start_hook = q{
    my $msg = $curproc->incoming_message() || undef;
    for (my $m = $msg; $m ; $m = $m->{ next } ) {
	my $hs = $m->message_fields() || '';
	if ($hs =~ /filename=.*\.(com|vbs|vbe|wsh|wse|js|exe|doc|rtf)/o) {
	    $curproc->log("attachment \.$1 found");
	    $curproc->stop_this_process();
	}
    }
};
</screen>
2004/12/08 以降なら、こんなかんじ。
<screen>
$distribute_verify_request_start_hook = q{
    my $msg  = $curproc->incoming_message() || undef;
    my $list = $msg->message_chain_as_array_ref();
    for my $m (@$list) {
	my $hs = $m->message_fields() || '';
	if ($hs =~ /filename=.*\.(com|vbs|vbe|wsh|wse|js|exe|doc|rtf)/o) {
	    $curproc->log("[new] attachment \.$1 found");
	    $curproc->stop_this_process();
	}
    }
};
</screen>
</para>

<para>
別の解としては、
<screen>
Content-Disposition: attachment;
</screen>
という表示になることを前提に、これを引っかけるという案もあります。
<screen>
$distribute_verify_request_start_hook = q{
    my $msg = $curproc->incoming_message() || undef;
    for (my $m = $msg; $m ; $m = $m->{ next } ) {
	my $hs = $m->message_fields() || '';
	if ($hs =~ /Content-Disposition:.*attachment;/o) {
	    $curproc->log("attachment \.$1 found");
	    $curproc->stop_this_process();
	}
    }
};
</screen>
2004/12/08 以降なら、こんなかんじ。
<screen>
$distribute_verify_request_start_hook = q{
    my $msg  = $curproc->incoming_message() || undef;
    my $list = $msg->message_chain_as_array_ref();
    for my $m (@$list) {
	my $hs = $m->message_fields() || '';
	if ($hs =~ /Content-Disposition:.*attachment;/o) {
	    $curproc->log("[new] attachment \.$1 found");
	    $curproc->stop_this_process();
	}
    }
};
</screen>
</para>

</answer>

</qandaentry>


<qandaentry>

<question>
<para>
フィルタで弾いた時に、エラーメールをどこへ返す
</para>
</question>

<answer>

<para>
デフォルトでは
<screen>
use_article_filter_reject_notice	=	yes
article_filter_reject_notice_recipient	=	maintainer sender
</screen>
となっています。
つまり、エラーメールを返し、返す先はＭＬの管理者と送信者の両方です。
</para>


<para>
返送する先を送信者( From: のアドレス )にするには
<screen>
article_filter_reject_notice_recipient	=	sender
</screen>
としてください。
</para>


<para>
ＭＬの管理者と送信者( From: のアドレス )の両方に返すには
<screen>
article_filter_reject_notice_recipient	=	maintainer sender
</screen>
としてください。
</para>


<para>
そもそもエラーメールを返さないようにするには、
<screen>
use_article_filter_reject_notice	=	no
</screen>
としてください。
</para>

</answer>

</qandaentry>


<qandaentry>

<question>
<para>
spamassassin で SPAM メールを無視する。
</para>
</question>

<answer>

<para>
<caution>
<para>
そもそも spamassassin をよびだす内蔵フィルタがあるのですが、
それをあえて使わない例です。
</para>

</caution>
</para>

<para>
1. 内蔵フィルタの方の設定です。
<screen>
use_article_spam_filter         =       yes
article_spam_filter_drivers     =       spamassassin
</screen>
</para>

<para>
2. HOOK でなんとかする/ごまかす例は次の通りです。
<screen>
$distribute_verify_request_end_hook = q{
        my $spamassassin = '/usr/pkg/bin/spamc -c';

        use FileHandle;
        my $wh  = new FileHandle "| $spamassassin";

        if (defined $wh) {
                $wh->autoflush(1);
                my $msg = $curproc->incoming_message();
                $msg->print($wh);
                $wh->close();
                if ($?) {
                        $curproc->log("spam: (code = $?)");
                        $curproc->stop_this_process();  
                }
        }
};
</screen>
</para>

</answer>

</qandaentry>


<qandaentry>
<question>
<para>
spamassassin で SPAM メールと判定したら、
ヘッダに X-Spam-Status: Yes をつける。
</para>
</question>

<answer>
<para>
<screen>
$distribute_verify_request_end_hook = q{
	my $spamassassin = '/usr/pkg/bin/spamc -c';

	use FileHandle;
	my $wh  = new FileHandle "| $spamassassin";

	if (defined $wh) {
		$wh->autoflush(1);
		my $msg = $curproc->incoming_message();
		$msg->print($wh);
		$wh->close();
		if ($?) {
			$curproc->log("spam: (code = $?)");
			my $hdr = $curproc->incoming_message_header();
			$hdr->add('X-Spam-Status', 'Yes');
		}
	}
};
</screen>
</para>

<para>
ちょっとトリッキーですが、動きます。
</para>

</answer>

</qandaentry>


<qandaentry>

<question>
<para>
SPAM 対策として、To: および Cc: に ML 名を必須としたい
</para>
</question>

<answer>
<para>
HOOK で行なうなら次のようになります。
<screen>
$article_filter_end_hook = q{
	my $header = $curproc->incoming_message_header();
	my $addr   = $config->{ article_post_address };
	my $to     = $header->get('To') || '';
	my $cc     = $header->get('Cc') || '';
	my $to_cc  = "$to, $cc";
	my $found  = 0;

	use Mail::Address;
	my (@addrlist) = Mail::Address->parse($to_cc);
	for my $a (@addrlist) {
		my $_addr = $a->address();
		if ($_addr =~ /^$addr$/i) {
			$found = 1;
		}	
	}

	if ($found) {
		$curproc->log("article_post_address found in header");
	}
	else {
		$curproc->log("article_post_address not found in header");
		$curproc->policy_reject_this_message();
	}
};
</screen>
</para>
</answer>

</qandaentry>


<qandaentry>

<question>
<para>
ＭＬ独自の Message-ID: をつけて矯正したい
</para>
</question>

<answer>
<para>
未実装です。
</para>

<para>
&fml4; では「In-Reply-To: や References: をつけてくれない MUA があるた
めにスレッドがうまく扱えない」という問題を解決するために、ＭＬ独自の 
Message-ID: をつけて矯正するという機能がありました。現代では、そういっ
た MUA も死に絶えたでしょうから &fml8; では考慮しないことにします。
</para>

</answer>

</qandaentry>


</qandaset>
