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 * http://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.pop3;
19
20 import java.io.BufferedReader;
21 import java.io.BufferedWriter;
22 import java.io.EOFException;
23 import java.io.IOException;
24 import java.io.InputStreamReader;
25 import java.io.OutputStreamWriter;
26 import java.util.ArrayList;
27 import java.util.List;
28
29 import org.apache.commons.net.MalformedServerReplyException;
30 import org.apache.commons.net.ProtocolCommandSupport;
31 import org.apache.commons.net.SocketClient;
32 import org.apache.commons.net.io.CRLFLineReader;
33
34 /***
35 * The POP3 class is not meant to be used by itself and is provided
36 * only so that you may easily implement your own POP3 client if
37 * you so desire. If you have no need to perform your own implementation,
38 * you should use {@link org.apache.commons.net.pop3.POP3Client}.
39 * <p>
40 * Rather than list it separately for each method, we mention here that
41 * every method communicating with the server and throwing an IOException
42 * can also throw a
43 * {@link org.apache.commons.net.MalformedServerReplyException}
44 * , which is a subclass
45 * of IOException. A MalformedServerReplyException will be thrown when
46 * the reply received from the server deviates enough from the protocol
47 * specification that it cannot be interpreted in a useful manner despite
48 * attempts to be as lenient as possible.
49 * <p>
50 * <p>
51 * @see POP3Client
52 * @see org.apache.commons.net.MalformedServerReplyException
53 ***/
54
55 public class POP3 extends SocketClient
56 {
57 /*** The default POP3 port. Set to 110 according to RFC 1288. ***/
58 public static final int DEFAULT_PORT = 110;
59 /***
60 * A constant representing the state where the client is not yet connected
61 * to a POP3 server.
62 ***/
63 public static final int DISCONNECTED_STATE = -1;
64 /*** A constant representing the POP3 authorization state. ***/
65 public static final int AUTHORIZATION_STATE = 0;
66 /*** A constant representing the POP3 transaction state. ***/
67 public static final int TRANSACTION_STATE = 1;
68 /*** A constant representing the POP3 update state. ***/
69 public static final int UPDATE_STATE = 2;
70
71 static final String _OK = "+OK";
72 // The reply indicating intermediate response to a command.
73 static final String _OK_INT = "+ ";
74 static final String _ERROR = "-ERR";
75
76 // We have to ensure that the protocol communication is in ASCII
77 // but we use ISO-8859-1 just in case 8-bit characters cross
78 // the wire.
79 static final String _DEFAULT_ENCODING = "ISO-8859-1";
80
81 private int __popState;
82 BufferedWriter _writer;
83
84 BufferedReader _reader;
85 int _replyCode;
86 String _lastReplyLine;
87 List<String> _replyLines;
88
89 /**
90 * A ProtocolCommandSupport object used to manage the registering of
91 * ProtocolCommandListeners and te firing of ProtocolCommandEvents.
92 */
93 protected ProtocolCommandSupport _commandSupport_;
94
95 /***
96 * The default POP3Client constructor. Initializes the state
97 * to <code>DISCONNECTED_STATE</code>.
98 ***/
99 public POP3()
100 {
101 setDefaultPort(DEFAULT_PORT);
102 __popState = DISCONNECTED_STATE;
103 _reader = null;
104 _writer = null;
105 _replyLines = new ArrayList<String>();
106 _commandSupport_ = new ProtocolCommandSupport(this);
107 }
108
109 private void __getReply() throws IOException
110 {
111 String line;
112
113 _replyLines.clear();
114 line = _reader.readLine();
115
116 if (line == null) {
117 throw new EOFException("Connection closed without indication.");
118 }
119
120 if (line.startsWith(_OK)) {
121 _replyCode = POP3Reply.OK;
122 } else if (line.startsWith(_ERROR)) {
123 _replyCode = POP3Reply.ERROR;
124 } else if (line.startsWith(_OK_INT)) {
125 _replyCode = POP3Reply.OK_INT;
126 } else {
127 throw new
128 MalformedServerReplyException(
129 "Received invalid POP3 protocol response from server." + line);
130 }
131
132 _replyLines.add(line);
133 _lastReplyLine = line;
134
135 fireReplyReceived(_replyCode, getReplyString());
136 }
137
138
139 /***
140 * Performs connection initialization and sets state to
141 * <code> AUTHORIZATION_STATE </code>.
142 ***/
143 @Override
144 protected void _connectAction_() throws IOException
145 {
146 super._connectAction_();
147 _reader =
148 new CRLFLineReader(new InputStreamReader(_input_,
149 _DEFAULT_ENCODING));
150 _writer =
151 new BufferedWriter(new OutputStreamWriter(_output_,
152 _DEFAULT_ENCODING));
153 __getReply();
154 setState(AUTHORIZATION_STATE);
155 }
156
157
158 /**
159 * Set the internal POP3 state.
160 * @param state the new state. This must be one of the <code>_STATE</code> constants.
161 */
162 public void setState(int state)
163 {
164 __popState = state;
165 }
166
167
168 /***
169 * Returns the current POP3 client state.
170 * <p>
171 * @return The current POP3 client state.
172 ***/
173 public int getState()
174 {
175 return __popState;
176 }
177
178
179 /***
180 * Retrieves the additional lines of a multi-line server reply.
181 ***/
182 public void getAdditionalReply() throws IOException
183 {
184 String line;
185
186 line = _reader.readLine();
187 while (line != null)
188 {
189 _replyLines.add(line);
190 if (line.equals(".")) {
191 break;
192 }
193 line = _reader.readLine();
194 }
195 }
196
197
198 /***
199 * Disconnects the client from the server, and sets the state to
200 * <code> DISCONNECTED_STATE </code>. The reply text information
201 * from the last issued command is voided to allow garbage collection
202 * of the memory used to store that information.
203 * <p>
204 * @exception IOException If there is an error in disconnecting.
205 ***/
206 @Override
207 public void disconnect() throws IOException
208 {
209 super.disconnect();
210 _reader = null;
211 _writer = null;
212 _lastReplyLine = null;
213 _replyLines.clear();
214 setState(DISCONNECTED_STATE);
215 }
216
217
218 /***
219 * Sends a command an arguments to the server and returns the reply code.
220 * <p>
221 * @param command The POP3 command to send.
222 * @param args The command arguments.
223 * @return The server reply code (either POP3Reply.OK, POP3Reply.ERROR or POP3Reply.OK_INT).
224 ***/
225 public int sendCommand(String command, String args) throws IOException
226 {
227 if (_writer == null) {
228 throw new IllegalStateException("Socket is not connected");
229 }
230 StringBuilder __commandBuffer = new StringBuilder();
231 __commandBuffer.append(command);
232
233 if (args != null)
234 {
235 __commandBuffer.append(' ');
236 __commandBuffer.append(args);
237 }
238 __commandBuffer.append(SocketClient.NETASCII_EOL);
239
240 String message = __commandBuffer.toString();
241 _writer.write(message);
242 _writer.flush();
243
244 fireCommandSent(command, message);
245
246 __getReply();
247 return _replyCode;
248 }
249
250 /***
251 * Sends a command with no arguments to the server and returns the
252 * reply code.
253 * <p>
254 * @param command The POP3 command to send.
255 * @return The server reply code (either POP3Reply.OK, POP3Reply.ERROR or POP3Reply.OK_INT).
256 ***/
257 public int sendCommand(String command) throws IOException
258 {
259 return sendCommand(command, null);
260 }
261
262 /***
263 * Sends a command an arguments to the server and returns the reply code.
264 * <p>
265 * @param command The POP3 command to send
266 * (one of the POP3Command constants).
267 * @param args The command arguments.
268 * @return The server reply code (either POP3Reply.OK, POP3Reply.ERROR or POP3Reply.OK_INT).
269 ***/
270 public int sendCommand(int command, String args) throws IOException
271 {
272 return sendCommand(POP3Command._commands[command], args);
273 }
274
275 /***
276 * Sends a command with no arguments to the server and returns the
277 * reply code.
278 * <p>
279 * @param command The POP3 command to send
280 * (one of the POP3Command constants).
281 * @return The server reply code (either POP3Reply.OK, POP3Reply.ERROR or POP3Reply.OK_INT).
282 ***/
283 public int sendCommand(int command) throws IOException
284 {
285 return sendCommand(POP3Command._commands[command], null);
286 }
287
288
289 /***
290 * Returns an array of lines received as a reply to the last command
291 * sent to the server. The lines have end of lines truncated. If
292 * the reply is a single line, but its format ndicates it should be
293 * a multiline reply, then you must call
294 * {@link #getAdditionalReply getAdditionalReply() } to
295 * fetch the rest of the reply, and then call <code>getReplyStrings</code>
296 * again. You only have to worry about this if you are implementing
297 * your own client using the {@link #sendCommand sendCommand } methods.
298 * <p>
299 * @return The last server response.
300 ***/
301 public String[] getReplyStrings()
302 {
303 return _replyLines.toArray(new String[_replyLines.size()]);
304 }
305
306 /***
307 * Returns the reply to the last command sent to the server.
308 * The value is a single string containing all the reply lines including
309 * newlines. If the reply is a single line, but its format ndicates it
310 * should be a multiline reply, then you must call
311 * {@link #getAdditionalReply getAdditionalReply() } to
312 * fetch the rest of the reply, and then call <code>getReplyString</code>
313 * again. You only have to worry about this if you are implementing
314 * your own client using the {@link #sendCommand sendCommand } methods.
315 * <p>
316 * @return The last server response.
317 ***/
318 public String getReplyString()
319 {
320 StringBuilder buffer = new StringBuilder(256);
321
322 for (String entry : _replyLines)
323 {
324 buffer.append(entry);
325 buffer.append(SocketClient.NETASCII_EOL);
326 }
327
328 return buffer.toString();
329 }
330
331 /**
332 * Removes a ProtocolCommandListener.
333 *
334 * Delegates this incorrectly named method - removeProtocolCommandistener (note the missing "L")- to
335 * the correct method {@link SocketClient#removeProtocolCommandListener}
336 * @param listener The ProtocolCommandListener to remove
337 */
338 public void removeProtocolCommandistener(org.apache.commons.net.ProtocolCommandListener listener){
339 removeProtocolCommandListener(listener);
340 }
341
342 /**
343 * Provide command support to super-class
344 */
345 @Override
346 protected ProtocolCommandSupport getCommandSupport() {
347 return _commandSupport_;
348 }
349 }
350