OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 149 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
 * 
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each file.
 */
 
 package org.openconcerto.utils.io;

import org.openconcerto.utils.ExceptionUtils;

import java.util.Objects;
import java.util.Properties;

import javax.mail.Address;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.SendFailedException;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;

/**
 * A mail account.
 * 
 * @author Sylvain
 */
public class MailAccount {

    static public void addTimeouts(final Properties props, final String protocol) {
        // 3 minutes
        props.setProperty("mail." + protocol + ".connectiontimeout", "180000");
        props.setProperty("mail." + protocol + ".timeout", "180000");
        props.setProperty("mail." + protocol + ".writetimeout", "180000");
    }

    /**
     * Workaround for <code>ATT######.dat</code> attachments.
     * 
     * @param props must currently be {@link System#getProperties()}.
     */
    static public final void setEncodedParametersForOutlook(final Properties props) {
        // don't use RFC 2231 for all parameters for 2 reasons :
        // 1. Outlook (at least 2007) doesn't support it. Thunderbird seems to get around this by
        // encoding the filename of Content-Disposition according to RFC 2231 and also encoding it
        // in Content-Type using the old de facto standard.
        // 2. Java Mail 1.6.0 doesn't always use Parameter Value Continuations (not for filenames :
        // javax.mail.internet.MimeBodyPart.setFileName() uses ParameterList.Value which isn't
        // split by toString())
        // used in ParameterList.encodeParameters
        props.setProperty("mail.mime.encodeparameters", "false");
        // since we don't use RFC 2231, use the old de facto standard to have a charset specified
        // somewhere.
        // used in MimeBodyPart.encodeFileName
        props.setProperty("mail.mime.encodefilename", "true");
    }

    /**
     * Parse the given exception to a more human readable format.
     * 
     * @param mex an exception.
     * @return some useful informations.
     */
    public static final String handle(MessagingException mex) {
        final StringBuilder sb = new StringBuilder(512);
        sb.append(ExceptionUtils.getStackTrace(mex) + "\n");
        Exception ex = mex;
        do {
            if (ex instanceof SendFailedException) {
                SendFailedException sfex = (SendFailedException) ex;
                Address[] invalid = sfex.getInvalidAddresses();
                if (invalid != null) {
                    sb.append("    ** Invalid Addresses\n");
                    if (invalid != null) {
                        for (int i = 0; i < invalid.length; i++)
                            sb.append("         " + invalid[i]);
                    }
                }
                Address[] validUnsent = sfex.getValidUnsentAddresses();
                if (validUnsent != null) {
                    sb.append("    ** ValidUnsent Addresses\n");
                    if (validUnsent != null) {
                        for (int i = 0; i < validUnsent.length; i++)
                            sb.append("         " + validUnsent[i]);
                    }
                }
                Address[] validSent = sfex.getValidSentAddresses();
                if (validSent != null) {
                    sb.append("    ** ValidSent Addresses\n");
                    if (validSent != null) {
                        for (int i = 0; i < validSent.length; i++)
                            sb.append("         " + validSent[i]);
                    }
                }
            }
            sb.append("\n");
            if (ex instanceof MessagingException)
                ex = ((MessagingException) ex).getNextException();
            else
                ex = null;
        } while (ex != null);

        return sb.toString();
    }

    public static final MailAccount create(final String fromAddr, String smtpServer, String smtpLogin, String smtpPassword) throws AddressException {
        Objects.requireNonNull(fromAddr, "Missing 'From:' address");
        final InternetAddress fromInetAddr = new InternetAddress(fromAddr, true);
        if (smtpServer == null) {
            smtpServer = "smtp." + fromInetAddr.getAddress().substring(fromInetAddr.getAddress().indexOf('@') + 1);
        }
        if (smtpLogin == null) {
            smtpLogin = fromInetAddr.getAddress().substring(0, fromInetAddr.getAddress().indexOf('@'));
        }
        final MailAccount res = new MailAccount(fromInetAddr.getPersonal(), fromInetAddr.getAddress(), smtpServer);
        res.setAuth(new PasswordAuthentication(smtpLogin, smtpPassword));
        return res;
    }

    // nullable
    private final String name;
    private final String address;
    private final String smtpServer;
    private final int port;
    private PasswordAuthentication auth;

    /**
     * Create a new account.
     * 
     * @param name the personal name, e.g. "J. Smith", can be <code>null</code>.
     * @param address the email, e.g. "j.smith@foo.com".
     * @param smtpServer the SMTP server.
     */
    public MailAccount(String name, String address, String smtpServer) {
        this(name, address, smtpServer, -1);
    }

    public MailAccount(String name, String address, String smtpServer, int port) {
        super();
        this.name = name;
        this.address = address;
        this.smtpServer = smtpServer;
        this.port = port;
        this.auth = null;
    }

    public final String getName() {
        return this.name;
    }

    public final String getAddress() {
        return this.address;
    }

    public final String getSMTPServer() {
        return this.smtpServer;
    }

    public final int getPort() {
        return this.port;
    }

    public final PasswordAuthentication getAuth() {
        return this.auth;
    }

    public void setAuth(PasswordAuthentication auth) {
        this.auth = auth;
    }

    public String getSMTPProtocol() {
        return "smtp";
    }

    public final Properties createProperties() {
        return this.createProperties(false);
    }

    public final Properties createProperties(final boolean trustServer) {
        final Properties props = new Properties();
        final String proto = getSMTPProtocol();
        props.setProperty("mail.transport.protocol", proto);
        final String prefix = "mail." + proto + ".";
        props.setProperty(prefix + "host", this.getSMTPServer());
        props.setProperty(prefix + "port", this.getPort() < 0 ? "587" : String.valueOf(this.getPort()));
        props.setProperty(prefix + "starttls.required", "true");
        if (trustServer)
            props.setProperty(prefix + "ssl.trust", this.getSMTPServer());
        addTimeouts(props, proto);
        return props;
    }

    public final void send(final Message msg) throws MessagingException {
        final PasswordAuthentication auth = this.getAuth();
        final Properties props = msg.getSession().getProperties();
        props.setProperty("mail." + getSMTPProtocol() + ".auth", auth == null ? "false" : "true");
        try (final Transport mailTransport = msg.getSession().getTransport()) {
            if (auth == null)
                mailTransport.connect();
            else
                mailTransport.connect(auth.getUserName(), auth.getPassword());
            mailTransport.sendMessage(msg, msg.getAllRecipients());
        } catch (MessagingException e) {
            throw new MessagingException("Couldn't send as " + auth.getUserName() + " using\n" + props, e);
        }
    }

    @Override
    public String toString() {
        return this.getClass().getSimpleName() + " " + getAddress() + " through " + this.getSMTPServer();
    }
}