1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.commons.net;
19
20 import java.io.PrintStream;
21 import java.io.PrintWriter;
22 import java.util.Locale;
23 import java.util.Objects;
24
25 import org.apache.commons.net.io.Util;
26
27 /**
28 * This is a support class for some example programs. It is a sample implementation of the ProtocolCommandListener interface which just prints out to a
29 * specified stream all command/reply traffic.
30 *
31 * @since 2.0
32 */
33 public class PrintCommandListener implements ProtocolCommandListener {
34
35 private static final String DIRECTION_MARKER_RECEIVE = "< ";
36 private static final String DIRECTION_MARKER_SEND = "> ";
37 private static final String HIDDEN_MARKER = " *******";
38 private static final String CMD_LOGIN = "LOGIN";
39 private static final String CMD_USER = "USER";
40 private static final String CMD_PASS = "PASS";
41 private final PrintWriter writer;
42 private final boolean noLogin;
43 private final char eolMarker;
44 private final boolean showDirection;
45
46 /**
47 * Constructs an instance which prints everything using the default Charset.
48 *
49 * @param printStream where to write the commands and responses e.g. System.out
50 * @since 3.0
51 */
52 @SuppressWarnings("resource")
53 public PrintCommandListener(final PrintStream printStream) {
54 this(Util.newPrintWriter(printStream));
55 }
56
57 /**
58 * Constructs an instance which optionally suppresses login command text and indicates where the EOL starts with the specified character.
59 *
60 * @param printStream where to write the commands and responses
61 * @param suppressLogin if {@code true}, only print command name for login
62 * @since 3.0
63 */
64 @SuppressWarnings("resource")
65 public PrintCommandListener(final PrintStream printStream, final boolean suppressLogin) {
66 this(Util.newPrintWriter(printStream), suppressLogin);
67 }
68
69 /**
70 * Constructs an instance which optionally suppresses login command text and indicates where the EOL starts with the specified character.
71 *
72 * @param printStream where to write the commands and responses
73 * @param suppressLogin if {@code true}, only print command name for login
74 * @param eolMarker if non-zero, add a marker just before the EOL.
75 * @since 3.0
76 */
77 @SuppressWarnings("resource")
78 public PrintCommandListener(final PrintStream printStream, final boolean suppressLogin, final char eolMarker) {
79 this(Util.newPrintWriter(printStream), suppressLogin, eolMarker);
80 }
81
82 /**
83 * Constructs an instance which optionally suppresses login command text and indicates where the EOL starts with the specified character.
84 *
85 * @param printStream where to write the commands and responses
86 * @param suppressLogin if {@code true}, only print command name for login
87 * @param eolMarker if non-zero, add a marker just before the EOL.
88 * @param showDirection if {@code true}, add {@code "> "} or {@code "< "} as appropriate to the output
89 * @since 3.0
90 */
91 @SuppressWarnings("resource")
92 public PrintCommandListener(final PrintStream printStream, final boolean suppressLogin, final char eolMarker, final boolean showDirection) {
93 this(Util.newPrintWriter(printStream), suppressLogin, eolMarker, showDirection);
94 }
95
96 /**
97 * Constructs the default instance which prints everything.
98 *
99 * @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 }