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