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