001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.xbean.terminal.telnet;
018
019 import java.io.FilterInputStream;
020 import java.io.IOException;
021 import java.io.InputStream;
022 import java.io.OutputStream;
023
024 public class TelnetInputStream extends FilterInputStream implements TelnetCodes {
025 // state table for what options have been negotiated
026 private TelnetOption[] options = new TelnetOption[256];
027 private OutputStream out = null;
028
029 /**
030 * We haven yet implemented any Telnet options, so we just explicitly
031 * disable some common options for safety sake.
032 * <p/>
033 * Certain Telnet clients (MS Windows Telnet) are enabling options without
034 * asking first. Shame, shame, shame.
035 *
036 * @throws IOException
037 */
038 public TelnetInputStream(InputStream in, OutputStream out) throws IOException {
039 super(in);
040 this.out = out;
041 negotiateOption(DONT, 1);
042 negotiateOption(DONT, 6);
043 negotiateOption(DONT, 24);
044 negotiateOption(DONT, 33);
045 negotiateOption(DONT, 34);
046 }
047
048 public int read() throws IOException {
049 int b = super.read();
050 if (b == IAC) {
051 // The cosole has a reference
052 // to this input stream
053 processCommand();
054 // Call read recursively as
055 // the next character could
056 // also be a command
057 b = this.read();
058 }
059 //System.out.println("B="+b);
060 return b;
061 }
062
063 /**
064 * This is only called by TelnetInputStream
065 * it is assumed that the IAC byte has already been read from the stream.
066 *
067 * @throws IOException
068 */
069 private void processCommand() throws IOException {
070 // Debug statement
071 print("C: IAC ");
072 int command = super.read();
073 switch (command) {
074 case WILL:
075 senderWillEnableOption(super.read());
076 break;
077 case DO:
078 pleaseDoEnableOption(super.read());
079 break;
080 case WONT:
081 senderWontEnableOption(super.read());
082 break;
083 case DONT:
084 pleaseDontEnableOption(super.read());
085 break;
086 default:
087 unimplementedCommand(command);
088 break;
089 }
090 }
091
092 private void unimplementedCommand(int command) {
093 println(command + ": command not found");
094 }
095
096 /**
097 * Client says: I will enable OptionX
098 * <p/>
099 * 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 }