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      */
275     void _stopSpyStream() {
276         spyStream = null;
277     }
278 
279     /**
280      * Registers a new TelnetOptionHandler for this telnet to use.
281      *
282      * @param opthand - option handler to be registered.
283      * @throws InvalidTelnetOptionException - The option code is invalid.
284      * @throws IOException                  on error
285      **/
286     void addOptionHandler(final TelnetOptionHandler opthand) throws InvalidTelnetOptionException, IOException {
287         final int optcode = opthand.getOptionCode();
288         if (!TelnetOption.isValidOption(optcode)) {
289             throw new InvalidTelnetOptionException("Invalid Option Code", optcode);
290         }
291         if (optionHandlers[optcode] != null) {
292             throw new InvalidTelnetOptionException("Already registered option", optcode);
293         }
294         optionHandlers[optcode] = opthand;
295         if (isConnected()) {
296             if (opthand.getInitLocal()) {
297                 requestWill(optcode);
298             }
299 
300             if (opthand.getInitRemote()) {
301                 requestDo(optcode);
302             }
303         }
304     }
305 
306     /**
307      * Unregisters a TelnetOptionHandler.
308      *
309      * @param optcode - Code of the option to be unregistered.
310      * @throws InvalidTelnetOptionException - The option code is invalid.
311      * @throws IOException                  on error
312      **/
313     void deleteOptionHandler(final int optcode) throws InvalidTelnetOptionException, IOException {
314         if (!TelnetOption.isValidOption(optcode)) {
315             throw new InvalidTelnetOptionException("Invalid Option Code", optcode);
316         }
317         if (optionHandlers[optcode] == null) {
318             throw new InvalidTelnetOptionException("Unregistered option", optcode);
319         }
320         final TelnetOptionHandler opthand = optionHandlers[optcode];
321         optionHandlers[optcode] = null;
322 
323         if (opthand.getWill()) {
324             requestWont(optcode);
325         }
326 
327         if (opthand.getDo()) {
328             requestDont(optcode);
329         }
330     }
331     /* open TelnetOptionHandler functionality (end) */
332 
333     /* Code Section added for supporting AYT (start) */
334     /**
335      * Processes the response of an AYT
336      */
337     final synchronized void processAYTResponse() {
338         if (!aytFlag) {
339             synchronized (aytMonitor) {
340                 aytFlag = true;
341                 aytMonitor.notifyAll();
342             }
343         }
344     }
345     /* Code Section added for supporting AYT (end) */
346 
347     /**
348      * Processes a COMMAND.
349      *
350      * @param command - option code to be set.
351      **/
352     void processCommand(final int command) {
353         if (debugoptions) {
354             System.err.println("RECEIVED COMMAND: " + command);
355         }
356 
357         if (notifhand != null) {
358             notifhand.receivedNegotiation(TelnetNotificationHandler.RECEIVED_COMMAND, command);
359         }
360     }
361 
362     /**
363      * Processes a {@code DO} request.
364      *
365      * @param option - option code to be set.
366      * @throws IOException - Exception in I/O.
367      **/
368     void processDo(final int option) throws IOException {
369         if (debugoptions) {
370             System.err.println("RECEIVED DO: " + TelnetOption.getOption(option));
371         }
372 
373         if (notifhand != null) {
374             notifhand.receivedNegotiation(TelnetNotificationHandler.RECEIVED_DO, option);
375         }
376 
377         boolean acceptNewState = false;
378 
379         /* open TelnetOptionHandler functionality (start) */
380         if (optionHandlers[option] != null) {
381             acceptNewState = optionHandlers[option].getAcceptLocal();
382         } else if (option == TERMINAL_TYPE && terminalType != null && !terminalType.isEmpty()) {
383             acceptNewState = true;
384         }
385         /* TERMINAL-TYPE option (end) */
386         /* open TelnetOptionHandler functionality (start) */
387 
388         if (willResponse[option] > 0) {
389             --willResponse[option];
390             if (willResponse[option] > 0 && stateIsWill(option)) {
391                 --willResponse[option];
392             }
393         }
394 
395         if (willResponse[option] == 0) {
396             if (requestedWont(option)) {
397 
398                 switch (option) {
399 
400                 default:
401                     break;
402 
403                 }
404 
405                 if (acceptNewState) {
406                     setWantWill(option);
407                     sendWill(option);
408                 } else {
409                     ++willResponse[option];
410                     sendWont(option);
411                 }
412             } else {
413                 // Other end has acknowledged option.
414 
415                 switch (option) {
416 
417                 default:
418                     break;
419 
420                 }
421 
422             }
423         }
424 
425         setWill(option);
426     }
427 
428     /**
429      * Processes a {@code DONT} request.
430      *
431      * @param option - option code to be set.
432      * @throws IOException - Exception in I/O.
433      **/
434     void processDont(final int option) throws IOException {
435         if (debugoptions) {
436             System.err.println("RECEIVED DONT: " + TelnetOption.getOption(option));
437         }
438         if (notifhand != null) {
439             notifhand.receivedNegotiation(TelnetNotificationHandler.RECEIVED_DONT, option);
440         }
441         if (willResponse[option] > 0) {
442             --willResponse[option];
443             if (willResponse[option] > 0 && stateIsWont(option)) {
444                 --willResponse[option];
445             }
446         }
447 
448         if (willResponse[option] == 0 && requestedWill(option)) {
449 
450             switch (option) {
451 
452             default:
453                 break;
454 
455             }
456 
457             /* FIX for a BUG in the negotiation (start) */
458             if (stateIsWill(option) || requestedWill(option)) {
459                 sendWont(option);
460             }
461 
462             setWantWont(option);
463             /* FIX for a BUG in the negotiation (end) */
464         }
465 
466         setWont(option);
467     }
468 
469     /* TERMINAL-TYPE option (start) */
470     /**
471      * Processes a suboption negotiation.
472      *
473      * @param suboption       - subnegotiation data received
474      * @param suboptionLength - length of data received
475      * @throws IOException - Exception in I/O.
476      **/
477     void processSuboption(final int[] suboption, final int suboptionLength) throws IOException {
478         if (debug) {
479             System.err.println("PROCESS SUBOPTION.");
480         }
481 
482         /* open TelnetOptionHandler functionality (start) */
483         if (suboptionLength > 0) {
484             if (optionHandlers[suboption[0]] != null) {
485                 final int[] responseSuboption = optionHandlers[suboption[0]].answerSubnegotiation(suboption, suboptionLength);
486                 _sendSubnegotiation(responseSuboption);
487             } else if (suboptionLength > 1) {
488                 if (debug) {
489                     for (int ii = 0; ii < suboptionLength; ii++) {
490                         System.err.println("SUB[" + ii + "]: " + suboption[ii]);
491                     }
492                 }
493                 if (suboption[0] == TERMINAL_TYPE && suboption[1] == TERMINAL_TYPE_SEND) {
494                     sendTerminalType();
495                 }
496             }
497         }
498         /* open TelnetOptionHandler functionality (end) */
499     }
500 
501     /**
502      * Processes a {@code WILL} request.
503      *
504      * @param option - option code to be set.
505      * @throws IOException - Exception in I/O.
506      **/
507     void processWill(final int option) throws IOException {
508         if (debugoptions) {
509             System.err.println("RECEIVED WILL: " + TelnetOption.getOption(option));
510         }
511 
512         if (notifhand != null) {
513             notifhand.receivedNegotiation(TelnetNotificationHandler.RECEIVED_WILL, option);
514         }
515 
516         boolean acceptNewState = false;
517 
518         /* open TelnetOptionHandler functionality (start) */
519         if (optionHandlers[option] != null) {
520             acceptNewState = optionHandlers[option].getAcceptRemote();
521         }
522         /* open TelnetOptionHandler functionality (end) */
523 
524         if (doResponse[option] > 0) {
525             --doResponse[option];
526             if (doResponse[option] > 0 && stateIsDo(option)) {
527                 --doResponse[option];
528             }
529         }
530 
531         if (doResponse[option] == 0 && requestedDont(option)) {
532 
533             switch (option) {
534 
535             default:
536                 break;
537 
538             }
539 
540             if (acceptNewState) {
541                 setWantDo(option);
542                 sendDo(option);
543             } else {
544                 ++doResponse[option];
545                 sendDont(option);
546             }
547         }
548 
549         setDo(option);
550     }
551 
552     /**
553      * Processes a {@code WONT} request.
554      *
555      * @param option - option code to be set.
556      * @throws IOException - Exception in I/O.
557      **/
558     void processWont(final int option) throws IOException {
559         if (debugoptions) {
560             System.err.println("RECEIVED WONT: " + TelnetOption.getOption(option));
561         }
562 
563         if (notifhand != null) {
564             notifhand.receivedNegotiation(TelnetNotificationHandler.RECEIVED_WONT, option);
565         }
566 
567         if (doResponse[option] > 0) {
568             --doResponse[option];
569             if (doResponse[option] > 0 && stateIsDont(option)) {
570                 --doResponse[option];
571             }
572         }
573 
574         if (doResponse[option] == 0 && requestedDo(option)) {
575 
576             switch (option) {
577 
578             default:
579                 break;
580 
581             }
582 
583             /* FIX for a BUG in the negotiation (start) */
584             if (stateIsDo(option) || requestedDo(option)) {
585                 sendDont(option);
586             }
587 
588             setWantDont(option);
589             /* FIX for a BUG in the negotiation (end) */
590         }
591 
592         setDont(option);
593     }
594 
595     /**
596      * Registers a notification handler to which will be sent notifications of received telnet option negotiation commands.
597      *
598      * @param notifhand - TelnetNotificationHandler to be registered
599      */
600     public void registerNotifHandler(final TelnetNotificationHandler notifhand) {
601         this.notifhand = notifhand;
602     }
603 
604     /**
605      * Requests a DO.
606      *
607      * @param option - Option code.
608      * @throws IOException - Exception in I/O.
609      **/
610     final synchronized void requestDo(final int option) throws IOException {
611         if (doResponse[option] == 0 && stateIsDo(option) || requestedDo(option)) {
612             return;
613         }
614         setWantDo(option);
615         ++doResponse[option];
616         sendDo(option);
617     }
618 
619     /**
620      * Requests a {@code DONT}.
621      *
622      * @param option - Option code.
623      * @throws IOException - Exception in I/O.
624      **/
625     final synchronized void requestDont(final int option) throws IOException {
626         if (doResponse[option] == 0 && stateIsDont(option) || requestedDont(option)) {
627             return;
628         }
629         setWantDont(option);
630         ++doResponse[option];
631         sendDont(option);
632     }
633 
634     /**
635      * Looks for the state of the option.
636      *
637      * @return returns true if a {@code DO} has been requested.
638      *
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 returns true if a {@code DONT} has been requested
649      *
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 returns true if a {@code WILL} has been requested
660      *
661      * @param option - option code to be looked up.
662      */
663     boolean requestedWill(final int option) {
664         return (options[option] & REQUESTED_WILL_MASK) != 0;
665     }
666 
667     /**
668      * Looks for the state of the option.
669      *
670      * @return returns true if a {@code WONT} has been requested
671      *
672      * @param option - option code to be looked up.
673      */
674     boolean requestedWont(final int option) {
675         return !requestedWill(option);
676     }
677 
678     /**
679      * Requests a {@code WILL}.
680      *
681      * @param option - Option code.
682      * @throws IOException - Exception in I/O.
683      **/
684     final synchronized void requestWill(final int option) throws IOException {
685         if (willResponse[option] == 0 && stateIsWill(option) || requestedWill(option)) {
686             return;
687         }
688         setWantWill(option);
689         ++doResponse[option];
690         sendWill(option);
691     }
692 
693     /* TERMINAL-TYPE option (end) */
694 
695     /**
696      * Requests a {@code WONT}.
697      *
698      * @param option - Option code.
699      * @throws IOException - Exception in I/O.
700      **/
701     final synchronized void requestWont(final int option) throws IOException {
702         if (willResponse[option] == 0 && stateIsWont(option) || requestedWont(option)) {
703             return;
704         }
705         setWantWont(option);
706         ++doResponse[option];
707         sendWont(option);
708     }
709 
710     /**
711      * Sends a byte.
712      *
713      * @param b - byte to send
714      * @throws IOException - Exception in I/O.
715      **/
716     final synchronized void sendByte(final int b) throws IOException {
717         _output_.write(b);
718 
719         /* Code Section added for supporting spystreams (start) */
720         spyWrite(b);
721         /* Code Section added for supporting spystreams (end) */
722 
723     }
724 
725     /**
726      * Sends a {@code DO}.
727      *
728      * @param option - Option code.
729      * @throws IOException - Exception in I/O.
730      **/
731     final synchronized void sendDo(final int option) throws IOException {
732         if (debug || debugoptions) {
733             System.err.println("DO: " + TelnetOption.getOption(option));
734         }
735         _output_.write(COMMAND_DO);
736         _output_.write(option);
737 
738         /* Code Section added for sending the negotiation ASAP (start) */
739         _output_.flush();
740         /* Code Section added for sending the negotiation ASAP (end) */
741     }
742 
743     /**
744      * Sends a {@code DONT}.
745      *
746      * @param option - Option code.
747      * @throws IOException - Exception in I/O.
748      **/
749     final synchronized void sendDont(final int option) throws IOException {
750         if (debug || debugoptions) {
751             System.err.println("DONT: " + TelnetOption.getOption(option));
752         }
753         _output_.write(COMMAND_DONT);
754         _output_.write(option);
755 
756         /* Code Section added for sending the negotiation ASAP (start) */
757         _output_.flush();
758         /* Code Section added for sending the negotiation ASAP (end) */
759     }
760 
761     /**
762      * Sends terminal type information.
763      *
764      * @throws IOException - Exception in I/O.
765      */
766     final synchronized void sendTerminalType() throws IOException {
767         if (debug) {
768             System.err.println("SEND TERMINAL-TYPE: " + terminalType);
769         }
770         if (terminalType != null) {
771             _output_.write(COMMAND_SB);
772             _output_.write(COMMAND_IS);
773             _output_.write(terminalType.getBytes(getCharset()));
774             _output_.write(COMMAND_SE);
775             _output_.flush();
776         }
777     }
778 
779     /**
780      * Sends a {@code WILL}.
781      *
782      * @param option - Option code.
783      * @throws IOException - Exception in I/O.
784      **/
785     final synchronized void sendWill(final int option) throws IOException {
786         if (debug || debugoptions) {
787             System.err.println("WILL: " + TelnetOption.getOption(option));
788         }
789         _output_.write(COMMAND_WILL);
790         _output_.write(option);
791 
792         /* Code Section added for sending the negotiation ASAP (start) */
793         _output_.flush();
794         /* Code Section added for sending the negotiation ASAP (end) */
795     }
796 
797     /**
798      * Sends a {@code WONT}.
799      *
800      * @param option - Option code.
801      * @throws IOException - Exception in I/O.
802      **/
803     final synchronized void sendWont(final int option) throws IOException {
804         if (debug || debugoptions) {
805             System.err.println("WONT: " + TelnetOption.getOption(option));
806         }
807         _output_.write(COMMAND_WONT);
808         _output_.write(option);
809 
810         /* Code Section added for sending the negotiation ASAP (start) */
811         _output_.flush();
812         /* Code Section added for sending the negotiation ASAP (end) */
813     }
814 
815     /**
816      * Sets the state of the option.
817      *
818      * @param option - option code to be set.
819      * @throws IOException
820      */
821     void setDo(final int option) throws IOException {
822         options[option] |= DO_MASK;
823 
824         /* open TelnetOptionHandler functionality (start) */
825         if (requestedDo(option) && optionHandlers[option] != null) {
826             optionHandlers[option].setDo(true);
827 
828             final int[] subneg = optionHandlers[option].startSubnegotiationRemote();
829 
830             if (subneg != null) {
831                 _sendSubnegotiation(subneg);
832             }
833         }
834         /* open TelnetOptionHandler functionality (end) */
835     }
836 
837     /**
838      * Sets the state of the option.
839      *
840      * @param option - option code to be set.
841      */
842     void setDont(final int option) {
843         options[option] &= ~DO_MASK;
844 
845         /* open TelnetOptionHandler functionality (start) */
846         if (optionHandlers[option] != null) {
847             optionHandlers[option].setDo(false);
848         }
849         /* open TelnetOptionHandler functionality (end) */
850     }
851 
852     /**
853      * Sets the state of the option.
854      *
855      * @param option - option code to be set.
856      */
857     void setWantDo(final int option) {
858         options[option] |= REQUESTED_DO_MASK;
859     }
860 
861     /**
862      * Sets the state of the option.
863      *
864      * @param option - option code to be set.
865      */
866     void setWantDont(final int option) {
867         options[option] &= ~REQUESTED_DO_MASK;
868     }
869 
870     /**
871      * Sets the state of the option.
872      *
873      * @param option - option code to be set.
874      */
875     void setWantWill(final int option) {
876         options[option] |= REQUESTED_WILL_MASK;
877     }
878 
879     /**
880      * Sets the state of the option.
881      *
882      * @param option - option code to be set.
883      */
884     void setWantWont(final int option) {
885         options[option] &= ~REQUESTED_WILL_MASK;
886     }
887 
888     /**
889      * Sets the state of the option.
890      *
891      * @param option - option code to be set.
892      * @throws IOException
893      */
894     void setWill(final int option) throws IOException {
895         options[option] |= WILL_MASK;
896 
897         /* open TelnetOptionHandler functionality (start) */
898         if (requestedWill(option) && optionHandlers[option] != null) {
899             optionHandlers[option].setWill(true);
900 
901             final int[] subneg = optionHandlers[option].startSubnegotiationLocal();
902 
903             if (subneg != null) {
904                 _sendSubnegotiation(subneg);
905             }
906         }
907         /* open TelnetOptionHandler functionality (end) */
908     }
909 
910     /* open TelnetOptionHandler functionality (start) */
911 
912     /**
913      * Sets the state of the option.
914      *
915      * @param option - option code to be set.
916      */
917     void setWont(final int option) {
918         options[option] &= ~WILL_MASK;
919 
920         /* open TelnetOptionHandler functionality (start) */
921         if (optionHandlers[option] != null) {
922             optionHandlers[option].setWill(false);
923         }
924         /* open TelnetOptionHandler functionality (end) */
925     }
926 
927     /**
928      * Sends a read char on the spy stream.
929      *
930      * @param ch - character read from the session
931      */
932     void spyRead(final int ch) {
933         final OutputStream spy = spyStream;
934         if (spy != null) {
935             try {
936                 if (ch != '\r') // never write '\r' on its own
937                 {
938                     if (ch == '\n') {
939                         spy.write('\r'); // add '\r' before '\n'
940                     }
941                     spy.write(ch); // write original character
942                     spy.flush();
943                 }
944             } catch (final IOException e) {
945                 spyStream = null;
946             }
947         }
948     }
949 
950     /**
951      * Sends a written char on the spy stream.
952      *
953      * @param ch - character written to the session
954      */
955     void spyWrite(final int ch) {
956         if (!(stateIsDo(TelnetOption.ECHO) && requestedDo(TelnetOption.ECHO))) {
957             final OutputStream spy = spyStream;
958             if (spy != null) {
959                 try {
960                     spy.write(ch);
961                     spy.flush();
962                 } catch (final IOException e) {
963                     spyStream = null;
964                 }
965             }
966         }
967     }
968     /* Code Section added for supporting spystreams (end) */
969 
970     /**
971      * Looks for the state of the option.
972      *
973      * @return returns true if a {@code DO} has been acknowledged.
974      *
975      * @param option - option code to be looked up.
976      */
977     boolean stateIsDo(final int option) {
978         return (options[option] & DO_MASK) != 0;
979     }
980 
981     /**
982      * Looks for the state of the option.
983      *
984      * @return returns true if a {@code DONT} has been acknowledged
985      *
986      * @param option - option code to be looked up.
987      */
988     boolean stateIsDont(final int option) {
989         return !stateIsDo(option);
990     }
991 
992     /**
993      * Looks for the state of the option.
994      *
995      * @return returns true if a {@code WILL} has been acknowledged
996      *
997      * @param option - option code to be looked up.
998      */
999     boolean stateIsWill(final int option) {
1000         return (options[option] & WILL_MASK) != 0;
1001     }
1002 
1003     /**
1004      * Looks for the state of the option.
1005      *
1006      * @return returns true if a {@code WONT} has been acknowledged
1007      *
1008      * @param option - option code to be looked up.
1009      */
1010     boolean stateIsWont(final int option) {
1011         return !stateIsWill(option);
1012     }
1013 
1014     /**
1015      * Unregisters the current notification handler.
1016      *
1017      */
1018     public void unregisterNotifHandler() {
1019         this.notifhand = null;
1020     }
1021 }