001    /****************************************************************
002     * Licensed to the Apache Software Foundation (ASF) under one   *
003     * or more contributor license agreements.  See the NOTICE file *
004     * distributed with this work for additional information        *
005     * regarding copyright ownership.  The ASF licenses this file   *
006     * to you under the Apache License, Version 2.0 (the            *
007     * "License"); you may not use this file except in compliance   *
008     * with the License.  You may obtain a copy of the License at   *
009     *                                                              *
010     *   http://www.apache.org/licenses/LICENSE-2.0                 *
011     *                                                              *
012     * Unless required by applicable law or agreed to in writing,   *
013     * software distributed under the License is distributed on an  *
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
015     * KIND, either express or implied.  See the License for the    *
016     * specific language governing permissions and limitations      *
017     * under the License.                                           *
018     ****************************************************************/
019    
020    package org.apache.james.fetchmail;
021    
022    import java.net.UnknownHostException;
023    import java.util.ArrayList;
024    import java.util.Collection;
025    import java.util.Enumeration;
026    import java.util.Iterator;
027    import java.util.StringTokenizer;
028    
029    import javax.mail.Address;
030    import javax.mail.Flags;
031    import javax.mail.MessagingException;
032    import javax.mail.internet.InternetAddress;
033    import javax.mail.internet.MimeMessage;
034    import javax.mail.internet.ParseException;
035    
036    import org.apache.james.core.MailImpl;
037    import org.apache.james.domainlist.api.DomainListException;
038    import org.apache.james.user.api.UsersRepositoryException;
039    import org.apache.mailet.base.RFC2822Headers;
040    import org.apache.mailet.Mail;
041    import org.apache.mailet.MailAddress;
042    
043    /**
044     * <p>
045     * Class <code>MessageProcessor</code> handles the delivery of
046     * <code>MimeMessages</code> to the James input spool.
047     * </p>
048     * 
049     * <p>
050     * Messages written to the input spool always have the following Mail Attributes
051     * set:
052     * </p>
053     * <dl>
054     * <dt>org.apache.james.fetchmail.taskName (java.lang.String)</dt>
055     * <dd>The name of the fetch task that processed the message</dd>
056     * <dt>org.apache.james.fetchmail.folderName (java.lang.String)</dt>
057     * <dd>The name of the folder from which the message was fetched</dd>
058     * </dl>
059     * 
060     * <p>
061     * Messages written to the input spool have the following Mail Attributes set if
062     * the corresponding condition is satisfied:
063     * <dl>
064     * <dt>org.apache.james.fetchmail.isBlacklistedRecipient</dt>
065     * <dd>The recipient is in the configured blacklist</dd>
066     * <dt>org.apache.james.fetchmail.isMaxMessageSizeExceeded (java.lang.String)</dt>
067     * <dd>The message size exceeds the configured limit. An empty message is
068     * written to the input spool. The Mail Attribute value is a String representing
069     * the size of the original message in bytes.</dd>
070     * <dt>org.apache.james.fetchmail.isRecipientNotFound</dt>
071     * <dd>The recipient could not be found. Delivery is to the configured
072     * recipient. See the discussion of delivery to a sole intended recipient below.
073     * </dd>
074     * <dt>org.apache.james.fetchmail.isRemoteRecievedHeaderInvalid</dt>
075     * <dd>The Receieved header at the index specified by parameter
076     * <code>remoteReceivedHeaderIndex</code> is invalid.</dd>
077     * <dt>org.apache.james.fetchmail.isRemoteRecipient</dt>
078     * <dd>The recipient is on a remote host</dd>
079     * <dt>org.apache.james.fetchmail.isUserUndefined</dt>
080     * <dd>The recipient is on a localhost but not defined to James</dd>
081     * <dt>org.apache.james.fetchmail.isDefaultSenderLocalPart</dt>
082     * <dd>The local part of the sender address could not be obtained. The default
083     * value has been used.</dd>
084     * <dt>org.apache.james.fetchmail.isDefaultSenderDomainPart</dt>
085     * <dd>The domain part of the sender address could not be obtained. The default
086     * value has been used.</dd>
087     * <dt>org.apache.james.fetchmail.isDefaultRemoteAddress</dt>
088     * <dd>The remote address could not be determined. The default value
089     * (localhost/127.0.0.1)has been used.</dd>
090     * </dl>
091     * 
092     * <p>
093     * Configuration settings - see
094     * <code>org.apache.james.fetchmail.ParsedConfiguration</code> - control the
095     * messages that are written to the James input spool, those that are rejected
096     * and what happens to messages that are rejected.
097     * </p>
098     * 
099     * <p>
100     * Rejection processing is based on the following filters:
101     * </p>
102     * <dl>
103     * <dt>RejectRemoteRecipient</dt>
104     * <dd>Rejects recipients on remote hosts</dd>
105     * <dt>RejectBlacklistedRecipient</dt>
106     * <dd>Rejects recipients configured in a blacklist</dd>
107     * <dt>RejectUserUndefined</dt>
108     * <dd>Rejects recipients on local hosts who are not defined as James users</dd>
109     * <dt>RejectRecipientNotFound</dt>
110     * <dd>See the discussion of delivery to a sole intended recipient below</dd>
111     * <dt>RejectMaxMessageSizeExceeded</dt>
112     * <dd>Rejects messages whose size exceeds the configured limit</dd>
113     * <dt>RejectRemoteReceievedHeaderInvalid</dt>
114     * <dd>Rejects messages whose Received header is invalid.</dd>
115     * </dl>
116     * 
117     * <p>
118     * Rejection processing is intentionally limited to managing the status of the
119     * messages that are rejected on the server from which they were fetched. View
120     * it as a simple automation of the manual processing an end-user would perform
121     * through a mail client. Messages may be marked as seen or be deleted.
122     * </p>
123     * 
124     * <p>
125     * Further processing can be achieved by configuring to disable rejection for
126     * one or more filters. This enables Messages that would have been rejected to
127     * be written to the James input spool. The conditional Mail Attributes
128     * described above identify the filter states. The Matcher/Mailet chain can then
129     * be used to perform any further processing required, such as notifying the
130     * Postmaster and/or sender, marking the message for error processing, etc.
131     * </p>
132     * 
133     * <p>
134     * Note that in the case of a message exceeding the message size limit, the
135     * message that is written to the input spool has no content. This enables
136     * configuration of a mailet notifying the sender that their mail has not been
137     * delivered due to its size while maintaining the purpose of the filter which
138     * is to avoid injecting excessively large messages into the input spool.
139     * </p>
140     * 
141     * <p>
142     * Delivery is to a sole intended recipient. The recipient is determined in the
143     * following manner:
144     * </p>
145     * 
146     * <ol>
147     * <li>If isIgnoreIntendedRecipient(), use the configured recipient</li>
148     * <li>If the Envelope contains a for: stanza, use the recipient in the stanza</li>
149     * <li>If the Message has a sole intended recipient, use this recipient</li>
150     * <li>If not rejectRecipientNotFound(), use the configured recipient</li>
151     * </ol>
152     * 
153     * <p>
154     * If a recipient cannot be determined after these steps, the message is
155     * rejected.
156     * </p>
157     * 
158     * <p>
159     * Every delivered message CURRENTLY has an "X-fetched-from" header added
160     * containing the name of the fetch task. Its primary uses are to detect
161     * bouncing mail and provide backwards compatibility with the fetchPop task that
162     * inserted this header to enable injected messages to be detected in the
163     * Matcher/Mailet chain. This header is DEPRECATED and WILL BE REMOVED in a
164     * future version of fetchmail. Use the Mail Attribute
165     * <code>org.apache.james.fetchmail.taskName</code> instead.
166     * 
167     * <p>
168     * <code>MessageProcessor</code> is as agnostic as it can be about the format
169     * and contents of the messages it delivers. There are no RFCs that govern its
170     * behavior. The most releveant RFCs relate to the exchange of messages between
171     * MTA servers, but not POP3 or IMAP servers which are normally end-point
172     * servers and not expected to re-inject mail into MTAs. None the less, the
173     * intent is to conform to the 'spirit' of the RFCs.
174     * <code>MessageProcessor</code> relies on the MTA (James in this
175     * implementation) to manage and validate the injected mail just as it would
176     * when receiving mail from an upstream MTA.
177     * </p>
178     * 
179     * <p>
180     * The only correction applied by <code>MessageProcessor</code> is to correct a
181     * missing or partial sender address. If the sender address can not be obtained,
182     * the default local part and default domain part is added. If the sender domain
183     * part is absent, the default domain part is added.
184     * </p>
185     * 
186     * <p>
187     * Mail with corrections applied to the sender address will most likely pass
188     * Matcher tests on the sender that they might otherwise fail. The Mail
189     * Attributes <code>org.apache.james.fetchmail.isDefaultSenderLocalPart</code>
190     * and <code>org.apache.james.fetchmail.isDefaultSenderDomainPart</code> are
191     * added to the injected mail to enable such mail to be detected and processed
192     * accordingly.
193     * </p>
194     * 
195     * <p>
196     * The status of messages on the server from which they were fetched that cannot
197     * be injected into the input spool due to non-correctable errors is determined
198     * by the undeliverable configuration options.
199     * </p>
200     */
201    public class MessageProcessor extends ProcessorAbstract {
202        private MimeMessage fieldMessageIn;
203    
204        /**
205         * Recipient cannot be found
206         */
207        private boolean fieldRecipientNotFound = false;
208    
209        /**
210         * Recipient is a local user on a local host
211         */
212        private boolean fieldRemoteRecipient = true;
213    
214        /**
215         * The mail's Received header at index remoteReceivedHeaderIndex is invalid.
216         */
217        private Boolean fieldRemoteReceivedHeaderInvalid;
218    
219        /**
220         * Recipient is not a local user
221         */
222        private boolean fieldUserUndefined = false;
223    
224        /**
225         * The Maximum Message has been exceeded
226         */
227        private Boolean fieldMaxMessageSizeExceeded;
228    
229        /**
230         * Field names for an RFC2822 compliant RECEIVED Header
231         */
232        static final private String fieldRFC2822RECEIVEDHeaderFields = "from by via with id for ;";
233    
234        /**
235         * Recipient is blacklisted
236         */
237        private boolean fieldBlacklistedRecipient = false;
238    
239        /**
240         * The RFC2822 compliant "Received : from" domain
241         */
242        private String fieldRemoteDomain;
243    
244        /**
245         * The remote address derived from the remote domain
246         */
247        private String fieldRemoteAddress;
248    
249        /**
250         * The remote host name derived from the remote domain
251         */
252        private String fieldRemoteHostName;
253    
254        /**
255         * The default sender local part has been used.
256         */
257        private boolean fieldDefaultSenderLocalPart = false;
258    
259        /**
260         * The default sender domain part has been used.
261         */
262        private boolean fieldDefaultSenderDomainPart = false;
263    
264        /**
265         * The default remote address has been used.
266         */
267        private boolean fieldDefaultRemoteAddress = false;
268    
269        /**
270         * Constructor for MessageProcessor.
271         * 
272         * @param account
273         */
274        private MessageProcessor(Account account) {
275            super(account);
276        }
277    
278        /**
279         * Constructor for MessageProcessor.
280         * 
281         * @param messageIn
282         * @param account
283         */
284    
285        MessageProcessor(MimeMessage messageIn, Account account) {
286            this(account);
287            setMessageIn(messageIn);
288        }
289    
290        /**
291         * Method process attempts to deliver a fetched message.
292         * 
293         * @see org.apache.james.fetchmail.ProcessorAbstract#process()
294         */
295        public void process() throws MessagingException {
296            // Log delivery attempt
297            if (getLogger().isDebugEnabled()) {
298                StringBuilder logMessageBuffer = new StringBuilder("Attempting delivery of message with id. ");
299                logMessageBuffer.append(getMessageIn().getMessageID());
300                getLogger().debug(logMessageBuffer.toString());
301            }
302    
303            // Determine the intended recipient
304            MailAddress intendedRecipient = getIntendedRecipient();
305            setRecipientNotFound(null == intendedRecipient);
306    
307            if (isRecipientNotFound()) {
308                if (isDeferRecipientNotFound()) {
309    
310                    String messageID = getMessageIn().getMessageID();
311                    if (!getDeferredRecipientNotFoundMessageIDs().contains(messageID)) {
312                        getDeferredRecipientNotFoundMessageIDs().add(messageID);
313                        if (getLogger().isDebugEnabled()) {
314                            StringBuilder messageBuffer = new StringBuilder("Deferred processing of message for which the intended recipient could not be found. Message ID: ");
315                            messageBuffer.append(messageID);
316                            getLogger().debug(messageBuffer.toString());
317                        }
318                        return;
319                    } else {
320                        getDeferredRecipientNotFoundMessageIDs().remove(messageID);
321                        if (getLogger().isDebugEnabled()) {
322                            StringBuilder messageBuffer = new StringBuilder("Processing deferred message for which the intended recipient could not be found. Message ID: ");
323                            messageBuffer.append(messageID);
324                            getLogger().debug(messageBuffer.toString());
325                        }
326                    }
327                }
328    
329                if (isRejectRecipientNotFound()) {
330                    rejectRecipientNotFound();
331                    return;
332                }
333                intendedRecipient = getRecipient();
334                StringBuilder messageBuffer = new StringBuilder("Intended recipient not found. Using configured recipient as new envelope recipient - ");
335                messageBuffer.append(intendedRecipient);
336                messageBuffer.append('.');
337                logStatusInfo(messageBuffer.toString());
338            }
339    
340            // Set the filter states
341            setBlacklistedRecipient(isBlacklistedRecipient(intendedRecipient));
342            setRemoteRecipient(!isLocalServer(intendedRecipient));
343            try {
344                setUserUndefined(!isLocalRecipient(intendedRecipient));
345            } catch (UsersRepositoryException e) {
346                throw new MessagingException("Unable to access USersRepository", e);
347            }
348    
349            // Apply the filters. Return if rejected
350            if (isRejectBlacklisted() && isBlacklistedRecipient()) {
351                rejectBlacklistedRecipient(intendedRecipient);
352                return;
353            }
354    
355            if (isRejectRemoteRecipient() && isRemoteRecipient()) {
356                rejectRemoteRecipient(intendedRecipient);
357                return;
358            }
359    
360            if (isRejectUserUndefined() && isUserUndefined()) {
361                rejectUserUndefined(intendedRecipient);
362                return;
363            }
364    
365            if (isRejectMaxMessageSizeExceeded() && isMaxMessageSizeExceeded().booleanValue()) {
366                rejectMaxMessageSizeExceeded(getMessageIn().getSize());
367                return;
368            }
369    
370            if (isRejectRemoteReceivedHeaderInvalid() && isRemoteReceivedHeaderInvalid().booleanValue()) {
371                rejectRemoteReceivedHeaderInvalid();
372                return;
373            }
374    
375            // Create the mail
376            // If any of the mail addresses are malformed, we will get a
377            // ParseException.
378            // If the IP address and host name for the remote domain cannot
379            // be found, we will get an UnknownHostException.
380            // In both cases, we log the problem and
381            // return. The message disposition is defined by the
382            // <undeliverable> attributes.
383            Mail mail = null;
384            try {
385                mail = createMail(createMessage(), intendedRecipient);
386            } catch (ParseException ex) {
387                handleParseException(ex);
388                return;
389            } catch (UnknownHostException ex) {
390                handleUnknownHostException(ex);
391                return;
392            }
393    
394            addMailAttributes(mail);
395            addErrorMessages(mail);
396    
397            // If this mail is bouncing move it to the ERROR repository
398            if (isBouncing()) {
399                handleBouncing(mail);
400                return;
401            }
402    
403            // OK, lets send that mail!
404            sendMail(mail);
405        }
406    
407        /**
408         * Method rejectRemoteRecipient.
409         * 
410         * @param recipient
411         * @throws MessagingException
412         */
413        protected void rejectRemoteRecipient(MailAddress recipient) throws MessagingException {
414            // Update the flags of the received message
415            if (!isLeaveRemoteRecipient())
416                setMessageDeleted();
417    
418            if (isMarkRemoteRecipientSeen())
419                setMessageSeen();
420    
421            StringBuilder messageBuffer = new StringBuilder("Rejected mail intended for remote recipient: ");
422            messageBuffer.append(recipient);
423            messageBuffer.append('.');
424            logStatusInfo(messageBuffer.toString());
425        }
426    
427        /**
428         * Method rejectBlacklistedRecipient.
429         * 
430         * @param recipient
431         * @throws MessagingException
432         */
433        protected void rejectBlacklistedRecipient(MailAddress recipient) throws MessagingException {
434            // Update the flags of the received message
435            if (!isLeaveBlacklisted())
436                setMessageDeleted();
437            if (isMarkBlacklistedSeen())
438                setMessageSeen();
439    
440            StringBuilder messageBuffer = new StringBuilder("Rejected mail intended for blacklisted recipient: ");
441            messageBuffer.append(recipient);
442            messageBuffer.append('.');
443            logStatusInfo(messageBuffer.toString());
444        }
445    
446        /**
447         * Method rejectRecipientNotFound.
448         * 
449         * @throws MessagingException
450         */
451        protected void rejectRecipientNotFound() throws MessagingException {
452            // Update the flags of the received message
453            if (!isLeaveRecipientNotFound())
454                setMessageDeleted();
455    
456            if (isMarkRecipientNotFoundSeen())
457                setMessageSeen();
458    
459            StringBuilder messageBuffer = new StringBuilder("Rejected mail for which a sole intended recipient could not be found.");
460            messageBuffer.append(" Recipients: ");
461            Address[] allRecipients = getMessageIn().getAllRecipients();
462            for (int i = 0; i < allRecipients.length; i++) {
463                messageBuffer.append(allRecipients[i]);
464                messageBuffer.append(' ');
465            }
466            messageBuffer.append('.');
467            logStatusInfo(messageBuffer.toString());
468        }
469    
470        /**
471         * Method rejectUserUndefined.
472         * 
473         * @param recipient
474         * @throws MessagingException
475         */
476        protected void rejectUserUndefined(MailAddress recipient) throws MessagingException {
477            // Update the flags of the received message
478            if (!isLeaveUserUndefined())
479                setMessageDeleted();
480    
481            if (isMarkUserUndefinedSeen())
482                setMessageSeen();
483    
484            StringBuilder messageBuffer = new StringBuilder("Rejected mail intended for undefined user: ");
485            messageBuffer.append(recipient);
486            messageBuffer.append('.');
487            logStatusInfo(messageBuffer.toString());
488        }
489    
490        /**
491         * Method rejectMaxMessageSizeExceeded.
492         * 
493         * @param messageSize
494         *            size
495         * @throws MessagingException
496         */
497        protected void rejectMaxMessageSizeExceeded(int messageSize) throws MessagingException {
498            // Update the flags of the received message
499            if (!isLeaveMaxMessageSizeExceeded())
500                setMessageDeleted();
501    
502            if (isMarkMaxMessageSizeExceededSeen())
503                setMessageSeen();
504    
505            StringBuilder messageBuffer = new StringBuilder("Rejected mail exceeding message size limit. Message size: ");
506            messageBuffer.append(messageSize / 1024);
507            messageBuffer.append("KB.");
508            logStatusInfo(messageBuffer.toString());
509        }
510    
511        /**
512         * Method rejectRemoteReceivedHeaderInvalid.
513         * 
514         * @throws MessagingException
515         */
516        protected void rejectRemoteReceivedHeaderInvalid() throws MessagingException {
517            // Update the flags of the received message
518            if (!isLeaveRemoteReceivedHeaderInvalid())
519                setMessageDeleted();
520    
521            if (isMarkRemoteReceivedHeaderInvalidSeen())
522                setMessageSeen();
523    
524            StringBuilder messageBuffer = new StringBuilder("Rejected mail with an invalid Received: header at index ");
525            messageBuffer.append(getRemoteReceivedHeaderIndex());
526            messageBuffer.append(".");
527            logStatusInfo(messageBuffer.toString());
528        }
529    
530        /**
531         * <p>
532         * Method createMessage answers a new <code>MimeMessage</code> from the
533         * fetched message.
534         * </p>
535         * 
536         * <p>
537         * If the maximum message size is exceeded, an empty message is created,
538         * else the new message is a copy of the received message.
539         * </p>
540         * 
541         * @return MimeMessage
542         * @throws MessagingException
543         */
544        protected MimeMessage createMessage() throws MessagingException {
545            // Create a new messsage from the received message
546            MimeMessage messageOut = null;
547            if (isMaxMessageSizeExceeded().booleanValue())
548                messageOut = createEmptyMessage();
549            else
550                messageOut = new MimeMessage(getMessageIn());
551    
552            // set the X-fetched headers
553            // Note this is still required to detect bouncing mail and
554            // for backwards compatibility with fetchPop
555            messageOut.addHeader("X-fetched-from", getFetchTaskName());
556    
557            return messageOut;
558        }
559    
560        /**
561         * Method createEmptyMessage answers a new <code>MimeMessage</code> from the
562         * fetched message with the message contents removed.
563         * 
564         * @return MimeMessage
565         * @throws MessagingException
566         */
567        @SuppressWarnings("unchecked")
568        protected MimeMessage createEmptyMessage() throws MessagingException {
569            // Create an empty messsage
570            MimeMessage messageOut = new MimeMessage(getSession());
571    
572            // Propogate the headers and subject
573            Enumeration headersInEnum = getMessageIn().getAllHeaderLines();
574            while (headersInEnum.hasMoreElements())
575                messageOut.addHeaderLine((String) headersInEnum.nextElement());
576            messageOut.setSubject(getMessageIn().getSubject());
577    
578            // Add empty text
579            messageOut.setText("");
580    
581            // Save
582            messageOut.saveChanges();
583    
584            return messageOut;
585        }
586    
587        /**
588         * Method createMail creates a new <code>Mail</code>.
589         * 
590         * @param message
591         * @param recipient
592         * @return Mail
593         * @throws MessagingException
594         */
595        protected Mail createMail(MimeMessage message, MailAddress recipient) throws MessagingException, UnknownHostException {
596            Collection<MailAddress> recipients = new ArrayList<MailAddress>(1);
597            recipients.add(recipient);
598            MailImpl mail = new MailImpl(MailImpl.getId(), getSender(), recipients, message);
599    
600            try {
601                mail.setRemoteAddr(getRemoteAddress());
602                mail.setRemoteHost(getRemoteHostName());
603                setDefaultRemoteAddress(false);
604            } catch (UnknownHostException e) {
605                // check if we should ignore this
606                // See: JAMES-795
607                if (isRejectRemoteReceivedHeaderInvalid() == false) {
608                    // Ensure the mail is created with non-null remote host name and
609                    // address,
610                    // otherwise the Mailet chain may go splat!
611                    mail.setRemoteAddr("127.0.0.1");
612                    mail.setRemoteHost("localhost");
613                    setDefaultRemoteAddress(true);
614                    logStatusInfo("Remote address could not be determined. Using localhost/127.0.0.1");
615                } else {
616                    throw e;
617                }
618            }
619    
620            logMailCreation(mail);
621            return mail;
622        }
623    
624        @SuppressWarnings("unchecked")
625        private void logMailCreation(MailImpl mail) {
626            if (getLogger().isDebugEnabled()) {
627                StringBuilder messageBuffer = new StringBuilder("Created mail with name: ");
628                messageBuffer.append(mail.getName());
629                messageBuffer.append(", sender: ");
630                messageBuffer.append(mail.getSender());
631                messageBuffer.append(", recipients: ");
632                Iterator recipientIterator = mail.getRecipients().iterator();
633                while (recipientIterator.hasNext()) {
634                    messageBuffer.append(recipientIterator.next());
635                    messageBuffer.append(' ');
636                }
637                messageBuffer.append(", remote address: ");
638                messageBuffer.append(mail.getRemoteAddr());
639                messageBuffer.append(", remote host name: ");
640                messageBuffer.append(mail.getRemoteHost());
641                messageBuffer.append('.');
642                getLogger().debug(messageBuffer.toString());
643            }
644        }
645    
646        /**
647         * <p>
648         * Method getSender answers a <code>MailAddress</code> for the sender. When
649         * the sender local part and/or domain part can not be obtained from the
650         * mail, default values are used. The flags 'defaultSenderLocalPart' and
651         * 'defaultSenderDomainPart' are set accordingly.
652         * </p>
653         * 
654         * @return MailAddress
655         * @throws MessagingException
656         */
657        protected MailAddress getSender() throws MessagingException {
658            String from = null;
659            InternetAddress internetAddress = null;
660    
661            try {
662                from = ((InternetAddress) getMessageIn().getFrom()[0]).getAddress().trim();
663                setDefaultSenderLocalPart(false);
664            } catch (Exception _) {
665                from = getDefaultLocalPart();
666                setDefaultSenderLocalPart(true);
667                StringBuilder buffer = new StringBuilder(32);
668                buffer.append("Sender localpart is absent. Using default value (");
669                buffer.append(getDefaultLocalPart());
670                buffer.append(')');
671                logStatusInfo(buffer.toString());
672            }
673    
674            // Check for domain part, add default if missing
675            if (from.indexOf('@') < 0) {
676                StringBuilder fromBuffer = new StringBuilder(from);
677                fromBuffer.append('@');
678                fromBuffer.append(getDefaultDomainName());
679                internetAddress = new InternetAddress(fromBuffer.toString());
680                setDefaultSenderDomainPart(true);
681    
682                StringBuilder buffer = new StringBuilder(32);
683                buffer.append("Sender domain is absent. Using default value (");
684                buffer.append(getDefaultDomainName());
685                buffer.append(')');
686                logStatusInfo(buffer.toString());
687            } else {
688                internetAddress = new InternetAddress(from);
689                setDefaultSenderDomainPart(false);
690            }
691    
692            return new MailAddress(internetAddress);
693        }
694    
695        /**
696         * <p>
697         * Method computeRemoteDomain answers a <code>String</code> that is the
698         * RFC2822 compliant "Received : from" domain extracted from the message
699         * being processed for the remote domain that sent the message.
700         * </p>
701         * 
702         * <p>
703         * Often the remote domain is the domain that sent the message to the host
704         * of the message store, the second "received" header, which has an index of
705         * 1. Other times, messages may be received by a edge mail server and
706         * relayed internally through one or more internal mail servers prior to
707         * arriving at the message store host. In these cases the index is 1 + the
708         * number of internal servers through which a mail passes.
709         * </p>
710         * <p>
711         * The index of the header to use is specified by the configuration
712         * parameter <code>RemoteReceivedHeaderIndex</code>. This is set to point to
713         * the received header prior to the remote mail server, the one prior to the
714         * edge mail server.
715         * </p>
716         * <p>
717         * "received" headers are searched starting at the specified index. If a
718         * domain in the "received" header is not found, successively closer
719         * "received" headers are tried. If a domain is not found in this way, the
720         * local machine is used as the domain. Finally, if the local domain cannot
721         * be determined, the local address 127.0.0.1 is used.
722         * </p>
723         * 
724         * @return String An RFC2822 compliant "Received : from" domain name
725         */
726        protected String computeRemoteDomain() throws MessagingException {
727            StringBuilder domainBuffer = new StringBuilder();
728            String[] headers = null;
729    
730            if (getRemoteReceivedHeaderIndex() > -1)
731                headers = getMessageIn().getHeader(RFC2822Headers.RECEIVED);
732    
733            // There are RECEIVED headers if the array is not null
734            // and its length at is greater than 0
735            boolean hasHeaders = (null == headers ? false : headers.length > 0);
736    
737            // If there are RECEIVED headers try and extract the domain
738            if (hasHeaders) {
739                final String headerTokens = " \n\r";
740    
741                // Search the headers for a domain
742                for (int headerIndex = headers.length > getRemoteReceivedHeaderIndex() ? getRemoteReceivedHeaderIndex() : headers.length - 1; headerIndex >= 0 && domainBuffer.length() == 0; headerIndex--) {
743                    // Find the "from" token
744                    StringTokenizer tokenizer = new StringTokenizer(headers[headerIndex], headerTokens);
745                    boolean inFrom = false;
746                    while (!inFrom && tokenizer.hasMoreTokens())
747                        inFrom = tokenizer.nextToken().equals("from");
748                    // Add subsequent tokens to the domain buffer until another
749                    // field is encountered or there are no more tokens
750                    while (inFrom && tokenizer.hasMoreTokens()) {
751                        String token = tokenizer.nextToken();
752                        inFrom = (getRFC2822RECEIVEDHeaderFields().indexOf(token) == -1);
753                        if (inFrom) {
754                            domainBuffer.append(token);
755                            domainBuffer.append(' ');
756                        }
757                    }
758                }
759            }
760            // If a domain was not found, the default is the local host and
761            // if we cannot resolve this, the local address 127.0.0.1
762            // Note that earlier versions of this code simply used 'localhost'
763            // which works fine with java.net but is not resolved by dnsjava
764            // which was introduced in v2.2.0. See Jira issue JAMES-302.
765            if (domainBuffer.length() == 0) {
766                try {
767                    domainBuffer.append(getDNSServer().getLocalHost().getCanonicalHostName());
768                } catch (UnknownHostException ue) {
769                    domainBuffer.append("[127.0.0.1]");
770                }
771            }
772            return domainBuffer.toString().trim();
773        }
774    
775        /**
776         * Method handleBouncing sets the Mail state to ERROR and delete from the
777         * message store.
778         * 
779         * @param mail
780         */
781        protected void handleBouncing(Mail mail) throws MessagingException {
782            mail.setState(Mail.ERROR);
783            setMessageDeleted();
784    
785            mail.setErrorMessage("This mail from FetchMail task " + getFetchTaskName() + " seems to be bouncing!");
786            logStatusError("Message is bouncing! Deleted from message store and moved to the Error repository.");
787        }
788    
789        /**
790         * Method handleParseException.
791         * 
792         * @param ex
793         * @throws MessagingException
794         */
795        protected void handleParseException(ParseException ex) throws MessagingException {
796            // Update the flags of the received message
797            if (!isLeaveUndeliverable())
798                setMessageDeleted();
799            if (isMarkUndeliverableSeen())
800                setMessageSeen();
801            logStatusWarn("Message could not be delivered due to an error parsing a mail address.");
802            if (getLogger().isDebugEnabled()) {
803                StringBuilder messageBuffer = new StringBuilder("UNDELIVERABLE Message ID: ");
804                messageBuffer.append(getMessageIn().getMessageID());
805                getLogger().debug(messageBuffer.toString(), ex);
806            }
807        }
808    
809        /**
810         * Method handleUnknownHostException.
811         * 
812         * @param ex
813         * @throws MessagingException
814         */
815        protected void handleUnknownHostException(UnknownHostException ex) throws MessagingException {
816            // Update the flags of the received message
817            if (!isLeaveUndeliverable())
818                setMessageDeleted();
819    
820            if (isMarkUndeliverableSeen())
821                setMessageSeen();
822    
823            logStatusWarn("Message could not be delivered due to an error determining the remote domain.");
824            if (getLogger().isDebugEnabled()) {
825                StringBuilder messageBuffer = new StringBuilder("UNDELIVERABLE Message ID: ");
826                messageBuffer.append(getMessageIn().getMessageID());
827                getLogger().debug(messageBuffer.toString(), ex);
828            }
829        }
830    
831        /**
832         * Method isLocalRecipient.
833         * 
834         * @param recipient
835         * @return boolean
836         * @throws UsersRepositoryException
837         */
838        protected boolean isLocalRecipient(MailAddress recipient) throws UsersRepositoryException {
839            if (isLocalServer(recipient)) {
840                // check if we use virtualhosting or not and use the right part of
841                // the recipient in respect of this
842                // See JAMES-1135
843                if (getConfiguration().getLocalUsers().supportVirtualHosting()) {
844                    return getLocalUsers().contains(recipient.toString());
845                } else {
846                    return getLocalUsers().contains(recipient.getLocalPart());
847                }
848            }
849            return false;
850        }
851    
852        /**
853         * Method isLocalServer.
854         * 
855         * @param recipient
856         * @return boolean
857         */
858        protected boolean isLocalServer(MailAddress recipient) {
859            try {
860                return getConfiguration().getDomainList().containsDomain(recipient.getDomain());
861            } catch (DomainListException e) {
862                getLogger().error("Unable to access DomainList", e);
863                return false;
864            }
865        }
866    
867        /**
868         * Method isBlacklistedRecipient.
869         * 
870         * @param recipient
871         * @return boolean
872         */
873        protected boolean isBlacklistedRecipient(MailAddress recipient) {
874            return getBlacklist().contains(recipient);
875        }
876    
877        /**
878         * Check if this mail has been bouncing by counting the X-fetched-from
879         * headers for this task
880         * 
881         * @return boolean
882         */
883        @SuppressWarnings("unchecked")
884        protected boolean isBouncing() throws MessagingException {
885            Enumeration enumeration = getMessageIn().getMatchingHeaderLines(new String[] { "X-fetched-from" });
886            int count = 0;
887            while (enumeration.hasMoreElements()) {
888                String header = (String) enumeration.nextElement();
889                if (header.equals(getFetchTaskName()))
890                    count++;
891            }
892            return count >= 3;
893        }
894    
895        /**
896         * Method sendMail.
897         * 
898         * @param mail
899         * @throws MessagingException
900         */
901        @SuppressWarnings("unchecked")
902        protected void sendMail(Mail mail) throws MessagingException {
903            // queue the mail
904            getMailQueue().enQueue(mail);
905    
906            // Update the flags of the received message
907            if (!isLeave())
908                setMessageDeleted();
909    
910            if (isMarkSeen())
911                setMessageSeen();
912    
913            // Log the status
914            StringBuilder messageBuffer = new StringBuilder("Spooled message to recipients: ");
915            Iterator recipientIterator = mail.getRecipients().iterator();
916            while (recipientIterator.hasNext()) {
917                messageBuffer.append(recipientIterator.next());
918                messageBuffer.append(' ');
919            }
920            messageBuffer.append('.');
921            logStatusInfo(messageBuffer.toString());
922        }
923    
924        /**
925         * Method getEnvelopeRecipient answers the recipient if found else null.
926         * 
927         * Try and parse the "for" parameter from a Received header.<br>
928         * Maybe not the most accurate parsing in the world but it should do.<br>
929         * I opted not to use ORO (maybe I should have).
930         * 
931         * @param msg
932         * @return String
933         */
934    
935        @SuppressWarnings("unchecked")
936        protected String getEnvelopeRecipient(MimeMessage msg) throws MessagingException {
937            String res = getCustomRecipientHeader();
938            if (res != null && res.length() > 0) {
939                String[] headers = msg.getHeader(getCustomRecipientHeader());
940                if (headers != null) {
941                    String mailFor = headers[0];
942                    if (mailFor.startsWith("<") && mailFor.endsWith(">"))
943                        mailFor = mailFor.substring(1, (mailFor.length() - 1));
944                    return mailFor;
945                }
946            } else {
947                try {
948                    Enumeration enumeration = msg.getMatchingHeaderLines(new String[] { "Received" });
949                    while (enumeration.hasMoreElements()) {
950                        String received = (String) enumeration.nextElement();
951    
952                        int nextSearchAt = 0;
953                        int i = 0;
954                        int start = 0;
955                        int end = 0;
956                        boolean hasBracket = false;
957                        boolean usableAddress = false;
958                        while (!usableAddress && (i != -1)) {
959                            hasBracket = false;
960                            i = received.indexOf("for ", nextSearchAt);
961                            if (i > 0) {
962                                start = i + 4;
963                                end = 0;
964                                nextSearchAt = start;
965                                for (int c = start; c < received.length(); c++) {
966                                    char ch = received.charAt(c);
967                                    switch (ch) {
968                                    case '<':
969                                        hasBracket = true;
970                                        continue;
971                                    case '@':
972                                        usableAddress = true;
973                                        continue;
974                                    case ' ':
975                                        end = c;
976                                        break;
977                                    case ';':
978                                        end = c;
979                                        break;
980                                    }
981                                    if (end > 0)
982                                        break;
983                                }
984                            }
985                        }
986                        if (usableAddress) {
987                            // lets try and grab the email address
988                            String mailFor = received.substring(start, end);
989    
990                            // strip the <> around the address if there are any
991                            if (mailFor.startsWith("<") && mailFor.endsWith(">"))
992                                mailFor = mailFor.substring(1, (mailFor.length() - 1));
993    
994                            return mailFor;
995                        }
996                    }
997                } catch (MessagingException me) {
998                    logStatusWarn("No Received headers found.");
999                }
1000            }
1001            return null;
1002        }
1003    
1004        /**
1005         * Method getIntendedRecipient answers the sole intended recipient else
1006         * null.
1007         * 
1008         * @return MailAddress
1009         * @throws MessagingException
1010         */
1011        protected MailAddress getIntendedRecipient() throws MessagingException {
1012            // If the original recipient should be ignored, answer the
1013            // hard-coded recipient
1014            if (isIgnoreRecipientHeader()) {
1015                StringBuilder messageBuffer = new StringBuilder("Ignoring recipient header. Using configured recipient as new envelope recipient: ");
1016                messageBuffer.append(getRecipient());
1017                messageBuffer.append('.');
1018                logStatusInfo(messageBuffer.toString());
1019                return getRecipient();
1020            }
1021    
1022            // If we can determine who the message was received for, answer
1023            // the target recipient
1024            String targetRecipient = getEnvelopeRecipient(getMessageIn());
1025            if (targetRecipient != null) {
1026                MailAddress recipient = new MailAddress(targetRecipient);
1027                StringBuilder messageBuffer = new StringBuilder("Using original envelope recipient as new envelope recipient: ");
1028                messageBuffer.append(recipient);
1029                messageBuffer.append('.');
1030                logStatusInfo(messageBuffer.toString());
1031                return recipient;
1032            }
1033    
1034            // If we can determine the intended recipient from all of the
1035            // recipients,
1036            // answer the intended recipient. This requires that there is exactly
1037            // one
1038            // recipient answered by getAllRecipients(), which examines the TO: CC:
1039            // and
1040            // BCC: headers
1041            Address[] allRecipients = getMessageIn().getAllRecipients();
1042            if (allRecipients.length == 1) {
1043                MailAddress recipient = new MailAddress((InternetAddress) allRecipients[0]);
1044                StringBuilder messageBuffer = new StringBuilder("Using sole recipient header address as new envelope recipient: ");
1045                messageBuffer.append(recipient);
1046                messageBuffer.append('.');
1047                logStatusInfo(messageBuffer.toString());
1048                return recipient;
1049            }
1050    
1051            return null;
1052        }
1053    
1054        /**
1055         * Returns the messageIn.
1056         * 
1057         * @return MimeMessage
1058         */
1059        protected MimeMessage getMessageIn() {
1060            return fieldMessageIn;
1061        }
1062    
1063        /**
1064         * Sets the messageIn.
1065         * 
1066         * @param messageIn
1067         *            The messageIn to set
1068         */
1069        protected void setMessageIn(MimeMessage messageIn) {
1070            fieldMessageIn = messageIn;
1071        }
1072    
1073        /**
1074         * Returns the localRecipient.
1075         * 
1076         * @return boolean
1077         */
1078        protected boolean isRemoteRecipient() {
1079            return fieldRemoteRecipient;
1080        }
1081    
1082        /**
1083         * Returns <code>boolean</code> indicating if the message to be delivered
1084         * was unprocessed in a previous delivery attempt.
1085         * 
1086         * @return boolean
1087         */
1088        protected boolean isPreviouslyUnprocessed() {
1089            return true;
1090        }
1091    
1092        /**
1093         * Log the status of the current message as INFO.
1094         * 
1095         * @param detailMsg
1096         */
1097        protected void logStatusInfo(String detailMsg) throws MessagingException {
1098            getLogger().info(getStatusReport(detailMsg).toString());
1099        }
1100    
1101        /**
1102         * Log the status the current message as WARN.
1103         * 
1104         * @param detailMsg
1105         */
1106        protected void logStatusWarn(String detailMsg) throws MessagingException {
1107            getLogger().warn(getStatusReport(detailMsg).toString());
1108        }
1109    
1110        /**
1111         * Log the status the current message as ERROR.
1112         * 
1113         * @param detailMsg
1114         */
1115        protected void logStatusError(String detailMsg) throws MessagingException {
1116            getLogger().error(getStatusReport(detailMsg).toString());
1117        }
1118    
1119        /**
1120         * Answer a <code>StringBuilder</code> containing a message reflecting the
1121         * current status of the message being processed.
1122         * 
1123         * @param detailMsg
1124         * @return StringBuilder
1125         */
1126        protected StringBuilder getStatusReport(String detailMsg) throws MessagingException {
1127            StringBuilder messageBuffer = new StringBuilder(detailMsg);
1128            if (detailMsg.length() > 0)
1129                messageBuffer.append(' ');
1130            messageBuffer.append("Message ID: ");
1131            messageBuffer.append(getMessageIn().getMessageID());
1132            messageBuffer.append(". Flags: Seen = ");
1133            messageBuffer.append(Boolean.valueOf(isMessageSeen()));
1134            messageBuffer.append(", Delete = ");
1135            messageBuffer.append(Boolean.valueOf(isMessageDeleted()));
1136            messageBuffer.append('.');
1137            return messageBuffer;
1138        }
1139    
1140        /**
1141         * Returns the userUndefined.
1142         * 
1143         * @return boolean
1144         */
1145        protected boolean isUserUndefined() {
1146            return fieldUserUndefined;
1147        }
1148    
1149        /**
1150         * Is the DELETED flag set?
1151         * 
1152         * @throws MessagingException
1153         */
1154        protected boolean isMessageDeleted() throws MessagingException {
1155            return getMessageIn().isSet(Flags.Flag.DELETED);
1156        }
1157    
1158        /**
1159         * Is the SEEN flag set?
1160         * 
1161         * @throws MessagingException
1162         */
1163        protected boolean isMessageSeen() throws MessagingException {
1164            return getMessageIn().isSet(Flags.Flag.SEEN);
1165        }
1166    
1167        /**
1168         * Set the DELETED flag.
1169         * 
1170         * @throws MessagingException
1171         */
1172        protected void setMessageDeleted() throws MessagingException {
1173            getMessageIn().setFlag(Flags.Flag.DELETED, true);
1174        }
1175    
1176        /**
1177         * Set the SEEN flag.
1178         * 
1179         * @throws MessagingException
1180         */
1181        protected void setMessageSeen() throws MessagingException {
1182            // If the Seen flag is not handled by the folder
1183            // allow a handler to do whatever it deems necessary
1184            if (!getMessageIn().getFolder().getPermanentFlags().contains(Flags.Flag.SEEN))
1185                handleMarkSeenNotPermanent();
1186            else
1187                getMessageIn().setFlag(Flags.Flag.SEEN, true);
1188        }
1189    
1190        /**
1191         * <p>
1192         * Handler for when the folder does not support the SEEN flag. The default
1193         * behaviour implemented here is to log a warning and set the flag anyway.
1194         * </p>
1195         * 
1196         * <p>
1197         * Subclasses may choose to override this and implement their own solutions.
1198         * </p>
1199         * 
1200         * @throws MessagingException
1201         */
1202        protected void handleMarkSeenNotPermanent() throws MessagingException {
1203            getMessageIn().setFlag(Flags.Flag.SEEN, true);
1204            logStatusWarn("Message marked as SEEN, but the folder does not support a permanent SEEN flag.");
1205        }
1206    
1207        /**
1208         * Returns the Blacklisted.
1209         * 
1210         * @return boolean
1211         */
1212        protected boolean isBlacklistedRecipient() {
1213            return fieldBlacklistedRecipient;
1214        }
1215    
1216        /**
1217         * Sets the localRecipient.
1218         * 
1219         * @param localRecipient
1220         *            The localRecipient to set
1221         */
1222        protected void setRemoteRecipient(boolean localRecipient) {
1223            fieldRemoteRecipient = localRecipient;
1224        }
1225    
1226        /**
1227         * Sets the userUndefined.
1228         * 
1229         * @param userUndefined
1230         *            The userUndefined to set
1231         */
1232        protected void setUserUndefined(boolean userUndefined) {
1233            fieldUserUndefined = userUndefined;
1234        }
1235    
1236        /**
1237         * Adds the mail attributes to a <code>Mail</code>.
1238         * 
1239         * @param aMail
1240         *            a Mail instance
1241         */
1242        protected void addMailAttributes(Mail aMail) throws MessagingException {
1243            aMail.setAttribute(getAttributePrefix() + "taskName", getFetchTaskName());
1244    
1245            aMail.setAttribute(getAttributePrefix() + "folderName", getMessageIn().getFolder().getFullName());
1246    
1247            if (isRemoteRecipient())
1248                aMail.setAttribute(getAttributePrefix() + "isRemoteRecipient", null);
1249    
1250            if (isUserUndefined())
1251                aMail.setAttribute(getAttributePrefix() + "isUserUndefined", true);
1252    
1253            if (isBlacklistedRecipient())
1254                aMail.setAttribute(getAttributePrefix() + "isBlacklistedRecipient", true);
1255    
1256            if (isRecipientNotFound())
1257                aMail.setAttribute(getAttributePrefix() + "isRecipientNotFound", true);
1258    
1259            if (isMaxMessageSizeExceeded().booleanValue())
1260                aMail.setAttribute(getAttributePrefix() + "isMaxMessageSizeExceeded", Integer.toString(getMessageIn().getSize()));
1261    
1262            if (isRemoteReceivedHeaderInvalid().booleanValue())
1263                aMail.setAttribute(getAttributePrefix() + "isRemoteReceivedHeaderInvalid", true);
1264    
1265            if (isDefaultSenderLocalPart())
1266                aMail.setAttribute(getAttributePrefix() + "isDefaultSenderLocalPart", true);
1267    
1268            if (isDefaultSenderDomainPart())
1269                aMail.setAttribute(getAttributePrefix() + "isDefaultSenderDomainPart", true);
1270    
1271            if (isDefaultRemoteAddress())
1272                aMail.setAttribute(getAttributePrefix() + "isDefaultRemoteAddress", true);
1273        }
1274    
1275        /**
1276         * Adds any required error messages to a <code>Mail</code>.
1277         * 
1278         * @param mail
1279         *            a Mail instance
1280         */
1281        protected void addErrorMessages(Mail mail) throws MessagingException {
1282            if (isMaxMessageSizeExceeded().booleanValue()) {
1283                StringBuilder msgBuffer = new StringBuilder("550 - Rejected - This message has been rejected as the message size of ");
1284                msgBuffer.append(getMessageIn().getSize() * 1000 / 1024 / 1000f);
1285                msgBuffer.append("KB exceeds the maximum permitted size of ");
1286                msgBuffer.append(getMaxMessageSizeLimit() / 1024);
1287                msgBuffer.append("KB.");
1288                mail.setErrorMessage(msgBuffer.toString());
1289            }
1290        }
1291    
1292        /**
1293         * Sets the Blacklisted.
1294         * 
1295         * @param blacklisted
1296         *            The blacklisted to set
1297         */
1298        protected void setBlacklistedRecipient(boolean blacklisted) {
1299            fieldBlacklistedRecipient = blacklisted;
1300        }
1301    
1302        /**
1303         * Returns the recipientNotFound.
1304         * 
1305         * @return boolean
1306         */
1307        protected boolean isRecipientNotFound() {
1308            return fieldRecipientNotFound;
1309        }
1310    
1311        /**
1312         * Sets the recipientNotFound.
1313         * 
1314         * @param recipientNotFound
1315         *            The recipientNotFound to set
1316         */
1317        protected void setRecipientNotFound(boolean recipientNotFound) {
1318            fieldRecipientNotFound = recipientNotFound;
1319        }
1320    
1321        /**
1322         * Returns the remoteDomain, lazily initialised as required.
1323         * 
1324         * @return String
1325         */
1326        protected String getRemoteDomain() throws MessagingException {
1327            String remoteDomain;
1328            if (null == (remoteDomain = getRemoteDomainBasic())) {
1329                updateRemoteDomain();
1330                return getRemoteDomain();
1331            }
1332            return remoteDomain;
1333        }
1334    
1335        /**
1336         * Returns the remoteDomain.
1337         * 
1338         * @return String
1339         */
1340        private String getRemoteDomainBasic() {
1341            return fieldRemoteDomain;
1342        }
1343    
1344        /**
1345         * Sets the remoteDomain.
1346         * 
1347         * @param remoteDomain
1348         *            The remoteDomain to set
1349         */
1350        protected void setRemoteDomain(String remoteDomain) {
1351            fieldRemoteDomain = remoteDomain;
1352        }
1353    
1354        /**
1355         * Updates the remoteDomain.
1356         */
1357        protected void updateRemoteDomain() throws MessagingException {
1358            setRemoteDomain(computeRemoteDomain());
1359        }
1360    
1361        /**
1362         * Answer the IP Address of the remote server for the message being
1363         * processed.
1364         * 
1365         * @return String
1366         * @throws MessagingException
1367         * @throws UnknownHostException
1368         */
1369        protected String computeRemoteAddress() throws MessagingException, UnknownHostException {
1370            String domain = getRemoteDomain();
1371            String address = null;
1372            String validatedAddress = null;
1373            int ipAddressStart = domain.indexOf('[');
1374            int ipAddressEnd = -1;
1375    
1376            if (ipAddressStart > -1) {
1377                ipAddressEnd = domain.indexOf(']', ipAddressStart);
1378            } else {
1379                // Handle () which enclose the ipaddress
1380                // See JAMES-344
1381                ipAddressStart = domain.indexOf('(');
1382                if (ipAddressStart > -1) {
1383                    ipAddressEnd = domain.indexOf(')', ipAddressStart);
1384                }
1385            }
1386            if (ipAddressEnd > -1) {
1387                address = domain.substring(ipAddressStart + 1, ipAddressEnd);
1388            } else {
1389                int hostNameEnd = domain.indexOf(' ');
1390                if (hostNameEnd == -1)
1391                    hostNameEnd = domain.length();
1392                address = domain.substring(0, hostNameEnd);
1393            }
1394            validatedAddress = getDNSServer().getByName(address).getHostAddress();
1395    
1396            return validatedAddress;
1397        }
1398    
1399        /**
1400         * Answer the Canonical host name of the remote server for the message being
1401         * processed.
1402         * 
1403         * @return String
1404         * @throws MessagingException
1405         * @throws UnknownHostException
1406         */
1407        protected String computeRemoteHostName() throws MessagingException, UnknownHostException {
1408            return getDNSServer().getHostName(getDNSServer().getByName(getRemoteAddress()));
1409        }
1410    
1411        /**
1412         * Returns the remoteAddress, lazily initialised as required.
1413         * 
1414         * @return String
1415         */
1416        protected String getRemoteAddress() throws MessagingException, UnknownHostException {
1417            String remoteAddress;
1418            if (null == (remoteAddress = getRemoteAddressBasic())) {
1419                updateRemoteAddress();
1420                return getRemoteAddress();
1421            }
1422            return remoteAddress;
1423        }
1424    
1425        /**
1426         * Returns the remoteAddress.
1427         * 
1428         * @return String
1429         */
1430        private String getRemoteAddressBasic() {
1431            return fieldRemoteAddress;
1432        }
1433    
1434        /**
1435         * Returns the remoteHostName, lazily initialised as required.
1436         * 
1437         * @return String
1438         */
1439        protected String getRemoteHostName() throws MessagingException, UnknownHostException {
1440            String remoteHostName;
1441            if (null == (remoteHostName = getRemoteHostNameBasic())) {
1442                updateRemoteHostName();
1443                return getRemoteHostName();
1444            }
1445            return remoteHostName;
1446        }
1447    
1448        /**
1449         * Returns the remoteHostName.
1450         * 
1451         * @return String
1452         */
1453        private String getRemoteHostNameBasic() {
1454            return fieldRemoteHostName;
1455        }
1456    
1457        /**
1458         * Sets the remoteAddress.
1459         * 
1460         * @param remoteAddress
1461         *            The remoteAddress to set
1462         */
1463        protected void setRemoteAddress(String remoteAddress) {
1464            fieldRemoteAddress = remoteAddress;
1465        }
1466    
1467        /**
1468         * Updates the remoteAddress.
1469         */
1470        protected void updateRemoteAddress() throws MessagingException, UnknownHostException {
1471            setRemoteAddress(computeRemoteAddress());
1472        }
1473    
1474        /**
1475         * Sets the remoteHostName.
1476         * 
1477         * @param remoteHostName
1478         *            The remoteHostName to set
1479         */
1480        protected void setRemoteHostName(String remoteHostName) {
1481            fieldRemoteHostName = remoteHostName;
1482        }
1483    
1484        /**
1485         * Updates the remoteHostName.
1486         */
1487        protected void updateRemoteHostName() throws MessagingException, UnknownHostException {
1488            setRemoteHostName(computeRemoteHostName());
1489        }
1490    
1491        /**
1492         * Returns the rFC2822RECEIVEDHeaderFields.
1493         * 
1494         * @return String
1495         */
1496        public static String getRFC2822RECEIVEDHeaderFields() {
1497            return fieldRFC2822RECEIVEDHeaderFields;
1498        }
1499    
1500        /**
1501         * Returns the maxMessageSizeExceeded, lazily initialised as required.
1502         * 
1503         * @return Boolean
1504         */
1505        protected Boolean isMaxMessageSizeExceeded() throws MessagingException {
1506            Boolean isMaxMessageSizeExceeded = null;
1507            if (null == (isMaxMessageSizeExceeded = isMaxMessageSizeExceededBasic())) {
1508                updateMaxMessageSizeExceeded();
1509                return isMaxMessageSizeExceeded();
1510            }
1511            return isMaxMessageSizeExceeded;
1512        }
1513    
1514        /**
1515         * Refreshes the maxMessageSizeExceeded.
1516         */
1517        protected void updateMaxMessageSizeExceeded() throws MessagingException {
1518            setMaxMessageSizeExceeded(computeMaxMessageSizeExceeded());
1519        }
1520    
1521        /**
1522         * Compute the maxMessageSizeExceeded.
1523         * 
1524         * @return Boolean
1525         */
1526        protected Boolean computeMaxMessageSizeExceeded() throws MessagingException {
1527            if (0 == getMaxMessageSizeLimit())
1528                return Boolean.FALSE;
1529            return Boolean.valueOf(getMessageIn().getSize() > getMaxMessageSizeLimit());
1530        }
1531    
1532        /**
1533         * Returns the maxMessageSizeExceeded.
1534         * 
1535         * @return Boolean
1536         */
1537        private Boolean isMaxMessageSizeExceededBasic() {
1538            return fieldMaxMessageSizeExceeded;
1539        }
1540    
1541        /**
1542         * Sets the maxMessageSizeExceeded.
1543         * 
1544         * @param maxMessageSizeExceeded
1545         *            The maxMessageSizeExceeded to set
1546         */
1547        protected void setMaxMessageSizeExceeded(Boolean maxMessageSizeExceeded) {
1548            fieldMaxMessageSizeExceeded = maxMessageSizeExceeded;
1549        }
1550    
1551        /**
1552         * Returns the remoteReceivedHeaderInvalid, lazily initialised.
1553         * 
1554         * @return Boolean
1555         */
1556        protected Boolean isRemoteReceivedHeaderInvalid() throws MessagingException {
1557            Boolean isInvalid = null;
1558            if (null == (isInvalid = isRemoteReceivedHeaderInvalidBasic())) {
1559                updateRemoteReceivedHeaderInvalid();
1560                return isRemoteReceivedHeaderInvalid();
1561            }
1562            return isInvalid;
1563        }
1564    
1565        /**
1566         * Computes the remoteReceivedHeaderInvalid.
1567         * 
1568         * @return Boolean
1569         */
1570        protected Boolean computeRemoteReceivedHeaderInvalid() throws MessagingException {
1571            Boolean isInvalid = Boolean.FALSE;
1572            try {
1573                getRemoteAddress();
1574            } catch (UnknownHostException e) {
1575                isInvalid = Boolean.TRUE;
1576            }
1577            return isInvalid;
1578        }
1579    
1580        /**
1581         * Returns the remoteReceivedHeaderInvalid.
1582         * 
1583         * @return Boolean
1584         */
1585        private Boolean isRemoteReceivedHeaderInvalidBasic() {
1586            return fieldRemoteReceivedHeaderInvalid;
1587        }
1588    
1589        /**
1590         * Sets the remoteReceivedHeaderInvalid.
1591         * 
1592         * @param remoteReceivedHeaderInvalid
1593         *            The remoteReceivedHeaderInvalid to set
1594         */
1595        protected void setRemoteReceivedHeaderInvalid(Boolean remoteReceivedHeaderInvalid) {
1596            fieldRemoteReceivedHeaderInvalid = remoteReceivedHeaderInvalid;
1597        }
1598    
1599        /**
1600         * Updates the remoteReceivedHeaderInvalid.
1601         */
1602        protected void updateRemoteReceivedHeaderInvalid() throws MessagingException {
1603            setRemoteReceivedHeaderInvalid(computeRemoteReceivedHeaderInvalid());
1604        }
1605    
1606        /**
1607         * Returns the defaultSenderDomainPart.
1608         * 
1609         * @return boolean
1610         */
1611        protected boolean isDefaultSenderDomainPart() {
1612            return fieldDefaultSenderDomainPart;
1613        }
1614    
1615        /**
1616         * Returns the defaultSenderLocalPart.
1617         * 
1618         * @return boolean
1619         */
1620        protected boolean isDefaultSenderLocalPart() {
1621            return fieldDefaultSenderLocalPart;
1622        }
1623    
1624        /**
1625         * Sets the defaultSenderDomainPart.
1626         * 
1627         * @param defaultSenderDomainPart
1628         *            The defaultSenderDomainPart to set
1629         */
1630        protected void setDefaultSenderDomainPart(boolean defaultSenderDomainPart) {
1631            fieldDefaultSenderDomainPart = defaultSenderDomainPart;
1632        }
1633    
1634        /**
1635         * Sets the defaultSenderLocalPart.
1636         * 
1637         * @param defaultSenderLocalPart
1638         *            The defaultSenderLocalPart to set
1639         */
1640        protected void setDefaultSenderLocalPart(boolean defaultSenderLocalPart) {
1641            fieldDefaultSenderLocalPart = defaultSenderLocalPart;
1642        }
1643    
1644        /**
1645         * Returns the defaultRemoteAddress.
1646         * 
1647         * @return boolean
1648         */
1649        protected boolean isDefaultRemoteAddress() {
1650            return fieldDefaultRemoteAddress;
1651        }
1652    
1653        /**
1654         * Sets the defaultRemoteAddress.
1655         * 
1656         * @param defaultRemoteAddress
1657         *            The defaultRemoteAddress to set
1658         */
1659        protected void setDefaultRemoteAddress(boolean defaultRemoteAddress) {
1660            fieldDefaultRemoteAddress = defaultRemoteAddress;
1661        }
1662    
1663    }