    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      https://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017
    018package org.apache.commons.net;
    019
    020import java.io.PrintStream;
    021import java.io.PrintWriter;
    022import java.util.Locale;
    023import java.util.Objects;
    024
    025import org.apache.commons.net.io.Util;
    026
    027/**
    028 * This is a support class for some example programs. It is a sample implementation of the ProtocolCommandListener interface which just prints out to a
    029 * specified stream all command/reply traffic.
    030 *
    031 * @since 2.0
    032 */
    033public class PrintCommandListener implements ProtocolCommandListener {
    034
    035    private static final String DIRECTION_MARKER_RECEIVE = "< ";
    036    private static final String DIRECTION_MARKER_SEND = "> ";
    037    private static final String HIDDEN_MARKER = " *******";
    038    private static final String CMD_LOGIN = "LOGIN";
    039    private static final String CMD_USER = "USER";
    040    private static final String CMD_PASS = "PASS";
    041    private final PrintWriter writer;
    042    private final boolean noLogin;
    043    private final char eolMarker;
    044    private final boolean showDirection;
    045
    046    /**
    047     * Constructs an instance which prints everything using the default Charset.
    048     *
    049     * @param printStream where to write the commands and responses e.g. System.out
    050     * @since 3.0
    051     */
    052    @SuppressWarnings("resource")
    053    public PrintCommandListener(final PrintStream printStream) {
    054        this(Util.newPrintWriter(printStream));
    055    }
    056
    057    /**
    058     * Constructs an instance which optionally suppresses login command text and indicates where the EOL starts with the specified character.
    059     *
    060     * @param printStream        where to write the commands and responses
    061     * @param suppressLogin if {@code true}, only print command name for login
    062     * @since 3.0
    063     */
    064    @SuppressWarnings("resource")
    065    public PrintCommandListener(final PrintStream printStream, final boolean suppressLogin) {
    066        this(Util.newPrintWriter(printStream), suppressLogin);
    067    }
    068
    069    /**
    070     * Constructs an instance which optionally suppresses login command text and indicates where the EOL starts with the specified character.
    071     *
    072     * @param printStream        where to write the commands and responses
    073     * @param suppressLogin if {@code true}, only print command name for login
    074     * @param eolMarker     if non-zero, add a marker just before the EOL.
    075     * @since 3.0
    076     */
    077    @SuppressWarnings("resource")
    078    public PrintCommandListener(final PrintStream printStream, final boolean suppressLogin, final char eolMarker) {
    079        this(Util.newPrintWriter(printStream), suppressLogin, eolMarker);
    080    }
    081
    082    /**
    083     * Constructs an instance which optionally suppresses login command text and indicates where the EOL starts with the specified character.
    084     *
    085     * @param printStream        where to write the commands and responses
    086     * @param suppressLogin if {@code true}, only print command name for login
    087     * @param eolMarker     if non-zero, add a marker just before the EOL.
    088     * @param showDirection if {@code true}, add {@code "> "} or {@code "< "} as appropriate to the output
    089     * @since 3.0
    090     */
    091    @SuppressWarnings("resource")
    092    public PrintCommandListener(final PrintStream printStream, final boolean suppressLogin, final char eolMarker, final boolean showDirection) {
    093        this(Util.newPrintWriter(printStream), suppressLogin, eolMarker, showDirection);
    094    }
    095
    096    /**
    097     * Constructs the default instance which prints everything.
    098     *
    099     * @param writer where to write the commands and responses
    100     */
    101    public PrintCommandListener(final PrintWriter writer) {
    102        this(writer, false);
    103    }
    104
    105    /**
    106     * Constructs an instance which optionally suppresses login command text.
    107     *
    108     * @param writer        where to write the commands and responses
    109     * @param suppressLogin if {@code true}, only print command name for login
    110     * @since 3.0
    111     */
    112    public PrintCommandListener(final PrintWriter writer, final boolean suppressLogin) {
    113        this(writer, suppressLogin, (char) 0);
    114    }
    115
    116    /**
    117     * Constructs an instance which optionally suppresses login command text and indicates where the EOL starts with the specified character.
    118     *
    119     * @param writer        where to write the commands and responses
    120     * @param suppressLogin if {@code true}, only print command name for login
    121     * @param eolMarker     if non-zero, add a marker just before the EOL.
    122     * @since 3.0
    123     */
    124    public PrintCommandListener(final PrintWriter writer, final boolean suppressLogin, final char eolMarker) {
    125        this(writer, suppressLogin, eolMarker, false);
    126    }
    127
    128    /**
    129     * Constructs an instance which optionally suppresses login command text and indicates where the EOL starts with the specified character.
    130     *
    131     * @param writer        where to write the commands and responses, not null.
    132     * @param suppressLogin if {@code true}, only print command name for login
    133     * @param eolMarker     if non-zero, add a marker just before the EOL.
    134     * @param showDirection if {@code true}, add {@code ">} " or {@code "< "} as appropriate to the output
    135     * @since 3.0
    136     */
    137    public PrintCommandListener(final PrintWriter writer, final boolean suppressLogin, final char eolMarker, final boolean showDirection) {
    138        this.writer = Objects.requireNonNull(writer, "writer");
    139        this.noLogin = suppressLogin;
    140        this.eolMarker = eolMarker;
    141        this.showDirection = showDirection;
    142    }
    143
    144    private String getCommand(final ProtocolCommandEvent event) {
    145        return Objects.toString(event.getCommand()).toUpperCase(Locale.ROOT);
    146    }
    147
    148    private String getMessage(final ProtocolCommandEvent event) {
    149        return Objects.toString(event.getMessage());
    150    }
    151
    152    private String getPrintableString(final String msg) {
    153        if (eolMarker == 0) {
    154            return msg;
    155        }
    156        final int pos = msg.indexOf(SocketClient.NETASCII_EOL);
    157        if (pos > 0) {
    158            final StringBuilder sb = new StringBuilder(msg + 1);
    159            sb.append(msg.substring(0, pos));
    160            sb.append(eolMarker);
    161            sb.append(msg.substring(pos));
    162            return sb.toString();
    163        }
    164        return msg;
    165    }
    166
    167    @Override
    168    public void protocolCommandSent(final ProtocolCommandEvent event) {
    169        if (showDirection) {
    170            writer.print(DIRECTION_MARKER_SEND);
    171        }
    172        if (noLogin) {
    173            final String cmd = getCommand(event);
    174            if (CMD_PASS.equals(cmd) || CMD_USER.equals(cmd)) {
    175                writer.print(cmd);
    176                writer.println(HIDDEN_MARKER); // Don't bother with EOL marker for this!
    177            } else if (CMD_LOGIN.equals(cmd)) { // IMAP
    178                String msg = getMessage(event);
    179                msg = msg.substring(0, msg.indexOf(CMD_LOGIN) + CMD_LOGIN.length());
    180                writer.print(msg);
    181                writer.println(HIDDEN_MARKER); // Don't bother with EOL marker for this!
    182            } else {
    183                writer.print(getPrintableString(getMessage(event)));
    184            }
    185        } else {
    186            writer.print(getPrintableString(getMessage(event)));
    187        }
    188        writer.flush();
    189    }
    190
    191    @Override
    192    public void protocolReplyReceived(final ProtocolCommandEvent event) {
    193        if (showDirection) {
    194            writer.print(DIRECTION_MARKER_RECEIVE);
    195        }
    196        final String message = getMessage(event);
    197        final char last = message.charAt(message.length() - 1);
    198        writer.print(message);
    199        if (last != '\r' && last != '\n') {
    200            writer.println();
    201        }
    202        writer.flush();
    203    }
    204}