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.nio.charset.Charset; 027import java.nio.charset.StandardCharsets; 028import java.util.ArrayList; 029import java.util.List; 030 031import org.apache.commons.net.MalformedServerReplyException; 032import org.apache.commons.net.ProtocolCommandSupport; 033import org.apache.commons.net.SocketClient; 034import org.apache.commons.net.io.CRLFLineReader; 035 036/*** 037 * The POP3 class is not meant to be used by itself and is provided 038 * only so that you may easily implement your own POP3 client if 039 * you so desire. If you have no need to perform your own implementation, 040 * you should use {@link org.apache.commons.net.pop3.POP3Client}. 041 * <p> 042 * Rather than list it separately for each method, we mention here that 043 * every method communicating with the server and throwing an IOException 044 * can also throw a 045 * {@link org.apache.commons.net.MalformedServerReplyException} 046 * , which is a subclass 047 * of IOException. A MalformedServerReplyException will be thrown when 048 * the reply received from the server deviates enough from the protocol 049 * specification that it cannot be interpreted in a useful manner despite 050 * attempts to be as lenient as possible. 051 * 052 * 053 * @see POP3Client 054 * @see org.apache.commons.net.MalformedServerReplyException 055 ***/ 056 057public class POP3 extends SocketClient 058{ 059 /*** The default POP3 port. Set to 110 according to RFC 1288. ***/ 060 public static final int DEFAULT_PORT = 110; 061 /*** 062 * A constant representing the state where the client is not yet connected 063 * to a POP3 server. 064 ***/ 065 public static final int DISCONNECTED_STATE = -1; 066 /*** A constant representing the POP3 authorization state. ***/ 067 public static final int AUTHORIZATION_STATE = 0; 068 /*** A constant representing the POP3 transaction state. ***/ 069 public static final int TRANSACTION_STATE = 1; 070 /*** A constant representing the POP3 update state. ***/ 071 public static final int UPDATE_STATE = 2; 072 073 static final String _OK = "+OK"; 074 // The reply indicating intermediate response to a command. 075 static final String _OK_INT = "+ "; 076 static final String _ERROR = "-ERR"; 077 078 // We have to ensure that the protocol communication is in ASCII 079 // but we use ISO-8859-1 just in case 8-bit characters cross 080 // the wire. 081 static final Charset _DEFAULT_ENCODING = StandardCharsets.ISO_8859_1; 082 083 private int popState; 084 BufferedWriter writer; 085 086 BufferedReader reader; 087 int replyCode; 088 String lastReplyLine; 089 List<String> replyLines; 090 091 /** 092 * A ProtocolCommandSupport object used to manage the registering of 093 * ProtocolCommandListeners and the firing of ProtocolCommandEvents. 094 */ 095 protected ProtocolCommandSupport _commandSupport_; 096 097 /*** 098 * The default POP3Client constructor. Initializes the state 099 * to <code>DISCONNECTED_STATE</code>. 100 ***/ 101 public POP3() 102 { 103 setDefaultPort(DEFAULT_PORT); 104 popState = DISCONNECTED_STATE; 105 reader = null; 106 writer = null; 107 replyLines = new ArrayList<>(); 108 _commandSupport_ = new ProtocolCommandSupport(this); 109 } 110 111 private void getReply() throws IOException 112 { 113 String line; 114 115 replyLines.clear(); 116 line = reader.readLine(); 117 118 if (line == null) { 119 throw new EOFException("Connection closed without indication."); 120 } 121 122 if (line.startsWith(_OK)) { 123 replyCode = POP3Reply.OK; 124 } else if (line.startsWith(_ERROR)) { 125 replyCode = POP3Reply.ERROR; 126 } else if (line.startsWith(_OK_INT)) { 127 replyCode = POP3Reply.OK_INT; 128 } else { 129 throw new 130 MalformedServerReplyException( 131 "Received invalid POP3 protocol response from server." + line); 132 } 133 134 replyLines.add(line); 135 lastReplyLine = line; 136 137 fireReplyReceived(replyCode, getReplyString()); 138 } 139 140 141 /*** 142 * Performs connection initialization and sets state to 143 * <code> AUTHORIZATION_STATE </code>. 144 ***/ 145 @Override 146 protected void _connectAction_() throws IOException 147 { 148 super._connectAction_(); 149 reader = 150 new CRLFLineReader(new InputStreamReader(_input_, 151 _DEFAULT_ENCODING)); 152 writer = 153 new BufferedWriter(new OutputStreamWriter(_output_, 154 _DEFAULT_ENCODING)); 155 getReply(); 156 setState(AUTHORIZATION_STATE); 157 } 158 159 160 /** 161 * Set the internal POP3 state. 162 * @param state the new state. This must be one of the <code>_STATE</code> constants. 163 */ 164 public void setState(final int state) 165 { 166 popState = state; 167 } 168 169 170 /*** 171 * Returns the current POP3 client state. 172 * 173 * @return The current POP3 client state. 174 ***/ 175 public int getState() 176 { 177 return popState; 178 } 179 180 181 /*** 182 * Retrieves the additional lines of a multi-line server reply. 183 * @throws IOException on error 184 ***/ 185 public void getAdditionalReply() throws IOException 186 { 187 String line; 188 189 line = reader.readLine(); 190 while (line != null) 191 { 192 replyLines.add(line); 193 if (line.equals(".")) { 194 break; 195 } 196 line = reader.readLine(); 197 } 198 } 199 200 201 /*** 202 * Disconnects the client from the server, and sets the state to 203 * <code> DISCONNECTED_STATE </code>. The reply text information 204 * from the last issued command is voided to allow garbage collection 205 * of the memory used to store that information. 206 * 207 * @throws IOException If there is an error in disconnecting. 208 ***/ 209 @Override 210 public void disconnect() throws IOException 211 { 212 super.disconnect(); 213 reader = null; 214 writer = null; 215 lastReplyLine = null; 216 replyLines.clear(); 217 setState(DISCONNECTED_STATE); 218 } 219 220 221 /*** 222 * Sends a command an arguments to the server and returns the reply code. 223 * 224 * @param command The POP3 command to send. 225 * @param args The command arguments. 226 * @return The server reply code (either POP3Reply.OK, POP3Reply.ERROR or POP3Reply.OK_INT). 227 * @throws IOException on error 228 ***/ 229 public int sendCommand(final String command, final String args) throws IOException 230 { 231 if (writer == null) { 232 throw new IllegalStateException("Socket is not connected"); 233 } 234 final StringBuilder __commandBuffer = new StringBuilder(); 235 __commandBuffer.append(command); 236 237 if (args != null) 238 { 239 __commandBuffer.append(' '); 240 __commandBuffer.append(args); 241 } 242 __commandBuffer.append(SocketClient.NETASCII_EOL); 243 244 final String message = __commandBuffer.toString(); 245 writer.write(message); 246 writer.flush(); 247 248 fireCommandSent(command, message); 249 250 getReply(); 251 return replyCode; 252 } 253 254 /*** 255 * Sends a command with no arguments to the server and returns the 256 * reply code. 257 * 258 * @param command The POP3 command to send. 259 * @return The server reply code (either POP3Reply.OK, POP3Reply.ERROR or POP3Reply.OK_INT). 260 * @throws IOException on error 261 ***/ 262 public int sendCommand(final String command) throws IOException 263 { 264 return sendCommand(command, null); 265 } 266 267 /*** 268 * Sends a command an arguments to the server and returns the reply code. 269 * 270 * @param command The POP3 command to send 271 * (one of the POP3Command constants). 272 * @param args The command arguments. 273 * @return The server reply code (either POP3Reply.OK, POP3Reply.ERROR or POP3Reply.OK_INT). 274 * @throws IOException on error 275 ***/ 276 public int sendCommand(final int command, final String args) throws IOException 277 { 278 return sendCommand(POP3Command.commands[command], args); 279 } 280 281 /*** 282 * Sends a command with no arguments to the server and returns the 283 * reply code. 284 * 285 * @param command The POP3 command to send 286 * (one of the POP3Command constants). 287 * @return The server reply code (either POP3Reply.OK, POP3Reply.ERROR or POP3Reply.OK_INT). 288 * @throws IOException on error 289 ***/ 290 public int sendCommand(final int command) throws IOException 291 { 292 return sendCommand(POP3Command.commands[command], null); 293 } 294 295 296 /*** 297 * Returns an array of lines received as a reply to the last command 298 * sent to the server. The lines have end of lines truncated. If 299 * the reply is a single line, but its format ndicates it should be 300 * a multiline reply, then you must call 301 * {@link #getAdditionalReply getAdditionalReply() } to 302 * fetch the rest of the reply, and then call <code>getReplyStrings</code> 303 * again. You only have to worry about this if you are implementing 304 * your own client using the {@link #sendCommand sendCommand } methods. 305 * 306 * @return The last server response. 307 ***/ 308 public String[] getReplyStrings() 309 { 310 return replyLines.toArray(new String[replyLines.size()]); 311 } 312 313 /*** 314 * Returns the reply to the last command sent to the server. 315 * The value is a single string containing all the reply lines including 316 * newlines. If the reply is a single line, but its format ndicates it 317 * should be a multiline reply, then you must call 318 * {@link #getAdditionalReply getAdditionalReply() } to 319 * fetch the rest of the reply, and then call <code>getReplyString</code> 320 * again. You only have to worry about this if you are implementing 321 * your own client using the {@link #sendCommand sendCommand } methods. 322 * 323 * @return The last server response. 324 ***/ 325 public String getReplyString() 326 { 327 final StringBuilder buffer = new StringBuilder(256); 328 329 for (final String entry : replyLines) 330 { 331 buffer.append(entry); 332 buffer.append(SocketClient.NETASCII_EOL); 333 } 334 335 return buffer.toString(); 336 } 337 338 /** 339 * Removes a ProtocolCommandListener. 340 * 341 * Delegates this incorrectly named method - removeProtocolCommandistener (note the missing "L")- to 342 * the correct method {@link SocketClient#removeProtocolCommandListener} 343 * @param listener The ProtocolCommandListener to remove 344 */ 345 public void removeProtocolCommandistener(final org.apache.commons.net.ProtocolCommandListener listener){ 346 removeProtocolCommandListener(listener); 347 } 348 349 /** 350 * Provide command support to super-class 351 */ 352 @Override 353 protected ProtocolCommandSupport getCommandSupport() { 354 return _commandSupport_; 355 } 356} 357