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 private static final String __DEFAULT_ENCODING = "ISO-8859-1";
080
081 private int __popState;
082 private 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 if (line.startsWith(_OK))
120 _replyCode = POP3Reply.OK;
121 else if (line.startsWith(_ERROR))
122 _replyCode = POP3Reply.ERROR;
123 else if (line.startsWith(_OK_INT))
124 _replyCode = POP3Reply.OK_INT;
125 else
126 throw new
127 MalformedServerReplyException(
128 "Received invalid POP3 protocol response from server." + line);
129
130 _replyLines.add(line);
131 _lastReplyLine = line;
132
133 fireReplyReceived(_replyCode, getReplyString());
134 }
135
136
137 /***
138 * Performs connection initialization and sets state to
139 * <code> AUTHORIZATION_STATE </code>.
140 ***/
141 @Override
142 protected void _connectAction_() throws IOException
143 {
144 super._connectAction_();
145 _reader =
146 new CRLFLineReader(new InputStreamReader(_input_,
147 __DEFAULT_ENCODING));
148 __writer =
149 new BufferedWriter(new OutputStreamWriter(_output_,
150 __DEFAULT_ENCODING));
151 __getReply();
152 setState(AUTHORIZATION_STATE);
153 }
154
155
156 /**
157 * Set the internal POP3 state.
158 * @param state the new state. This must be one of the <code>_STATE</code> constants.
159 */
160 public void setState(int state)
161 {
162 __popState = state;
163 }
164
165
166 /***
167 * Returns the current POP3 client state.
168 * <p>
169 * @return The current POP3 client state.
170 ***/
171 public int getState()
172 {
173 return __popState;
174 }
175
176
177 /***
178 * Retrieves the additional lines of a multi-line server reply.
179 ***/
180 public void getAdditionalReply() throws IOException
181 {
182 String line;
183
184 line = _reader.readLine();
185 while (line != null)
186 {
187 _replyLines.add(line);
188 if (line.equals("."))
189 break;
190 line = _reader.readLine();
191 }
192 }
193
194
195 /***
196 * Disconnects the client from the server, and sets the state to
197 * <code> DISCONNECTED_STATE </code>. The reply text information
198 * from the last issued command is voided to allow garbage collection
199 * of the memory used to store that information.
200 * <p>
201 * @exception IOException If there is an error in disconnecting.
202 ***/
203 @Override
204 public void disconnect() throws IOException
205 {
206 super.disconnect();
207 _reader = null;
208 __writer = null;
209 _lastReplyLine = null;
210 _replyLines.clear();
211 setState(DISCONNECTED_STATE);
212 }
213
214
215 /***
216 * Sends a command an arguments to the server and returns the reply code.
217 * <p>
218 * @param command The POP3 command to send.
219 * @param args The command arguments.
220 * @return The server reply code (either POP3Reply.OK, POP3Reply.ERROR or POP3Reply.OK_INT).
221 ***/
222 public int sendCommand(String command, String args) throws IOException
223 {
224 if (__writer == null) {
225 throw new IllegalStateException("Socket is not connected");
226 }
227 StringBuilder __commandBuffer = new StringBuilder();
228 __commandBuffer.append(command);
229
230 if (args != null)
231 {
232 __commandBuffer.append(' ');
233 __commandBuffer.append(args);
234 }
235 __commandBuffer.append(SocketClient.NETASCII_EOL);
236
237 String message = __commandBuffer.toString();
238 __writer.write(message);
239 __writer.flush();
240
241 fireCommandSent(command, message);
242
243 __getReply();
244 return _replyCode;
245 }
246
247 /***
248 * Sends a command with no arguments to the server and returns the
249 * reply code.
250 * <p>
251 * @param command The POP3 command to send.
252 * @return The server reply code (either POP3Reply.OK, POP3Reply.ERROR or POP3Reply.OK_INT).
253 ***/
254 public int sendCommand(String command) throws IOException
255 {
256 return sendCommand(command, null);
257 }
258
259 /***
260 * Sends a command an arguments to the server and returns the reply code.
261 * <p>
262 * @param command The POP3 command to send
263 * (one of the POP3Command constants).
264 * @param args The command arguments.
265 * @return The server reply code (either POP3Reply.OK, POP3Reply.ERROR or POP3Reply.OK_INT).
266 ***/
267 public int sendCommand(int command, String args) throws IOException
268 {
269 return sendCommand(POP3Command._commands[command], args);
270 }
271
272 /***
273 * Sends a command with no arguments to the server and returns the
274 * reply code.
275 * <p>
276 * @param command The POP3 command to send
277 * (one of the POP3Command constants).
278 * @return The server reply code (either POP3Reply.OK, POP3Reply.ERROR or POP3Reply.OK_INT).
279 ***/
280 public int sendCommand(int command) throws IOException
281 {
282 return sendCommand(POP3Command._commands[command], null);
283 }
284
285
286 /***
287 * Returns an array of lines received as a reply to the last command
288 * sent to the server. The lines have end of lines truncated. If
289 * the reply is a single line, but its format ndicates it should be
290 * a multiline reply, then you must call
291 * {@link #getAdditionalReply getAdditionalReply() } to
292 * fetch the rest of the reply, and then call <code>getReplyStrings</code>
293 * again. You only have to worry about this if you are implementing
294 * your own client using the {@link #sendCommand sendCommand } methods.
295 * <p>
296 * @return The last server response.
297 ***/
298 public String[] getReplyStrings()
299 {
300 return _replyLines.toArray(new String[_replyLines.size()]);
301 }
302
303 /***
304 * Returns the reply to the last command sent to the server.
305 * The value is a single string containing all the reply lines including
306 * newlines. If the reply is a single line, but its format ndicates it
307 * should be a multiline reply, then you must call
308 * {@link #getAdditionalReply getAdditionalReply() } to
309 * fetch the rest of the reply, and then call <code>getReplyString</code>
310 * again. You only have to worry about this if you are implementing
311 * your own client using the {@link #sendCommand sendCommand } methods.
312 * <p>
313 * @return The last server response.
314 ***/
315 public String getReplyString()
316 {
317 StringBuilder buffer = new StringBuilder(256);
318
319 for (String entry : _replyLines)
320 {
321 buffer.append(entry);
322 buffer.append(SocketClient.NETASCII_EOL);
323 }
324
325 return buffer.toString();
326 }
327
328 /**
329 * Removes a ProtocolCommandListener.
330 *
331 * Delegates this incorrectly named method - removeProtocolCommandistener (note the missing "L")- to
332 * the correct method {@link SocketClient#removeProtocolCommandListener}
333 * @param listener The ProtocolCommandListener to remove
334 */
335 public void removeProtocolCommandistener(org.apache.commons.net.ProtocolCommandListener listener){
336 removeProtocolCommandListener(listener);
337 }
338
339 /**
340 * Provide command support to super-class
341 */
342 @Override
343 protected ProtocolCommandSupport getCommandSupport() {
344 return _commandSupport_;
345 }
346 }
347