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 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 * <p> 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 ***/ 182 public void getAdditionalReply() throws IOException 183 { 184 String line; 185 186 line = _reader.readLine(); 187 while (line != null) 188 { 189 _replyLines.add(line); 190 if (line.equals(".")) { 191 break; 192 } 193 line = _reader.readLine(); 194 } 195 } 196 197 198 /*** 199 * Disconnects the client from the server, and sets the state to 200 * <code> DISCONNECTED_STATE </code>. The reply text information 201 * from the last issued command is voided to allow garbage collection 202 * of the memory used to store that information. 203 * <p> 204 * @exception IOException If there is an error in disconnecting. 205 ***/ 206 @Override 207 public void disconnect() throws IOException 208 { 209 super.disconnect(); 210 _reader = null; 211 _writer = null; 212 _lastReplyLine = null; 213 _replyLines.clear(); 214 setState(DISCONNECTED_STATE); 215 } 216 217 218 /*** 219 * Sends a command an arguments to the server and returns the reply code. 220 * <p> 221 * @param command The POP3 command to send. 222 * @param args The command arguments. 223 * @return The server reply code (either POP3Reply.OK, POP3Reply.ERROR or POP3Reply.OK_INT). 224 ***/ 225 public int sendCommand(String command, String args) throws IOException 226 { 227 if (_writer == null) { 228 throw new IllegalStateException("Socket is not connected"); 229 } 230 StringBuilder __commandBuffer = new StringBuilder(); 231 __commandBuffer.append(command); 232 233 if (args != null) 234 { 235 __commandBuffer.append(' '); 236 __commandBuffer.append(args); 237 } 238 __commandBuffer.append(SocketClient.NETASCII_EOL); 239 240 String message = __commandBuffer.toString(); 241 _writer.write(message); 242 _writer.flush(); 243 244 fireCommandSent(command, message); 245 246 __getReply(); 247 return _replyCode; 248 } 249 250 /*** 251 * Sends a command with no arguments to the server and returns the 252 * reply code. 253 * <p> 254 * @param command The POP3 command to send. 255 * @return The server reply code (either POP3Reply.OK, POP3Reply.ERROR or POP3Reply.OK_INT). 256 ***/ 257 public int sendCommand(String command) throws IOException 258 { 259 return sendCommand(command, null); 260 } 261 262 /*** 263 * Sends a command an arguments to the server and returns the reply code. 264 * <p> 265 * @param command The POP3 command to send 266 * (one of the POP3Command constants). 267 * @param args The command arguments. 268 * @return The server reply code (either POP3Reply.OK, POP3Reply.ERROR or POP3Reply.OK_INT). 269 ***/ 270 public int sendCommand(int command, String args) throws IOException 271 { 272 return sendCommand(POP3Command._commands[command], args); 273 } 274 275 /*** 276 * Sends a command with no arguments to the server and returns the 277 * reply code. 278 * <p> 279 * @param command The POP3 command to send 280 * (one of the POP3Command constants). 281 * @return The server reply code (either POP3Reply.OK, POP3Reply.ERROR or POP3Reply.OK_INT). 282 ***/ 283 public int sendCommand(int command) throws IOException 284 { 285 return sendCommand(POP3Command._commands[command], null); 286 } 287 288 289 /*** 290 * Returns an array of lines received as a reply to the last command 291 * sent to the server. The lines have end of lines truncated. If 292 * the reply is a single line, but its format ndicates it should be 293 * a multiline reply, then you must call 294 * {@link #getAdditionalReply getAdditionalReply() } to 295 * fetch the rest of the reply, and then call <code>getReplyStrings</code> 296 * again. You only have to worry about this if you are implementing 297 * your own client using the {@link #sendCommand sendCommand } methods. 298 * <p> 299 * @return The last server response. 300 ***/ 301 public String[] getReplyStrings() 302 { 303 return _replyLines.toArray(new String[_replyLines.size()]); 304 } 305 306 /*** 307 * Returns the reply to the last command sent to the server. 308 * The value is a single string containing all the reply lines including 309 * newlines. If the reply is a single line, but its format ndicates it 310 * should be a multiline reply, then you must call 311 * {@link #getAdditionalReply getAdditionalReply() } to 312 * fetch the rest of the reply, and then call <code>getReplyString</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 getReplyString() 319 { 320 StringBuilder buffer = new StringBuilder(256); 321 322 for (String entry : _replyLines) 323 { 324 buffer.append(entry); 325 buffer.append(SocketClient.NETASCII_EOL); 326 } 327 328 return buffer.toString(); 329 } 330 331 /** 332 * Removes a ProtocolCommandListener. 333 * 334 * Delegates this incorrectly named method - removeProtocolCommandistener (note the missing "L")- to 335 * the correct method {@link SocketClient#removeProtocolCommandListener} 336 * @param listener The ProtocolCommandListener to remove 337 */ 338 public void removeProtocolCommandistener(org.apache.commons.net.ProtocolCommandListener listener){ 339 removeProtocolCommandListener(listener); 340 } 341 342 /** 343 * Provide command support to super-class 344 */ 345 @Override 346 protected ProtocolCommandSupport getCommandSupport() { 347 return _commandSupport_; 348 } 349 } 350