View Javadoc

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