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 * http://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
018 package org.apache.commons.net.pop3;
019
020 import java.io.BufferedReader;
021 import java.io.BufferedWriter;
022 import java.io.EOFException;
023 import java.io.IOException;
024 import java.io.InputStreamReader;
025 import java.io.OutputStreamWriter;
026 import java.util.ArrayList;
027 import java.util.List;
028
029 import org.apache.commons.net.MalformedServerReplyException;
030 import org.apache.commons.net.ProtocolCommandSupport;
031 import org.apache.commons.net.SocketClient;
032 import org.apache.commons.net.io.CRLFLineReader;
033
034 /***
035 * The POP3 class is not meant to be used by itself and is provided
036 * only so that you may easily implement your own POP3 client if
037 * you so desire. If you have no need to perform your own implementation,
038 * you should use {@link org.apache.commons.net.pop3.POP3Client}.
039 * <p>
040 * Rather than list it separately for each method, we mention here that
041 * every method communicating with the server and throwing an IOException
042 * can also throw a
043 * {@link org.apache.commons.net.MalformedServerReplyException}
044 * , which is a subclass
045 * of IOException. A MalformedServerReplyException will be thrown when
046 * the reply received from the server deviates enough from the protocol
047 * specification that it cannot be interpreted in a useful manner despite
048 * attempts to be as lenient as possible.
049 * <p>
050 * <p>
051 * @see POP3Client
052 * @see org.apache.commons.net.MalformedServerReplyException
053 ***/
054
055 public class POP3 extends SocketClient
056 {
057 /*** The default POP3 port. Set to 110 according to RFC 1288. ***/
058 public static final int DEFAULT_PORT = 110;
059 /***
060 * A constant representing the state where the client is not yet connected
061 * to a POP3 server.
062 ***/
063 public static final int DISCONNECTED_STATE = -1;
064 /*** A constant representing the POP3 authorization state. ***/
065 public static final int AUTHORIZATION_STATE = 0;
066 /*** A constant representing the POP3 transaction state. ***/
067 public static final int TRANSACTION_STATE = 1;
068 /*** A constant representing the POP3 update state. ***/
069 public static final int UPDATE_STATE = 2;
070
071 static final String _OK = "+OK";
072 // The reply indicating intermediate response to a command.
073 static final String _OK_INT = "+ ";
074 static final String _ERROR = "-ERR";
075
076 // We have to ensure that the protocol communication is in ASCII
077 // but we use ISO-8859-1 just in case 8-bit characters cross
078 // the wire.
079 static final String _DEFAULT_ENCODING = "ISO-8859-1";
080
081 private int __popState;
082 BufferedWriter _writer;
083
084 BufferedReader _reader;
085 int _replyCode;
086 String _lastReplyLine;
087 List<String> _replyLines;
088
089 /**
090 * A ProtocolCommandSupport object used to manage the registering of
091 * ProtocolCommandListeners and te firing of ProtocolCommandEvents.
092 */
093 protected ProtocolCommandSupport _commandSupport_;
094
095 /***
096 * The default POP3Client constructor. Initializes the state
097 * to <code>DISCONNECTED_STATE</code>.
098 ***/
099 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