mitm.common.postfix.PostfixQueueParser.java Source code

Java tutorial

Introduction

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

Source

/*
 * Copyright (c) 2008-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.StringReader;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import mitm.common.util.Check;

import org.apache.commons.io.IOUtils;
import org.apache.commons.io.LineIterator;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.lang.mutable.MutableInt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Parses the output of the "postqueue -p" command. The output is not strictly defined so parsing it is
 * not based on a defined API and it's therefore possible that it will break with new versions of Postfix. 
 * 
 * @author Martijn Brinkers
 *
 */
public class PostfixQueueParser {
    private static final Logger logger = LoggerFactory.getLogger(PostfixQueueParser.class);

    /*
     * The following reg. expr. parts are used to build the queue item pattern
     */
    private static String QUEUE_ID_PART = "(\\w+)([\\*!])?";

    private static String SIZE_PART = "\\s+(\\d+)";

    private static String DATE_PART = "\\s+(\\w+\\s+\\w+\\s+\\d+\\s+\\d+(?::\\d+){2})";

    private static String SENDER_PART = "\\s+(\\S*)";

    private static String FAILURE_PART = "\\s+(\\(.*\\)(?!\\)))?";

    private static String RECIPIENT_PART = "\\s*(.+)\\s*";

    protected static Pattern QUEUE_PATTERN = Pattern
            .compile(QUEUE_ID_PART + SIZE_PART + DATE_PART + SENDER_PART + FAILURE_PART + RECIPIENT_PART);

    /*
     * The items from the queue
     */
    private List<PostfixQueueItem> queueItems;

    /*
     * Pattern used to match the lines we are interested in. If null all lines will match
     */
    private final Pattern searchPattern;

    private interface LineHandler {
        /*
         * called when a new line is called. Return false if parsing must be stopped
         */
        public boolean lineFound(String line);
    }

    private class ParseQueueEntryLineHandler implements LineHandler {
        /*
         * The index of the first item
         */
        private final int startIndex;

        /*
         * The max items to return
         */
        private final int maxItems;

        /*
         * The current index
         */
        int index;

        public ParseQueueEntryLineHandler(int startIndex, int maxItems) {
            this.startIndex = startIndex;
            this.maxItems = maxItems;
        }

        @Override
        public boolean lineFound(String line) {
            boolean keepParsing = true;

            if (index >= startIndex) {
                Matcher matcher = QUEUE_PATTERN.matcher(line);

                PostfixQueueItem newItem;

                if (matcher.matches() && matcher.groupCount() == 7) {
                    String queueID = matcher.group(1);
                    PostfixQueueStatus status = PostfixQueueStatus.fromString(matcher.group(2));
                    int messageSize = NumberUtils.toInt(matcher.group(3));
                    String deliveryDate = matcher.group(4);
                    String sender = matcher.group(5);
                    String failure = matcher.group(6);
                    List<String> recipients = Arrays.asList(StringUtils.split(matcher.group(7)));

                    newItem = new PostfixQueueItem(queueID, status, messageSize, deliveryDate, sender, failure,
                            recipients);
                } else {
                    logger.warn("Mail queue line is not recognized: " + line);

                    /*
                     * Because we did not recognize the line we do not know how to handle it. To make sure the user
                     * sees at least the complete line we will add the line to the failure and set all other to null
                     */
                    newItem = new PostfixQueueItem(null, null, -1, null, null, line, null);
                }

                queueItems.add(newItem);
            }

            index++;

            if ((index - startIndex) >= maxItems) {
                keepParsing = false;
            }

            return keepParsing;
        }
    };

    public PostfixQueueParser(Pattern searchPattern) {
        this.searchPattern = searchPattern;
    }

    public PostfixQueueParser() {
        this(null);
    }

    private void parse(String queue, LineHandler lineHandler) {
        Check.notNull(lineHandler, "lineHandler");

        StringReader reader = new StringReader(queue);

        try {
            LineIterator iterator = IOUtils.lineIterator(reader);

            /*
             * If the mail queue is empty the first line is "Mail queue is empty". If the mail queue is
             * not empty the first line should be the header. We should therefore skip the first line
             */
            if (iterator.hasNext()) {
                iterator.next();
            }

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

                if (line.startsWith("--")) {
                    /*
                     * The last line starts with -- so we are finished
                     */
                    break;
                }

                /*
                 * We need to collect all lines that belong to one queue item. Queue items use multiple lines
                 * which are separated by an empty line
                 */
                while (iterator.hasNext()) {
                    String otherLine = iterator.nextLine();

                    if (otherLine.length() == 0) {
                        break;
                    }

                    line = line + " " + otherLine;
                }

                boolean match = true;

                if (searchPattern != null) {
                    Matcher matcher = searchPattern.matcher(line);

                    if (!matcher.find()) {
                        match = false;
                    }
                }

                if (match && !lineHandler.lineFound(line)) {
                    break;
                }
            }
        } finally {
            IOUtils.closeQuietly(reader);
        }
    }

    /**
     * Creates a list of QueueItem's by parsing the raw postqueue output.
     */
    public List<PostfixQueueItem> parseQueue(String queue, final int startIndex, final int maxItems) {
        queueItems = new LinkedList<PostfixQueueItem>();

        parse(queue, new ParseQueueEntryLineHandler(startIndex, maxItems));

        return queueItems;
    }

    /**
     * Returns the number of items in the Postfix queue.
     */
    public int getQueueLength(String queue) {
        final MutableInt length = new MutableInt(0);

        LineHandler lineHandler = new LineHandler() {
            @Override
            public boolean lineFound(String line) {
                length.increment();

                return true;
            }
        };

        parse(queue, lineHandler);

        return length.intValue();
    }

    /**
     * Returns the queue items. Returns null if no items have been parsed and an empty List if 
     * there are no items.
     */
    public List<PostfixQueueItem> getQueueItems() {
        return queueItems;
    }
}