/*
* Copyright (C) 1996 - 2008 Alan Williamson
*
* This file is part of Mail25 Mailet Container.
*
* Mail25 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* Free Software Foundation,version 3.
*
* Mail25 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Mail25. If not, see http://www.gnu.org/licenses/
*
* http://alan.blog-city.com/
*
* $Id: Mail25.java 1720 2011-10-07 19:39:14Z alan $
*/
/**
* Mail25 - Apache Mailet Container
*
* Properties ____
*
* mail25.bindaddress=[ip you want to bind this process to]
* mail25.maxconnections=[max number of connections]
*
* mail25.deliveryqueue=[directory for mails that are to be delivered]
* mail25.spooldir=[directory for large incoming emails]
*
* mail25.port=[port to listen on, defaults to 25]
* mail25.logfile=[path to the log file, defaults to mailcatcher.log]
*
* If you are finding the resolving of MX records empty you can pass in the DNS server as a
* JVM parameter: -Ddns.server=dns1,dns2
*
*/
package org.alanwilliamson.mail25.server;
import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Properties;
import java.util.StringTokenizer;
import javax.mail.internet.InternetAddress;
import org.alanwilliamson.mail25.chain.Chain;
import org.alanwilliamson.mail25.mailet.CaughtMail;
import org.alanwilliamson.mail25.mailet.Mail25Context;
import org.alanwilliamson.mail25.mailet.PreMailFilterInterface;
import org.apache.mailet.Mail;
import org.apache.mailet.MailetContext;
import org.quickserver.net.server.QuickServer;
public class Mail25 extends Object {
private QuickServer mailServer;
private File spoolDirectoryIn;
private Mail25Context mailContext;
private HashMap chainManagers;
private PreMailFilterInterface preFilterMail;
private boolean bDebugMode = false;
public Mail25( String name, Properties config, String banner ) throws Exception {
// Set up the QuickServer details for this Mail
int portNo = Integer.parseInt( config.getProperty("mail25.port", "25") );
mailServer = new QuickServer( "org.alanwilliamson.mail25.server.MailHandler", portNo );
mailServer.setClientData( "org.alanwilliamson.mail25.server.MailData" );
mailServer.setClientEventHandler( "org.alanwilliamson.mail25.server.MailHandler" );
mailServer.setStoreObjects( new Object[]{ this, banner } );
if ( config.getProperty("mail25.bindaddress") != null )
mailServer.setBindAddr( config.getProperty("mail25.bindaddress") );
mailServer.setMaxConnection( Integer.parseInt( config.getProperty("mail25.maxconnections", "5") ) );
mailServer.setMaxConnectionMsg( "Mail25 experiencing high load, please try again later" );
mailServer.setTimeout( 120 * 1000 ); //- 2 minutes socket timeout
//-- Setup the spool directory for large emails
spoolDirectoryIn = new File( config.getProperty("mail25.spooldir", "./spool_in_for_" + name ) );
if ( !spoolDirectoryIn.isDirectory() ){
spoolDirectoryIn.mkdirs();
}
//-- Let us attempt to create the Matcher class
try{
if ( config.containsKey("prefiltermail.class") ){
Class c = Class.forName( config.getProperty( "mail25.prefiltermail.class" ) );
preFilterMail = (PreMailFilterInterface)c.newInstance();
}
}catch(Exception e){
throw new Exception( "PreMailFilterInterface: [mail25.prefiltermail.class=" + config.getProperty( "mail25.prefiltermail.class" ) + "] " + e.getMessage() );
}
//- Create the Mail Context, one per server
mailContext = new Mail25Context( config );
//- Create the Chain Managers
chainManagers = new HashMap();
initChainManagers( config );
if ( chainManagers.size() == 0 || !chainManagers.containsKey( Mail.DEFAULT.toUpperCase() ) )
throw new Exception("No [" + Mail.DEFAULT + "] chain defined");
log( "Mail25: Listening on port#" + portNo + "; IP=" + (config.getProperty("mail25.bindaddress")==null? "*":config.getProperty("mail25.bindaddress")) );
log( "Mail25: Incoming Spool Dir=" + spoolDirectoryIn.getAbsolutePath() );
mailServer.setServerBanner( "Mail25 Mailet Container @alanwilliamson.org v1.2" );
mailServer.setAppLogger( null );
try{
mailServer.startServer();
}catch(Exception e){
System.out.println( "Failed to create server: " + e.getMessage() );
shutdown();
}
}
private void initChainManagers(Properties config) throws Exception {
String chainList = config.getProperty( "spoolchain.name" );
if ( chainList == null || chainList.length() == 0 )
return;
StringTokenizer st = new StringTokenizer( chainList, "," );
while ( st.hasMoreTokens() ){
String chainName = st.nextToken();
chainManagers.put( chainName.toUpperCase(), new Chain( chainName, config, this ) );
}
}
/**
* Allows you to set the pre-filter class
*/
public void setPreFilterClass( PreMailFilterInterface preFilterMail ) {
this.preFilterMail = preFilterMail;
}
/**
* Shut downs the server.
*/
public void shutdown() {
try {
mailServer.closeAllPools();
} catch (Exception ignore) {}
try{
mailServer.stopServer();
} catch (Exception ignore) {}
//- close down the chain runners
Iterator it = chainManagers.values().iterator();
while ( it.hasNext() ){
Chain c = (Chain)it.next();
c.shutdown();
}
mailContext.shutdown();
}
/**
* Creates a unique file in the spool directory.
*
* @return java.io.File
*/
public synchronized File getSpoolFile(){
//- Get a unused file name
File fileSpool = new File( spoolDirectoryIn, System.currentTimeMillis() + ".email" );
while ( fileSpool.isFile() ){
fileSpool = new File( spoolDirectoryIn, (System.currentTimeMillis()-4311231231L) + ".email" );
}
return fileSpool;
}
public void log( String item ){
mailContext.log( item );
}
public void log( String item, Throwable t ){
mailContext.log( item, t );
}
//------------------------------
//- Methods for collecting stats
//------------------------------
private int stats_connectedclients = 0;
private int stats_mailetsran = 0;
private int stats_currentclients = 0;
public void statsClientConnected(){
stats_connectedclients++;
stats_currentclients++;
}
/**
* Returns the total number of clients that have connected to MailCatcher since startup
*
* @return int
*/
public int getClientsConnected(){
return stats_connectedclients;
}
/**
* Returns the number of times the mailet service() was triggered
*
* @return int
*/
public int getMailetsRan(){
return stats_mailetsran;
}
public void statsLostConnection(){
stats_currentclients--;
}
/**
* Returns the number of clients that are presently connected to MailCatcher
*
* @return int
*/
public int getConnectedClients(){
return stats_currentclients;
}
public MailetContext getMailContext() {
return mailContext;
}
public void acceptMail(CaughtMail mail) {
acceptMail( Mail.DEFAULT, mail );
}
public void acceptMail(String chainname, CaughtMail mail) {
stats_mailetsran++;
// Get the chain
Chain c = (Chain)chainManagers.get( chainname.toUpperCase() );
if ( c == null ){
log( toString() + "[InvalidChain]=" + chainname );
}else{
c.acceptMail( mail );
}
}
public boolean inDebugMode() {
return bDebugMode;
}
public boolean acceptMailFrom( cfMailSession cfmailsession, InternetAddress from, String ipAddress ){
if ( preFilterMail == null )
return true;
else
return preFilterMail.acceptMailFrom(cfmailsession, from, ipAddress);
}
public boolean acceptMailTo( cfMailSession cfmailsession, InternetAddress to, String ipAddress ){
if ( preFilterMail == null )
return true;
else
return preFilterMail.acceptMailTo(cfmailsession, to, ipAddress);
}
}