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(InetAddress host, 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(String hostname, 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(InetAddress host, int port,
222                        InetAddress localAddr, 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(InetAddress host, int port, InetAddress localAddr, 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(String hostname, int port,
264                        InetAddress localAddr, 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(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(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(Socket socket) {
332        if (socket != null){
333            try {
334                socket.close();
335            } catch (IOException e) {
336                // Ignored
337            }
338        }
339    }
340
341    private void closeQuietly(Closeable close){
342        if (close != null){
343            try {
344                close.close();
345            } catch (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    public boolean isAvailable(){
375        if (isConnected()) {
376            try
377            {
378                if (_socket_.getInetAddress() == null) {
379                    return false;
380                }
381                if (_socket_.getPort() == 0) {
382                    return false;
383                }
384                if (_socket_.getRemoteSocketAddress() == null) {
385                    return false;
386                }
387                if (_socket_.isClosed()) {
388                    return false;
389                }
390                /* these aren't exact checks (a Socket can be half-open),
391                   but since we usually require two-way data transfer,
392                   we check these here too: */
393                if (_socket_.isInputShutdown()) {
394                    return false;
395                }
396                if (_socket_.isOutputShutdown()) {
397                    return false;
398                }
399                /* ignore the result, catch exceptions: */
400                _socket_.getInputStream();
401                _socket_.getOutputStream();
402            }
403            catch (IOException ioex)
404            {
405                return false;
406            }
407            return true;
408        } else {
409            return false;
410        }
411    }
412
413    /**
414     * Sets the default port the SocketClient should connect to when a port
415     * is not specified.  The {@link #_defaultPort_  _defaultPort_ }
416     * variable stores this value.  If never set, the default port is equal
417     * to zero.
418     * <p>
419     * @param port  The default port to set.
420     */
421    public void setDefaultPort(int port)
422    {
423        _defaultPort_ = port;
424    }
425
426    /**
427     * Returns the current value of the default port (stored in
428     * {@link #_defaultPort_  _defaultPort_ }).
429     * <p>
430     * @return The current value of the default port.
431     */
432    public int getDefaultPort()
433    {
434        return _defaultPort_;
435    }
436
437
438    /**
439     * Set the default timeout in milliseconds to use when opening a socket.
440     * This value is only used previous to a call to
441     * {@link #connect connect()}
442     * and should not be confused with {@link #setSoTimeout setSoTimeout()}
443     * which operates on an the currently opened socket.  _timeout_ contains
444     * the new timeout value.
445     * <p>
446     * @param timeout  The timeout in milliseconds to use for the socket
447     *                 connection.
448     */
449    public void setDefaultTimeout(int timeout)
450    {
451        _timeout_ = timeout;
452    }
453
454
455    /**
456     * Returns the default timeout in milliseconds that is used when
457     * opening a socket.
458     * <p>
459     * @return The default timeout in milliseconds that is used when
460     *         opening a socket.
461     */
462    public int getDefaultTimeout()
463    {
464        return _timeout_;
465    }
466
467
468    /**
469     * Set the timeout in milliseconds of a currently open connection.
470     * Only call this method after a connection has been opened
471     * by {@link #connect connect()}.
472     * <p>
473     * To set the initial timeout, use {@link #setDefaultTimeout(int)} instead.
474     *
475     * @param timeout  The timeout in milliseconds to use for the currently
476     *                 open socket connection.
477     * @throws SocketException If the operation fails.
478     * @throws NullPointerException if the socket is not currently open
479     */
480    public void setSoTimeout(int timeout) throws SocketException
481    {
482        _socket_.setSoTimeout(timeout);
483    }
484
485
486    /**
487     * Set the underlying socket send buffer size.
488     * <p>
489     * @param size The size of the buffer in bytes.
490     * @throws SocketException never thrown, but subclasses might want to do so
491     * @since 2.0
492     */
493    public void setSendBufferSize(int size) throws SocketException {
494        sendBufferSize = size;
495    }
496
497    /**
498     * Get the current sendBuffer size
499     * @return the size, or -1 if not initialised
500     * @since 3.0
501     */
502    protected int getSendBufferSize(){
503        return sendBufferSize;
504    }
505
506    /**
507     * Sets the underlying socket receive buffer size.
508     * <p>
509     * @param size The size of the buffer in bytes.
510     * @throws SocketException never (but subclasses may wish to do so)
511     * @since 2.0
512     */
513    public void setReceiveBufferSize(int size) throws SocketException  {
514        receiveBufferSize = size;
515    }
516
517    /**
518     * Get the current receivedBuffer size
519     * @return the size, or -1 if not initialised
520     * @since 3.0
521     */
522    protected int getReceiveBufferSize(){
523        return receiveBufferSize;
524    }
525
526    /**
527     * Returns the timeout in milliseconds of the currently opened socket.
528     * <p>
529     * @return The timeout in milliseconds of the currently opened socket.
530     * @throws SocketException If the operation fails.
531     * @throws NullPointerException if the socket is not currently open
532     */
533    public int getSoTimeout() throws SocketException
534    {
535        return _socket_.getSoTimeout();
536    }
537
538    /**
539     * Enables or disables the Nagle's algorithm (TCP_NODELAY) on the
540     * currently opened socket.
541     * <p>
542     * @param on  True if Nagle's algorithm is to be enabled, false if not.
543     * @throws SocketException If the operation fails.
544     * @throws NullPointerException if the socket is not currently open
545     */
546    public void setTcpNoDelay(boolean on) throws SocketException
547    {
548        _socket_.setTcpNoDelay(on);
549    }
550
551
552    /**
553     * Returns true if Nagle's algorithm is enabled on the currently opened
554     * socket.
555     * <p>
556     * @return True if Nagle's algorithm is enabled on the currently opened
557     *        socket, false otherwise.
558     * @throws SocketException If the operation fails.
559     * @throws NullPointerException if the socket is not currently open
560     */
561    public boolean getTcpNoDelay() throws SocketException
562    {
563        return _socket_.getTcpNoDelay();
564    }
565
566    /**
567     * Sets the SO_KEEPALIVE flag on the currently opened socket.
568     *
569     * From the Javadocs, the default keepalive time is 2 hours (although this is
570     * implementation  dependent). It looks as though the Windows WSA sockets implementation
571     * allows a specific keepalive value to be set, although this seems not to be the case on
572     * other systems.
573     * @param  keepAlive If true, keepAlive is turned on
574     * @throws SocketException if there is a problem with the socket
575     * @throws NullPointerException if the socket is not currently open
576     * @since 2.2
577     */
578    public void setKeepAlive(boolean keepAlive) throws SocketException {
579        _socket_.setKeepAlive(keepAlive);
580    }
581
582    /**
583     * Returns the current value of the SO_KEEPALIVE flag on the currently opened socket.
584     * Delegates to {@link Socket#getKeepAlive()}
585     * @return True if SO_KEEPALIVE is enabled.
586     * @throws SocketException if there is a problem with the socket
587     * @throws NullPointerException if the socket is not currently open
588     * @since 2.2
589     */
590    public boolean getKeepAlive() throws SocketException {
591        return _socket_.getKeepAlive();
592    }
593
594    /**
595     * Sets the SO_LINGER timeout on the currently opened socket.
596     * <p>
597     * @param on  True if linger is to be enabled, false if not.
598     * @param val The linger timeout (in hundredths of a second?)
599     * @throws SocketException If the operation fails.
600     * @throws NullPointerException if the socket is not currently open
601     */
602    public void setSoLinger(boolean on, int val) throws SocketException
603    {
604        _socket_.setSoLinger(on, val);
605    }
606
607
608    /**
609     * Returns the current SO_LINGER timeout of the currently opened socket.
610     * <p>
611     * @return The current SO_LINGER timeout.  If SO_LINGER is disabled returns
612     *         -1.
613     * @throws SocketException If the operation fails.
614     * @throws NullPointerException if the socket is not currently open
615     */
616    public int getSoLinger() throws SocketException
617    {
618        return _socket_.getSoLinger();
619    }
620
621
622    /**
623     * Returns the port number of the open socket on the local host used
624     * for the connection.
625     * Delegates to {@link Socket#getLocalPort()}
626     * <p>
627     * @return The port number of the open socket on the local host used
628     *         for the connection.
629     * @throws NullPointerException if the socket is not currently open
630     */
631    public int getLocalPort()
632    {
633        return _socket_.getLocalPort();
634    }
635
636
637    /**
638     * Returns the local address  to which the client's socket is bound.
639     * Delegates to {@link Socket#getLocalAddress()}
640     * <p>
641     * @return The local address to which the client's socket is bound.
642     * @throws NullPointerException if the socket is not currently open
643     */
644    public InetAddress getLocalAddress()
645    {
646        return _socket_.getLocalAddress();
647    }
648
649    /**
650     * Returns the port number of the remote host to which the client is
651     * connected.
652     * Delegates to {@link Socket#getPort()}
653     * <p>
654     * @return The port number of the remote host to which the client is
655     *         connected.
656     * @throws NullPointerException if the socket is not currently open
657     */
658    public int getRemotePort()
659    {
660        return _socket_.getPort();
661    }
662
663
664    /**
665     * @return The remote address to which the client is connected.
666     * Delegates to {@link Socket#getInetAddress()}
667     * @throws NullPointerException if the socket is not currently open
668     */
669    public InetAddress getRemoteAddress()
670    {
671        return _socket_.getInetAddress();
672    }
673
674
675    /**
676     * Verifies that the remote end of the given socket is connected to the
677     * the same host that the SocketClient is currently connected to.  This
678     * is useful for doing a quick security check when a client needs to
679     * accept a connection from a server, such as an FTP data connection or
680     * a BSD R command standard error stream.
681     * <p>
682     * @param socket the item to check against
683     * @return True if the remote hosts are the same, false if not.
684     */
685    public boolean verifyRemote(Socket socket)
686    {
687        InetAddress host1, host2;
688
689        host1 = socket.getInetAddress();
690        host2 = getRemoteAddress();
691
692        return host1.equals(host2);
693    }
694
695
696    /**
697     * Sets the SocketFactory used by the SocketClient to open socket
698     * connections.  If the factory value is null, then a default
699     * factory is used (only do this to reset the factory after having
700     * previously altered it).
701     * Any proxy setting is discarded.
702     * <p>
703     * @param factory  The new SocketFactory the SocketClient should use.
704     */
705    public void setSocketFactory(SocketFactory factory)
706    {
707        if (factory == null) {
708            _socketFactory_ = __DEFAULT_SOCKET_FACTORY;
709        } else {
710            _socketFactory_ = factory;
711        }
712        // re-setting the socket factory makes the proxy setting useless,
713        // so set the field to null so that getProxy() doesn't return a
714        // Proxy that we're actually not using.
715        connProxy = null;
716    }
717
718    /**
719     * Sets the ServerSocketFactory used by the SocketClient to open ServerSocket
720     * connections.  If the factory value is null, then a default
721     * factory is used (only do this to reset the factory after having
722     * previously altered it).
723     * <p>
724     * @param factory  The new ServerSocketFactory the SocketClient should use.
725     * @since 2.0
726     */
727    public void setServerSocketFactory(ServerSocketFactory factory) {
728        if (factory == null) {
729            _serverSocketFactory_ = __DEFAULT_SERVER_SOCKET_FACTORY;
730        } else {
731            _serverSocketFactory_ = factory;
732        }
733    }
734
735    /**
736     * Sets the connection timeout in milliseconds, which will be passed to the {@link Socket} object's
737     * connect() method.
738     * @param connectTimeout The connection timeout to use (in ms)
739     * @since 2.0
740     */
741    public void setConnectTimeout(int connectTimeout) {
742        this.connectTimeout = connectTimeout;
743    }
744
745    /**
746     * Get the underlying socket connection timeout.
747     * @return timeout (in ms)
748     * @since 2.0
749     */
750    public int getConnectTimeout() {
751        return connectTimeout;
752    }
753
754    /**
755     * Get the underlying {@link ServerSocketFactory}
756     * @return The server socket factory
757     * @since 2.2
758     */
759    public ServerSocketFactory getServerSocketFactory() {
760        return _serverSocketFactory_;
761    }
762
763
764    /**
765     * Adds a ProtocolCommandListener.
766     *
767     * @param listener  The ProtocolCommandListener to add.
768     * @since 3.0
769     */
770    public void addProtocolCommandListener(ProtocolCommandListener listener) {
771        getCommandSupport().addProtocolCommandListener(listener);
772    }
773
774    /**
775     * Removes a ProtocolCommandListener.
776     *
777     * @param listener  The ProtocolCommandListener to remove.
778     * @since 3.0
779     */
780    public void removeProtocolCommandListener(ProtocolCommandListener listener) {
781        getCommandSupport().removeProtocolCommandListener(listener);
782    }
783
784    /**
785     * If there are any listeners, send them the reply details.
786     *
787     * @param replyCode the code extracted from the reply
788     * @param reply the full reply text
789     * @since 3.0
790     */
791    protected void fireReplyReceived(int replyCode, String reply) {
792        if (getCommandSupport().getListenerCount() > 0) {
793            getCommandSupport().fireReplyReceived(replyCode, reply);
794        }
795    }
796
797    /**
798     * If there are any listeners, send them the command details.
799     *
800     * @param command the command name
801     * @param message the complete message, including command name
802     * @since 3.0
803     */
804    protected void fireCommandSent(String command, String message) {
805        if (getCommandSupport().getListenerCount() > 0) {
806            getCommandSupport().fireCommandSent(command, message);
807        }
808    }
809
810    /**
811     * Create the CommandSupport instance if required
812     */
813    protected void createCommandSupport(){
814        __commandSupport = new ProtocolCommandSupport(this);
815    }
816
817    /**
818     * Subclasses can override this if they need to provide their own
819     * instance field for backwards compatibilty.
820     *
821     * @return the CommandSupport instance, may be {@code null}
822     * @since 3.0
823     */
824    protected ProtocolCommandSupport getCommandSupport() {
825        return __commandSupport;
826    }
827
828    /**
829     * Sets the proxy for use with all the connections.
830     * The proxy is used for connections established after the
831     * call to this method.
832     *
833     * @param proxy the new proxy for connections.
834     * @since 3.2
835     */
836    public void setProxy(Proxy proxy) {
837        setSocketFactory(new DefaultSocketFactory(proxy));
838        connProxy = proxy;
839    }
840
841    /**
842     * Gets the proxy for use with all the connections.
843     * @return the current proxy for connections.
844     */
845    public Proxy getProxy() {
846        return connProxy;
847    }
848
849    /**
850     * Gets the charset name.
851     *
852     * @return the charset.
853     * @since 3.3
854     * @deprecated Since the code now requires Java 1.6 as a mininmum
855     */
856    @Deprecated
857    public String getCharsetName() {
858        return charset.name();
859    }
860
861    /**
862     * Gets the charset.
863     *
864     * @return the charset.
865     * @since 3.3
866     */
867    public Charset getCharset() {
868        return charset;
869    }
870
871    /**
872     * Sets the charset.
873     *
874     * @param charset the charset.
875     * @since 3.3
876     */
877    public void setCharset(Charset charset) {
878        this.charset = charset;
879    }
880
881    /*
882     *  N.B. Fields cannot be pulled up into a super-class without breaking binary compatibility,
883     *  so the abstract method is needed to pass the instance to the methods which were moved here.
884     */
885}
886
887