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