FileDocCategorySizeDatePackage
MessageProcessor.javaAPI DocApache James 2.3.159677Wed Apr 25 16:00:22 BST 2007org.apache.james.fetchmail

MessageProcessor

public class MessageProcessor extends ProcessorAbstract

Class MessageProcessor handles the delivery of MimeMessages to the James input spool.

Messages written to the input spool always have the following Mail Attributes set:

org.apache.james.fetchmail.taskName (java.lang.String)
The name of the fetch task that processed the message
org.apache.james.fetchmail.folderName (java.lang.String)
The name of the folder from which the message was fetched

Messages written to the input spool have the following Mail Attributes set if the corresponding condition is satisfied:

org.apache.james.fetchmail.isBlacklistedRecipient
The recipient is in the configured blacklist
org.apache.james.fetchmail.isMaxMessageSizeExceeded (java.lang.String)
The message size exceeds the configured limit. An empty message is written to the input spool. The Mail Attribute value is a String representing the size of the original message in bytes.
org.apache.james.fetchmail.isRecipientNotFound
The recipient could not be found. Delivery is to the configured recipient. See the discussion of delivery to a sole intended recipient below.
org.apache.james.fetchmail.isRemoteRecievedHeaderInvalid
The Receieved header at the index specified by parameter remoteReceivedHeaderIndex is invalid.
org.apache.james.fetchmail.isRemoteRecipient
The recipient is on a remote host
org.apache.james.fetchmail.isUserUndefined
The recipient is on a localhost but not defined to James
org.apache.james.fetchmail.isDefaultSenderLocalPart
The local part of the sender address could not be obtained. The default value has been used.
org.apache.james.fetchmail.isDefaultSenderDomainPart
The domain part of the sender address could not be obtained. The default value has been used.
org.apache.james.fetchmail.isDefaultRemoteAddress
The remote address could not be determined. The default value (localhost/127.0.0.1)has been used.

Configuration settings - see org.apache.james.fetchmail.ParsedConfiguration - control the messages that are written to the James input spool, those that are rejected and what happens to messages that are rejected.

Rejection processing is based on the following filters:

RejectRemoteRecipient
Rejects recipients on remote hosts
RejectBlacklistedRecipient
Rejects recipients configured in a blacklist
RejectUserUndefined
Rejects recipients on local hosts who are not defined as James users
RejectRecipientNotFound
See the discussion of delivery to a sole intended recipient below
RejectMaxMessageSizeExceeded
Rejects messages whose size exceeds the configured limit
RejectRemoteReceievedHeaderInvalid
Rejects messages whose Received header is invalid.

Rejection processing is intentionally limited to managing the status of the messages that are rejected on the server from which they were fetched. View it as a simple automation of the manual processing an end-user would perform through a mail client. Messages may be marked as seen or be deleted.

Further processing can be achieved by configuring to disable rejection for one or more filters. This enables Messages that would have been rejected to be written to the James input spool. The conditional Mail Attributes described above identify the filter states. The Matcher/Mailet chain can then be used to perform any further processing required, such as notifying the Postmaster and/or sender, marking the message for error processing, etc.

Note that in the case of a message exceeding the message size limit, the message that is written to the input spool has no content. This enables configuration of a mailet notifying the sender that their mail has not been delivered due to its size while maintaining the purpose of the filter which is to avoid injecting excessively large messages into the input spool.

Delivery is to a sole intended recipient. The recipient is determined in the following manner:

  1. If isIgnoreIntendedRecipient(), use the configured recipient
  2. If the Envelope contains a for: stanza, use the recipient in the stanza
  3. If the Message has a sole intended recipient, use this recipient
  4. If not rejectRecipientNotFound(), use the configured recipient

If a recipient cannot be determined after these steps, the message is rejected.

Every delivered message CURRENTLY has an "X-fetched-from" header added containing the name of the fetch task. Its primary uses are to detect bouncing mail and provide backwards compatibility with the fetchPop task that inserted this header to enable injected messages to be detected in the Matcher/Mailet chain. This header is DEPRECATED and WILL BE REMOVED in a future version of fetchmail. Use the Mail Attribute org.apache.james.fetchmail.taskName instead.

MessageProcessor is as agnostic as it can be about the format and contents of the messages it delivers. There are no RFCs that govern its behavior. The most releveant RFCs relate to the exchange of messages between MTA servers, but not POP3 or IMAP servers which are normally end-point servers and not expected to re-inject mail into MTAs. None the less, the intent is to conform to the 'spirit' of the RFCs. MessageProcessor relies on the MTA (James in this implementation) to manage and validate the injected mail just as it would when receiving mail from an upstream MTA.

The only correction applied by MessageProcessor is to correct a missing or partial sender address. If the sender address can not be obtained, the default local part and default domain part is added. If the sender domain part is absent, the default domain part is added.

Mail with corrections applied to the sender address will most likely pass Matcher tests on the sender that they might otherwise fail. The Mail Attributes org.apache.james.fetchmail.isDefaultSenderLocalPart and org.apache.james.fetchmail.isDefaultSenderDomainPart are added to the injected mail to enable such mail to be detected and processed accordingly.

The status of messages on the server from which they were fetched that cannot be injected into the input spool due to non-correctable errors is determined by the undeliverable configuration options.

Fields Summary
private MimeMessage
fieldMessageIn
private boolean
fieldRecipientNotFound
Recipient cannot be found
private boolean
fieldRemoteRecipient
Recipient is a local user on a local host
private Boolean
fieldRemoteReceivedHeaderInvalid
The mail's Received header at index remoteReceivedHeaderIndex is invalid.
private boolean
fieldUserUndefined
Recipient is not a local user
private Boolean
fieldMaxMessageSizeExceeded
The Maximum Message has been exceeded
private static final String
fieldRFC2822RECEIVEDHeaderFields
Field names for an RFC2822 compliant RECEIVED Header
private boolean
fieldBlacklistedRecipient
Recipient is blacklisted
private String
fieldRemoteDomain
The RFC2822 compliant "Received : from" domain
private String
fieldRemoteAddress
The remote address derived from the remote domain
private String
fieldRemoteHostName
The remote host name derived from the remote domain
private boolean
fieldDefaultSenderLocalPart
The default sender local part has been used.
private boolean
fieldDefaultSenderDomainPart
The default sender domain part has been used.
private boolean
fieldDefaultRemoteAddress
The default remote address has been used.
Constructors Summary
private MessageProcessor(Account account)
Constructor for MessageProcessor.

param
account

        
    
               
      
    
        super(account);
    
MessageProcessor(MimeMessage messageIn, Account account)
Constructor for MessageProcessor.

param
messageIn
param
account

        this(account);
        setMessageIn(messageIn);
    
Methods Summary
protected voidaddErrorMessages(org.apache.mailet.Mail mail)
Adds any required error messages to a Mail.

param
aMail a Mail instance

        if (isMaxMessageSizeExceeded().booleanValue())
        {
            StringBuffer msgBuffer =
                new StringBuffer("550 - Rejected - This message has been rejected as the message size of ");
            msgBuffer.append(getMessageIn().getSize() * 1000 / 1024 / 1000f);
            msgBuffer.append("KB exceeds the maximum permitted size of ");
            msgBuffer.append(getMaxMessageSizeLimit() / 1024);
            msgBuffer.append("KB.");
            mail.setErrorMessage(msgBuffer.toString());
        }
    
protected voidaddMailAttributes(org.apache.mailet.Mail aMail)
Adds the mail attributes to a Mail.

param
aMail a Mail instance

        aMail.setAttribute(
            getAttributePrefix() + "taskName",
            getFetchTaskName());

        aMail.setAttribute(
            getAttributePrefix() + "folderName",
            getMessageIn().getFolder().getFullName());

        if (isRemoteRecipient())
            aMail.setAttribute(
                getAttributePrefix() + "isRemoteRecipient",
                null);

        if (isUserUndefined())
            aMail.setAttribute(getAttributePrefix() + "isUserUndefined", null);

        if (isBlacklistedRecipient())
            aMail.setAttribute(
                getAttributePrefix() + "isBlacklistedRecipient",
                null);

        if (isRecipientNotFound())
            aMail.setAttribute(
                getAttributePrefix() + "isRecipientNotFound",
                null);

        if (isMaxMessageSizeExceeded().booleanValue())
            aMail.setAttribute(
                getAttributePrefix() + "isMaxMessageSizeExceeded",
                new Integer(getMessageIn().getSize()).toString());
                
        if (isRemoteReceivedHeaderInvalid().booleanValue())
            aMail.setAttribute(
                getAttributePrefix() + "isRemoteReceivedHeaderInvalid",
                null); 
                
        if (isDefaultSenderLocalPart())
            aMail.setAttribute(
                getAttributePrefix() + "isDefaultSenderLocalPart",
                null);
                
        if (isDefaultSenderDomainPart())
            aMail.setAttribute(
                getAttributePrefix() + "isDefaultSenderDomainPart",
                null);
                
        if (isDefaultRemoteAddress())
            aMail.setAttribute(
                getAttributePrefix() + "isDefaultRemoteAddress",
                null);                                                                
    
protected java.lang.BooleancomputeMaxMessageSizeExceeded()
Compute the maxMessageSizeExceeded.

return
Boolean

        if (0 == getMaxMessageSizeLimit())
            return Boolean.FALSE;
        return new Boolean(getMessageIn().getSize() > getMaxMessageSizeLimit());
    
protected java.lang.StringcomputeRemoteAddress()
Answer the IP Address of the remote server for the message being processed.

return
String
throws
MessagingException
throws
UnknownHostException

        String domain = getRemoteDomain();
        String address = null;
        String validatedAddress = null;
        int ipAddressStart = domain.indexOf('[");
        int ipAddressEnd = -1;
        if (ipAddressStart > -1)
            ipAddressEnd = domain.indexOf(']", ipAddressStart);
        if (ipAddressEnd > -1)
            address = domain.substring(ipAddressStart + 1, ipAddressEnd);
        else
        {
            int hostNameEnd = domain.indexOf(' ");
            if (hostNameEnd == -1)
                hostNameEnd = domain.length();
            address = domain.substring(0, hostNameEnd);
        }
        validatedAddress = org.apache.james.dnsserver.DNSServer.getByName(address).getHostAddress();

        return validatedAddress;
    
protected java.lang.StringcomputeRemoteDomain()

Method computeRemoteDomain answers a String that is the RFC2822 compliant "Received : from" domain extracted from the message being processed for the remote domain that sent the message.

Often the remote domain is the domain that sent the message to the host of the message store, the second "received" header, which has an index of 1. Other times, messages may be received by a edge mail server and relayed internally through one or more internal mail servers prior to arriving at the message store host. In these cases the index is 1 + the number of internal servers through which a mail passes.

The index of the header to use is specified by the configuration parameter RemoteReceivedHeaderIndex. This is set to point to the received header prior to the remote mail server, the one prior to the edge mail server.

"received" headers are searched starting at the specified index. If a domain in the "received" header is not found, successively closer "received" headers are tried. If a domain is not found in this way, the local machine is used as the domain. Finally, if the local domain cannot be determined, the local address 127.0.0.1 is used.

return
String An RFC2822 compliant "Received : from" domain name

        StringBuffer domainBuffer = new StringBuffer();
        String[] headers = null;

        if (getRemoteReceivedHeaderIndex() > -1)
            headers = getMessageIn().getHeader(RFC2822Headers.RECEIVED);

        // There are RECEIVED headers if the array is not null
        // and its length at is greater than 0             
        boolean hasHeaders = (null == headers ? false : headers.length > 0);

        // If there are RECEIVED headers try and extract the domain
        if (hasHeaders)
        {
            final String headerTokens = " \n\r";

            // Search the headers for a domain
            for (int headerIndex =
                headers.length > getRemoteReceivedHeaderIndex()
                    ? getRemoteReceivedHeaderIndex()
                    : headers.length - 1;
                headerIndex >= 0 && domainBuffer.length() == 0;
                headerIndex--)
            {
                // Find the "from" token
                StringTokenizer tokenizer =
                    new StringTokenizer(headers[headerIndex], headerTokens);
                boolean inFrom = false;
                while (!inFrom && tokenizer.hasMoreTokens())
                    inFrom = tokenizer.nextToken().equals("from");
                // Add subsequent tokens to the domain buffer until another                  
                // field is encountered or there are no more tokens
                while (inFrom && tokenizer.hasMoreTokens())
                {
                    String token = tokenizer.nextToken();
                    if (inFrom =
                        getRFC2822RECEIVEDHeaderFields().indexOf(token) == -1)
                    {
                        domainBuffer.append(token);
                        domainBuffer.append(' ");
                    }
                }
            }
        }
        // If a domain was not found, the default is the local host and         
        // if we cannot resolve this, the local address 127.0.0.1         
        // Note that earlier versions of this code simply used 'localhost'         
        // which works fine with java.net but is not resolved by dnsjava         
        // which was introduced in v2.2.0. See Jira issue JAMES-302.          
        if (domainBuffer.length() == 0)
        {
            try
            {
                InetAddress addr1 = java.net.InetAddress.getLocalHost();
                // These shenanigans are required to get the fully qualified                 
                // hostname prior to JDK 1.4 in which getCanonicalHostName()                 
                // does the job for us                 
                InetAddress addr2 =
                    java.net.InetAddress.getByName(addr1.getHostAddress());
                InetAddress addr3 =
                    java.net.InetAddress.getByName(addr2.getHostName());
                domainBuffer.append(addr3.getHostName());
            }
            catch (UnknownHostException ue)
            {
                domainBuffer.append("[127.0.0.1]");
            }
        }
        return domainBuffer.toString().trim();
    
protected java.lang.StringcomputeRemoteHostName()
Answer the Canonical host name of the remote server for the message being processed.

return
String
throws
MessagingException
throws
UnknownHostException

        // These shenanigans are required to get the fully qualified
        // hostname prior to JDK 1.4 in which get getCanonicalHostName()
        // does the job for us
        InetAddress addr1 = org.apache.james.dnsserver.DNSServer.getByName(getRemoteAddress());
        InetAddress addr2 = org.apache.james.dnsserver.DNSServer.getByName(addr1.getHostAddress());
        return addr2.getHostName();
    
protected java.lang.BooleancomputeRemoteReceivedHeaderInvalid()
Computes the remoteReceivedHeaderInvalid.

return
Boolean

        Boolean isInvalid = Boolean.FALSE;
        try
        {
            getRemoteAddress();
        }
        catch (UnknownHostException e)
        {
            isInvalid = Boolean.TRUE;
        }
        return isInvalid;
    
protected javax.mail.internet.MimeMessagecreateEmptyMessage()
Method createEmptyMessage answers a new MimeMessage from the fetched message with the message contents removed.

return
MimeMessage
throws
MessagingException

        // Create an empty messsage
        MimeMessage messageOut = new MimeMessage(getSession());

        // Propogate the headers and subject
        Enumeration headersInEnum = getMessageIn().getAllHeaderLines();
        while (headersInEnum.hasMoreElements())
            messageOut.addHeaderLine((String) headersInEnum.nextElement());
        messageOut.setSubject(getMessageIn().getSubject());

        // Add empty text
        messageOut.setText("");

        // Save
        messageOut.saveChanges();

        return messageOut;
    
protected org.apache.mailet.MailcreateMail(javax.mail.internet.MimeMessage message, org.apache.mailet.MailAddress recipient)
Method createMail creates a new Mail.

param
message
param
recipient
return
Mail
throws
MessagingException

        Collection recipients = new ArrayList(1);
        recipients.add(recipient);
        MailImpl mail =
            new MailImpl(getServer().getId(), getSender(), recipients, message);
        // Ensure the mail is created with non-null remote host name and address,
        // otherwise the Mailet chain may go splat!
        if (getRemoteAddress() == null || getRemoteHostName() == null)
        {
            mail.setRemoteAddr("127.0.0.1");
            mail.setRemoteHost("localhost");
            setDefaultRemoteAddress(true);          
            logStatusInfo("Remote address could not be determined. Using localhost/127.0.0.1");             
        }
        else
        {
            mail.setRemoteAddr(getRemoteAddress());
            mail.setRemoteHost(getRemoteHostName());
            setDefaultRemoteAddress(false);            
        }

        if (getLogger().isDebugEnabled())
        {
            StringBuffer messageBuffer =
                new StringBuffer("Created mail with name: ");
            messageBuffer.append(mail.getName());
            messageBuffer.append(", sender: ");
            messageBuffer.append(mail.getSender());
            messageBuffer.append(", recipients: ");
            Iterator recipientIterator = mail.getRecipients().iterator();
            while (recipientIterator.hasNext())
            {
                messageBuffer.append(recipientIterator.next());
                messageBuffer.append(' ");
            }
            messageBuffer.append(", remote address: ");
            messageBuffer.append(mail.getRemoteAddr());
            messageBuffer.append(", remote host name: ");
            messageBuffer.append(mail.getRemoteHost());
            messageBuffer.append('.");
            getLogger().debug(messageBuffer.toString());
        }
        return mail;
    
protected javax.mail.internet.MimeMessagecreateMessage()

Method createMessage answers a new MimeMessage from the fetched message.

If the maximum message size is exceeded, an empty message is created, else the new message is a copy of the received message.

return
MimeMessage
throws
MessagingException

        // Create a new messsage from the received message
        MimeMessage messageOut = null;
        if (isMaxMessageSizeExceeded().booleanValue())
            messageOut = createEmptyMessage();
        else
            messageOut = new MimeMessage(getMessageIn());

        // set the X-fetched headers
        // Note this is still required to detect bouncing mail and
        // for backwards compatibility with fetchPop 
        messageOut.addHeader("X-fetched-from", getFetchTaskName());

        return messageOut;
    
protected java.lang.StringgetEnvelopeRecipient(javax.mail.internet.MimeMessage msg)
Method getEnvelopeRecipient answers the recipient if found else null. Try and parse the "for" parameter from a Received header Maybe not the most accurate parsing in the world but it should do I opted not to use ORO (maybe I should have)

param
msg
return
String

        String res = getCustomRecipientHeader();
        if (res != null && res.length() > 0) {
            String[] headers = msg.getHeader(getCustomRecipientHeader());
            if (headers != null) {
                String mailFor = headers[0];
              if (mailFor.startsWith("<") && mailFor.endsWith(">"))
                  mailFor = mailFor.substring(1, (mailFor.length() - 1));
              return mailFor;
              }
          } else {
            try
            {
                Enumeration enumeration =
                    msg.getMatchingHeaderLines(new String[] { "Received" });
                while (enumeration.hasMoreElements())
                {
                    String received = (String) enumeration.nextElement();
    
                    int nextSearchAt = 0;
                    int i = 0;
                    int start = 0;
                    int end = 0;
                    boolean hasBracket = false;
                    boolean usableAddress = false;
                    while (!usableAddress && (i != -1))
                    {
                        hasBracket = false;
                        i = received.indexOf("for ", nextSearchAt);
                        if (i > 0)
                        {
                            start = i + 4;
                            end = 0;
                            nextSearchAt = start;
                            for (int c = start; c < received.length(); c++)
                            {
                                char ch = received.charAt(c);
                                switch (ch)
                                {
                                    case '<" :
                                        hasBracket = true;
                                        continue;
                                    case '@" :
                                        usableAddress = true;
                                        continue;
                                    case ' " :
                                        end = c;
                                        break;
                                    case ';" :
                                        end = c;
                                        break;
                                }
                                if (end > 0)
                                    break;
                            }
                        }
                    }
                    if (usableAddress)
                    {
                        // lets try and grab the email address
                        String mailFor = received.substring(start, end);
    
                        // strip the <> around the address if there are any
                        if (mailFor.startsWith("<") && mailFor.endsWith(">"))
                            mailFor = mailFor.substring(1, (mailFor.length() - 1));
    
                        return mailFor;
                    }
                }
            }
            catch (MessagingException me)
            {
                logStatusWarn("No Received headers found.");
            }
        }
        return null;
    
protected org.apache.mailet.MailAddressgetIntendedRecipient()
Method getIntendedRecipient answers the sole intended recipient else null.

return
MailAddress
throws
MessagingException

        // If the original recipient should be ignored, answer the 
        // hard-coded recipient
        if (isIgnoreRecipientHeader())
        {
            StringBuffer messageBuffer =
                new StringBuffer("Ignoring recipient header. Using configured recipient as new envelope recipient: ");
            messageBuffer.append(getRecipient());
            messageBuffer.append('.");            
            logStatusInfo(messageBuffer.toString());
            return getRecipient();
        }

        // If we can determine who the message was received for, answer
        // the target recipient
        String targetRecipient = getEnvelopeRecipient(getMessageIn());
        if (targetRecipient != null)
        {
            MailAddress recipient = new MailAddress(targetRecipient);
            StringBuffer messageBuffer =
                new StringBuffer("Using original envelope recipient as new envelope recipient: ");
            messageBuffer.append(recipient);
            messageBuffer.append('.");              
            logStatusInfo(messageBuffer.toString());
            return recipient;
        }

        // If we can determine the intended recipient from all of the recipients,
        // answer the intended recipient. This requires that there is exactly one
        // recipient answered by getAllRecipients(), which examines the TO: CC: and
        // BCC: headers
        Address[] allRecipients = getMessageIn().getAllRecipients();
        if (allRecipients.length == 1)
        {
            MailAddress recipient =
                new MailAddress((InternetAddress) allRecipients[0]);
            StringBuffer messageBuffer =
                new StringBuffer("Using sole recipient header address as new envelope recipient: ");
            messageBuffer.append(recipient);
            messageBuffer.append('.");              
            logStatusInfo(messageBuffer.toString());
            return recipient;
        }

        return null;
    
protected javax.mail.internet.MimeMessagegetMessageIn()
Returns the messageIn.

return
MimeMessage

        return fieldMessageIn;
    
public static java.lang.StringgetRFC2822RECEIVEDHeaderFields()
Returns the rFC2822RECEIVEDHeaderFields.

return
String

        return fieldRFC2822RECEIVEDHeaderFields;
    
protected java.lang.StringgetRemoteAddress()
Returns the remoteAddress, lazily initialised as required.

return
String

        String remoteAddress;
        if (null == (remoteAddress = getRemoteAddressBasic()))
        {
            updateRemoteAddress();
            return getRemoteAddress();
        }
        return remoteAddress;
    
private java.lang.StringgetRemoteAddressBasic()
Returns the remoteAddress.

return
String

        return fieldRemoteAddress;
    
protected java.lang.StringgetRemoteDomain()
Returns the remoteDomain, lazily initialised as required.

return
String

    
        String remoteDomain;
        if (null == (remoteDomain = getRemoteDomainBasic()))
        {
            updateRemoteDomain();
            return getRemoteDomain();
        }    
        return remoteDomain;
    
private java.lang.StringgetRemoteDomainBasic()
Returns the remoteDomain.

return
String

        return fieldRemoteDomain;
    
protected java.lang.StringgetRemoteHostName()
Returns the remoteHostName, lazily initialised as required.

return
String

        String remoteHostName;
        if (null == (remoteHostName = getRemoteHostNameBasic()))
        {
            updateRemoteHostName();
            return getRemoteHostName();
        }
        return remoteHostName;
    
private java.lang.StringgetRemoteHostNameBasic()
Returns the remoteHostName.

return
String

        return fieldRemoteHostName;
    
protected org.apache.mailet.MailAddressgetSender()

Method getSender answers a MailAddress for the sender. When the sender local part and/or domain part can not be obtained from the mail, default values are used. The flags 'defaultSenderLocalPart' and 'defaultSenderDomainPart' are set accordingly.

return
MailAddress
throws
MessagingException

        String from = null;
        InternetAddress internetAddress = null;
                
        try {
            from = ((InternetAddress) getMessageIn().getFrom()[0]).getAddress().trim();
            setDefaultSenderLocalPart(false);            
        }
        catch (Exception _) {
            from = getDefaultLocalPart();
            setDefaultSenderLocalPart(true);
            StringBuffer buffer = new StringBuffer(32);
            buffer.append("Sender localpart is absent. Using default value (");
            buffer.append(getDefaultLocalPart());
            buffer.append(')");            
            logStatusInfo(buffer.toString());            
        }

        // Check for domain part, add default if missing
        if (from.indexOf('@") < 0)
        {
            StringBuffer fromBuffer = new StringBuffer(from);
            fromBuffer.append('@");
            fromBuffer.append(getDefaultDomainName());
            internetAddress = new InternetAddress(fromBuffer.toString());
            setDefaultSenderDomainPart(true);
            
            StringBuffer buffer = new StringBuffer(32);
            buffer.append("Sender domain is absent. Using default value (");
            buffer.append(getDefaultDomainName());
            buffer.append(')");            
            logStatusInfo(buffer.toString());             
        }
        else
        {
            internetAddress = new InternetAddress(from);
            setDefaultSenderDomainPart(false);            
        }

        return new MailAddress(internetAddress);
    
protected java.lang.StringBuffergetStatusReport(java.lang.String detailMsg)
Answer a StringBuffer containing a message reflecting the current status of the message being processed.

param
detailMsg
return
StringBuffer

        StringBuffer messageBuffer = new StringBuffer(detailMsg);
        if (detailMsg.length() > 0)
            messageBuffer.append(' ");
        messageBuffer.append("Message ID: ");
        messageBuffer.append(getMessageIn().getMessageID());
        messageBuffer.append(". Flags: Seen = ");
        messageBuffer.append(new Boolean(isMessageSeen()));
        messageBuffer.append(", Delete = ");
        messageBuffer.append(new Boolean(isMessageDeleted()));
        messageBuffer.append('.");
        return messageBuffer;
    
protected voidhandleBouncing(org.apache.mailet.Mail mail)
Method handleBouncing sets the Mail state to ERROR and delete from the message store.

param
mail

        mail.setState(Mail.ERROR);
        setMessageDeleted();

        mail.setErrorMessage(
            "This mail from FetchMail task "
                + getFetchTaskName()
                + " seems to be bouncing!");
        logStatusError("Message is bouncing! Deleted from message store and moved to the Error repository.");
    
protected voidhandleMarkSeenNotPermanent()

Handler for when the folder does not support the SEEN flag. The default behaviour implemented here is to log a warning and set the flag anyway.

Subclasses may choose to override this and implement their own solutions.

throws
MessagingException

        getMessageIn().setFlag(Flags.Flag.SEEN, true);
        logStatusWarn("Message marked as SEEN, but the folder does not support a permanent SEEN flag.");
    
protected voidhandleParseException(javax.mail.internet.ParseException ex)
Method handleParseException.

param
ex
throws
MessagingException

        // Update the flags of the received message
        if (!isLeaveUndeliverable())
            setMessageDeleted();
        if (isMarkUndeliverableSeen())
            setMessageSeen();
        logStatusWarn("Message could not be delivered due to an error parsing a mail address.");
        if (getLogger().isDebugEnabled())
        {
            StringBuffer messageBuffer =
                new StringBuffer("UNDELIVERABLE Message ID: ");
            messageBuffer.append(getMessageIn().getMessageID());
            getLogger().debug(messageBuffer.toString(), ex);
        }
    
protected voidhandleUnknownHostException(java.net.UnknownHostException ex)
Method handleUnknownHostException.

param
ex
throws
MessagingException

        // Update the flags of the received message
        if (!isLeaveUndeliverable())
            setMessageDeleted();
    
        if (isMarkUndeliverableSeen())
            setMessageSeen();
    
        logStatusWarn("Message could not be delivered due to an error determining the remote domain.");
        if (getLogger().isDebugEnabled())
        {
            StringBuffer messageBuffer =
                new StringBuffer("UNDELIVERABLE Message ID: ");
            messageBuffer.append(getMessageIn().getMessageID());
            getLogger().debug(messageBuffer.toString(), ex);
        }
    
protected booleanisBlacklistedRecipient(org.apache.mailet.MailAddress recipient)
Method isBlacklistedRecipient.

param
recipient
return
boolean

        return getBlacklist().contains(recipient);
    
protected booleanisBlacklistedRecipient()
Returns the Blacklisted.

return
boolean

        return fieldBlacklistedRecipient;
    
protected booleanisBouncing()
Check if this mail has been bouncing by counting the X-fetched-from headers for this task

return
boolean

        Enumeration enumeration =
            getMessageIn().getMatchingHeaderLines(
                new String[] { "X-fetched-from" });
        int count = 0;
        while (enumeration.hasMoreElements())
        {
            String header = (String) enumeration.nextElement();
            if (header.equals(getFetchTaskName()))
                count++;
        }
        return count >= 3;
    
protected booleanisDefaultRemoteAddress()
Returns the defaultRemoteAddress.

return
boolean

        return fieldDefaultRemoteAddress;
    
protected booleanisDefaultSenderDomainPart()
Returns the defaultSenderDomainPart.

return
boolean

        return fieldDefaultSenderDomainPart;
    
protected booleanisDefaultSenderLocalPart()
Returns the defaultSenderLocalPart.

return
boolean

        return fieldDefaultSenderLocalPart;
    
protected booleanisLocalRecipient(org.apache.mailet.MailAddress recipient)
Method isLocalRecipient.

param
recipient
return
boolean

        return isLocalUser(recipient) && isLocalServer(recipient);
    
protected booleanisLocalServer(org.apache.mailet.MailAddress recipient)
Method isLocalServer.

param
recipient
return
boolean

        return getServer().isLocalServer(recipient.getHost());
    
protected booleanisLocalUser(org.apache.mailet.MailAddress recipient)
Method isLocalUser.

param
recipient
return
boolean

        return getLocalUsers().containsCaseInsensitive(recipient.getUser());
    
protected java.lang.BooleanisMaxMessageSizeExceeded()
Returns the maxMessageSizeExceeded, lazily initialised as required.

return
Boolean

        Boolean isMaxMessageSizeExceeded = null;
        if (null
            == (isMaxMessageSizeExceeded = isMaxMessageSizeExceededBasic()))
        {
            updateMaxMessageSizeExceeded();
            return isMaxMessageSizeExceeded();
        }
        return isMaxMessageSizeExceeded;
    
private java.lang.BooleanisMaxMessageSizeExceededBasic()
Returns the maxMessageSizeExceeded.

return
Boolean

        return fieldMaxMessageSizeExceeded;
    
protected booleanisMessageDeleted()
Is the DELETED flag set?

throws
MessagingException

       return getMessageIn().isSet(Flags.Flag.DELETED);
    
protected booleanisMessageSeen()
Is the SEEN flag set?

throws
MessagingException

       return getMessageIn().isSet(Flags.Flag.SEEN);
    
protected booleanisPreviouslyUnprocessed()
Returns boolean indicating if the message to be delivered was unprocessed in a previous delivery attempt.

return
boolean

        return true;
    
protected booleanisRecipientNotFound()
Returns the recipientNotFound.

return
boolean

        return fieldRecipientNotFound;
    
protected java.lang.BooleanisRemoteReceivedHeaderInvalid()
Returns the remoteReceivedHeaderInvalid, lazily initialised.

return
Boolean

        Boolean isInvalid = null;
        if (null == (isInvalid = isRemoteReceivedHeaderInvalidBasic()))
        {
            updateRemoteReceivedHeaderInvalid();
            return isRemoteReceivedHeaderInvalid();
        }    
        return isInvalid;
    
private java.lang.BooleanisRemoteReceivedHeaderInvalidBasic()
Returns the remoteReceivedHeaderInvalid.

return
Boolean

        return fieldRemoteReceivedHeaderInvalid;
    
protected booleanisRemoteRecipient()
Returns the localRecipient.

return
boolean

        return fieldRemoteRecipient;
    
protected booleanisUserUndefined()
Returns the userUndefined.

return
boolean

        return fieldUserUndefined;
    
protected voidlogStatusError(java.lang.String detailMsg)
Log the status the current message as ERROR.

param
detailMsg

        getLogger().error(getStatusReport(detailMsg).toString());
    
protected voidlogStatusInfo(java.lang.String detailMsg)
Log the status of the current message as INFO.

param
detailMsg

        getLogger().info(getStatusReport(detailMsg).toString());
    
protected voidlogStatusWarn(java.lang.String detailMsg)
Log the status the current message as WARN.

param
detailMsg

        getLogger().warn(getStatusReport(detailMsg).toString());
    
public voidprocess()
Method process attempts to deliver a fetched message.

see
org.apache.james.fetchmail.ProcessorAbstract#process()

        // Log delivery attempt
        if (getLogger().isDebugEnabled())
        {
            StringBuffer logMessageBuffer =
                new StringBuffer("Attempting delivery of message with id. ");
            logMessageBuffer.append(getMessageIn().getMessageID());
            getLogger().debug(logMessageBuffer.toString());
        }

        // Determine the intended recipient
        MailAddress intendedRecipient = getIntendedRecipient();
        setRecipientNotFound(null == intendedRecipient);

        if (isRecipientNotFound())
        {
            if (isDeferRecipientNotFound())
            {

                String messageID = getMessageIn().getMessageID();
                if (!getDeferredRecipientNotFoundMessageIDs()
                    .contains(messageID))
                {
                    getDeferredRecipientNotFoundMessageIDs().add(messageID);
                    if (getLogger().isDebugEnabled())
                    {
                        StringBuffer messageBuffer =
                            new StringBuffer("Deferred processing of message for which the intended recipient could not be found. Message ID: ");
                        messageBuffer.append(messageID);
                        getLogger().debug(messageBuffer.toString());
                    }
                    return;
                }
                else
                {
                    getDeferredRecipientNotFoundMessageIDs().remove(messageID);
                    if (getLogger().isDebugEnabled())
                    {
                        StringBuffer messageBuffer =
                            new StringBuffer("Processing deferred message for which the intended recipient could not be found. Message ID: ");
                        messageBuffer.append(messageID);
                        getLogger().debug(messageBuffer.toString());
                    }
                }
            }

            if (isRejectRecipientNotFound())
            {
                rejectRecipientNotFound();
                return;
            }
            intendedRecipient = getRecipient();
            StringBuffer messageBuffer =
                new StringBuffer("Intended recipient not found. Using configured recipient as new envelope recipient - ");
            messageBuffer.append(intendedRecipient);
            messageBuffer.append('.");
            logStatusInfo(messageBuffer.toString());
        }

        // Set the filter states
        setBlacklistedRecipient(isBlacklistedRecipient(intendedRecipient));
        setRemoteRecipient(!isLocalServer(intendedRecipient));
        setUserUndefined(!isLocalRecipient(intendedRecipient));

        // Apply the filters. Return if rejected
        if (isRejectBlacklisted() && isBlacklistedRecipient())
        {
            rejectBlacklistedRecipient(intendedRecipient);
            return;
        }

        if (isRejectRemoteRecipient() && isRemoteRecipient())
        {
            rejectRemoteRecipient(intendedRecipient);
            return;
        }

        if (isRejectUserUndefined() && isUserUndefined())
        {
            rejectUserUndefined(intendedRecipient);
            return;
        }

        if (isRejectMaxMessageSizeExceeded()
            && isMaxMessageSizeExceeded().booleanValue())
        {
            rejectMaxMessageSizeExceeded(getMessageIn().getSize());
            return;
        }
        
        if (isRejectRemoteReceivedHeaderInvalid()
            && isRemoteReceivedHeaderInvalid().booleanValue())
        {
            rejectRemoteReceivedHeaderInvalid();
            return;
        }        

        // Create the mail
        // If any of the mail addresses are malformed, we will get a
        // ParseException. 
        // If the IP address and host name for the remote domain cannot
        // be found, we will get an UnknownHostException.
        // In both cases, we log the problem and
        // return. The message disposition is defined by the
        // <undeliverable> attributes.
        Mail mail = null;
        try
        {
            mail = createMail(createMessage(), intendedRecipient);
        }
        catch (ParseException ex)
        {
            handleParseException(ex);
            return;
        }
        catch (UnknownHostException ex)
        {
            handleUnknownHostException(ex);
            return;
        }

        addMailAttributes(mail);
        addErrorMessages(mail);        

        // If this mail is bouncing move it to the ERROR repository
        if (isBouncing())
        {
            handleBouncing(mail);
            return;
        }

        // OK, lets send that mail!
        sendMail(mail);
    
protected voidrejectBlacklistedRecipient(org.apache.mailet.MailAddress recipient)
Method rejectBlacklistedRecipient.

param
recipient
throws
MessagingException

        // Update the flags of the received message
        if (!isLeaveBlacklisted())
            setMessageDeleted();
        if (isMarkBlacklistedSeen())
            setMessageSeen();

        StringBuffer messageBuffer =
            new StringBuffer("Rejected mail intended for blacklisted recipient: ");
        messageBuffer.append(recipient);
        messageBuffer.append('.");        
        logStatusInfo(messageBuffer.toString());

        return;
    
protected voidrejectMaxMessageSizeExceeded(int messageSize)
Method rejectMaxMessageSizeExceeded.

param
message size
throws
MessagingException

        // Update the flags of the received message
        if (!isLeaveMaxMessageSizeExceeded())
            setMessageDeleted();

        if (isMarkMaxMessageSizeExceededSeen())
            setMessageSeen();

        StringBuffer messageBuffer =
            new StringBuffer("Rejected mail exceeding message size limit. Message size: ");
        messageBuffer.append(messageSize/1024);
        messageBuffer.append("KB.");          
        logStatusInfo(messageBuffer.toString());

        return;
    
protected voidrejectRecipientNotFound()
Method rejectRecipientNotFound.

throws
MessagingException

        // Update the flags of the received message
        if (!isLeaveRecipientNotFound())
            setMessageDeleted();

        if (isMarkRecipientNotFoundSeen())
            setMessageSeen();

        StringBuffer messageBuffer =
            new StringBuffer("Rejected mail for which a sole intended recipient could not be found.");
        messageBuffer.append(" Recipients: ");
        Address[] allRecipients = getMessageIn().getAllRecipients();
        for (int i = 0; i < allRecipients.length; i++)
        {
            messageBuffer.append(allRecipients[i]);
            messageBuffer.append(' ");
        }
        messageBuffer.append('.");          
        logStatusInfo(messageBuffer.toString());
        return;
    
protected voidrejectRemoteReceivedHeaderInvalid()
Method rejectRemoteReceivedHeaderInvalid.

throws
MessagingException

        // Update the flags of the received message
        if (!isLeaveRemoteReceivedHeaderInvalid())
            setMessageDeleted();

        if (isMarkRemoteReceivedHeaderInvalidSeen())
            setMessageSeen();

        StringBuffer messageBuffer =
            new StringBuffer("Rejected mail with an invalid Received: header at index ");
        messageBuffer.append(getRemoteReceivedHeaderIndex());
        messageBuffer.append(".");          
        logStatusInfo(messageBuffer.toString());       
        return;
    
protected voidrejectRemoteRecipient(org.apache.mailet.MailAddress recipient)
Method rejectRemoteRecipient.

param
recipient
throws
MessagingException

        // Update the flags of the received message
        if (!isLeaveRemoteRecipient())
            setMessageDeleted();

        if (isMarkRemoteRecipientSeen())
            setMessageSeen();

        StringBuffer messageBuffer =
            new StringBuffer("Rejected mail intended for remote recipient: ");
        messageBuffer.append(recipient);
        messageBuffer.append('.");          
        logStatusInfo(messageBuffer.toString());

        return;
    
protected voidrejectUserUndefined(org.apache.mailet.MailAddress recipient)
Method rejectUserUndefined.

param
recipient
throws
MessagingException

        // Update the flags of the received message
        if (!isLeaveUserUndefined())
            setMessageDeleted();

        if (isMarkUserUndefinedSeen())
            setMessageSeen();

        StringBuffer messageBuffer =
            new StringBuffer("Rejected mail intended for undefined user: ");
        messageBuffer.append(recipient);
        messageBuffer.append('.");          
        logStatusInfo(messageBuffer.toString());

        return;
    
protected voidsendMail(org.apache.mailet.Mail mail)
Method sendMail.

param
mail
throws
MessagingException

        // send the mail
        getServer().sendMail(mail);

        // Update the flags of the received message
        if (!isLeave())
            setMessageDeleted();

        if (isMarkSeen())
            setMessageSeen();

        // Log the status
        StringBuffer messageBuffer =
            new StringBuffer("Spooled message to recipients: ");
        Iterator recipientIterator = mail.getRecipients().iterator();
        while (recipientIterator.hasNext())
        {
            messageBuffer.append(recipientIterator.next());
            messageBuffer.append(' ");
        }
        messageBuffer.append('.");
        logStatusInfo(messageBuffer.toString());
    
protected voidsetBlacklistedRecipient(boolean blacklisted)
Sets the Blacklisted.

param
blacklisted The blacklisted to set

        fieldBlacklistedRecipient = blacklisted;
    
protected voidsetDefaultRemoteAddress(boolean defaultRemoteAddress)
Sets the defaultRemoteAddress.

param
defaultRemoteAddress The defaultRemoteAddress to set

        fieldDefaultRemoteAddress = defaultRemoteAddress;
    
protected voidsetDefaultSenderDomainPart(boolean defaultSenderDomainPart)
Sets the defaultSenderDomainPart.

param
defaultSenderDomainPart The defaultSenderDomainPart to set

        fieldDefaultSenderDomainPart = defaultSenderDomainPart;
    
protected voidsetDefaultSenderLocalPart(boolean defaultSenderLocalPart)
Sets the defaultSenderLocalPart.

param
defaultSenderLocalPart The defaultSenderLocalPart to set

        fieldDefaultSenderLocalPart = defaultSenderLocalPart;
    
protected voidsetMaxMessageSizeExceeded(java.lang.Boolean maxMessageSizeExceeded)
Sets the maxMessageSizeExceeded.

param
maxMessageSizeExceeded The maxMessageSizeExceeded to set

        fieldMaxMessageSizeExceeded = maxMessageSizeExceeded;
    
protected voidsetMessageDeleted()
Set the DELETED flag.

throws
MessagingException

            getMessageIn().setFlag(Flags.Flag.DELETED, true);
    
protected voidsetMessageIn(javax.mail.internet.MimeMessage messageIn)
Sets the messageIn.

param
messageIn The messageIn to set

        fieldMessageIn = messageIn;
    
protected voidsetMessageSeen()

        // If the Seen flag is not handled by the folder
        // allow a handler to do whatever it deems necessary
        if (!getMessageIn()
            .getFolder()
            .getPermanentFlags()
            .contains(Flags.Flag.SEEN))
            handleMarkSeenNotPermanent();
        else
            getMessageIn().setFlag(Flags.Flag.SEEN, true);
    
protected voidsetRecipientNotFound(boolean recipientNotFound)
Sets the recipientNotFound.

param
recipientNotFound The recipientNotFound to set

        fieldRecipientNotFound = recipientNotFound;
    
protected voidsetRemoteAddress(java.lang.String remoteAddress)
Sets the remoteAddress.

param
remoteAddress The remoteAddress to set

        fieldRemoteAddress = remoteAddress;
    
protected voidsetRemoteDomain(java.lang.String remoteDomain)
Sets the remoteDomain.

param
remoteDomain The remoteDomain to set

        fieldRemoteDomain = remoteDomain;
    
protected voidsetRemoteHostName(java.lang.String remoteHostName)
Sets the remoteHostName.

param
remoteHostName The remoteHostName to set

        fieldRemoteHostName = remoteHostName;
    
protected voidsetRemoteReceivedHeaderInvalid(java.lang.Boolean remoteReceivedHeaderInvalid)
Sets the remoteReceivedHeaderInvalid.

param
remoteReceivedHeaderInvalid The remoteReceivedHeaderInvalid to set

        fieldRemoteReceivedHeaderInvalid = remoteReceivedHeaderInvalid;
    
protected voidsetRemoteRecipient(boolean localRecipient)
Sets the localRecipient.

param
localRecipient The localRecipient to set

        fieldRemoteRecipient = localRecipient;
    
protected voidsetUserUndefined(boolean userUndefined)
Sets the userUndefined.

param
userUndefined The userUndefined to set

        fieldUserUndefined = userUndefined;
    
protected voidupdateMaxMessageSizeExceeded()
Refreshes the maxMessageSizeExceeded.

        setMaxMessageSizeExceeded(computeMaxMessageSizeExceeded());
    
protected voidupdateRemoteAddress()
Updates the remoteAddress.

        setRemoteAddress(computeRemoteAddress());
    
protected voidupdateRemoteDomain()
Updates the remoteDomain.

        setRemoteDomain(computeRemoteDomain());
    
protected voidupdateRemoteHostName()
Updates the remoteHostName.

        setRemoteHostName(computeRemoteHostName());
    
protected voidupdateRemoteReceivedHeaderInvalid()
Updates the remoteReceivedHeaderInvalid.

        setRemoteReceivedHeaderInvalid(computeRemoteReceivedHeaderInvalid());