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;
019
020import java.io.Closeable;
021import java.io.IOException;
022import java.io.InputStream;
023import java.io.OutputStream;
024import java.net.InetAddress;
025import java.net.InetSocketAddress;
026import java.net.Proxy;
027import java.net.Socket;
028import java.net.SocketException;
029import java.nio.charset.Charset;
030
031import javax.net.ServerSocketFactory;
032import javax.net.SocketFactory;
033
034
035/**
036 * The SocketClient provides the basic operations that are required of
037 * client objects accessing sockets.  It is meant to be
038 * subclassed to avoid having to rewrite the same code over and over again
039 * to open a socket, close a socket, set timeouts, etc.  Of special note
040 * is the {@link #setSocketFactory  setSocketFactory }
041 * method, which allows you to control the type of Socket the SocketClient
042 * creates for initiating network connections.  This is especially useful
043 * for adding SSL or proxy support as well as better support for applets.  For
044 * example, you could create a
045 * {@link javax.net.SocketFactory} that
046 * requests browser security capabilities before creating a socket.
047 * All classes derived from SocketClient should use the
048 * {@link #_socketFactory_  _socketFactory_ } member variable to
049 * create Socket and ServerSocket instances rather than instantiating
050 * them by directly invoking a constructor.  By honoring this contract
051 * you guarantee that a user will always be able to provide his own
052 * Socket implementations by substituting his own SocketFactory.
053 * @see SocketFactory
054 */
055public abstract class SocketClient
056{
057    /**
058     * The end of line character sequence used by most IETF protocols.  That
059     * is a carriage return followed by a newline: "\r\n"
060     */
061    public static final String NETASCII_EOL = "\r\n";
062
063    /** The default SocketFactory shared by all SocketClient instances. */
064    private static final SocketFactory DEFAULT_SOCKET_FACTORY =
065            SocketFactory.getDefault();
066
067    /** The default {@link ServerSocketFactory} */
068    private static final ServerSocketFactory DEFAULT_SERVER_SOCKET_FACTORY =
069            ServerSocketFactory.getDefault();
070
071    /**
072     * A ProtocolCommandSupport object used to manage the registering of
073     * ProtocolCommandListeners and the firing of ProtocolCommandEvents.
074     */
075    private ProtocolCommandSupport commandSupport;
076
077    /** The timeout to use after opening a socket. */
078    protected int _timeout_;
079
080    /** The socket used for the connection. */
081    protected Socket _socket_;
082
083    /** The hostname used for the connection (null = no hostname supplied). */
084    protected String _hostname_;
085
086    /** The default port the client should connect to. */
087    protected int _defaultPort_;
088
089    /** The socket's InputStream. */
090    protected InputStream _input_;
091
092    /** The socket's OutputStream. */
093    protected OutputStream _output_;
094
095    /** The socket's SocketFactory. */
096    protected SocketFactory _socketFactory_;
097
098    /** The socket's ServerSocket Factory. */
099    protected ServerSocketFactory _serverSocketFactory_;
100
101    /** The socket's connect timeout (0 = infinite timeout) */
102    private static final int DEFAULT_CONNECT_TIMEOUT = 0;
103    protected int connectTimeout = DEFAULT_CONNECT_TIMEOUT;
104
105    /** Hint for SO_RCVBUF size */
106    private int receiveBufferSize = -1;
107
108    /** Hint for SO_SNDBUF size */
109    private int sendBufferSize = -1;
110
111    /** The proxy to use when connecting. */
112    private Proxy connProxy;
113
114    /**
115     * Charset to use for byte IO.
116     */
117    private Charset charset = Charset.defaultCharset();
118
119    /**
120     * Default constructor for SocketClient.  Initializes
121     * _socket_ to null, _timeout_ to 0, _defaultPort to 0,
122     * _isConnected_ to false, charset to {@code Charset.defaultCharset()}
123     * and _socketFactory_ to a shared instance of
124     * {@link org.apache.commons.net.DefaultSocketFactory}.
125     */
126    public SocketClient()
127    {
128        _socket_ = null;
129        _hostname_ = null;
130        _input_ = null;
131        _output_ = null;
132        _timeout_ = 0;
133        _defaultPort_ = 0;
134        _socketFactory_ = DEFAULT_SOCKET_FACTORY;
135        _serverSocketFactory_ = DEFAULT_SERVER_SOCKET_FACTORY;
136    }
137
138
139    /**
140     * Because there are so many connect() methods, the _connectAction_()
141     * method is provided as a means of performing some action immediately
142     * after establishing a connection, rather than reimplementing all
143     * of the connect() methods.  The last action performed by every
144     * connect() method after opening a socket is to call this method.
145     * <p>
146     * This method sets the timeout on the just opened socket to the default
147     * timeout set by {@link #setDefaultTimeout  setDefaultTimeout() },
148     * sets _input_ and _output_ to the socket's InputStream and OutputStream
149     * respectively, and sets _isConnected_ to true.
150     * <p>
151     * Subclasses overriding this method should start by calling
152     * <code> super._connectAction_() </code> first to ensure the
153     * initialization of the aforementioned protected variables.
154     * @throws IOException (SocketException) if a problem occurs with the socket
155     */
156    protected void _connectAction_() throws IOException
157    {
158        _socket_.setSoTimeout(_timeout_);
159        _input_ = _socket_.getInputStream();
160        _output_ = _socket_.getOutputStream();
161    }
162
163
164    /**
165     * Opens a Socket connected to a remote host at the specified port and
166     * originating from the current host at a system assigned port.
167     * Before returning, {@link #_connectAction_  _connectAction_() }
168     * is called to perform connection initialization actions.
169     * <p>
170     * @param host  The remote host.
171     * @param port  The port to connect to on the remote host.
172     * @throws SocketException If the socket timeout could not be set.
173     * @throws IOException If the socket could not be opened.  In most
174     *  cases you will only want to catch IOException since SocketException is
175     *  derived from it.
176     */
177    public void connect(final InetAddress host, final int port)
178    throws SocketException, IOException
179    {
180        _hostname_ = null;
181        _connect(host, port, null, -1);
182    }
183
184    /**
185     * Opens a Socket connected to a remote host at the specified port and
186     * originating from the current host at a system assigned port.
187     * Before returning, {@link #_connectAction_  _connectAction_() }
188     * is called to perform connection initialization actions.
189     * <p>
190     * @param hostname  The name of the remote host.
191     * @param port  The port to connect to on the remote host.
192     * @throws SocketException If the socket timeout could not be set.
193     * @throws IOException If the socket could not be opened.  In most
194     *  cases you will only want to catch IOException since SocketException is
195     *  derived from it.
196     * @throws java.net.UnknownHostException If the hostname cannot be resolved.
197     */
198    public void connect(final String hostname, final int port)
199    throws SocketException, IOException
200    {
201        _hostname_ = hostname;
202        _connect(InetAddress.getByName(hostname), port, null, -1);
203    }
204
205
206    /**
207     * Opens a Socket connected to a remote host at the specified port and
208     * originating from the specified local address and port.
209     * Before returning, {@link #_connectAction_  _connectAction_() }
210     * is called to perform connection initialization actions.
211     * <p>
212     * @param host  The remote host.
213     * @param port  The port to connect to on the remote host.
214     * @param localAddr  The local address to use.
215     * @param localPort  The local port to use.
216     * @throws SocketException If the socket timeout could not be set.
217     * @throws IOException If the socket could not be opened.  In most
218     *  cases you will only want to catch IOException since SocketException is
219     *  derived from it.
220     */
221    public void connect(final InetAddress host, final int port,
222                        final InetAddress localAddr, final int localPort)
223    throws SocketException, IOException
224    {
225        _hostname_ = null;
226        _connect(host, port, localAddr, localPort);
227    }
228
229    // helper method to allow code to be shared with connect(String,...) methods
230    private void _connect(final InetAddress host, final int port, final InetAddress localAddr, final int localPort)
231        throws SocketException, IOException
232    {
233        _socket_ = _socketFactory_.createSocket();
234        if (receiveBufferSize != -1) {
235            _socket_.setReceiveBufferSize(receiveBufferSize);
236        }
237        if (sendBufferSize != -1) {
238            _socket_.setSendBufferSize(sendBufferSize);
239        }
240        if (localAddr != null) {
241            _socket_.bind(new InetSocketAddress(localAddr, localPort));
242        }
243        _socket_.connect(new InetSocketAddress(host, port), connectTimeout);
244        _connectAction_();
245    }
246
247    /**
248     * Opens a Socket connected to a remote host at the specified port and
249     * originating from the specified local address and port.
250     * Before returning, {@link #_connectAction_  _connectAction_() }
251     * is called to perform connection initialization actions.
252     * <p>
253     * @param hostname  The name of the remote host.
254     * @param port  The port to connect to on the remote host.
255     * @param localAddr  The local address to use.
256     * @param localPort  The local port to use.
257     * @throws SocketException If the socket timeout could not be set.
258     * @throws IOException If the socket could not be opened.  In most
259     *  cases you will only want to catch IOException since SocketException is
260     *  derived from it.
261     * @throws java.net.UnknownHostException If the hostname cannot be resolved.
262     */
263    public void connect(final String hostname, final int port,
264                        final InetAddress localAddr, final int localPort)
265    throws SocketException, IOException
266    {
267        _hostname_ = hostname;
268       _connect(InetAddress.getByName(hostname), port, localAddr, localPort);
269    }
270
271
272    /**
273     * Opens a Socket connected to a remote host at the current default port
274     * and originating from the current host at a system assigned port.
275     * Before returning, {@link #_connectAction_  _connectAction_() }
276     * is called to perform connection initialization actions.
277     * <p>
278     * @param host  The remote host.
279     * @throws SocketException If the socket timeout could not be set.
280     * @throws IOException If the socket could not be opened.  In most
281     *  cases you will only want to catch IOException since SocketException is
282     *  derived from it.
283     */
284    public void connect(final InetAddress host) throws SocketException, IOException
285    {
286        _hostname_ = null;
287        connect(host, _defaultPort_);
288    }
289
290
291    /**
292     * Opens a Socket connected to a remote host at the current default
293     * port and originating from the current host at a system assigned port.
294     * Before returning, {@link #_connectAction_  _connectAction_() }
295     * is called to perform connection initialization actions.
296     * <p>
297     * @param hostname  The name of the remote host.
298     * @throws SocketException If the socket timeout could not be set.
299     * @throws IOException If the socket could not be opened.  In most
300     *  cases you will only want to catch IOException since SocketException is
301     *  derived from it.
302     * @throws java.net.UnknownHostException If the hostname cannot be resolved.
303     */
304    public void connect(final String hostname) throws SocketException, IOException
305    {
306        connect(hostname, _defaultPort_);
307    }
308
309
310    /**
311     * Disconnects the socket connection.
312     * You should call this method after you've finished using the class
313     * instance and also before you call
314     * {@link #connect connect() }
315     * again.  _isConnected_ is set to false, _socket_ is set to null,
316     * _input_ is set to null, and _output_ is set to null.
317     * <p>
318     * @throws IOException  If there is an error closing the socket.
319     */
320    public void disconnect() throws IOException
321    {
322        closeQuietly(_socket_);
323        closeQuietly(_input_);
324        closeQuietly(_output_);
325        _socket_ = null;
326        _hostname_ = null;
327        _input_ = null;
328        _output_ = null;
329    }
330
331    private void closeQuietly(final Socket socket) {
332        if (socket != null){
333            try {
334                socket.close();
335            } catch (final IOException e) {
336                // Ignored
337            }
338        }
339    }
340
341    private void closeQuietly(final Closeable close){
342        if (close != null){
343            try {
344                close.close();
345            } catch (final IOException e) {
346                // Ignored
347            }
348        }
349    }
350    /**
351     * Returns true if the client is currently connected to a server.
352     * <p>
353     * Delegates to {@link Socket#isConnected()}
354     * @return True if the client is currently connected to a server,
355     *         false otherwise.
356     */
357    public boolean isConnected()
358    {
359        if (_socket_ == null) {
360            return false;
361        }
362
363        return _socket_.isConnected();
364    }
365
366    /**
367     * Make various checks on the socket to test if it is available for use.
368     * Note that the only sure test is to use it, but these checks may help
369     * in some cases.
370     * @see <a href="https://issues.apache.org/jira/browse/NET-350">NET-350</a>
371     * @return {@code true} if the socket appears to be available for use
372     * @since 3.0
373     */
374    @SuppressWarnings("resource")
375    public boolean isAvailable(){
376        if (isConnected()) {
377            try
378            {
379                if (_socket_.getInetAddress() == null) {
380                    return false;
381                }
382                if (_socket_.getPort() == 0) {
383                    return false;
384                }
385                if (_socket_.getRemoteSocketAddress() == null) {
386                    return false;
387                }
388                if (_socket_.isClosed()) {
389                    return false;
390                }
391                /* these aren't exact checks (a Socket can be half-open),
392                   but since we usually require two-way data transfer,
393                   we check these here too: */
394                if (_socket_.isInputShutdown()) {
395                    return false;
396                }
397                if (_socket_.isOutputShutdown()) {
398                    return false;
399                }
400                /* ignore the result, catch exceptions: */
401                // No need to close
402                _socket_.getInputStream();
403                // No need to close
404                _socket_.getOutputStream();
405            }
406            catch (final IOException ioex)
407            {
408                return false;
409            }
410            return true;
411        }
412        return false;
413    }
414
415    /**
416     * Sets the default port the SocketClient should connect to when a port
417     * is not specified.  The {@link #_defaultPort_  _defaultPort_ }
418     * variable stores this value.  If never set, the default port is equal
419     * to zero.
420     * <p>
421     * @param port  The default port to set.
422     */
423    public void setDefaultPort(final int port)
424    {
425        _defaultPort_ = port;
426    }
427
428    /**
429     * Returns the current value of the default port (stored in
430     * {@link #_defaultPort_  _defaultPort_ }).
431     * <p>
432     * @return The current value of the default port.
433     */
434    public int getDefaultPort()
435    {
436        return _defaultPort_;
437    }
438
439
440    /**
441     * Set the default timeout in milliseconds to use when opening a socket.
442     * This value is only used previous to a call to
443     * {@link #connect connect()}
444     * and should not be confused with {@link #setSoTimeout setSoTimeout()}
445     * which operates on an the currently opened socket.  _timeout_ contains
446     * the new timeout value.
447     * <p>
448     * @param timeout  The timeout in milliseconds to use for the socket
449     *                 connection.
450     */
451    public void setDefaultTimeout(final int timeout)
452    {
453        _timeout_ = timeout;
454    }
455
456
457    /**
458     * Returns the default timeout in milliseconds that is used when
459     * opening a socket.
460     * <p>
461     * @return The default timeout in milliseconds that is used when
462     *         opening a socket.
463     */
464    public int getDefaultTimeout()
465    {
466        return _timeout_;
467    }
468
469
470    /**
471     * Set the timeout in milliseconds of a currently open connection.
472     * Only call this method after a connection has been opened
473     * by {@link #connect connect()}.
474     * <p>
475     * To set the initial timeout, use {@link #setDefaultTimeout(int)} instead.
476     *
477     * @param timeout  The timeout in milliseconds to use for the currently
478     *                 open socket connection.
479     * @throws SocketException If the operation fails.
480     * @throws NullPointerException if the socket is not currently open
481     */
482    public void setSoTimeout(final int timeout) throws SocketException
483    {
484        _socket_.setSoTimeout(timeout);
485    }
486
487
488    /**
489     * Set the underlying socket send buffer size.
490     * <p>
491     * @param size The size of the buffer in bytes.
492     * @throws SocketException never thrown, but subclasses might want to do so
493     * @since 2.0
494     */
495    public void setSendBufferSize(final int size) throws SocketException {
496        sendBufferSize = size;
497    }
498
499    /**
500     * Get the current sendBuffer size
501     * @return the size, or -1 if not initialised
502     * @since 3.0
503     */
504    protected int getSendBufferSize(){
505        return sendBufferSize;
506    }
507
508    /**
509     * Sets the underlying socket receive buffer size.
510     * <p>
511     * @param size The size of the buffer in bytes.
512     * @throws SocketException never (but subclasses may wish to do so)
513     * @since 2.0
514     */
515    public void setReceiveBufferSize(final int size) throws SocketException  {
516        receiveBufferSize = size;
517    }
518
519    /**
520     * Get the current receivedBuffer size
521     * @return the size, or -1 if not initialised
522     * @since 3.0
523     */
524    protected int getReceiveBufferSize(){
525        return receiveBufferSize;
526    }
527
528    /**
529     * Returns the timeout in milliseconds of the currently opened socket.
530     * <p>
531     * @return The timeout in milliseconds of the currently opened socket.
532     * @throws SocketException If the operation fails.
533     * @throws NullPointerException if the socket is not currently open
534     */
535    public int getSoTimeout() throws SocketException
536    {
537        return _socket_.getSoTimeout();
538    }
539
540    /**
541     * Enables or disables the Nagle's algorithm (TCP_NODELAY) on the
542     * currently opened socket.
543     * <p>
544     * @param on  True if Nagle's algorithm is to be enabled, false if not.
545     * @throws SocketException If the operation fails.
546     * @throws NullPointerException if the socket is not currently open
547     */
548    public void setTcpNoDelay(final boolean on) throws SocketException
549    {
550        _socket_.setTcpNoDelay(on);
551    }
552
553
554    /**
555     * Returns true if Nagle's algorithm is enabled on the currently opened
556     * socket.
557     * <p>
558     * @return True if Nagle's algorithm is enabled on the currently opened
559     *        socket, false otherwise.
560     * @throws SocketException If the operation fails.
561     * @throws NullPointerException if the socket is not currently open
562     */
563    public boolean getTcpNoDelay() throws SocketException
564    {
565        return _socket_.getTcpNoDelay();
566    }
567
568    /**
569     * Sets the SO_KEEPALIVE flag on the currently opened socket.
570     *
571     * From the Javadocs, the default keepalive time is 2 hours (although this is
572     * implementation  dependent). It looks as though the Windows WSA sockets implementation
573     * allows a specific keepalive value to be set, although this seems not to be the case on
574     * other systems.
575     * @param  keepAlive If true, keepAlive is turned on
576     * @throws SocketException if there is a problem with the socket
577     * @throws NullPointerException if the socket is not currently open
578     * @since 2.2
579     */
580    public void setKeepAlive(final boolean keepAlive) throws SocketException {
581        _socket_.setKeepAlive(keepAlive);
582    }
583
584    /**
585     * Returns the current value of the SO_KEEPALIVE flag on the currently opened socket.
586     * Delegates to {@link Socket#getKeepAlive()}
587     * @return True if SO_KEEPALIVE is enabled.
588     * @throws SocketException if there is a problem with the socket
589     * @throws NullPointerException if the socket is not currently open
590     * @since 2.2
591     */
592    public boolean getKeepAlive() throws SocketException {
593        return _socket_.getKeepAlive();
594    }
595
596    /**
597     * Sets the SO_LINGER timeout on the currently opened socket.
598     * <p>
599     * @param on  True if linger is to be enabled, false if not.
600     * @param val The linger timeout (in hundredths of a second?)
601     * @throws SocketException If the operation fails.
602     * @throws NullPointerException if the socket is not currently open
603     */
604    public void setSoLinger(final boolean on, final int val) throws SocketException
605    {
606        _socket_.setSoLinger(on, val);
607    }
608
609
610    /**
611     * Returns the current SO_LINGER timeout of the currently opened socket.
612     * <p>
613     * @return The current SO_LINGER timeout.  If SO_LINGER is disabled returns
614     *         -1.
615     * @throws SocketException If the operation fails.
616     * @throws NullPointerException if the socket is not currently open
617     */
618    public int getSoLinger() throws SocketException
619    {
620        return _socket_.getSoLinger();
621    }
622
623
624    /**
625     * Returns the port number of the open socket on the local host used
626     * for the connection.
627     * Delegates to {@link Socket#getLocalPort()}
628     * <p>
629     * @return The port number of the open socket on the local host used
630     *         for the connection.
631     * @throws NullPointerException if the socket is not currently open
632     */
633    public int getLocalPort()
634    {
635        return _socket_.getLocalPort();
636    }
637
638
639    /**
640     * Returns the local address  to which the client's socket is bound.
641     * Delegates to {@link Socket#getLocalAddress()}
642     * <p>
643     * @return The local address to which the client's socket is bound.
644     * @throws NullPointerException if the socket is not currently open
645     */
646    public InetAddress getLocalAddress()
647    {
648        return _socket_.getLocalAddress();
649    }
650
651    /**
652     * Returns the port number of the remote host to which the client is
653     * connected.
654     * Delegates to {@link Socket#getPort()}
655     * <p>
656     * @return The port number of the remote host to which the client is
657     *         connected.
658     * @throws NullPointerException if the socket is not currently open
659     */
660    public int getRemotePort()
661    {
662        return _socket_.getPort();
663    }
664
665
666    /**
667     * @return The remote address to which the client is connected.
668     * Delegates to {@link Socket#getInetAddress()}
669     * @throws NullPointerException if the socket is not currently open
670     */
671    public InetAddress getRemoteAddress()
672    {
673        return _socket_.getInetAddress();
674    }
675
676
677    /**
678     * Verifies that the remote end of the given socket is connected to the
679     * the same host that the SocketClient is currently connected to.  This
680     * is useful for doing a quick security check when a client needs to
681     * accept a connection from a server, such as an FTP data connection or
682     * a BSD R command standard error stream.
683     * <p>
684     * @param socket the item to check against
685     * @return True if the remote hosts are the same, false if not.
686     */
687    public boolean verifyRemote(final Socket socket)
688    {
689        InetAddress host1, host2;
690
691        host1 = socket.getInetAddress();
692        host2 = getRemoteAddress();
693
694        return host1.equals(host2);
695    }
696
697
698    /**
699     * Sets the SocketFactory used by the SocketClient to open socket
700     * connections.  If the factory value is null, then a default
701     * factory is used (only do this to reset the factory after having
702     * previously altered it).
703     * Any proxy setting is discarded.
704     * <p>
705     * @param factory  The new SocketFactory the SocketClient should use.
706     */
707    public void setSocketFactory(final SocketFactory factory)
708    {
709        if (factory == null) {
710            _socketFactory_ = DEFAULT_SOCKET_FACTORY;
711        } else {
712            _socketFactory_ = factory;
713        }
714        // re-setting the socket factory makes the proxy setting useless,
715        // so set the field to null so that getProxy() doesn't return a
716        // Proxy that we're actually not using.
717        connProxy = null;
718    }
719
720    /**
721     * Sets the ServerSocketFactory used by the SocketClient to open ServerSocket
722     * connections.  If the factory value is null, then a default
723     * factory is used (only do this to reset the factory after having
724     * previously altered it).
725     * <p>
726     * @param factory  The new ServerSocketFactory the SocketClient should use.
727     * @since 2.0
728     */
729    public void setServerSocketFactory(final ServerSocketFactory factory) {
730        if (factory == null) {
731            _serverSocketFactory_ = DEFAULT_SERVER_SOCKET_FACTORY;
732        } else {
733            _serverSocketFactory_ = factory;
734        }
735    }
736
737    /**
738     * Sets the connection timeout in milliseconds, which will be passed to the {@link Socket} object's
739     * connect() method.
740     * @param connectTimeout The connection timeout to use (in ms)
741     * @since 2.0
742     */
743    public void setConnectTimeout(final int connectTimeout) {
744        this.connectTimeout = connectTimeout;
745    }
746
747    /**
748     * Get the underlying socket connection timeout.
749     * @return timeout (in ms)
750     * @since 2.0
751     */
752    public int getConnectTimeout() {
753        return connectTimeout;
754    }
755
756    /**
757     * Get the underlying {@link ServerSocketFactory}
758     * @return The server socket factory
759     * @since 2.2
760     */
761    public ServerSocketFactory getServerSocketFactory() {
762        return _serverSocketFactory_;
763    }
764
765
766    /**
767     * Adds a ProtocolCommandListener.
768     *
769     * @param listener  The ProtocolCommandListener to add.
770     * @since 3.0
771     */
772    public void addProtocolCommandListener(final ProtocolCommandListener listener) {
773        getCommandSupport().addProtocolCommandListener(listener);
774    }
775
776    /**
777     * Removes a ProtocolCommandListener.
778     *
779     * @param listener  The ProtocolCommandListener to remove.
780     * @since 3.0
781     */
782    public void removeProtocolCommandListener(final ProtocolCommandListener listener) {
783        getCommandSupport().removeProtocolCommandListener(listener);
784    }
785
786    /**
787     * If there are any listeners, send them the reply details.
788     *
789     * @param replyCode the code extracted from the reply
790     * @param reply the full reply text
791     * @since 3.0
792     */
793    protected void fireReplyReceived(final int replyCode, final String reply) {
794        if (getCommandSupport().getListenerCount() > 0) {
795            getCommandSupport().fireReplyReceived(replyCode, reply);
796        }
797    }
798
799    /**
800     * If there are any listeners, send them the command details.
801     *
802     * @param command the command name
803     * @param message the complete message, including command name
804     * @since 3.0
805     */
806    protected void fireCommandSent(final String command, final String message) {
807        if (getCommandSupport().getListenerCount() > 0) {
808            getCommandSupport().fireCommandSent(command, message);
809        }
810    }
811
812    /**
813     * Create the CommandSupport instance if required
814     */
815    protected void createCommandSupport(){
816        commandSupport = new ProtocolCommandSupport(this);
817    }
818
819    /**
820     * Subclasses can override this if they need to provide their own
821     * instance field for backwards compatibilty.
822     *
823     * @return the CommandSupport instance, may be {@code null}
824     * @since 3.0
825     */
826    protected ProtocolCommandSupport getCommandSupport() {
827        return commandSupport;
828    }
829
830    /**
831     * Sets the proxy for use with all the connections.
832     * The proxy is used for connections established after the
833     * call to this method.
834     *
835     * @param proxy the new proxy for connections.
836     * @since 3.2
837     */
838    public void setProxy(final Proxy proxy) {
839        setSocketFactory(new DefaultSocketFactory(proxy));
840        connProxy = proxy;
841    }
842
843    /**
844     * Gets the proxy for use with all the connections.
845     * @return the current proxy for connections.
846     */
847    public Proxy getProxy() {
848        return connProxy;
849    }
850
851    /**
852     * Gets the charset name.
853     *
854     * @return the charset.
855     * @since 3.3
856     * @deprecated Since the code now requires Java 1.6 as a mininmum
857     */
858    @Deprecated
859    public String getCharsetName() {
860        return charset.name();
861    }
862
863    /**
864     * Gets the charset.
865     *
866     * @return the charset.
867     * @since 3.3
868     */
869    public Charset getCharset() {
870        return charset;
871    }
872
873    /**
874     * Sets the charset.
875     *
876     * @param charset the charset.
877     * @since 3.3
878     */
879    public void setCharset(final Charset charset) {
880        this.charset = charset;
881    }
882
883    /*
884     *  N.B. Fields cannot be pulled up into a super-class without breaking binary compatibility,
885     *  so the abstract method is needed to pass the instance to the methods which were moved here.
886     */
887}
888
889