←メール環境整備編 ↑おもちゃ箱 →即席ニュース購読編

¶ メールの振り分けと自動返送編

  1. MH の slocal + .maildelivery を使う
  2. .maildelivery の書き方
  3. .maildelivery の例
  4. procmail を使う
  5. procmailrc の書き方
  6. procmailrc の正規表現
  7. procmailrc の例
  8. SPAM メールを除去
  9. 振り分けたメールの新着チェック

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

メーリングリストのメールなどが増えてくると、メールを差出人や Subject から分類し、フォルダを分けて格納したくなります。これは、 MH(slocal) や procmail を使って、メールの自動振り分けをする手引です。
  1. MH の slocal + .maildelivery を使う

    この設定については、とても書き切れないので

    O'Reilly & Associates,Inc. A NUTSHELL HANDBOOK
    "MH & xmh (E-mail for Users & Programmers)" 日本語訳
    ISBN4-7561-0298-0

    を参照することをお勧めします。

    ~/.forward の書き方
    "| /usr/local/lib/mh/slocal -user LOGIN_NAME"
    ダブルクォーテーション "" を必ず入れて下さい! .forward の注意点は、"" の前や後に余分なコードが 含まれてはいけないということです。特に改行コードを入れてしまいがち なので、emacs 派の方も vi で気をつけて作る方が良いかもしれません。 また、.forward は .maildelivery の設定が済んでから、最後に書き換え ます。別ファイルに書いておいて、 cp forward.src .forward するのも いいでしょう。

  2. .maildelivery の書き方

    .maildelivery は先頭から 1 行ずつ読み込まれて実行されます。 各 1 行は空白またはカンマ (,) で区切った field pattern action result string の 5 つから成ります。 引数自身に空白やカンマが含まれる場合は、ダブルクォート (") で囲ま なければなりません。ダブルクォート自身を引数に含めるには、 \" のようにエスケープできます。 第 3 引数 の action で、メールに対してどんな動作を実行するかを 指定し、ファイルに保存する場合は、実際にファイルに書き込まれた 場合に成功となります。 action が成功すると、配布済みのマークが付けられます。 各フィールドの意味は以下となります。
    1. field
      どのヘッダ行を対象とするかの指定です。 通常のヘッダ(From, Subject, Reply-to,..)なら何でも指定でき ますが、以下の特殊な値が使えます。
      • addr addr ryo2 とすれば、システム内で ryo2 にエイリアスさせた すべてのメールが対象になります。例えば、 ftpadmin: ryo2 が /etc/aliases にあれば、ftpadmin 宛メールも 含むことになります。
      • source MTAの付ける送信者アドレス(普通 From:) を 対象とします。
      • default それまでの行指定で、配布済みとならなかった メールが対象となります。
      • * 配布済みか否かに関わらず、すべてのメールを対象とします。
    2. pattern
      fieldで指定したヘッダフィールドに対して、 マッチさせる文字列を指定します。この指定を部分文字列として 持つ場合にマッチしているとみなされ、正規表現は残念ながら 使えません。例えば Subjectフィールドに [hogehoge ML] をマッチ させると、「Re: あなたも [hogehoge ML] に入る?」という Subject にも当然マッチします。 第 1 引数に default* を指定した場合、 pattern としてハイフン - を指定します。
    3. action
      選択されたメールに対する処理動作を書きます。以下の記号または 単語で指定します。
      • file または >
        第 5 引数のパス名のファイルにメールを追加します。 例えば、 /var/mail/MYNAME などのスプールを指定するなど。
      • qpipe または ^
        rcvstore などメールを読み込んで動作するプログラムを 指定します。メールは標準入力から渡されます。 例えば、
        *  -  ^  R /usr/local/lib/mh/rcvtty
        *  -  ^  R "/usr/local/lib/mh/rcvtty -format \"Mail $(size) bytes from $(sender)\""
        なら それぞれ「メールの Subject」「メールのサイズと送信者」 をコンソールに通知します。ただし、 $(sender) のような 変数にダブルクォートが含まれると大変なので要注意です。 (mesg n していると通知されません)
      • pipe または |
        qpipe と同じですが、第 5 引数で指定したコマンドを Bourne シェルを通して実行します。複数のコマンドを 実行したり、リダイレクトするのでなければ qpipe の方が効率は良いです。第 5 引数が "" で囲まれているとき、その中の '' は qpipe で解釈されず pipe では解釈されます。例えば、
        To,ryo2,|,A,"/bin/echo \"Thanks for your message. -- Ryo2\" | /usr/bin/mail '$(reply-to)';/usr/local/lib/mh/rcvpack ryo2
        では、 ryo2宛のメールに対して、自動応答メールを送り、 ファイル ryo2 に受信メールを圧縮保存します。実際には From: "George Harrison" からメールが 来ると(そんなわきゃない) 、
        /bin/echo "Thanks for..-- Ryo2" | /usr/bin/mail '"George Harrison" '
        を 実行することになります。
      • destroy
        これは、第 4 引数に A を、第 5 引数にハイフン - を付けて 使い、メールを配布済みとし、実際には何もしません。 つまり、指定の受信メールを捨てるときに使います。
    4. result
      以下のような一文字で指定します。
      A
      field,patternで指定されたメールに対し action が成功すると配布済みフラグを立てます。
      R
      field,patternで指定されたメールに対し action を実行しますが、配布済みフラグを立てません。
      ?
      配布済みフラグが設定されていないメールに対して、 アクションを実行します。アクションが成功すると、 メールを配布済みとします。
      N
      配布済みフラグが設定されておらず、直前のアクションが 成功したメールのみを対象とします。アクションが成功すると、 メールを配布済みとします。ただし、直前のアクション は * で選択されたものではないことが条件です。
    5. string
      選択されたメールに対して実行するコマンドを指定します。 第 3 引数が file の場合は追加保存するファイル名を指定します。 コマンドやファイル名に空白やカンマ (,) が含まれている場合は、 ダブルクォート "" で囲まなければなりません。
    6. select
      第 6 引数として、 select を指定できます。これには select starttime endtime のように時刻指定を第 7,8 引数として、 hh:mm (時:分)の形で しなければなりません。この select は、「メールを処理している 時刻が starttimeから endtimeの間である」または「slocalコマンド を起動しているホストにログインしている」場合にはこの行が 実行されないという指定になります。

  3. .maildelivery の例

    # (A) Throw away all mail from this guy:
    from    foo@bar.com   destroy ?   -
    # (B) Put the rest into loop maildrop
    resent-comments noloop  destroy A   -
    # (C) File foo mailing list in +ML_name folder; I read it later:
    reply-to man-jp@jp.freebsd.org  A "/usr/local/lib/mh/rcvstore +man-jp"
    # (D) recieve message
    *  -  ^  R  /usr/local/lib/mh/rcvtty
    # (E) Put the rest into my maildrop:
    default -               file    ?    /var/mail/ryo2
    
    この例で有効な A〜E の後の 5 つの行は、それぞれ以下の意味です。

    (A) foo@bar.com からのメールは捨てる
    (B) ループしているメールを再送しないおまじない
    (C) man-jp メーリングリスト宛のメールは、 すべて man-jp フォルダに入れる。
    (D) 受け取ったメールの Subject をコンソールに 通知する。
    (E) 上述の A, B, C で引っかからなかったものは、 個人用スプールに落す。(incしたときに inbox に入る)

  4. procmail を使う

    procmail は、正規表現も使え、MH + .maildelivery より細かい指定が できて大変便利です。FreeBSD には port がありますが、 make ; make install の際は、 unset LD_LIBRARY_PATH してから やって下さい。 また、設定例だけを参照して使うのは危ないので、斜め読みでも 良いのでから、必ずマニュアル man procmail, man procmailrcを 眺めましょう。メーリングリストを運営する人には man procmailex も必要になります。以下の説明は、 procmail-3.11pre7 を想定しており、 MH のフォルダにメールを 格納することを前提として書きます。

  5. procmailrc の書き方

    procmail は、配送レシピ procmailrc に依って動きます。システムとしては /usr/local/etc/procmailrc を参照し、それが無ければ、 $HOME/.procmailrc を見に行きます。また、 procmail -p RCFILE として別の ファイルを指定も出来ます。
    procmailrc には上記環境変数が無い場合のデフォルトを書き込んでおけます。 '#' があると行末までをコメントとみなします。 本体の基本形は、
    :0  [フラグ]  [ : [ローカルロックファイル] ]
    <一行に一つの条件文(複数の条件も可)>
    <アクション文(一行のみ)>
    で、このブロックをレシピと呼びます。カッコ [ ]内は省略可能を 意味します。例えば
    :0                 # foo@bar.comから来たメールすべて
    * ^From.*foo@bar.com
    todd/.             # MHのフォルダ $MAILDIR/todd に入れる
    
    が一つのレシピです。 :0: や :0 b: と書くとローカルロックファイルを作成することになり、
    :0:mllock のように書くとローカルロックファイル名を指定することになります。
    フラグの文法は、以下です。

    H
    ヘッダを内蔵の egrep にかける (デフォルト) 。
    B
    ボディを内蔵の egrep にかける。
    D
    内蔵 egrep に大文字小文字を区別させる (デフォルトは区別しない) 。
    A
    直前のレシピの条件文に該当した場合に実行される。
    a
    A フラグと同じだが、直前に実行されたレシピのアクション文が 成功していて、エラー終了していないことが、実行の条件。
    E
    直前のレシピの条件文に該当せず、実行されなかった場合に実行される。 E の付くレシピを繰り返すと、 if 〜 elseif 〜 elseif のような作業が可能。
    e
    直前のレシピのアクション文がエラー終了した時に実行される。
    h
    メールヘッダをアクション文のコマンドへパイプする(デフォルト)
    b
    メールボディをアクション文のコマンドへパイプする(デフォルト)
    f
    パイプ(アクション文)をフィルタ とみなす。
    c
    メール(プロセス)のカーボンコピーを作成し、このレシピ部分の 作業が子プロセスとして分離され、条件にマッチした場合でも 次のレシピに進みます。すなわち、 .maildelivery の 第 4 引数(result) の R と同じ意味となります。
    w
    アクション文のフィルタやプログラムの実行を待って、終了コードを チェックする (デフォルトでは無視する) 。エラー終了すると、 フィルタにメールを渡さないことになる。
    W
    w フラグと同じだが、'プログラムエラー'のメッセージは出さない。
    i
    そのレシピの書き込みエラーを無視する。
    r
    raw モード。メールの最後が空行であることを確認しない。
    各フラグは、 :0 Aic : のように続けて記述できます。

    条件文の先頭は、通常 * で始めますが、以下のものが使えます。

    !
    条件の否定(反対)
    $
    メタキャラクタが sh の "" 内と同じに扱われる。
    ?
    指定したプログラムの終了コードを条件に使う。
    < NUMBER
    メールのサイズが NUMBERバイト(10進)以下なら条件に該当。
    > NUMBER
    メールのサイズが NUMBERバイト(10進)以上なら条件に該当。
    {
    直後に 1 つ以上の空白、タブ、改行を入れて、ブロックの 開始を意味します。 } は単にブロックを閉じる意味ですが、 ブロックは閉じなければなりません。ブロック内には フラグとしては B, H しか効きません。
    条件文を * ^To:.*foo@bar.com とすると To: フィールドに foo@bar.com の 単語が含まれる場合のみになりますが、
    * ^TOfoo@bar.com とすると、 To:, Cc:, Bcc: のいずれに foo@bar.com という単語が含まれている場合にマッチします。
    * ^TO_foo@bar.com とすると、 To:, Cc:, Bcc: のいずれに foo@bar.com のアドレスが含まれているとマッチします。つまり、 "To: foolish man " は、^TO_foo にはマッチしない が、 ^TOfoo では マッチすることになります。 ^TO_ の方が厳密に選択されるので、通常は ^TO_ がお勧めです。 (ただし、この ^TO_ は procmail-3.11 以前のものでは使えません)

    ファイルに保存したい場合は、アクション文に保存するファイル名を 指定しますが、
    foldername/.
    とした場合、$MAILDIR/subdir フォルダ(ディレクトリ)の下に MH 形式で メールをセーブします。単に foldername
    とすると、$MAILDIR/foldername/msg.??? などというファイルにセーブして しまうので気を付けて下さい。

  6. procmailrc の正規表現

    procmailrc の内部 egrep には以下のような通常の正規表現が使えます。

    ^
    行頭
    $
    行末
    .
    改行以外の任意の 1 文字
    a*
    0 個以上の a
    a+
    1 個以上の a
    a?
    0 個または 1 個の a
    [^-a-d]
    -, a, b, c, d 以外の一文字
    de|abc
    de または abc
    (abc)*
    0 個以上の abc の繰り返し
    \.
    ドット (.) 一文字を意味します。

    procmail に特有な表現として、以下も使えます。

    ^ または $
    複数の連続した改行にマッチ
    ^^
    検索領域の一番先頭。表現の最後に使った場合は検索領域の一番最後。
    \< または \>
    単語の前と後にマッチ。[^a-zA-Z0-9_] や改行にマッチします。 単語間の空白でなく、単語境界であるかどうかのチェックに使います。
    \/
    これ以降の正規表現にマッチした文字を環境変数 MATCH に入れます。

  7. procmailrc の例

    以下は ~/.procmailrc に書く基本的なレシピの例です。

    *メーリングリストのメールの仕分け
    FreeBSD-users-jp ML のメールを freebsd-users フォルダへ、 リムネットからのお知らせを rim-support フォルダへ入れる場合です。 どちらもローカルロックファイルを使うよう指定しています。
    PATH=/bin:/sbin:/usr/bin:/usr/local/bin:.
    MAILDIR=$HOME/Mail
    DEFAULT=/var/mail/ryo2
    LOGFILE=$MAILDIR/from
    LOCKFILE=$HOME/.lockmail
    SHELL=/bin/sh
    SENDMAIL=/usr/sbin/sendmail
    
    :0:
    * ^TO_FreeBSD-users-jp@jp.freebsd.org
    freebsd-users/.
    
    :0:
    * ^From.*rim-office@rim.or.jp
    rim-support/.
    

    *vacation コマンド相当の自動返信メールを出す場合
    vocation コマンド (自分が長期留守にする場合の自動応答メール) と同等のことをするレシピは次のようなものです。 ただし、メーリングリストからのメール一通毎に自動で返信して しまいますので、決してこのままでは使わないで下さい。 知人の範囲内に制限するのが無難かと思います。
    :0 Whc: vacation.lock
    * $^To:.*\<$\LOGNAME\>
    * !^FROM_DAEMON
    * !^X-Loop: flyboy@n24.net
    | formail -rD 8192 vacation.cache
    
      :0 ehc         # if the name was not in the cache
      | (formail -rA"Precedence: junk" \
           -A"X-Loop: your@own.mail.address" ; \
         echo "I received your mail,"; \
         echo "but I won't be back until Monday."; \
         echo "-- "; cat $HOME/.signature \
        ) | $SENDMAIL -oi -t
    

    *WEB ページの記帳者へ自動でお礼メールを出す場合
    例えば、 WEB ページの訪問者登録で CGI から、自分宛に

    Subject: [WWW Visitor] Signed Up
    From: WEB-Server <nobody@serverhost>
    To: flyboy@n24.n24 (自分)
    Date: Thu, 03 Jul 1997 17:44:14 +0900
    Reply-To: foo@bar.com (訪問者)

    というヘッダで自動メールを出すようにしている場合に、 procmail で訪問者宛に自動お礼メールを出すことを考えてみると
    :0 Whc:
    * ^From.*WEB-Server
    * ^Subject:.*[WWW Visitor]
    | (formail -rA"Precedence: junk" \
       -A"X-Loop: flyboy@n24.net" ; \
       -A"Content-Type: Text/Plain; charset=iso-2022-jp" ; \
       echo "Thanks to sign my guestbook up."; \
       echo ""; \
       cat $HOME/.signature \    ←日本語(JIS-2022-JP-1)のシグネチャ
      ) | $SENDMAIL -oi -t
    
    日本語のお礼文を送るためには、途中 (formail .. ; ここ ; ) に cat thanks.txt のように JIS の文章を挟めばできるのは、 言うまでもありません。(..言ってるけど ^^;)

    *転送と保存を同時に行なう場合
    peter から来たメールで Subject に compilers を含むものは、 william に転送して、かつフォルダ petcompil にセーブするなら こうなります。
    :0 c
    * ^From.*peter
    * ^Subject:.*compilers
    ! william@somewhere.edu
    
    :0 A
    petcompil	    
    
    全く同じことを
    :0
    * ^From.*peter
    * ^Subject:.*compilers
    {
       :0 c
       ! william@somewhere.edu
    
       :0
       petcompil
    }
    と書くこともできます

    *メーリングリストからのメールを MHonARC で自動的に Web に登録する場合
    例えば、FreeBSD-users-jp メーリングリストからのメールを、フォルダ ~/Mail/users-jp に保存し、MHonArc で自動的に Web を作るなら、
    
    LOCKFILE=$HOME/.lockmail
    MAILDIR=$HOME/Mail
    MHONARC=/home/ryo2/bin/mhonarc
    MHONARCRC=/home/arai/lib/MHonArc/MHonArcRC
    PATH=/bin:/usr/bin:/usr/local/bin:.
    
    :0
    * ^X-Sequence:.*FreeBSD-users-jp
    {
            :0 c
            FreeBSD-users-jp/.
    
            :0 w
            | cd $MAILDIR && ($MHONARC -add -rcfile $MHONARCRC -outdir /home/ryo2/public_html/users-jp FreeBSD-users-jp < /dev/null)
    }
    のようにすれば、メーリングリストからのメールが来る度に
    http://(WWWサーバ名)/~ryo2/users-jp/ にページが増えてゆき、 http://(WWWサーバ名)/~ryo2/users-jp/(maillists|threads).html も更新されていくことになります。

    procmailrc の書き方のいろいろな例は、 man procmailex で見られますが、 man procmairc も読むことをお勧めします。

  8. SPAM メールを除去

    俗に言う SPAM メールには誰しもがうんざりさせられていると思います。 撃退方法は環境に依るわけで、専用線接続の場合は、メールサーバ側で リレーの制限と共に、ORDB(Open Relay DataBase)MAPS(Mail Abuse Prevension System)のような SPAM メールデータベースを作って受信拒否をするということもできます。 しかし、これはサーバを管理している人にしかできず、Dialup PPP等で メールを POP してくる環境では簡単な方法はありません。
    例えば、POPクライアントを改造し、POP 時にヘッダを読み出しチェック して、SPAM フィルタ(もちろん自分で作る)に引っかかったものは消去し て POP しない、という方法も考えられます。この方法ではすべての受信 メールのヘッダを一度読み込む必要があり、POP に余分に時間がかかると いう欠点があります。
    以下は、シェル環境の使えたリムネットで私の利用していた方法です。 自前で procmail をプロバイダのホストにインストールし、 プロバイダのホスト上で ~/.procmailrc に
    :0:
    * !(^TO|^Subject:.*)(fvwm|ryo2)
    * !(^TO|^Cc:.*).*(\.jp|\.org)
    * !^FROM_DAEMON
    * !^FROM_MAILER
    * !(^X-Sequence|^X-ML-Name).+
    spam/.
    というレシピを入れてました。これは、

    という条件を満たしたものだけは、spamフォルダに入れる(つまり POPしなく て済む)という、かなりいい加減なしろものです。

    もちろんこれは、「自分のメールアドレスが .jp か .orgで、海外からの メールは fvwmメーリングリスト以外は必ず To: や Cc: に自分のアドレスが 入っているメールしか来ない」という場合に限って使える方法であり、自分の アドレスが To: に含まれている SPAM や国内発の SPAM メールは除けません。
    もちろん、このままではプロバイダの自分のディスクスペースを喰うので、定 期的に cron ないし 手動で $MAILDIR/spam 以下のゴミ掃除をする必要があり ますが、「.procmailrc の記述が問題なし」という自信がついたら spam/.のところを/dev/nullにしてしまえば掃除も不要になり ます。 これは、「インターネットサービスプロバイダでメールサーバが動いているホスト でシェル環境が使える」という、かなり限られた場合においてのみ使える、かなり 乱暴な解決策でしかないので、御自分の環境に合わせて考えてみて下さい。 たとえ SPAM メールを POPせざるを得ない自宅の環境でも、上記の方法を使う と、メール整理の手間が減り inbox に SPAM が混じらない、という精神衛生上の メリットはあるかもしれません(^^;)。

    ■ SPAM 情報について

    自分で管理できるメールサーバを持っている場合は、 SPAM の現状と対策SPAM情報 等に、SPAM とは何か、現状、自サーバが SPAM メールの踏み台にされる (=中継に利用される)ことを避ける方法等が詳しく書かれていて、勉強になります。 また、sendmailの設定に限れば、http://www.sendmail.org/m4/anti-spam.htmlに設定方法があります。


  9. 振り分けたメールの新着チェック

    新しいメールが複数のフォルダに振り分けられた場合、未読のメールを順 番に調べるのは簡単ではありません。 私は MH, mh-e, Mew(1.85以前)の場合は ~/.mh_profile に
    Unseen-Sequence: unseen
    などと書いて、未読チェックスクリプトunseen を作ったりしていましたが、これも inbox フォルダ一つしか対処できな いので、slocalや procmailによるメール振り分けには無効です。
    メーラに Mewを使っている場合は、 Prom-Mew という、至極便利なツールがあります。