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