    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 * <p>
    050 * <p>
    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     * <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