1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.commons.net.pop3;
17
18 import java.io.BufferedReader;
19 import java.io.BufferedWriter;
20 import java.io.EOFException;
21 import java.io.IOException;
22 import java.io.InputStreamReader;
23 import java.io.OutputStreamWriter;
24 import java.lang.StringBuffer;
25 import java.util.Enumeration;
26 import java.util.Vector;
27 import org.apache.commons.net.MalformedServerReplyException;
28 import org.apache.commons.net.ProtocolCommandListener;
29 import org.apache.commons.net.ProtocolCommandSupport;
30 import org.apache.commons.net.SocketClient;
31
32 /***
33 * The POP3 class is not meant to be used by itself and is provided
34 * only so that you may easily implement your own POP3 client if
35 * you so desire. If you have no need to perform your own implementation,
36 * you should use {@link org.apache.commons.net.pop3.POP3Client}.
37 * <p>
38 * Rather than list it separately for each method, we mention here that
39 * every method communicating with the server and throwing an IOException
40 * can also throw a
41 * {@link org.apache.commons.net.MalformedServerReplyException}
42 * , which is a subclass
43 * of IOException. A MalformedServerReplyException will be thrown when
44 * the reply received from the server deviates enough from the protocol
45 * specification that it cannot be interpreted in a useful manner despite
46 * attempts to be as lenient as possible.
47 * <p>
48 * <p>
49 * @author Daniel F. Savarese
50 * @see POP3Client
51 * @see org.apache.commons.net.MalformedServerReplyException
52 ***/
53
54 public class POP3 extends SocketClient
55 {
56 /*** The default POP3 port. Set to 110 according to RFC 1288. ***/
57 public static final int DEFAULT_PORT = 110;
58 /***
59 * A constant representing the state where the client is not yet connected
60 * to a POP3 server.
61 ***/
62 public static final int DISCONNECTED_STATE = -1;
63 /*** A constant representing the POP3 authorization state. ***/
64 public static final int AUTHORIZATION_STATE = 0;
65 /*** A constant representing the POP3 transaction state. ***/
66 public static final int TRANSACTION_STATE = 1;
67 /*** A constant representing the POP3 update state. ***/
68 public static final int UPDATE_STATE = 2;
69
70 static final String _OK = "+OK";
71 static final String _ERROR = "-ERR";
72
73
74
75
76 private static final String __DEFAULT_ENCODING = "ISO-8859-1";
77
78 private int __popState;
79 private BufferedWriter __writer;
80 private StringBuffer __commandBuffer;
81
82 BufferedReader _reader;
83 int _replyCode;
84 String _lastReplyLine;
85 Vector _replyLines;
86
87 /***
88 * A ProtocolCommandSupport object used to manage the registering of
89 * ProtocolCommandListeners and te firing of ProtocolCommandEvents.
90 ***/
91 protected ProtocolCommandSupport _commandSupport_;
92
93 /***
94 * The default POP3Client constructor. Initializes the state
95 * to <code>DISCONNECTED_STATE</code>.
96 ***/
97 public POP3()
98 {
99 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