James君!〜用意されているMailet

Mailet の注意事項

具体的にメールへの処理をさせるのが Mailet である。しかし、「させたい内容」に応じて、次節で触れる「パイプライン」の流れの中で、処理を継続するか、処理を終了させるかが違う...というのが理解できるだろう。だから、Mailet の種別に応じて次の3種類のやり方があるので、それを各 Mailet の先頭に明記することにしよう。

[終了]
メール処理を打ち切る。
[継続]
メール処理を継続する。
[可変]
オプションによって変更可能。メール処理を扱うオプションは
<mailet match="All" class="SomeMailet">
   <passThrough>true|false。false ならパイプラインを終了する。default は true
      (処理継続)</passThrough>
</mailet>
で統一されている。

少し James の「メール処理」について、注意事項を述べる。ポイントは、

あらゆるメールは、「メール処理パイプライン」を通って処理される。これは「受信したメール」も「送信するメール」も区別がない。

ということである。これは、例えば「自動返信」とか「バウンス」「Postmasterへの通知メール」なども例外ではない、ということである。言い替えると、Mailet 処理の中で「新たにメールを生成して送り出す」処理を指定した場合、新たに生成されたメールは、やはりこの「メール処理パイプライン」を通って処理される。

ということは、「ループを作りやすい」ということに注意して処理をすべきだ、ということである。たとえば、

<mailet match="All" class="Forward">
   <forwardTo>外部メールアカウント</forwardTo>
</mailet>

という一見問題のなさそうなメール処理も、実はループを作ってしまうのである! つまり、この Mailet で新たに生成されたメールは、更にこのパイプラインをもう一度辿り直し、同じ Mailet で更に「Forward」されることになり、これが永遠に続いてしまう。まあ、8回くらい送った後で、エラーとして識別はされ、james-2.2.0/apps/james/logs/spoolmanager-*.log に次のような例外が記録されて止まる。

15/12/05 13:01:44 ERROR spoolmanager.transport: Exception calling Forward: Unable 
to create a new message name: too long. Possible loop in config.xml.
javax.mail.MessagingException: Unable to create a new message name: too long. 
Possible loop in config.xml.
        at org.apache.james.transport.mailets.AbstractRedirect.newName(AbstractRedirect.java:1134)
        (スタックトレースは略)
15/12/05 13:01:44 ERROR spoolmanager: Exception in processor <transport>
javax.mail.MessagingException: Unable to create a new message name: too long. 
Possible loop in config.xml.
        at org.apache.james.transport.mailets.AbstractRedirect.newName(AbstractRedirect.java:1134)
        (スタックトレースは略)
15/12/05 13:01:44 ERROR spoolmanager: An error occurred processing 
Mail1134618823476-0-!346903-!849259-!872456-!965433-!686902-!120910-!955775-!488020 
through transport
15/12/05 13:01:44 ERROR spoolmanager: Result was error

要するに内部的に使う識別IDが長くなり過ぎてエラーするようだね....。だから、Jamesに送られて来たメールをすべて特定の外部アドレスに Forward したい場合には、最低でもこうしなくてはならない。

<mailet match="HostIsLocal" class="Forward">
   <forwardTo>外部メールアカウント</forwardTo>
</mailet>

まあ、新たにメールをつくり出す Mailet では、All Matcher は使うべきではないな.....かなり重要なハマりポイントなので指摘する。

Mailet のリスト

で、具体的に用意されているのは、org.apache.james.transport.mailets にあって、次の32種類だ。

フロー制御 Mailet

これらはパイプラインのフローを制御するための Mailet である。次節で使いかたを見るための準備だと思ってほしい。

Null

[終了] 何もしない。オプションもない。メール処理を打ち切るのに使う。

ToProcessor

[継続] 処理を新しい processor(サブフロー)に任せる。オプションは、

<mailet match="All" class="ToProcessor">
  <processor>プロセッサ名</processor>
  <notice>メールのエラーメッセージに追加すべき内容</notice>
  <debug>true|false。defaultはfalse</debug>
</mailet>

これについては、次節で「どう使っているか?」を詳しく見る。

終端系 Mailet

とりあえずここで、パイプラインの最後に位置する Mailet たち(配送/保存)をまとめよう。要するに受け取ったメールを「どこかに保存する」、発信したメールを「外部に配達する」機能をここにまとめてある。だから、これらの Mailet が呼ばれたら、ここで終点である。以降のパイプラインには流れない。

LocalDelivery

[終了] ローカル配信する。オプションはなし。要するに、james-2.2.0/apps/james/var/mail/inboxes/ユーザ名/ 以下にメールを保存することになり、これを POP3 を使って読むことができるようになる。特に James 上では、「file://var/mail/inboxes/ユーザ名」というかたちで、メールの保存先を書くやり方があり、これを「リポジトリ」と呼ぶ。だから、「LocalDeliveryとは、inboxes リポジトリにメールを保存する」という言い方ができる。

RemoteDelivery

[終了] メールをリモート配信する。要するに通常の送信メールの終端処理になる Mailet である。オプションはいろいろある。

<mailet match="All" class="RemoteDelivery">
  <outgoing>必須。送信メールを貯めておくリポジトリを指定する。デフォルト
    なら file://var/mail/outgoing が指定されていることだろう。</outgoing>
  <delayTime>オプション。送信失敗のケースなど何回かリトライをするが、
    その各回の遅延時間をここで指定する。デフォルトの指定だと複数の項目をこの
    タグで記述し、5分、10分、45分、2時間、3時間、6時間...と遅延時間を伸ばし
    ながらリトライをしていく設定になっている。</delayTime>
  <maxRetries>オプション。最大のリトライ数。</maxRetries>
  <timeout></timeout>
  <delivertyThreads>オプション。送信のために使われるスレッドの数</delivertyThreads>
  <sendPartial>オプション。複数の宛先に送付する場合、1つでも送信に失敗
    したら全体を失敗にする(=false)か、それでも送れる宛先に対して送信を試みる
    か(=true)か。デフォルトは false。</sendPartial>
  <gateway>オプション。メールを送る上で、gateway に送信する必要があるの
    ならば、ここで設定する。ファイアウォールの中で James ホストを立ち上げた
    ケース用だな、これ。</gateway>
  <gatewayPort>オプション。gateway を使う場合に使うポート番号</gatewayPort>
  <bind>オプション。メール送信に使うインターフェイスをIPアドレスのかたち
    でここに指定する。</bind>
  <bounceProcessor>送信失敗をした場合、デフォルトで送信元に Bounce 
    メッセージで通知するが、もしこれが指定されていれば、そのプロセッサを使って
     Bounce メッセージを送るようになる。この bounceProcessor 専用の Mailet 
    として DSNBounce Mailet があるので、そちらを
    参照されたい。</bounceProcessor>
   <debug>true|false。defaultはfalse</debug>
</mailet>

やたらと大量のオプションだが、大概の環境では config.xml で実際にやっている設定で問題なかろう。

ToRepository

[可変] メールをリポジトリに保存する。「リポジトリ」とは、「メールの保存場所」のことだが、単に「通常の宛先(LocalDeliveryで保存)」される「inboxesリポジトリ」以外に、「エラーの生じたメールを保存する file://var/mail/error」、「スパムメールを保存する file://var/mail/spam」、「送信するメールを仮置きする file://var/mail/outgoing」などいろいろなリポジトリがある。オプションは、

<mailet match="All" class="ToRepository">
  <repositoryPath>保存すべきリポジトリ</repository>
   <passThrough>true|false。false ならパイプラインを終了する。default は false
      (終了)</passThrough>
</mailet>

だから、エラーが生じたメールはこれを使って、

<mailet match="All" class="ToRepository">
    <repositoryPath>file://var/mail/error/</repositoryPath>
    <!-- データベースに保存するならこんな具合 -->
    <!--
    <repositoryPath> db://maildb/deadletter/error </repositoryPath>
    -->
</mailet>

というように config.xml の中で多用されている。

再送信系 Mailet

新たなメールを生成して送り出す機能を持った Mailet たちである。これは転送の様態に応じていろいろなものが用意されており(過ぎて)、ちょいと混乱しそうである。なので、比較表を用意しよう。どちらか言えば個々のオプションの説明は比較表の方が詳しく書いてある。→再送信系Mailet機能一覧表

この再送信系 Mailet は、パイプラインを流れて来たメールの他に、新たにメールを生成して、パイプラインの先頭に流し込んで「返信」をする。だから、これらはループを作りやすいので利用には注意が必要である。送るメールを絶対にこの「再送信系 Mailet」に反応させてはならない。

まあ、同じようなオプションで設定内容が少し違う...という Mailet が以下に8つ続く。なので、機能的に判りやすく明解なもの(具体的なもの)から実例をつけて解説を始めていこう。

Bounce

[可変] メールをバウンスさせる。言い替えると、送信元(Return-Path:)にメールを RFC822 のフォーマットで、内容を添付して返信する。これは「バウンス」であって返信ではないので、From: や Reply-To: ヘッダを見て返信先を決めるのではないことに注意しよう。オプションはこんなところ。

<mailet match="All" class="Bounce">
   <sender>postmaster|sender|unaltered|アドレス。送信者を指定する。デフォルトは postmaster</sender>
   <sendindAddress>sender と同様だが、こっちが優先する。</sendingAddress>
   <attachError>true|false。処理中に発生したエラーのメッセージを(新たな Part
        として)付け加える。default はfalse</attachError>
   <message>この内容をメッセージとして追加する(任意)</message>
   <notice>この内容をメッセージとして追加する。message と同様だが、
        こっちが優先する。</notice>
   <prefix>Subject: の前にこの内容を追加する(任意)</prefix>
   <inline>unaltered|body|heads|all|none。Bounce メッセージの構築方法。
        unaltered=そのまま旧→新(再構築しない)、body=旧body→新、
        heads=旧header→新、all=すべて→新、none=添付しない(default)</inline>
   <attachment>body|heads|all|message|none。Bounce メッセージの構築方法。
        body=旧bodyをテキストで添付、heads=旧headerをテキストで添付、all=旧body,
        header共テキストで添付、message=旧全体を message/rfc822 形式で添付(default)、
        none=添付しない</attachment>
   <passThrough>true|false。false ならパイプラインを終了する。default は false
        (終了)</passThrough>
   <fakeDomainCheck>true|false。返信メールの Sender が正しいドメイン
         であるかどうかをチェックする。defaultはtrue</fakeDomainCheck>
   <debug>true|false。defaultはfalse</debug>
</mailet>

たとえば、こんな感じの設定で、バウンスさせると、

   <mailet match="All" class="Bounce" >
      <sender>postmaster</sender>
      <message>Added in config.xml</message>
      <prefix>NOTICE!:</prefix>
      <passThrough>true</passThrough>
   </mailet>

バウンスされたメールはRFC822フォーマットでこんな具合になる。

Return-Path: <>  ←バウンスメール自身の Return-Path は空
X-Original-To: james@shopmail.jp
Delivered-To: james@shopmail.jp
Received: from bizet (localhost [127.0.0.1])
        by bizet.kobe-du.ac.jp (Postfix) with SMTP id 6F4846FB1D
        for <james@shopmail.jp>; Thu, 15 Dec 2005 10:26:50 +0900 (JST)
Message-ID: <5218268.1134609955188.JavaMail.sug@bizet>
From: postmaster@localhost.mydomain.jp
To: james@shopmail.jp
Subject: NOTICE!:test of bounce  ←prefix がヘッダに追加されている
Mime-Version: 1.0
Content-Type: multipart/mixed;
        boundary="----=_Part_4_32189467.1134609955175"
Date: Thu, 15 Dec 2005 10:24:59 +0900 (GMT+09:00)
Status: R

------=_Part_4_32189467.1134609955175
Content-Type: multipart/alternative;
        boundary="----=_Part_5_10272075.1134609955175"

------=_Part_5_10272075.1134609955175
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

Added in config.xml  ←message が追加されている

Message details:
  Subject: test of bounce
  Sent date: Thu Dec 15 10:24:59 GMT+09:00 2005
  MAIL FROM: james@shopmail.jp
  RCPT TO: sug@mydomain.jp
  From: james@shopmail.jp
  Size (in bytes): 18


------=_Part_5_10272075.1134609955175--

------=_Part_4_32189467.1134609955175  以下送ったメールの添付
Content-Type: message/rfc822; name="test of bounce"
Content-Disposition: Attachment; filename="test of bounce"

Return-Path: <james@shopmail.jp>
Received: from localhost ([127.0.0.1])
          by bizet (JAMES SMTP Server 2.2.0) with SMTP ID 739
          for <sug@localhost>;
          Thu, 15 Dec 2005 10:24:59 +0900 (GMT+09:00)
Subject: test of bounce
Date: Thu, 15 Dec 2005 10:24:59 +0900 (GMT+09:00)
From: james@shopmail.jp

hello, now bounce?
------=_Part_4_32189467.1134609955175--

DSNBounce

[可変] 配信ステータスを付与した Bounce メッセージを送信元(Return-Path:)に返信する。

以前記述が間違っていた...ご指摘を受けて修正する。

この Mailet はかなり特殊用途であり、通常の processor パイプラインには書けず、書いたとしても結果として例外を投げて終わる。では、どういう風に使うのか..というと、この Mailet は「RemoteDelivery Mailet」のエラー時の後始末」の専用Bounceメッセージを担当させるのである。だから、RemoteDelivery Mailet のオプションに <bouceProcessor> というものがあるが、ここで指定する「配信エラー時のプロセッサ」に対してのみ、この Mailet を書くのである。

オプションはこんなところだ。

<mailet match="All" class="DSNBounce">
   <sender>送信者を指定する。デフォルトは postmaster</sender>
   <prefix>Subject: の前にこの内容を追加する(任意)</prefix>
   <attachment>message|none。Bounce メッセージの構築方法。
       message=旧全体を message/rfc822 形式で添付(default)、
       none=添付しない</attachment>
   <messageString>メッセージとして与えられる内容。特に [machine] と
       書けば、それが実ホスト名に変換されて出力される。デフォルトの内容は、
   Hi. This is the James mail server at [machine].
   I'm afraid I wasn't able to deliver your message to the following addresses.
   This is a permanent error; I've given up. Sorry it didn't work out.  Below
   I include the list of recipients and the reason why I was unable to deliver
   your message.</messageString>
   <passThrough>true|false。false ならパイプラインを終了する。default は false
       (処理終了)</passThrough>
   <debug>true|false。defaultはfalse</debug>
</mailet>

だから、これを使うケースだと、こういう感じの config.xml になる。

<processor name="transport">
  <mailet match="All" class="RemoteDelivery">
    <outgoing> file://var/mail/outgoing/ </outgoing>
    <!-- bounceProcessor として適当な名前の Processor を用意する -->
    <bounceProcessor>bounce</bounceProcessor>
  </mailet>
</processor>

<!-- 配信失敗処理専用 -->
<processor name="bounce">
  <mailet match="All" class="DSNBounce">
    <prefix>[DSNBounce]</prefix>
  </mailet>
</processor>

ここで実験のために、RemoteDelivery の送信処理について知識を仕入れておこう。RemoteDelivery は次の手順で外部向けに送信をする。

  1. RemoteDelivery#init() で、メール送信用のスレッドを起動しておく。
  2. RemoteDelivery#service() では、渡された Mail を outgoing リポジトリに保存すして、GHOST を Status にセットして終わる。だから、実際のメール配信はすべてスレッドで行う。
  3. 適当なタイミングでスレッド(RemoteDelivery#run())は起きて、outgoing リポジトリから送信すべきメールを読み出して、配信を企てる。
  4. 配信(RemoteDelivery#deliver())が成功すればそれでよし。
  5. 配信が失敗した場合、制御は RemoteDelivery#failMessage() に移る。ここで再送信に関する設定を参照し、Retry 回数内であれば、再送信の準備をして終わる。もし、再送信カウントを越えていれば、bounceProcessor を見る。
  6. bounceProcessor が設定されていれば、そのプロセッサに向けてメールを流す。もし設定されていなければ、デフォルトの bounce() を使ってバウンスメッセージを送る。

という流れである。だから、DSNBounce はこの bounceMessage で指定されたプロセッサに指定する「専用の配信エラーBounce Mailet」のわけだ。この「専用」ということについては、要するに、RemoteDelivery Mailet が「delivery-error」という「どういうエラーが起きたか?」を示すメッセージを持ったアトリビュートをメールにセットして、それを処理する DSNBounce Mailet は、「delivery-error で示されるエラー内容」をメール本文に含んだメールを送信する、という機能を持っているわけである。

ここで delivery-error アトリビュートは、RemoteDelivery Mailet が受けた例外(MessagingException)オブジェクトであり、例外の文字列がメッセージに化けるということである。もし、delivery-error アトリビュートが DSNBounce Mailet でセットされていないと、NullPointerException を投げることになって、動かないのである....だから、この DSNBounce はたとえば Bounce Mailet とは違って、通常のパイプラインでは正しく動かずに、RemoteDelivery の後始末 Processor の中でしか動作しないわけだ。

簡単にここらへんを実験してみよう。配信エラー自体は「存在しないメールアドレス」にメールを送ることで生成できるのだが、再送信設定が絡むので、「送ったからと言って、すぐに DSNBouce に処理が流れる...」というわけではない。再送信設定を最小限にしないと、「送って→DSNBounce をすぐに確認」はできない。こんな感じがいいだろう。

<mailet match="All" class="RemoteDelivery">
  <outgoing> file://var/mail/outgoing/ </outgoing>
  <!-- delayTime は再送信トライアルの間隔を示す -->
  <delayTime>  1 minutes </delayTime>
  <!-- 再送信回数 -->
  <maxRetries> 1 </maxRetries>
  <!-- なので、1分後に再送信を企てて、失敗すればそれで終わり -->
  <deliveryThreads> 1 </deliveryThreads>
  <sendpartial>false</sendpartial>
  <bounceProcessor>bounce</bounceProcessor>
</mailet>

注意!! 上記設定は実験のためであり、運用するサーバではこの設定は RFC 2821 4.5.4.1 の記述に違反しています。

ある特定の宛先への送信が失敗した後、クライアントは再送を遅延させなければならない(MUST)。一般にこの再送の間隔は少なくとも 30 分であるべき(SHOULD)だが、配送不能の原因を SMTP クライアントが特定できる場合には、より繊細で柔軟な戦略が有益だろう。
メッセージが送信されるか送信者があきらめるまで再送は続けられる。一般にあきらめるまでの時間は少なくとも 4、5 日を必要とする。この再送アルゴリズムへのパラメータは設定可能でなければならない(MUST)。(RFC2821 4.5.4.1 翻訳 )

上記設定では、再送信は1分後に1回だけ行われ、その後に bounce プロセッサに処理が流れるようになっている。ここで例えば、

  <delayTime>  5 minutes </delayTime>
  <delayTime> 10 minutes </delayTime>
  <delayTime> 45 minutes </delayTime>
  <delayTime>  2 hours </delayTime>
  <delayTime>  3 hours </delayTime>
  <delayTime>  6 hours </delayTime>
  <maxRetries> 1 </maxRetries>

という風に、maxRetries だけを修正しても、実は maxRetries が 6 に勝手に修正されてしまうようになっているので、maxRetries の示す数と delayTime の個数とを一致させないと再送信回数を減らせないので注意(maxRetries==0 は設定できないようだ)。

この設定で適当な存在しないメールアドレスに宛ててメールを送信すればいい。そうすると、1分後の再送信1回が失敗して、すぐに DSNBounce によるエラーのBounceメールが届く。内容はこんな感じになる。

Return-Path: <>   ←Return-Path: は空
Message-ID: <18923308.1179021600952.JavaMail.root@bizet>
Date: Sun, 13 May 2007 11:00:00 +0900 (GMT+09:00)
From: postmaster@localhost    ←From: は sender で指定可能
To: sug@localhost             ← オリジナル送信者宛て
Subject: [DSNBounce]not receive   ←頭に prefix 指定内容が追加
In-Reply-To: <20559758.1179021536070.JavaMail.root@bizet>
Mime-Version: 1.0
Content-Type: multipart/report; report-type=delivery-status;
        boundary="----=_Part_0_23979164.1179021600925"
Delivered-To: sug@localhost

------=_Part_0_23979164.1179021600925
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

Hi. This is the James mail server at bizet.   ← messageString 
I'm afraid I wasn't able to deliver your message to the following addresses.
This is a permanent error; I've given up. Sorry it didn't work out.  Below
I include the list of recipients and the reason why I was unable to deliver
your message.

Failed recipient(s):   ←送信に失敗した宛先
hogehoge@hoge.hoge.jp

Error message:   ← エラー内容
No mail server(s) available at this time.


------=_Part_0_23979164.1179021600925
Content-Type: message/delivery-status; name=status.dat
Content-Transfer-Encoding: 7bit
Content-Description: Delivery Status Notification
Content-Disposition: attachment; filename=status.dat

Message-ID: <22608339.1179021600939.JavaMail.root@bizet>
Mime-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

Reporting-MTA: dns; bizet
Received-From-MTA: dns; bizet

Final-Recipient: rfc822; hogehoge@hoge.hoge.jp
Action: failed
Status: 4.4.1
Diagnostic-Code: X-James; No mail server(s) available at this time.
Last-Attempt-Date: Sun, 13 May 2007 11:00:00 +0900 (GMT+09:00)

------=_Part_0_23979164.1179021600925   ←オリジナルメール
Content-Type: message/rfc822; name="not receive"
Content-Disposition: Attachment; filename="not receive"

Return-Path: <sug@localhost>
Received: from localhost ([127.0.0.1])
          by bizet (JAMES SMTP Server 2.2.0) with SMTP ID 963
          for <hoge@hoge.hoge.zz>;
          Sun, 13 May 2007 10:58:54 +0900 (GMT+09:00)
Subject: not receive
Date: Sun, 13 May 2007 10:58:54 +0900 (GMT+09:00)
From: sug@localhost
Content-Transfer-Encoding: 7bit
Content-Type: text/plain; charset=us-ascii
Mime-Version: 1.0
Message-ID: <20559758.1179021536070.JavaMail.root@bizet>

Fake recipient!
------=_Part_0_23979164.1179021600925--

Forward

[可変] メールをフォワード転送する。オプションはこんなところ。

<mailet match="UserIs=sug" class="Forward">
   <forwardTo>postmaster|sender|replyTo|reversePath|unaltered|recipients|
      to|null|アドレス。コンマ区切りで転送先アドレスを指定する。</forwardTo>
   <passThrough>true|false。false ならパイプラインを終了する。default は false
         (処理終了)</passThrough>
   <fakeDomainCheck>true|false。返信メールの Sender が正しい
          ドメインであるかどうかをチェックする。defaultはtrue</fakeDomainCheck>
   <debug>true|false。defaultはfalse</debug>
</mailet>

こんな感じの設定で、[発信者]sug@shopmail.jp → [James上の受取人]sug@mydomain.jp → [転送先]james@shopmail.jp みたいな転送をしてみよう。

   <mailet match="UserIs=sug" class="Forward" >
      <forwardTo>james@shopmail.jp</forwardTo>
      <passThrough>true</passThrough>
   

受取人の inbox には、pathTrough == true なので保存され、

Return-Path: <sug@shopmail.jp>
Received: from localhost ([127.0.0.1])
          by bizet (JAMES SMTP Server 2.2.0) with SMTP ID 950
          for <sug@mydomain.jp>;
          Thu, 15 Dec 2005 11:31:18 +0900 (GMT+09:00)
Subject: Forward Test
Date: Thu, 15 Dec 2005 11:31:18 +0900 (GMT+09:00)
From: sug@shopmail.jp
Delivered-To: sug@mydomain.jp
Content-Transfer-Encoding: 7bit
Content-Type: text/plain; charset=us-ascii
Mime-Version: 1.0
Message-ID: <17431955.1134613934623.JavaMail.sug@bizet>

Testing forward.

転送先には、

Return-Path: <sug@shopmail.jp>
X-Original-To: james@shopmail.jp
Delivered-To: james@shopmail.jp
Received: from bizet (localhost [127.0.0.1])
        by bizet.kobe-du.ac.jp (Postfix) with SMTP id 998146FB1D
        for <james@shopmail.jp>; Thu, 15 Dec 2005 11:33:09 +0900 (JST)
Received: from localhost ([127.0.0.1])
          by bizet (JAMES SMTP Server 2.2.0) with SMTP ID 950
          for <sug@mydomain.jp>;
          Thu, 15 Dec 2005 11:31:18 +0900 (GMT+09:00)
Subject: Forward Test
Date: Thu, 15 Dec 2005 11:31:18 +0900 (GMT+09:00)
From: sug@shopmail.jp
Content-Transfer-Encoding: 7bit
Content-Type: text/plain; charset=us-ascii
Mime-Version: 1.0
Message-ID: <4898828.1134613934582.JavaMail.sug@bizet>
To: undisclosed-recipients:;
Status: R

Testing forward.

のように転送される。この Matcher は便利で使いやすいが、何度も述べているようにループを作りやすいので、利用は注意されたい。

Resend

これが「ヴァニラ」の転送系Mailetである。つまり転送系Mailetならすべてこの Resend Mailet によって書き換えることが可能であるわけだ。そういう一般的な転送Mailetなので、オプションは大量である。

<mailet match="All" class="Resend">
  <recipients>sender|from|replyTo|postmaster|reversePath|recipients|to
     |null|unaltered|メールアドレス。要するに新しいメールの受取人をここで指定
     する。カンマ区切りで複数の宛先を許容する。各種ヘッダなどから取得したり
    (sender,from,replyTo,reversePath, recipients)、config.xml の設定から
    取得したり(postmaster)、変更しない(unaltered,null)など多彩な受取人指定
    が出来るのと同時に、直打ちも可能になっている。デフォルトは 
     unaltered(そのまま)。</recipients>
   <to>可能な値は <recipients> と同様。To: ヘッダに入れる値の指定。
     デフォルトは unaltered(そのまま)。</to>
   <sender>sender|postmaster|unaltered|メールアドレス。これも意味は 
     <recipients> などと同様。From: ヘッダに入る値の指定である。
     デフォルトは unaltered(そのまま)。</sender>
   <message>本文に追加されるメッセージ。オプション。</message>
   <inline>unaltered|body|heads|all|none。メッセージの構築方法。
        unaltered=そのまま旧→新(再構築しない。default)、body=旧body→新、
        heads=旧header→新、all=すべて→新、none=添付しない</inline>
   <attachment>body|heads|all|message|none。メッセージの構築方法。
        body=旧bodyをテキストで添付、heads=旧headerをテキストで添付、all=旧body,
        header共テキストで添付、message=旧全体を message/rfc822 形式で添付、
        none=添付しない(default)</attachment>
   <passThrough>true|false。false ならパイプラインを終了する。default は false
      (処理終了)</passThrough>
   <fakeDomainCheck>true|false。返信メールの Sender が正しいドメイン
     であるかどうかをチェックする。defaultはtrue</fakeDomainCheck>
   <attachError>true|false。処理中に発生したエラーのメッセージを(新たな Part
     として)付け加える。default はfalse</attachError>
   <replyTo>sender|postmaster|null|unaltered|メールアドレス。Reply-To: 
     ヘッダに入れる内容。デフォルトは unaltered(そのまま)。</replyTo>
   <reversePath>sender|postmaster|null|unaltered|メールアドレス。
     Reverse-Path: ヘッダに入れる内容。null だと「Reverse-Path: <>」
     になる。デフォルトは unaltered(そのまま)。</reversePath>
    <subject>オプション。Subject: に入れる内容。これはここでの指定で置き
     換わる。</subject>
    <prefix>オプション。Subject: の先頭に追加する内容。</prefix>
    <isReply>true|false。true なら、In-Reply-To: ヘッダが現在のメールの
     IDで設定されて追加される。デフォルトは false(追加しない)。</isReply>
    <debug>true|false。defaultはfalse</debug>
</mailet>

Redirect

これも「ヴァニラ」の転送系Mailetに近いが、Resend を若干親切にした Mailet である。オプションは大量であるが、Resend と少しだけ違うので要注意だ。

<mailet match="All" class="Redirect">
  <recipients>sender|from|replyTo|postmaster|reversePath|recipients|to
     |null|unaltered|メールアドレス。受取人。デフォルトは unaltered(そのまま)。
     機能などは Resend と同様だが、Resend では recipients と to が別のオプション
     だったのに対して、Redirect では recipients と to は同一視される。
   </recipients>
   <to>可能な値は <recipients> と同様。To: ヘッダに入れる値の指定。
     デフォルトは unaltered(そのまま)。上記参照。</to>
   <sender>sender|postmaster|unaltered|メールアドレス。これも意味は 
     <recipients> などと同様。From: ヘッダに入る値の指定である。
     デフォルトは unaltered(そのまま)。Resend と違い、reversePath を省略した
     場合、この sender の値が reversePath の値として使われる。</sender>
   <message>本文に追加されるメッセージ。オプション。</message>
   <inline>unaltered|body|heads|all|none。メッセージの構築方法。
        unaltered=そのまま旧→新(再構築しない)、body=旧body→新(default)、
        heads=旧header→新、all=すべて→新、none=添付しない。Resend とは
	デフォルト値が違うのに注意。</inline>
   <attachment>body|heads|all|message|none。メッセージの構築方法。
        body=旧bodyをテキストで添付、heads=旧headerをテキストで添付、all=旧body,
        header共テキストで添付、message=旧全体を message/rfc822 形式で添付、
        none=添付しない(default)</attachment>
   <passThrough>true|false。false ならパイプラインを終了する。default は false
      (処理終了)</passThrough>
   <fakeDomainCheck>true|false。返信メールの Sender が正しいドメイン
     であるかどうかをチェックする。defaultはtrue</fakeDomainCheck>
   <attachError>true|false。処理中に発生したエラーのメッセージを(新たな Part
     として)付け加える。default はfalse</attachError>
   <replyTo>sender|postmaster|null|unaltered|メールアドレス。Reply-To: 
     ヘッダに入れる内容。デフォルトは unaltered(そのまま)。</replyTo>
   <reversePath>sender|postmaster|null|メールアドレス。
     Reverse-Path: ヘッダに入れる内容。null だと「Reverse-Path: <>」
     になる。これは unaltered が使えず、デフォルトは sender。</reversePath>
    <subject>オプション。Subject: に入れる内容。これはここでの指定で置き
     換わる。</subject>
    <prefix>オプション。Subject: の先頭に追加する内容。</prefix>
    <isReply>true|false。true なら、In-Reply-To: ヘッダが現在のメールの
     IDで設定されて追加される。デフォルトは false(追加しない)。</isReply>
    <debug>true|false。defaultはfalse</debug>
    <static>これは Redirect 専用タグ。true|false。要するにこのメールを再利用し
      ないならば、true にして内部情報を書き換えてしまう。それで効率が上がる..
      ということのようだ。デフォルトは false。</static>
</mailet>

まあ、少し実用的な例をやってみようか。ありがちだが、メールを受けたら自動返信する、という奴である。

<mailet match="RecipientIs=sug@mydomain.jp" class="Redirect" >
   <recipients>sender</recipients>
   <sender>sug@mydomain.jp</sender>
   <message>あなたのメールを受け取りました(このメールは自動返信です)。
なるべく早く返事を致しますので、それをお待ち下さいませ。
これからもよろしくお願い致します。</message>
   <inline>none</inline>
   <replyTo>sug@mydomain.jp</replyTo>
   <reversePath>sug@mydomain.jp</reversePath>
   <subject>メールを受け取りました!</subject>
   <passThrough>true</passThrough>
</mailet>

みたいな設定で、ご挨拶系の返信が出来てしまう。注意点は passThrough で、Resend のデフォルトは false なので、true に設定し忘れるとメールが宙に消えてしまう...まあ、気をつけてくれたまえ。あと、reversePath とか sender とかも正しく設定しておかないと、返信を受けた方が迷惑するので、ちゃんと設定しろよ。で、次のような返事が返る。

Return-Path: <sug@mydomain.jp>
X-Original-To: sug@shopmail.jp
Delivered-To: sug@shopmail.jp
Received: from bizet (localhost [127.0.0.1])
        by bizet.kobe-du.ac.jp (Postfix) with SMTP id 59C536FB1D
        for <sug@shopmail.jp>; Thu, 22 Dec 2005 12:24:34 +0900 (JST)
Message-ID: <32079775.1135221873740.JavaMail.sug@bizet>
From: sug@mydomain.jp
Reply-To: sug@mydomain.jp
To: sug@shopmail.jp
Subject: =?EUC-JP-LINUX?B?peGhvKXrpPK89aSxvOik6qTepLekv6Gq?=
Mime-Version: 1.0
Content-Type: multipart/mixed; boundary="----=_Part_0_6259058.1135221873469"
Date: Thu, 22 Dec 2005 12:24:32 +0900 (GMT+09:00)
Status: R

------=_Part_0_6259058.1135221873469
Content-Type: multipart/alternative;
        boundary="----=_Part_1_23047631.1135221873487"

------=_Part_1_23047631.1135221873487
Content-Type: text/plain; charset=EUC-JP-LINUX
Content-Transfer-Encoding: base64
Content-Disposition: inline

pKKkyqS/pM6l4aG8peuk8rz1pLG86KTqpN6kt6S/ocqks6TOpeGhvKXrpM+8q8awyta/rqTHpLmh
y6GjCqTKpOuk2aSvweGkr8rWu/ak8sPXpLek3qS5pM6kx6GipL2k7KTypKrC1KTBsryktaSkpN6k
u6GjCqSzpOykq6TppOKk6KTtpLekr6SqtOqkpMPXpLek3qS5oaMK
------=_Part_1_23047631.1135221873487--

------=_Part_0_6259058.1135221873469--

まあ、日本語内容で返信してるから、MIME/Base64 でエンコードされているわけで、一般向けMUAで見れば正しく「日本語の返信文」が返っていることがわかる。

ServerTime

[継続] メールを受け取った際に、「メールを何時に受け取りました」というような簡単な返信を出す目的の Mailet である。特に設定項目はない。返信先は Return-Path: ヘッダを使う。次のような返事が送信元に返る。

実はこの ServerTime のみ、返信系 Mailet では統一的なやり方に従っておらず、直接 GenericMailet を継承している。そのため、これが「一番単純な返信Mailet」ということになるので、勉強のためソースを見る価値があろう。

Return-Path: <sug@mydomain.jp>
X-Original-To: james@shopmail.jp
Delivered-To: james@shopmail.jp
Received: from bizet (localhost [127.0.0.1])
        by bizet.kobe-du.ac.jp (Postfix) with SMTP id 417A16FB1D
        for <james@shopmail.jp>; Thu, 15 Dec 2005 14:34:24 +0900 (JST)
Message-ID: <5122060.1134624809463.JavaMail.sug@bizet>
From: sug@mydomain.jp
To: james@shopmail.jp
Subject: The time is now...
Mime-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Date: Thu, 15 Dec 2005 14:34:24 +0900 (JST)
Status: R

This mail server thinks it's Thu Dec 15 14:33:29 GMT+09:00 2005.

ちょっとした受付にも使えるかな...しかしこれもループに注意!

NotifyPostmaster

[可変] James で登録されている Postmaster アカウントにメールを送る。オプションは大量だが、定型的な転送モノに過ぎない。

<mailet match="All" class="NotifyPostmaster">
   <sender>postmaster|sender|unalterd。デフォルトはpostmaster。</sender>
   <sendindAddress>sender と同様だが、こっちが優先する。</sendingAddress>
   <attachError>true|false。処理中に発生したエラーのメッセージを(新たな Part
        として)付け加える。default はfalse</attachError>
   <attachStackTrace>attachError の別名。こっちが優先。</attachStackTrace>
   <message>この内容をメッセージとして追加する(任意)</message>
   <notice>この内容をメッセージとして追加する。message と同様だが、
        こっちが優先する。</notice>
   <prefix>Subject: の前にこの内容を追加する(任意)</prefix>
   <inline>unaltered|body|heads|all|none。Bounce メッセージの構築方法。
        unaltered=そのまま旧→新(再構築しない)、body=旧body→新、
        heads=旧header→新、all=すべて→新、none=添付しない(default)</inline>
   <attachment>body|heads|all|message|none。Bounce メッセージの構築方法。
        body=旧bodyをテキストで添付、heads=旧headerをテキストで添付、all=旧body,
        header共テキストで添付、message=旧全体を message/rfc822 形式で添付(default)、
        none=添付しない</attachment>
   <passThrough>true|false。false ならパイプラインを終了する。default は false
        (処理終了)</passThrough>
   <fakeDomainCheck>true|false。返信メールの Sender が正しいドメイン
         であるかどうかをチェックする。defaultはtrue</fakeDomainCheck>
   <to>postmaster|unaltered。あれば、To: ヘッダを置き換える。デフォルトは postmaster。
      実質上設定不能なオプションである。</to>
   <debug>true|false。defaultはfalse</debug>
</mailet>

NotifySender

[可変] 送信元(Sender)に「メール受信」を通知する。これも通知モノである。オプションは、

<mailet match="All" class="NotifySender">
   <sender>postmaster|sender|unalterd。デフォルトはsender。</sender>
   <sendindAddress>sender と同様だが、こっちが優先する。</sendingAddress>
   <attachError>true|false。処理中に発生したエラーのメッセージを(新たな Part
        として)付け加える。default はfalse</attachError>
   <attachStackTrace>attachError の別名。こっちが優先。</attachStackTrace>
   <message>この内容をメッセージとして追加する(任意)</message>
   <notice>この内容をメッセージとして追加する。message と同様だが、
        こっちが優先する。</notice>
   <prefix>Subject: の前にこの内容を追加する(任意)</prefix>
   <inline>unaltered|body|heads|all|none。メッセージの構築方法。
        unaltered=そのまま旧→新(再構築しない)、body=旧body→新、
        heads=旧header→新、all=すべて→新、none=添付しない(default)</inline>
   <attachment>body|heads|all|message|none。メッセージの構築方法。
        body=旧bodyをテキストで添付、heads=旧headerをテキストで添付、all=旧body,
        header共テキストで添付、message=旧全体を message/rfc822 形式で添付(default)、
        none=添付しない</attachment>
   <passThrough>true|false。false ならパイプラインを終了する。default は false
        (処理終了)</passThrough>
   <fakeDomainCheck>true|false。返信メールの Sender が正しいドメイン
         であるかどうかをチェックする。defaultはtrue</fakeDomainCheck>
   <to>sender|unaltered|from。もしあれば、To: ヘッダを置き換える。
       デフォルトは送信元(Sender)である。</to>
   <debug>true|false。defaultはfalse</debug>
</mailet>

編集系 Mailet

メール内容を変更する Mailet たちをここにまとめよう。

AddHeader

[継続] サブエレメント <name>ヘッダ名</name><value>ヘッダの値</value> を取り、それをヘッダに追加する。追加ヘッダは1つしか指定できないので、複数追加するのなら何回も書くべきである。

<mailet match="All" class="AddHeader">
   <name>ヘッダ名。たとえば、X-Add-Header</name>
   <value>その値</value>
</mailet>

UseHeaderRecipients

[終了] 通常のメールヘッダがアテにならないケースなどで、メールヘッダではなくて、「SMTPでの送信先(MAIL-FOR)」を送信先として利用する。特にオプションはない。一応「MAIL-FOR」ヘッダを見たあと、これがなければ To: と Cc: を見て、メール自体を再構築するので、[終了] Mailet である。

AddFooter

[継続] フッタをメール本文の最後に追加する。メールが text/plain だろうと、text/html だろうと multipart だろうと、最善の努力をする。オプションは次の通り。

<mailet match="All" class="AddFooter">
   <text>フッタとして追加すべきテキスト内容</text>
</mailet>

AddHabeasWarrantMark

[継続] メールヘッダに Habeas Warrant Mark を追加する。オプションはなし。

SetMailAttribute

[継続] 「Mail Attribute」をセットする。「Mail Attribute」は、James が処理するメールに勝手に与える属性である(org.apache.james.core.MailImpl 参照)。ここで与えた Attribute を HasMailAttribute Matcher で検出することができる。使いかたは、

<mailet match="All" class="SetMailAttribute">
   <アトリビュート名></アトリビュート名>
   <アトリビュート名></アトリビュート名>
   ....
</mailet>

で勝手なアトリビュートを設定できる。

RemoveAllMailAttributes

[継続] 設定された「Mail Attribute」をすべて破棄する。オプションはない。

RemoveMailAttribute

[継続] 設定された「Mail Attribute」を名前を特定して破棄する。使いかたは、

<mailet match="All" class="RemoveMailAttribute">
   <name>破棄するアトリビュート名</name>
   ....
</mailet>

受取人の Alias 処理

管理するドメインのユーザたちについて、その別名(Alias)を定義する MTA でお馴染みな処理である。Recipient をこれらの Mailet は置き換えるので、パイプラインの先頭で定義するのが良かろう(デフォルトの config.xml もそうしている)。まあ、James は本質的に「仮想メールドメイン環境」の MTA なので、何もしなくても「UNIXユーザ≠メールユーザ」で安全だが、もし Alias をしたいのならばこの Mailet で設定する。

PostmasterAlias

[継続] もし、Recipient にローカルホストの postmaster があれば、config.xml で設定された postmaster エレメントのアドレスに変換する。オプションはなし。

実はこれはかなり特殊な扱いを受けている Mailet である。実際には config.xml の中で設定する必要はなくて、James のコード内(org.apache.james.transport.JamesSpoolManager#initialize())root プロセッサの一番最初に内緒で追加されている。わざわざconfig.xml の中に追加する必要はまったくない。

XMLVirtualUserTable

[継続] アカウント名に対して、Alias 処理をする。仮想ユーザ環境をこれで作ることができる。たとえば、

<mailet match="All" class="XMLVirtualUserTable">
   <mapping>sug@mydomain.jp=sugiura@mydomain.jp</mapping>
   <mapping>postmaster@mydomain.jp=sugiura@mydomain.jp</mapping>
   <mapping>root@mydomain.jp=sugiura@mydomain.jp</mapping>
   ...
</mailet>

という風にして、sug@mydomain.jp、postmaster@mydomain.jp、root@mydomain.jp 宛メールをすべて sugiura@mydomain.jp で受け取ることを出来るようにする。

また、ユーザ名とドメイン名に対してはワイルドカード「*」を使うことができるが、部分一致はしない。たとえば、

<mailet match="All" class="XMLVirtualUserTable">
   <mapping>sug@*=sugiura@mydomain.jp</mapping>
   <mapping>*@mydomain.jp=sugiura@mydomain.jp</mapping>
</mailet>

のようにだけワイルドカードが使える。

JDBCAlias

[継続] データベースから、Recipient の Alias を取得する。オプションはこんな風になる。

 <mailet match="All" class="JDBCAlias">
   <mappings>db://DB名/テーブル名</mappings>
   <source_column>アカウントの変換元のフィールド</source_column>
   <target_column>アカウントの変換先のフィールド</target_column>
</mailet>

「DB名」は、config.xml の database-connections ブロック内の data-source エレメントとして定義されている必要がある。まあ、ここらへんの要領については後で触れる

JDBCVirtualUserTable

[継続] これは Recipient の DB からの変換でも、先ほどの JDBCAlias よりも強烈なものである。JDBCAlias は結構ルーズなDB仕様だが、こっちは強力な代わりにかなり厳格だ。DB仕様は次のように決められている。

CREATE TABLE VirtualUserTable
(
  user varchar(64) NOT NULL default '',
  domain varchar(255) NOT NULL default '',
  target_address varchar(255) NOT NULL default '',
  PRIMARY KEY (user,domain)
);

で、次のようなオプションでこれを使う。

<mailet match="All" class="JDBCVirtualUserTable">
   <table>db://DB名/VirtualUserTable</table>
   <sqlquery>オプション。SQLコマンドを修正したければここで修正する</sqlquery>
</mailet>

で、単に完全一致での変換だけではなくて、「user@%」「%@domain」といった LIKE 検索による一致も可能である。実際、デフォルトの SQL は、

select VirtualUserTable.target_address from VirtualUserTable, VirtualUserTable 
as VUTDomains where (VirtualUserTable.user like ? or VirtualUserTable.user like
 '\\%') and (VirtualUserTable.domain like ? or (VirtualUserTable.domain like 
'\\%' and VUTDomains.domain like ?)) order by concat(VirtualUserTable.user,'@',
VirtualUserTable.domain) desc limit 1

なんていうタマンナイものである。sqlquery オプションでこれを書き換える奴の気が知れないな。

メーリングリスト処理

さて、James は「いろいろなことが出来る」わけだが、特に要望があるのか、いわゆる「メーリングリスト」を安直に実現できる機能を持っている。ただ「登録されたアドレス」のリストに対して一括配信をするだけではなくて、「メーリングリスト名-on@host」にメールを送れば、新規に配信をリクエストすることになるし、「メーリングリスト名-off@host」にメールを送れば、配信を中止する...なんて機能を実現している。

とはいえ、この機能は2系統---新しいの(2.2.0で追加)と古いのがある。新しい方は「-on」「-off」以外のコマンドを追加できるとか、結構すごいものがあるが、かなり複雑化しているので、まずは「古い方」から説明しよう。

AvalonListservManager

AvalonListserv

両方とも[終了]。これが1セットになって、メーリングリスト機能を実現できる。AvalonListserv の方は「ここにメールを送ればメーリングリスト全体への配信となる」メールアドレスを管理し、AvalonListeservManager の方は、「新規購読」「購読中止」のユーザコマンドを実現する。だから、こういうメーリングリストを運営するんだと、

メーリングリスト名
myjames
ユーザ配信要求用アカウント
myjames-***@mydomain.jp。だから、myjames-on@mydomain.jp なら購読開始、myjames-off@mydomain.jp なら購読中止である。
メーリングリスト宛送信アカウント
myjames@mydomain.jp

設定はこんな感じになる。Matcher の CommandForListserv は Matcher の説明だと今ひとつ意味不明だったが、こっちを見れば意味が判るな...要するに「アカウント-コマンド@ホスト名」というタイプの宛先アドレスを判定するわけである。

<mailet match="RecipientIs=myjames@mydomain.jp" class="AvalonListserv">
   <repositoryName>myjames。使うリポジトリの名前を登録する</repositoryName>
   <membersonly>true|false。投稿できるのを登録メンバーのみに制限するか
     否か。デフォルトは false(制限しない)。</membersonly>
   <attachmentsallowed>true|false。添付ファイル付きメールの投稿が出来る
     かどうか。デフォルトは true(投稿できる)。</attachmentsallowed>
   <replytolist>true|false。Reply-To: ヘッダをセットするか否か。true 
     だと 送信者(Sender)がセットされる。</replytolist>
   <autobracket>true|false。Subject: に追加する内容(subjectprefix)を
     ブラケットで囲むかどうか。</autobracket>
   <subjectprefix>Subject: の先頭に追加する内容。もし autobracket==true
     なら「[追加内容] 送ったSubject:」の要領になる</subjectprefix>
</mailet>

<mailet match="CommandForListserv=myjames@mydomain.jp" class="AvalonListservManager">
  <repositoryName>myjames。使うリポジトリ名</repositoryName>
</mailet>

で当然、「使うリポジトリ」に関する設定も必要だ。メーリングリスト用に、新規に「ユーザ管理のリポジトリ」(file://var/users と同様に...)を作る必要がある。だから、config.xml の <users-store> ブロックに、

<users-store>
   <!-- James のメールサービス用のユーザたちのリポジトリ定義 -->
   <repository name="LocalUsers" class="org.apache.james.userrepository.UsersFileRepository">
      <destination URL="file://var/users/"/>
   </repository>
   <!-- 以下追加 -->
   <repository name="myjames" class="org.apache.james.userrepository.UsersFileRepository">
       <destination URL="file://var/lists/myjames/" />
   </repository>
   <!-- 追加終り -->
</users-store>

のように、新しい「ユーザ管理用リポジトリ」を追加すれば、メーリングリスト用のリポジトリが作れる。James を再起動すれば、james-2.2.0/apps/james/var/lists/myjames というディレクトリが出来るので確認されたい。

で、myjames-on@mydomain.jp に空メールを送れば、myjames メーリングリストへの配信開始要求になり、myjames-off@mydomain.jp に空メールを送れば配信の中止要求になる。myjames@mydomain.jp 宛に送ったメールは現在登録されているメーリングリスト全体へのブロードキャスト配信になるわけだ。じゃあ、とりあえず登録用に myjames-on@mydomain.jp に空メールを送ってみると、次のような返信が届く。

Return-Path: <>
X-Original-To: sug@shopmail.jp
Delivered-To: sug@shopmail.jp
Received: from bizet (localhost [127.0.0.1])
	by bizet.kobe-du.ac.jp (Postfix) with SMTP id 563296FB1D
	for <sug@shopmail.jp>; Tue, 20 Dec 2005 09:47:11 +0900 (JST)
Message-ID: <14863189.1135039631237.JavaMail.sug@bizet>
Date: Tue, 20 Dec 2005 09:47:11 +0900 (GMT+09:00)
From: postmaster@localhost.kobe-du.ac.jp
To: sug@shopmail.jp
Subject: Re: 
Mime-Version: 1.0
Content-Type: multipart/mixed;
  boundary="----=_Part_2_23845098.1135039631232"
Status: R

------=_Part_2_23845098.1135039631232
Content-Type: multipart/alternative; 
	boundary="----=_Part_3_23994289.1135039631232"

------=_Part_3_23994289.1135039631232
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

Successfully added to listserv.
------=_Part_3_23994289.1135039631232--

------=_Part_2_23845098.1135039631232
Content-Type: message/rfc822; name="No Subject"
Content-Disposition: attachment; filename="No Subject"

Return-Path: <sug@shopmail.jp>
Received: from localhost ([127.0.0.1])
          by bizet (JAMES SMTP Server 2.2.0) with SMTP ID 132
          for <myjames-on@mydomain.jp>;
          Tue, 20 Dec 2005 09:47:11 +0900 (GMT+09:00)
Subject:
Date: Tue, 20 Dec 2005 09:47:11 +0900 (GMT+09:00)
From: sug@shopmail.jp
Content-Transfer-Encoding: 7bit
Content-Type: text/plain; charset=us-ascii
Mime-Version: 1.0
Message-ID: <26435810.1135039631271.JavaMail.sug@bizet>


------=_Part_2_23845098.1135039631232--

のようなマルチパートの返信メールで「登録完了」を知らせる。そして、james-2.2.0/apps/james/var/lists/myjames/ に、オブジェクトファイルが出来ており、これがメーリングリストの宛先を保存しているわけだ。

じゃあ、myjames@mydomain.jp 宛に、内容のあるメールを送ってみよう。そうするとメーリングリストの登録者すべてにメールが送られることになる。

Return-Path: <postmaster@mydomain.jp>
X-Original-To: sug@shopmail.jp
Delivered-To: sug@shopmail.jp
Received: from bizet (localhost [127.0.0.1])
	by bizet.kobe-du.ac.jp (Postfix) with SMTP id 78CC46FB1D
	for <sug@shopmail.jp>; Tue, 20 Dec 2005 09:55:59 +0900 (JST)
Received: from localhost ([127.0.0.1])
          by bizet (JAMES SMTP Server 2.2.0) with SMTP ID 359
          for <myjames@mydomain.jp>;
          Tue, 20 Dec 2005 09:55:52 +0900 (GMT+09:00)
Subject: [MyJamesML] first mail
Date: Tue, 20 Dec 2005 09:55:52 +0900 (GMT+09:00)
From: sug@shopmail.jp
X-been-there: myjames@mydomain.jp
Message-Id: <20051220005559.78CC46FB1D@bizet.kobe-du.ac.jp>
To: undisclosed-recipients:;
Status: R

This is a first mail to myjames mailing-list.
How are you?

この時、Subject: は、

[subjectprefix] オリジナルの Subject:

のようになっているのをご確認されたい。で、「X-been-there:」という見慣れないヘッダがあるが、これはこのメーリングリスト機能が「既に処理」のために立てるフラグの役割りを果たすヘッダであったりする。まあ、このヘッダを見れば、送信時にちょいとメーリングリストの配信メールを識別して何か処理する...なんて技も使えるな。

JDBCListserv

でこれは「古い方」のバリエーションだ。AvalonListserv はオプションでメーリングリスト動作をいろいろと設定したが、こっちはそういうオプションをすべてDBに保存するというゴツイものだ。だから、DBに対して、

SELECT members_only, attachments_allowed, reply_to_list, subject_prefix, 
  list_address FROM オプションテーブル名  WHERE listserv_id = メーリングリスト名
SELECT member FROM メンバーテーブル名 WHERE listserv_id = メーリングリスト名

というようなSQLを発行して、オプションや配信ユーザを取得する。だから設定項目は

<mailet match="myjames@mydomain.jp" class="JDBCListserv">
   <data_source>データソース名</datasource>
   <listserv_id>メーリングリスト名</listserv_id>
   <listserv_table>オプションテーブル名</listserv_table>
   <members_table>メンバーテーブル名</members_table>
</mailet>

という感じのものだ。

CommandListservManager

CommandListservProcessor

CommandListservFooter

すべて [終了]。さて、こっちは「新しい方」だ。「古い方」とどう違うのか?というと、古い方は「***-on(配信要求)」「***-off(中止要求)」の2つのコマンドしかなかったが、新しい方は「***-subscribe(配信要求)」「***-unsubscribe(中止要求)」の他に、

***-subscribe-confirm
配信されているか確認する
***-unsubscribe-confirm
配信中止されているか確認する
***-error
エラー時の処理
***-owner
メーリングリストの管理者宛にメールする
***-info
現在の登録者リストなどのメーリングリスト情報を返す

といった新コマンドが追加されたのと同時に、「自前で新しいコマンドを作ってもOK]というヘヴィな機能テンコ盛りのものだ。だから同様に設定してみると、

<mailet match="CommandListservMatcher=myjames@mydomain.jp" class="CommandListservManager">
  <resources>/usr/local/james-2.2.0/apps/james/conf/miResources.xml。
    要するにこれが使うリソースファイルのフルパスを指定する。</resources>
  <listName>myjames。メーリングリスト名</listName>
  <displayName>About James。表示上の名前</displayName>
  <listOwner>sug@mydomain.jp。メーリングリストの所有者</listOwner>
  <repositoryName>myjames。使うリポジトリ名</repositoryName>
  <listDomain>mydomain.jp。このメーリングリストが所属するドメイン名
    を指定</listDomain>

  <!-- 以降コマンドの設定。コマンドの実装クラスが存在するパッケージを指定する -->
  <commandpackages>
     <commandpackage>org.apache.james.transport.mailets.listservcommands</commandpackage>
  </commandpackages>

  <!-- コマンド名とそのハンドラのクラスとを結びつける -->
  <!-- 何か新しいコマンドを作りたければここで定義すればイイわけだ -->
  <commands>
     <command name="subscribe" class="Subscribe"/>
     <command name="subscribe-confirm" class="SubscribeConfirm"/>
     <command name="unsubscribe" class="UnSubscribe"/>
     <command name="unsubscribe-confirm" class="UnSubscribeConfirm"/>
     <command name="error" class="ErrorCommand"/>
     <command name="owner" class="Owner"/>
     <command name="info" class="Info"/>
  </commands>
</mailet>

<mailet match="RecipientIs=myjames@mydomain.jp" class="CommandListservProcessor">
  
  <membersonly>false</membersonly>
  <attachmentsallowed>true</attachmentsallowed>
  <replytolist>true</replytolist>
  <repositoryName>myjames</repositoryName>
  <subjectprefix>Announce</subjectprefix>
  <autobracket>true</autobracket>
  
  <listOwner>sug@mydomain.jp。所有者</listOwner>
  <listName>myjames。メーリングリスト名。</listName>
</mailet>

くらいの感じか。で、こいつはどうやら特殊な性格をいろいろ備えていたりするので、どうやらトップレベルのプロセッサ(<processor name="root">)内で定義しなきゃならないようだ。あと、<resources> タグで設定されるファイルは、james-2.2.0/apps/james/conf/miResources.xml でパッケージに入っているので、適切にフルパス指定をしてやればいい。これは XML 形式で新メーリングリスト機能が使う文面などのリソースが入っている。

まあここらへんややこしいのは開発者側でも判っているようで、james-2.2.0/apps/james/conf/james-listmanager.xml という設定ファイルを用意していたりしてくれる。そっちを参照するのもイイだろう。

じゃやってみようか。ここで myjames-info@mydomain.jp 宛にメールを送ると、

Return-Path: <sug@mydomain.jp>
X-Original-To: sug@shopmail.jp
Delivered-To: sug@shopmail.jp
Received: from bizet (localhost [127.0.0.1])
	by bizet.kobe-du.ac.jp (Postfix) with SMTP id B076D6FB1D
	for <sug@shopmail.jp>; Tue, 20 Dec 2005 11:01:00 +0900 (JST)
Message-ID: <14863189.1135044060367.JavaMail.sug@bizet>
Date: Tue, 20 Dec 2005 11:01:00 +0900 (GMT+09:00)
From: Mailing List about James <sug@mydomain.jp>
To: sug <sug@shopmail.jp>
Subject: Information regarding the myjames mailing list
Mime-Version: 1.0
Content-Type: multipart/mixed;
  boundary="----=_Part_0_27667505.1135044060141"
Status: R

------=_Part_0_27667505.1135044060141
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit


Hi! This is the list serv program. I'm managing the myjames@mydomain.jp mailing list.

I'm working for my owner, who can be reached at myjames-owner@mydomain.jp.
--- Information regarding the myjames list ---

This is a public mailing list associated with the mydomain.jp server.
It is intended to be a convienent mechanism to share ideas, news and current 
events with like minded people.

--- Common practices ---
Sending email to the list:
    If you want to send email to the list, you can simply click on this link: 
myjames@mydomain.jp and then send what ever email you'd like to share with the 
other members of the myjames mailing list.

Recieving email from the list:
    Every time someone sends email to the list, every member will recieve the 
email in their inbox.

Replying to list email:
    When you reply to email from the list, it will also go to every member on 
the list.

--- Current members ---
The list of current members are:  現在登録されている配信ユーザ
    
    sug@shopmail.jp
    james@shopmail.jp

--- Administrative commands for the myjames list ---

I can handle administrative requests automatically. Please do not send them to 
the list address! Instead, send your message to the correct command address:

To subscribe to the list, send a message to:
   <myjames-subscribe@mydomain.jp>

To remove your address from the list, send a message to:
   <myjames-unsubscribe@mydomain.jp>

Send mail to the following for info and FAQ for this list:
   <myjames-info@mydomain.jp>

If despite following these instructions, you do not get the desired results, 
please contact my owner at myjames-owner@mydomain.jp. Please be patient, my 
owner is a lot slower than I am ;-)
------=_Part_0_27667505.1135044060141--

という感じで登録ユーザなどを教えてくれる。

じゃあ、新しい方にメールを送って配信させてみよう。

Return-Path: <sug@mydomain.jp>
X-Original-To: sug@shopmail.jp
Delivered-To: sug@shopmail.jp
Received: from bizet (localhost [127.0.0.1])
	by bizet.kobe-du.ac.jp (Postfix) with SMTP
	id 28A806FB1D; Tue, 20 Dec 2005 11:15:34 +0900 (JST)
Received: from localhost ([127.0.0.1])
          by bizet (JAMES SMTP Server 2.2.0) with SMTP ID 432
          for <myjames@mydomain.jp>;
          Tue, 20 Dec 2005 11:15:29 +0900 (GMT+09:00)
Subject: [MyJamesML]test for new mailing-list
Date: Tue, 20 Dec 2005 11:15:29 +0900 (GMT+09:00)
From: sug@shopmail.jp
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
  charset=us-ascii
Mime-Version: 1.0
Message-ID: <12432643.1135044933739.JavaMail.sug@bizet>
X-been-there: myjames@mydomain.jp
Reply-To: myjames@mydomain.jp
To: undisclosed-recipients:;
Status: R

This is a test for NEW mailing-list myjames.
How are you?

---------------------------------------------------------------------
To unsubscribe, e-mail: myjames-unsubscribe@mydomain.jp
For additional commands, e-mail: myjames-info@mydomain.jp

そうすると今度は本文末尾に太字で強調したように、「小さな親切」的メッセージが追加されていたりもするな....まあ、「小さな親切」を「大きな御世話」だと思うならば、james-2.2.0/apps/james/conf/miResources.xml をイジって消せばOKだ。

  <group name="footer">
    <resource name="text" xml:space="preserve"><![CDATA[
---------------------------------------------------------------------
To unsubscribe, e-mail: ${LIST_NAME}-unsubscribe@${DOMAIN_NAME}
For additional commands, e-mail: ${LIST_NAME}-info@${DOMAIN_NAME}]]>
    </resource>
  </group>
  <group name="footer_html">
    <resource name="text" xml:space="preserve"><![CDATA[
---------------------------------------------------------------------
To unsubscribe, e-mail: <a href="mailto:${LIST_NAME}-unsubscribe@
${DOMAIN_NAME}">${LIST_NAME}-unsubscribe@${DOMAIN_NAME}</a>
For additional commands, e-mail: <a href="mailto:${LIST_NAME}-info@
${DOMAIN_NAME}">${LIST_NAME}-info@${DOMAIN_NAME}</a>]]>
    </resource>
  </group>

あと、一応 CommandListservFooter という Mailet クラスも存在するが、実はこれは CommandListservProcessor の内部で呼び出されて使われるだけの存在である。気にしなくてよい。

その他

FromRepository

[終了] すでに送られたメールを再処理させるかなり特殊な Mailet である。この Mailet が起動させられると、オプションで指定するリポジトリに保存されたメールを、指定のプロセッサによって再処理させる。だから、これを起動するメールは単にトリガとしての役割しかないので、ここでメールは終了する。たとえば次のようにして起動する。

<mailet match="RecipientIs=respool@localhost" class="FromRepository">
    <repositoryPath>リポジトリの指定</repositoryPath>
    <processor>扱わせる Processor。default は root</repositoryPath>
    <delete>true|false。処理後に元メールを削除するかどうか。デフォルトは false </delete>
</mailet>

再処理されたメールには、Mail Attribute として、"FromRepository" => true が付与される。

LogHeaders

[可変] ヘッダをログする。通常の設定だと出力先は、james-2.2.0/apps/james/logs/mailet-*.log だろう。[可変]Mailet であるあたり判った設計である。そのオプションだけを取る。

<mailet match="UserIs=sug" class="LogHeaders" >
   <passThrough>true|false。false ならパイプラインを終了する。default は true
      (処理継続)</passThrough>
</mailet>

出力はこんな感じ。

15/12/05 14:04:39 INFO  James.Mailet: LogHeaders: Logging mail Mail1134623028304-0
15/12/05 14:04:39 INFO  James.Mailet: LogHeaders:
Return-Path: <sug@shopmail.jp>
Received: from localhost ([127.0.0.1])
          by bizet (JAMES SMTP Server 2.2.0) with SMTP ID 366
          for <sug@mydomain.jp>;
          Thu, 15 Dec 2005 14:03:48 +0900 (GMT+09:00)
Subject: test Log
Date: Thu, 15 Dec 2005 14:03:48 +0900 (GMT+09:00)
From: sug@shopmail.jp



copyright by K.Sugiura, 1996-2006