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: /*** The default POP3 port. Set to 110 according to RFC 1288. ***/
056: public static final int DEFAULT_PORT = 110;
057: /***
058: * A constant representing the state where the client is not yet connected
059: * to a POP3 server.
060: ***/
061: public static final int DISCONNECTED_STATE = -1;
062: /*** A constant representing the POP3 authorization state. ***/
063: public static final int AUTHORIZATION_STATE = 0;
064: /*** A constant representing the POP3 transaction state. ***/
065: public static final int TRANSACTION_STATE = 1;
066: /*** A constant representing the POP3 update state. ***/
067: public static final int UPDATE_STATE = 2;
068:
069: static final String _OK = "+OK";
070: static final String _ERROR = "-ERR";
071:
072: // We have to ensure that the protocol communication is in ASCII
073: // but we use ISO-8859-1 just in case 8-bit characters cross
074: // the wire.
075: private static final String __DEFAULT_ENCODING = "ISO-8859-1";
076:
077: private int __popState;
078: private BufferedWriter __writer;
079: private StringBuffer __commandBuffer;
080:
081: BufferedReader _reader;
082: int _replyCode;
083: String _lastReplyLine;
084: Vector _replyLines;
085:
086: /***
087: * A ProtocolCommandSupport object used to manage the registering of
088: * ProtocolCommandListeners and te firing of ProtocolCommandEvents.
089: ***/
090: protected ProtocolCommandSupport _commandSupport_;
091:
092: /***
093: * The default POP3Client constructor. Initializes the state
094: * to <code>DISCONNECTED_STATE</code>.
095: ***/
096: public POP3() {
097: setDefaultPort(DEFAULT_PORT);
098: __commandBuffer = new StringBuffer();
099: __popState = DISCONNECTED_STATE;
100: _reader = null;
101: __writer = null;
102: _replyLines = new Vector();
103: _commandSupport_ = new ProtocolCommandSupport(this );
104: }
105:
106: private void __getReply() throws IOException {
107: String line;
108:
109: _replyLines.setSize(0);
110: line = _reader.readLine();
111:
112: if (line == null)
113: throw new EOFException(
114: "Connection closed without indication.");
115:
116: if (line.startsWith(_OK))
117: _replyCode = POP3Reply.OK;
118: else if (line.startsWith(_ERROR))
119: _replyCode = POP3Reply.ERROR;
120: else
121: throw new MalformedServerReplyException(
122: "Received invalid POP3 protocol response from server.");
123:
124: _replyLines.addElement(line);
125: _lastReplyLine = line;
126:
127: if (_commandSupport_.getListenerCount() > 0)
128: _commandSupport_.fireReplyReceived(_replyCode,
129: getReplyString());
130: }
131:
132: /***
133: * Performs connection initialization and sets state to
134: * <code> AUTHORIZATION_STATE </code>.
135: ***/
136: protected void _connectAction_() throws IOException {
137: super ._connectAction_();
138: _reader = new BufferedReader(new InputStreamReader(_input_,
139: __DEFAULT_ENCODING));
140: __writer = new BufferedWriter(new OutputStreamWriter(_output_,
141: __DEFAULT_ENCODING));
142: __getReply();
143: setState(AUTHORIZATION_STATE);
144: }
145:
146: /***
147: * Adds a ProtocolCommandListener. Delegates this task to
148: * {@link #_commandSupport_ _commandSupport_ }.
149: * <p>
150: * @param listener The ProtocolCommandListener to add.
151: ***/
152: public void addProtocolCommandListener(
153: ProtocolCommandListener listener) {
154: _commandSupport_.addProtocolCommandListener(listener);
155: }
156:
157: /***
158: * Removes a ProtocolCommandListener. Delegates this task to
159: * {@link #_commandSupport_ _commandSupport_ }.
160: * <p>
161: * @param listener The ProtocolCommandListener to remove.
162: ***/
163: public void removeProtocolCommandistener(
164: ProtocolCommandListener listener) {
165: _commandSupport_.removeProtocolCommandListener(listener);
166: }
167:
168: /***
169: * Sets POP3 client state. This must be one of the
170: * <code>_STATE</code> constants.
171: * <p>
172: * @param state The new state.
173: ***/
174: public void setState(int state) {
175: __popState = state;
176: }
177:
178: /***
179: * Returns the current POP3 client state.
180: * <p>
181: * @return The current POP3 client state.
182: ***/
183: public int getState() {
184: return __popState;
185: }
186:
187: /***
188: * Retrieves the additional lines of a multi-line server reply.
189: ***/
190: public void getAdditionalReply() throws IOException {
191: String line;
192:
193: line = _reader.readLine();
194: while (line != null) {
195: _replyLines.addElement(line);
196: if (line.equals("."))
197: break;
198: line = _reader.readLine();
199: }
200: }
201:
202: /***
203: * Disconnects the client from the server, and sets the state to
204: * <code> DISCONNECTED_STATE </code>. The reply text information
205: * from the last issued command is voided to allow garbage collection
206: * of the memory used to store that information.
207: * <p>
208: * @exception IOException If there is an error in disconnecting.
209: ***/
210: public void disconnect() throws IOException {
211: super .disconnect();
212: _reader = null;
213: __writer = null;
214: _lastReplyLine = null;
215: _replyLines.setSize(0);
216: setState(DISCONNECTED_STATE);
217: }
218:
219: /***
220: * Sends a command an arguments to the server and returns the reply code.
221: * <p>
222: * @param command The POP3 command to send.
223: * @param args The command arguments.
224: * @return The server reply code (either POP3Reply.OK or POP3Reply.ERROR).
225: ***/
226: public int sendCommand(String command, String args)
227: throws IOException {
228: String message;
229:
230: __commandBuffer.setLength(0);
231: __commandBuffer.append(command);
232:
233: if (args != null) {
234: __commandBuffer.append(' ');
235: __commandBuffer.append(args);
236: }
237: __commandBuffer.append(SocketClient.NETASCII_EOL);
238:
239: __writer.write(message = __commandBuffer.toString());
240: __writer.flush();
241:
242: if (_commandSupport_.getListenerCount() > 0)
243: _commandSupport_.fireCommandSent(command, message);
244:
245: __getReply();
246: return _replyCode;
247: }
248:
249: /***
250: * Sends a command with no arguments to the server and returns the
251: * reply code.
252: * <p>
253: * @param command The POP3 command to send.
254: * @return The server reply code (either POP3Reply.OK or POP3Reply.ERROR).
255: ***/
256: public int sendCommand(String command) throws IOException {
257: return sendCommand(command, null);
258: }
259:
260: /***
261: * Sends a command an arguments to the server and returns the reply code.
262: * <p>
263: * @param command The POP3 command to send
264: * (one of the POP3Command constants).
265: * @param args The command arguments.
266: * @return The server reply code (either POP3Reply.OK or POP3Reply.ERROR).
267: ***/
268: public int sendCommand(int command, String args) throws IOException {
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 or POP3Reply.ERROR).
279: ***/
280: public int sendCommand(int command) throws IOException {
281: return sendCommand(POP3Command._commands[command], null);
282: }
283:
284: /***
285: * Returns an array of lines received as a reply to the last command
286: * sent to the server. The lines have end of lines truncated. If
287: * the reply is a single line, but its format ndicates it should be
288: * a multiline reply, then you must call
289: * {@link #getAdditionalReply getAdditionalReply() } to
290: * fetch the rest of the reply, and then call <code>getReplyStrings</code>
291: * again. You only have to worry about this if you are implementing
292: * your own client using the {@link #sendCommand sendCommand } methods.
293: * <p>
294: * @return The last server response.
295: ***/
296: public String[] getReplyStrings() {
297: String[] lines;
298: lines = new String[_replyLines.size()];
299: _replyLines.copyInto(lines);
300: return lines;
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: Enumeration en;
317: StringBuffer buffer = new StringBuffer(256);
318:
319: en = _replyLines.elements();
320: while (en.hasMoreElements()) {
321: buffer.append((String) en.nextElement());
322: buffer.append(SocketClient.NETASCII_EOL);
323: }
324:
325: return buffer.toString();
326: }
327:
328: }
|