mitm.common.postfix.SaslPasswordManager.java Source code

Java tutorial

Introduction

Here is the source code for mitm.common.postfix.SaslPasswordManager.java

Source

/*
 * Copyright (c) 2010-2011, 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.common.postfix;

import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import mitm.common.locale.CharacterEncoding;
import mitm.common.util.Check;
import mitm.common.util.MiscStringUtils;
import mitm.common.util.ProcessRunner;
import mitm.common.util.SizeLimitedOutputStream;
import mitm.common.util.SizeUtils;

import org.apache.commons.io.IOUtils;
import org.apache.commons.io.LineIterator;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.lang.text.StrBuilder;
import org.apache.commons.lang.time.DateUtils;

public class SaslPasswordManager {
    /*
     * Parts of the SASL_PASSWORD_PATTERN. Used to make reading the regular expression 
     * somewhat easier
     */
    private final static String SERVER_PATTERN = "([\\w.-]++)";
    private final static String PORT_PATTERN = ":?\\s*(\\d{0,5})";
    private final static String USERNAME_PASSWORD_PATTERN = "([^:]+)\\s*:?\\s*(.*)";

    /*
     * Pattern for parsing sasl password lines:
     * 
     * Example:
     * 
     * [smtp.gmail.com]:587 test@gmail.com:secret!
     */
    private final static Pattern SASL_PASSWORD_PATTERN = Pattern.compile("\\s*(\\[)?\\s*" + SERVER_PATTERN
            + "\\s*(\\])?\\s*" + PORT_PATTERN + "\\s*" + USERNAME_PASSWORD_PATTERN);

    /*
     * The maximum time a command may run after which it is destroyed
     */
    private final static long TIMEOUT = 30 * DateUtils.MILLIS_PER_SECOND;

    /*
     * Maximal size of a command output (just to protect against a runaway command)
     */
    private final static long MAX_OUTPUT_LENGTH = SizeUtils.KB * 10;

    /*
     * The external command (script or bin) to run for configuring Fetchmail
     */
    private final File externalCommand;

    public SaslPasswordManager(File externalCommand) {
        Check.notNull(externalCommand, "externalCommand");

        this.externalCommand = externalCommand;
    }

    /*
     * Returns the content of the SASL password file by executing an external process
     * that returns the content of the SASL password file.
     */
    private String getSaslPasswordContent() throws IOException {
        List<String> cmd = new LinkedList<String>();

        cmd.add(externalCommand.getPath());
        cmd.add("get");

        ProcessRunner runner = new ProcessRunner();

        runner.setTimeout(TIMEOUT);

        ByteArrayOutputStream bos = new ByteArrayOutputStream();

        SizeLimitedOutputStream slos = new SizeLimitedOutputStream(bos, MAX_OUTPUT_LENGTH, true);

        runner.setOutput(slos);

        runner.run(cmd);

        return MiscStringUtils.toAsciiString(bos.toByteArray());
    }

    /*
     * Sets the content of the SASL password file by executing an external process
     * that reads the content of the SASL password file from the standard in.
     */
    private void setSaslPasswordContent(String content) throws IOException {
        List<String> cmd = new LinkedList<String>();

        cmd.add(externalCommand.getPath());
        cmd.add("set");

        ProcessRunner runner = new ProcessRunner();

        runner.setTimeout(TIMEOUT);

        /*
         * The content will be piped to the process
         */
        runner.setInput(IOUtils.toInputStream(content, CharacterEncoding.US_ASCII));

        runner.run(cmd);
    }

    /*
     * Parses the SASL password file contents
     */
    protected static List<SaslPassword> parseContent(String input) {
        List<SaslPassword> passwords = new LinkedList<SaslPassword>();

        if (StringUtils.isEmpty(input)) {
            return passwords;
        }

        LineIterator lineIterator = IOUtils.lineIterator(new StringReader(input));

        while (lineIterator.hasNext()) {
            String line = lineIterator.nextLine();

            if (!StringUtils.isBlank(line)) {
                Matcher m = SASL_PASSWORD_PATTERN.matcher(line);

                if (m.matches()) {
                    boolean mxLookup = !("[".equals(m.group(1)) && "]".equals(m.group(3)));
                    String server = StringUtils.trim(m.group(2));
                    Integer port = NumberUtils.toInt(StringUtils.trim(m.group(4)), 0);
                    String username = StringUtils.trim(m.group(5));
                    String password = StringUtils.trim(m.group(6));

                    SaslPassword saslPassword = new SaslPassword();

                    saslPassword.setMxLookup(mxLookup);
                    saslPassword.setServer(server);
                    saslPassword.setPort(port != 0 ? port : null);
                    saslPassword.setUsername(username);
                    saslPassword.setPassword(password);

                    passwords.add(saslPassword);
                }
            }
        }

        return passwords;
    }

    /*
     * Creates sasl password content
     */
    protected static String createContent(List<SaslPassword> passwords) {
        StrBuilder content = new StrBuilder(256);

        if (passwords != null) {
            for (SaslPassword password : passwords) {
                if (!password.isMxLookup()) {
                    content.append("[");
                }

                content.append(password.getServer());

                if (!password.isMxLookup()) {
                    content.append("]");
                }

                if (password.getPort() != null) {
                    content.append(":").append(password.getPort());
                }

                content.append(" ").append(password.getUsername());
                content.append(":").append(password.getPassword());
                content.appendNewLine();
            }
        }

        return content.toString();
    }

    public List<SaslPassword> getSaslPasswords() throws IOException {
        return parseContent(getSaslPasswordContent());
    }

    public void setSaslPasswords(List<SaslPassword> passwords) throws IOException {
        setSaslPasswordContent(createContent(passwords));
    }
}