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