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}