KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > james > transport > mailets > AbstractRedirect


1 /***********************************************************************
2  * Copyright (c) 2000-2004 The Apache Software Foundation. *
3  * All rights reserved. *
4  * ------------------------------------------------------------------- *
5  * Licensed under the Apache License, Version 2.0 (the "License"); you *
6  * may not use this file except in compliance with the License. You *
7  * may obtain a copy of the License at: *
8  * *
9  * http://www.apache.org/licenses/LICENSE-2.0 *
10  * *
11  * Unless required by applicable law or agreed to in writing, software *
12  * distributed under the License is distributed on an "AS IS" BASIS, *
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or *
14  * implied. See the License for the specific language governing *
15  * permissions and limitations under the License. *
16  ***********************************************************************/

17
18 package org.apache.james.transport.mailets;
19
20 import java.io.PrintWriter JavaDoc;
21 import java.io.StringWriter JavaDoc;
22
23 import java.util.Collection JavaDoc;
24 import java.util.Date JavaDoc;
25 import java.util.Enumeration JavaDoc;
26 import java.util.HashSet JavaDoc;
27 import java.util.Iterator JavaDoc;
28 import java.util.Locale JavaDoc;
29 import java.util.ArrayList JavaDoc;
30
31
32 import javax.mail.Message JavaDoc;
33 import javax.mail.MessagingException JavaDoc;
34 import javax.mail.internet.ParseException JavaDoc;
35 import javax.mail.Session JavaDoc;
36 import javax.mail.internet.InternetAddress JavaDoc;
37 import javax.mail.internet.MimeBodyPart JavaDoc;
38 import javax.mail.internet.MimeMessage JavaDoc;
39 import javax.mail.internet.MimeMultipart JavaDoc;
40 import javax.mail.internet.AddressException JavaDoc;
41
42 import org.apache.james.util.RFC2822Headers;
43 import org.apache.james.util.RFC822DateFormat;
44 import org.apache.james.core.MailImpl;
45
46 import org.apache.mailet.GenericMailet;
47 import org.apache.mailet.Mail;
48 import org.apache.mailet.MailAddress;
49
50
51 /**
52  * <P>Abstract mailet providing configurable redirection services.<BR>
53  * This mailet can be subclassed to make authoring redirection mailets simple.<BR>
54  * By extending it and overriding one or more of these methods new behaviour can
55  * be quickly created without the author having to address any other issue than
56  * the relevant one:</P>
57  * <UL>
58  * <LI>attachError() , should error messages be appended to the message</LI>
59  * <LI>getAttachmentType(), what should be attached to the message</LI>
60  * <LI>getInLineType(), what should be included in the message</LI>
61  * <LI>getMessage(), The text of the message itself</LI>
62  * <LI>getRecipients(), the recipients the mail is sent to</LI>
63  * <LI>getReplyTo(), where replies to this message will be sent</LI>
64  * <LI>getReversePath(), what to set the reverse-path to</LI>
65  * <LI>getSender(), who the mail is from</LI>
66  * <LI>getSubject(), a string to replace the message subject</LI>
67  * <LI>getSubjectPrefix(), a prefix to be added to the message subject, possibly already replaced by a new subject</LI>
68  * <LI>getTo(), a list of people to whom the mail is *apparently* sent</LI>
69  * <LI>isReply(), should this mailet set the IN_REPLY_TO header to the id of the current message</LI>
70  * <LI>getPassThrough(), should this mailet allow the original message to continue processing or GHOST it.</LI>
71  * <LI>getFakeDomainCheck(), should this mailet check if the sender domain address is valid.</LI>
72  * <LI>isStatic(), should this mailet run the get methods for every mail, or just once.</LI>
73  * </UL>
74  * <P>For each of the methods above (generically called "getX()" methods in this class
75  * and its subclasses), there is an associated "getX(Mail)" method and most times
76  * a "setX(Mail, Tx, Mail)" method.<BR>
77  * The roles are the following:</P>
78  * <UL>
79  * <LI>a "getX()" method returns the correspondent "X" value that can be evaluated "statically"
80  * once at init time and then stored in a variable and made available for later use by a
81  * "getX(Mail)" method;</LI>
82  * <LI>a "getX(Mail)" method is the one called to return the correspondent "X" value
83  * that can be evaluated "dynamically", tipically based on the currently serviced mail;
84  * the default behaviour is to return the value of getX();</LI>
85  * <LI>a "setX(Mail, Tx, Mail)" method is called to change the correspondent "X" value
86  * of the redirected Mail object, using the value returned by "gexX(Mail)";
87  * if such value is null, it does nothing.</LI>
88  * </UL>
89  * <P>Here follows the typical pattern of those methods:</P>
90  * <PRE><CODE>
91  * ...
92  * Tx x;
93  * ...
94  * protected boolean getX(Mail originalMail) throws MessagingException {
95  * boolean x = (isStatic()) ? this.x : getX();
96  * ...
97  * return x;
98  * }
99  * ...
100  * public void init() throws MessagingException {
101  * ...
102  * isStatic = (getInitParameter("static") == null) ? false : new Boolean(getInitParameter("static")).booleanValue();
103  * if(isStatic()) {
104  * ...
105  * X = getX();
106  * ...
107  * }
108  * ...
109  * public void service(Mail originalMail) throws MessagingException {
110  * ...
111  * setX(newMail, getX(originalMail), originalMail);
112  * ...
113  * }
114  * ...
115  * </CODE></PRE>
116  * <P>The <I>isStatic</I> variable and method is used to allow for the situations
117  * (deprecated since version 2.2, but possibly used by previoulsy written extensions
118  * to {@link Redirect}) in which the getX() methods are non static: in this case
119  * {@link #isStatic()} must return false.<BR>
120  * Finally, a "getX()" method may return a "special address" (see {@link SpecialAddress}),
121  * that later will be resolved ("late bound") by a "getX(Mail)" or "setX(Mail, Tx, Mail)":
122  * it is a dynamic value that does not require <CODE>isStatic</CODE> to be false.</P>
123  *
124  * <P>Supports by default the <CODE>passThrough</CODE> init parameter (false if missing).
125  * Subclasses can override this behaviour overriding {@link #getPassThrough()}.</P>
126  *
127  * @version CVS $Revision: 1.1.2.21 $ $Date: 2004/03/15 03:54:19 $
128  * @since 2.2.0
129  */

130
131 public abstract class AbstractRedirect extends GenericMailet {
132     
133     /**
134      * Gets the expected init parameters.
135      *
136      * @return null meaning no check
137      */

138     protected String JavaDoc[] getAllowedInitParameters() {
139         return null;
140     }
141     
142     /**
143      * Controls certain log messages.
144      */

145     protected boolean isDebug = false;
146
147     /**
148      * Holds the value of the <CODE>static</CODE> init parameter.
149      */

150     protected boolean isStatic = false;
151
152     private static class AddressMarker {
153         public static MailAddress SENDER;
154         public static MailAddress REVERSE_PATH;
155         public static MailAddress FROM;
156         public static MailAddress REPLY_TO;
157         public static MailAddress TO;
158         public static MailAddress RECIPIENTS;
159         public static MailAddress DELETE;
160         public static MailAddress UNALTERED;
161         public static MailAddress NULL;
162
163         static {
164             try {
165                 SENDER = new MailAddress("sender","address.marker");
166                 REVERSE_PATH = new MailAddress("reverse.path","address.marker");
167                 FROM = new MailAddress("from","address.marker");
168                 REPLY_TO = new MailAddress("reply.to","address.marker");
169                 TO = new MailAddress("to","address.marker");
170                 RECIPIENTS = new MailAddress("recipients","address.marker");
171                 DELETE = new MailAddress("delete","address.marker");
172                 UNALTERED = new MailAddress("unaltered","address.marker");
173                 NULL = new MailAddress("null","address.marker");
174
175             } catch (Exception JavaDoc _) {}
176         }
177     }
178
179     /**
180      * Class containing "special addresses" constants.
181      * Such addresses mean dynamic values that later will be resolved ("late bound")
182      * by a "getX(Mail)" or "setX(Mail, Tx, Mail)".
183      */

184     protected static class SpecialAddress {
185         public static final MailAddress SENDER = AddressMarker.SENDER;
186         public static final MailAddress REVERSE_PATH = AddressMarker.REVERSE_PATH;
187         public static final MailAddress FROM = AddressMarker.FROM;
188         public static final MailAddress REPLY_TO = AddressMarker.REPLY_TO;
189         public static final MailAddress TO = AddressMarker.TO;
190         public static final MailAddress RECIPIENTS = AddressMarker.RECIPIENTS;
191         public static final MailAddress DELETE = AddressMarker.DELETE;
192         public static final MailAddress UNALTERED = AddressMarker.UNALTERED;
193         public static final MailAddress NULL = AddressMarker.NULL;
194     }
195
196     // The values that indicate how to attach the original mail
197
// to the new mail.
198

199     protected static final int UNALTERED = 0;
200
201     protected static final int HEADS = 1;
202
203     protected static final int BODY = 2;
204
205     protected static final int ALL = 3;
206
207     protected static final int NONE = 4;
208
209     protected static final int MESSAGE = 5;
210
211     private boolean passThrough = false;
212     private boolean fakeDomainCheck = true;
213     private int attachmentType = NONE;
214     private int inLineType = BODY;
215     private String JavaDoc messageText;
216     private Collection JavaDoc recipients;
217     private MailAddress replyTo;
218     private MailAddress reversePath;
219     private MailAddress sender;
220     private String JavaDoc subject;
221     private String JavaDoc subjectPrefix;
222     private InternetAddress JavaDoc[] apparentlyTo;
223     private boolean attachError = false;
224     private boolean isReply = false;
225
226     private RFC822DateFormat rfc822DateFormat = new RFC822DateFormat();
227
228     /* ******************************************************************** */
229     /* ****************** Begin of getX and setX methods ****************** */
230     /* ******************************************************************** */
231
232     /**
233      * <P>Gets the <CODE>static</CODE> property.</P>
234      * <P>Return true to reduce calls to getTo, getSender, getRecipients, getReplyTo, getReversePath amd getMessage
235      * where these values don't change (eg hard coded, or got at startup from the mailet config);
236      * return false where any of these methods generate their results dynamically eg in response to the message being processed,
237      * or by reference to a repository of users.</P>
238      * <P>It is now (from version 2.2) somehow obsolete, as should be always true because the "good practice"
239      * is to use "getX()" methods statically, and use instead "getX(Mail)" methods for dynamic situations.
240      * A false value is now meaningful only for subclasses of {@link Redirect} older than version 2.2
241      * that were relying on this.</P>
242      *
243      * <P>Is a "getX()" method.</P>
244      *
245      * @return true, as normally "getX()" methods shouls be static
246      */

247     protected boolean isStatic() {
248         return true;
249     }
250
251     /**
252      * Gets the <CODE>passThrough</CODE> property.
253      * Return true to allow the original message to continue through the processor, false to GHOST it.
254      * Is a "getX()" method.
255      *
256      * @return the <CODE>passThrough</CODE> init parameter, or false if missing
257      */

258     protected boolean getPassThrough() throws MessagingException JavaDoc {
259         return new Boolean JavaDoc(getInitParameter("passThrough")).booleanValue();
260     }
261
262     /**
263      * Gets the <CODE>passThrough</CODE> property,
264      * built dynamically using the original Mail object.
265      * Is a "getX(Mail)" method.
266      *
267      * @return {@link #getPassThrough()}
268      */

269     protected boolean getPassThrough(Mail originalMail) throws MessagingException JavaDoc {
270         boolean passThrough = (isStatic()) ? this.passThrough : getPassThrough();
271         return passThrough;
272     }
273
274     /**
275      * Gets the <CODE>fakeDomainCheck</CODE> property.
276      * Return true to check if the sender domain is valid.
277      * Is a "getX()" method.
278      *
279      * @return the <CODE>fakeDomainCheck</CODE> init parameter, or true if missing
280      */

281     protected boolean getFakeDomainCheck() throws MessagingException JavaDoc {
282         return new Boolean JavaDoc(getInitParameter("fakeDomainCheck")).booleanValue();
283     }
284
285     /**
286      * Gets the <CODE>fakeDomainCheck</CODE> property,
287      * built dynamically using the original Mail object.
288      * Is a "getX(Mail)" method.
289      *
290      * @return {@link #getFakeDomainCheck()}
291      */

292     protected boolean getFakeDomainCheck(Mail originalMail) throws MessagingException JavaDoc {
293         boolean fakeDomainCheck = (isStatic()) ? this.fakeDomainCheck : getFakeDomainCheck();
294         return fakeDomainCheck;
295     }
296
297     /**
298      * Gets the <CODE>inline</CODE> property.
299      * May return one of the following values to indicate how to append the original message
300      * to build the new message:
301      * <ul>
302      * <li><CODE>UNALTERED</CODE> : original message is the new message body</li>
303      * <li><CODE>BODY</CODE> : original message body is appended to the new message</li>
304      * <li><CODE>HEADS</CODE> : original message headers are appended to the new message</li>
305      * <li><CODE>ALL</CODE> : original is appended with all headers</li>
306      * <li><CODE>NONE</CODE> : original is not appended</li>
307      * </ul>
308      * Is a "getX()" method.
309      *
310      * @return the <CODE>inline</CODE> init parameter, or <CODE>UNALTERED</CODE> if missing
311      */

312     protected int getInLineType() throws MessagingException JavaDoc {
313         if(getInitParameter("inline") == null) {
314             return UNALTERED;
315         } else {
316             return getTypeCode(getInitParameter("inline"));
317         }
318     }
319     
320     /**
321      * Gets the <CODE>inline</CODE> property,
322      * built dynamically using the original Mail object.
323      * Is a "getX(Mail)" method.
324      *
325      * @return {@link #getInLineType()}
326      */

327     protected int getInLineType(Mail originalMail) throws MessagingException JavaDoc {
328         int inLineType = (isStatic()) ? this.inLineType : getInLineType();
329         return inLineType;
330     }
331
332     /** Gets the <CODE>attachment</CODE> property.
333      * May return one of the following values to indicate how to attach the original message
334      * to the new message:
335      * <ul>
336      * <li><CODE>BODY</CODE> : original message body is attached as plain text to the new message</li>
337      * <li><CODE>HEADS</CODE> : original message headers are attached as plain text to the new message</li>
338      * <li><CODE>ALL</CODE> : original is attached as plain text with all headers</li>
339      * <li><CODE>MESSAGE</CODE> : original message is attached as type message/rfc822, a complete mail message.</li>
340      * <li><CODE>NONE</CODE> : original is not attached</li>
341      * </ul>
342      * Is a "getX()" method.
343      *
344      * @return the <CODE>attachment</CODE> init parameter, or <CODE>NONE</CODE> if missing
345      */

346     protected int getAttachmentType() throws MessagingException JavaDoc {
347         if(getInitParameter("attachment") == null) {
348             return NONE;
349         } else {
350             return getTypeCode(getInitParameter("attachment"));
351         }
352     }
353
354     /**
355      * Gets the <CODE>attachment</CODE> property,
356      * built dynamically using the original Mail object.
357      * Is a "getX(Mail)" method.
358      *
359      * @return {@link #getAttachmentType()}
360      */

361     protected int getAttachmentType(Mail originalMail) throws MessagingException JavaDoc {
362         int attachmentType = (isStatic()) ? this.attachmentType : getAttachmentType();
363         return attachmentType;
364     }
365
366     /**
367      * Gets the <CODE>message</CODE> property.
368      * Returns a message to which the original message can be attached/appended
369      * to build the new message.
370      * Is a "getX()" method.
371      *
372      * @return the <CODE>message</CODE> init parameter or an empty string if missing
373      */

374     protected String JavaDoc getMessage() throws MessagingException JavaDoc {
375         if(getInitParameter("message") == null) {
376             return "";
377         } else {
378             return getInitParameter("message");
379         }
380     }
381
382     /**
383      * Gets the <CODE>message</CODE> property,
384      * built dynamically using the original Mail object.
385      * Is a "getX(Mail)" method.
386      *
387      * @return {@link #getMessage()}
388      */

389     protected String JavaDoc getMessage(Mail originalMail) throws MessagingException JavaDoc {
390         String JavaDoc messageText = (isStatic()) ? this.messageText : getMessage();
391         return messageText;
392     }
393
394     /**
395      * Gets the <CODE>recipients</CODE> property.
396      * Returns the collection of recipients of the new message,
397      * or null if no change is requested.
398      * Is a "getX()" method.
399      *
400      * @return the <CODE>recipients</CODE> init parameter
401      * or the postmaster address
402      * or <CODE>SpecialAddress.SENDER</CODE>
403      * or <CODE>SpecialAddress.FROM</CODE>
404      * or <CODE>SpecialAddress.REPLY_TO</CODE>
405      * or <CODE>SpecialAddress.REVERSE_PATH</CODE>
406      * or <CODE>SpecialAddress.UNALTERED</CODE>
407      * or <CODE>SpecialAddress.RECIPIENTS</CODE>
408      * or <CODE>null</CODE> if missing
409      */

410     protected Collection JavaDoc getRecipients() throws MessagingException JavaDoc {
411         Collection JavaDoc newRecipients = new HashSet JavaDoc();
412         String JavaDoc addressList = getInitParameter("recipients");
413         
414         // if nothing was specified, return <CODE>null</CODE> meaning no change
415
if (addressList == null) {
416             return null;
417         }
418
419         try {
420             InternetAddress JavaDoc[] iaarray = InternetAddress.parse(addressList, false);
421             for (int i = 0; i < iaarray.length; i++) {
422                 String JavaDoc addressString = iaarray[i].getAddress();
423                 MailAddress specialAddress = getSpecialAddress(addressString,
424                 new String JavaDoc[] {"postmaster", "sender", "from", "replyTo", "reversePath", "unaltered", "recipients", "to", "null"});
425                 if (specialAddress != null) {
426                     newRecipients.add(specialAddress);
427                 } else {
428                     newRecipients.add(new MailAddress(iaarray[i]));
429                 }
430             }
431         } catch (Exception JavaDoc e) {
432             throw new MessagingException JavaDoc("Exception thrown in getRecipients() parsing: " + addressList, e);
433         }
434         if (newRecipients.size() == 0) {
435             throw new MessagingException JavaDoc("Failed to initialize \"recipients\" list; empty <recipients> init parameter found.");
436         }
437
438         return newRecipients;
439     }
440
441     /**
442      * Gets the <CODE>recipients</CODE> property,
443      * built dynamically using the original Mail object.
444      * Is a "getX(Mail)" method.
445      *
446      * @return {@link #replaceMailAddresses} on {@link #getRecipients()},
447      */

448     protected Collection JavaDoc getRecipients(Mail originalMail) throws MessagingException JavaDoc {
449         Collection JavaDoc recipients = (isStatic()) ? this.recipients : getRecipients();
450         if (recipients != null) {
451             if (recipients.size() == 1 && (recipients.contains(SpecialAddress.UNALTERED) || recipients.contains(SpecialAddress.RECIPIENTS))) {
452                 recipients = null;
453             } else {
454                 recipients = replaceMailAddresses(originalMail, recipients);
455             }
456         }
457         return recipients;
458     }
459
460     /**
461      * Sets the recipients of <I>newMail</I> to <I>recipients</I>.
462      * If the requested value is null does nothing.
463      * Is a "setX(Mail, Tx, Mail)" method.
464      */

465     protected void setRecipients(Mail newMail, Collection JavaDoc recipients, Mail originalMail) throws MessagingException JavaDoc {
466         if (recipients != null) {
467             ((MailImpl) newMail).setRecipients(recipients);
468             if (isDebug) {
469                 log("recipients set to: " + arrayToString(recipients.toArray()));
470             }
471         }
472     }
473
474     /**
475      * Gets the <CODE>to</CODE> property.
476      * Returns the "To:" recipients of the new message.
477      * or null if no change is requested.
478      * Is a "getX()" method.
479      *
480      * @return the <CODE>to</CODE> init parameter
481      * or the postmaster address
482      * or <CODE>SpecialAddress.SENDER</CODE>
483      * or <CODE>SpecialAddress.REVERSE_PATH</CODE>
484      * or <CODE>SpecialAddress.FROM</CODE>
485      * or <CODE>SpecialAddress.REPLY_TO</CODE>
486      * or <CODE>SpecialAddress.UNALTERED</CODE>
487      * or <CODE>SpecialAddress.TO</CODE>
488      * or <CODE>null</CODE> if missing
489      */

490     protected InternetAddress JavaDoc[] getTo() throws MessagingException JavaDoc {
491         InternetAddress JavaDoc[] iaarray = null;
492         String JavaDoc addressList = getInitParameter("to");
493         
494         // if nothing was specified, return null meaning no change
495
if (addressList == null) {
496             return null;
497         }
498
499         try {
500             iaarray = InternetAddress.parse(addressList, false);
501             for(int i = 0; i < iaarray.length; ++i) {
502                 String JavaDoc addressString = iaarray[i].getAddress();
503                 MailAddress specialAddress = getSpecialAddress(addressString,
504                                                 new String JavaDoc[] {"postmaster", "sender", "from", "replyTo", "reversePath", "unaltered", "recipients", "to", "null"});
505                 if (specialAddress != null) {
506                     iaarray[i] = specialAddress.toInternetAddress();
507                 }
508             }
509         } catch (Exception JavaDoc e) {
510             throw new MessagingException JavaDoc("Exception thrown in getTo() parsing: " + addressList, e);
511         }
512         if (iaarray.length == 0) {
513             throw new MessagingException JavaDoc("Failed to initialize \"to\" list; empty <to> init parameter found.");
514         }
515
516         return iaarray;
517     }
518
519     /**
520      * Gets the <CODE>to</CODE> property,
521      * built dynamically using the original Mail object.
522      * Its outcome will be the the value the <I>TO:</I> header will be set to,
523      * that could be different from the real recipient (see {@link #getRecipients}).
524      * Is a "getX(Mail)" method.
525      *
526      * @return {@link #replaceInternetAddresses} on {@link #getRecipients()},
527      */

528     protected InternetAddress JavaDoc[] getTo(Mail originalMail) throws MessagingException JavaDoc {
529         InternetAddress JavaDoc[] apparentlyTo = (isStatic()) ? this.apparentlyTo : getTo();
530         if (apparentlyTo != null) {
531             if ( apparentlyTo.length == 1
532                 && ( apparentlyTo[0].equals(SpecialAddress.UNALTERED.toInternetAddress())
533                     || apparentlyTo[0].equals(SpecialAddress.TO.toInternetAddress())
534                     )) {
535                 apparentlyTo = null;
536             } else {
537                 Collection JavaDoc toList = new ArrayList JavaDoc(apparentlyTo.length);
538                 for (int i = 0; i < apparentlyTo.length; i++) {
539                     toList.add(apparentlyTo[i]);
540                 }
541                 /* IMPORTANT: setTo() treats null differently from a zero length array,
542                   so it's ok to get a zero length array from replaceSpecialAddresses
543                  */

544                 apparentlyTo = (InternetAddress JavaDoc[]) replaceInternetAddresses(originalMail, toList).toArray(new InternetAddress JavaDoc[0]);
545             }
546         }
547         
548         return apparentlyTo;
549     }
550
551     /**
552      * Sets the "To:" header of <I>newMail</I> to <I>to</I>.
553      * If the requested value is null does nothing.
554      * Is a "setX(Mail, Tx, Mail)" method.
555      */

556     protected void setTo(Mail newMail, InternetAddress JavaDoc[] to, Mail originalMail) throws MessagingException JavaDoc {
557         if (to != null) {
558             newMail.getMessage().setRecipients(Message.RecipientType.TO, to);
559             if (isDebug) {
560                 log("apparentlyTo set to: " + arrayToString(to));
561             }
562         }
563     }
564
565     /**
566      * Gets the <CODE>replyto</CODE> property.
567      * Returns the Reply-To address of the new message,
568      * or null if no change is requested.
569      * Is a "getX()" method.
570      *
571      * @return the <CODE>replyto</CODE> init parameter
572      * or the postmaster address
573      * or <CODE>SpecialAddress.SENDER</CODE>
574      * or <CODE>SpecialAddress.UNALTERED</CODE>
575      * or <CODE>SpecialAddress.NULL</CODE>
576      * or <CODE>null</CODE> if missing
577      */

578     protected MailAddress getReplyTo() throws MessagingException JavaDoc {
579         String JavaDoc addressString = getInitParameter("replyTo");
580         if (addressString == null) {
581             addressString = getInitParameter("replyto");
582         }
583         if(addressString != null) {
584             MailAddress specialAddress = getSpecialAddress(addressString,
585                                             new String JavaDoc[] {"postmaster", "sender", "null", "unaltered"});
586             if (specialAddress != null) {
587                 return specialAddress;
588             }
589
590             try {
591                 return new MailAddress(addressString);
592             } catch(Exception JavaDoc e) {
593                 throw new MessagingException JavaDoc("Exception thrown in getReplyTo() parsing: " + addressString, e);
594             }
595         }
596
597         return null;
598     }
599
600     /**
601      * Gets the <CODE>replyTo</CODE> property,
602      * built dynamically using the original Mail object.
603      * Is a "getX(Mail)" method.
604      *
605      * @return {@link #getReplyTo()}
606      * replacing <CODE>SpecialAddress.UNALTERED</CODE> if applicable with null
607      * and <CODE>SpecialAddress.SENDER</CODE> with the original mail sender
608      */

609     protected MailAddress getReplyTo(Mail originalMail) throws MessagingException JavaDoc {
610         MailAddress replyTo = (isStatic()) ? this.replyTo : getReplyTo();
611         if (replyTo != null) {
612             if (replyTo == SpecialAddress.UNALTERED) {
613                 replyTo = null;
614             } else if (replyTo == SpecialAddress.SENDER) {
615                 replyTo = originalMail.getSender();
616             }
617         }
618         return replyTo;
619     }
620
621     /**
622      * <P>Sets the "Reply-To:" header of <I>newMail</I> to <I>replyTo</I>.</P>
623      * If the requested value is <CODE>SpecialAddress.NULL</CODE> will remove the "Reply-To:" header.
624      * If the requested value is null does nothing.</P>
625      * Is a "setX(Mail, Tx, Mail)" method.
626      */

627     protected void setReplyTo(Mail newMail, MailAddress replyTo, Mail originalMail) throws MessagingException JavaDoc {
628         if(replyTo != null) {
629             InternetAddress JavaDoc[] iart = null;
630             if (replyTo != SpecialAddress.NULL) {
631                 iart = new InternetAddress JavaDoc[1];
632                 iart[0] = replyTo.toInternetAddress();
633             }
634             
635             // Note: if iart is null will remove the header
636
newMail.getMessage().setReplyTo(iart);
637             
638             if (isDebug) {
639                 log("replyTo set to: " + replyTo);
640             }
641         }
642     }
643
644     /**
645      * Gets the <CODE>reversePath</CODE> property.
646      * Returns the reverse-path of the new message,
647      * or null if no change is requested.
648      * Is a "getX()" method.
649      *
650      * @return the <CODE>reversePath</CODE> init parameter
651      * or the postmaster address
652      * or <CODE>SpecialAddress.SENDER</CODE>
653      * or <CODE>SpecialAddress.NULL</CODE>
654      * or <CODE>SpecialAddress.UNALTERED</CODE>
655      * or <CODE>null</CODE> if missing
656      */

657     protected MailAddress getReversePath() throws MessagingException JavaDoc {
658         String JavaDoc addressString = getInitParameter("reversePath");
659         if(addressString != null) {
660             MailAddress specialAddress = getSpecialAddress(addressString,
661                                             new String JavaDoc[] {"postmaster", "sender", "null", "unaltered"});
662             if (specialAddress != null) {
663                 return specialAddress;
664             }
665
666             try {
667                 return new MailAddress(addressString);
668             } catch(Exception JavaDoc e) {
669                 throw new MessagingException JavaDoc("Exception thrown in getReversePath() parsing: " + addressString, e);
670             }
671         }
672
673         return null;
674     }
675
676     /**
677      * Gets the <CODE>reversePath</CODE> property,
678      * built dynamically using the original Mail object.
679      * Is a "getX(Mail)" method.
680      *
681      * @return {@link #getReversePath()},
682      * replacing <CODE>SpecialAddress.SENDER</CODE> if applicable with null,
683      * replacing <CODE>SpecialAddress.UNALTERED</CODE>
684      * and <CODE>SpecialAddress.REVERSE_PATH</CODE> if applicable with null,
685      * but not replacing <CODE>SpecialAddress.NULL</CODE>
686      * that will be handled by {@link #setReversePath}
687      */

688     protected MailAddress getReversePath(Mail originalMail) throws MessagingException JavaDoc {
689         MailAddress reversePath = (isStatic()) ? this.reversePath : getReversePath();
690         if (reversePath != null) {
691             if (reversePath == SpecialAddress.UNALTERED || reversePath == SpecialAddress.REVERSE_PATH) {
692                 reversePath = null;
693             }
694             else if (reversePath == SpecialAddress.SENDER) {
695                 reversePath = null;
696             }
697         }
698         return reversePath;
699     }
700
701     /**
702      * Sets the "reverse-path" of <I>newMail</I> to <I>reversePath</I>.
703      * If the requested value is <CODE>SpecialAddress.NULL</CODE> sets it to "<>".
704      * If the requested value is null does nothing.
705      * Is a "setX(Mail, Tx, Mail)" method.
706      */

707     protected void setReversePath(Mail newMail, MailAddress reversePath, Mail originalMail) throws MessagingException JavaDoc {
708         if(reversePath != null) {
709             String JavaDoc reversePathString;
710             if (reversePath == SpecialAddress.NULL) {
711                 reversePath = null;
712                 reversePathString = "";
713             } else {
714                 reversePathString = reversePath.toString();
715             }
716             ((MailImpl) newMail).setSender(reversePath);
717             newMail.getMessage().setHeader(RFC2822Headers.RETURN_PATH, "<" + reversePathString + ">");
718             if (isDebug) {
719                 log("reversePath set to: " + reversePath);
720             }
721         }
722     }
723
724     /**
725      * Gets the <CODE>sender</CODE> property.
726      * Returns the new sender as a MailAddress,
727      * or null if no change is requested.
728      * Is a "getX()" method.
729      *
730      * @return the <CODE>sender</CODE> init parameter
731      * or the postmaster address
732      * or <CODE>SpecialAddress.SENDER</CODE>
733      * or <CODE>SpecialAddress.UNALTERED</CODE>
734      * or <CODE>null</CODE> if missing
735      */

736     protected MailAddress getSender() throws MessagingException JavaDoc {
737         String JavaDoc addressString = getInitParameter("sender");
738         if(addressString != null) {
739             MailAddress specialAddress = getSpecialAddress(addressString,
740                                             new String JavaDoc[] {"postmaster", "sender", "unaltered"});
741             if (specialAddress != null) {
742                 return specialAddress;
743             }
744
745             try {
746                 return new MailAddress(addressString);
747             } catch(Exception JavaDoc e) {
748                 throw new MessagingException JavaDoc("Exception thrown in getSender() parsing: " + addressString, e);
749             }
750         }
751
752         return null;
753     }
754
755     /**
756      * Gets the <CODE>sender</CODE> property,
757      * built dynamically using the original Mail object.
758      * Is a "getX(Mail)" method.
759      *
760      * @return {@link #getSender()}
761      * replacing <CODE>SpecialAddress.UNALTERED</CODE>
762      * and <CODE>SpecialAddress.SENDER</CODE> if applicable with null
763      */

764     protected MailAddress getSender(Mail originalMail) throws MessagingException JavaDoc {
765         MailAddress sender = (isStatic()) ? this.sender : getSender();
766         if (sender != null) {
767             if (sender == SpecialAddress.UNALTERED || sender == SpecialAddress.SENDER) {
768                 sender = null;
769             }
770         }
771         return sender;
772     }
773
774     /**
775      * Sets the "From:" header of <I>newMail</I> to <I>sender</I>.
776      * If the requested value is null does nothing.
777      * Is a "setX(Mail, Tx, Mail)" method.
778      */

779     protected void setSender(Mail newMail, MailAddress sender, Mail originalMail) throws MessagingException JavaDoc {
780         if (sender != null) {
781             newMail.getMessage().setFrom(sender.toInternetAddress());
782             
783             if (isDebug) {
784                 log("sender set to: " + sender);
785             }
786         }
787     }
788     
789     /**
790      * Gets the <CODE>subject</CODE> property.
791      * Returns a string for the new message subject.
792      * Is a "getX()" method.
793      *
794      * @return the <CODE>subject</CODE> init parameter or null if missing
795      */

796     protected String JavaDoc getSubject() throws MessagingException JavaDoc {
797         if(getInitParameter("subject") == null) {
798             return null;
799         } else {
800             return getInitParameter("subject");
801         }
802     }
803
804     /**
805      * Gets the <CODE>subject</CODE> property,
806      * built dynamically using the original Mail object.
807      * Is a "getX(Mail)" method.
808      *
809      * @return {@link #getSubject()}
810      */

811     protected String JavaDoc getSubject(Mail originalMail) throws MessagingException JavaDoc {
812         String JavaDoc subject = (isStatic()) ? this.subject : getSubject();
813         return subject;
814     }
815
816     /**
817      * Gets the <CODE>prefix</CODE> property.
818      * Returns a prefix for the new message subject.
819      * Is a "getX()" method.
820      *
821      * @return the <CODE>prefix</CODE> init parameter or an empty string if missing
822      */

823     protected String JavaDoc getSubjectPrefix() throws MessagingException JavaDoc {
824         if(getInitParameter("prefix") == null) {
825             return null;
826         } else {
827             return getInitParameter("prefix");
828         }
829     }
830
831     /**
832      * Gets the <CODE>subjectPrefix</CODE> property,
833      * built dynamically using the original Mail object.
834      * Is a "getX(Mail)" method.
835      *
836      * @return {@link #getSubjectPrefix()}
837      */

838     protected String JavaDoc getSubjectPrefix(Mail originalMail) throws MessagingException JavaDoc {
839         String JavaDoc subjectPrefix = (isStatic()) ? this.subjectPrefix : getSubjectPrefix();
840         return subjectPrefix;
841     }
842
843     /**
844      * Builds the subject of <I>newMail</I> appending the subject
845      * of <I>originalMail</I> to <I>subjectPrefix</I>.
846      * Is a "setX(Mail, Tx, Mail)" method.
847      */

848     protected void setSubjectPrefix(Mail newMail, String JavaDoc subjectPrefix, Mail originalMail) throws MessagingException JavaDoc {
849         String JavaDoc subject = getSubject(originalMail);
850         if ((subjectPrefix != null && subjectPrefix.length() > 0) || subject != null) {
851             if (subject == null) {
852                 subject = originalMail.getMessage().getSubject();
853             } else {
854                 // replacing the subject
855
if (isDebug) {
856                     log("subject set to: " + subject);
857                 }
858             }
859             // Was null in original?
860
if (subject == null) {
861                 subject = "";
862             }
863             
864             if (subjectPrefix != null) {
865                 subject = subjectPrefix + subject;
866                 // adding a prefix
867
if (isDebug) {
868                     log("subjectPrefix set to: " + subjectPrefix);
869                 }
870             }
871 // newMail.getMessage().setSubject(subject);
872
changeSubject(newMail.getMessage(), subject);
873         }
874     }
875
876     /**
877      * Gets the <CODE>attachError</CODE> property.
878      * Returns a boolean indicating whether to append a description of any error to the main body part
879      * of the new message, if getInlineType does not return "UNALTERED".
880      * Is a "getX()" method.
881      *
882      * @return the <CODE>attachError</CODE> init parameter; false if missing
883      */

884     protected boolean attachError() throws MessagingException JavaDoc {
885         return new Boolean JavaDoc(getInitParameter("attachError")).booleanValue();
886     }
887
888     /**
889      * Gets the <CODE>attachError</CODE> property,
890      * built dynamically using the original Mail object.
891      * Is a "getX(Mail)" method.
892      *
893      * @return {@link #attachError()}
894      */

895     protected boolean attachError(Mail originalMail) throws MessagingException JavaDoc {
896         boolean attachError = (isStatic()) ? this.attachError : attachError();
897         return attachError;
898     }
899
900     /**
901      * Gets the <CODE>isReply</CODE> property.
902      * Returns a boolean indicating whether the new message must be considered
903      * a reply to the original message, setting the IN_REPLY_TO header of the new
904      * message to the id of the original message.
905      * Is a "getX()" method.
906      *
907      * @return the <CODE>isReply</CODE> init parameter; false if missing
908      */

909     protected boolean isReply() throws MessagingException JavaDoc {
910         return new Boolean JavaDoc(getInitParameter("isReply")).booleanValue();
911     }
912
913     /**
914      * Gets the <CODE>isReply</CODE> property,
915      * built dynamically using the original Mail object.
916      * Is a "getX(Mail)" method.
917      *
918      * @return {@link #isReply()}
919      */

920     protected boolean isReply(Mail originalMail) throws MessagingException JavaDoc {
921         boolean isReply = (isStatic()) ? this.isReply : isReply();
922         return isReply;
923     }
924
925     /**
926      * Sets the "In-Reply-To:" header of <I>newMail</I> to the "Message-Id:" of
927      * <I>originalMail</I>, if <I>isReply</I> is true.
928      */

929     protected void setIsReply(Mail newMail, boolean isReply, Mail originalMail) throws MessagingException JavaDoc {
930         if (isReply) {
931             String JavaDoc messageId = originalMail.getMessage().getMessageID();
932             if (messageId != null) {
933                 newMail.getMessage().setHeader(RFC2822Headers.IN_REPLY_TO, messageId);
934                 if (isDebug) {
935                     log("IN_REPLY_TO set to: " + messageId);
936                 }
937             }
938         }
939     }
940
941     /* ******************************************************************** */
942     /* ******************* End of getX and setX methods ******************* */
943     /* ******************************************************************** */
944
945     /**
946      * Mailet initialization routine.
947      * Will setup static values for each "x" initialization parameter in config.xml,
948      * using getX(), if {@link #isStatic()} returns true.
949      */

950     public void init() throws MessagingException JavaDoc {
951         isDebug = (getInitParameter("debug") == null) ? false : new Boolean JavaDoc(getInitParameter("debug")).booleanValue();
952
953         isStatic = (getInitParameter("static") == null) ? false : new Boolean JavaDoc(getInitParameter("static")).booleanValue();
954
955         if (isDebug) {
956             log("Initializing");
957         }
958         
959         // check that all init parameters have been declared in allowedInitParameters
960
checkInitParameters(getAllowedInitParameters());
961         
962         if(isStatic()) {
963             passThrough = getPassThrough();
964             fakeDomainCheck = getFakeDomainCheck();
965             attachmentType = getAttachmentType();
966             inLineType = getInLineType();
967             messageText = getMessage();
968             recipients = getRecipients();
969             replyTo = getReplyTo();
970             reversePath = getReversePath();
971             sender = getSender();
972             subject = getSubject();
973             subjectPrefix = getSubjectPrefix();
974             apparentlyTo = getTo();
975             attachError = attachError();
976             isReply = isReply();
977             if (isDebug) {
978                 StringBuffer JavaDoc logBuffer =
979                     new StringBuffer JavaDoc(1024)
980                             .append("static")
981                             .append(", passThrough=").append(passThrough)
982                             .append(", fakeDomainCheck=").append(fakeDomainCheck)
983                             .append(", sender=").append(sender)
984                             .append(", replyTo=").append(replyTo)
985                             .append(", reversePath=").append(reversePath)
986                             .append(", message=").append(messageText)
987                             .append(", recipients=").append(arrayToString(recipients == null ? null : recipients.toArray()))
988                             .append(", subject=").append(subject)
989                             .append(", subjectPrefix=").append(subjectPrefix)
990                             .append(", apparentlyTo=").append(arrayToString(apparentlyTo))
991                             .append(", attachError=").append(attachError)
992                             .append(", isReply=").append(isReply)
993                             .append(", attachmentType=").append(attachmentType)
994                             .append(", inLineType=").append(inLineType)
995                             .append(" ");
996                 log(logBuffer.toString());
997             }
998         }
999     }
1000
1001    /**
1002     * Service does the hard work,and redirects the originalMail in the form specified.
1003     *
1004     * @param originalMail the mail to process and redirect
1005     * @throws MessagingException if a problem arises formulating the redirected mail
1006     */

1007    public void service(Mail originalMail) throws MessagingException JavaDoc {
1008
1009        boolean keepMessageId = false;
1010
1011        // duplicates the Mail object, to be able to modify the new mail keeping the original untouched
1012
Mail newMail = ((MailImpl) originalMail).duplicate(newName((MailImpl) originalMail));
1013        // We don't need to use the original Remote Address and Host,
1014
// and doing so would likely cause a loop with spam detecting
1015
// matchers.
1016
try {
1017            ((MailImpl) newMail).setRemoteAddr(java.net.InetAddress.getLocalHost().getHostAddress());
1018            ((MailImpl) newMail).setRemoteHost(java.net.InetAddress.getLocalHost().getHostName());
1019        } catch (java.net.UnknownHostException JavaDoc _) {
1020            ((MailImpl) newMail).setRemoteAddr("127.0.0.1");
1021            ((MailImpl) newMail).setRemoteHost("localhost");
1022        }
1023
1024        if (isDebug) {
1025            MailImpl newMailImpl = (MailImpl) newMail;
1026            log("New mail - sender: " + newMailImpl.getSender()
1027                       + ", recipients: " + arrayToString(newMailImpl.getRecipients().toArray())
1028                       + ", name: " + newMailImpl.getName()
1029                       + ", remoteHost: " + newMailImpl.getRemoteHost()
1030                       + ", remoteAddr: " + newMailImpl.getRemoteAddr()
1031                       + ", state: " + newMailImpl.getState()
1032                       + ", lastUpdated: " + newMailImpl.getLastUpdated()
1033                       + ", errorMessage: " + newMailImpl.getErrorMessage());
1034        }
1035
1036        //Create the message
1037
if(getInLineType(originalMail) != UNALTERED) {
1038            if (isDebug) {
1039                log("Alter message");
1040            }
1041            newMail.setMessage(new MimeMessage JavaDoc(Session.getDefaultInstance(System.getProperties(),
1042                                                               null)));
1043
1044            // handle the new message if altered
1045
buildAlteredMessage(newMail, originalMail);
1046
1047        } else {
1048            // if we need the original, create a copy of this message to redirect
1049
if (getPassThrough(originalMail)) {
1050                newMail.setMessage(new MimeMessage JavaDoc(originalMail.getMessage()) {
1051                    protected void updateHeaders() throws MessagingException JavaDoc {
1052                        if (getMessageID() == null) super.updateHeaders();
1053                        else {
1054                            modified = false;
1055                        }
1056                    }
1057                });
1058            }
1059            if (isDebug) {
1060                log("Message resent unaltered.");
1061            }
1062            keepMessageId = true;
1063        }
1064
1065        //Set additional headers
1066

1067        setRecipients(newMail, getRecipients(originalMail), originalMail);
1068
1069        setTo(newMail, getTo(originalMail), originalMail);
1070
1071        setSubjectPrefix(newMail, getSubjectPrefix(originalMail), originalMail);
1072
1073        if(newMail.getMessage().getHeader(RFC2822Headers.DATE) == null) {
1074            newMail.getMessage().setHeader(RFC2822Headers.DATE, rfc822DateFormat.format(new Date JavaDoc()));
1075        }
1076
1077        setReplyTo(newMail, getReplyTo(originalMail), originalMail);
1078
1079        setReversePath(newMail, getReversePath(originalMail), originalMail);
1080
1081        setSender(newMail, getSender(originalMail), originalMail);
1082
1083        setIsReply(newMail, isReply(originalMail), originalMail);
1084
1085        newMail.getMessage().saveChanges();
1086
1087        if (keepMessageId) {
1088            setMessageId(newMail, originalMail);
1089        }
1090
1091        if (senderDomainIsValid(newMail)) {
1092            //Send it off...
1093
getMailetContext().sendMail(newMail);
1094        } else {
1095            StringBuffer JavaDoc logBuffer = new StringBuffer JavaDoc(256)
1096                                    .append(getMailetName())
1097                                    .append(" mailet cannot forward ")
1098                                    .append(((MailImpl) originalMail).getName())
1099                                    .append(". Invalid sender domain for ")
1100                                    .append(newMail.getSender())
1101                                    .append(". Consider using the Resend mailet ")
1102                                    .append("using a different sender.");
1103            throw new MessagingException JavaDoc(logBuffer.toString());
1104        }
1105
1106        if(!getPassThrough(originalMail)) {
1107            originalMail.setState(Mail.GHOST);
1108        }
1109    }
1110
1111    private static final java.util.Random JavaDoc random = new java.util.Random JavaDoc(); // Used to generate new mail names
1112

1113    /**
1114     * Create a unique new primary key name.
1115     *
1116     * @param mail the mail to use as the basis for the new mail name
1117     * @return a new name
1118     */

1119    private String JavaDoc newName(MailImpl mail) throws MessagingException JavaDoc {
1120        String JavaDoc oldName = mail.getName();
1121        
1122        // Checking if the original mail name is too long, perhaps because of a
1123
// loop caused by a configuration error.
1124
// it could cause a "null pointer exception" in AvalonMailRepository much
1125
// harder to understand.
1126
if (oldName.length() > 76) {
1127            int count = 0;
1128            int index = 0;
1129            while ((index = oldName.indexOf('!', index + 1)) >= 0) {
1130                count++;
1131            }
1132            // It looks like a configuration loop. It's better to stop.
1133
if (count > 7) {
1134                throw new MessagingException JavaDoc("Unable to create a new message name: too long."
1135                                             + " Possible loop in config.xml.");
1136            }
1137            else {
1138                oldName = oldName.substring(0, 76);
1139            }
1140        }
1141        
1142        StringBuffer JavaDoc nameBuffer =
1143                                 new StringBuffer JavaDoc(64)
1144                                 .append(oldName)
1145                                 .append("-!")
1146                                 .append(random.nextInt(1048576));
1147        return nameBuffer.toString();
1148    }
1149
1150    /**
1151     * A private method to convert types from string to int.
1152     *
1153     * @param param the string type
1154     * @return the corresponding int enumeration
1155     */

1156    protected int getTypeCode(String JavaDoc param) {
1157        param = param.toLowerCase(Locale.US);
1158        if(param.compareTo("unaltered") == 0) {
1159            return UNALTERED;
1160        }
1161        if(param.compareTo("heads") == 0) {
1162            return HEADS;
1163        }
1164        if(param.compareTo("body") == 0) {
1165            return BODY;
1166        }
1167        if(param.compareTo("all") == 0) {
1168            return ALL;
1169        }
1170        if(param.compareTo("none") == 0) {
1171            return NONE;
1172        }
1173        if(param.compareTo("message") == 0) {
1174            return MESSAGE;
1175        }
1176        return NONE;
1177    }
1178
1179    /**
1180     * Gets the MailAddress corresponding to the existing "Return-Path" of
1181     * <I>mail</I>.
1182     * If empty returns <CODE>SpecialAddress.NULL</CODE>,
1183     * if missing return <CODE>null</CODE>.
1184     */

1185    protected MailAddress getExistingReturnPath(Mail mail) throws MessagingException JavaDoc {
1186        MailAddress mailAddress = null;
1187        String JavaDoc[] returnPathHeaders = mail.getMessage().getHeader(RFC2822Headers.RETURN_PATH);
1188        String JavaDoc returnPathHeader = null;
1189        if (returnPathHeaders != null) {
1190            returnPathHeader = returnPathHeaders[0];
1191            if (returnPathHeader != null) {
1192                returnPathHeader = returnPathHeader.trim();
1193                if (returnPathHeader.equals("<>")) {
1194                    mailAddress = SpecialAddress.NULL;
1195                } else {
1196                    mailAddress = new MailAddress(new InternetAddress JavaDoc(returnPathHeader));
1197                }
1198            }
1199        }
1200        return mailAddress;
1201    }
1202
1203    /**
1204     * Utility method for obtaining a string representation of an array of Objects.
1205     */

1206    private String JavaDoc arrayToString(Object JavaDoc[] array) {
1207        if (array == null) {
1208            return "null";
1209        }
1210        StringBuffer JavaDoc sb = new StringBuffer JavaDoc(1024);
1211        sb.append("[");
1212        for (int i = 0; i < array.length; i++) {
1213            if (i > 0) {
1214                sb.append(",");
1215            }
1216            sb.append(array[i]);
1217        }
1218        sb.append("]");
1219        return sb.toString();
1220    }
1221
1222    /**
1223     * Utility method for obtaining a string representation of a
1224     * Message's headers
1225     */

1226    private String JavaDoc getMessageHeaders(MimeMessage JavaDoc message) throws MessagingException JavaDoc {
1227        Enumeration JavaDoc heads = message.getAllHeaderLines();
1228        StringBuffer JavaDoc headBuffer = new StringBuffer JavaDoc(1024);
1229        while(heads.hasMoreElements()) {
1230            headBuffer.append(heads.nextElement().toString()).append("\r\n");
1231        }
1232        return headBuffer.toString();
1233    }
1234
1235    /**
1236     * Utility method for obtaining a string representation of a
1237     * Message's body
1238     */

1239    private String JavaDoc getMessageBody(MimeMessage JavaDoc message) throws Exception JavaDoc {
1240        java.io.InputStream JavaDoc bis = null;
1241        java.io.OutputStream JavaDoc bos = null;
1242        java.io.ByteArrayOutputStream JavaDoc bodyOs = new java.io.ByteArrayOutputStream JavaDoc();
1243
1244        try {
1245            // Get the message as a stream. This will encode
1246
// objects as necessary, and we have some overhead from
1247
// decoding and re-encoding the stream. I'd prefer the
1248
// raw stream, but see the WARNING below.
1249
bos = javax.mail.internet.MimeUtility.encode(bodyOs, message.getEncoding());
1250            bis = message.getInputStream();
1251        } catch(javax.activation.UnsupportedDataTypeException JavaDoc udte) {
1252            /* If we get an UnsupportedDataTypeException try using
1253             * the raw input stream as a "best attempt" at rendering
1254             * a message.
1255             *
1256             * WARNING: JavaMail v1.3 getRawInputStream() returns
1257             * INVALID (unchanged) content for a changed message.
1258             * getInputStream() works properly, but in this case
1259             * has failed due to a missing DataHandler.
1260             *
1261             * MimeMessage.getRawInputStream() may throw a "no
1262             * content" MessagingException. In JavaMail v1.3, when
1263             * you initially create a message using MimeMessage
1264             * APIs, there is no raw content available.
1265             * getInputStream() works, but getRawInputStream()
1266             * throws an exception. If we catch that exception,
1267             * throw the UDTE. It should mean that someone has
1268             * locally constructed a message part for which JavaMail
1269             * doesn't have a DataHandler.
1270             */

1271
1272            try {
1273                bis = message.getRawInputStream();
1274                bos = bodyOs;
1275            } catch(javax.mail.MessagingException JavaDoc _) {
1276                throw udte;
1277            }
1278        }
1279        catch(javax.mail.MessagingException JavaDoc me) {
1280            /* This could be another kind of MessagingException
1281             * thrown by MimeMessage.getInputStream(), such as a
1282             * javax.mail.internet.ParseException.
1283             *
1284             * The ParseException is precisely one of the reasons
1285             * why the getRawInputStream() method exists, so that we
1286             * can continue to stream the content, even if we cannot
1287             * handle it. Again, if we get an exception, we throw
1288             * the one that caused us to call getRawInputStream().
1289             */

1290            try {
1291                bis = message.getRawInputStream();
1292                bos = bodyOs;
1293            } catch(javax.mail.MessagingException JavaDoc _) {
1294                throw me;
1295            }
1296        }
1297
1298        try {
1299            byte[] block = new byte[1024];
1300            int read = 0;
1301            while ((read = bis.read(block)) > -1) {
1302                bos.write(block, 0, read);
1303            }
1304            bos.flush();
1305            return bodyOs.toString();
1306        }
1307        finally {
1308            bis.close();
1309        }
1310    }
1311
1312    /**
1313     * Builds the message of the newMail in case it has to be altered.
1314     *
1315     * @param originalMail the original Mail object
1316     * @param newMail the Mail object to build
1317     */

1318    protected void buildAlteredMessage(Mail newMail, Mail originalMail) throws MessagingException JavaDoc {
1319
1320        MimeMessage JavaDoc originalMessage = originalMail.getMessage();
1321        MimeMessage JavaDoc newMessage = newMail.getMessage();
1322
1323        // Copy the relevant headers
1324
String JavaDoc[] relevantHeaderNames =
1325            {RFC2822Headers.DATE,
1326             RFC2822Headers.FROM,
1327             RFC2822Headers.REPLY_TO,
1328             RFC2822Headers.TO,
1329             RFC2822Headers.SUBJECT,
1330             RFC2822Headers.RETURN_PATH};
1331        Enumeration JavaDoc headerEnum = originalMessage.getMatchingHeaderLines(relevantHeaderNames);
1332        while (headerEnum.hasMoreElements()) {
1333            newMessage.addHeaderLine((String JavaDoc) headerEnum.nextElement());
1334        }
1335
1336        StringWriter JavaDoc sout = new StringWriter JavaDoc();
1337        PrintWriter JavaDoc out = new PrintWriter JavaDoc(sout, true);
1338        String JavaDoc head = getMessageHeaders(originalMessage);
1339        boolean all = false;
1340
1341        String JavaDoc messageText = getMessage(originalMail);
1342        if(messageText != null) {
1343            out.println(messageText);
1344        }
1345
1346        if (isDebug) {
1347            log("inline:" + getInLineType(originalMail));
1348        }
1349        switch(getInLineType(originalMail)) {
1350            case ALL: //ALL:
1351
all = true;
1352            case HEADS: //HEADS:
1353
out.println("Message Headers:");
1354                out.println(head);
1355                if(!all) {
1356                    break;
1357                }
1358            case BODY: //BODY:
1359
out.println("Message:");
1360                try {
1361                    out.println(getMessageBody(originalMessage));
1362                } catch(Exception JavaDoc e) {
1363                    out.println("body unavailable");
1364                }
1365                break;
1366            default:
1367            case NONE: //NONE:
1368
break;
1369        }
1370
1371        try {
1372            //Create the message body
1373
MimeMultipart JavaDoc multipart = new MimeMultipart JavaDoc("mixed");
1374
1375            // Create the message
1376
MimeMultipart JavaDoc mpContent = new MimeMultipart JavaDoc("alternative");
1377            MimeBodyPart JavaDoc contentPartRoot = new MimeBodyPart JavaDoc();
1378            contentPartRoot.setContent(mpContent);
1379
1380            multipart.addBodyPart(contentPartRoot);
1381
1382            MimeBodyPart JavaDoc part = new MimeBodyPart JavaDoc();
1383            part.setText(sout.toString());
1384            part.setDisposition("inline");
1385            mpContent.addBodyPart(part);
1386            if (isDebug) {
1387                log("attachmentType:" + getAttachmentType(originalMail));
1388            }
1389            if(getAttachmentType(originalMail) != NONE) {
1390                part = new MimeBodyPart JavaDoc();
1391                switch(getAttachmentType(originalMail)) {
1392                    case HEADS: //HEADS:
1393
part.setText(head);
1394                        break;
1395                    case BODY: //BODY:
1396
try {
1397                            part.setText(getMessageBody(originalMessage));
1398                        } catch(Exception JavaDoc e) {
1399                            part.setText("body unavailable");
1400                        }
1401                        break;
1402                    case ALL: //ALL:
1403
StringBuffer JavaDoc textBuffer =
1404                            new StringBuffer JavaDoc(1024)
1405                                .append(head)
1406                                .append("\r\nMessage:\r\n")
1407                                .append(getMessageBody(originalMessage));
1408                        part.setText(textBuffer.toString());
1409                        break;
1410                    case MESSAGE: //MESSAGE:
1411
part.setContent(originalMessage, "message/rfc822");
1412                        break;
1413                }
1414                if ((originalMessage.getSubject() != null) && (originalMessage.getSubject().trim().length() > 0)) {
1415                    part.setFileName(originalMessage.getSubject().trim());
1416                } else {
1417                    part.setFileName("No Subject");
1418                }
1419                part.setDisposition("Attachment");
1420                multipart.addBodyPart(part);
1421            }
1422            //if set, attach the original mail's error message
1423
if (attachError(originalMail) && originalMail.getErrorMessage() != null) {
1424                part = new MimeBodyPart JavaDoc();
1425                part.setContent(originalMail.getErrorMessage(), "text/plain");
1426                part.setHeader(RFC2822Headers.CONTENT_TYPE, "text/plain");
1427                part.setFileName("Reasons");
1428                part.setDisposition(javax.mail.Part.ATTACHMENT);
1429                multipart.addBodyPart(part);
1430            }
1431            newMail.getMessage().setContent(multipart);
1432            newMail.getMessage().setHeader(RFC2822Headers.CONTENT_TYPE, multipart.getContentType());
1433
1434        } catch (Exception JavaDoc ioe) {
1435            throw new MessagingException JavaDoc("Unable to create multipart body", ioe);
1436        }
1437    }
1438
1439    /**
1440     * Sets the message id of originalMail into newMail.
1441     */

1442    private void setMessageId(Mail newMail, Mail originalMail) throws MessagingException JavaDoc {
1443        String JavaDoc messageId = originalMail.getMessage().getMessageID();
1444        if (messageId != null) {
1445            newMail.getMessage().setHeader(RFC2822Headers.MESSAGE_ID, messageId);
1446            if (isDebug) {
1447                log("MESSAGE_ID restored to: " + messageId);
1448            }
1449        }
1450    }
1451
1452    /**
1453     * Returns the {@link SpecialAddress} that corresponds to an init parameter value.
1454     * The init parameter value is checked against a String[] of allowed values.
1455     * The checks are case insensitive.
1456     *
1457     * @param addressString the string to check if is a special address
1458     * @param allowedSpecials a String[] with the allowed special addresses
1459     * @return a SpecialAddress if found, null if not found or addressString is null
1460     * @throws MessagingException if is a special address not in the allowedSpecials array
1461     */

1462    protected final MailAddress getSpecialAddress(String JavaDoc addressString, String JavaDoc[] allowedSpecials) throws MessagingException JavaDoc {
1463        if (addressString == null) {
1464            return null;
1465        }
1466
1467        addressString = addressString.toLowerCase(Locale.US);
1468        addressString = addressString.trim();
1469
1470        MailAddress specialAddress = null;
1471
1472        if(addressString.compareTo("postmaster") == 0) {
1473            specialAddress = getMailetContext().getPostmaster();
1474        }
1475        if(addressString.compareTo("sender") == 0) {
1476            specialAddress = SpecialAddress.SENDER;
1477        }
1478        if(addressString.compareTo("reversepath") == 0) {
1479            specialAddress = SpecialAddress.REVERSE_PATH;
1480        }
1481        if(addressString.compareTo("from") == 0) {
1482            specialAddress = SpecialAddress.FROM;
1483        }
1484        if(addressString.compareTo("replyto") == 0) {
1485            specialAddress = SpecialAddress.REPLY_TO;
1486        }
1487        if(addressString.compareTo("to") == 0) {
1488            specialAddress = SpecialAddress.TO;
1489        }
1490        if(addressString.compareTo("recipients") == 0) {
1491            specialAddress = SpecialAddress.RECIPIENTS;
1492        }
1493        if(addressString.compareTo("delete") == 0) {
1494            specialAddress = SpecialAddress.DELETE;
1495        }
1496        if(addressString.compareTo("unaltered") == 0) {
1497            specialAddress = SpecialAddress.UNALTERED;
1498        }
1499        if(addressString.compareTo("null") == 0) {
1500            specialAddress = SpecialAddress.NULL;
1501        }
1502
1503        // if is a special address, must be in the allowedSpecials array
1504
if (specialAddress != null) {
1505            // check if is an allowed special
1506
boolean allowed = false;
1507            for (int i = 0; i < allowedSpecials.length; i++) {
1508                String JavaDoc allowedSpecial = allowedSpecials[i];
1509                allowedSpecial = allowedSpecial.toLowerCase(Locale.US);
1510                allowedSpecial = allowedSpecial.trim();
1511                if(addressString.compareTo(allowedSpecial) == 0) {
1512                    allowed = true;
1513                    break;
1514                }
1515            }
1516            if (!allowed) {
1517                throw new MessagingException JavaDoc("Special (\"magic\") address found not allowed: " + addressString +
1518                                             ", allowed values are \"" + arrayToString(allowedSpecials) + "\"");
1519            }
1520        }
1521
1522        return specialAddress;
1523    }
1524
1525    /**
1526     * <P>Checks if a sender domain of <I>mail</I> is valid.</P>
1527     * <P>If we do not do this check, and someone uses a redirection mailet in a
1528     * processor initiated by SenderInFakeDomain, then a fake
1529     * sender domain will cause an infinite loop (the forwarded
1530     * e-mail still appears to come from a fake domain).<BR>
1531     * Although this can be viewed as a configuration error, the
1532     * consequences of such a mis-configuration are severe enough
1533     * to warrant protecting against the infinite loop.</P>
1534     * <P>This check can be skipped if {@link #getFakeDomainCheck(Mail)} returns true.</P>
1535     *
1536     * @param mail the mail object to check
1537     * @return true if the if the sender is null or
1538     * {@link org.apache.mailet.MailetContext#getMailServers} returns true for
1539     * the sender host part
1540     */

1541    protected final boolean senderDomainIsValid(Mail mail) throws MessagingException JavaDoc {
1542        if (getFakeDomainCheck(mail)) {
1543            return mail.getSender() == null || getMailetContext().getMailServers(mail.getSender().getHost()).size() != 0;
1544        } else return true;
1545    }
1546    
1547    /**
1548     * Checks if there are unallowed init parameters specified in the configuration file
1549     * against the String[] allowedInitParameters.
1550     */

1551    private void checkInitParameters(String JavaDoc[] allowedArray) throws MessagingException JavaDoc {
1552        // if null then no check is requested
1553
if (allowedArray == null) {
1554            return;
1555        }
1556        
1557        Collection JavaDoc allowed = new HashSet JavaDoc();
1558        Collection JavaDoc bad = new ArrayList JavaDoc();
1559        
1560        for (int i = 0; i < allowedArray.length; i++) {
1561            allowed.add(allowedArray[i]);
1562        }
1563        
1564        Iterator JavaDoc iterator = getInitParameterNames();
1565        while (iterator.hasNext()) {
1566            String JavaDoc parameter = (String JavaDoc) iterator.next();
1567            if (!allowed.contains(parameter)) {
1568                bad.add(parameter);
1569            }
1570        }
1571        
1572        if (bad.size() > 0) {
1573            throw new MessagingException JavaDoc("Unexpected init parameters found: "
1574                                         + arrayToString(bad.toArray()));
1575        }
1576    }
1577
1578    /**
1579     * It changes the subject of the supplied message to to supplied value
1580     * but it also tries to preserve the original charset information.<BR>
1581     *
1582     * This method was needed to avoid sending the subject using a charset
1583     * (usually the default charset on the server) which doesn't contain
1584     * the characters in the subject, resulting in the loss of these characters.
1585     * The most simple method would be to either send it in ASCII unencoded
1586     * or in UTF-8 if non-ASCII characters are present but unfortunately UTF-8
1587     * is not yet a MIME standard and not all email clients
1588     * are supporting it. The optimal method would be to determine the best
1589     * charset by analyzing the actual characters. That would require much
1590     * more work (exept if an open source library already exists for this).
1591     * However there is nothing to stop somebody to add a detection algorithm
1592     * for a specific charset. <BR>
1593     *
1594     * The current algorithm works correctly if only ASCII characters are
1595     * added to an existing subject.<BR>
1596     *
1597     * If the new value is ASCII only, then it doesn't apply any encoding to
1598     * the subject header. (This is provided by MimeMessage.setSubject()).<BR>
1599     *
1600     * Possible enhancement: under java 1.4 java.nio the system can determine if the
1601     * suggested charset fits or not (if there is untranslatable
1602     * characters). If the charset doesn't fit the new value, it
1603     * can fall back to UTF-8.<BR>
1604     *
1605     * @param message the message of which subject is changed
1606     * @param newValue the new (unencoded) value of the subject. It must
1607     * not be null.
1608     * @throws MessagingException - according to the JavaMail doc most likely
1609     * this is never thrown
1610     */

1611    public static void changeSubject(MimeMessage JavaDoc message, String JavaDoc newValue)
1612            throws MessagingException JavaDoc
1613    {
1614        String JavaDoc rawSubject = message.getHeader(RFC2822Headers.SUBJECT, null);
1615        String JavaDoc mimeCharset = determineMailHeaderEncodingCharset(rawSubject);
1616        if (mimeCharset == null) { // most likely ASCII
1617
// it uses the system charset or the value of the
1618
// mail.mime.charset property if set
1619
message.setSubject(newValue);
1620            return;
1621        } else { // original charset determined
1622
String JavaDoc javaCharset = javax.mail.internet.MimeUtility.javaCharset(mimeCharset);
1623            try {
1624                message.setSubject(newValue, javaCharset);
1625            } catch (MessagingException JavaDoc e) {
1626                // known, but unsupported encoding
1627
// this should be logged, the admin may setup a more i18n
1628
// capable JRE, but the log API cannot be accessed from here
1629
//if (charset != null) log(charset +
1630
// " charset unsupported by the JRE, email subject may be damaged");
1631
message.setSubject(newValue); // recover
1632
}
1633        }
1634    }
1635     
1636    /**
1637     * It attempts to determine the charset used to encode an "unstructured"
1638     * RFC 822 header (like Subject). The encoding is specified in RFC 2047.
1639     * If it cannot determine or the the text is not encoded then it returns null.
1640     *
1641     * Here is an example raw text:
1642     * Subject: =?iso-8859-2?Q?leg=FAjabb_pr=F3ba_l=F5elemmel?=
1643     *
1644     * @param rawText the raw (not decoded) value of the header. Null means
1645     * that the header was not present (in this case it always return null).
1646     * @return the MIME charset name or null if no encoding applied
1647     */

1648    static private String JavaDoc determineMailHeaderEncodingCharset(String JavaDoc rawText)
1649    {
1650        if (rawText == null) return null;
1651        int iEncodingPrefix = rawText.indexOf("=?");
1652        if (iEncodingPrefix == -1) return null;
1653        int iCharsetBegin = iEncodingPrefix + 2;
1654        int iSecondQuestionMark = rawText.indexOf('?', iCharsetBegin);
1655        if (iSecondQuestionMark == -1) return null;
1656        // safety checks
1657
if (iSecondQuestionMark == iCharsetBegin) return null; // empty charset? impossible
1658
int iThirdQuestionMark = rawText.indexOf('?', iSecondQuestionMark + 1);
1659        if (iThirdQuestionMark == -1) return null; // there must be one after encoding
1660
if (-1 == rawText.indexOf("?=", iThirdQuestionMark + 1)) return null; // closing tag
1661
String JavaDoc mimeCharset = rawText.substring(iCharsetBegin, iSecondQuestionMark);
1662        return mimeCharset;
1663    }
1664    
1665    /**
1666     * Returns a new Collection built over <I>list</I> replacing special addresses
1667     * with real <CODE>MailAddress</CODE>-es.<BR>
1668     * Manages <CODE>SpecialAddress.SENDER</CODE>, <CODE>SpecialAddress.REVERSE_PATH</CODE>,
1669     * <CODE>SpecialAddress.FROM</CODE>, <CODE>SpecialAddress.REPLY_TO</CODE>,
1670     * <CODE>SpecialAddress.RECIPIENTS</CODE>, <CODE>SpecialAddress.TO</CODE>,
1671     * <CODE>SpecialAddress.NULL</CODE> and <CODE>SpecialAddress.UNALTERED</CODE>.<BR>
1672     * <CODE>SpecialAddress.FROM</CODE> is made equivalent to <CODE>SpecialAddress.SENDER</CODE>;
1673     * <CODE>SpecialAddress.TO</CODE> is made equivalent to <CODE>SpecialAddress.RECIPIENTS</CODE>.<BR>
1674     * <CODE>SpecialAddress.REPLY_TO</CODE> uses the ReplyTo header if available, otherwise the
1675     * From header if available, otherwise the Sender header if available, otherwise the return-path.<BR>
1676     * <CODE>SpecialAddress.NULL</CODE> and <CODE>SpecialAddress.UNALTERED</CODE> are ignored.<BR>
1677     * Any other address is not replaced.
1678     */

1679    protected Collection JavaDoc replaceMailAddresses(Mail mail, Collection JavaDoc list) {
1680        Collection JavaDoc newList = new HashSet JavaDoc(list.size());
1681        Iterator JavaDoc iterator = list.iterator();
1682        while (iterator.hasNext()) {
1683            MailAddress mailAddress = (MailAddress) iterator.next();
1684            if (!mailAddress.getHost().equalsIgnoreCase("address.marker")) {
1685                newList.add(mailAddress);
1686            } else if (mailAddress == SpecialAddress.SENDER || mailAddress == SpecialAddress.FROM) {
1687                MailAddress sender = mail.getSender();
1688                if (sender != null) {
1689                    newList.add(sender);
1690                }
1691            } else if (mailAddress == SpecialAddress.REPLY_TO) {
1692                int parsedAddressCount = 0;
1693                try {
1694                    InternetAddress JavaDoc[] replyToArray = (InternetAddress JavaDoc[]) mail.getMessage().getReplyTo();
1695                    if (replyToArray != null) {
1696                        for (int i = 0; i < replyToArray.length; i++) {
1697                            try {
1698                                newList.add(new MailAddress(replyToArray[i]));
1699                                parsedAddressCount++;
1700                            } catch (ParseException JavaDoc pe) {
1701                                log("Unable to parse a \"REPLY_TO\" header address in the original message: " + replyToArray[i] + "; ignoring.");
1702                            }
1703                        }
1704                    }
1705                } catch (MessagingException JavaDoc ae) {
1706                    log("Unable to parse the \"REPLY_TO\" header in the original message; ignoring.");
1707                }
1708                // no address was parsed?
1709
if (parsedAddressCount == 0) {
1710                    MailAddress sender = mail.getSender();
1711                    if (sender != null) {
1712                        newList.add(sender);
1713                    }
1714                }
1715            } else if (mailAddress == SpecialAddress.REVERSE_PATH) {
1716                MailAddress reversePath = mail.getSender();
1717                if (reversePath != null) {
1718                    newList.add(reversePath);
1719                }
1720            } else if (mailAddress == SpecialAddress.RECIPIENTS || mailAddress == SpecialAddress.TO) {
1721                newList.addAll(mail.getRecipients());
1722            } else if (mailAddress == SpecialAddress.UNALTERED) {
1723                continue;
1724            } else if (mailAddress == SpecialAddress.NULL) {
1725                continue;
1726            } else {
1727                newList.add(mailAddress);
1728            }
1729        }
1730        return newList;
1731    }
1732
1733    /**
1734     * Returns a new Collection built over <I>list</I> replacing special addresses
1735     * with real <CODE>InternetAddress</CODE>-es.<BR>
1736     * Manages <CODE>SpecialAddress.SENDER</CODE>, <CODE>SpecialAddress.REVERSE_PATH</CODE>,
1737     * <CODE>SpecialAddress.FROM</CODE>, <CODE>SpecialAddress.REPLY_TO</CODE>,
1738     * <CODE>SpecialAddress.RECIPIENTS</CODE>, <CODE>SpecialAddress.TO</CODE>,
1739     * <CODE>SpecialAddress.NULL</CODE> and <CODE>SpecialAddress.UNALTERED</CODE>.<BR>
1740     * <CODE>SpecialAddress.RECIPIENTS</CODE> is made equivalent to <CODE>SpecialAddress.TO</CODE>.<BR>
1741     * <CODE>SpecialAddress.FROM</CODE> uses the From header if available, otherwise the Sender header if available,
1742     * otherwise the return-path.<BR>
1743     * <CODE>SpecialAddress.REPLY_TO</CODE> uses the ReplyTo header if available, otherwise the
1744     * From header if available, otherwise the Sender header if available, otherwise the return-path.<BR>
1745     * <CODE>SpecialAddress.UNALTERED</CODE> is ignored.<BR>
1746     * Any other address is not replaced.<BR>
1747     */

1748    protected Collection JavaDoc replaceInternetAddresses(Mail mail, Collection JavaDoc list) throws MessagingException JavaDoc {
1749        Collection JavaDoc newList = new HashSet JavaDoc(list.size());
1750        Iterator JavaDoc iterator = list.iterator();
1751        while (iterator.hasNext()) {
1752            InternetAddress JavaDoc internetAddress = (InternetAddress JavaDoc) iterator.next();
1753            MailAddress mailAddress = new MailAddress(internetAddress);
1754            if (!mailAddress.getHost().equalsIgnoreCase("address.marker")) {
1755                newList.add(internetAddress);
1756            } else if (internetAddress.equals(SpecialAddress.SENDER.toInternetAddress())) {
1757                MailAddress sender = mail.getSender();
1758                if (sender != null) {
1759                    newList.add(sender.toInternetAddress());
1760                }
1761            } else if (internetAddress.equals(SpecialAddress.REVERSE_PATH.toInternetAddress())) {
1762                MailAddress reversePath = mail.getSender();
1763                if (reversePath != null) {
1764                    newList.add(reversePath.toInternetAddress());
1765                }
1766            } else if (internetAddress.equals(SpecialAddress.FROM.toInternetAddress())) {
1767                try {
1768                    InternetAddress JavaDoc[] fromArray = (InternetAddress JavaDoc[]) mail.getMessage().getFrom();
1769                    if (fromArray != null) {
1770                        for (int i = 0; i < fromArray.length; i++) {
1771                            newList.add(fromArray[i]);
1772                        }
1773                    } else {
1774                        MailAddress reversePath = mail.getSender();
1775                        if (reversePath != null) {
1776                            newList.add(reversePath.toInternetAddress());
1777                        }
1778                    }
1779                } catch (MessagingException JavaDoc me) {
1780                    log("Unable to parse the \"FROM\" header in the original message; ignoring.");
1781                }
1782            } else if (internetAddress.equals(SpecialAddress.REPLY_TO.toInternetAddress())) {
1783                try {
1784                    InternetAddress JavaDoc[] replyToArray = (InternetAddress JavaDoc[]) mail.getMessage().getReplyTo();
1785                    if (replyToArray != null) {
1786                        for (int i = 0; i < replyToArray.length; i++) {
1787                            newList.add(replyToArray[i]);
1788                        }
1789                    } else {
1790                        MailAddress reversePath = mail.getSender();
1791                        if (reversePath != null) {
1792                            newList.add(reversePath.toInternetAddress());
1793                        }
1794                    }
1795                } catch (MessagingException JavaDoc me) {
1796                    log("Unable to parse the \"REPLY_TO\" header in the original message; ignoring.");
1797                }
1798            } else if (internetAddress.equals(SpecialAddress.TO.toInternetAddress())
1799                       || internetAddress.equals(SpecialAddress.RECIPIENTS.toInternetAddress())) {
1800                try {
1801                    String JavaDoc[] toHeaders = mail.getMessage().getHeader(RFC2822Headers.TO);
1802                    if (toHeaders != null) {
1803                        for (int i = 0; i < toHeaders.length; i++) {
1804                            try {
1805                                InternetAddress JavaDoc[] originalToInternetAddresses = InternetAddress.parse(toHeaders[i], false);
1806                                for (int j = 0; j < originalToInternetAddresses.length; j++) {
1807                                    newList.add(originalToInternetAddresses[j]);
1808                                }
1809                            } catch (MessagingException JavaDoc ae) {
1810                                log("Unable to parse a \"TO\" header address in the original message: " + toHeaders[i] + "; ignoring.");
1811                            }
1812                        }
1813                    }
1814                } catch (MessagingException JavaDoc ae) {
1815                    log("Unable to parse the \"TO\" header in the original message; ignoring.");
1816                }
1817            } else if (internetAddress.equals(SpecialAddress.UNALTERED.toInternetAddress())) {
1818                continue;
1819            } else if (internetAddress.equals(SpecialAddress.NULL.toInternetAddress())) {
1820                continue;
1821            } else {
1822                newList.add(internetAddress);
1823            }
1824        }
1825        return newList;
1826    }
1827
1828}
1829
Popular Tags