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