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