001 /*
002 * Copyright 2001-2005 The Apache Software Foundation
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package org.apache.commons.net.pop3;
017
018 import java.io.BufferedReader;
019 import java.io.BufferedWriter;
020 import java.io.EOFException;
021 import java.io.IOException;
022 import java.io.InputStreamReader;
023 import java.io.OutputStreamWriter;
024 import java.lang.StringBuffer;
025 import java.util.Enumeration;
026 import java.util.Vector;
027 import org.apache.commons.net.MalformedServerReplyException;
028 import org.apache.commons.net.ProtocolCommandListener;
029 import org.apache.commons.net.ProtocolCommandSupport;
030 import org.apache.commons.net.SocketClient;
031
032 /***
033 * The POP3 class is not meant to be used by itself and is provided
034 * only so that you may easily implement your own POP3 client if
035 * you so desire. If you have no need to perform your own implementation,
036 * you should use {@link org.apache.commons.net.pop3.POP3Client}.
037 * <p>
038 * Rather than list it separately for each method, we mention here that
039 * every method communicating with the server and throwing an IOException
040 * can also throw a
041 * {@link org.apache.commons.net.MalformedServerReplyException}
042 * , which is a subclass
043 * of IOException. A MalformedServerReplyException will be thrown when
044 * the reply received from the server deviates enough from the protocol
045 * specification that it cannot be interpreted in a useful manner despite
046 * attempts to be as lenient as possible.
047 * <p>
048 * <p>
049 * @author Daniel F. Savarese
050 * @see POP3Client
051 * @see org.apache.commons.net.MalformedServerReplyException
052 ***/
053
054 public class POP3 extends SocketClient
055 {
056 /*** The default POP3 port. Set to 110 according to RFC 1288. ***/
057 public static final int DEFAULT_PORT = 110;
058 /***
059 * A constant representing the state where the client is not yet connected
060 * to a POP3 server.
061 ***/
062 public static final int DISCONNECTED_STATE = -1;
063 /*** A constant representing the POP3 authorization state. ***/
064 public static final int AUTHORIZATION_STATE = 0;
065 /*** A constant representing the POP3 transaction state. ***/
066 public static final int TRANSACTION_STATE = 1;
067 /*** A constant representing the POP3 update state. ***/
068 public static final int UPDATE_STATE = 2;
069
070 static final String _OK = "+OK";
071 static final String _ERROR = "-ERR";
072
073 // We have to ensure that the protocol communication is in ASCII
074 // but we use ISO-8859-1 just in case 8-bit characters cross
075 // the wire.
076 private static final String __DEFAULT_ENCODING = "ISO-8859-1";
077
078 private int __popState;
079 private BufferedWriter __writer;
080 private StringBuffer __commandBuffer;
081
082 BufferedReader _reader;
083 int _replyCode;
084 String _lastReplyLine;
085 Vector _replyLines;
086
087 /***
088 * A ProtocolCommandSupport object used to manage the registering of
089 * ProtocolCommandListeners and te firing of ProtocolCommandEvents.
090 ***/
091 protected ProtocolCommandSupport _commandSupport_;
092
093 /***
094 * The default POP3Client constructor. Initializes the state
095 * to <code>DISCONNECTED_STATE</code>.
096 ***/
097 public POP3()
098 {
099 setDefaultPort(DEFAULT_PORT);
100 __commandBuffer = new StringBuffer();
101 __popState = DISCONNECTED_STATE;
102 _reader = null;
103 __writer = null;
104 _replyLines = new Vector();
105 _commandSupport_ = new ProtocolCommandSupport(this);
106 }
107
108 private void __getReply() throws IOException
109 {
110 String line;
111
112 _replyLines.setSize(0);
113 line = _reader.readLine();
114
115 if (line == null)
116 throw new EOFException("Connection closed without indication.");
117
118 if (line.startsWith(_OK))
119 _replyCode = POP3Reply.OK;
120 else if (line.startsWith(_ERROR))
121 _replyCode = POP3Reply.ERROR;
122 else
123 throw new
124 MalformedServerReplyException(
125 "Received invalid POP3 protocol response from server.");
126
127 _replyLines.addElement(line);
128 _lastReplyLine = line;
129
130 if (_commandSupport_.getListenerCount() > 0)
131 _commandSupport_.fireReplyReceived(_replyCode, getReplyString());
132 }
133
134
135 /***
136 * Performs connection initialization and sets state to
137 * <code> AUTHORIZATION_STATE </code>.
138 ***/
139 protected void _connectAction_() throws IOException
140 {
141 super._connectAction_();
142 _reader =
143 new BufferedReader(new InputStreamReader(_input_,
144 __DEFAULT_ENCODING));
145 __writer =
146 new BufferedWriter(new OutputStreamWriter(_output_,
147 __DEFAULT_ENCODING));
148 __getReply();
149 setState(AUTHORIZATION_STATE);
150 }
151
152
153 /***
154 * Adds a ProtocolCommandListener. Delegates this task to
155 * {@link #_commandSupport_ _commandSupport_ }.
156 * <p>
157 * @param listener The ProtocolCommandListener to add.
158 ***/
159 public void addProtocolCommandListener(ProtocolCommandListener listener)
160 {
161 _commandSupport_.addProtocolCommandListener(listener);
162 }
163
164 /***
165 * Removes a ProtocolCommandListener. Delegates this task to
166 * {@link #_commandSupport_ _commandSupport_ }.
167 * <p>
168 * @param listener The ProtocolCommandListener to remove.
169 ***/
170 public void removeProtocolCommandistener(ProtocolCommandListener listener)
171 {
172 _commandSupport_.removeProtocolCommandListener(listener);
173 }
174
175
176 /***
177 * Sets POP3 client state. This must be one of the
178 * <code>_STATE</code> constants.
179 * <p>
180 * @param state The new state.
181 ***/
182 public void setState(int state)
183 {
184 __popState = state;
185 }
186
187
188 /***
189 * Returns the current POP3 client state.
190 * <p>
191 * @return The current POP3 client state.
192 ***/
193 public int getState()
194 {
195 return __popState;
196 }
197
198
199 /***
200 * Retrieves the additional lines of a multi-line server reply.
201 ***/
202 public void getAdditionalReply() throws IOException
203 {
204 String line;
205
206 line = _reader.readLine();
207 while (line != null)
208 {
209 _replyLines.addElement(line);
210 if (line.equals("."))
211 break;
212 line = _reader.readLine();
213 }
214 }
215
216
217 /***
218 * Disconnects the client from the server, and sets the state to
219 * <code> DISCONNECTED_STATE </code>. The reply text information
220 * from the last issued command is voided to allow garbage collection
221 * of the memory used to store that information.
222 * <p>
223 * @exception IOException If there is an error in disconnecting.
224 ***/
225 public void disconnect() throws IOException
226 {
227 super.disconnect();
228 _reader = null;
229 __writer = null;
230 _lastReplyLine = null;
231 _replyLines.setSize(0);
232 setState(DISCONNECTED_STATE);
233 }
234
235
236 /***
237 * Sends a command an arguments to the server and returns the reply code.
238 * <p>
239 * @param command The POP3 command to send.
240 * @param args The command arguments.
241 * @return The server reply code (either POP3Reply.OK or POP3Reply.ERROR).
242 ***/
243 public int sendCommand(String command, String args) throws IOException
244 {
245 String message;
246
247 __commandBuffer.setLength(0);
248 __commandBuffer.append(command);
249
250 if (args != null)
251 {
252 __commandBuffer.append(' ');
253 __commandBuffer.append(args);
254 }
255 __commandBuffer.append(SocketClient.NETASCII_EOL);
256
257 __writer.write(message = __commandBuffer.toString());
258 __writer.flush();
259
260 if (_commandSupport_.getListenerCount() > 0)
261 _commandSupport_.fireCommandSent(command, message);
262
263 __getReply();
264 return _replyCode;
265 }
266
267 /***
268 * Sends a command with no arguments to the server and returns the
269 * reply code.
270 * <p>
271 * @param command The POP3 command to send.
272 * @return The server reply code (either POP3Reply.OK or POP3Reply.ERROR).
273 ***/
274 public int sendCommand(String command) throws IOException
275 {
276 return sendCommand(command, null);
277 }
278
279 /***
280 * Sends a command an arguments to the server and returns the reply code.
281 * <p>
282 * @param command The POP3 command to send
283 * (one of the POP3Command constants).
284 * @param args The command arguments.
285 * @return The server reply code (either POP3Reply.OK or POP3Reply.ERROR).
286 ***/
287 public int sendCommand(int command, String args) throws IOException
288 {
289 return sendCommand(POP3Command._commands[command], args);
290 }
291
292 /***
293 * Sends a command with no arguments to the server and returns the
294 * reply code.
295 * <p>
296 * @param command The POP3 command to send
297 * (one of the POP3Command constants).
298 * @return The server reply code (either POP3Reply.OK or POP3Reply.ERROR).
299 ***/
300 public int sendCommand(int command) throws IOException
301 {
302 return sendCommand(POP3Command._commands[command], null);
303 }
304
305
306 /***
307 * Returns an array of lines received as a reply to the last command
308 * sent to the server. The lines have end of lines truncated. If
309 * the reply is a single line, but its format ndicates it should be
310 * a multiline reply, then you must call
311 * {@link #getAdditionalReply getAdditionalReply() } to
312 * fetch the rest of the reply, and then call <code>getReplyStrings</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[] getReplyStrings()
319 {
320 String[] lines;
321 lines = new String[_replyLines.size()];
322 _replyLines.copyInto(lines);
323 return lines;
324 }
325
326 /***
327 * Returns the reply to the last command sent to the server.
328 * The value is a single string containing all the reply lines including
329 * newlines. If the reply is a single line, but its format ndicates it
330 * should be a multiline reply, then you must call
331 * {@link #getAdditionalReply getAdditionalReply() } to
332 * fetch the rest of the reply, and then call <code>getReplyString</code>
333 * again. You only have to worry about this if you are implementing
334 * your own client using the {@link #sendCommand sendCommand } methods.
335 * <p>
336 * @return The last server response.
337 ***/
338 public String getReplyString()
339 {
340 Enumeration en;
341 StringBuffer buffer = new StringBuffer(256);
342
343 en = _replyLines.elements();
344 while (en.hasMoreElements())
345 {
346 buffer.append((String)en.nextElement());
347 buffer.append(SocketClient.NETASCII_EOL);
348 }
349
350 return buffer.toString();
351 }
352
353 }
354