mitm.application.djigzo.james.mailets.Notify.java Source code

Java tutorial

Introduction

Here is the source code for mitm.application.djigzo.james.mailets.Notify.java

Source

/*
 * Copyright (c) 2008-2012, Martijn Brinkers, Djigzo.
 * 
 * This file is part of Djigzo email encryption.
 *
 * Djigzo is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License 
 * version 3, 19 November 2007 as published by the Free Software 
 * Foundation.
 *
 * Djigzo is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public 
 * License along with Djigzo. If not, see <http://www.gnu.org/licenses/>
 *
 * Additional permission under GNU AGPL version 3 section 7
 * 
 * If you modify this Program, or any covered work, by linking or 
 * combining it with aspectjrt.jar, aspectjweaver.jar, tyrex-1.0.3.jar, 
 * freemarker.jar, dom4j.jar, mx4j-jmx.jar, mx4j-tools.jar, 
 * spice-classman-1.0.jar, spice-loggerstore-0.5.jar, spice-salt-0.8.jar, 
 * spice-xmlpolicy-1.0.jar, saaj-api-1.3.jar, saaj-impl-1.3.jar, 
 * wsdl4j-1.6.1.jar (or modified versions of these libraries), 
 * containing parts covered by the terms of Eclipse Public License, 
 * tyrex license, freemarker license, dom4j license, mx4j license,
 * Spice Software License, Common Development and Distribution License
 * (CDDL), Common Public License (CPL) the licensors of this Program grant 
 * you additional permission to convey the resulting work.
 */
package mitm.application.djigzo.james.mailets;

import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.mail.Address;
import javax.mail.MessagingException;
import javax.mail.Message.RecipientType;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.ParseException;

import mitm.application.djigzo.User;
import mitm.application.djigzo.james.DjigzoMailAttributes;
import mitm.application.djigzo.james.DjigzoMailAttributesImpl;
import mitm.application.djigzo.james.MailAddressUtils;
import mitm.application.djigzo.workflow.UserWorkflow;
import mitm.common.mail.EmailAddressUtils;
import mitm.common.properties.HierarchicalPropertiesException;

import org.apache.commons.lang.StringUtils;
import org.apache.mailet.Mail;
import org.apache.mailet.MailAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Notify is an extension of SendMailTemplate that allows some mail parameters to be set (like subject, from, to etc.).
 * 
 * The following template parameters are supported:
 * 
 * subject    : Set to subject of source mail (String)
 * from       : Set to from of source mail (only the first from is used) (Address)
 * replyTo    : Set to replyTo of source mail (only the first replyTo is used) (Address)
 * to         : Set to 'to' of the source mail (collection of Address)
 * cc         : Set to 'cc' of the source mail (collection of Address)
 * sender     : Set to the sender of the mail (MailAddress)
 * recipients : Set to recipients of the source mail (collection of MailAddress)
 * body       : Set to null
 * date       : Current date (Date)
 * mail       : The source mail (Mail)
 * 
 * Supported mailet parameters:
 * 
 * template                : Path to the Freemarker template file. The file must exist (no default, must be specified)
 * templateProperty        : The user property from which a template is read
 * processor               : The next processor for the new mail (default: root)
 * passThrough             : If false the source message will be removed (ghost'ed)
 * subject                 : The subject of the notification
 * from                    : The from header of the notification
 * replyTo                 : The replyTo header of the notification
 * to                      : The to header of the notification
 * cc                      : The cc header of the notification
 * sender                  : The sender of the notification
 * recipients              : The recipients of the notification
 * catchRuntimeExceptions  : If true all RunTimeExceptions are caught (default: true)
 * catchErrors             : If true all Errors are caught (default: true)
 * 
 * Some parameters support the following special addresses:
 * 
 * sameAsRecipients
 * sameAsMessage
 * originator
 * replyTo
 * sender
 * from
 * 
 * @author Martijn Brinkers
 *
 */
public class Notify extends SenderTemplatePropertySendMail {
    private final static Logger logger = LoggerFactory.getLogger(Notify.class);

    /*
     * Pattern to detect user variable references in special email addresses (i.e., address 
     * like #{some.var}
     */
    private final static Pattern USER_VAR_PATTERN = Pattern.compile("#\\{\\s*(.*?)\\s*\\}");

    /*
     * Pattern to detect Mail attribute pattern like ${some.var}
     */
    private final static Pattern MAIL_ATTR_PATTERN = Pattern.compile("\\$\\{\\s*(.*?)\\s*\\}");

    /*
     * Subject of the notification
     */
    private String subject;

    /*
     * from of the notification
     */
    private Address from;

    /*
     * replyTo of the notification
     */
    private String replyTo;

    /*
     * to headers of the notification
     */
    private String to;

    /*
     * cc headers of the notification
     */
    private String cc;

    /*
     * The sender of the notification
     */
    private MailAddress sender;

    /*
     * The recipients of the notification
     */
    private String recipients;

    /*
     * The mailet initialization parameters used by this mailet.
     */
    private enum Parameter {
        SUBJECT("subject"), FROM("from"), REPLY_TO("replyTo"), TO("to"), CC("cc"), SENDER("sender"), RECIPIENTS(
                "recipients");

        private String name;

        private Parameter(String name) {
            this.name = name;
        }
    };

    @Override
    protected Logger getLogger() {
        return logger;
    }

    private String checkNull(String input) {
        return input == null || SpecialAddress.fromName(input) == SpecialAddress.NULL ? null : input;
    }

    private Address toAddress(String email) throws AddressException {
        Address address = null;

        if (email != null) {
            address = new InternetAddress(email, false);
        }

        return address;
    }

    /*
     * We will override this method because we want the default setting for removal off
     * SMTP extensions from false to true for the Notify class.
     */
    @Override
    protected boolean getInitRemoveSMTPExtensions() {
        return getBooleanInitParameter(SendMailTemplate.Parameter.REMOVE_SMTP_EXTENSIONS.getName(), true);
    }

    @Override
    public void initMailet() throws MessagingException {
        super.initMailet();

        subject = getInitParameter(Parameter.SUBJECT.name);
        from = toAddress(checkNull(getInitParameter(Parameter.FROM.name)));
        replyTo = checkNull(getInitParameter(Parameter.REPLY_TO.name));
        to = checkNull(getInitParameter(Parameter.TO.name));
        cc = checkNull(getInitParameter(Parameter.CC.name));

        String senderEmail = checkNull(getInitParameter(Parameter.SENDER.name));

        if (senderEmail != null) {
            try {
                sender = new MailAddress(senderEmail);
            } catch (ParseException e) {
                throw new IllegalArgumentException("Sender address is not a valid email address: " + senderEmail);
            }
        }

        recipients = getInitParameter(Parameter.RECIPIENTS.name);

        if (recipients == null) {
            throw new IllegalArgumentException("recipients must be specified.");
        }
    }

    @Override
    protected String getSubject(Mail mail) throws MessagingException {
        return SpecialAddress.fromName(subject) == SpecialAddress.SAME_AS_MESSAGE ? mail.getMessage().getSubject()
                : subject;
    }

    @Override
    protected Address getFrom(Mail mail) throws MessagingException {
        return from;
    }

    @Override
    protected Address getReplyTo(Mail mail) throws MessagingException {
        return replyTo != null ? new InternetAddress(replyTo) : null;
    }

    @Override
    protected String getBody(Mail mail) throws MessagingException {
        DjigzoMailAttributes mailAttributes = new DjigzoMailAttributesImpl(mail);

        return mailAttributes.getMessage();
    }

    private void copyAddresses(Collection<MailAddress> source, Collection<Address> target) {
        if (source != null) {
            for (MailAddress address : source) {
                if (address == null) {
                    continue;
                }

                InternetAddress inetAddress = address.toInternetAddress();

                /*
                 * We never want the invalid@invalid.tld address
                 */
                if (EmailAddressUtils.isInvalidDummyAddress(inetAddress)) {
                    continue;
                }

                if (inetAddress != null) {
                    target.add(inetAddress);
                }
            }
        }
    }

    private void copyAddresses(Address[] source, Collection<Address> target) {
        if (source != null) {
            for (Address address : source) {
                if (address == null) {
                    continue;
                }

                String email;

                if (address instanceof InternetAddress) {
                    email = ((InternetAddress) address).getAddress();
                } else {
                    email = address.toString();
                }

                /*
                 * We never want the invalid@invalid.tld address
                 */
                if (EmailAddressUtils.isInvalidDummyAddress(email)) {
                    continue;
                }

                target.add(address);
            }
        }
    }

    private void copyMailAddresses(Address address, Collection<MailAddress> target) throws ParseException {
        copyMailAddresses(new Address[] { address }, target);
    }

    private void copyMailAddresses(Address[] source, Collection<MailAddress> target) throws ParseException {
        if (source != null) {
            for (Address address : source) {
                if (address instanceof InternetAddress) {
                    InternetAddress inetAddress = (InternetAddress) address;

                    /*
                     * We never want the invalid@invalid.tld address
                     */
                    if (EmailAddressUtils.isInvalidDummyAddress(inetAddress)) {
                        continue;
                    }

                    target.add(new MailAddress(inetAddress));
                }
            }
        }
    }

    private Collection<Address> getHeaderRecipients(String value, RecipientType recipientType, Mail mail)
            throws MessagingException {
        Collection<Address> result = null;

        if (StringUtils.isNotEmpty(value)) {
            result = new LinkedList<Address>();

            String[] stringRecipients = value.split("\\s*,\\s*");

            for (String recipient : stringRecipients) {
                recipient = StringUtils.trimToNull(recipient);

                if (recipient == null) {
                    continue;
                }

                SpecialAddress specialAddress = SpecialAddress.fromName(recipient);

                if (specialAddress == SpecialAddress.SAME_AS_RECIPIENTS) {
                    copyAddresses(getRecipients(mail), result);
                } else if (specialAddress == SpecialAddress.SAME_AS_MESSAGE) {
                    copyAddresses(mail.getMessage().getRecipients(recipientType), result);
                } else {
                    result.addAll(MailAddressUtils.toInternetAddressList(parseAddress(recipient, mail)));
                }
            }
        }

        return result;
    }

    @Override
    protected Collection<Address> getTo(Mail mail) throws MessagingException {
        return getHeaderRecipients(to, RecipientType.TO, mail);
    }

    @Override
    protected Collection<Address> getCC(Mail mail) throws MessagingException {
        return getHeaderRecipients(cc, RecipientType.CC, mail);
    }

    @Override
    protected MailAddress getSender(Mail mail) throws MessagingException {
        return sender;
    }

    @Override
    protected Collection<MailAddress> getRecipients(Mail mail)
            throws MessagingException, MissingRecipientsException {
        Collection<MailAddress> result = new LinkedHashSet<MailAddress>();

        String[] stringRecipients = recipients.split("\\s*,\\s*");

        for (String recipient : stringRecipients) {
            result.addAll(parseAddress(recipient, mail));
        }

        if (result.size() == 0) {
            throw new MissingRecipientsException("There are no recipients.");
        }

        return result;
    }

    /*
     * Parses the address and if it's a special address, the special address is converted into a 
     * real email address.
     */
    private Collection<MailAddress> parseAddress(String input, Mail mail) throws MessagingException {
        input = StringUtils.trimToNull(input);

        Collection<MailAddress> result = new LinkedHashSet<MailAddress>();

        if (input != null) {
            SpecialAddress specialAddress = SpecialAddress.fromName(input);

            if (specialAddress != null) {
                handleSpecialAddress(mail, result, specialAddress);
            } else {
                /*
                 * Check if the address is a mail attribute
                 */
                Matcher matcher = MAIL_ATTR_PATTERN.matcher(input);

                if (matcher.matches()) {
                    handleMailAttribute(mail, matcher.group(1), result);
                } else {
                    /*
                     * Check if the address is a user property address
                     */
                    matcher = USER_VAR_PATTERN.matcher(input);

                    if (matcher.matches()) {
                        handleUserProperty(mail, matcher.group(1), result);
                    } else {
                        /*
                         * Assume the input is a normal email address. However, we never want 
                         * the invalid@invalid.tld address
                         */
                        if (!EmailAddressUtils.isInvalidDummyAddress(input)) {
                            result.add(new MailAddress(input));
                        }
                    }
                }
            }
        }

        return result;
    }

    private void handleSpecialAddress(Mail mail, Collection<MailAddress> result, SpecialAddress specialAddress)
            throws MessagingException {
        switch (specialAddress) {
        case ORIGINATOR:
            copyMailAddresses(getMessageOriginatorIdentifier().getOriginator(mail), result);
            break;
        case REPLY_TO:
            copyMailAddresses(mail.getMessage().getReplyTo(), result);
            break;
        case SENDER:
            copyMailAddresses(MailAddressUtils.toInternetAddress(mail.getSender()), result);
            break;
        case FROM:
            copyMailAddresses(mail.getMessage().getFrom(), result);
            break;

        default:
            throw new MessagingException("Unsupported SpecialAddress.");
        }
    }

    /*
     * Tries to convert input to MailAddress's.
     */
    private void addEmails(Object input, Collection<MailAddress> target) {
        if (input == null) {
            return;
        }

        if (input instanceof String) {
            String addresses = StringUtils.trimToNull((String) input);

            if (addresses != null) {
                try {
                    InternetAddress[] emails = InternetAddress.parse(addresses, false);

                    if (emails != null) {
                        for (InternetAddress email : emails) {
                            if (email == null) {
                                continue;
                            }

                            /*
                             * We never want the invalid@invalid.tld address
                             */
                            if (EmailAddressUtils.isInvalidDummyAddress(email)) {
                                continue;
                            }

                            try {
                                target.add(new MailAddress(email));
                            } catch (ParseException e) {
                                getLogger().warn("Email address is invalid. Skipping address: " + email);
                            }
                        }
                    }
                } catch (AddressException e) {
                    getLogger().warn("Email address(s) are invalid " + addresses);
                }
            }
        } else if (input instanceof MailAddress) {
            MailAddress mailAddress = (MailAddress) input;
            /*
             * We never want the invalid@invalid.tld address
             */
            if (!EmailAddressUtils.isInvalidDummyAddress(mailAddress.toInternetAddress())) {
                target.add(mailAddress);
            }
        } else if (input instanceof InternetAddress) {
            InternetAddress inetAddress = (InternetAddress) input;
            /*
             * We never want the invalid@invalid.tld address
             */
            if (!EmailAddressUtils.isInvalidDummyAddress(inetAddress)) {
                try {
                    target.add(new MailAddress(inetAddress));
                } catch (ParseException e) {
                    getLogger().warn("Email address is invalid. Skipping address: " + input);
                }
            }
        } else if (input instanceof Collection) {
            for (Object o : (Collection<?>) input) {
                addEmails(o, target);
            }
        } else if (input instanceof Object[]) {
            for (Object o : (Object[]) input) {
                addEmails(o, target);
            }
        } else {
            /*
             * Fallback to #toString
             */
            addEmails(input.toString(), target);
        }
    }

    private void handleUserProperty(Mail mail, String userProperty, Collection<MailAddress> result)
            throws MessagingException, AddressException {
        InternetAddress originator = getMessageOriginatorIdentifier().getOriginator(mail);

        if (originator != null) {
            try {
                User user = getUserWorkflow().getUser(originator.getAddress(),
                        UserWorkflow.GetUserMode.CREATE_IF_NOT_EXIST);

                String userValue = user.getUserPreferences().getProperties().getProperty(userProperty, false);

                addEmails(userValue, result);
            } catch (HierarchicalPropertiesException e) {
                getLogger().error("Error getting user property " + userProperty, e);
            }
        }
    }

    private void handleMailAttribute(Mail mail, String attribute, Collection<MailAddress> result) {
        Object attributeValue = mail.getAttribute(attribute);

        if (getLogger().isDebugEnabled()) {
            getLogger().debug("Mail attr " + attribute + " value: " + attributeValue);
        }

        if (attributeValue != null) {
            addEmails(attributeValue, result);
        }
    }
}