2011年5月10日 星期二

Apache James Server 3.0(M2) - Install & setting

本篇文章中會提到的有James Server 3.0 的安裝跟基本設定, 還有Mailet & Matcher
首先要安裝 James Server 3.0
可以參考 Quick Start
1: 先確認系統環境,必須有安裝JDK 1.5+
2: 下載james-server-container-spring-3.0-M2-bin.zip ,並解壓到指定資料夾(EX c:\)
然後要做一些設定,James 3.0 版本在設定上分成多個XML
相關XML的細節可參考 Configure James Server , 這邊只針對幾個設定做說明
打開cmd 輸入ipconfig /all , 查看local 的DNS設定
打開{yourJamesServerPath}\conf\dnsservice.xml ,設定如下
dnsservice.xml 相關的設定如下:
<!-- DNS Service Block -->
<!-- -->
<!-- Specifies DNS Server information for use by various components inside -->
<!-- James. -->
<!-- -->
<!-- If autodiscover is true, James will attempt to autodiscover the DNS servers configured on your underlying system.-->
<!-- Currently, this works if the OS has a unix-like /etc/resolv.conf,-->
<!-- or the system is Windows based with ipconfig or winipcfg.-->
<!-- -->
<!-- If no DNS servers are found and you have not specified any below, 127.0.0.1 will be used-->
<!-- If you use autodiscover and add DNS servers manually a combination of all the dns servers will be used  -->
<!--  -->
<!-- Information includes a list of DNS Servers to be used by James.  These are -->
<!-- specified by the server elements, each of which is a child element of the -->
<!-- servers element.  Each server element is the IP address of a single DNS server. -->
<!-- The servers element can have multiple server children. -->
<dnsservice>
    <servers>
        <!--Enter ip address of your DNS server, one IP address per server -->
        <!-- element. -->
        <!--
        <server>127.0.0.1</server>
        -->
    </servers>
    <!-- Change autodiscover to false if you would like to turn off autodiscovery -->
    <!-- and set the DNS servers manually in the <servers> section -->
    <autodiscover>true</autodiscover>
    <authoritative>false</authoritative>

    <!-- Maximum number of entries to maintain in the DNS cache -->
    <maxcachesize>50000</maxcachesize>
      
    <!-- Uncomment this if you want James to try a single server for each -->
    <!-- multihomed mx host. -->
    <!--
    <singleIPperMX> true </singleIPperMX>
    -->
</dnsservice>


打開{yourJamesServerPath}\conf\domainlist.xml, 設定如下,這邊主要設定dns namespace identifies
以下是domainlist.xml的說明
<!-- Domainnames identifies the DNS namespace served by this instance of James. -->
<!-- These domainnames are used for both matcher/mailet processing and SMTP auth -->
<!-- to determine when a mail is intended for local delivery. -->
<!-- -->
<!-- If autodetect is TRUE, James wil attempt to discover its own host name AND -->
<!-- use any explicitly specified servernames. -->
<!-- If autodetect is FALSE, James will use only the specified domainnames. -->
<!-- -->
<!-- If autodetectIP is not FALSE, James will also allow add the IP address for each servername. -->
<!-- The automatic IP detection is to support RFC 2821, Sec 4.1.3, address literals. -->
<!-- -->
<!-- To override autodetected domainames names simply add explicit domainname elements. -->
<!-- In most cases this will be necessary. -->
<!-- By default, the domainname 'localhost' is specified. This can be removed, if required. -->
<!-- -->
<!-- Warning: If you are using fetchmail it is important to include the -->
<!-- fetched domains in the server name list to prevent looping.       -->    
<domainlists>

  <!-- XML based DomainList implementation -->
  <domainlist class="org.apache.james.domainlist.xml.XMLDomainList">
    <domainnames>
      <domainname>localhost</domainname>
    </domainnames>
    <autodetect>true</autodetect>
    <autodetectIP>true</autodetectIP>
  </domainlist>
  
  <!-- JPA implementation for DomainList -->
  <!-- 
  <domainlist class="org.apache.james.domainlist.jpa.JPADomainList">
    <autodetect>true</autodetect>
    <autodetectIP>true</autodetectIP>
  </domainlist>
  -->
  
  <!-- JDBC implementation for DomainList. This is deprecated and should not be used. -->
  <!-- Use JPADomainList if you need a db backend DomainList -->
  <!-- 
  <domainlist class="org.apache.james.domainlist.jdbc.JDBCDomainList">
    <repositoryPath>db://maildb/domain</repositoryPath>
    <sqlFile>file://conf/sqlResources.xml</sqlFile>
    <autodetect>true</autodetect>
    <autodetectIP>true</autodetectIP>
  </domainlist>  
  -->
</domainlists>

修改C:\WINDOWS\system32\drivers\etc\hosts , 增加domain & ip mapping

打開{yourJamesServerPath}\conf\mailserver.xml, 設定如下

defaultDomain 為預設的domain name, 設成mydomain.james
postmaster設為Postmaster@mydomain.james , 如果新增一個user 叫AAA ,那mail位置就為AAA@mydomain.james

mailserver.xml的相關設定說明
<mailserver>
    <!-- CHECKME! -->
    <!-- This is the postmaster email address for this mail server. -->
    <!-- Set this to the appropriate email address for error reports -->
    <!-- If this is set to a non-local email address, the mail server -->
    <!-- will still function, but will generate a warning on startup. -->
    <postmaster>Postmaster@localhost</postmaster>
    
    <!-- Set to true to support virtualHosting. If virtualHosting support is enabled the server will accept thread every user independ on -->
    <!-- domain level. -->
    <enableVirtualHosting> false </enableVirtualHosting>     
      
    <!-- Set the default domain which will be used if an email is send to a recipient without a domain part -->
    <!-- If not defaultdomain is set the first domain of the DomainList get used -->
    <defaultDomain> localhost </defaultDomain>
      
    <!-- This is the name used by the server to identify itself in the RemoteManager -->
    <!-- protocol.  If autodetect is TRUE, the server will discover its -->
    <!-- own host name and use that in the protocol.  If discovery fails, -->
    <!-- the value of 'localhost' is used.  If autodetect is FALSE, James -->
    <!-- will use the specified value. -->
    <!-- Set the default helloName which is used in all services if not overridden in the specific service-->
    <helloName autodetect="true">myMailServer</helloName>
</mailserver>

然後在cmd 下進入{yourJamesServerPath}\bin, 輸入james install, 安裝james
再輸入james start , 會啟動james server,如下圖所示:

然後輸入telnet mydomain.james 4555 ,連進admin設定, 預設的acct/passwd 都是root
進入後可以新增user , 使用adduser [acctname] [passwd] , 新增一個test , 位置就是test@mydomain.james

然後可以telnet 到mydomain.james 25 做動作,但這邊我用outlook 收發mail
設定如下,
就能正常的收發信了(外信如gmail要發到內部domain這邊不在處理範圍內:p )

再來就是主要的Mailet & Matcher , Mailet API是一個用來做郵件處理程序的簡單的API。 James是Mailet的容器​​,通過Mailet(定制的或已有的)完成各種複雜的郵件處理任務,類似Servlet ,

這邊用Maven Create 一個sample Maven Project , pom.xml如下
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

 <modelVersion>4.0.0</modelVersion>
 <groupId>com.my.james</groupId>
 <artifactId>MyJames.Mailet</artifactId>
 <version>1.0.0</version>

 <dependencies>
  <dependency>
   <groupId>james</groupId>
   <artifactId>james</artifactId>
   <version>3.0a1</version>
  </dependency>
  <dependency>
   <groupId>org.apache.james</groupId>
   <artifactId>apache-mailet-base</artifactId>
   <version>1.1</version>
  </dependency>
  <dependency>
   <groupId>james</groupId>
   <artifactId>mailet-api</artifactId>
   <version>3.0</version>
  </dependency>
  <dependency>
   <groupId>javax.mail</groupId>
   <artifactId>mail</artifactId>
   <version>1.4.1</version>
  </dependency>


 </dependencies>
</project>

Mailet 的部份
com.mypackage.mailets.FilterByMailet
package com.mypackage.mailets;

import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;

import javax.mail.BodyPart;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;

import org.apache.mailet.Mail;
import org.apache.mailet.MailAddress;
import org.apache.mailet.base.GenericMailet;

/**
 * @author momo
 * 
 */
public class FilterByMailet extends GenericMailet {

 @Override
 public void init(){
  this.log("===> test for FilterByMailet init()");
  System.out.println("===> test for FilterByMailet init()");
 }
 
 
 
 public void service(Mail mail) throws MessagingException {
  this.log("");
  try {
   
   this.log("====> MailetInfo ="   this.getMailetInfo());
   System.out.println("====> MailetInfo ="   this.getMailetInfo());
   // TODO Auto-generated method stub
   this.log("====> sender = "   mail.getSender().toString());
   Collection c = mail.getRecipients();
   Iterator it = c.iterator();
   
   if (it.hasNext()) {
    MailAddress recipient = (MailAddress) it.next();
    this.log("====> recipient = "   recipient.toString());
   }
   MimeMessage msg = mail.getMessage();
   this.log("====> subject = "   msg.getSubject());
   this.log("====> contentType = "   msg.getContentType());
   if (msg.getContent() instanceof MimeMultipart) {
    MimeMultipart content = (MimeMultipart) msg.getContent();
    for (int i = 0; i < content.getCount(); i  ) {
     BodyPart p = content.getBodyPart(i);
     this.log(i   ".n"   p.getContentType()   "n"
         p.getContent());
    }
   }

  } catch (IOException e) {
   this.log(e.getMessage());
  } catch (MessagingException e) {
   this.log(e.getMessage());
  }
 }

}


init()可有,可沒有, service() 為必須的, 透過Matcher return collection 後會進入mailet service(), 如果Matcher沒有轉給其他的processor的話,

然後是Matcher的部份,這邊實作的是GenericMatcher , 有另一個為GenericRecipientMatcher ,
GenericMatcher return 為null or mail.getRecipients(), mail.getRecipients()為一個Collection

com.mypackage.matcher.MatcherBySubject
/**
 * 
 */
package com.mypackage.matcher;

import java.util.Collection;

import javax.mail.MessagingException;

import org.apache.mailet.Mail;
import org.apache.mailet.MailAddress;
import org.apache.mailet.base.GenericMatcher;
import org.apache.mailet.base.GenericRecipientMatcher;

/**
 * @author momo
 *
 */
public class MatcherBySubject extends GenericMatcher  {

 @Override
 public Collection match(Mail mail) throws MessagingException {
  this.log("-->MatcherBySubject start ");
  System.out.println("MatcherBySubject return -true");
  
  this.log("getSender =" mail.getSender());
  System.out.println("getSender =" mail.getSender());
  
  return null;
 }
}

然後打開{yourJamesServerPath}\conf\mailetcontainer.xml, 設定如下


這邊要注意一點,自訂的mailet & matcher設定如下
<mailet match="MatcherBySubject" class="com.mypackage.mailets.FilterByMailet"/>

以上的設定要在 RecipientIsLocal , 這個matcher 的前面,
<mailet match="HostIsLocal" class="ToProcessor">
          <processor>local-address-error</processor>
          <notice>550 - Requested action not taken: no such user here</notice>
       </mailet>

主要是因為Mailet 透過SpoolManager 來控制, SpoolManager 針對processor children 為樹狀模式, 每個processor 都有一個唯一的name屬性, 主要的processor為必要的(如root, error), 而"ghost" 是不可使用的,ghost用來表示不再接受更進一步處理的訊息。James的SpoolManager在processor的名稱和郵件“state”之間創建對應關係。如果狀態改變了,那這個消息就不會繼續留在當前的處理器中。如果為ghost對這個的處理就中止,相對的matcher也可以設定state轉發到另一個processor 。 HostIsLocal的內容如下:
/**
         * This mailet redirects the mail to the named processor
         *
         * Sample configuration:
         * <mailet match="All" class="ToProcessor">
         *   <processor>spam</processor>
         *   <notice>Notice attached to the message (optional)</notice>
         * </mailet>
         *
         */
        public class ToProcessor extends GenericMailet {

            /**
             * Controls certain log messages
             */
            private boolean isDebug = false;

            /**
             * The name of the processor to which this mailet forwards mail
             */
            String processor;

            /**
             * The error message to attach to the forwarded message
             */
            String noticeText = null;

            /**
             * Initialize the mailet
             *
             * @throws MailetException if the processor parameter is missing
             */
            public void init() throws MailetException {
                isDebug = (getInitParameter("debug") == null) ? false
                        : new Boolean(getInitParameter("debug")).booleanValue();
                processor = getInitParameter("processor");
                if (processor == null) {
                    throw new MailetException("processor parameter is required");
                }
                noticeText = getInitParameter("notice");
            }

            /**
             * Deliver a mail to the processor.
             *
             * @param mail the mail to process
             *
             * @throws MessagingException in all cases
             */
            public void service(Mail mail) throws MessagingException {
                if (isDebug) {
                    StringBuffer logBuffer = new StringBuffer(128).append(
                            "Sending mail ").append(mail).append(" to ")
                            .append(processor);
                    log(logBuffer.toString());
                }
                mail.setState(processor);
                if (noticeText != null) {
                    if (mail.getErrorMessage() == null) {
                        mail.setErrorMessage(noticeText);
                    } else {
                        StringBuffer errorMessageBuffer = new StringBuffer(256)
                                .append(mail.getErrorMessage()).append("\r\n")
                                .append(noticeText);
                        mail.setErrorMessage(errorMessageBuffer.toString());
                    }
                }
            }

            /**
             * Return a string describing this mailet.
             *
             * @return a string describing this mailet
             */
            public String getMailetInfo() {
                return "ToProcessor Mailet";
            }
        }

因為這邊會對自己發信,matcher會match到為local 所以會進入ToProcessor mailet, 在這邊processor = getInitParameter("processor");會設定為local-address-error , 進入service時mail.setState(processor);,這邊就會轉發給local-address-error , 所以我們自訂Mailet 時要注意mailet的位置。



update:
修改Default database from DERBY to MYSQL ,需要mysql-connector-java-5.1.15.jar , 將其放到{yourJamesPath}\conf\lib 下面 ,打開{yourJamesServerPath}\conf\database.properties, 設定如下

然後重啟james server , telnet myhdomain.james 4555 , 重新新增user , James Server 會對db 做create table 的動作。 每一封信寄到James Server時會insert 到database , 當使用者用tools收信時會清除資料。

Update 05/24:
修改smtpserver.xml , 可以透過增加 authorizedAddresses ,讓來源IP 能不需要認證使用mail relay的功能, 新增一個192.168.2.168
<!-- See http://james.apache.org/server/3/config.html for usage -->

<smtpserver enabled="true">
  <port>25</port>
  <connectionBacklog>200</connectionBacklog>
  <tls socketTLS="false" startTLS="false">
  </tls>
  <handler>
     <connectiontimeout>360</connectiontimeout>
     <connectionLimit> 0 </connectionLimit>
     <connectionLimitPerIP> 0 </connectionLimitPerIP>
     <authorizedAddresses>127.0.0.0/8,192.168.2.168</authorizedAddresses>
     <authRequired>true</authRequired>
     <maxmessagesize>0</maxmessagesize>
     <addressBracketsEnforcement>true</addressBracketsEnforcement>
     <handlerchain>
         <handler class="org.apache.james.smtpserver.fastfail.ValidRcptHandler"/>
         <handler class="org.apache.james.smtpserver.CoreCmdHandlerLoader"/>
     </handlerchain>            
  </handler>
</smtpserver>



Reference:

Mailet API Overview
Mailet Container Configuration
Mailets and Matchers Reference

Working with James, Part 1: An introduction to Apache's James enterprise e-mail server
James - Mailet Sample - Omikuji

13 則留言:

  1. 請問一下~ 關於設定server的部分
    為什麼我的{yourJamesServerPath}\conf\裡面沒有mailserver.xml檔呢 !!?

    回覆刪除
  2. 我比對了一下這編在測試用的版本james-server-container-spring-3.0-M2 跟官網現在3.0 的beta版本apache-james-3.0-beta3-app, 新的版本確實是沒有mailserver.xml ,但是在quicstart裡面又有提到mailserver.xml 的檔案(http://james.apache.org/server/3/quick-start.html , 看Step 4: Configure ) 你可以試試看自己建一個mailserver.xml 放上去...@@

    回覆刪除
  3. 我有測了一下,mailserver.xml 不放的話 照著quick-start 的去啟動,是可以正常啟動mailserver的,可能新版的設定跟我之前用的m2版有些差別吧

    回覆刪除
  4. 不好意思~ 再請問一下

    為什麼我打telnet mydomain.james 4555時會出現下面這種訊息呢?
    'telnet' is not recognized as an internal or external command,
    operable program or batch file.

    我該在哪個目錄下面打這行指令呢?

    回覆刪除
  5. 不好意思~ 我知道囉!! 是win 7把功能鎖調而已 XD

    回覆刪除
  6. 啊,我上週回是用gmail直接回信...忘了他是noreplay的Orz...
    不過你有解掉就好了^^

    回覆刪除
  7. 天啊!! 怎麼一直碰到問題..好笨阿我 @@

    為什麼我打telnet mydomain.james 4555後會出現下面的錯誤訊息 :
    Connecting To mydomain.james...Could not open connection to the host, on port 45
    55: Connect failed

    回覆刪除
  8. 這應該是james啟動的時候不認得mydomain.james , 你可以試著用localhost or 127.0.0.1 看行不行, 或著啟動james server後先連看看telnet 127.0.0.1 25試試james server正常啟動了嗎, 新的3.0 beta 的設定還沒去研究過,照我上面的設定應該admin 連的時候會有問題

    回覆刪除
  9. 終於OK囉!!!! 結果我又灌回2.3.2版~ 那個問題就沒有了 ^^

    回覆刪除
  10. 3.0 beta的話還在開發,建議可以等release再用, 不然就要去apache project fourm看討論或問題了, 因為他每次進版會少或多了什麼設定檔也不一定...
    如果只是要用mail server的話2.3.x其實還算可用..
    只是功能太少,太舊 ..XD

    回覆刪除
  11. @@ ~~~
    Sorry , I didn't update blog for a long time. XDDD

    回覆刪除