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     * @exception SocketException If the socket timeout could not be set.
173     * @exception 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        _socket_ = _socketFactory_.createSocket();
182        if (receiveBufferSize != -1) {
183            _socket_.setReceiveBufferSize(receiveBufferSize);
184        }
185        if (sendBufferSize != -1) {
186            _socket_.setSendBufferSize(sendBufferSize);
187        }
188        _socket_.connect(new InetSocketAddress(host, port), connectTimeout);
189        _connectAction_();
190    }
191
192    /**
193     * Opens a Socket connected to a remote host at the specified port and
194     * originating from the current host at a system assigned port.
195     * Before returning, {@link #_connectAction_  _connectAction_() }
196     * is called to perform connection initialization actions.
197     * <p>
198     * @param hostname  The name of the remote host.
199     * @param port  The port to connect to on the remote host.
200     * @exception SocketException If the socket timeout could not be set.
201     * @exception IOException If the socket could not be opened.  In most
202     *  cases you will only want to catch IOException since SocketException is
203     *  derived from it.
204     * @exception java.net.UnknownHostException If the hostname cannot be resolved.
205     */
206    public void connect(String hostname, int port)
207    throws SocketException, IOException
208    {
209        connect(InetAddress.getByName(hostname), port);
210        _hostname_ = hostname;
211    }
212
213
214    /**
215     * Opens a Socket connected to a remote host at the specified port and
216     * originating from the specified local address and port.
217     * Before returning, {@link #_connectAction_  _connectAction_() }
218     * is called to perform connection initialization actions.
219     * <p>
220     * @param host  The remote host.
221     * @param port  The port to connect to on the remote host.
222     * @param localAddr  The local address to use.
223     * @param localPort  The local port to use.
224     * @exception SocketException If the socket timeout could not be set.
225     * @exception IOException If the socket could not be opened.  In most
226     *  cases you will only want to catch IOException since SocketException is
227     *  derived from it.
228     */
229    public void connect(InetAddress host, int port,
230                        InetAddress localAddr, int localPort)
231    throws SocketException, IOException
232    {
233        _hostname_ = null;
234        _socket_ = _socketFactory_.createSocket();
235        if (receiveBufferSize != -1) {
236            _socket_.setReceiveBufferSize(receiveBufferSize);
237        }
238        if (sendBufferSize != -1) {
239            _socket_.setSendBufferSize(sendBufferSize);
240        }
241        _socket_.bind(new InetSocketAddress(localAddr, localPort));
242        _socket_.connect(new InetSocketAddress(host, port), connectTimeout);
243        _connectAction_();
244    }
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     * @exception SocketException If the socket timeout could not be set.
258     * @exception 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     * @exception 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       connect(InetAddress.getByName(hostname), port, localAddr, localPort);
268       _hostname_ = hostname;
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     * @exception SocketException If the socket timeout could not be set.
280     * @exception 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     * @exception SocketException If the socket timeout could not be set.
299     * @exception 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     * @exception 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        _hostname_ = hostname;
308    }
309
310
311    /**
312     * Disconnects the socket connection.
313     * You should call this method after you've finished using the class
314     * instance and also before you call
315     * {@link #connect connect() }
316     * again.  _isConnected_ is set to false, _socket_ is set to null,
317     * _input_ is set to null, and _output_ is set to null.
318     * <p>
319     * @exception IOException  If there is an error closing the socket.
320     */
321    public void disconnect() throws IOException
322    {
323        closeQuietly(_socket_);
324        closeQuietly(_input_);
325        closeQuietly(_output_);
326        _socket_ = null;
327        _hostname_ = null;
328        _input_ = null;
329        _output_ = null;
330    }
331
332    private void closeQuietly(Socket socket) {
333        if (socket != null){
334            try {
335                socket.close();
336            } catch (IOException e) {
337                // Ignored
338            }
339        }
340    }
341
342    private void closeQuietly(Closeable close){
343        if (close != null){
344            try {
345                close.close();
346            } catch (IOException e) {
347                // Ignored
348            }
349        }
350    }
351    /**
352     * Returns true if the client is currently connected to a server.
353     * <p>
354     * Delegates to {@link Socket#isConnected()}
355     * @return True if the client is currently connected to a server,
356     *         false otherwise.
357     */
358    public boolean isConnected()
359    {
360        if (_socket_ == null) {
361            return false;
362        }
363
364        return _socket_.isConnected();
365    }
366
367    /**
368     * Make various checks on the socket to test if it is available for use.
369     * Note that the only sure test is to use it, but these checks may help
370     * in some cases.
371     * @see <a href="https://issues.apache.org/jira/browse/NET-350">NET-350</a>
372     * @return {@code true} if the socket appears to be available for use
373     * @since 3.0
374     */
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                _socket_.getInputStream();
402                _socket_.getOutputStream();
403            }
404            catch (IOException ioex)
405            {
406                return false;
407            }
408            return true;
409        } else {
410            return false;
411        }
412    }
413
414    /**
415     * Sets the default port the SocketClient should connect to when a port
416     * is not specified.  The {@link #_defaultPort_  _defaultPort_ }
417     * variable stores this value.  If never set, the default port is equal
418     * to zero.
419     * <p>
420     * @param port  The default port to set.
421     */
422    public void setDefaultPort(int port)
423    {
424        _defaultPort_ = port;
425    }
426
427    /**
428     * Returns the current value of the default port (stored in
429     * {@link #_defaultPort_  _defaultPort_ }).
430     * <p>
431     * @return The current value of the default port.
432     */
433    public int getDefaultPort()
434    {
435        return _defaultPort_;
436    }
437
438
439    /**
440     * Set the default timeout in milliseconds to use when opening a socket.
441     * This value is only used previous to a call to
442     * {@link #connect connect()}
443     * and should not be confused with {@link #setSoTimeout setSoTimeout()}
444     * which operates on an the currently opened socket.  _timeout_ contains
445     * the new timeout value.
446     * <p>
447     * @param timeout  The timeout in milliseconds to use for the socket
448     *                 connection.
449     */
450    public void setDefaultTimeout(int timeout)
451    {
452        _timeout_ = timeout;
453    }
454
455
456    /**
457     * Returns the default timeout in milliseconds that is used when
458     * opening a socket.
459     * <p>
460     * @return The default timeout in milliseconds that is used when
461     *         opening a socket.
462     */
463    public int getDefaultTimeout()
464    {
465        return _timeout_;
466    }
467
468
469    /**
470     * Set the timeout in milliseconds of a currently open connection.
471     * Only call this method after a connection has been opened
472     * by {@link #connect connect()}.
473     * <p>
474     * To set the initial timeout, use {@link #setDefaultTimeout(int)} instead.
475     *
476     * @param timeout  The timeout in milliseconds to use for the currently
477     *                 open socket connection.
478     * @exception SocketException If the operation fails.
479     * @throws NullPointerException if the socket is not currently open
480     */
481    public void setSoTimeout(int timeout) throws SocketException
482    {
483        _socket_.setSoTimeout(timeout);
484    }
485
486
487    /**
488     * Set the underlying socket send buffer size.
489     * <p>
490     * @param size The size of the buffer in bytes.
491     * @throws SocketException never thrown, but subclasses might want to do so
492     * @since 2.0
493     */
494    public void setSendBufferSize(int size) throws SocketException {
495        sendBufferSize = size;
496    }
497
498    /**
499     * Get the current sendBuffer size
500     * @return the size, or -1 if not initialised
501     * @since 3.0
502     */
503    protected int getSendBufferSize(){
504        return sendBufferSize;
505    }
506
507    /**
508     * Sets the underlying socket receive buffer size.
509     * <p>
510     * @param size The size of the buffer in bytes.
511     * @throws SocketException never (but subclasses may wish to do so)
512     * @since 2.0
513     */
514    public void setReceiveBufferSize(int size) throws SocketException  {
515        receiveBufferSize = size;
516    }
517
518    /**
519     * Get the current receivedBuffer size
520     * @return the size, or -1 if not initialised
521     * @since 3.0
522     */
523    protected int getReceiveBufferSize(){
524        return receiveBufferSize;
525    }
526
527    /**
528     * Returns the timeout in milliseconds of the currently opened socket.
529     * <p>
530     * @return The timeout in milliseconds of the currently opened socket.
531     * @exception SocketException If the operation fails.
532     * @throws NullPointerException if the socket is not currently open
533     */
534    public int getSoTimeout() throws SocketException
535    {
536        return _socket_.getSoTimeout();
537    }
538
539    /**
540     * Enables or disables the Nagle's algorithm (TCP_NODELAY) on the
541     * currently opened socket.
542     * <p>
543     * @param on  True if Nagle's algorithm is to be enabled, false if not.
544     * @exception SocketException If the operation fails.
545     * @throws NullPointerException if the socket is not currently open
546     */
547    public void setTcpNoDelay(boolean on) throws SocketException
548    {
549        _socket_.setTcpNoDelay(on);
550    }
551
552
553    /**
554     * Returns true if Nagle's algorithm is enabled on the currently opened
555     * socket.
556     * <p>
557     * @return True if Nagle's algorithm is enabled on the currently opened
558     *        socket, false otherwise.
559     * @exception SocketException If the operation fails.
560     * @throws NullPointerException if the socket is not currently open
561     */
562    public boolean getTcpNoDelay() throws SocketException
563    {
564        return _socket_.getTcpNoDelay();
565    }
566
567    /**
568     * Sets the SO_KEEPALIVE flag on the currently opened socket.
569     *
570     * From the Javadocs, the default keepalive time is 2 hours (although this is
571     * implementation  dependent). It looks as though the Windows WSA sockets implementation
572     * allows a specific keepalive value to be set, although this seems not to be the case on
573     * other systems.
574     * @param  keepAlive If true, keepAlive is turned on
575     * @throws SocketException if there is a problem with the socket
576     * @throws NullPointerException if the socket is not currently open
577     * @since 2.2
578     */
579    public void setKeepAlive(boolean keepAlive) throws SocketException {
580        _socket_.setKeepAlive(keepAlive);
581    }
582
583    /**
584     * Returns the current value of the SO_KEEPALIVE flag on the currently opened socket.
585     * Delegates to {@link Socket#getKeepAlive()}
586     * @return True if SO_KEEPALIVE is enabled.
587     * @throws SocketException if there is a problem with the socket
588     * @throws NullPointerException if the socket is not currently open
589     * @since 2.2
590     */
591    public boolean getKeepAlive() throws SocketException {
592        return _socket_.getKeepAlive();
593    }
594
595    /**
596     * Sets the SO_LINGER timeout on the currently opened socket.
597     * <p>
598     * @param on  True if linger is to be enabled, false if not.
599     * @param val The linger timeout (in hundredths of a second?)
600     * @exception SocketException If the operation fails.
601     * @throws NullPointerException if the socket is not currently open
602     */
603    public void setSoLinger(boolean on, int val) throws SocketException
604    {
605        _socket_.setSoLinger(on, val);
606    }
607
608
609    /**
610     * Returns the current SO_LINGER timeout of the currently opened socket.
611     * <p>
612     * @return The current SO_LINGER timeout.  If SO_LINGER is disabled returns
613     *         -1.
614     * @exception SocketException If the operation fails.
615     * @throws NullPointerException if the socket is not currently open
616     */
617    public int getSoLinger() throws SocketException
618    {
619        return _socket_.getSoLinger();
620    }
621
622
623    /**
624     * Returns the port number of the open socket on the local host used
625     * for the connection.
626     * Delegates to {@link Socket#getLocalPort()}
627     * <p>
628     * @return The port number of the open socket on the local host used
629     *         for the connection.
630     * @throws NullPointerException if the socket is not currently open
631     */
632    public int getLocalPort()
633    {
634        return _socket_.getLocalPort();
635    }
636
637
638    /**
639     * Returns the local address  to which the client's socket is bound.
640     * Delegates to {@link Socket#getLocalAddress()}
641     * <p>
642     * @return The local address to which the client's socket is bound.
643     * @throws NullPointerException if the socket is not currently open
644     */
645    public InetAddress getLocalAddress()
646    {
647        return _socket_.getLocalAddress();
648    }
649
650    /**
651     * Returns the port number of the remote host to which the client is
652     * connected.
653     * Delegates to {@link Socket#getPort()}
654     * <p>
655     * @return The port number of the remote host to which the client is
656     *         connected.
657     * @throws NullPointerException if the socket is not currently open
658     */
659    public int getRemotePort()
660    {
661        return _socket_.getPort();
662    }
663
664
665    /**
666     * @return The remote address to which the client is connected.
667     * Delegates to {@link Socket#getInetAddress()}
668     * @throws NullPointerException if the socket is not currently open
669     */
670    public InetAddress getRemoteAddress()
671    {
672        return _socket_.getInetAddress();
673    }
674
675
676    /**
677     * Verifies that the remote end of the given socket is connected to the
678     * the same host that the SocketClient is currently connected to.  This
679     * is useful for doing a quick security check when a client needs to
680     * accept a connection from a server, such as an FTP data connection or
681     * a BSD R command standard error stream.
682     * <p>
683     * @param socket the item to check against
684     * @return True if the remote hosts are the same, false if not.
685     */
686    public boolean verifyRemote(Socket socket)
687    {
688        InetAddress host1, host2;
689
690        host1 = socket.getInetAddress();
691        host2 = getRemoteAddress();
692
693        return host1.equals(host2);
694    }
695
696
697    /**
698     * Sets the SocketFactory used by the SocketClient to open socket
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     * Any proxy setting is discarded.
703     * <p>
704     * @param factory  The new SocketFactory the SocketClient should use.
705     */
706    public void setSocketFactory(SocketFactory factory)
707    {
708        if (factory == null) {
709            _socketFactory_ = __DEFAULT_SOCKET_FACTORY;
710        } else {
711            _socketFactory_ = factory;
712        }
713        // re-setting the socket factory makes the proxy setting useless,
714        // so set the field to null so that getProxy() doesn't return a
715        // Proxy that we're actually not using.
716        connProxy = null;
717    }
718
719    /**
720     * Sets the ServerSocketFactory used by the SocketClient to open ServerSocket
721     * connections.  If the factory value is null, then a default
722     * factory is used (only do this to reset the factory after having
723     * previously altered it).
724     * <p>
725     * @param factory  The new ServerSocketFactory the SocketClient should use.
726     * @since 2.0
727     */
728    public void setServerSocketFactory(ServerSocketFactory factory) {
729        if (factory == null) {
730            _serverSocketFactory_ = __DEFAULT_SERVER_SOCKET_FACTORY;
731        } else {
732            _serverSocketFactory_ = factory;
733        }
734    }
735
736    /**
737     * Sets the connection timeout in milliseconds, which will be passed to the {@link Socket} object's
738     * connect() method.
739     * @param connectTimeout The connection timeout to use (in ms)
740     * @since 2.0
741     */
742    public void setConnectTimeout(int connectTimeout) {
743        this.connectTimeout = connectTimeout;
744    }
745
746    /**
747     * Get the underlying socket connection timeout.
748     * @return timeout (in ms)
749     * @since 2.0
750     */
751    public int getConnectTimeout() {
752        return connectTimeout;
753    }
754
755    /**
756     * Get the underlying {@link ServerSocketFactory}
757     * @return The server socket factory
758     * @since 2.2
759     */
760    public ServerSocketFactory getServerSocketFactory() {
761        return _serverSocketFactory_;
762    }
763
764
765    /**
766     * Adds a ProtocolCommandListener.
767     *
768     * @param listener  The ProtocolCommandListener to add.
769     * @since 3.0
770     */
771    public void addProtocolCommandListener(ProtocolCommandListener listener) {
772        getCommandSupport().addProtocolCommandListener(listener);
773    }
774
775    /**
776     * Removes a ProtocolCommandListener.
777     *
778     * @param listener  The ProtocolCommandListener to remove.
779     * @since 3.0
780     */
781    public void removeProtocolCommandListener(ProtocolCommandListener listener) {
782        getCommandSupport().removeProtocolCommandListener(listener);
783    }
784
785    /**
786     * If there are any listeners, send them the reply details.
787     *
788     * @param replyCode the code extracted from the reply
789     * @param reply the full reply text
790     * @since 3.0
791     */
792    protected void fireReplyReceived(int replyCode, String reply) {
793        if (getCommandSupport().getListenerCount() > 0) {
794            getCommandSupport().fireReplyReceived(replyCode, reply);
795        }
796    }
797
798    /**
799     * If there are any listeners, send them the command details.
800     *
801     * @param command the command name
802     * @param message the complete message, including command name
803     * @since 3.0
804     */
805    protected void fireCommandSent(String command, String message) {
806        if (getCommandSupport().getListenerCount() > 0) {
807            getCommandSupport().fireCommandSent(command, message);
808        }
809    }
810
811    /**
812     * Create the CommandSupport instance if required
813     */
814    protected void createCommandSupport(){
815        __commandSupport = new ProtocolCommandSupport(this);
816    }
817
818    /**
819     * Subclasses can override this if they need to provide their own
820     * instance field for backwards compatibilty.
821     *
822     * @return the CommandSupport instance, may be {@code null}
823     * @since 3.0
824     */
825    protected ProtocolCommandSupport getCommandSupport() {
826        return __commandSupport;
827    }
828
829    /**
830     * Sets the proxy for use with all the connections.
831     * The proxy is used for connections established after the
832     * call to this method.
833     *
834     * @param proxy the new proxy for connections.
835     * @since 3.2
836     */
837    public void setProxy(Proxy proxy) {
838        setSocketFactory(new DefaultSocketFactory(proxy));
839        connProxy = proxy;
840    }
841
842    /**
843     * Gets the proxy for use with all the connections.
844     * @return the current proxy for connections.
845     */
846    public Proxy getProxy() {
847        return connProxy;
848    }
849
850    /**
851     * Gets the charset name.
852     *
853     * @return the charset.
854     * @since 3.3
855     * TODO Will be deprecated once the code requires Java 1.6 as a mininmum
856     */
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