FileDocCategorySizeDatePackage
Redirect.javaAPI DocApache James 2.3.118261Fri Jan 12 12:56:30 GMT 2007org.apache.james.transport.mailets

Redirect.java

/****************************************************************
 * Licensed to the Apache Software Foundation (ASF) under one   *
 * or more contributor license agreements.  See the NOTICE file *
 * distributed with this work for additional information        *
 * regarding copyright ownership.  The ASF licenses this file   *
 * to you under the Apache License, Version 2.0 (the            *
 * "License"); you may not use this file except in compliance   *
 * with the License.  You may obtain a copy of the License at   *
 *                                                              *
 *   http://www.apache.org/licenses/LICENSE-2.0                 *
 *                                                              *
 * Unless required by applicable law or agreed to in writing,   *
 * software distributed under the License is distributed on an  *
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
 * KIND, either express or implied.  See the License for the    *
 * specific language governing permissions and limitations      *
 * under the License.                                           *
 ****************************************************************/

package org.apache.james.transport.mailets;

import java.util.Collection;
import java.util.HashSet;


import javax.mail.MessagingException;
import javax.mail.internet.InternetAddress;

import org.apache.mailet.Mail;
import org.apache.mailet.MailAddress;


/**
 * <P>A mailet providing configurable redirection services.</P>
 * <P>Can produce listserver, forward and notify behaviour, with the original
 * message intact, attached, appended or left out altogether.</P>
 * <P>It differs from {@link Resend} because
 * (i) some defaults are different,
 * notably for the following parameters: <I><recipients></I>, <I><to></I>,
 * <I><reversePath></I> and <I><inline></I>;
 * (ii) because it allows the use of the <I><static></I> parameter;.<BR>
 * Use <CODE>Resend</CODE> if you need full control, <CODE>Redirect</CODE> if
 * the more automatic behaviour of some parameters is appropriate.</P>
 * <P>This built in functionality is controlled by the configuration as laid out below.
 * In the table please note that the parameters controlling message headers
 * accept the <B>"unaltered"</B> value, whose meaning is to keep the associated
 * header unchanged and, unless stated differently, corresponds to the assumed default
 * if the parameter is missing.</P>
 * <P>The configuration parameters are:</P>
 * <TABLE width="75%" border="1" cellspacing="2" cellpadding="2">
 * <TR valign=top>
 * <TD width="20%"><recipients></TD>
 * <TD width="80%">
 * A comma delimited list of addresses for recipients of
 * this message; it will use the "to" list if not specified, and "unaltered"
 * if none of the lists is specified.<BR>
 * These addresses will only appear in the To: header if no "to" list is
 * supplied.<BR>
 * Such addresses can contain "full names", like
 * <I>Mr. John D. Smith <john.smith@xyz.com></I>.<BR>
 * The list can include constants "sender", "from", "replyTo", "postmaster", "reversePath", "recipients", "to", "null" and "unaltered";
 * "replyTo" uses the ReplyTo header if available, otherwise the
 * From header if available, otherwise the Sender header if available, otherwise the return-path;
 * "from" is made equivalent to "sender", and "to" is made equivalent to "recipients";
 * "null" is ignored.
 * </TD>
 * </TR>
 * <TR valign=top>
 * <TD width="20%"><to></TD>
 * <TD width="80%">
 * A comma delimited list of addresses to appear in the To: header;
 * the email will be delivered to any of these addresses if it is also in the recipients
 * list.<BR>
 * The recipients list will be used if this list is not supplied;
 * if none of the lists is specified it will be "unaltered".<BR>
 * Such addresses can contain "full names", like
 * <I>Mr. John D. Smith <john.smith@xyz.com></I>.<BR>
 * The list can include constants "sender", "from", "replyTo", "postmaster", "reversePath", "recipients", "to", "null" and "unaltered";
 * "from" uses the From header if available, otherwise the Sender header if available,
 * otherwise the return-path;
 * "replyTo" uses the ReplyTo header if available, otherwise the
 * From header if available, otherwise the Sender header if available, otherwise the return-path;
 * "recipients" is made equivalent to "to";
 * if "null" is specified alone it will remove this header.
 * </TD>
 * </TR>
 * <TR valign=top>
 * <TD width="20%"><sender></TD>
 * <TD width="80%">
 * A single email address to appear in the From: and Return-Path: headers and become the sender.<BR>
 * It can include constants "sender", "postmaster" and "unaltered";
 * "sender" is equivalent to "unaltered".<BR>
 * Default: "unaltered".
 * </TD>
 * </TR>
 * <TR valign=top>
 * <TD width="20%"><message></TD>
 * <TD width="80%">
 * A text message to insert into the body of the email.<BR>
 * Default: no message is inserted.
 * </TD>
 * </TR>
 * <TR valign=top>
 * <TD width="20%"><inline></TD>
 * <TD width="80%">
 * <P>One of the following items:</P>
 * <UL>
 * <LI>unaltered     The original message is the new
 * message, for forwarding/aliasing</LI>
 * <LI>heads          The
 * headers of the original message are appended to the message</LI>
 * <LI>body           The
 * body of the original is appended to the new message</LI>
 * <LI>all               Both
 * headers and body are appended</LI>
 * <LI>none           Neither
 * body nor headers are appended</LI>
 * </UL>
 * Default: "body".
 * </TD>
 * </TR>
 * <TR valign=top>
 * <TD width="20%"><attachment></TD>
 * <TD width="80%">
 * <P>One of the following items:</P>
 * <UL>
 * <LI>heads      The headers of the original
 * are attached as text</LI>
 * <LI>body       The body of the original
 * is attached as text</LI>
 * <LI>all           Both
 * headers and body are attached as a single text file</LI>
 * <LI>none       Nothing is attached</LI>
 * <LI>message  The original message is attached as type message/rfc822,
 * this means that it can, in many cases, be opened, resent, fw'd, replied
 * to etc by email client software.</LI>
 * </UL>
 * Default: "none".
 * </TD>
 * </TR>
 * <TR valign=top>
 * <TD width="20%"><passThrough></TD>
 * <TD width="80%">
 * true or false, if true the original message continues in the
 * mailet processor after this mailet is finished. False causes the original
 * to be stopped.<BR>
 * Default: false.
 * </TD>
 * </TR>
 * <TR valign=top>
 * <TD width="20%"><fakeDomainCheck></TD>
 * <TD width="80%">
 * true or false, if true will check if the sender domain is valid.<BR>
 * Default: true.
 * </TD>
 * </TR>
 * <TR valign=top>
 * <TD width="20%"><attachError></TD>
 * <TD width="80%">
 * true or false, if true any error message available to the
 * mailet is appended to the message body (except in the case of inline ==
 * unaltered).<BR>
 * Default: false.
 * </TD>
 * </TR>
 * <TR valign=top>
 * <TD width="20%"><replyTo></TD>
 * <TD width="80%">
 * A single email address to appear in the Reply-To: header.<BR>
 * It can include constants "sender", "postmaster" "null" and "unaltered";
 * if "null" is specified it will remove this header.<BR>
 * Default: "unaltered".
 * </TD>
 * </TR>
 * </TR>
 * <TR valign=top>
 * <TD width="20%"><reversePath></TD>
 * <TD width="80%">
 * A single email address to appear in the Return-Path: header.<BR>
 * It can include constants "sender", "postmaster" and "null";
 * if "null" is specified then it will set it to <>, meaning "null return path".<BR>
 * Notice: the "unaltered" value is <I>not allowed</I>.<BR>
 * Default: the value of the <I><sender></I> parameter, if set, otherwise remains unaltered.
 * </TD>
 * </TR>
 * <TR valign=top>
 * <TD width="20%"><subject></TD>
 * <TD width="80%">
 * An optional string to use as the subject.<BR>
 * Default: keep the original message subject.
 * </TD>
 * </TR>
 * <TR valign=top>
 * <TD width="20%"><prefix></TD>
 * <TD width="80%">
 * An optional subject prefix prepended to the original message
 * subject, or to a new subject specified with the <I><subject></I> parameter.<BR>
 * For example: <I>[Undeliverable mail]</I>.<BR>
 * Default: "".
 * </TD>
 * </TR>
 * <TR valign=top>
 * <TD width="20%"><isReply></TD>
 * <TD width="80%">
 * true or false, if true the IN_REPLY_TO header will be set to the
 * id of the current message.<BR>
 * Default: false.
 * </TD>
 * </TR>
 * <TR valign=top>
 * <TD width="20%"><debug></TD>
 * <TD width="80%">
 * true or false.  If this is true it tells the mailet to write some debugging
 * information to the mailet log.<BR>
 * Default: false.
 * </TD>
 * </TR>
 * <TR valign=top>
 * <TD width="20%"><static></TD>
 * <TD width="80%">
 * true or false.  If this is true it tells the mailet that it can
 * reuse all the initial parameters (to, from, etc) without re-calculating
 * 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.<BR>
 * Default: false.
 * </TD>
 * </TR>
 * </TABLE>
 *
 * <P>Example:</P>
 * <PRE><CODE>
 *  <mailet match="RecipientIs=test@localhost" class="Redirect">
 *    <recipients>x@localhost, y@localhost, z@localhost</recipients>
 *    <to>list@localhost</to>
 *    <sender>owner@localhost</sender>
 *    <message>sent on from James</message>
 *    <inline>unaltered</inline>
 *    <passThrough>FALSE</passThrough>
 *    <replyTo>postmaster</replyTo>
 *    <prefix xml:space="preserve">[test mailing] </prefix>
 *    <!-- note the xml:space="preserve" to preserve whitespace -->
 *    <static>TRUE</static>
 * </mailet>
 * </CODE></PRE>
 * 
 * <P>and:</P>
 *
 * <PRE><CODE>
 *  <mailet match="All" class="Redirect">
 *    <recipients>x@localhost</recipients>
 *    <sender>postmaster</sender>
 *    <message xml:space="preserve">Message marked as spam:</message>
 *    <inline>heads</inline>
 *    <attachment>message</attachment>
 *    <passThrough>FALSE</passThrough>
 *    <attachError>TRUE</attachError>
 *    <replyTo>postmaster</replyTo>
 *    <prefix>[spam notification]</prefix>
 *    <static>TRUE</static>
 *  </mailet>
 * </CODE></PRE>
 * <P><I>replyto</I> can be used instead of
 * <I>replyTo</I>; such name is kept for backward compatibility.</P>
 *
 * @version CVS $Revision: 494012 $ $Date: 2007-01-08 11:23:58 +0100 (Mo, 08 Jan 2007) $
 */

public class Redirect extends AbstractRedirect {

    /**
     * Returns a string describing this mailet.
     *
     * @return a string describing this mailet
     */
    public String getMailetInfo() {
        return "Redirect Mailet";
    }

    /** Gets the expected init parameters. */
    protected  String[] getAllowedInitParameters() {
        String[] allowedArray = {
            "static",
            "debug",
            "passThrough",
            "fakeDomainCheck",
            "inline",
            "attachment",
            "message",
            "recipients",
            "to",
            "replyTo",
            "replyto",
            "reversePath",
            "sender",
            "subject",
            "prefix",
            "attachError",
            "isReply"
        };
        return allowedArray;
    }

    /* ******************************************************************** */
    /* ****************** Begin of getX and setX methods ****************** */
    /* ******************************************************************** */

    /**
     * @return the <CODE>static</CODE> init parameter
    */
    protected boolean isStatic() {
        return isStatic;
    }

    /**
     * @return the <CODE>inline</CODE> init parameter
     */
    protected int getInLineType() throws MessagingException {
        return getTypeCode(getInitParameter("inline","body"));
    }

    /**
     * @return the <CODE>recipients</CODE> init parameter
     * or the postmaster address
     * or <CODE>SpecialAddress.SENDER</CODE>
     * or <CODE>SpecialAddress.REVERSE_PATH</CODE>
     * or <CODE>SpecialAddress.UNALTERED</CODE>
     * or the <CODE>to</CODE> init parameter if missing
     * or <CODE>null</CODE> if also the latter is missing
     */
    protected Collection getRecipients() throws MessagingException {
        Collection newRecipients = new HashSet();
        String addressList = getInitParameter("recipients",getInitParameter("to"));
                                 
        // if nothing was specified, return <CODE>null</CODE> meaning no change
        if (addressList == null) {
            return null;
        }

        try {
            InternetAddress[] iaarray = InternetAddress.parse(addressList, false);
            for (int i = 0; i < iaarray.length; i++) {
                String addressString = iaarray[i].getAddress();
                MailAddress specialAddress = getSpecialAddress(addressString,
                new String[] {"postmaster", "sender", "from", "replyTo", "reversePath", "unaltered", "recipients", "to", "null"});
                if (specialAddress != null) {
                    newRecipients.add(specialAddress);
                } else {
                    newRecipients.add(new MailAddress(iaarray[i]));
                }
            }
        } catch (Exception e) {
            throw new MessagingException("Exception thrown in getRecipients() parsing: " + addressList, e);
        }
        if (newRecipients.size() == 0) {
            throw new MessagingException("Failed to initialize \"recipients\" list; empty <recipients> init parameter found.");
        }

        return newRecipients;
    }

    /**
     * @return the <CODE>to</CODE> init parameter
     * or the postmaster address
     * or <CODE>SpecialAddress.SENDER</CODE>
     * or <CODE>SpecialAddress.REVERSE_PATH</CODE>
     * or <CODE>SpecialAddress.UNALTERED</CODE>
     * or the <CODE>recipients</CODE> init parameter if missing
     * or <CODE>null</CODE> if also the latter is missing
     */
    protected InternetAddress[] getTo() throws MessagingException {
        InternetAddress[] iaarray = null;
        String addressList = getInitParameter("to",getInitParameter("recipients"));

        // if nothing was specified, return null meaning no change
        if (addressList == null) {
            return null;
        }

        try {
            iaarray = InternetAddress.parse(addressList, false);
            for(int i = 0; i < iaarray.length; ++i) {
                String addressString = iaarray[i].getAddress();
                MailAddress specialAddress = getSpecialAddress(addressString,
                                                new String[] {"postmaster", "sender", "from", "replyTo", "reversePath", "unaltered", "recipients", "to", "null"});
                if (specialAddress != null) {
                    iaarray[i] = specialAddress.toInternetAddress();
                }
            }
        } catch (Exception e) {
            throw new MessagingException("Exception thrown in getTo() parsing: " + addressList, e);
        }
        if (iaarray.length == 0) {
            throw new MessagingException("Failed to initialize \"to\" list; empty <to> init parameter found.");
        }

        return iaarray;
    }

    /**
     * @return the <CODE>reversePath</CODE> init parameter 
     * or the postmaster address
     * or <CODE>SpecialAddress.SENDER</CODE>
     * or <CODE>SpecialAddress.NULL</CODE>
     * or <CODE>null</CODE> if missing
     */
    protected MailAddress getReversePath() throws MessagingException {
        String addressString = getInitParameter("reversePath");
        if(addressString != null) {
            MailAddress specialAddress = getSpecialAddress(addressString,
                                            new String[] {"postmaster", "sender", "null"});
            if (specialAddress != null) {
                return specialAddress;
            }

            try {
                return new MailAddress(addressString);
            } catch(Exception e) {
                throw new MessagingException("Exception thrown in getReversePath() parsing: " + addressString, e);
            }
        }

        return null;
    }

    /**
     * @return {@link AbstractRedirect#getReversePath()};
     * if null return {@link AbstractRedirect#getSender(Mail)},
     * meaning the new requested sender if any
     */
    protected MailAddress getReversePath(Mail originalMail) throws MessagingException {
        MailAddress reversePath = super.getReversePath(originalMail);
        if (reversePath == null) {
            reversePath = getSender(originalMail);
        }
        return reversePath;
    }

    /* ******************************************************************** */
    /* ******************* End of getX and setX methods ******************* */
    /* ******************************************************************** */

}