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