View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.net.telnet;
19  
20  import java.io.BufferedInputStream;
21  import java.io.BufferedOutputStream;
22  import java.io.IOException;
23  import java.io.OutputStream;
24  import java.time.Duration;
25  import java.util.Arrays;
26  
27  import org.apache.commons.net.SocketClient;
28  
29  class Telnet extends SocketClient {
30      static final boolean debug = /* true; */ false;
31  
32      static final boolean debugoptions = /* true; */ false;
33  
34      static final byte[] COMMAND_DO = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.DO };
35  
36      static final byte[] COMMAND_DONT = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.DONT };
37  
38      static final byte[] COMMAND_WILL = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.WILL };
39  
40      static final byte[] COMMAND_WONT = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.WONT };
41  
42      static final byte[] COMMAND_SB = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.SB };
43  
44      static final byte[] COMMAND_SE = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.SE };
45  
46      static final int WILL_MASK = 0x01;
47      static final int DO_MASK = 0x02;
48      static final int REQUESTED_WILL_MASK = 0x04;
49      static final int REQUESTED_DO_MASK = 0x08;
50  
51      /* public */
52      static final int DEFAULT_PORT = 23;
53  
54      /* TERMINAL-TYPE option (start) */
55      /**
56       * Terminal type option
57       */
58      protected static final int TERMINAL_TYPE = 24;
59      /**
60       * Send (for subnegotiation)
61       */
62      protected static final int TERMINAL_TYPE_SEND = 1;
63      /**
64       * Is (for subnegotiation)
65       */
66      protected static final int TERMINAL_TYPE_IS = 0;
67  
68      /**
69       * Is sequence (for subnegotiation)
70       */
71      static final byte[] COMMAND_IS = { (byte) TERMINAL_TYPE, (byte) TERMINAL_TYPE_IS };
72  
73      /* Code Section added for supporting AYT (start) */
74      /**
75       * AYT sequence
76       */
77      static final byte[] COMMAND_AYT = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.AYT };
78  
79      private final int[] doResponse;
80  
81      private final int[] willResponse;
82  
83      private final int[] options;
84  
85      /**
86       * Terminal type
87       */
88      private String terminalType;
89      /* TERMINAL-TYPE option (end) */
90  
91      /* open TelnetOptionHandler functionality (end) */
92  
93      /* open TelnetOptionHandler functionality (start) */
94      /**
95       * Array of option handlers
96       */
97      private final TelnetOptionHandler[] optionHandlers;
98  
99      /**
100      * monitor to wait for AYT
101      */
102     private final Object aytMonitor = new Object();
103 
104     /**
105      * flag for AYT
106      */
107     private volatile boolean aytFlag = true;
108     /* Code Section added for supporting AYT (end) */
109 
110     /**
111      * The stream on which to spy
112      */
113     private volatile OutputStream spyStream;
114 
115     /**
116      * The notification handler
117      */
118     private TelnetNotificationHandler notifhand;
119 
120     /**
121      * Empty Constructor
122      */
123     Telnet() {
124         setDefaultPort(DEFAULT_PORT);
125         doResponse = new int[TelnetOption.MAX_OPTION_VALUE + 1];
126         willResponse = new int[TelnetOption.MAX_OPTION_VALUE + 1];
127         options = new int[TelnetOption.MAX_OPTION_VALUE + 1];
128         optionHandlers = new TelnetOptionHandler[TelnetOption.MAX_OPTION_VALUE + 1];
129     }
130 
131     /* TERMINAL-TYPE option (start) */
132     /**
133      * This constructor lets you specify the terminal type.
134      *
135      * @param termtype - terminal type to be negotiated (ej. VT100)
136      */
137     Telnet(final String termtype) {
138         setDefaultPort(DEFAULT_PORT);
139         doResponse = new int[TelnetOption.MAX_OPTION_VALUE + 1];
140         willResponse = new int[TelnetOption.MAX_OPTION_VALUE + 1];
141         options = new int[TelnetOption.MAX_OPTION_VALUE + 1];
142         terminalType = termtype;
143         optionHandlers = new TelnetOptionHandler[TelnetOption.MAX_OPTION_VALUE + 1];
144     }
145     /* TERMINAL-TYPE option (end) */
146 
147     /**
148      * Called upon connection.
149      *
150      * @throws IOException - Exception in I/O.
151      */
152     @Override
153     protected void _connectAction_() throws IOException {
154         /* (start). BUGFIX: clean the option info for each connection */
155         for (int ii = 0; ii < TelnetOption.MAX_OPTION_VALUE + 1; ii++) {
156             doResponse[ii] = 0;
157             willResponse[ii] = 0;
158             options[ii] = 0;
159             if (optionHandlers[ii] != null) {
160                 optionHandlers[ii].setDo(false);
161                 optionHandlers[ii].setWill(false);
162             }
163         }
164         /* (end). BUGFIX: clean the option info for each connection */
165 
166         super._connectAction_();
167         _input_ = new BufferedInputStream(_input_);
168         _output_ = new BufferedOutputStream(_output_);
169 
170         /* open TelnetOptionHandler functionality (start) */
171         for (int ii = 0; ii < TelnetOption.MAX_OPTION_VALUE + 1; ii++) {
172             if (optionHandlers[ii] != null) {
173                 if (optionHandlers[ii].getInitLocal()) {
174                     requestWill(optionHandlers[ii].getOptionCode());
175                 }
176 
177                 if (optionHandlers[ii].getInitRemote()) {
178                     requestDo(optionHandlers[ii].getOptionCode());
179                 }
180             }
181         }
182         /* open TelnetOptionHandler functionality (end) */
183     }
184 
185     /* Code Section added for supporting spystreams (start) */
186     /**
187      * Registers an OutputStream for spying what's going on in the Telnet session.
188      *
189      * @param spystream - OutputStream on which session activity will be echoed.
190      */
191     void _registerSpyStream(final OutputStream spystream) {
192         spyStream = spystream;
193     }
194 
195     /* Code Section added for supporting AYT (start) */
196     /**
197      * Sends an {@code Are You There (AYT)} sequence and waits for the result.
198      *
199      * @param timeout - Time to wait for a response.
200      * @throws IOException              - Exception in I/O.
201      * @throws IllegalArgumentException - Illegal argument
202      * @throws InterruptedException     - Interrupted during wait.
203      * @return true if AYT received a response, false otherwise
204      **/
205     final boolean _sendAYT(final Duration timeout) throws IOException, IllegalArgumentException, InterruptedException {
206         boolean retValue = false;
207         synchronized (aytMonitor) {
208             synchronized (this) {
209                 aytFlag = false;
210                 _output_.write(COMMAND_AYT);
211                 _output_.flush();
212             }
213             aytMonitor.wait(timeout.toMillis());
214             if (!aytFlag) {
215                 aytFlag = true;
216             } else {
217                 retValue = true;
218             }
219         }
220 
221         return retValue;
222     }
223     /* Code Section added for supporting AYT (end) */
224 
225     /**
226      * Sends a command, automatically adds IAC prefix and flushes the output.
227      *
228      * @param cmd - command data to be sent
229      * @throws IOException - Exception in I/O.
230      * @since 3.0
231      */
232     final synchronized void _sendCommand(final byte cmd) throws IOException {
233         _output_.write(TelnetCommand.IAC);
234         _output_.write(cmd);
235         _output_.flush();
236     }
237 
238     /* open TelnetOptionHandler functionality (start) */
239     /**
240      * Manages subnegotiation for Terminal Type.
241      *
242      * @param subn - subnegotiation data to be sent
243      * @throws IOException - Exception in I/O.
244      **/
245     final synchronized void _sendSubnegotiation(final int[] subn) throws IOException {
246         if (debug) {
247             System.err.println("SEND SUBNEGOTIATION: ");
248             if (subn != null) {
249                 System.err.println(Arrays.toString(subn));
250             }
251         }
252         if (subn != null) {
253             _output_.write(COMMAND_SB);
254             // Note _output_ is buffered, so might as well simplify by writing single bytes
255             for (final int element : subn) {
256                 final byte b = (byte) element;
257                 if (b == (byte) TelnetCommand.IAC) { // cast is necessary because IAC is outside the signed byte range
258                     _output_.write(b); // double any IAC bytes
259                 }
260                 _output_.write(b);
261             }
262             _output_.write(COMMAND_SE);
263 
264             /* Code Section added for sending the negotiation ASAP (start) */
265             _output_.flush();
266             /* Code Section added for sending the negotiation ASAP (end) */
267         }
268     }
269     /* open TelnetOptionHandler functionality (end) */
270 
271     /**
272      * Stops spying this Telnet.
273      */
274     void _stopSpyStream() {
275         spyStream = null;
276     }
277 
278     /**
279      * Registers a new TelnetOptionHandler for this telnet to use.
280      *
281      * @param opthand - option handler to be registered.
282      * @throws InvalidTelnetOptionException - The option code is invalid.
283      * @throws IOException                  on error
284      **/
285     void addOptionHandler(final TelnetOptionHandler opthand) throws InvalidTelnetOptionException, IOException {
286         final int optcode = opthand.getOptionCode();
287         if (!TelnetOption.isValidOption(optcode)) {
288             throw new InvalidTelnetOptionException("Invalid Option Code", optcode);
289         }
290         if (optionHandlers[optcode] != null) {
291             throw new InvalidTelnetOptionException("Already registered option", optcode);
292         }
293         optionHandlers[optcode] = opthand;
294         if (isConnected()) {
295             if (opthand.getInitLocal()) {
296                 requestWill(optcode);
297             }
298 
299             if (opthand.getInitRemote()) {
300                 requestDo(optcode);
301             }
302         }
303     }
304 
305     /**
306      * Unregisters a TelnetOptionHandler.
307      *
308      * @param optcode - Code of the option to be unregistered.
309      * @throws InvalidTelnetOptionException - The option code is invalid.
310      * @throws IOException                  on error
311      **/
312     void deleteOptionHandler(final int optcode) throws InvalidTelnetOptionException, IOException {
313         if (!TelnetOption.isValidOption(optcode)) {
314             throw new InvalidTelnetOptionException("Invalid Option Code", optcode);
315         }
316         if (optionHandlers[optcode] == null) {
317             throw new InvalidTelnetOptionException("Unregistered option", optcode);
318         }
319         final TelnetOptionHandler opthand = optionHandlers[optcode];
320         optionHandlers[optcode] = null;
321 
322         if (opthand.getWill()) {
323             requestWont(optcode);
324         }
325 
326         if (opthand.getDo()) {
327             requestDont(optcode);
328         }
329     }
330     /* open TelnetOptionHandler functionality (end) */
331 
332     /* Code Section added for supporting AYT (start) */
333     /**
334      * Processes the response of an AYT
335      */
336     final synchronized void processAYTResponse() {
337         if (!aytFlag) {
338             synchronized (aytMonitor) {
339                 aytFlag = true;
340                 aytMonitor.notifyAll();
341             }
342         }
343     }
344     /* Code Section added for supporting AYT (end) */
345 
346     /**
347      * Processes a COMMAND.
348      *
349      * @param command - option code to be set.
350      **/
351     void processCommand(final int command) {
352         if (debugoptions) {
353             System.err.println("RECEIVED COMMAND: " + command);
354         }
355 
356         if (notifhand != null) {
357             notifhand.receivedNegotiation(TelnetNotificationHandler.RECEIVED_COMMAND, command);
358         }
359     }
360 
361     /**
362      * Processes a {@code DO} request.
363      *
364      * @param option - option code to be set.
365      * @throws IOException - Exception in I/O.
366      **/
367     void processDo(final int option) throws IOException {
368         if (debugoptions) {
369             System.err.println("RECEIVED DO: " + TelnetOption.getOption(option));
370         }
371 
372         if (notifhand != null) {
373             notifhand.receivedNegotiation(TelnetNotificationHandler.RECEIVED_DO, option);
374         }
375 
376         boolean acceptNewState = false;
377 
378         /* open TelnetOptionHandler functionality (start) */
379         if (optionHandlers[option] != null) {
380             acceptNewState = optionHandlers[option].getAcceptLocal();
381         } else if (option == TERMINAL_TYPE && terminalType != null && !terminalType.isEmpty()) {
382             acceptNewState = true;
383         }
384         /* TERMINAL-TYPE option (end) */
385         /* open TelnetOptionHandler functionality (start) */
386 
387         if (willResponse[option] > 0) {
388             --willResponse[option];
389             if (willResponse[option] > 0 && stateIsWill(option)) {
390                 --willResponse[option];
391             }
392         }
393 
394         if (willResponse[option] == 0) {
395             if (requestedWont(option)) {
396 
397                 switch (option) {
398 
399                 default:
400                     break;
401 
402                 }
403 
404                 if (acceptNewState) {
405                     setWantWill(option);
406                     sendWill(option);
407                 } else {
408                     ++willResponse[option];
409                     sendWont(option);
410                 }
411             } else {
412                 // Other end has acknowledged option.
413 
414                 switch (option) {
415 
416                 default:
417                     break;
418 
419                 }
420 
421             }
422         }
423 
424         setWill(option);
425     }
426 
427     /**
428      * Processes a {@code DONT} request.
429      *
430      * @param option - option code to be set.
431      * @throws IOException - Exception in I/O.
432      **/
433     void processDont(final int option) throws IOException {
434         if (debugoptions) {
435             System.err.println("RECEIVED DONT: " + TelnetOption.getOption(option));
436         }
437         if (notifhand != null) {
438             notifhand.receivedNegotiation(TelnetNotificationHandler.RECEIVED_DONT, option);
439         }
440         if (willResponse[option] > 0) {
441             --willResponse[option];
442             if (willResponse[option] > 0 && stateIsWont(option)) {
443                 --willResponse[option];
444             }
445         }
446 
447         if (willResponse[option] == 0 && requestedWill(option)) {
448 
449             switch (option) {
450 
451             default:
452                 break;
453 
454             }
455 
456             /* FIX for a BUG in the negotiation (start) */
457             if (stateIsWill(option) || requestedWill(option)) {
458                 sendWont(option);
459             }
460 
461             setWantWont(option);
462             /* FIX for a BUG in the negotiation (end) */
463         }
464 
465         setWont(option);
466     }
467 
468     /* TERMINAL-TYPE option (start) */
469     /**
470      * Processes a suboption negotiation.
471      *
472      * @param suboption       - subnegotiation data received
473      * @param suboptionLength - length of data received
474      * @throws IOException - Exception in I/O.
475      **/
476     void processSuboption(final int[] suboption, final int suboptionLength) throws IOException {
477         if (debug) {
478             System.err.println("PROCESS SUBOPTION.");
479         }
480 
481         /* open TelnetOptionHandler functionality (start) */
482         if (suboptionLength > 0) {
483             if (optionHandlers[suboption[0]] != null) {
484                 final int[] responseSuboption = optionHandlers[suboption[0]].answerSubnegotiation(suboption, suboptionLength);
485                 _sendSubnegotiation(responseSuboption);
486             } else if (suboptionLength > 1) {
487                 if (debug) {
488                     for (int ii = 0; ii < suboptionLength; ii++) {
489                         System.err.println("SUB[" + ii + "]: " + suboption[ii]);
490                     }
491                 }
492                 if (suboption[0] == TERMINAL_TYPE && suboption[1] == TERMINAL_TYPE_SEND) {
493                     sendTerminalType();
494                 }
495             }
496         }
497         /* open TelnetOptionHandler functionality (end) */
498     }
499 
500     /**
501      * Processes a {@code WILL} request.
502      *
503      * @param option - option code to be set.
504      * @throws IOException - Exception in I/O.
505      **/
506     void processWill(final int option) throws IOException {
507         if (debugoptions) {
508             System.err.println("RECEIVED WILL: " + TelnetOption.getOption(option));
509         }
510 
511         if (notifhand != null) {
512             notifhand.receivedNegotiation(TelnetNotificationHandler.RECEIVED_WILL, option);
513         }
514 
515         boolean acceptNewState = false;
516 
517         /* open TelnetOptionHandler functionality (start) */
518         if (optionHandlers[option] != null) {
519             acceptNewState = optionHandlers[option].getAcceptRemote();
520         }
521         /* open TelnetOptionHandler functionality (end) */
522 
523         if (doResponse[option] > 0) {
524             --doResponse[option];
525             if (doResponse[option] > 0 && stateIsDo(option)) {
526                 --doResponse[option];
527             }
528         }
529 
530         if (doResponse[option] == 0 && requestedDont(option)) {
531 
532             switch (option) {
533 
534             default:
535                 break;
536 
537             }
538 
539             if (acceptNewState) {
540                 setWantDo(option);
541                 sendDo(option);
542             } else {
543                 ++doResponse[option];
544                 sendDont(option);
545             }
546         }
547 
548         setDo(option);
549     }
550 
551     /**
552      * Processes a {@code WONT} request.
553      *
554      * @param option - option code to be set.
555      * @throws IOException - Exception in I/O.
556      **/
557     void processWont(final int option) throws IOException {
558         if (debugoptions) {
559             System.err.println("RECEIVED WONT: " + TelnetOption.getOption(option));
560         }
561 
562         if (notifhand != null) {
563             notifhand.receivedNegotiation(TelnetNotificationHandler.RECEIVED_WONT, option);
564         }
565 
566         if (doResponse[option] > 0) {
567             --doResponse[option];
568             if (doResponse[option] > 0 && stateIsDont(option)) {
569                 --doResponse[option];
570             }
571         }
572 
573         if (doResponse[option] == 0 && requestedDo(option)) {
574 
575             switch (option) {
576 
577             default:
578                 break;
579 
580             }
581 
582             /* FIX for a BUG in the negotiation (start) */
583             if (stateIsDo(option) || requestedDo(option)) {
584                 sendDont(option);
585             }
586 
587             setWantDont(option);
588             /* FIX for a BUG in the negotiation (end) */
589         }
590 
591         setDont(option);
592     }
593 
594     /**
595      * Registers a notification handler to which will be sent notifications of received telnet option negotiation commands.
596      *
597      * @param notifhand - TelnetNotificationHandler to be registered
598      */
599     public void registerNotifHandler(final TelnetNotificationHandler notifhand) {
600         this.notifhand = notifhand;
601     }
602 
603     /**
604      * Requests a DO.
605      *
606      * @param option - Option code.
607      * @throws IOException - Exception in I/O.
608      **/
609     final synchronized void requestDo(final int option) throws IOException {
610         if (doResponse[option] == 0 && stateIsDo(option) || requestedDo(option)) {
611             return;
612         }
613         setWantDo(option);
614         ++doResponse[option];
615         sendDo(option);
616     }
617 
618     /**
619      * Requests a {@code DONT}.
620      *
621      * @param option - Option code.
622      * @throws IOException - Exception in I/O.
623      **/
624     final synchronized void requestDont(final int option) throws IOException {
625         if (doResponse[option] == 0 && stateIsDont(option) || requestedDont(option)) {
626             return;
627         }
628         setWantDont(option);
629         ++doResponse[option];
630         sendDont(option);
631     }
632 
633     /**
634      * Looks for the state of the option.
635      *
636      * @return returns true if a {@code DO} has been requested.
637      *
638      * @param option - option code to be looked up.
639      */
640     boolean requestedDo(final int option) {
641         return (options[option] & REQUESTED_DO_MASK) != 0;
642     }
643 
644     /**
645      * Looks for the state of the option.
646      *
647      * @return returns true if a {@code DONT} has been requested
648      *
649      * @param option - option code to be looked up.
650      */
651     boolean requestedDont(final int option) {
652         return !requestedDo(option);
653     }
654 
655     /**
656      * Looks for the state of the option.
657      *
658      * @return returns true if a {@code WILL} has been requested
659      *
660      * @param option - option code to be looked up.
661      */
662     boolean requestedWill(final int option) {
663         return (options[option] & REQUESTED_WILL_MASK) != 0;
664     }
665 
666     /**
667      * Looks for the state of the option.
668      *
669      * @return returns true if a {@code WONT} has been requested
670      *
671      * @param option - option code to be looked up.
672      */
673     boolean requestedWont(final int option) {
674         return !requestedWill(option);
675     }
676 
677     /**
678      * Requests a {@code WILL}.
679      *
680      * @param option - Option code.
681      * @throws IOException - Exception in I/O.
682      **/
683     final synchronized void requestWill(final int option) throws IOException {
684         if (willResponse[option] == 0 && stateIsWill(option) || requestedWill(option)) {
685             return;
686         }
687         setWantWill(option);
688         ++doResponse[option];
689         sendWill(option);
690     }
691 
692     /* TERMINAL-TYPE option (end) */
693 
694     /**
695      * Requests a {@code WONT}.
696      *
697      * @param option - Option code.
698      * @throws IOException - Exception in I/O.
699      **/
700     final synchronized void requestWont(final int option) throws IOException {
701         if (willResponse[option] == 0 && stateIsWont(option) || requestedWont(option)) {
702             return;
703         }
704         setWantWont(option);
705         ++doResponse[option];
706         sendWont(option);
707     }
708 
709     /**
710      * Sends a byte.
711      *
712      * @param b - byte to send
713      * @throws IOException - Exception in I/O.
714      **/
715     final synchronized void sendByte(final int b) throws IOException {
716         _output_.write(b);
717 
718         /* Code Section added for supporting spystreams (start) */
719         spyWrite(b);
720         /* Code Section added for supporting spystreams (end) */
721 
722     }
723 
724     /**
725      * Sends a {@code DO}.
726      *
727      * @param option - Option code.
728      * @throws IOException - Exception in I/O.
729      **/
730     final synchronized void sendDo(final int option) throws IOException {
731         if (debug || debugoptions) {
732             System.err.println("DO: " + TelnetOption.getOption(option));
733         }
734         _output_.write(COMMAND_DO);
735         _output_.write(option);
736 
737         /* Code Section added for sending the negotiation ASAP (start) */
738         _output_.flush();
739         /* Code Section added for sending the negotiation ASAP (end) */
740     }
741 
742     /**
743      * Sends a {@code DONT}.
744      *
745      * @param option - Option code.
746      * @throws IOException - Exception in I/O.
747      **/
748     final synchronized void sendDont(final int option) throws IOException {
749         if (debug || debugoptions) {
750             System.err.println("DONT: " + TelnetOption.getOption(option));
751         }
752         _output_.write(COMMAND_DONT);
753         _output_.write(option);
754 
755         /* Code Section added for sending the negotiation ASAP (start) */
756         _output_.flush();
757         /* Code Section added for sending the negotiation ASAP (end) */
758     }
759 
760     /**
761      * Sends terminal type information.
762      *
763      * @throws IOException - Exception in I/O.
764      */
765     final synchronized void sendTerminalType() throws IOException {
766         if (debug) {
767             System.err.println("SEND TERMINAL-TYPE: " + terminalType);
768         }
769         if (terminalType != null) {
770             _output_.write(COMMAND_SB);
771             _output_.write(COMMAND_IS);
772             _output_.write(terminalType.getBytes(getCharset()));
773             _output_.write(COMMAND_SE);
774             _output_.flush();
775         }
776     }
777 
778     /**
779      * Sends a {@code WILL}.
780      *
781      * @param option - Option code.
782      * @throws IOException - Exception in I/O.
783      **/
784     final synchronized void sendWill(final int option) throws IOException {
785         if (debug || debugoptions) {
786             System.err.println("WILL: " + TelnetOption.getOption(option));
787         }
788         _output_.write(COMMAND_WILL);
789         _output_.write(option);
790 
791         /* Code Section added for sending the negotiation ASAP (start) */
792         _output_.flush();
793         /* Code Section added for sending the negotiation ASAP (end) */
794     }
795 
796     /**
797      * Sends a {@code WONT}.
798      *
799      * @param option - Option code.
800      * @throws IOException - Exception in I/O.
801      **/
802     final synchronized void sendWont(final int option) throws IOException {
803         if (debug || debugoptions) {
804             System.err.println("WONT: " + TelnetOption.getOption(option));
805         }
806         _output_.write(COMMAND_WONT);
807         _output_.write(option);
808 
809         /* Code Section added for sending the negotiation ASAP (start) */
810         _output_.flush();
811         /* Code Section added for sending the negotiation ASAP (end) */
812     }
813 
814     /**
815      * Sets the state of the option.
816      *
817      * @param option - option code to be set.
818      * @throws IOException
819      */
820     void setDo(final int option) throws IOException {
821         options[option] |= DO_MASK;
822 
823         /* open TelnetOptionHandler functionality (start) */
824         if (requestedDo(option) && optionHandlers[option] != null) {
825             optionHandlers[option].setDo(true);
826 
827             final int[] subneg = optionHandlers[option].startSubnegotiationRemote();
828 
829             if (subneg != null) {
830                 _sendSubnegotiation(subneg);
831             }
832         }
833         /* open TelnetOptionHandler functionality (end) */
834     }
835 
836     /**
837      * Sets the state of the option.
838      *
839      * @param option - option code to be set.
840      */
841     void setDont(final int option) {
842         options[option] &= ~DO_MASK;
843 
844         /* open TelnetOptionHandler functionality (start) */
845         if (optionHandlers[option] != null) {
846             optionHandlers[option].setDo(false);
847         }
848         /* open TelnetOptionHandler functionality (end) */
849     }
850 
851     /**
852      * Sets the state of the option.
853      *
854      * @param option - option code to be set.
855      */
856     void setWantDo(final int option) {
857         options[option] |= REQUESTED_DO_MASK;
858     }
859 
860     /**
861      * Sets the state of the option.
862      *
863      * @param option - option code to be set.
864      */
865     void setWantDont(final int option) {
866         options[option] &= ~REQUESTED_DO_MASK;
867     }
868 
869     /**
870      * Sets the state of the option.
871      *
872      * @param option - option code to be set.
873      */
874     void setWantWill(final int option) {
875         options[option] |= REQUESTED_WILL_MASK;
876     }
877 
878     /**
879      * Sets the state of the option.
880      *
881      * @param option - option code to be set.
882      */
883     void setWantWont(final int option) {
884         options[option] &= ~REQUESTED_WILL_MASK;
885     }
886 
887     /**
888      * Sets the state of the option.
889      *
890      * @param option - option code to be set.
891      * @throws IOException
892      */
893     void setWill(final int option) throws IOException {
894         options[option] |= WILL_MASK;
895 
896         /* open TelnetOptionHandler functionality (start) */
897         if (requestedWill(option) && optionHandlers[option] != null) {
898             optionHandlers[option].setWill(true);
899 
900             final int[] subneg = optionHandlers[option].startSubnegotiationLocal();
901 
902             if (subneg != null) {
903                 _sendSubnegotiation(subneg);
904             }
905         }
906         /* open TelnetOptionHandler functionality (end) */
907     }
908 
909     /* open TelnetOptionHandler functionality (start) */
910 
911     /**
912      * Sets the state of the option.
913      *
914      * @param option - option code to be set.
915      */
916     void setWont(final int option) {
917         options[option] &= ~WILL_MASK;
918 
919         /* open TelnetOptionHandler functionality (start) */
920         if (optionHandlers[option] != null) {
921             optionHandlers[option].setWill(false);
922         }
923         /* open TelnetOptionHandler functionality (end) */
924     }
925 
926     /**
927      * Sends a read char on the spy stream.
928      *
929      * @param ch - character read from the session
930      */
931     void spyRead(final int ch) {
932         final OutputStream spy = spyStream;
933         if (spy != null) {
934             try {
935                 if (ch != '\r') // never write '\r' on its own
936                 {
937                     if (ch == '\n') {
938                         spy.write('\r'); // add '\r' before '\n'
939                     }
940                     spy.write(ch); // write original character
941                     spy.flush();
942                 }
943             } catch (final IOException e) {
944                 spyStream = null;
945             }
946         }
947     }
948 
949     /**
950      * Sends a written char on the spy stream.
951      *
952      * @param ch - character written to the session
953      */
954     void spyWrite(final int ch) {
955         if (!(stateIsDo(TelnetOption.ECHO) && requestedDo(TelnetOption.ECHO))) {
956             final OutputStream spy = spyStream;
957             if (spy != null) {
958                 try {
959                     spy.write(ch);
960                     spy.flush();
961                 } catch (final IOException e) {
962                     spyStream = null;
963                 }
964             }
965         }
966     }
967     /* Code Section added for supporting spystreams (end) */
968 
969     /**
970      * Looks for the state of the option.
971      *
972      * @return returns true if a {@code DO} has been acknowledged.
973      *
974      * @param option - option code to be looked up.
975      */
976     boolean stateIsDo(final int option) {
977         return (options[option] & DO_MASK) != 0;
978     }
979 
980     /**
981      * Looks for the state of the option.
982      *
983      * @return returns true if a {@code DONT} has been acknowledged
984      *
985      * @param option - option code to be looked up.
986      */
987     boolean stateIsDont(final int option) {
988         return !stateIsDo(option);
989     }
990 
991     /**
992      * Looks for the state of the option.
993      *
994      * @return returns true if a {@code WILL} has been acknowledged
995      *
996      * @param option - option code to be looked up.
997      */
998     boolean stateIsWill(final int option) {
999         return (options[option] & WILL_MASK) != 0;
1000     }
1001 
1002     /**
1003      * Looks for the state of the option.
1004      *
1005      * @return returns true if a {@code WONT} has been acknowledged
1006      *
1007      * @param option - option code to be looked up.
1008      */
1009     boolean stateIsWont(final int option) {
1010         return !stateIsWill(option);
1011     }
1012 
1013     /**
1014      * Unregisters the current notification handler.
1015      */
1016     public void unregisterNotifHandler() {
1017         this.notifhand = null;
1018     }
1019 }