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