View Javadoc

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