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  package org.apache.xbean.terminal.telnet;
18  
19  import java.io.FilterInputStream;
20  import java.io.IOException;
21  import java.io.InputStream;
22  import java.io.OutputStream;
23  
24  public class TelnetInputStream extends FilterInputStream implements TelnetCodes {
25      // state table for what options have been negotiated
26      private TelnetOption[] options = new TelnetOption[256];
27      private OutputStream out = null;
28  
29      /**
30       * We haven yet implemented any Telnet options, so we just explicitly
31       * disable some common options for safety sake.
32       * <p/>
33       * Certain Telnet clients (MS Windows Telnet) are enabling options without
34       * asking first. Shame, shame, shame.
35       *
36       * @throws IOException
37       */
38      public TelnetInputStream(InputStream in, OutputStream out) throws IOException {
39          super(in);
40          this.out = out;
41          negotiateOption(DONT, 1);
42          negotiateOption(DONT, 6);
43          negotiateOption(DONT, 24);
44          negotiateOption(DONT, 33);
45          negotiateOption(DONT, 34);
46      }
47  
48      public int read() throws IOException {
49          int b = super.read();
50          if (b == IAC) {
51              // The cosole has a reference
52              // to this input stream
53              processCommand();
54              // Call read recursively as
55              // the next character could
56              // also be a command
57              b = this.read();
58          }
59          //System.out.println("B="+b);
60          return b;
61      }
62  
63      /**
64       * This is only called by TelnetInputStream
65       * it is assumed that the IAC byte has already been read from the stream.
66       *
67       * @throws IOException
68       */
69      private void processCommand() throws IOException {
70          // Debug statement
71          print("C: IAC ");
72          int command = super.read();
73          switch (command) {
74              case WILL:
75                  senderWillEnableOption(super.read());
76                  break;
77              case DO:
78                  pleaseDoEnableOption(super.read());
79                  break;
80              case WONT:
81                  senderWontEnableOption(super.read());
82                  break;
83              case DONT:
84                  pleaseDontEnableOption(super.read());
85                  break;
86              default:
87                  unimplementedCommand(command);
88                  break;
89          }
90      }
91  
92      private void unimplementedCommand(int command) {
93          println(command + ": command not found");
94      }
95  
96      /**
97       * Client says: I will enable OptionX
98       * <p/>
99       * If the sender initiated the negotiation of the
100      * option, we must send a reply. Replies can be DO or DON'T.
101      *
102      * @param optionID
103      * @throws IOException
104      */
105     private void senderWillEnableOption(int optionID) throws IOException {
106         // Debug statement
107         println("WILL " + optionID);
108         TelnetOption option = getOption(optionID);
109         if (option.hasBeenNegotiated()) return;
110         if (option.isInNegotiation()) {
111             option.enable();
112         } else if (!option.isInNegotiation() && option.isSupported()) {
113             negotiateOption(DO, optionID);
114             option.enable();
115         } else if (!option.isInNegotiation() && !option.isSupported()) {
116             negotiateOption(DONT, optionID);
117             option.disable();
118         }
119     }
120 
121     /**
122      * Client says: Please, do enable OptionX
123      * <p/>
124      * If the sender initiated the negotiation of the
125      * option, we must send a reply.
126      * <p/>
127      * Replies can be WILL or WON'T.
128      *
129      * @param optionID
130      * @throws IOException
131      */
132     private void pleaseDoEnableOption(int optionID) throws IOException {
133         // Debug statement
134         println("DO " + optionID);
135         TelnetOption option = getOption(optionID);
136         if (option.hasBeenNegotiated()) return;
137         if (option.isInNegotiation()) {
138             option.enable();
139         } else if (!option.isInNegotiation() && option.isSupported()) {
140             negotiateOption(WILL, optionID);
141             option.enable();
142         } else if (!option.isInNegotiation() && !option.isSupported()) {
143             negotiateOption(WONT, optionID);
144             option.disable();
145         }
146     }
147 
148     /**
149      * Client says: I won't enable OptionX
150      * <p/>
151      * <p/>
152      * If the sender initiated the negotiation of the
153      * option, we must send a reply.
154      * <p/>
155      * Replies can only be DON'T.
156      *
157      * @param optionID
158      * @throws IOException
159      */
160     private void senderWontEnableOption(int optionID) throws IOException {
161         println("WONT " + optionID);
162         TelnetOption option = getOption(optionID);
163         if (option.hasBeenNegotiated()) return;
164         if (!option.isInNegotiation()) {
165             negotiateOption(DONT, optionID);
166         }
167         option.disable();
168     }
169 
170     /**
171      * Client says: Please, don't enable OptionX
172      * <p/>
173      * If the sender initiated the negotiation of the
174      * option, we must send a reply.
175      * <p/>
176      * Replies can only be WON'T.
177      *
178      * @param optionID
179      * @throws IOException
180      */
181     private void pleaseDontEnableOption(int optionID) throws IOException {
182         // Debug statement
183         println("DONT " + optionID);
184         TelnetOption option = getOption(optionID);
185         if (option.hasBeenNegotiated()) return;
186         if (!option.isInNegotiation()) {
187             negotiateOption(WONT, optionID);
188         }
189         option.disable();
190     }
191 
192     // TODO:0: Replace with actual logging
193     private void println(String s) {
194         // System.out.println(s);
195     }
196 
197     // TODO:0: Replace with actual logging
198     private void print(String s) {
199         // System.out.print(s);
200     }
201 
202     /**
203      * Send an option negitiation command to the client
204      *
205      * @param negotiate
206      * @param optionID
207      * @throws IOException
208      */
209     private void negotiateOption(int negotiate, int optionID)
210             throws IOException {
211         TelnetOption option = getOption(optionID);
212         option.isInNegotiation(true);
213         String n = null;
214         switch (negotiate) {
215             case WILL:
216                 n = "WILL ";
217                 break;
218             case DO:
219                 n = "DO ";
220                 break;
221             case WONT:
222                 n = "WONT ";
223                 break;
224             case DONT:
225                 n = "DONT ";
226                 break;
227         }
228         // Debug statement
229         println("S: IAC " + n + optionID);
230         synchronized (out) {
231             out.write(IAC);
232             out.write(negotiate);
233             out.write(optionID);
234         }
235     }
236 
237     private TelnetOption getOption(int optionID) {
238         TelnetOption opt = options[optionID];
239         if (opt == null) {
240             opt = new TelnetOption(optionID);
241             options[optionID] = opt;
242         }
243         return opt;
244     }
245 }