    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.telnet;
    019
    020    import java.io.BufferedInputStream;
    021    import java.io.IOException;
    022    import java.io.InputStream;
    023    import java.io.OutputStream;
    024
    025    /***
    026     * The TelnetClient class implements the simple network virtual
    027     * terminal (NVT) for the Telnet protocol according to RFC 854.  It
    028     * does not implement any of the extra Telnet options because it
    029     * is meant to be used within a Java program providing automated
    030     * access to Telnet accessible resources.
    031     * <p>
    032     * The class can be used by first connecting to a server using the
    033     * SocketClient
    034     * {@link org.apache.commons.net.SocketClient#connect connect}
    035     * method.  Then an InputStream and OutputStream for sending and
    036     * receiving data over the Telnet connection can be obtained by
    037     * using the {@link #getInputStream  getInputStream() } and
    038     * {@link #getOutputStream  getOutputStream() } methods.
    039     * When you finish using the streams, you must call
    040     * {@link #disconnect  disconnect } rather than simply
    041     * closing the streams.
    042     * <p>
    043     * <p>
    044     * @author Bruno D'Avanzo
    045     ***/
    046
    047    public class TelnetClient extends Telnet
    048    {
    049        private InputStream __input;
    050        private OutputStream __output;
    051        protected boolean readerThread = true;
    052        private TelnetInputListener inputListener;
    053
    054        /***
    055         * Default TelnetClient constructor, sets terminal-type {@code VT100}.
    056         ***/
    057        public TelnetClient()
    058        {
    059            /* TERMINAL-TYPE option (start)*/
    060            super ("VT100");
    061            /* TERMINAL-TYPE option (end)*/
    062            __input = null;
    063            __output = null;
    064        }
    065
    066        /**
    067         * Construct an instance with the specified terminal type.
    068         *
    069         * @param termtype the terminal type to use, e.g. {@code VT100}
    070         */
    071        /* TERMINAL-TYPE option (start)*/
    072        public TelnetClient(String termtype)
    073        {
    074            super (termtype);
    075            __input = null;
    076            __output = null;
    077        }
    078        /* TERMINAL-TYPE option (end)*/
    079
    080        void _flushOutputStream() throws IOException
    081        {
    082            _output_.flush();
    083        }
    084        void _closeOutputStream() throws IOException
    085        {
    086            _output_.close();
    087        }
    088
    089        /***
    090         * Handles special connection requirements.
    091         * <p>
    092         * @exception IOException  If an error occurs during connection setup.
    093         ***/
    094        @Override
    095        protected void _connectAction_() throws IOException
    096        {
    097            super._connectAction_();
    098            TelnetInputStream tmp = new TelnetInputStream(_input_, this, readerThread);
    099            if(readerThread)
    100            {
    101                tmp._start();
    102            }
    103            // __input CANNOT refer to the TelnetInputStream.  We run into
    104            // blocking problems when some classes use TelnetInputStream, so
    105            // we wrap it with a BufferedInputStream which we know is safe.
    106            // This blocking behavior requires further investigation, but right
    107            // now it looks like classes like InputStreamReader are not implemented
    108            // in a safe manner.
    109            __input = new BufferedInputStream(tmp);
    110            __output = new TelnetOutputStream(this);
    111        }
    112
    113        /***
    114         * Disconnects the telnet session, closing the input and output streams
    115         * as well as the socket.  If you have references to the
    116         * input and output streams of the telnet connection, you should not
    117         * close them yourself, but rather call disconnect to properly close
    118         * the connection.
    119         ***/
    120        @Override
    121        public void disconnect() throws IOException
    122        {
    123            if (__input != null) {
    124                __input.close();
    125            }
    126            if (__output != null) {
    127                __output.close();
    128            }
    129            super.disconnect();
    130        }
    131
    132        /***
    133         * Returns the telnet connection output stream.  You should not close the
    134         * stream when you finish with it.  Rather, you should call
    135         * {@link #disconnect  disconnect }.
    136         * <p>
    137         * @return The telnet connection output stream.
    138         ***/
    139        public OutputStream getOutputStream()
    140        {
    141            return __output;
    142        }
    143
    144        /***
    145         * Returns the telnet connection input stream.  You should not close the
    146         * stream when you finish with it.  Rather, you should call
    147         * {@link #disconnect  disconnect }.
    148         * <p>
    149         * @return The telnet connection input stream.
    150         ***/
    151        public InputStream getInputStream()
    152        {
    153            return __input;
    154        }
    155
    156        /***
    157         * Returns the state of the option on the local side.
    158         * <p>
    159         * @param option - Option to be checked.
    160         * <p>
    161         * @return The state of the option on the local side.
    162         ***/
    163        public boolean getLocalOptionState(int option)
    164        {
    165            /* BUG (option active when not already acknowledged) (start)*/
    166            return (_stateIsWill(option) && _requestedWill(option));
    167            /* BUG (option active when not already acknowledged) (end)*/
    168        }
    169
    170        /***
    171         * Returns the state of the option on the remote side.
    172         * <p>
    173         * @param option - Option to be checked.
    174         * <p>
    175         * @return The state of the option on the remote side.
    176         ***/
    177        public boolean getRemoteOptionState(int option)
    178        {
    179            /* BUG (option active when not already acknowledged) (start)*/
    180            return (_stateIsDo(option) && _requestedDo(option));
    181            /* BUG (option active when not already acknowledged) (end)*/
    182        }
    183        /* open TelnetOptionHandler functionality (end)*/
    184
    185        /* Code Section added for supporting AYT (start)*/
    186
    187        /***
    188         * Sends an Are You There sequence and waits for the result.
    189         * <p>
    190         * @param timeout - Time to wait for a response (millis.)
    191         * <p>
    192         * @return true if AYT received a response, false otherwise
    193         * <p>
    194         * @throws InterruptedException
    195         * @throws IllegalArgumentException
    196         * @throws IOException
    197         ***/
    198        public boolean sendAYT(long timeout)
    199        throws IOException, IllegalArgumentException, InterruptedException
    200        {
    201            return (_sendAYT(timeout));
    202        }
    203        /* Code Section added for supporting AYT (start)*/
    204
    205        /***
    206         * Sends a protocol-specific subnegotiation message to the remote peer.
    207         * {@link TelnetClient} will add the IAC SB &amp; IAC SE framing bytes;
    208         * the first byte in {@code message} should be the appropriate telnet
    209         * option code.
    210         *
    211         * <p>
    212         * This method does not wait for any response. Subnegotiation messages
    213         * sent by the remote end can be handled by registering an approrpriate
    214         * {@link TelnetOptionHandler}.
    215         * </p>
    216         *
    217         * @param message option code followed by subnegotiation payload
    218         * @throws IllegalArgumentException if {@code message} has length zero
    219         * @throws IOException if an I/O error occurs while writing the message
    220         * @since 3.0
    221         ***/
    222        public void sendSubnegotiation(int[] message)
    223        throws IOException, IllegalArgumentException
    224        {
    225            if (message.length < 1) {
    226                throw new IllegalArgumentException("zero length message");
    227            }
    228            _sendSubnegotiation(message);
    229        }
    230
    231        /***
    232         * Sends a command byte to the remote peer, adding the IAC prefix.
    233         *
    234         * <p>
    235         * This method does not wait for any response. Messages
    236         * sent by the remote end can be handled by registering an approrpriate
    237         * {@link TelnetOptionHandler}.
    238         * </p>
    239         *
    240         * @param command the code for the command
    241         * @throws IOException if an I/O error occurs while writing the message
    242         * @since 3.0
    243         ***/
    244        public void sendCommand(byte command)
    245        throws IOException, IllegalArgumentException
    246        {
    247            _sendCommand(command);
    248        }
    249
    250        /* open TelnetOptionHandler functionality (start)*/
    251
    252        /***
    253         * Registers a new TelnetOptionHandler for this telnet client to use.
    254         * <p>
    255         * @param opthand - option handler to be registered.
    256         * <p>
    257         * @throws InvalidTelnetOptionException
    258         * @throws IOException
    259         ***/
    260        @Override
    261        public void addOptionHandler(TelnetOptionHandler opthand)
    262        throws InvalidTelnetOptionException, IOException
    263        {
    264            super.addOptionHandler(opthand);
    265        }
    266        /* open TelnetOptionHandler functionality (end)*/
    267
    268        /***
    269         * Unregisters a  TelnetOptionHandler.
    270         * <p>
    271         * @param optcode - Code of the option to be unregistered.
    272         * <p>
    273         * @throws InvalidTelnetOptionException
    274         * @throws IOException
    275         ***/
    276        @Override
    277        public void deleteOptionHandler(int optcode)
    278        throws InvalidTelnetOptionException, IOException
    279        {
    280            super.deleteOptionHandler(optcode);
    281        }
    282
    283        /* Code Section added for supporting spystreams (start)*/
    284        /***
    285         * Registers an OutputStream for spying what's going on in
    286         * the TelnetClient session.
    287         * <p>
    288         * @param spystream - OutputStream on which session activity
    289         * will be echoed.
    290         ***/
    291        public void registerSpyStream(OutputStream  spystream)
    292        {
    293            super._registerSpyStream(spystream);
    294        }
    295
    296        /***
    297         * Stops spying this TelnetClient.
    298         * <p>
    299         ***/
    300        public void stopSpyStream()
    301        {
    302            super._stopSpyStream();
    303        }
    304        /* Code Section added for supporting spystreams (end)*/
    305
    306        /***
    307         * Registers a notification handler to which will be sent
    308         * notifications of received telnet option negotiation commands.
    309         * <p>
    310         * @param notifhand - TelnetNotificationHandler to be registered
    311         ***/
    312        @Override
    313        public void registerNotifHandler(TelnetNotificationHandler  notifhand)
    314        {
    315            super.registerNotifHandler(notifhand);
    316        }
    317
    318        /***
    319         * Unregisters the current notification handler.
    320         * <p>
    321         ***/
    322        @Override
    323        public void unregisterNotifHandler()
    324        {
    325            super.unregisterNotifHandler();
    326        }
    327
    328        /***
    329         * Sets the status of the reader thread.
    330         *
    331         * <p>
    332         * When enabled, a seaparate internal reader thread is created for new
    333         * connections to read incoming data as it arrives. This results in
    334         * immediate handling of option negotiation, notifications, etc.
    335         * (at least until the fixed-size internal buffer fills up).
    336         * Otherwise, no thread is created an all negotiation and option
    337         * handling is deferred until a read() is performed on the
    338         * {@link #getInputStream input stream}.
    339         * </p>
    340         *
    341         * <p>
    342         * The reader thread must be enabled for {@link TelnetInputListener}
    343         * support.
    344         * </p>
    345         *
    346         * <p>
    347         * When this method is invoked, the reader thread status will apply to all
    348         * subsequent connections; the current connection (if any) is not affected.
    349         * </p>
    350         *
    351         * @param flag true to enable the reader thread, false to disable
    352         * @see #registerInputListener
    353         ***/
    354        public void setReaderThread(boolean flag)
    355        {
    356            readerThread = flag;
    357        }
    358
    359        /***
    360         * Gets the status of the reader thread.
    361         * <p>
    362         * @return true if the reader thread is enabled, false otherwise
    363         ***/
    364        public boolean getReaderThread()
    365        {
    366            return (readerThread);
    367        }
    368
    369        /***
    370         * Register a listener to be notified when new incoming data is
    371         * available to be read on the {@link #getInputStream input stream}.
    372         * Only one listener is supported at a time.
    373         *
    374         * <p>
    375         * More precisely, notifications are issued whenever the number of
    376         * bytes available for immediate reading (i.e., the value returned
    377         * by {@link InputStream#available}) transitions from zero to non-zero.
    378         * Note that (in general) multiple reads may be required to empty the
    379         * buffer and reset this notification, because incoming bytes are being
    380         * added to the internal buffer asynchronously.
    381         * </p>
    382         *
    383         * <p>
    384         * Notifications are only supported when a {@link #setReaderThread
    385         * reader thread} is enabled for the connection.
    386         * </p>
    387         *
    388         * @param listener listener to be registered; replaces any previous
    389         * @since 3.0
    390         ***/
    391        public synchronized void registerInputListener(TelnetInputListener listener)
    392        {
    393            this.inputListener = listener;
    394        }
    395
    396        /***
    397         * Unregisters the current {@link TelnetInputListener}, if any.
    398         *
    399         * @since 3.0
    400         ***/
    401        public synchronized void unregisterInputListener()
    402        {
    403            this.inputListener = null;
    404        }
    405
    406        // Notify input listener
    407        void notifyInputListener() {
    408            TelnetInputListener listener;
    409            synchronized (this) {
    410                listener = this.inputListener;
    411            }
    412            if (listener != null) {
    413                listener.telnetInputAvailable();
    414            }
    415        }
    416    }