Mac OS X Server での Sieve の利用方法

2013/8/27, 2013/9/17, 2013/10/10, 2013/10/11


Contents

概要

メールフィルタリング言語 Sieve の Mac OS X Server (10.8.4 Mountain Lion) における使い方の簡単な説明をします。

準備

Mac OS X Server 10.8.4 では、Sieve の使用を開始するための特別な設定は必要なく、ユーザ毎の作業ディレクトリとスクリプトファイルを用意すれば使用可能となります。

その作業ディレクトリなどにはユーザの GeneratedUID が使われています。GeneratedUID は、オープンディレクトリにいるユーザの場合は、

$ dscl /LDAPv3/127.0.0.1 -read /Users/$LOGNAME GeneratedUID
ローカルアカウントにいるユーザの場合は、
$ dscl . -read /Users/$LOGANME GeneratedUID
というコマンドで表示することができます。

また、次のようにしておくと、自身の GeneratedUID を環境変数 $GUID として利用可能になり、便利です。よって、以降の説明では、GeneratedUID は $GUID と表します。

$ export GUID=$(dscl /LDAPv3/127.0.0.1 -read /Users/$LOGNAME GeneratedUID | cut -f 2 -d ' ')

さて、ここで設定に必要な作業ディレクトリのパスは /Library/Server/Mail/Data/rules/$GUID です。
このパスとなっているのは Mac では、dovecotの設定ファイル /Library/Server/Mail/Config/dovecot/conf.d/90-sieve.conf の 設定パラメータ sieve_dir で設定されているからです。
作業ディレクトリを作成したら、まず、dovecot プロセスが書き込み可能にしておきましょう。例えば、オーナーをそれぞれのユーザ、グループを mail にして、グループに書き込み権を付けるなどとします。

さらに、スクリプトファイルのパスは /Library/Server/Mail/Data/rules/$GUID/dovecot.sieve となっています。
ちなみにこのパスは、dovecot の設定ファイル /Library/Server/Mail/Config/dovecot/conf.d/90-sieve.conf の設定パラメータ sieve で設定されています。

さて、この作業ディレクトリにはログファイルやコンパイル済みのスクリプトファイル (dovecot.svbin) が保存されます。
コンパイル済みスクリプトファイルが存在しない場合や、スクリプトファイルよりタイムスタンプが古い場合は、メール受信時に自動的にコンパイル済みスクリプトファイルが作成され、実際の動作においてはコンパイル済みスクリプトファイルが使用されることに留意しましょう。

そういったスクリプトファイルのコンパイルは sievec コマンドで行うことができます。
スクリプトファイルを上記パスに置く前に、手動でコンパイルしてエラーチェックをしておくとよいでしょう。

スクリプトファイル

スクリプトファイルの書き方は以下の記事がわかりやすく参考になります。

また、Dovecot で実装されいてる機能の詳細が以下で説明されています。

基本的なスクリプトの書き方や詳細は上記を参考にしてください。
ここでは以下のサンプルスクリプトをもとに、いくつかの応用的な使い方を説明します。

require "fileinto";
require "mailbox";
require "variables";
require "copy";
require "regex";
require "date";
require "envelope";

set "SW_FORWARDING" "on";
set "FORWARDING_TO" "foo@example.net";

set "RECEIVERS" "foo@example.com|bar@example.com";

if string :is "${SW_FORWARDING}" "on" {
   redirect :copy "${FORWARDING_TO}";
}

if
allof (
      address :regex ["To", "Cc"] "${RECEIVERS}",
      not envelope :regex "From" "MAILER-DAEMON|${RECEIVERS}",
      not address  :regex "From" "MAILER-DAEMON|${RECEIVERS}"
)
{
   if currentdate :matches "year"  "*" { set "year"  "${1}"; }
   if currentdate :matches "month" "*" { set "month" "${1}"; }
   if currentdate :matches "day"   "*" { set "day"   "${1}"; }

   fileinto :create "Test.${year}${month}${day}";
}

このスクリプトは次のような動作をします。

変数を用いた条件判断

まず、メールを転送するか否かの判断とメールの転送は以下の部分で行っています。

set "SW_FORWARDING" "on";
set "FORWARDING_TO" "foo@example.net";
	:
if string :is "${SW_FORWARDING}" "on" {
   redirect :copy "${FORWARDING_TO}";
}

string:is で、変数 SW_FORWARDINGon であるかを判断し、真のとき、redirect コマンドで、変数 FORWARDING_TO にセットされたアドレス(この場合 foo@example.net) へメールを転送しています。
ここで、:copy によって、keepの暗黙のキャンセルをしないようにしています。これがない場合には、他のスクリプト行との関連によってはサーバにコピーが残らなくなることがあるので注意が必要です。

上記の例では、変数 SW_FORWARDINGon であるためメールの転送が行われますが、以下のように変数への値のセット部分を変えるだけでメールの転送は行われなくなります。

set "SW_FORWARDING" "off";

なお、変数を使うには variables 拡張、:copy を利用するには copy 拡張が必要となります。

正規表現の利用

次に、メールの宛先、送信者によって、メールをフォルダへ振り分けるか否かを判断している部分を以下に示します。

set "RECEIVERS" "foo@example.com|bar@example.com";
	:
if
allof (
      address :regex ["To", "Cc"] "${RECEIVERS}",
      not envelope :regex "From" "MAILER-DAEMON|${RECEIVERS}",
      not address  :regex "From" "MAILER-DAEMON|${RECEIVERS}"
)
{
	:
}

ここでは、変数と正規表現を利用した条件判断を行っています。

allof はカンマ区切りで列挙されたすべての条件が真であった場合に真となる論理演算です。
この場合の最初の条件は、変数 RECEIVERS にセットされたメールアドレス(ここでは、foo@example.com または bar@example.com) 宛 (To または Cc) のメールであること、二番目と三番目の条件は、送信者が MAILER-DAEMON でも、変数 RECEIVERS にセットされたどのメールアドレスでもないことの判断を行っています。
ここで、address はメールヘッダの From, To, Cc などを参照しますが、envelope は SMTP 通信中の MAIL FROM や RCPT TO などを参照する条件判断です。なお、envelope を利用するには envelope 拡張が必要となります。

このように複数のメールアドレスのセットを何回も利用したい場合、変数が便利ですが、通常の条件判断では、例えば

set "RECEIVERS" "foo@example.com,bar@example.com";
if address :contains ["To", "Cc"] ["${RECEIVERS}"]
のように書いたとしても foo@example.com と bar@example.com が列挙されたとは見なされず、"foo@example.com,bar@example.com" とマッチするか判断されてしまいます。それに対し、正規表現を用いるとより柔軟な条件判断が可能になります。

なお、正規表現を利用するには regex 拡張が必要となります。

日付・時刻の利用

続いて、Test.受信日 という名前のフォルダを作るために、現在の年月日をそれぞれ変数 year、変数 month、変数 day にセットしている部分を以下に示します。

   if currentdate :matches "year"  "*" { set "year"  "${1}"; }
   if currentdate :matches "month" "*" { set "month" "${1}"; }
   if currentdate :matches "day"   "*" { set "day"   "${1}"; }

年月日を変数に直接セットすることができないため、テストでマッチした部分を取り出すことができる variables 拡張の仕組みを用いて実現しています。この方法は、RFC 5260: Sieve Email Filtering: Date and Index Extensions に記載されています。

なお、日付・時刻を使うには、date 拡張が必要となります。

フォルダの作成と振り分け

最後に、以下の部分でメールのフォルダへの振り分けを行っています。

   fileinto :create "Test.${year}${month}${day}";

fileinto コマンドでメールを Test.受信日 フォルダへ振り分けを行っていますが、ここで、:create があるため、もし Test.受信日 フォルダが存在しなかった場合には自動的に作成されます。

このように、名前に日付を含むなど、あらかじめ作成しておくことが難しいフォルダも、スクリプトファイルで必要なときに作成することが可能です。

なお、fileinto コマンドは fileinto 拡張、:create を利用するには mailbox 拡張が必要となります。

欠点

現在のところ、外部コマンドが使用できません。

(2013/9/17追記)
Dovecot の sieve サポートは Pigeonhole プロジェクトで行われています。
その最新版では独自の拡張である pipe 拡張、filter 拡張、execute 拡張で外部コマンドの利用が可能となっています。
また、同じ message ID のメールを複数配信しないようにする独自拡張である duplicate 拡張も、実験的ではありますが存在します。
ただし、これらの独自拡張は既定では無効となっており、Mac OS X Server 10.8.4 の Dovecot においても利用はできません。


Copyright (C) 2013 by Jun Takahashi