View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.net.pop3;
19  
20  import java.io.BufferedReader;
21  import java.io.BufferedWriter;
22  import java.io.EOFException;
23  import java.io.IOException;
24  import java.io.InputStreamReader;
25  import java.io.OutputStreamWriter;
26  import java.util.Enumeration;
27  import java.util.Vector;
28  
29  import org.apache.commons.net.MalformedServerReplyException;
30  import org.apache.commons.net.ProtocolCommandListener;
31  import org.apache.commons.net.ProtocolCommandSupport;
32  import org.apache.commons.net.SocketClient;
33  
34  /***
35   * The POP3 class is not meant to be used by itself and is provided
36   * only so that you may easily implement your own POP3 client if
37   * you so desire.  If you have no need to perform your own implementation,
38   * you should use {@link org.apache.commons.net.pop3.POP3Client}.
39   * <p>
40   * Rather than list it separately for each method, we mention here that
41   * every method communicating with the server and throwing an IOException
42   * can also throw a
43   * {@link org.apache.commons.net.MalformedServerReplyException}
44   * , which is a subclass
45   * of IOException.  A MalformedServerReplyException will be thrown when
46   * the reply received from the server deviates enough from the protocol
47   * specification that it cannot be interpreted in a useful manner despite
48   * attempts to be as lenient as possible.
49   * <p>
50   * <p>
51   * @author Daniel F. Savarese
52   * @see POP3Client
53   * @see org.apache.commons.net.MalformedServerReplyException
54   ***/
55  
56  public class POP3 extends SocketClient
57  {
58      /*** The default POP3 port.  Set to 110 according to RFC 1288. ***/
59      public static final int DEFAULT_PORT = 110;
60      /***
61       * A constant representing the state where the client is not yet connected
62       * to a POP3 server.
63       ***/
64      public static final int DISCONNECTED_STATE = -1;
65      /***  A constant representing the POP3 authorization state. ***/
66      public static final int AUTHORIZATION_STATE = 0;
67      /***  A constant representing the POP3 transaction state. ***/
68      public static final int TRANSACTION_STATE = 1;
69      /***  A constant representing the POP3 update state. ***/
70      public static final int UPDATE_STATE = 2;
71  
72      static final String _OK = "+OK";
73      static final String _ERROR = "-ERR";
74  
75      // We have to ensure that the protocol communication is in ASCII
76      // but we use ISO-8859-1 just in case 8-bit characters cross
77      // the wire.
78      private static final String __DEFAULT_ENCODING = "ISO-8859-1";
79  
80      private int __popState;
81      private BufferedWriter __writer;
82      private StringBuffer __commandBuffer;
83  
84      BufferedReader _reader;
85      int _replyCode;
86      String _lastReplyLine;
87      Vector<String> _replyLines;
88  
89      /***
90       * A ProtocolCommandSupport object used to manage the registering of
91       * ProtocolCommandListeners and te firing of ProtocolCommandEvents.
92       ***/
93      protected ProtocolCommandSupport _commandSupport_;
94  
95      /***
96       * The default POP3Client constructor.  Initializes the state
97       * to <code>DISCONNECTED_STATE</code>.
98       ***/
99      public POP3()
100     {
101         setDefaultPort(DEFAULT_PORT);
102         __commandBuffer = new StringBuffer();
103         __popState = DISCONNECTED_STATE;
104         _reader = null;
105         __writer = null;
106         _replyLines = new Vector<String>();
107         _commandSupport_ = new ProtocolCommandSupport(this);
108     }
109 
110     private void __getReply() throws IOException
111     {
112         String line;
113 
114         _replyLines.setSize(0);
115         line = _reader.readLine();
116 
117         if (line == null)
118             throw new EOFException("Connection closed without indication.");
119 
120         if (line.startsWith(_OK))
121             _replyCode = POP3Reply.OK;
122         else if (line.startsWith(_ERROR))
123             _replyCode = POP3Reply.ERROR;
124         else
125             throw new
126             MalformedServerReplyException(
127                 "Received invalid POP3 protocol response from server.");
128 
129         _replyLines.addElement(line);
130         _lastReplyLine = line;
131 
132         if (_commandSupport_.getListenerCount() > 0)
133             _commandSupport_.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 BufferedReader(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      * Adds a ProtocolCommandListener.  Delegates this task to
158      * {@link #_commandSupport_  _commandSupport_ }.
159      * <p>
160      * @param listener  The ProtocolCommandListener to add.
161      ***/
162     public void addProtocolCommandListener(ProtocolCommandListener listener)
163     {
164         _commandSupport_.addProtocolCommandListener(listener);
165     }
166 
167     /***
168      * Removes a ProtocolCommandListener.  Delegates this task to
169      * {@link #_commandSupport_  _commandSupport_ }.
170      * <p>
171      * @param listener  The ProtocolCommandListener to remove.
172      ***/
173     public void removeProtocolCommandistener(ProtocolCommandListener listener)
174     {
175         _commandSupport_.removeProtocolCommandListener(listener);
176     }
177 
178 
179     /***
180      * Sets POP3 client state.  This must be one of the
181      * <code>_STATE</code> constants.
182      * <p>
183      * @param state  The new state.
184      ***/
185     public void setState(int state)
186     {
187         __popState = state;
188     }
189 
190 
191     /***
192      * Returns the current POP3 client state.
193      * <p>
194      * @return The current POP3 client state.
195      ***/
196     public int getState()
197     {
198         return __popState;
199     }
200 
201 
202     /***
203      * Retrieves the additional lines of a multi-line server reply.
204      ***/
205     public void getAdditionalReply() throws IOException
206     {
207         String line;
208 
209         line = _reader.readLine();
210         while (line != null)
211         {
212             _replyLines.addElement(line);
213             if (line.equals("."))
214                 break;
215             line = _reader.readLine();
216         }
217     }
218 
219 
220     /***
221      * Disconnects the client from the server, and sets the state to
222      * <code> DISCONNECTED_STATE </code>.  The reply text information
223      * from the last issued command is voided to allow garbage collection
224      * of the memory used to store that information.
225      * <p>
226      * @exception IOException  If there is an error in disconnecting.
227      ***/
228     @Override
229     public void disconnect() throws IOException
230     {
231         super.disconnect();
232         _reader = null;
233         __writer = null;
234         _lastReplyLine = null;
235         _replyLines.setSize(0);
236         setState(DISCONNECTED_STATE);
237     }
238 
239 
240     /***
241      * Sends a command an arguments to the server and returns the reply code.
242      * <p>
243      * @param command  The POP3 command to send.
244      * @param args     The command arguments.
245      * @return  The server reply code (either POP3Reply.OK or POP3Reply.ERROR).
246      ***/
247     public int sendCommand(String command, String args) throws IOException
248     {
249         String message;
250 
251         __commandBuffer.setLength(0);
252         __commandBuffer.append(command);
253 
254         if (args != null)
255         {
256             __commandBuffer.append(' ');
257             __commandBuffer.append(args);
258         }
259         __commandBuffer.append(SocketClient.NETASCII_EOL);
260 
261         __writer.write(message = __commandBuffer.toString());
262         __writer.flush();
263 
264         if (_commandSupport_.getListenerCount() > 0)
265             _commandSupport_.fireCommandSent(command, message);
266 
267         __getReply();
268         return _replyCode;
269     }
270 
271     /***
272      * Sends a command with no arguments to the server and returns the
273      * reply code.
274      * <p>
275      * @param command  The POP3 command to send.
276      * @return  The server reply code (either POP3Reply.OK or POP3Reply.ERROR).
277      ***/
278     public int sendCommand(String command) throws IOException
279     {
280         return sendCommand(command, null);
281     }
282 
283     /***
284      * Sends a command an arguments to the server and returns the reply code.
285      * <p>
286      * @param command  The POP3 command to send
287      *                  (one of the POP3Command constants).
288      * @param args     The command arguments.
289      * @return  The server reply code (either POP3Reply.OK or POP3Reply.ERROR).
290      ***/
291     public int sendCommand(int command, String args) throws IOException
292     {
293         return sendCommand(POP3Command._commands[command], args);
294     }
295 
296     /***
297      * Sends a command with no arguments to the server and returns the
298      * reply code.
299      * <p>
300      * @param command  The POP3 command to send
301      *                  (one of the POP3Command constants).
302      * @return  The server reply code (either POP3Reply.OK or POP3Reply.ERROR).
303      ***/
304     public int sendCommand(int command) throws IOException
305     {
306         return sendCommand(POP3Command._commands[command], null);
307     }
308 
309 
310     /***
311      * Returns an array of lines received as a reply to the last command
312      * sent to the server.  The lines have end of lines truncated.  If
313      * the reply is a single line, but its format ndicates it should be
314      * a multiline reply, then you must call
315      * {@link #getAdditionalReply  getAdditionalReply() } to
316      * fetch the rest of the reply, and then call <code>getReplyStrings</code>
317      * again.  You only have to worry about this if you are implementing
318      * your own client using the {@link #sendCommand  sendCommand } methods.
319      * <p>
320      * @return The last server response.
321      ***/
322     public String[] getReplyStrings()
323     {
324         String[] lines;
325         lines = new String[_replyLines.size()];
326         _replyLines.copyInto(lines);
327         return lines;
328     }
329 
330     /***
331      * Returns the reply to the last command sent to the server.
332      * The value is a single string containing all the reply lines including
333      * newlines.  If the reply is a single line, but its format ndicates it
334      * should be a multiline reply, then you must call
335      * {@link #getAdditionalReply  getAdditionalReply() } to
336      * fetch the rest of the reply, and then call <code>getReplyString</code>
337      * again.  You only have to worry about this if you are implementing
338      * your own client using the {@link #sendCommand  sendCommand } methods.
339      * <p>
340      * @return The last server response.
341      ***/
342     public String getReplyString()
343     {
344         Enumeration<String> en;
345         StringBuffer buffer = new StringBuffer(256);
346 
347         en = _replyLines.elements();
348         while (en.hasMoreElements())
349         {
350             buffer.append(en.nextElement());
351             buffer.append(SocketClient.NETASCII_EOL);
352         }
353 
354         return buffer.toString();
355     }
356 
357 }
358