Package org.apache.james

Source Code of org.apache.james.James

/***********************************************************************
* Copyright (c) 2000-2004 The Apache Software Foundation.             *
* All rights reserved.                                                *
* ------------------------------------------------------------------- *
* Licensed under the Apache License, Version 2.0 (the "License"); you *
* may not use this file except in compliance with the License. You    *
* may obtain a copy of the License at:                                *
*                                                                     *
*     http://www.apache.org/licenses/LICENSE-2.0                      *
*                                                                     *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS,   *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or     *
* implied.  See the License for the specific language governing       *
* permissions and limitations under the License.                      *
***********************************************************************/

package org.apache.james;

import org.apache.avalon.framework.activity.Initializable;
import org.apache.avalon.framework.component.Component;
import org.apache.avalon.framework.component.ComponentException;
import org.apache.avalon.framework.component.ComponentManager;
import org.apache.avalon.framework.component.Composable;
import org.apache.avalon.framework.component.DefaultComponentManager;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.configuration.DefaultConfiguration;
import org.apache.avalon.framework.context.Context;
import org.apache.avalon.framework.context.Contextualizable;
import org.apache.avalon.framework.context.DefaultContext;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.framework.logger.Logger;
import org.apache.james.core.MailHeaders;
import org.apache.james.core.MailImpl;
import org.apache.james.imapserver.ImapHost;
import org.apache.james.imapserver.store.ImapMailbox;
import org.apache.james.imapserver.store.MailboxException;
import org.apache.james.services.DNSServer;
import org.apache.james.services.JamesUser;
import org.apache.james.services.MailRepository;
import org.apache.james.services.MailServer;
import org.apache.james.services.MailStore;
import org.apache.james.services.SpoolRepository;
import org.apache.james.services.UsersRepository;
import org.apache.james.services.UsersStore;
import org.apache.james.userrepository.DefaultJamesUser;
import org.apache.james.util.RFC2822Headers;
import org.apache.james.util.RFC822DateFormat;
import org.apache.mailet.Mail;
import org.apache.mailet.MailAddress;
import org.apache.mailet.MailetContext;

import javax.mail.Address;
import javax.mail.MessagingException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Collection;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Vector;

/**
* Core class for JAMES. Provides three primary services:
* <br> 1) Instantiates resources, such as user repository, and protocol
* handlers
* <br> 2) Handles interactions between components
* <br> 3) Provides container services for Mailets
*
*
* @version This is $Revision: 1.1.2.3 $

*/
public class James
        extends AbstractLogEnabled
        implements Contextualizable, Composable, Configurable, JamesMBean,
        Initializable, MailServer, MailetContext, Component
{

    /**
     * The software name and version
     */
    private final static String SOFTWARE_NAME_VERSION = Constants.SOFTWARE_NAME + " " + Constants.SOFTWARE_VERSION;

    /**
     * The component manager used both internally by James and by Mailets.
     */
    private DefaultComponentManager compMgr; //Components shared

    /**
     * TODO: Investigate what this is supposed to do.  Looks like it
     *       was supposed to be the Mailet context.
     */
    private DefaultContext context;

    /**
     * The top level configuration object for this server.
     */
    private Configuration conf;

    /**
     * The logger used by the Mailet API.
     */
    private Logger mailetLogger = null;

    /**
     * The mail store containing the inbox repository and the spool.
     */
    private MailStore mailstore;

    /**
     * The store containing the local user repository.
     */
    private UsersStore usersStore;

    /**
     * The spool used for processing mail handled by this server.
     */
    private SpoolRepository spool;

    /**
     * The repository that stores the user inboxes.
     */
    private MailRepository localInbox;

    /**
     * The root URL used to get mailboxes from the repository
     */
    private String inboxRootURL;

    /**
     * The user repository for this mail server.  Contains all the users with inboxes
     * on this server.
     */
    private UsersRepository localusers;

    /**
     * The collection of domain/server names for which this instance of James
     * will receive and process mail.
     */
    private Collection serverNames;

    /**
     * Whether to ignore case when looking up user names on this server
     */
    private boolean ignoreCase;

    /**
     * Whether to enable aliasing for users on this server
     */
    private boolean enableAliases;

    /**
     * Whether to enable forwarding for users on this server
     */
    private boolean enableForwarding;

    /**
     * The number of mails generated.  Access needs to be synchronized for
     * thread safety and to ensure that all threads see the latest value.
     */
    private static long count;

    /**
     * The address of the postmaster for this server
     */
    private MailAddress postmaster;

    /**
     * A map used to store mailboxes and reduce the cost of lookup of individual
     * mailboxes.
     */
    private Map mailboxes; //Not to be shared!

    /**
     * A hash table of server attributes
     * These are the MailetContext attributes
     */
    private Hashtable attributes = new Hashtable();

    /**
     * The Avalon context used by the instance
     */
    protected Context myContext;

    /**
     * An RFC822 date formatter used to format dates in mail headers
     */
    private RFC822DateFormat rfc822DateFormat = new RFC822DateFormat();

    /**
     * Whether James should use IMAP storage
     */
    private boolean useIMAPstorage = false;

    /**
     * The host to be used for IMAP storage
     */
    private ImapHost imapHost;

    /**
     * @see Contextualizable#contextualize(Context)
     */
    public void contextualize( final Context context )
    {
        this.myContext = context;
    }

    /**
     * @see Composable#compose(ComponentManager)
     */
    public void compose( ComponentManager comp )
    {
        compMgr = new DefaultComponentManager( comp );
        mailboxes = new HashMap( 31 );
    }

    /**
     * @see Configurable#configure(Configuration)
     */
    public void configure( Configuration conf )
    {
        this.conf = conf;
    }

    /**
     * @see Initializable#initialize()
     */
    public void initialize() throws Exception
    {

        getLogger().info( "JAMES init..." );

        // TODO: This should retrieve a more specific named thread pool from
        // Context that is set up in server.xml
        try {
            mailstore = ( MailStore ) compMgr.lookup( MailStore.ROLE );
        }
        catch ( Exception e ) {
            if ( getLogger().isWarnEnabled() ) {
                getLogger().warn( "Can't get Store: " + e );
            }
        }
        if ( getLogger().isDebugEnabled() ) {
            getLogger().debug( "Using MailStore: " + mailstore.toString() );
        }
        try {
            usersStore = ( UsersStore ) compMgr.lookup( UsersStore.ROLE );
        }
        catch ( Exception e ) {
            if ( getLogger().isWarnEnabled() ) {
                getLogger().warn( "Can't get Store: " + e );
            }
        }
        if ( getLogger().isDebugEnabled() ) {
            getLogger().debug( "Using UsersStore: " + usersStore.toString() );
        }

        String hostName = null;
        try {
            hostName = InetAddress.getLocalHost().getHostName();
        }
        catch ( UnknownHostException ue ) {
            hostName = "localhost";
        }

        context = new DefaultContext();
        context.put( "HostName", hostName );
        getLogger().info( "Local host is: " + hostName );

        // Get the domains and hosts served by this instance
        serverNames = new HashSet();
        Configuration serverConf = conf.getChild( "servernames" );
        if ( serverConf.getAttributeAsBoolean( "autodetect" ) && ( !hostName.equals( "localhost" ) ) ) {
            serverNames.add( hostName.toLowerCase( Locale.US ) );
        }

        final Configuration[] serverNameConfs =
                conf.getChild( "servernames" ).getChildren( "servername" );
        for ( int i = 0; i < serverNameConfs.length; i++ ) {
            serverNames.add( serverNameConfs[i].getValue().toLowerCase( Locale.US ) );

            if ( serverConf.getAttributeAsBoolean( "autodetectIP", true ) ) {
                try {
                    /* This adds the IP address(es) for each host to support
                     * support <user@address-literal> - RFC 2821, sec 4.1.3.
                     * It might be proper to use the actual IP addresses
                     * available on this server, but we can't do that
                     * without NetworkInterface from JDK 1.4.  Because of
                     * Virtual Hosting considerations, we may need to modify
                     * this to keep hostname and IP associated, rather than
                     * just both in the set.
                     */
                    InetAddress[] addrs = InetAddress.getAllByName( serverNameConfs[i].getValue() );
                    for ( int j = 0; j < addrs.length; j++ ) {
                        serverNames.add( addrs[j].getHostAddress() );
                    }
                }
                catch ( Exception genericException ) {
                    getLogger().error( "Cannot get IP address(es) for " + serverNameConfs[i].getValue() );
                }
            }
        }
        if ( serverNames.isEmpty() ) {
            throw new ConfigurationException( "Fatal configuration error: no servernames specified!" );
        }

        if ( getLogger().isInfoEnabled() ) {
            for ( Iterator i = serverNames.iterator(); i.hasNext(); ) {
                getLogger().info( "Handling mail for: " + i.next() );
            }
        }
        context.put( Constants.SERVER_NAMES, this.serverNames );
        attributes.put( Constants.SERVER_NAMES, this.serverNames );


        // Get postmaster
        String postMasterAddress = conf.getChild( "postmaster" ).getValue( "postmaster" );
        // if there is no @domain part, then add the first one from the
        // list of supported domains that isn't localhost.  If that
        // doesn't work, use the hostname, even if it is localhost.
        if ( postMasterAddress.indexOf( '@' ) < 0 ) {
            String domainName = null;    // the domain to use
            // loop through candidate domains until we find one or exhaust the list
            for ( int i = 0; domainName == null && i < serverNameConfs.length; i++ ) {
                String serverName = serverNameConfs[i].getValue().toLowerCase( Locale.US );
                if ( !( "localhost".equals( serverName ) ) ) {
                    domainName = serverName;    // ok, not localhost, so use it
                }
            }
            // if we found a suitable domain, use it.  Otherwise fallback to the host name.
            postMasterAddress = postMasterAddress + "@" + ( domainName != null ? domainName : hostName );
        }
        this.postmaster = new MailAddress( postMasterAddress );
        context.put( Constants.POSTMASTER, postmaster );

        if ( !isLocalServer( postmaster.getHost() ) ) {
            StringBuffer warnBuffer
                    = new StringBuffer( 320 )
                    .append( "The specified postmaster address ( " )
                    .append( postmaster )
                    .append( " ) is not a local address.  This is not necessarily a problem, but it does mean that emails addressed to the postmaster will be routed to another server.  For some configurations this may cause problems." );
            getLogger().warn( warnBuffer.toString() );
        }

        Configuration userNamesConf = conf.getChild( "usernames" );
        ignoreCase = userNamesConf.getAttributeAsBoolean( "ignoreCase", false );
        enableAliases = userNamesConf.getAttributeAsBoolean( "enableAliases", false );
        enableForwarding = userNamesConf.getAttributeAsBoolean( "enableForwarding", false );

        //Get localusers
        try {
            localusers = ( UsersRepository ) usersStore.getRepository( "LocalUsers" );
        }
        catch ( Exception e ) {
            getLogger().error( "Cannot open private UserRepository" );
            throw e;
        }
        //}
        compMgr.put( UsersRepository.ROLE, ( Component ) localusers );
        getLogger().info( "Local users repository opened" );

        try {
            // Get storage config param
            if ( conf.getChild( "storage" ).getValue().equals( "IMAP" ) ) {
                useIMAPstorage = true;
                getLogger().info( "Using IMAP Store-System" );
            }
        }
        catch ( Exception e ) {
            // No storage entry found in config file
        }

        // Get the LocalInbox repository
        if ( useIMAPstorage ) {
            try {
                // We will need to use a no-args constructor for flexibility
                imapHost = ( ImapHost ) compMgr.lookup( ImapHost.ROLE );
            }
            catch ( Exception e ) {
                getLogger().error( "Exception in IMAP Storage init: " + e.getMessage() );
                throw e;
            }
        }
        else {
            Configuration inboxConf = conf.getChild( "inboxRepository" );
            Configuration inboxRepConf = inboxConf.getChild( "repository" );
            try {
                localInbox = ( MailRepository ) mailstore.select( inboxRepConf );
            }
            catch ( Exception e ) {
                getLogger().error( "Cannot open private MailRepository" );
                throw e;
            }
            inboxRootURL = inboxRepConf.getAttribute( "destinationURL" );
        }
        getLogger().info( "Private Repository LocalInbox opened" );

        // Add this to comp
        compMgr.put( MailServer.ROLE, this );

        spool = mailstore.getInboundSpool();
        if ( getLogger().isDebugEnabled() ) {
            getLogger().debug( "Got spool" );
        }

        // For mailet engine provide MailetContext
        //compMgr.put("org.apache.mailet.MailetContext", this);
        // For AVALON aware mailets and matchers, we put the Component object as
        // an attribute
        attributes.put( Constants.AVALON_COMPONENT_MANAGER, compMgr );

        System.out.println( SOFTWARE_NAME_VERSION );
        getLogger().info( "JAMES ...init end" );
    }

    /**
     * Place a mail on the spool for processing
     *
     * @param message the message to send
     *
     * @throws MessagingException if an exception is caught while placing the mail
     *                            on the spool
     */
    public void sendMail( MimeMessage message ) throws MessagingException
    {
        MailAddress sender = new MailAddress( ( InternetAddress ) message.getFrom()[0] );
        Collection recipients = new HashSet();
        Address addresses[] = message.getAllRecipients();
        for ( int i = 0; i < addresses.length; i++ ) {
            recipients.add( new MailAddress( ( InternetAddress ) addresses[i] ) );
        }
        sendMail( sender, recipients, message );
    }

    /**
     * Place a mail on the spool for processing
     *
     * @param sender the sender of the mail
     * @param recipients the recipients of the mail
     * @param message the message to send
     *
     * @throws MessagingException if an exception is caught while placing the mail
     *                            on the spool
     */
    public void sendMail( MailAddress sender, Collection recipients, MimeMessage message )
            throws MessagingException
    {
        sendMail( sender, recipients, message, Mail.DEFAULT );
    }

    /**
     * Place a mail on the spool for processing
     *
     * @param sender the sender of the mail
     * @param recipients the recipients of the mail
     * @param message the message to send
     * @param state the state of the message
     *
     * @throws MessagingException if an exception is caught while placing the mail
     *                            on the spool
     */
    public void sendMail( MailAddress sender, Collection recipients, MimeMessage message, String state )
            throws MessagingException
    {
        MailImpl mail = new MailImpl( getId(), sender, recipients, message );
        mail.setState( state );
        sendMail( mail );
    }

    /**
     * Place a mail on the spool for processing
     *
     * @param sender the sender of the mail
     * @param recipients the recipients of the mail
     * @param msg an <code>InputStream</code> containing the message
     *
     * @throws MessagingException if an exception is caught while placing the mail
     *                            on the spool
     */
    public void sendMail( MailAddress sender, Collection recipients, InputStream msg )
            throws MessagingException
    {
        // parse headers
        MailHeaders headers = new MailHeaders( msg );

        // if headers do not contains minimum REQUIRED headers fields throw Exception
        if ( !headers.isValid() ) {
            throw new MessagingException( "Some REQURED header field is missing. Invalid Message" );
        }
        ByteArrayInputStream headersIn = new ByteArrayInputStream( headers.toByteArray() );
        sendMail( new MailImpl( getId(), sender, recipients, new SequenceInputStream( headersIn, msg ) ) );
    }

    /**
     * Place a mail on the spool for processing
     *
     * @param mail the mail to place on the spool
     *
     * @throws MessagingException if an exception is caught while placing the mail
     *                            on the spool
     */
    public void sendMail( Mail mail ) throws MessagingException
    {
        MailImpl mailimpl = ( MailImpl ) mail;
        try {
            spool.store( mailimpl );
        }
        catch ( Exception e ) {
            try {
                spool.remove( mailimpl );
            }
            catch ( Exception ignored ) {
            }
            throw new MessagingException( "Exception spooling message: " + e.getMessage(), e );
        }
        if ( getLogger().isDebugEnabled() ) {
            StringBuffer logBuffer =
                    new StringBuffer( 64 )
                    .append( "Mail " )
                    .append( mailimpl.getName() )
                    .append( " pushed in spool" );
            getLogger().debug( logBuffer.toString() );
        }
    }

    /**
     * <p>Retrieve the mail repository for a user</p>
     *
     * <p>For POP3 server only - at the moment.</p>
     *
     * @param userName the name of the user whose inbox is to be retrieved
     *
     * @return the POP3 inbox for the user
     */
    public synchronized MailRepository getUserInbox( String userName )
    {
        MailRepository userInbox = ( MailRepository ) null;

        userInbox = ( MailRepository ) mailboxes.get( userName );

        if ( userInbox != null ) {
            return userInbox;
        }
        else if ( mailboxes.containsKey( userName ) ) {
            // we have a problem
            getLogger().error( "Null mailbox for non-null key" );
            throw new RuntimeException( "Error in getUserInbox." );
        }
        else {
            // need mailbox object
            if ( getLogger().isDebugEnabled() ) {
                getLogger().debug( "Retrieving and caching inbox for " + userName );
            }
            StringBuffer destinationBuffer =
                    new StringBuffer( 192 )
                    .append( inboxRootURL )
                    .append( userName )
                    .append( "/" );
            String destination = destinationBuffer.toString();
            DefaultConfiguration mboxConf
                    = new DefaultConfiguration( "repository", "generated:AvalonFileRepository.compose()" );
            mboxConf.setAttribute( "destinationURL", destination );
            mboxConf.setAttribute( "type", "MAIL" );
            try {
                userInbox = ( MailRepository ) mailstore.select( mboxConf );
                mailboxes.put( userName, userInbox );
            }
            catch ( Exception e ) {
                e.printStackTrace();
                if ( getLogger().isErrorEnabled() ) {
                    getLogger().error( "Cannot open user Mailbox" + e );
                }
                throw new RuntimeException( "Error in getUserInbox." + e );
            }
            return userInbox;
        }
    }

    /**
     * Return a new mail id.
     *
     * @return a new mail id
     */
    public String getId()
    {
        long localCount = -1;
        synchronized ( James.class ) {
            localCount = count++;
        }
        StringBuffer idBuffer =
                new StringBuffer( 64 )
                .append( "Mail" )
                .append( System.currentTimeMillis() )
                .append( "-" )
                .append( count++ );
        return idBuffer.toString();
    }

    /**
     * The main method.  Should never be invoked, as James must be called
     * from within an Avalon framework container.
     *
     * @param args the command line arguments
     */
    public static void main( String[] args )
    {
        System.out.println( "ERROR!" );
        System.out.println( "Cannot execute James as a stand alone application." );
        System.out.println( "To run James, you need to have the Avalon framework installed." );
        System.out.println( "Please refer to the Readme file to know how to run James." );
    }

    //Methods for MailetContext

    /**
     * <p>Get the prioritized list of mail servers for a given host.</p>
     *
     * <p>TODO: This needs to be made a more specific ordered subtype of Collection.</p>
     *
     * @param host
     */
    public Collection getMailServers( String host )
    {
        DNSServer dnsServer = null;
        try {
            dnsServer = ( DNSServer ) compMgr.lookup( DNSServer.ROLE );
        }
        catch ( final ComponentException cme ) {
            getLogger().error( "Fatal configuration error - DNS Servers lost!", cme );
            throw new RuntimeException( "Fatal configuration error - DNS Servers lost!" );
        }
        return dnsServer.findMXRecords( host );
    }

    public Object getAttribute( String key )
    {
        return attributes.get( key );
    }

    public void setAttribute( String key, Object object )
    {
        attributes.put( key, object );
    }

    public void removeAttribute( String key )
    {
        attributes.remove( key );
    }

    public Iterator getAttributeNames()
    {
        Vector names = new Vector();
        for ( Enumeration e = attributes.keys(); e.hasMoreElements(); ) {
            names.add( e.nextElement() );
        }
        return names.iterator();
    }

    /**
     * This generates a response to the Return-Path address, or the address of
     * the message's sender if the Return-Path is not available.  Note that
     * this is different than a mail-client's reply, which would use the
     * Reply-To or From header. This will send the bounce with the server's
     * postmaster as the sender.
     */
    public void bounce( Mail mail, String message ) throws MessagingException
    {
        bounce( mail, message, getPostmaster() );
    }

    /**
     * This generates a response to the Return-Path address, or the address of
     * the message's sender if the Return-Path is not available.  Note that
     * this is different than a mail-client's reply, which would use the
     * Reply-To or From header.
     */
    public void bounce( Mail mail, String message, MailAddress bouncer ) throws MessagingException
    {
        MimeMessage orig = mail.getMessage();
        //Create the reply message
        MimeMessage reply = ( MimeMessage ) orig.reply( false );
        //If there is a Return-Path header,
        if ( orig.getHeader( RFC2822Headers.RETURN_PATH ) != null ) {
            //Return the message to that address, not to the Reply-To address
            reply.setRecipient( MimeMessage.RecipientType.TO, new InternetAddress( orig.getHeader( RFC2822Headers.RETURN_PATH )[0] ) );
        }
        //Create the list of recipients in our MailAddress format
        Collection recipients = new HashSet();
        Address addresses[] = reply.getAllRecipients();
        for ( int i = 0; i < addresses.length; i++ ) {
            recipients.add( new MailAddress( ( InternetAddress ) addresses[i] ) );
        }
        //Change the sender...
        reply.setFrom( bouncer.toInternetAddress() );
        try {
            //Create the message body
            MimeMultipart multipart = new MimeMultipart();
            //Add message as the first mime body part
            MimeBodyPart part = new MimeBodyPart();
            part.setContent( message, "text/plain" );
            part.setHeader( RFC2822Headers.CONTENT_TYPE, "text/plain" );
            multipart.addBodyPart( part );

            //Add the original message as the second mime body part
            part = new MimeBodyPart();
            part.setContent( orig.getContent(), orig.getContentType() );
            part.setHeader( RFC2822Headers.CONTENT_TYPE, orig.getContentType() );
            multipart.addBodyPart( part );
            reply.setHeader( RFC2822Headers.DATE, rfc822DateFormat.format( new Date() ) );
            reply.setContent( multipart );
            reply.setHeader( RFC2822Headers.CONTENT_TYPE, multipart.getContentType() );
        }
        catch ( IOException ioe ) {
            throw new MessagingException( "Unable to create multipart body", ioe );
        }
        //Send it off...
        sendMail( bouncer, recipients, reply );
    }

    /**
     * Returns whether that account has a local inbox on this server
     *
     * @param name the name to be checked
     *
     * @return whether the account has a local inbox
     */
    public boolean isLocalUser( String name )
    {
        if ( ignoreCase ) {
            return localusers.containsCaseInsensitive( name );
        }
        else {
            return localusers.contains( name );
        }
    }

    /**
     * Returns the address of the postmaster for this server.
     *
     * @return the <code>MailAddress</code> for the postmaster
     */
    public MailAddress getPostmaster()
    {
        return postmaster;
    }

    public void storeMail( MailAddress sender, MailAddress recipient, MimeMessage message )
            throws MessagingException
    {
        String username;
        if ( recipient == null ) {
            throw new IllegalArgumentException( "Recipient for mail to be spooled cannot be null." );
        }
        if ( message == null ) {
            throw new IllegalArgumentException( "Mail message to be spooled cannot be null." );
        }
        if ( ignoreCase ) {
            String originalUsername = recipient.getUser();
            username = localusers.getRealName( originalUsername );
            if ( username == null ) {
                StringBuffer errorBuffer =
                        new StringBuffer( 128 )
                        .append( "The inbox for user " )
                        .append( originalUsername )
                        .append( " was not found on this server." );
                throw new MessagingException( errorBuffer.toString() );
            }
        }
        else {
            username = recipient.getUser();
        }
        JamesUser user;
        if ( enableAliases || enableForwarding ) {
            user = ( JamesUser ) localusers.getUserByName( username );
            if ( enableAliases && user.getAliasing() ) {
                username = user.getAlias();
            }
            // Forwarding takes precedence over local aliases
            if ( enableForwarding && user.getForwarding() ) {
                MailAddress forwardTo = user.getForwardingDestination();
                if ( forwardTo == null ) {
                    StringBuffer errorBuffer =
                            new StringBuffer( 128 )
                            .append( "Forwarding was enabled for " )
                            .append( username )
                            .append( " but no forwarding address was set for this account." );
                    throw new MessagingException( errorBuffer.toString() );
                }
                Collection recipients = new HashSet();
                recipients.add( forwardTo );
                try {
                    sendMail( sender, recipients, message );
                    if ( getLogger().isInfoEnabled() ) {
                        StringBuffer logBuffer =
                                new StringBuffer( 128 )
                                .append( "Mail for " )
                                .append( username )
                                .append( " forwarded to " )
                                .append( forwardTo.toString() );
                        getLogger().info( logBuffer.toString() );
                    }
                    return;
                }
                catch ( MessagingException me ) {
                    if ( getLogger().isErrorEnabled() ) {
                        StringBuffer logBuffer =
                                new StringBuffer( 128 )
                                .append( "Error forwarding mail to " )
                                .append( forwardTo.toString() )
                                .append( "attempting local delivery" );
                        getLogger().error( logBuffer.toString() );
                    }
                    throw me;
                }
            }
        }

        if ( useIMAPstorage ) {
            ImapMailbox mbox = null;
            try {
                user = ( JamesUser ) localusers.getUserByName( username );
                mbox = imapHost.getInbox( user );
                MailImpl mail = new MailImpl( message );
                mbox.store( mail );
                getLogger().info( "Message " + message.getMessageID() +
                                  " stored in " +
                                  mbox.getFullName() );
                mbox = null;
            }
            catch ( Exception e ) {
                getLogger().error( "Exception storing mail: " + e );
                e.printStackTrace();
                if ( mbox != null ) {
                    mbox = null;
                }
                throw new RuntimeException( "Exception storing mail: " + e );
            }
        }
        else {
            Collection recipients = new HashSet();
            recipients.add( recipient );
            MailImpl mailImpl = new MailImpl( getId(), sender, recipients, message );
            MailRepository userInbox = getUserInbox( username );
            if ( userInbox == null ) {
                StringBuffer errorBuffer =
                        new StringBuffer( 128 )
                        .append( "The inbox for user " )
                        .append( username )
                        .append( " was not found on this server." );
                throw new MessagingException( errorBuffer.toString() );
            }
            userInbox.store( mailImpl );
        }
    }

    /**
     * Return the major version number for the server
     *
     * @return the major vesion number for the server
     */
    public int getMajorVersion()
    {
        return 2;
    }

    /**
     * Return the minor version number for the server
     *
     * @return the minor vesion number for the server
     */
    public int getMinorVersion()
    {
        return 1;
    }

    /**
     * Check whether the mail domain in question is to be
     * handled by this server.
     *
     * @param serverName the name of the server to check
     * @return whether the server is local
     */
    public boolean isLocalServer( final String serverName )
    {
        return serverNames.contains( serverName.toLowerCase( Locale.US ) );
    }

    /**
     * Return the type of the server
     *
     * @return the type of the server
     */
    public String getServerInfo()
    {
        return "Apache Jakarta JAMES";
    }

    /**
     * Return the logger for the Mailet API
     *
     * @return the logger for the Mailet API
     */
    private Logger getMailetLogger()
    {
        if ( mailetLogger == null ) {
            mailetLogger = getLogger().getChildLogger( "Mailet" );
        }
        return mailetLogger;
    }

    /**
     * Log a message to the Mailet logger
     *
     * @param message the message to pass to the Mailet logger
     */
    public void log( String message )
    {
        getMailetLogger().info( message );
    }

    /**
     * Log a message and a Throwable to the Mailet logger
     *
     * @param message the message to pass to the Mailet logger
     * @param t the <code>Throwable</code> to be logged
     */
    public void log( String message, Throwable t )
    {
        getMailetLogger().info( message, t );
    }

    /**
     * Adds a user to this mail server. Currently just adds user to a
     * UsersRepository.
     * <p> As we move to IMAP support this will also create mailboxes and
     * access control lists.
     *
     * @param userName String representing user name, that is the portion of
     * an email address before the '@<domain>'.
     * @param password String plaintext password
     * @return boolean true if user added succesfully, else false.
     */
    public boolean addUser( String userName, String password )
    {
        boolean success;
        DefaultJamesUser user = new DefaultJamesUser( userName, "SHA" );
        user.setPassword( password );
        user.initialize();
        success = localusers.addUser( user );
        if ( useIMAPstorage && success ) {
            try {
                imapHost.createPrivateMailAccount( user );
                getLogger().info( "New MailAccount created for" + userName );
            }
            catch ( MailboxException e ) {
                return false;
            }
        }
        return success;
    }
}
TOP

Related Classes of org.apache.james.James

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.