james-server-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From n...@apache.org
Subject cvs commit: jakarta-james/src/java/org/apache/james/transport/mailets Redirect.java
Date Mon, 02 Jun 2003 05:40:29 GMT
noel        2003/06/01 22:40:28

  Modified:    src/java/org/apache/james/transport Tag: branch_2_1_fcs
                        LinearProcessor.java
               src/java/org/apache/james/transport/mailets Tag:
                        branch_2_1_fcs Redirect.java
  Log:
  Improved performance when recipient matching is all or nothing.
  
  Revision  Changes    Path
  No                   revision
  
  
  No                   revision
  
  
  1.10.4.3  +13 -8     jakarta-james/src/java/org/apache/james/transport/LinearProcessor.java
  
  Index: LinearProcessor.java
  ===================================================================
  RCS file: /home/cvs/jakarta-james/src/java/org/apache/james/transport/LinearProcessor.java,v
  retrieving revision 1.10.4.2
  retrieving revision 1.10.4.3
  diff -u -r1.10.4.2 -r1.10.4.3
  --- LinearProcessor.java	8 Mar 2003 21:54:07 -0000	1.10.4.2
  +++ LinearProcessor.java	2 Jun 2003 05:40:27 -0000	1.10.4.3
  @@ -73,7 +73,6 @@
   import java.util.Collection;
   import java.util.List;
   import java.util.Random;
  -import java.util.Vector;
   import java.util.Iterator;
   
   /**
  @@ -369,19 +368,25 @@
               try {
                   recipients = matcher.match(mail);
                   if (recipients == null) {
  -                    //In case the matcher returned null, create an empty Vector
  -                    recipients = new Vector();
  +                    //In case the matcher returned null, create an empty Collection
  +                    recipients = new ArrayList(0);
  +                } else if (recipients != mail.getRecipients()) {
  +                    //Make sure all the objects are MailAddress objects
  +                    verifyMailAddresses(recipients);
                   }
  -                //Make sure all the objects are MailAddress objects
  -                verifyMailAddresses(recipients);
               } catch (MessagingException me) {
                   handleException(me, mail, matcher.getMatcherConfig().getMatcherName());
               }
  +
               // Split the recipients into two pools.  notRecipients will contain the
               // recipients on the message that the matcher did not return.
  -            Collection notRecipients = new Vector();
  -            notRecipients.addAll(mail.getRecipients());
  -            notRecipients.removeAll(recipients);
  +            Collection notRecipients;
  +            if (recipients == mail.getRecipients() || recipients.size() == 0) {
  +                notRecipients = new ArrayList(0);
  +            } else {
  +                notRecipients = new ArrayList(mail.getRecipients());
  +                notRecipients.removeAll(recipients);
  +            }
   
               if (recipients.size() == 0) {
                   //Everything was not a match... store it in the next spot in the array
  
  
  
  No                   revision
  
  
  No                   revision
  
  
  1.18.4.4  +766 -213  jakarta-james/src/java/org/apache/james/transport/mailets/Redirect.java
  
  Index: Redirect.java
  ===================================================================
  RCS file: /home/cvs/jakarta-james/src/java/org/apache/james/transport/mailets/Redirect.java,v
  retrieving revision 1.18.4.3
  retrieving revision 1.18.4.4
  diff -u -r1.18.4.3 -r1.18.4.4
  --- Redirect.java	12 Mar 2003 19:37:09 -0000	1.18.4.3
  +++ Redirect.java	2 Jun 2003 05:40:28 -0000	1.18.4.4
  @@ -68,7 +68,7 @@
   import java.util.Iterator;
   import java.util.Locale;
   import java.util.StringTokenizer;
  -import java.util.Vector;
  +import java.util.ArrayList;
   
   
   import javax.mail.Message;
  @@ -81,6 +81,7 @@
   
   import org.apache.james.util.RFC2822Headers;
   import org.apache.james.util.RFC822DateFormat;
  +import org.apache.james.core.MailImpl;
   
   import org.apache.mailet.GenericMailet;
   import org.apache.mailet.Mail;
  @@ -104,9 +105,11 @@
   *<LI>getMessage(), The text of the message itself</LI>
   *<LI>getRecipients(), the recipients the mail is sent to</LI>
   *<LI>getReplyTo(), where replys to this message will be sent</LI>
  +*<LI>getReturnPath(), what to set the Return-Path to</LI>
   *<LI>getSender(), who the mail is from</LI>
   *<LI>getSubjectPrefix(), a prefix to be added to the message subject</LI>
   *<LI>getTo(), a list of people to whom the mail is *apparently* sent</LI>
  +*<LI>isReply(), should this mailet set the IN_REPLY_TO header to the id of the current message</LI>
   *<LI>getPassThrough(), should this mailet allow the original message to continue processing or GHOST it.</LI>
   *<LI>isStatic(), should this mailet run the get methods for every mail, or just
   *once. </LI>
  @@ -118,14 +121,16 @@
   *<TD width="80%">A comma delimited list of email addresses for recipients of
   *this message, it will use the &quot;to&quot; list if not specified. These
   *addresses will only appear in the To: header if no &quot;to&quot; list is
  -*supplied.</TD>
  +*supplied.<BR>
  +*It can include constants &quot;sender&quot; and &quot;postmaster&quot;</TD>
   *</TR>
   *<TR>
   *<TD width="20%">&lt;to&gt;</TD>
   *<TD width="80%">A comma delimited list of addresses to appear in the To: header,
   *the email will only be delivered to these addresses if they are in the recipients
   *list.<BR>
  -*The recipients list will be used if this is not supplied.</TD>
  +*The recipients list will be used if this is not supplied.<BR>
  +*It can include constants &quot;sender&quot;, &quot;postmaster&quot; and &quot;unaltered&quot;</TD>
   *</TR>
   *<TR>
   *<TD width="20%">&lt;sender&gt;</TD>
  @@ -186,9 +191,15 @@
   *</TR>
   *<TR>
   *<TD width="20%">&lt;replyto&gt;</TD>
  -*<TD width="80%">A single email address to appear in the Rely-To: header, can
  +*<TD width="80%">A single email address to appear in the Reply-To: header, can
   *also be &quot;sender&quot; or &quot;postmaster&quot;, this header is not
  -*set if this is omited.</TD>
  +*set if this is omitted.</TD>
  +*</TR>
  +*<TR>
  +*<TD width="20%">&lt;returnPath&gt;</TD>
  +*<TD width="80%">A single email address to appear in the Return-Path: header, can
  +*also be &quot;sender&quot; or &quot;postmaster&quot; or &quot;null&quot;; this header is not
  +*set if this parameter is omitted.</TD>
   *</TR>
   *<TR>
   *<TD width="20%">&lt;prefix&gt;</TD>
  @@ -197,6 +208,11 @@
   *Undeliverable mail: </TD>
   *</TR>
   *<TR>
  +*<TD width="20%">&lt;isReply&gt;</TD>
  +*<TD width="80%">TRUE or FALSE, if true the IN_REPLY_TO header will be set to the
  +*id of the current message.</TD>
  +*</TR>
  +*<TR>
   *<TD width="20%">&lt;static&gt;</TD>
   *<TD width="80%">
   *<P>TRUE or FALSE.  If this is TRUE it tells the mailet that it can
  @@ -204,8 +220,6 @@
   *their values.  This will boost performance where a redirect task
   *doesn't contain any dynamic values.  If this is FALSE, it tells the
   *mailet to recalculate the values for each e-mail processed.</P>
  -*<P>Note: If you use "magic words" such as "sender" in the &lt;sender&gt;
  -*tag, you must NOT use set static to TRUE.</P>
   *<P>This defaults to false.<BR>
   *</TD>
   *</TR>
  @@ -240,69 +254,127 @@
   *&lt;static&gt;TRUE&lt;/static&gt;<BR>
   *&lt;/mailet&gt;</P>
    *
  - * @author  Danny Angus   <danny@thought.co.uk>
    *
    */
  -public class Redirect extends GenericMailet {
   
  +public class Redirect extends GenericMailet {
  +            
       /**
        * Controls certain log messages
        */
  -    private boolean isDebug = false;
  +    protected boolean isDebug = false;
  +
  +    private static class AddressMarker {
  +        public static MailAddress SENDER;
  +        public static MailAddress TO;
  +        public static MailAddress RECIPIENTS;
  +        public static MailAddress DELETE;
  +        public static MailAddress UNALTERED;
  +        public static MailAddress NULL;
  +
  +        static {
  +            try {
  +                MailAddress SENDER      = new MailAddress("sender","Address.Marker");
  +                MailAddress TO          = new MailAddress("to","Address.Marker");
  +                MailAddress RECIPIENTS  = new MailAddress("recipients","Address.Marker");
  +                MailAddress DELETE      = new MailAddress("delete","Address.Marker");
  +                MailAddress UNALTERED   = new MailAddress("unaltered","Address.Marker");
  +                MailAddress NULL        = new MailAddress("null","Address.Marker");
  +
  +            } catch (Exception _) {}
  +        }
  +    }
  +
  +    protected static class SpecialAddress {
  +        public static final MailAddress SENDER      = AddressMarker.SENDER;
  +        public static final MailAddress TO          = AddressMarker.TO;
  +        public static final MailAddress RECIPIENTS  = AddressMarker.RECIPIENTS;
  +        public static final MailAddress DELETE      = AddressMarker.DELETE;
  +        public static final MailAddress UNALTERED   = AddressMarker.UNALTERED;
  +        public static final MailAddress NULL        = AddressMarker.NULL;
  +    }
   
       // The values that indicate how to attach the original mail
  -    // to the redirected mail.
  +    // to the new mail.
   
  -    private static final int UNALTERED           = 0;
  +    protected static final int UNALTERED        = 0;
   
  -    private static final int HEADS               = 1;
  +    protected static final int HEADS            = 1;
   
  -    private static final int BODY                = 2;
  +    protected static final int BODY             = 2;
   
  -    private static final int ALL                 = 3;
  +    protected static final int ALL              = 3;
   
  -    private static final int NONE                = 4;
  +    protected static final int NONE             = 4;
   
  -    private static final int MESSAGE             = 5;
  +    protected static final int MESSAGE          = 5;
   
  -    private InternetAddress[] apparentlyTo;
  +    private boolean isStatic = false;
  +    
  +    private int attachmentType = NONE;
  +    private int inLineType = BODY;
       private String messageText;
       private Collection recipients;
       private MailAddress replyTo;
  +    private MailAddress returnPath;
       private MailAddress sender;
  +    private String subjectPrefix;
  +    private InternetAddress[] apparentlyTo;
  +    private boolean attachError = false;
  +    private boolean isReply = false;
   
       private RFC822DateFormat rfc822DateFormat = new RFC822DateFormat();
   
       /**
  -     *     returns one of these values to indicate how to attach the original message
  -     *<ul>
  -     *    <li>BODY : original message body is attached as plain text to the new message</li>
  -     *    <li>HEADS : original message headers are attached as plain text to the new message</li>
  -     *    <li>ALL : original is attached as plain text with all headers</li>
  -     *    <li>MESSAGE : original message is attached as type message/rfc822, a complete mail message.</li>
  -     *    <li>NONE : original is not attached</li>
  -     *</ul>
  +     * Returns a string describing this mailet.
        *
  +     * @return a string describing this mailet
        */
  -    public int getAttachmentType() {
  -        if(getInitParameter("attachment") == null) {
  -            return NONE;
  +    public String getMailetInfo() {
  +        return "Resend Mailet";
  +    }
  +
  +    /**
  +     * Gets the <CODE>passThrough</CODE> init parameter.
  +     *
  +     * @return true to allow the original message to continue through the processor, false to GHOST it
  +     */
  +    protected boolean getPassThrough() {
  +        if(getInitParameter("passThrough") == null) {
  +            return false;
           } else {
  -            return getTypeCode(getInitParameter("attachment"));
  +            return new Boolean(getInitParameter("passThrough")).booleanValue();
           }
       }
   
       /**
  -     * returns one of these values to indicate how to append the original message
  -     *<ul>
  +     * Gets the <CODE>static</CODE> init parameter.
  +     * return true to reduce calls to getTo, getSender, getRecipients, getReplyTo, getReturnPath amd getMessage
  +     * where these values don't change (eg hard coded, or got at startup from the mailet config)<br>
  +     * return false where any of these methods generate their results dynamically eg in response to the message being processed,
  +     * or by reference to a repository of users
  +     * Is a "getX()" method.
  +     */
  +    protected boolean isStatic() {
  +        return isStatic;
  +    }
  +
  +    /**
  +     * Gets the <CODE>inline</CODE> init parameter.
  +     * May return one of the following values to indicate how to append the original message
  +     * to build the new message:
  +     * <ul>
        *    <li>UNALTERED : original message is the new message body</li>
        *    <li>BODY : original message body is appended to the new message</li>
        *    <li>HEADS : original message headers are appended to the new message</li>
        *    <li>ALL : original is appended with all headers</li>
        *    <li>NONE : original is not appended</li>
  -     *</ul>
  +     * </ul>
  +     * Is a "getX()" method.
  +     *
  +     * @return the inline type value code
        */
  -    public int getInLineType() {
  +    protected int getInLineType() throws MessagingException {
           if(getInitParameter("inline") == null) {
               return BODY;
           } else {
  @@ -311,45 +383,107 @@
       }
   
       /**
  -     * Return a string describing this mailet.
  +     * Gets the <CODE>inline</CODE> init parameter,
  +     * built dynamically using the original Mail object.
  +     * Is a "getX(Mail)" method.
        *
  -     * @return a string describing this mailet
  +     * @return {@link #getInlineType()}
        */
  -    public String getMailetInfo() {
  -        return "Resend Mailet";
  +    protected int getInLineType(Mail originalMail) throws MessagingException {
  +        int inLineType = (isStatic()) ? this.inLineType : getInLineType();
  +        return inLineType;
       }
   
       /**
  -     * must return either an empty string, or a message to which the redirect can be attached/appended
  +     * Gets the <CODE>attachment</CODE> init parameter.
  +     * May return one of the following values to indicate how to attach the original message
  +     * to the new message:
  +     * <ul>
  +     *    <li>BODY : original message body is attached as plain text to the new message</li>
  +     *    <li>HEADS : original message headers are attached as plain text to the new message</li>
  +     *    <li>ALL : original is attached as plain text with all headers</li>
  +     *    <li>MESSAGE : original message is attached as type message/rfc822, a complete mail message.</li>
  +     *    <li>NONE : original is not attached</li>
  +     * </ul>
  +     * Is a "getX()" method.
  +     *
  +     * @return the attachmentType value code
        */
  -    public String getMessage() {
  -        if(getInitParameter("message") == null) {
  -            return "";
  +    protected int getAttachmentType() throws MessagingException {
  +        if(getInitParameter("attachment") == null) {
  +            return NONE;
           } else {
  -            return getInitParameter("message");
  +            return getTypeCode(getInitParameter("attachment"));
           }
       }
   
       /**
  -     * return true to allow thie original message to continue through the processor, false to GHOST it
  +     * Gets the <CODE>attachment</CODE> init parameter,
  +     * built dynamically using the original Mail object.
  +     * Is a "getX(Mail)" method.
  +     *
  +     * @return {@link #getAttachmentType()}
        */
  -    public boolean getPassThrough() {
  -        if(getInitParameter("passThrough") == null) {
  -            return false;
  +    protected int getAttachmentType(Mail originalMail) throws MessagingException {
  +        int attachmentType = (isStatic()) ? this.attachmentType : getAttachmentType();
  +        return attachmentType;
  +    }
  +
  +    /**
  +     * Gets the <CODE>message</CODE> init parameter.
  +     * Returns a message to which the original message can be attached/appended
  +     * to build the new message.
  +     * Is a "getX()" method.
  +     *
  +     * @return the message or an empty string if parameter is missing
  +     */
  +    protected String getMessage() throws MessagingException {
  +        if(getInitParameter("message") == null) {
  +            return "";
           } else {
  -            return new Boolean(getInitParameter("passThrough")).booleanValue();
  +            return getInitParameter("message");
           }
       }
   
       /**
  -     * must return a Collection of recipient MailAddresses
  +     * Gets the <CODE>message</CODE> init parameter,
  +     * built dynamically using the original Mail object.
  +     * Is a "getX(Mail)" method.
  +     *
  +     * @return {@link #getMessage()}
        */
  -    public Collection getRecipients() {
  -        Collection newRecipients           = new HashSet();
  -        String addressList                 = (getInitParameter("recipients") == null)
  -                                                 ? getInitParameter("to")
  -                                                 : getInitParameter("recipients");
  -        StringTokenizer st                 = new StringTokenizer(addressList, ",", false);
  +    protected String getMessage(Mail originalMail) throws MessagingException {
  +        String messageText = (isStatic()) ? this.messageText : getMessage();
  +        return messageText;
  +    }
  +    
  +    /**
  +     * Gets the <CODE>recipients</CODE> init parameter.
  +     * Returns the collection of recipients of the new message.
  +     * If the <CODE>recipients</CODE> init parameter is missing,
  +     * returns the <CODE>to</CODE> init parameter.
  +     * Is a "getX()" method.
  +     *
  +     * @return the addresses or SENDER or null if missing
  +     */
  +    protected Collection getRecipients() throws MessagingException {
  +        Collection newRecipients = new HashSet();
  +        String addressList = (getInitParameter("recipients") == null)
  +                                 ? getInitParameter("to")
  +                                 : getInitParameter("recipients");
  +        // if nothing was specified, return null meaning no change
  +        if (addressList == null) {
  +            return null;
  +        }
  +        if(addressList.compareTo("postmaster") == 0) {
  +            newRecipients.add(getMailetContext().getPostmaster());
  +            return newRecipients;
  +        }
  +        if(addressList.compareTo("sender") == 0) {
  +            newRecipients.add(SpecialAddress.SENDER);
  +            return newRecipients;
  +        }
  +        StringTokenizer st = new StringTokenizer(addressList, ",", false);
           while(st.hasMoreTokens()) {
               try {
                   newRecipients.add(new MailAddress(st.nextToken()));
  @@ -361,11 +495,123 @@
       }
   
       /**
  -     * Returns the reply to address as a string.
  +     * Gets the <CODE>recipients</CODE> init parameter,
  +     * built dynamically using the original Mail object.
  +     * Is a "getX(Mail)" method.
  +     *
  +     * @return {@link #getRecipients()}, replacing SENDER if applicable
  +     */
  +    protected Collection getRecipients(Mail originalMail) throws MessagingException {
  +        // TODO: implement MailAddress.RETURN_PATH
  +        Collection recipients = (isStatic()) ? this.recipients : getRecipients();
  +        if (recipients != null && recipients.size() == 1) {
  +            if (recipients.contains(SpecialAddress.SENDER)) {
  +                recipients = new ArrayList();
  +                recipients.add(originalMail.getSender());
  +            }
  +        }
  +        return recipients;
  +    }
  +    
  +    /**
  +     * Sets the recipients of <I>newMail</I> to <I>recipients</I>.
  +     */
  +    protected void setRecipients(Mail newMail, Collection recipients, Mail originalMail) throws MessagingException {
  +        if (recipients != null) {
  +            ((MailImpl) newMail).setRecipients(recipients);
  +            if (isDebug) {
  +                log("recipients set to: " + arrayToString(recipients.toArray()));
  +            }
  +        }
  +    }
  +    
  +    /**
  +     * Gets the <CODE>to</CODE> init parameter.
  +     * Returns the "To:" recipients of the new message.
  +     * If the <CODE>to</CODE> init parameter is missing,
  +     * returns the <CODE>recipients</CODE> init parameter.
  +     * Is a "getX()" method.
  +     *
  +     * @return the addresses or SENDER or UNALTERED or null meaning no change
  +     */
  +    protected InternetAddress[] getTo() throws MessagingException {
  +        String addressList = (getInitParameter("to") == null)
  +                                 ? getInitParameter("recipients")
  +                                 : getInitParameter("to");
  +        // if nothing was specified, return null meaning no change
  +        if (addressList == null) {
  +            return null;
  +        }
  +        if(addressList.compareTo("postmaster") == 0) {
  +            InternetAddress[] iaarray = new InternetAddress[1];
  +            iaarray[0] = getMailetContext().getPostmaster().toInternetAddress();
  +            return iaarray;
  +        }
  +        if(addressList.compareTo("sender") == 0) {
  +            InternetAddress[] iaarray = new InternetAddress[1];
  +            iaarray[0] = SpecialAddress.SENDER.toInternetAddress();
  +            return iaarray;
  +        }
  +        if(addressList.compareTo("unaltered") == 0) {
  +            InternetAddress[] iaarray = new InternetAddress[1];
  +            iaarray[0] = SpecialAddress.UNALTERED.toInternetAddress();
  +            return iaarray;
  +        }
  +        StringTokenizer rec       = new StringTokenizer(addressList, ",");
  +        int tokensn               = rec.countTokens();
  +        InternetAddress[] iaarray = new InternetAddress[tokensn];
  +        String tokenx             = "";
  +        for(int i = 0; i < tokensn; ++i) {
  +            try {
  +                tokenx     = rec.nextToken();
  +                iaarray[i] = new InternetAddress(tokenx);
  +            } catch(Exception e) {
  +                log("Internet address exception in getTo()");
  +            }
  +        }
  +        return iaarray;
  +    }
  +
  +    /**
  +     * Gets the <CODE>to</CODE> init parameter,
  +     * built dynamically using the original Mail object.
  +     * Is a "getX(Mail)" method.
  +     *
  +     * @return {@link #getTo()}, replacing SENDER and UNALTERED if applicable
  +     */
  +    protected InternetAddress[] getTo(Mail originalMail) throws MessagingException {
  +        InternetAddress[] apparentlyTo = (isStatic()) ? this.apparentlyTo : getTo();
  +        if (apparentlyTo != null && apparentlyTo.length == 1) {
  +            if (apparentlyTo[0].equals(SpecialAddress.SENDER.toInternetAddress())) {
  +                apparentlyTo = new InternetAddress[1];
  +                apparentlyTo[0] = originalMail.getSender().toInternetAddress();
  +            } else if (apparentlyTo[0].equals(SpecialAddress.UNALTERED.toInternetAddress())) {
  +                apparentlyTo = (InternetAddress[]) originalMail.getMessage().getRecipients(Message.RecipientType.TO);
  +            }
  +        }
  +        return apparentlyTo;
  +    }
  +    
  +    /**
  +     * Sets the "To:" header of <I>newMail</I> to <I>to</I>.
  +     */
  +    protected void setTo(Mail newMail, InternetAddress[] to, Mail originalMail) throws MessagingException {
  +        if (to != null) {
  +            newMail.getMessage().setRecipients(Message.RecipientType.TO, to);
  +            if (isDebug) {
  +                log("apparentlyTo set to: " + arrayToString(to));
  +            }
  +        }
  +    }
  +    
  +    /**
  +     * Gets the <CODE>replyto</CODE> init parameter.
  +     * Returns the Reply-To address of the new message.
  +     * Is a "getX()" method.
        *
  -     * @return the replyto address for the mail as a string
  +     * @return an address or null if parameter is missing or == "sender" (null means "use original")
        */
  -    public MailAddress getReplyTo() {
  +    protected MailAddress getReplyTo() throws MessagingException {
           String sr = getInitParameter("replyto");
           if(sr != null) {
               MailAddress rv;
  @@ -374,6 +620,7 @@
                   return rv;
               }
               if(sr.compareTo("sender") == 0) {
  +                // means no change
                   return null;
               }
               try {
  @@ -387,9 +634,104 @@
       }
   
       /**
  -     * returns the senders address, as a MailAddress
  +     * Gets the <CODE>replyTo</CODE> init parameter,
  +     * built dynamically using the original Mail object.
  +     * Is a "getX(Mail)" method.
  +     *
  +     * @return {@link #getReplyTo()}
  +     */
  +    protected MailAddress getReplyTo(Mail originalMail) throws MessagingException {
  +        MailAddress replyTo = (isStatic()) ? this.replyTo : getReplyTo();
  +        return replyTo;
  +    }
  +    
  +    /**
  +     * Sets the "Reply-To:" header of <I>newMail</I> to <I>replyTo</I>.
  +     */
  +    protected void setReplyTo(Mail newMail, MailAddress replyTo, Mail originalMail) throws MessagingException {
  +        if(replyTo != null) {
  +            InternetAddress[] iart = new InternetAddress[1];
  +            iart[0] = replyTo.toInternetAddress();
  +            newMail.getMessage().setReplyTo(iart);
  +            if (isDebug) {
  +                log("replyTo set to: " + replyTo);
  +            }
  +        }
  +    }
  +    
  +    /**
  +     * Gets the <CODE>returnPath</CODE> init parameter.
  +     * Returns the Return-Path of the new message.
  +     * Is a "getX()" method.
  +     *
  +     * @return an address or NULL or SENDER or null if parameter is missing (null means "use original")
  +     */
  +    protected MailAddress getReturnPath() throws MessagingException {
  +        String sr = getInitParameter("returnPath");
  +        if(sr != null) {
  +            MailAddress rv;
  +            if(sr.compareTo("postmaster") == 0) {
  +                return getMailetContext().getPostmaster();
  +            }
  +            if(sr.compareTo("NULL") == 0) {
  +                return SpecialAddress.NULL;
  +            }
  +            if(sr.compareTo("sender") == 0) {
  +                return SpecialAddress.SENDER;
  +            }
  +            try {
  +                rv = new MailAddress(sr);
  +                return rv;
  +            } catch(Exception e) {
  +                log("Parse error in getReturnPath " + sr);
  +            }
  +        }
  +        return null;
  +    }
  +
  +    /**
  +     * Gets the <CODE>returnPath</CODE> init parameter,
  +     * built dynamically using the original Mail object.
  +     * Is a "getX(Mail)" method.
  +     *
  +     * @return {@link #getReturnPath()}, replacing SENDER if applicable, but not replacing NULL
  +     */
  +    protected MailAddress getReturnPath(Mail originalMail) throws MessagingException {
  +        MailAddress returnPath = (isStatic()) ? this.returnPath : getReturnPath();
  +        if (returnPath != null) {
  +            if (returnPath == SpecialAddress.SENDER) {
  +                returnPath = originalMail.getSender();
  +            }
  +        }
  +        return returnPath;
  +    }
  +    
  +    /**
  +     * Sets the "Return-Path:" header of <I>newMail</I> to <I>returnPath</I>.
  +     */
  +    protected void setReturnPath(Mail newMail, MailAddress returnPath, Mail originalMail) throws MessagingException {
  +        if(returnPath != null) {
  +            String returnPathString;
  +            if (returnPath == SpecialAddress.NULL) {
  +                returnPathString = "";
  +            } else {
  +                returnPathString = returnPath.toString();
  +            }
  +            newMail.getMessage().setHeader(RFC2822Headers.RETURN_PATH, "<" + returnPathString + ">");
  +            if (isDebug) {
  +                log("returnPath set to: " + returnPath);
  +            }
  +        }
  +    }
  +    
  +    /**
  +     * Gets the <CODE>sender</CODE> init parameter.
  +     * Returns the new sender as a MailAddress.
  +     * Is a "getX()" method.
  +     *
  +     * @return an address or null if parameter is missing or == "sender", meaning "use original"
        */
  -    public MailAddress getSender() {
  +    protected MailAddress getSender() throws MessagingException {
           String sr = getInitParameter("sender");
           if(sr != null) {
               MailAddress rv;
  @@ -398,6 +740,7 @@
                   return rv;
               }
               if(sr.compareTo("sender") == 0) {
  +                // means no change: use FROM header; kept as is for compatibility
                   return null;
               }
               try {
  @@ -411,22 +754,43 @@
       }
   
       /**
  -     * return true to reduce calls to getTo, getSender, getRecipients, getReplyTo amd getMessage
  -     * where these values don't change (eg hard coded, or got at startup from the mailet config)<br>
  -     * return false where any of these methods generate their results dynamically eg in response to the message being processed,
  -     * or by refrence to a repository of users
  +     * Gets the <CODE>sender</CODE> init parameter,
  +     * built dynamically using the original Mail object.
  +     * Is a "getX(Mail)" method.
  +     *
  +     * @return {@link #getSender()}
        */
  -    public boolean isStatic() {
  -        if(getInitParameter("static") == null) {
  -            return false;
  +    protected MailAddress getSender(Mail originalMail) throws MessagingException {
  +        MailAddress sender = (isStatic()) ? this.sender : getSender();
  +        return sender;
  +    }
  +    
  +    /**
  +     * Sets the sender and the "From:" header of <I>newMail</I> to <I>sender</I>.
  +     * If sender is null will set such values to the ones in <I>originalMail</I>.
  +     */
  +    protected void setSender(Mail newMail, MailAddress sender, Mail originalMail) throws MessagingException {
  +        if (sender == null) {
  +            MailAddress originalSender = new MailAddress(((InternetAddress) originalMail.getMessage().getFrom()[0]).getAddress());
  +            newMail.getMessage().setHeader(RFC2822Headers.FROM, originalMail.getMessage().getHeader(RFC2822Headers.FROM, ","));
  +            ((MailImpl) newMail).setSender(originalSender);
  +        } else {
  +            newMail.getMessage().setFrom(sender.toInternetAddress());
  +            ((MailImpl) newMail).setSender(sender);
  +            if (isDebug) {
  +                log("sender set to: " + sender);
  +            }
           }
  -        return new Boolean(getInitParameter("static")).booleanValue();
       }
  -
  +    
       /**
  -     * return a prefix for the message subject
  +     * Gets the <CODE>prefix</CODE> init parameter.
  +     * Returns a prefix for the new message subject.
  +     * Is a "getX()" method.
  +     *
  +     * @return the prefix or an empty string if parameter is missing
        */
  -    public String getSubjectPrefix() {
  +    protected String getSubjectPrefix() throws MessagingException {
           if(getInitParameter("prefix") == null) {
               return "";
           } else {
  @@ -435,31 +799,41 @@
       }
   
       /**
  -     * returns an array of InternetAddress 'es for the To: header
  +     * Gets the <CODE>subjectPrefix</CODE> init parameter,
  +     * built dynamically using the original Mail object.
  +     * Is a "getX(Mail)" method.
  +     *
  +     * @return {@link #getSubjectPrefix()}
        */
  -    public InternetAddress[] getTo() {
  -        String addressList        = (getInitParameter("to") == null)
  -                                        ? getInitParameter("recipients") : getInitParameter("to");
  -        StringTokenizer rec       = new StringTokenizer(addressList, ",");
  -        int tokensn               = rec.countTokens();
  -        InternetAddress[] iaarray = new InternetAddress[tokensn];
  -        String tokenx             = "";
  -        for(int i = 0; i < tokensn; ++i) {
  -            try {
  -                tokenx     = rec.nextToken();
  -                iaarray[i] = new InternetAddress(tokenx);
  -            } catch(Exception e) {
  -                log("Internet address exception in getTo()");
  -            }
  +    protected String getSubjectPrefix(Mail originalMail) throws MessagingException {
  +        String subjectPrefix = (isStatic()) ? this.subjectPrefix : getSubjectPrefix();
  +        return subjectPrefix;
  +    }
  +    
  +    /**
  +     * Builds the subject of <I>newMail</I> appending the subject
  +     * of <I>originalMail</I> to <I>subjectPrefix</I>.
  +     */
  +    protected void setSubjectPrefix(Mail newMail, String subjectPrefix, Mail originalMail) throws MessagingException {
  +        String subject = originalMail.getMessage().getSubject();
  +        if (subject == null) {
  +            subject = "";
  +        }
  +        newMail.getMessage().setSubject(subjectPrefix + subject);
  +        if (isDebug) {
  +            log("subjectPrefix set to: " + subjectPrefix);
           }
  -        return iaarray;
       }
  -
  +    
       /**
  -     * return true to append a description of any error to the main body part
  -     * if getInlineType does not return "UNALTERED"
  +     * Gets the <CODE>attachError</CODE> init parameter.
  +     * Returns a boolean indicating whether to append a description of any error to the main body part
  +     * of the new message, if getInlineType does not return "UNALTERED".
  +     * Is a "getX()" method.
  +     *
  +     * @return true or false; false if init parameter missing
        */
  -    public boolean attachError() {
  +    protected boolean attachError() throws MessagingException {
           if(getInitParameter("attachError") == null) {
               return false;
           } else {
  @@ -468,31 +842,101 @@
       }
   
       /**
  -      * init will setup static values for sender, recipients, message text, and reply to
  -      * <br> if isStatic() returns true
  -      * it calls getSender(), getReplyTo(), getMessage(), and getRecipients() and getTo()
  -      *
  -      */
  +     * Gets the <CODE>attachError</CODE> init parameter,
  +     * built dynamically using the original Mail object.
  +     * Is a "getX(Mail)" method.
  +     *
  +     * @return {@link #attachError()}
  +     */
  +    protected boolean attachError(Mail originalMail) throws MessagingException {
  +        boolean attachError = (isStatic()) ? this.attachError : attachError();
  +        return attachError;
  +    }
  +
  +    /**
  +     * Gets the <CODE>isReply</CODE> init parameter.
  +     * Returns a boolean indicating whether the new message must be considered
  +     * a reply to the original message, setting the IN_REPLY_TO header of the new
  +     * message to the id of the original message.
  +     * Is a "getX()" method.
  +     *
  +     * @return true or false; false if init parameter missing
  +     */
  +    protected boolean isReply() throws MessagingException {
  +        if(getInitParameter("isReply") == null) {
  +            return false;
  +        }
  +        return new Boolean(getInitParameter("isReply")).booleanValue();
  +    }
  +
  +    /**
  +     * Gets the <CODE>isReply</CODE> init parameter,
  +     * built dynamically using the original Mail object.
  +     * Is a "getX(Mail)" method.
  +     *
  +     * @return {@link #isReply()}
  +     */
  +    protected boolean isReply(Mail originalMail) throws MessagingException {
  +        boolean isReply = (isStatic()) ? this.isReply : isReply();
  +        return isReply;
  +    }
  +    
  +    /**
  +     * Sets the "In-Reply-To:" header of <I>newMail</I> to the "Message-Id:" of
  +     * <I>originalMail</I>, if <I>isReply</I> is true.
  +     */
  +    protected void setIsReply(Mail newMail, boolean isReply, Mail originalMail) throws MessagingException {
  +        if (isReply) {
  +            String messageId = originalMail.getMessage().getMessageID();
  +            if (messageId != null) {
  +                newMail.getMessage().setHeader(RFC2822Headers.IN_REPLY_TO, messageId);
  +                if (isDebug) {
  +                    log("IN_REPLY_TO set to: " + messageId);
  +                }
  +            }
  +        }
  +    }
  +    
  +    /**
  +     * Mailet initialization routine.
  +     * Will setup static values for each "x" initialization parameter in config.xml,
  +     * using getX(), if isStatic() returns true.
  +     *
  +     */
       public void init() throws MessagingException {
           if (isDebug) {
               log("Redirect init");
           }
           isDebug = (getInitParameter("debug") == null) ? false : new Boolean(getInitParameter("debug")).booleanValue();
  +        
  +        isStatic = (getInitParameter("static") == null) ? false : new Boolean(getInitParameter("static")).booleanValue();
  +
           if(isStatic()) {
  -            sender       = getSender();
  -            replyTo      = getReplyTo();
  -            messageText  = getMessage();
  -            recipients   = getRecipients();
  -            apparentlyTo = getTo();
  +            attachmentType  = getAttachmentType();
  +            inLineType      = getInLineType();
  +            messageText     = getMessage();
  +            recipients      = getRecipients();
  +            replyTo         = getReplyTo();
  +            returnPath      = getReturnPath();
  +            sender          = getSender();
  +            subjectPrefix   = getSubjectPrefix();
  +            apparentlyTo    = getTo();
  +            attachError     = attachError();
  +            isReply         = isReply();
               if (isDebug) {
                   StringBuffer logBuffer =
                       new StringBuffer(1024)
  -                            .append("static, sender=")
  -                            .append(sender)
  -                            .append(", replyTo=")
  -                            .append(replyTo)
  -                            .append(", message=")
  -                            .append(messageText)
  +                            .append("static, sender=").append(sender)
  +                            .append(", replyTo=").append(replyTo)
  +                            .append(", returnPath=").append(returnPath)
  +                            .append(", message=").append(messageText)
  +                            .append(", recipients=").append(arrayToString(recipients.toArray()))
  +                            .append(", subjectPrefix=").append(subjectPrefix)
  +                            .append(", apparentlyTo=").append(arrayToString(apparentlyTo))
  +                            .append(", attachError=").append(attachError)
  +                            .append(", isReply=").append(isReply)
  +                            .append(", attachmentType=").append(attachmentType)
  +                            .append(", inLineType=").append(inLineType)
                               .append(" ");
                   log(logBuffer.toString());
               }
  @@ -500,133 +944,103 @@
       }
   
       /**
  -     * Service does the hard work,and redirects the mail in the form specified
  +     * Service does the hard work,and redirects the originalMail in the form specified.
        *
  -     * @param mail the mail to process and redirect
  -     * @throws MessagingException if a problem arising formulating the redirected mail
  +     * @param originalMail the mail to process and redirect
  +     * @throws MessagingException if a problem arises formulating the redirected mail
        */
  -    public void service(Mail mail) throws MessagingException {
  -        if(!isStatic()) {
  -            sender       = getSender();
  -            replyTo      = getReplyTo();
  -            messageText  = getMessage();
  -            recipients   = getRecipients();
  -            apparentlyTo = getTo();
  -        }
  +    public void service(Mail originalMail) throws MessagingException {
  +        
  +        boolean keepMessageId = false;
   
  -        MimeMessage message = mail.getMessage();
  -        MimeMessage reply = null;
  +        // duplicates the Mail object, to be able to modify the new mail keeping the original untouched
  +        Mail newMail = ((MailImpl) originalMail).duplicate(newName((MailImpl)originalMail));
  +        
  +        if (isDebug) {
  +            MailImpl newMailImpl = (MailImpl) newMail;
  +            log("New mail - sender: " + newMailImpl.getSender()
  +                       + ", recipients: " + arrayToString(newMailImpl.getRecipients().toArray())
  +                       + ", name: " + newMailImpl.getName()
  +                       + ", remoteHost: " + newMailImpl.getRemoteHost()
  +                       + ", remoteAddr: " + newMailImpl.getRemoteAddr()
  +                       + ", state: " + newMailImpl.getState()
  +                       + ", lastUpdated: " + newMailImpl.getLastUpdated()
  +                       + ", errorMessage: " + newMailImpl.getErrorMessage());
  +        }
  +        
           //Create the message
  -        if(getInLineType() != UNALTERED) {
  +        if(getInLineType(originalMail) != UNALTERED) {
               if (isDebug) {
  -                log("Alter message inline=:" + getInLineType());
  -            }
  -            reply = new MimeMessage(Session.getDefaultInstance(System.getProperties(),
  -                                                               null));
  -            StringWriter sout = new StringWriter();
  -            PrintWriter out   = new PrintWriter(sout, true);
  -            Enumeration heads = message.getAllHeaderLines();
  -            String head       = "";
  -            StringBuffer headBuffer = new StringBuffer(1024);
  -            while(heads.hasMoreElements()) {
  -                headBuffer.append(heads.nextElement().toString()).append("\n");
  -            }
  -            head = headBuffer.toString();
  -            boolean all = false;
  -            if(messageText != null) {
  -                out.println(messageText);
  -            }
  -            switch(getInLineType()) {
  -                case ALL: //ALL:
  -                    all = true;
  -                case HEADS: //HEADS:
  -                    out.println("Message Headers:");
  -                    out.println(head);
  -                    if(!all) {
  -                        break;
  -                    }
  -                case BODY: //BODY:
  -                    out.println("Message:");
  -                    try {
  -                        out.println(message.getContent().toString());
  -                    } catch(Exception e) {
  -                        out.println("body unavailable");
  -                    }
  -                    break;
  -                default:
  -                case NONE: //NONE:
  -                    break;
  -            }
  -            MimeMultipart multipart = new MimeMultipart();
  -            //Add message as the first mime body part
  -            MimeBodyPart part       = new MimeBodyPart();
  -            part.setText(sout.toString());
  -            part.setDisposition("inline");
  -            multipart.addBodyPart(part);
  -            if(getAttachmentType() != NONE) {
  -                part = new MimeBodyPart();
  -                switch(getAttachmentType()) {
  -                    case HEADS: //HEADS:
  -                        part.setText(head);
  -                        break;
  -                    case BODY: //BODY:
  -                        try {
  -                            part.setText(message.getContent().toString());
  -                        } catch(Exception e) {
  -                            part.setText("body unavailable");
  -                        }
  -                        break;
  -                    case ALL: //ALL:
  -                        StringBuffer textBuffer =
  -                            new StringBuffer(1024)
  -                                .append(head)
  -                                .append("\n\n")
  -                                .append(message.toString());
  -                        part.setText(textBuffer.toString());
  -                        break;
  -                    case MESSAGE: //MESSAGE:
  -                        part.setContent(message, "message/rfc822");
  -                        break;
  -                }
  -                part.setDisposition("Attachment");
  -                multipart.addBodyPart(part);
  +                log("Alter message inline=:" + getInLineType(originalMail));
               }
  -            reply.setContent(multipart);
  -            reply.setHeader(RFC2822Headers.CONTENT_TYPE, multipart.getContentType());
  -            reply.setRecipients(Message.RecipientType.TO, apparentlyTo);
  +            newMail.setMessage(new MimeMessage(Session.getDefaultInstance(System.getProperties(),
  +                                                               null)));
  +            
  +            // handle the new message if altered
  +            buildAlteredMessage(newMail, originalMail);
  +            
  +            setTo(newMail, getTo(originalMail), originalMail);
  +        
           } else {
               // if we need the original, create a copy of this message to redirect
  -            reply = getPassThrough() ? new MimeMessage(message) : message;
  +            if (getPassThrough()) {
  +                newMail.setMessage(new MimeMessage(originalMail.getMessage()));
  +            }
               if (isDebug) {
                   log("Message resent unaltered.");
               }
  +            keepMessageId = true;
           }
  +        
           //Set additional headers
  -        reply.setSubject(getSubjectPrefix() + message.getSubject());
  -        if(reply.getHeader(RFC2822Headers.DATE) == null) {
  -            reply.setHeader(RFC2822Headers.DATE, rfc822DateFormat.format(new Date()));
  -        }
           
  -        //
  -         
  -        if(replyTo != null) {
  -            InternetAddress[] iart = new InternetAddress[1];
  -            iart[0] = replyTo.toInternetAddress();
  -            reply.setReplyTo(iart);
  +        setRecipients(newMail, getRecipients(originalMail), originalMail);
  +        
  +        setSubjectPrefix(newMail, getSubjectPrefix(originalMail), originalMail);
  +        
  +        if(newMail.getMessage().getHeader(RFC2822Headers.DATE) == null) {
  +            newMail.getMessage().setHeader(RFC2822Headers.DATE, rfc822DateFormat.format(new Date()));
           }
  -        if(sender == null) {
  -            reply.setHeader(RFC2822Headers.FROM, message.getHeader(RFC2822Headers.FROM, ","));
  -            sender = new MailAddress(((InternetAddress)message.getFrom()[0]).getAddress());
  -        } else {
  -            reply.setFrom(sender.toInternetAddress());
  +        
  +        setReplyTo(newMail, getReplyTo(originalMail), originalMail);
  +
  +        setReturnPath(newMail, getReturnPath(originalMail), originalMail);
  +
  +        setSender(newMail, getSender(originalMail), originalMail);
  +        
  +        setIsReply(newMail, isReply(originalMail), originalMail);
  +
  +        newMail.getMessage().saveChanges();
  +        
  +        if (keepMessageId) {
  +            setMessageId(newMail, originalMail);
           }
  +
           //Send it off...
  -        getMailetContext().sendMail(sender, recipients, reply);
  +        getMailetContext().sendMail(newMail);
  +        
           if(!getPassThrough()) {
  -            mail.setState(Mail.GHOST);
  +            originalMail.setState(Mail.GHOST);
           }
       }
   
  +    private static final java.util.Random random = new java.util.Random();  // Used to generate new mail names
  +    /**
  +     * Create a unique new primary key name.
  +     *
  +     * @param mail the mail to use as the basis for the new mail name
  +     * 
  +     * @return a new name
  +     */
  +    private String newName(MailImpl mail) {
  +        StringBuffer nameBuffer =
  +                                 new StringBuffer(64)
  +                                 .append(mail.getName())
  +                                 .append("-!")
  +                                 .append(random.nextInt(1048576));
  +        return nameBuffer.toString();
  +    }
  +
       /**
        * A private method to convert types from string to int.
        *
  @@ -656,5 +1070,144 @@
               return MESSAGE;
           }
           return NONE;
  +    }
  +
  +    /**
  +     * Utility method for obtaining a string representation of an array of Objects.
  +     */
  +    private String arrayToString(Object[] array) {
  +        StringBuffer sb = new StringBuffer(1024);
  +        sb.append("[");
  +        for (int i = 0; i < array.length; i++) {
  +            sb.append(array[i]);
  +        }
  +        sb.append("]");
  +        return sb.toString();
  +    }
  +
  +    /**
  +     * Builds the message of the newMail in case it has to be altered.
  +     *
  +     * @param originalMail the original Mail object
  +     * @param newMail the Mail object to build
  +     */
  +    protected void buildAlteredMessage(Mail newMail, Mail originalMail) throws MessagingException {
  +
  +        MimeMessage message = originalMail.getMessage();
  +        
  +        StringWriter sout = new StringWriter();
  +        PrintWriter out   = new PrintWriter(sout, true);
  +        Enumeration heads = message.getAllHeaderLines();
  +        String head       = "";
  +        StringBuffer headBuffer = new StringBuffer(1024);
  +        while(heads.hasMoreElements()) {
  +            headBuffer.append(heads.nextElement().toString()).append("\n");
  +        }
  +        head = headBuffer.toString();
  +        boolean all = false;
  +        
  +        String messageText = getMessage(originalMail);
  +        if(messageText != null) {
  +            out.println(messageText);
  +        }
  +        
  +        switch(getInLineType(originalMail)) {
  +            case ALL: //ALL:
  +                all = true;
  +            case HEADS: //HEADS:
  +                out.println("Message Headers:");
  +                out.println(head);
  +                if(!all) {
  +                    break;
  +                }
  +            case BODY: //BODY:
  +                out.println("Message:");
  +                try {
  +                    out.println(message.getContent().toString());
  +                } catch(Exception e) {
  +                    out.println("body unavailable");
  +                }
  +                break;
  +            default:
  +            case NONE: //NONE:
  +                break;
  +        }
  +
  +        try {
  +            //Create the message body
  +            MimeMultipart multipart = new MimeMultipart("mixed");
  +
  +            // Create the message
  +            MimeMultipart mpContent = new MimeMultipart("alternative");
  +            MimeBodyPart contentPartRoot = new MimeBodyPart();
  +            contentPartRoot.setContent(mpContent);
  +
  +            multipart.addBodyPart(contentPartRoot);
  +
  +            MimeBodyPart part = new MimeBodyPart();
  +            part.setText(sout.toString());
  +            part.setDisposition("inline");
  +            mpContent.addBodyPart(part);
  +            if(getAttachmentType() != NONE) {
  +                part = new MimeBodyPart();
  +                switch(getAttachmentType()) {
  +                    case HEADS: //HEADS:
  +                        part.setText(head);
  +                        break;
  +                    case BODY: //BODY:
  +                        try {
  +                            part.setText(message.getContent().toString());
  +                        } catch(Exception e) {
  +                            part.setText("body unavailable");
  +                        }
  +                        break;
  +                    case ALL: //ALL:
  +                        StringBuffer textBuffer =
  +                            new StringBuffer(1024)
  +                                .append(head)
  +                                .append("\n\n")
  +                                .append(message.toString());
  +                        part.setText(textBuffer.toString());
  +                        break;
  +                    case MESSAGE: //MESSAGE:
  +                        part.setContent(message, "message/rfc822");
  +                        break;
  +                }
  +                if ((message.getSubject() != null) && (message.getSubject().trim().length() > 0)) {
  +                    part.setFileName(message.getSubject().trim());
  +                } else {
  +                    part.setFileName("No Subject");
  +                }
  +                part.setDisposition("Attachment");
  +                multipart.addBodyPart(part);
  +            }
  +            //if set, attach the full stack trace
  +            if (attachError(originalMail) && originalMail.getErrorMessage() != null) {
  +                part = new MimeBodyPart();
  +                part.setContent(originalMail.getErrorMessage(), "text/plain");
  +                part.setHeader(RFC2822Headers.CONTENT_TYPE, "text/plain");
  +                part.setFileName("Reasons");
  +                part.setDisposition(javax.mail.Part.ATTACHMENT);
  +                multipart.addBodyPart(part);
  +            }
  +            newMail.getMessage().setContent(multipart);
  +            newMail.getMessage().setHeader(RFC2822Headers.CONTENT_TYPE, multipart.getContentType());
  +            
  +        } catch (Exception ioe) {
  +            throw new MessagingException("Unable to create multipart body", ioe);
  +        }
  +    }
  +    
  +    /**
  +     * Sets the message id of originalMail into newMail.
  +     */
  +    private void setMessageId(Mail newMail, Mail originalMail) throws MessagingException {
  +        String messageId = originalMail.getMessage().getMessageID();
  +        if (messageId != null) {
  +            newMail.getMessage().setHeader(RFC2822Headers.MESSAGE_ID, messageId);
  +            if (isDebug) {
  +                log("MESSAGE_ID restored to: " + messageId);
  +            }
  +        }
       }
   }
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: james-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: james-dev-help@jakarta.apache.org


Mime
View raw message