View Javadoc
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 }