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