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