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.Enumeration; 027 import java.util.Vector; 028 029 import org.apache.commons.net.MalformedServerReplyException; 030 import org.apache.commons.net.ProtocolCommandListener; 031 import org.apache.commons.net.ProtocolCommandSupport; 032 import org.apache.commons.net.SocketClient; 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 * @author Daniel F. Savarese 052 * @see POP3Client 053 * @see org.apache.commons.net.MalformedServerReplyException 054 ***/ 055 056 public class POP3 extends SocketClient 057 { 058 /*** The default POP3 port. Set to 110 according to RFC 1288. ***/ 059 public static final int DEFAULT_PORT = 110; 060 /*** 061 * A constant representing the state where the client is not yet connected 062 * to a POP3 server. 063 ***/ 064 public static final int DISCONNECTED_STATE = -1; 065 /*** A constant representing the POP3 authorization state. ***/ 066 public static final int AUTHORIZATION_STATE = 0; 067 /*** A constant representing the POP3 transaction state. ***/ 068 public static final int TRANSACTION_STATE = 1; 069 /*** A constant representing the POP3 update state. ***/ 070 public static final int UPDATE_STATE = 2; 071 072 static final String _OK = "+OK"; 073 static final String _ERROR = "-ERR"; 074 075 // We have to ensure that the protocol communication is in ASCII 076 // but we use ISO-8859-1 just in case 8-bit characters cross 077 // the wire. 078 private static final String __DEFAULT_ENCODING = "ISO-8859-1"; 079 080 private int __popState; 081 private BufferedWriter __writer; 082 private StringBuffer __commandBuffer; 083 084 BufferedReader _reader; 085 int _replyCode; 086 String _lastReplyLine; 087 Vector<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 __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 StringBuilder buffer = new StringBuilder(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