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