2006-07-29
■[技術] Windowsで動くウェブ開発専用のSMTP・POPサーバ

Apache Jamesを使って、開発に都合のいいSMTP・POPサーバを構築します。
0. この記事を書くに至るまでの経緯、とか。
開発用メールサーバに欲しい機能として、以下の4つがあげられます。
1。メールを外に送出しない設定ができること。ウェブアプリケーションの開発に、リアルワールドに直結したメールサーバを使っていると、ちょっとしたミスで、外に出してはいけないメールを送ってしまう可能性があります。いままでメールの操作で何度かひどいミスをしているので(@の前に自分の苗字を書くつもりで名前を書いたりとか……)、なんとかしたいと思っていました。
2。ローカルのWindows上で動いてほしい。今年に入って開発環境をノートPCに移したため、ネットワークにつながらない環境で開発を行う機会が増えました。「会員登録」のような、外のメールサーバにつなぎにいく処理でネットにつながらないと、メールに書かれた内容を確認するためにログの設定を変えたり、DBの中身を見たりと、非常に面倒です。
3。全メールを1つのinboxにフォワードしてほしい。複数のテスト用アドレスを準備しても受信するのが面倒だとだんだん1つの(しかも日常使っている)アドレスしか使わなくなったりします。きちんとテストするために、アドレスは何でも使えて、受信も簡単な環境にしたいと思っていました。
4。Seleniumでテストしたい。たぶんブラウザで動くPOPメーラかなんかが使えるだろうと思います。Seleniumで動かすには同じドメインに入っている必要があるので、Gmailでというわけにはいきません。自動テストにはシンプルなインタフェースの方がいいので、自作するといいかもしれません。
とりあえず最後のは置いといて(まだ探してません)、上の3つはApache Jamesを使うことで解決できました。以下、インストール手順の紹介です。Windowsでしか試していませんが、Javaが動けばどのような環境でも使えると思います。
1. ローカルホストにホスト名を設定する
user@localhost のようなメールアドレスは、アプリケーションのバリデーションを通らない可能性があります。例えばCommons Validatorに含まれるEmailValidatorの場合、「user@[127.0.0.1]」は通しますが「user@localhost」は通しません*1。これでは動作テストには使用できません。
この問題を手っ取り早く回避するため、hostsファイルを書き換えてローカルホストにホスト名を設定します。自分は、以下の投稿などを参考に、ローカルホスト用のTLDとして'.local'を使うことにしました。
今のところ、プライベート IP アドレスと類似の、LAN 上で安心して使えるドメイン名はありません。私が使っている .localは draft-ietf-dnsind-local-names で提案されていたものですが、このドラフトは現在は期限切れで削除されています。
期限切れのドラフトは以下にあります。
というわけで、進行中のプロジェクトには「(プロジェクト名).local」を使用し、メールアドレスや、プロジェクト共通で使うウェブアプリケーション(phpMyAdminとか)には「default.local」を使うことにしました。
127.0.0.1 default.local
2. Apache Jamesをダウンロードして起動する
- http://james.apache.org/download.cgi で james-2.2.0.zip をダウンロードします。
- 適当な場所に展開して、bin/run.batを実行。
でとりあえずは動きます。
Phoenix 4.0.1 James 2.2.0 Remote Manager Service started plain:4555 POP3 Service started plain:110 SMTP Service started plain:25 NNTP Service started plain:119 Fetch POP Disabled FetchMail Disabled
3. Jamesの管理サービスにtelnetで接続してユーザを追加する
telnetでlocalhostポート4555に接続します。ID:root / PASS:root。
C:\> telnet localhost 4555 JAMES Remote Administration Tool 2.2.0 Please enter your login and password Login id: root Password: root Welcome root. HELP for a list of commands help Currently implemented commands: help display this help listusers display existing accounts countusers display the number of existing accounts adduser [username] [password] add a new user verify [username] verify if specified user exist deluser [username] delete existing user setpassword [username] [password] sets a user's password setalias [user] [alias] locally forwards all email for 'user' to 'alias' showalias [username] shows a user's current email alias unsetalias [user] unsets an alias for 'user' setforwarding [username] [emailaddress] forwards a user's email to another email address showforwarding [username] shows a user's current email forwarding unsetforwarding [username] removes a forward user [repositoryname] change to another user repository shutdown kills the current JVM (convenient when J ames is run as a daemon) quit close connection # ユーザを作成します。 add user test1 test User test1 added add user test2 test User test2 added add user test3 test User test3 added quit Bye
4. 設定ファイルを修正
初回の起動で、設定ファイルが './apps/james/SAR-INF' 以下に展開されています。一度起動しないと設定ファイルは生成されないことに注意してください。設定を変える前にconfig.xmlのバックアップをしておきます。
config.xmlに、以下の修正を加えます。
上でhostsファイルに設定したホスト名をservernameに設定。
<servername>localhost</servername> ↓ <servername>default.local</servername>
<mailet match="HostIsLocal" class="ToProcessor"> <processor> local-address-error </processor> <notice>550 - Requested action not taken: no such user here</notice> </mailet>
リモートに送信しないよう、以下を削除。
<mailet match="All" class="RemoteDelivery"> <outgoing> file://var/mail/outgoing/ </outgoing> <!-- alternative database repository example below --> : (略) : </mailet>
同じ位置に、以下を追加。
<mailet match="All" class="Forward"> <forwardTo>test1@default.local</forwardTo> </mailet>
これで存在しないローカルアドレスと、全リモートアドレスが test1@default.local にフォワードされます。
Control-CでJamesを止めて、run.batで再度起動して下さい。
これで、
- test1@default.local
- test2@default.local
- test3@default.local
のアドレスが使えるようになっています。
SMTPをdefault.localに設定してtest1@default.localにメールを送信、POPサーバにtest1/testで接続してメールが受信できることを確認してください。同様にtest2、test3の送受信ができます。
また、ユーザを作成したtest1〜3以外のあらゆるアドレスにメールを送信した場合は、test1にメールがフォワードされます。確認してみてください。
5. 設定ファイルをダイエット
配布されている巨大なconfig.xmlから、コメントや開発に不要そうなスパムブラックリストの設定などを取り除いた設定を以下に示します。このまま使う場合は、assembly.xmlから、NNTP Server、NNTP Repository、FetchPOP Service、FetchMail Serviceをコメントアウトする必要があります。
<?xml version="1.0"?>
<!DOCTYPE config [
<!ENTITY listserverConfig SYSTEM "../conf/james-listmanager.xml">
<!ENTITY listserverStores SYSTEM "../conf/james-liststores.xml">
<!ENTITY fetchmailConfig SYSTEM "../conf/james-fetchmail.xml">
]>
<config>
<James>
<postmaster>Postmaster@default.local</postmaster>
<servernames autodetect="true" autodetectIP="true">
<servername>default.local</servername>
</servernames>
<usernames ignoreCase="true" enableAliases="true" enableForwarding="true"/>
<inboxRepository>
<repository destinationURL="file://var/mail/inboxes/" type="MAIL"/>
</inboxRepository>
</James>
<spoolmanager>
<threads> 10 </threads>
<mailetpackages>
<mailetpackage>org.apache.james.transport.mailets</mailetpackage>
</mailetpackages>
<matcherpackages>
<matcherpackage>org.apache.james.transport.matchers</matcherpackage>
</matcherpackages>
<processor name="root">
<mailet match="RelayLimit=30" class="Null"/>
<mailet match="All" class="ToProcessor">
<processor> transport </processor>
</mailet>
</processor>
<processor name="transport">
<mailet match="RecipientIsLocal" class="LocalDelivery"/>
<mailet match="RemoteAddrNotInNetwork=127.0.0.1" class="ToProcessor">
<processor> relay-denied </processor>
<notice>550 - Requested action not taken: relaying denied</notice>
</mailet>
<!-- 外に送信する場合 -->
<!--
<mailet match="All" class="RemoteDelivery">
<outgoing> file://var/mail/outgoing/ </outgoing>
<delayTime datetime="2006-07-30T05:23:56+09:00"> 5 minutes </delayTime>
<delayTime datetime="2006-07-30T05:23:56+09:00"> 10 minutes </delayTime>
<delayTime datetime="2006-07-30T05:23:56+09:00"> 45 minutes </delayTime>
<delayTime datetime="2006-07-30T05:23:56+09:00"> 2 hours </delayTime>
<delayTime datetime="2006-07-30T05:23:56+09:00"> 3 hours </delayTime>
<delayTime datetime="2006-07-30T05:23:56+09:00"> 6 hours </delayTime>
<maxRetries> 25 </maxRetries>
<deliveryThreads datetime="2006-07-30T05:23:56+09:00"> 1 </deliveryThreads>
<sendpartial>false</sendpartial>
</mailet>
-->
<!-- バウンスメールの実験用。bounce_で始まるアドレスがバウンスする。-->
<!--
<mailet match="RecipientIsRegex=bounce_.+" class="Bounce">
<message>bounce mail test</message>
<passThrough>false</passThrough>
</mailet>
-->
<mailet match="All" class="Forward">
<forwardTo>test1@default.local</forwardTo>
</mailet>
</processor>
<processor name="error">
<mailet match="All" class="ToRepository">
<repositoryPath>file://var/mail/error/</repositoryPath>
</mailet>
</processor>
<processor name="relay-denied">
<mailet match="All" class="ToRepository">
<repositoryPath>file://var/mail/relay-denied/</repositoryPath>
</mailet>
</processor>
</spoolmanager>
<dnsserver>
<servers></servers>
<autodiscover>false</autodiscover>
<authoritative>false</authoritative>
</dnsserver>
<remotemanager>
<port>4555</port>
<handler>
<helloName autodetect="true">myMailServer</helloName>
<administrator_accounts>
<account login="root" password="root"/>
</administrator_accounts>
<connectiontimeout> 60000 </connectiontimeout>
</handler>
</remotemanager>
<pop3server enabled="true">
<port>110</port>
<handler>
<helloName autodetect="true">myMailServer</helloName>
<connectiontimeout>120000</connectiontimeout>
</handler>
</pop3server>
<smtpserver enabled="true">
<port>25</port>
<handler>
<helloName autodetect="true">myMailServer</helloName>
<connectiontimeout>360000</connectiontimeout>
<authorizedAddresses>127.0.0.0/8</authorizedAddresses>
<maxmessagesize>0</maxmessagesize>
</handler>
</smtpserver>
<mailstore>
<repositories>
<repository class="org.apache.james.mailrepository.AvalonMailRepository">
<protocols>
<protocol>file</protocol>
</protocols>
<types>
<type>MAIL</type>
</types>
</repository>
<repository class="org.apache.james.mailrepository.AvalonSpoolRepository">
<protocols>
<protocol>file</protocol>
</protocols>
<types>
<type>SPOOL</type>
</types>
</repository>
<repository class="org.apache.james.mailrepository.JDBCMailRepository">
<protocols>
<protocol>db</protocol>
</protocols>
<types>
<type>MAIL</type>
</types>
<config>
<sqlFile>file://conf/sqlResources.xml</sqlFile>
</config>
</repository>
<repository class="org.apache.james.mailrepository.JDBCSpoolRepository">
<protocols>
<protocol>db</protocol>
</protocols>
<types>
<type>SPOOL</type>
</types>
<config>
<sqlFile>file://conf/sqlResources.xml</sqlFile>
<maxcache>1000</maxcache>
</config>
</repository>
<repository class="org.apache.james.mailrepository.JDBCMailRepository">
<protocols>
<protocol>dbfile</protocol>
</protocols>
<types>
<type>MAIL</type>
</types>
<config>
<sqlFile>file://conf/sqlResources.xml</sqlFile>
<filestore>file://var/dbmail</filestore>
</config>
</repository>
<repository class="org.apache.james.mailrepository.JDBCSpoolRepository">
<protocols>
<protocol>dbfile</protocol>
</protocols>
<types>
<type>SPOOL</type>
</types>
<config>
<sqlFile>file://conf/sqlResources.xml</sqlFile>
<filestore>file://var/dbmail</filestore>
<maxcache>1000</maxcache>
</config>
</repository>
<repository class="org.apache.james.mailrepository.MBoxMailRepository">
<protocols>
<protocol>mbox</protocol>
</protocols>
<types>
<type>MAIL</type>
</types>
</repository>
</repositories>
<spoolRepository>
<repository destinationURL="file://var/mail/spool/" type="SPOOL"/>
</spoolRepository>
</mailstore>
<users-store>
<repository name="LocalUsers" class="org.apache.james.userrepository.UsersFileRepository">
<destination URL="file://var/users/"/>
</repository>
</users-store>
<database-connections>
<data-sources></data-sources>
</database-connections>
<objectstorage>
<repositories>
<repository class="org.apache.james.mailrepository.filepair.File_Persistent_Object_Repository">
<protocols>
<protocol>file</protocol>
</protocols>
<types>
<type>OBJECT</type>
</types>
<models>
<model>SYNCHRONOUS</model>
<model>ASYNCHRONOUS</model>
<model>CACHE</model>
</models>
</repository>
<repository class="org.apache.james.mailrepository.filepair.File_Persistent_Stream_Repository">
<protocols>
<protocol>file</protocol>
</protocols>
<types>
<type>STREAM</type>
</types>
<models>
<model>SYNCHRONOUS</model>
<model>ASYNCHRONOUS</model>
<model>CACHE</model>
</models>
</repository>
</repositories>
</objectstorage>
<connections>
<idle-timeout>300000</idle-timeout>
<max-connections>30</max-connections>
</connections>
<sockets>
<server-sockets>
<factory name="plain" class="org.apache.avalon.cornerstone.blocks.sockets.DefaultServerSocketFactory"/>
</server-sockets>
<client-sockets>
<factory name="plain" class="org.apache.avalon.cornerstone.blocks.sockets.DefaultSocketFactory"/>
</client-sockets>
</sockets>
<thread-manager>
<thread-group>
<name>default</name>
<priority>5</priority>
<is-daemon>false</is-daemon>
<max-threads>100</max-threads>
<min-threads>20</min-threads>
<min-spare-threads>20</min-spare-threads>
</thread-group>
</thread-manager>
</config>
*1:ドコモのRFC違反アドレスを通すCommons EmailValidator拡張 のテストケースを参照してください。
- 305 http://blog.magellanic.net/archives/78
- 211 http://d.hatena.ne.jp/koseki2/20081030/mocksmtpd
- 140 http://blogs.dion.ne.jp/surviveplus/archives/8378330.html
- 122 http://www.google.co.jp/search?sourceid=navclient&hl=ja&ie=UTF-8&rls=DVXA,DVXA:2006-17,DVXA:ja&q=海がめのスープ
- 75 http://geocities.yahoo.co.jp/gl/xmlinux2004/view/20090203/1233634107
- 63 http://d.hatena.ne.jp/keyword/TrackBack
- 56 http://www.google.co.jp/search?hl=ja&client=firefox-a&rls=org.mozilla:ja:official&hs=u5L&q=smtp+開発用&btnG=検索&lr=lang_ja
- 26 http://www.teria.com/~koseki/
- 23 http://www.google.co.jp/search?hl=ja&lr=lang_ja&client=firefox-a&rls=org.mozilla:ja:official&hs=8Ti&q=james+smtp+pop3+設定&suggest=1&sa=X&oi=cjkrefinements&resnum=0&ct=result&cd=2
- 23 http://www.google.com/search?hl=ja&lr=lang_ja&ie=UTF-8&oe=UTF-8&q=+WSSE+認証&num=50

Doom3内のアーケードマシンでDoom1が動くMOD
http://battleteam.net/tech/fis/
アーケードマシンを動かす仕組みがあるみたい。面白い。
http://battleteam.net/tech/fis/arcade.html
リリースされました。
http://james.apache.org
Apache James(JavaMail?)には文字化けの問題があるようなので、新しいバージョンも試してみます(ダメそうですが)。
http://tech.g.hatena.ne.jp/koseki/20060902#James
http://www.javadrive.jp