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 & 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 }