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 }