    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.Enumeration;
    027    import java.util.Vector;
    028
    029    import org.apache.commons.net.MalformedServerReplyException;
    030    import org.apache.commons.net.ProtocolCommandListener;
    031    import org.apache.commons.net.ProtocolCommandSupport;
    032    import org.apache.commons.net.SocketClient;
    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     * @author Daniel F. Savarese
    052     * @see POP3Client
    053     * @see org.apache.commons.net.MalformedServerReplyException
    054     ***/
    055
    056    public class POP3 extends SocketClient
    057    {
    058        /*** The default POP3 port.  Set to 110 according to RFC 1288. ***/
    059        public static final int DEFAULT_PORT = 110;
    060        /***
    061         * A constant representing the state where the client is not yet connected
    062         * to a POP3 server.
    063         ***/
    064        public static final int DISCONNECTED_STATE = -1;
    065        /***  A constant representing the POP3 authorization state. ***/
    066        public static final int AUTHORIZATION_STATE = 0;
    067        /***  A constant representing the POP3 transaction state. ***/
    068        public static final int TRANSACTION_STATE = 1;
    069        /***  A constant representing the POP3 update state. ***/
    070        public static final int UPDATE_STATE = 2;
    071
    072        static final String _OK = "+OK";
    073        static final String _ERROR = "-ERR";
    074
    075        // We have to ensure that the protocol communication is in ASCII
    076        // but we use ISO-8859-1 just in case 8-bit characters cross
    077        // the wire.
    078        private static final String __DEFAULT_ENCODING = "ISO-8859-1";
    079
    080        private int __popState;
    081        private BufferedWriter __writer;
    082        private StringBuffer __commandBuffer;
    083
    084        BufferedReader _reader;
    085        int _replyCode;
    086        String _lastReplyLine;
    087        Vector<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            __commandBuffer = new StringBuffer();
    103            __popState = DISCONNECTED_STATE;
    104            _reader = null;
    105            __writer = null;
    106            _replyLines = new Vector<String>();
    107            _commandSupport_ = new ProtocolCommandSupport(this);
    108        }
    109
    110        private void __getReply() throws IOException
    111        {
    112            String line;
    113
    114            _replyLines.setSize(0);
    115            line = _reader.readLine();
    116
    117            if (line == null)
    118                throw new EOFException("Connection closed without indication.");
    119
    120            if (line.startsWith(_OK))
    121                _replyCode = POP3Reply.OK;
    122            else if (line.startsWith(_ERROR))
    123                _replyCode = POP3Reply.ERROR;
    124            else
    125                throw new
    126                MalformedServerReplyException(
    127                    "Received invalid POP3 protocol response from server.");
    128
    129            _replyLines.addElement(line);
    130            _lastReplyLine = line;
    131
    132            if (_commandSupport_.getListenerCount() > 0)
    133                _commandSupport_.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 BufferedReader(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         * Adds a ProtocolCommandListener.  Delegates this task to
    158         * {@link #_commandSupport_  _commandSupport_ }.
    159         * <p>
    160         * @param listener  The ProtocolCommandListener to add.
    161         ***/
    162        public void addProtocolCommandListener(ProtocolCommandListener listener)
    163        {
    164            _commandSupport_.addProtocolCommandListener(listener);
    165        }
    166
    167        /***
    168         * Removes a ProtocolCommandListener.  Delegates this task to
    169         * {@link #_commandSupport_  _commandSupport_ }.
    170         * <p>
    171         * @param listener  The ProtocolCommandListener to remove.
    172         ***/
    173        public void removeProtocolCommandistener(ProtocolCommandListener listener)
    174        {
    175            _commandSupport_.removeProtocolCommandListener(listener);
    176        }
    177
    178
    179        /***
    180         * Sets POP3 client state.  This must be one of the
    181         * <code>_STATE</code> constants.
    182         * <p>
    183         * @param state  The new state.
    184         ***/
    185        public void setState(int state)
    186        {
    187            __popState = state;
    188        }
    189
    190
    191        /***
    192         * Returns the current POP3 client state.
    193         * <p>
    194         * @return The current POP3 client state.
    195         ***/
    196        public int getState()
    197        {
    198            return __popState;
    199        }
    200
    201
    202        /***
    203         * Retrieves the additional lines of a multi-line server reply.
    204         ***/
    205        public void getAdditionalReply() throws IOException
    206        {
    207            String line;
    208
    209            line = _reader.readLine();
    210            while (line != null)
    211            {
    212                _replyLines.addElement(line);
    213                if (line.equals("."))
    214                    break;
    215                line = _reader.readLine();
    216            }
    217        }
    218
    219
    220        /***
    221         * Disconnects the client from the server, and sets the state to
    222         * <code> DISCONNECTED_STATE </code>.  The reply text information
    223         * from the last issued command is voided to allow garbage collection
    224         * of the memory used to store that information.
    225         * <p>
    226         * @exception IOException  If there is an error in disconnecting.
    227         ***/
    228        @Override
    229        public void disconnect() throws IOException
    230        {
    231            super.disconnect();
    232            _reader = null;
    233            __writer = null;
    234            _lastReplyLine = null;
    235            _replyLines.setSize(0);
    236            setState(DISCONNECTED_STATE);
    237        }
    238
    239
    240        /***
    241         * Sends a command an arguments to the server and returns the reply code.
    242         * <p>
    243         * @param command  The POP3 command to send.
    244         * @param args     The command arguments.
    245         * @return  The server reply code (either POP3Reply.OK or POP3Reply.ERROR).
    246         ***/
    247        public int sendCommand(String command, String args) throws IOException
    248        {
    249            String message;
    250
    251            __commandBuffer.setLength(0);
    252            __commandBuffer.append(command);
    253
    254            if (args != null)
    255            {
    256                __commandBuffer.append(' ');
    257                __commandBuffer.append(args);
    258            }
    259            __commandBuffer.append(SocketClient.NETASCII_EOL);
    260
    261            __writer.write(message = __commandBuffer.toString());
    262            __writer.flush();
    263
    264            if (_commandSupport_.getListenerCount() > 0)
    265                _commandSupport_.fireCommandSent(command, message);
    266
    267            __getReply();
    268            return _replyCode;
    269        }
    270
    271        /***
    272         * Sends a command with no arguments to the server and returns the
    273         * reply code.
    274         * <p>
    275         * @param command  The POP3 command to send.
    276         * @return  The server reply code (either POP3Reply.OK or POP3Reply.ERROR).
    277         ***/
    278        public int sendCommand(String command) throws IOException
    279        {
    280            return sendCommand(command, null);
    281        }
    282
    283        /***
    284         * Sends a command an arguments to the server and returns the reply code.
    285         * <p>
    286         * @param command  The POP3 command to send
    287         *                  (one of the POP3Command constants).
    288         * @param args     The command arguments.
    289         * @return  The server reply code (either POP3Reply.OK or POP3Reply.ERROR).
    290         ***/
    291        public int sendCommand(int command, String args) throws IOException
    292        {
    293            return sendCommand(POP3Command._commands[command], args);
    294        }
    295
    296        /***
    297         * Sends a command with no arguments to the server and returns the
    298         * reply code.
    299         * <p>
    300         * @param command  The POP3 command to send
    301         *                  (one of the POP3Command constants).
    302         * @return  The server reply code (either POP3Reply.OK or POP3Reply.ERROR).
    303         ***/
    304        public int sendCommand(int command) throws IOException
    305        {
    306            return sendCommand(POP3Command._commands[command], null);
    307        }
    308
    309
    310        /***
    311         * Returns an array of lines received as a reply to the last command
    312         * sent to the server.  The lines have end of lines truncated.  If
    313         * the reply is a single line, but its format ndicates it should be
    314         * a multiline reply, then you must call
    315         * {@link #getAdditionalReply  getAdditionalReply() } to
    316         * fetch the rest of the reply, and then call <code>getReplyStrings</code>
    317         * again.  You only have to worry about this if you are implementing
    318         * your own client using the {@link #sendCommand  sendCommand } methods.
    319         * <p>
    320         * @return The last server response.
    321         ***/
    322        public String[] getReplyStrings()
    323        {
    324            String[] lines;
    325            lines = new String[_replyLines.size()];
    326            _replyLines.copyInto(lines);
    327            return lines;
    328        }
    329
    330        /***
    331         * Returns the reply to the last command sent to the server.
    332         * The value is a single string containing all the reply lines including
    333         * newlines.  If the reply is a single line, but its format ndicates it
    334         * should be a multiline reply, then you must call
    335         * {@link #getAdditionalReply  getAdditionalReply() } to
    336         * fetch the rest of the reply, and then call <code>getReplyString</code>
    337         * again.  You only have to worry about this if you are implementing
    338         * your own client using the {@link #sendCommand  sendCommand } methods.
    339         * <p>
    340         * @return The last server response.
    341         ***/
    342        public String getReplyString()
    343        {
    344            Enumeration<String> en;
    345            StringBuilder buffer = new StringBuilder(256);
    346
    347            en = _replyLines.elements();
    348            while (en.hasMoreElements())
    349            {
    350                buffer.append(en.nextElement());
    351                buffer.append(SocketClient.NETASCII_EOL);
    352            }
    353
    354            return buffer.toString();
    355        }
    356
    357    }
    358