| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| /*** |
| * Java TelnetD library (embeddable telnet daemon) |
| * Copyright (c) 2000-2005 Dieter Wimberger |
| * All rights reserved. |
| * <p/> |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * <p/> |
| * Neither the name of the author nor the names of its contributors |
| * may be used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * <p/> |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS |
| * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| ***/ |
| |
| package org.apache.felix.gogo.jline.telnet; |
| |
| import java.io.BufferedOutputStream; |
| import java.io.DataInputStream; |
| import java.io.DataOutputStream; |
| import java.io.IOException; |
| import java.net.InetAddress; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| |
| import org.apache.felix.gogo.jline.telnet.ConnectionEvent.Type; |
| |
| /** |
| * Class that represents the TelnetIO implementation. It contains |
| * an inner IACHandler class to handle the telnet protocol level |
| * communication. |
| * <p/> |
| * Although supposed to work full-duplex, we only process the telnet protocol |
| * layer communication in case of reading requests from the higher levels. |
| * This is the only way to meet the one thread per connection requirement. |
| * </p> |
| * <p/> |
| * The output is done via byte-oriented streams, definately suitable for the |
| * telnet protocol. The format of the output is UTF-8 (Unicode), which is a |
| * standard and supported by any telnet client, including the ones included |
| * in Microsoft OS's. |
| * </p> |
| * <em>Notes:</em> |
| * <ul> |
| * <li>The underlying output is buffered, to ensure that all bytes written |
| * are send, the flush() method has to be called. |
| * <li>This low-level routines ensure nice multithreading behaviour on I/O. |
| * Neither large outputs, nor input sequences excuted by the connection thread |
| * can hog the system. |
| * </ul> |
| * |
| * @author Dieter Wimberger |
| * @version 2.0 (16/07/2006) |
| */ |
| public class TelnetIO { |
| |
| /** |
| * Interpret As Command |
| */ |
| protected static final int IAC = 255; |
| /** |
| * Go Ahead <BR> Newer Telnets do not make use of this option |
| * that allows a specific communication mode. |
| */ |
| protected static final int GA = 249; |
| /** |
| * Negotiation: Will do option |
| */ |
| protected static final int WILL = 251; |
| /** |
| * Negotiation: Wont do option |
| */ |
| protected static final int WONT = 252; |
| /** |
| * Negotiation: Do option |
| */ |
| protected static final int DO = 253; |
| /** |
| * Negotiation: Dont do option |
| */ |
| protected static final int DONT = 254; |
| /** |
| * Marks start of a subnegotiation. |
| */ |
| protected static final int SB = 250; |
| /** |
| * Marks end of subnegotiation. |
| */ |
| protected static final int SE = 240; |
| /** |
| * No operation |
| */ |
| protected static final int NOP = 241; |
| /** |
| * Data mark its the data part of a SYNCH which helps to clean up the buffers between |
| * Telnet Server <-> Telnet Client. <BR> |
| * It should work like this we send a TCP urgent package and <IAC> <DM> the receiver |
| * should get the urgent package (SYNCH) and just discard everything until he receives |
| * our <IAC> <DM>.<BR> |
| * <EM>Remark</EM>: |
| * <OL> |
| * <LI>can we send a TCP urgent package? |
| * <LI>can we make use of the thing at all? |
| * </OL> |
| */ |
| protected static final int DM = 242; |
| /** |
| * Break |
| */ |
| protected static final int BRK = 243; |
| /** |
| * Interrupt Process |
| */ |
| protected static final int IP = 244; |
| /** |
| * Abort Output |
| */ |
| protected static final int AO = 245; |
| |
| /**** Implementation of OutputStream ****************************************************/ |
| /** |
| * Are You There |
| */ |
| protected static final int AYT = 246; |
| /** |
| * Erase Char |
| */ |
| protected static final int EC = 247; |
| /** |
| * Erase Line |
| */ |
| protected static final int EL = 248; |
| /** |
| * Telnet Option: ECHO |
| */ |
| protected static final int ECHO = 1; |
| /** |
| * Telnet Option: SUPress Go Ahead<br> |
| * This will be negotiated, all new telnet protocol implementations are |
| * recommended to do this. |
| */ |
| protected static final int SUPGA = 3; |
| /** |
| * Telnet Option: Negotiate About Window Size<br> |
| * <ul> |
| * <li>Server request is IAC DO NAWS |
| * <li>Client response contains subnegotiation with data (columns, rows). |
| * </ul> |
| */ |
| protected static final int NAWS = 31; |
| /** |
| * Telnet Option: Terminal TYPE <br> |
| * <ul> |
| * <li>Server request contains subnegotiation SEND |
| * <li>Client response contains subnegotiation with data IS,terminal type string |
| * </ul> |
| */ |
| protected static final int TTYPE = 24; |
| /** |
| * TTYPE subnegotiation: IS |
| */ |
| protected static final int IS = 0; |
| /** |
| * TTYPE subnegotiation: SEND |
| */ |
| protected static final int SEND = 1; |
| |
| /**** End implementation of OutputStream ***********************************************/ |
| |
| |
| /**** Implementation of InputStream ****************************************************/ |
| /** |
| * Telnet Option: Logout<br> |
| * This allows nice goodbye to time-outed or unwanted clients. |
| */ |
| protected static final int LOGOUT = 18; |
| /** |
| * Telnet Option: Linemode |
| * <p/> |
| * The infamous line mode option. |
| */ |
| protected static final int LINEMODE = 34; |
| protected static final int LM_MODE = 1; |
| protected static final int LM_EDIT = 1; |
| protected static final int LM_TRAPSIG = 2; |
| |
| /**** Implementation of InputStream ****************************************************/ |
| |
| |
| /**** |
| * Following methods implement init/request/answer procedures for telnet |
| * protocol level communication. |
| */ |
| protected static final int LM_MODEACK = 4; |
| protected static final int LM_FORWARDMASK = 2; |
| protected static final int LM_SLC = 3; |
| protected static final int LM_SLC_NOSUPPORT = 0; |
| protected static final int LM_SLC_DEFAULT = 3; |
| |
| |
| /**** End telnet protocol level communication methods *******************************/ |
| protected static final int LM_SLC_VALUE = 2; |
| |
| |
| /** Constants declaration ***********************************************/ |
| |
| //Telnet Protocoll Constants |
| protected static final int LM_SLC_CANTCHANGE = 1; |
| protected static final int LM_SLC_LEVELBITS = 3; |
| protected static final int LM_SLC_ACK = 128; |
| protected static final int LM_SLC_FLUSHIN = 64; |
| protected static final int LM_SLC_FLUSHOUT = 32; |
| protected static final int LM_SLC_SYNCH = 1; |
| protected static final int LM_SLC_BRK = 2; |
| protected static final int LM_SLC_IP = 3; |
| protected static final int LM_SLC_AO = 4; |
| protected static final int LM_SLC_AYT = 5; |
| protected static final int LM_SLC_EOR = 6; |
| |
| /** |
| * The following implement the NVT (network virtual terminal) which offers the concept |
| * of a simple "printer". They are the basical meanings of control possibilities |
| * on a standard telnet implementation. |
| */ |
| protected static final int LM_SLC_ABORT = 7; |
| protected static final int LM_SLC_EOF = 8; |
| protected static final int LM_SLC_SUSP = 9; |
| /** |
| * Telnet Option: Environment |
| */ |
| protected static final int NEWENV = 39; |
| protected static final int NE_INFO = 2; |
| |
| /** |
| * The following are constants for supported options, |
| * which can be negotiated based upon the telnet protocol |
| * specification. |
| */ |
| protected static final int NE_VAR = 0; |
| protected static final int NE_VALUE = 1; |
| |
| /** |
| * The following options are options for which we also support subnegotiation |
| * based upon the telnet protocol specification. |
| */ |
| protected static final int NE_ESC = 2; |
| protected static final int NE_USERVAR = 3; |
| protected static final int NE_VAR_OK = 2; |
| protected static final int NE_VAR_DEFINED = 1; |
| protected static final int NE_VAR_DEFINED_EMPTY = 0; |
| protected static final int NE_VAR_UNDEFINED = -1; |
| protected static final int NE_IN_ERROR = -2; |
| protected static final int NE_IN_END = -3; |
| protected static final int NE_VAR_NAME_MAXLENGTH = 50; |
| protected static final int NE_VAR_VALUE_MAXLENGTH = 1000; |
| /** |
| * Unused |
| */ |
| protected static final int EXT_ASCII = 17; //Defines Extended ASCII |
| protected static final int SEND_LOC = 23; //Defines Send Location |
| protected static final int AUTHENTICATION = 37; //Defines Authentication |
| protected static final int ENCRYPT = 38; //Defines Encryption |
| private static final Logger LOG = Logger.getLogger(TelnetIO.class.getName()); |
| /** |
| * Window Size Constants |
| */ |
| private static final int SMALLEST_BELIEVABLE_WIDTH = 20; |
| private static final int SMALLEST_BELIEVABLE_HEIGHT = 6; |
| private static final int DEFAULT_WIDTH = 80; |
| private static final int DEFAULT_HEIGHT = 25; |
| private Connection connection; //a reference to the connection this instance works for |
| private ConnectionData connectionData; //holds all important information of the connection |
| private DataOutputStream out; //the byte oriented outputstream |
| private DataInputStream in; //the byte oriented input stream |
| //Aggregations |
| private IACHandler iacHandler; //holds a reference to the aggregated IACHandler |
| //Members |
| private InetAddress localAddress; //address of the host the telnetd is running on |
| private boolean noIac = false; //describes if IAC was found and if its just processed |
| @SuppressWarnings("unused") |
| private boolean initializing; |
| private boolean crFlag; |
| /** |
| * Creates a TelnetIO object for the given connection.<br> |
| * Input- and OutputStreams are properly set and the primary telnet |
| * protocol initialization is carried out by the inner IACHandler class.<BR> |
| */ |
| public TelnetIO() { |
| }//constructor |
| |
| public void initIO() throws IOException { |
| //we make an instance of our inner class |
| iacHandler = new IACHandler(); |
| //we setup underlying byte oriented streams |
| in = new DataInputStream(connectionData.getSocket().getInputStream()); |
| out = new DataOutputStream(new BufferedOutputStream(connectionData.getSocket().getOutputStream())); |
| |
| //we save the local address (necessary?) |
| localAddress = connectionData.getSocket().getLocalAddress(); |
| crFlag = false; |
| //bootstrap telnet communication |
| initTelnetCommunication(); |
| }//initIO |
| |
| public void setConnection(Connection con) { |
| connection = con; |
| connectionData = connection.getConnectionData(); |
| }//setConnection |
| |
| /** |
| * Method to output a byte. Ensures that CR(\r) is never send |
| * alone,but CRLF(\r\n), which is a rule of the telnet protocol. |
| * |
| * @param b Byte to be written. |
| */ |
| public void write(byte b) throws IOException { |
| //ensure CRLF(\r\n) is written for LF(\n) to adhere |
| //to the telnet protocol. |
| if (!crFlag && b == 10) { |
| out.write(13); |
| } |
| |
| out.write(b); |
| |
| crFlag = b == 13; |
| }//write(byte) |
| |
| /** |
| * Method to output an int. |
| * |
| * @param i Integer to be written. |
| */ |
| public void write(int i) |
| throws IOException { |
| write((byte) i); |
| }//write(int) |
| |
| /** |
| * Method to write an array of bytes. |
| * |
| * @param sequence byte[] to be written. |
| */ |
| public void write(byte[] sequence) throws IOException { |
| for (byte b : sequence) { |
| write(b); |
| } |
| }//write(byte[]) |
| |
| /** |
| * Method to output an array of int' s. |
| * |
| * @param sequence int [] to write |
| */ |
| public void write(int[] sequence) throws IOException { |
| for (int i : sequence) { |
| write((byte) i); |
| } |
| }//write(int[]) |
| |
| /** |
| * Method to write a char. |
| * |
| * @param ch char to be written. |
| */ |
| public void write(char ch) throws IOException { |
| write((byte) ch); |
| }//write(char) |
| |
| /** |
| * Method to output a string. |
| * |
| * @param str String to be written. |
| */ |
| public void write(String str) throws IOException { |
| write(str.getBytes()); |
| }//write(String) |
| |
| /** |
| * Method to flush all buffered output. |
| */ |
| public void flush() throws IOException { |
| out.flush(); |
| }//flush |
| |
| /** |
| * Method to close the underlying output stream to free system resources.<br> |
| * Most likely only to be called by the ConnectionManager upon clean up of |
| * connections that ended or died. |
| */ |
| public void closeOutput() { |
| |
| try { |
| //sends telnetprotocol logout acknowledgement |
| write(IAC); |
| write(DO); |
| write(LOGOUT); |
| //and now close underlying outputstream |
| |
| out.close(); |
| } catch (IOException ex) { |
| LOG.log(Level.SEVERE, "closeOutput()", ex); |
| //handle? |
| } |
| }//close |
| |
| private void rawWrite(int i) throws IOException { |
| out.write(i); |
| }//rawWrite |
| |
| /** |
| * Method to read a byte from the InputStream. |
| * Invokes the IACHandler upon IAC (Byte=255). |
| * |
| * @return int read from stream. |
| */ |
| public int read() throws IOException { |
| int c = rawread(); |
| //if (c == 255) { |
| noIac = false; |
| while ((c == 255) && (!noIac)) { |
| /** |
| * Read next, and invoke |
| * the IACHandler he is taking care of the rest. Or at least he should :) |
| */ |
| c = rawread(); |
| if (c != 255) { |
| iacHandler.handleC(c); |
| c = rawread(); |
| } else { |
| noIac = true; |
| } |
| } |
| return stripCRSeq(c); |
| }//read |
| |
| /** |
| * Method to close the underlying inputstream to free system resources.<br> |
| * Most likely only to be called by the ConnectionManager upon clean up of |
| * connections that ended or died. |
| */ |
| public void closeInput() { |
| try { |
| in.close(); |
| } catch (IOException e) { |
| //handle? |
| } |
| }//closeInput |
| |
| /** |
| * This method reads an unsigned 16bit Integer from the stream, |
| * its here for getting the NAWS Data Values for height and width. |
| */ |
| private int read16int() throws IOException { |
| int c = in.readUnsignedShort(); |
| return c; |
| }//read16int |
| |
| /** |
| * The following options are options which might be of interest, but are not |
| * yet implemented or in use. |
| */ |
| |
| /** |
| * Method to read a raw byte from the InputStream.<br> |
| * Telnet protocol layer communication is filtered and processed here. |
| * |
| * @return int read from stream. |
| */ |
| private int rawread() throws IOException { |
| int b = 0; |
| |
| //try { |
| b = in.readUnsignedByte(); |
| connectionData.activity(); |
| return b; |
| }//rawread |
| |
| /** |
| * Checks for the telnet protocol specified CR followed by NULL or LF<BR> |
| * Subsequently reads for the next byte and forwards |
| * only a ENTER represented by LF internally. |
| */ |
| private int stripCRSeq(int input) throws IOException { |
| if (input == 13) { |
| rawread(); |
| return 10; |
| } |
| return input; |
| }//stripCRSeq |
| |
| /** |
| * Method that initializes the telnet communication layer. |
| */ |
| private void initTelnetCommunication() { |
| |
| initializing = true; |
| try { |
| //start out, some clients just wait |
| if (connectionData.isLineMode()) { |
| iacHandler.doLineModeInit(); |
| LOG.log(Level.FINE, "Line mode initialized."); |
| } else { |
| iacHandler.doCharacterModeInit(); |
| LOG.log(Level.FINE, "Character mode initialized."); |
| } |
| //open for a defined timeout so we read incoming negotiation |
| connectionData.getSocket().setSoTimeout(1000); |
| read(); |
| |
| } catch (Exception e) { |
| //handle properly |
| //log.error("initTelnetCommunication()",e); |
| } finally { |
| //this is important, dont ask me why :) |
| try { |
| connectionData.getSocket().setSoTimeout(0); |
| } catch (Exception ex) { |
| LOG.log(Level.SEVERE, "initTelnetCommunication()", ex); |
| } |
| } |
| initializing = false; |
| }//initTelnetCommunication |
| |
| /** |
| * Method that represents the answer to the |
| * AreYouThere question of the telnet protocol specification |
| * <p/> |
| * Output of the String [HostAdress:Yes] |
| */ |
| private void IamHere() { |
| try { |
| write("[" + localAddress.toString() + ":Yes]"); |
| flush(); |
| } catch (Exception ex) { |
| LOG.log(Level.SEVERE, "IamHere()", ex); |
| } |
| }//IamHere |
| |
| /** |
| * Network virtual terminal break. |
| */ |
| private void nvtBreak() { |
| connection.processConnectionEvent(new ConnectionEvent(connection, ConnectionEvent.Type.CONNECTION_BREAK)); |
| }//nvtBreak |
| |
| /** |
| * Method that checks reported terminal sizes and sets the |
| * asserted values in the ConnectionData instance associated with |
| * the connection. |
| * |
| * @param width Integer that represents the Window width in chars |
| * @param height Integer that represents the Window height in chars |
| */ |
| private void setTerminalGeometry(int width, int height) { |
| if (width < SMALLEST_BELIEVABLE_WIDTH) { |
| width = DEFAULT_WIDTH; |
| } |
| if (height < SMALLEST_BELIEVABLE_HEIGHT) { |
| height = DEFAULT_HEIGHT; |
| } |
| //DEBUG: write("[New Window Size " + window_width + "x" + window_height + "]"); |
| connectionData.setTerminalGeometry(width, height); |
| connection.processConnectionEvent(new ConnectionEvent(connection, |
| Type.CONNECTION_TERMINAL_GEOMETRY_CHANGED)); |
| }//setTerminalGeometry |
| |
| public void setEcho(boolean b) { |
| }//setEcho |
| |
| /** |
| * An inner class for handling incoming option negotiations implementing the <B>telnet protocol</B> |
| * specification based upon following Standards and RFCs: |
| * <OL> |
| * <LI><A HREF="ftp://ds.internic.net/rfc/rfc854.txt">854 Telnet Protocol Specification</A> |
| * <LI><A HREF="ftp://ds.internic.net/rfc/rfc855.txt">855 Telnet Option Specifications</A> |
| * <LI><A HREF="ftp://ds.internic.net/rfc/rfc857.txt">857 Telnet Echo Option</A> |
| * <LI><A HREF="ftp://ds.internic.net/rfc/rfc858.txt">858 Telnet Supress Go Ahead Option</A> |
| * <LI><A HREF="ftp://ds.internic.net/rfc/rfc727.txt">727 Telnet Logout Option</A> |
| * <LI><A HREF="ftp://ds.internic.net/rfc/rfc1073.txt">1073 Telnet Window Size Option</A> |
| * <LI><A HREF="ftp://ds.internic.net/rfc/rfc1091.txt">1091 Telnet Terminal-Type Option</A> |
| * </OL> |
| * <p/> |
| * Furthermore there are some more, which helped to solve problems, or might be important |
| * for future enhancements:<BR> |
| * <A HREF="ftp://ds.internic.net/rfc/rfc1143.txt">1143 The Q Method of Implementing Option Negotiation</A><BR> |
| * <A HREF="ftp://ds.internic.net/rfc/rfc1416.txt">1416 Telnet Authentication Option</A><BR> |
| * <p/> |
| * After an intense study of the available material (mainly cryptical written RFCs, |
| * a telnet client implementation for the macintosh based upon NCSA telnet, and a server side |
| * implementation called key, a mud-like system completely written in Java) I realized |
| * the problems we are facing regarding to the telnet protocol: |
| * <OL> |
| * <LI> a minimal spread of invented options, which means there are a lot of invented options, |
| * but rarely they made it through to become a standard. |
| * <LI> Dependency on a special type of implementation is dangerous in our case. |
| * We are no kind of host that offers the user to run several processes at once, |
| * a BBS is intended to be a single process the user is interacting with. |
| * <LI> The <B>LAMER</B> has to be expected to log in with the standard Microsoft telnet |
| * implementation. This means forget every nice feature and most of the almost-standards. |
| * <p/> |
| * </OL> |
| * <BR> |
| * |
| * @author Dieter Wimberger |
| * @version 1.1 16/06/1998 |
| * <p/> |
| * <p/> |
| * <B>To-Do</B>:<UL> |
| * <LI>UNIX conform new style TTYPE negotiation. Setting a list and selecting from it... |
| * </UL> |
| */ |
| class IACHandler { |
| |
| /** |
| * Telnet readin buffer |
| * Here its implemented guys. Open your eyes upon this solution. |
| * The others take a one byte solution :) |
| */ |
| private int[] buffer = new int[2]; |
| |
| /** |
| * DO_ECHO or not |
| */ |
| private boolean DO_ECHO = false; |
| |
| /** |
| * DO_SUPGA or not |
| */ |
| private boolean DO_SUPGA = false; |
| |
| /** |
| * DO_NAWS or not |
| */ |
| private boolean DO_NAWS = false; |
| |
| /** |
| * DO_TTYPE or not |
| */ |
| private boolean DO_TTYPE = false; |
| |
| /** |
| * DO_LINEMODE or not |
| */ |
| private boolean DO_LINEMODE = false; |
| |
| /** |
| * DO_NEWENV or not |
| */ |
| private boolean DO_NEWENV = false; |
| |
| /** |
| * Are we waiting for a DO reply? |
| */ |
| private boolean WAIT_DO_REPLY_SUPGA = false; |
| private boolean WAIT_DO_REPLY_ECHO = false; |
| private boolean WAIT_DO_REPLY_NAWS = false; |
| private boolean WAIT_DO_REPLY_TTYPE = false; |
| private boolean WAIT_DO_REPLY_LINEMODE = false; |
| private boolean WAIT_LM_MODE_ACK = false; |
| private boolean WAIT_LM_DO_REPLY_FORWARDMASK = false; |
| private boolean WAIT_DO_REPLY_NEWENV = false; |
| @SuppressWarnings("unused") |
| private boolean WAIT_NE_SEND_REPLY = false; |
| |
| /** |
| * Are we waiting for a WILL reply? |
| */ |
| private boolean WAIT_WILL_REPLY_SUPGA = false; |
| private boolean WAIT_WILL_REPLY_ECHO = false; |
| private boolean WAIT_WILL_REPLY_NAWS = false; |
| private boolean WAIT_WILL_REPLY_TTYPE = false; |
| |
| |
| public void doCharacterModeInit() throws IOException { |
| sendCommand(WILL, ECHO, true); |
| sendCommand(DONT, ECHO, true); //necessary for some clients |
| sendCommand(DO, NAWS, true); |
| sendCommand(WILL, SUPGA, true); |
| sendCommand(DO, SUPGA, true); |
| sendCommand(DO, TTYPE, true); |
| sendCommand(DO, NEWENV, true); //environment variables |
| }//doCharacterModeInit |
| |
| public void doLineModeInit() throws IOException { |
| sendCommand(DO, NAWS, true); |
| sendCommand(WILL, SUPGA, true); |
| sendCommand(DO, SUPGA, true); |
| sendCommand(DO, TTYPE, true); |
| sendCommand(DO, LINEMODE, true); |
| sendCommand(DO, NEWENV, true); |
| }//doLineModeInit |
| |
| |
| /** |
| * Method to handle a IAC that came in over the line. |
| * |
| * @param i (int)ed byte that followed the IAC |
| */ |
| public void handleC(int i) throws IOException { |
| buffer[0] = i; |
| if (!parseTWO(buffer)) { |
| buffer[1] = rawread(); |
| parse(buffer); |
| } |
| buffer[0] = 0; |
| buffer[1] = 0; |
| }//handleC |
| |
| /** |
| * Method that parses for options with two characters. |
| * |
| * @param buf int [] that represents the first byte that followed the IAC first. |
| * @return true when it was a two byte command (IAC OPTIONBYTE) |
| */ |
| private boolean parseTWO(int[] buf) { |
| switch (buf[0]) { |
| case IAC: |
| //doubled IAC to escape 255 is handled within the |
| //read method. |
| break; |
| case AYT: |
| IamHere(); |
| break; |
| case AO: |
| case IP: |
| case EL: |
| case EC: |
| case NOP: |
| break; |
| case BRK: |
| nvtBreak(); |
| break; |
| default: |
| return false; |
| } |
| return true; |
| }//parseTWO |
| |
| /** |
| * Method that parses further on for options. |
| * |
| * @param buf that represents the first two bytes that followed the IAC. |
| */ |
| private void parse(int[] buf) throws IOException { |
| switch (buf[0]) { |
| /* First switch on the Negotiation Option */ |
| case WILL: |
| if (supported(buf[1]) && isEnabled(buf[1])) { |
| // do nothing |
| } else { |
| if (waitDOreply(buf[1]) && supported(buf[1])) { |
| enable(buf[1]); |
| setWait(DO, buf[1], false); |
| } else { |
| if (supported(buf[1])) { |
| sendCommand(DO, buf[1], false); |
| enable(buf[1]); |
| } else { |
| sendCommand(DONT, buf[1], false); |
| } |
| } |
| } |
| break; |
| case WONT: |
| if (waitDOreply(buf[1]) && supported(buf[1])) { |
| setWait(DO, buf[1], false); |
| } else { |
| if (supported(buf[1]) && isEnabled(buf[1])) { |
| // eanable() Method disables an Option that is already enabled |
| enable(buf[1]); |
| } |
| } |
| break; |
| case DO: |
| if (supported(buf[1]) && isEnabled(buf[1])) { |
| // do nothing |
| } else { |
| if (waitWILLreply(buf[1]) && supported(buf[1])) { |
| enable(buf[1]); |
| setWait(WILL, buf[1], false); |
| } else { |
| if (supported(buf[1])) { |
| sendCommand(WILL, buf[1], false); |
| enable(buf[1]); |
| } else { |
| sendCommand(WONT, buf[1], false); |
| } |
| } |
| } |
| break; |
| case DONT: |
| if (waitWILLreply(buf[1]) && supported(buf[1])) { |
| setWait(WILL, buf[1], false); |
| } else { |
| if (supported(buf[1]) && isEnabled(buf[1])) { |
| // enable() Method disables an Option that is already enabled |
| enable(buf[1]); |
| } |
| } |
| break; |
| |
| /* Now about other two byte IACs */ |
| case DM: //How do I implement a SYNCH signal? |
| break; |
| case SB: //handle subnegotiations |
| if ((supported(buf[1])) && (isEnabled(buf[1]))) { |
| switch (buf[1]) { |
| case NAWS: |
| handleNAWS(); |
| break; |
| case TTYPE: |
| handleTTYPE(); |
| break; |
| case LINEMODE: |
| handleLINEMODE(); |
| break; |
| case NEWENV: |
| handleNEWENV(); |
| break; |
| default: |
| } |
| } else { |
| //do nothing |
| } |
| break; |
| default: |
| }//switch |
| }//parse |
| |
| /** |
| * Method that reads a NawsSubnegotiation that ends up with a IAC SE |
| * If the measurements are unbelieveable it switches to the defaults. |
| */ |
| private void handleNAWS() throws IOException { |
| int width = read16int(); |
| if (width == 255) { |
| width = read16int(); //handle doubled 255 value; |
| } |
| int height = read16int(); |
| if (height == 255) { |
| height = read16int(); //handle doubled 255 value; |
| } |
| skipToSE(); |
| setTerminalGeometry(width, height); |
| }//handleNAWS |
| |
| /** |
| * Method that reads a TTYPE Subnegotiation String that ends up with a IAC SE |
| * If no Terminal is valid, we switch to the dumb "none" terminal. |
| */ |
| private void handleTTYPE() throws IOException { |
| String tmpstr = ""; |
| // The next read should be 0 which is IS by the protocol |
| // specs. hmmm? |
| rawread(); //that should be the is :) |
| tmpstr = readIACSETerminatedString(40); |
| LOG.log(Level.FINE, "Reported terminal name " + tmpstr); |
| connectionData.setNegotiatedTerminalType(tmpstr); |
| }//handleTTYPE |
| |
| /** |
| * Method that handles LINEMODE subnegotiation. |
| */ |
| public void handleLINEMODE() throws IOException { |
| int c = rawread(); |
| switch (c) { |
| case LM_MODE: |
| handleLMMode(); |
| break; |
| case LM_SLC: |
| handleLMSLC(); |
| break; |
| case WONT: |
| case WILL: |
| handleLMForwardMask(c); |
| break; |
| default: |
| //skip to (including) SE |
| skipToSE(); |
| } |
| }//handleLINEMODE |
| |
| public void handleLMMode() throws IOException { |
| //we sent the default which no client might deny |
| //so we only wait the ACK |
| if (WAIT_LM_MODE_ACK) { |
| int mask = rawread(); |
| if (mask != (LM_EDIT | LM_TRAPSIG | LM_MODEACK)) { |
| LOG.log(Level.FINE, "Client violates linemodeack sent: " + mask); |
| } |
| WAIT_LM_MODE_ACK = false; |
| } |
| skipToSE(); |
| }//handleLMMode |
| |
| public void handleLMSLC() throws IOException { |
| int[] triple = new int[3]; |
| if (!readTriple(triple)) return; |
| |
| //SLC will be initiated by the client |
| //case 1. client requests set |
| //LINEMODE SLC 0 SLC_DEFAULT 0 |
| if ((triple[0] == 0) && (triple[1] == LM_SLC_DEFAULT) && (triple[2] == 0)) { |
| skipToSE(); |
| //reply with SLC xxx SLC_DEFAULT 0 |
| rawWrite(IAC); |
| rawWrite(SB); |
| rawWrite(LINEMODE); |
| rawWrite(LM_SLC); |
| //triples defaults for all |
| for (int i = 1; i < 12; i++) { |
| rawWrite(i); |
| rawWrite(LM_SLC_DEFAULT); |
| rawWrite(0); |
| } |
| rawWrite(IAC); |
| rawWrite(SE); |
| flush(); |
| } else { |
| |
| //case 2: just acknowledge anything we get from the client |
| rawWrite(IAC); |
| rawWrite(SB); |
| rawWrite(LINEMODE); |
| rawWrite(LM_SLC); |
| rawWrite(triple[0]); |
| rawWrite(triple[1] | LM_SLC_ACK); |
| rawWrite(triple[2]); |
| while (readTriple(triple)) { |
| rawWrite(triple[0]); |
| rawWrite(triple[1] | LM_SLC_ACK); |
| rawWrite(triple[2]); |
| } |
| rawWrite(IAC); |
| rawWrite(SE); |
| flush(); |
| } |
| }//handleLMSLC |
| |
| public void handleLMForwardMask(int WHAT) throws IOException { |
| switch (WHAT) { |
| case WONT: |
| if (WAIT_LM_DO_REPLY_FORWARDMASK) { |
| WAIT_LM_DO_REPLY_FORWARDMASK = false; |
| } |
| break; |
| } |
| skipToSE(); |
| }//handleLMForward |
| |
| public void handleNEWENV() throws IOException { |
| LOG.log(Level.FINE, "handleNEWENV()"); |
| int c = rawread(); |
| switch (c) { |
| case IS: |
| handleNEIs(); |
| break; |
| case NE_INFO: |
| handleNEInfo(); |
| break; |
| default: |
| //skip to (including) SE |
| skipToSE(); |
| } |
| }//handleNEWENV |
| |
| /* |
| The characters following a "type" up to the next "type" or VALUE specify the |
| variable name. |
| |
| If a "type" is not followed by a VALUE |
| (e.g., by another VAR, USERVAR, or IAC SE) then that variable is |
| undefined. |
| */ |
| private int readNEVariableName(StringBuffer sbuf) throws IOException { |
| LOG.log(Level.FINE, "readNEVariableName()"); |
| int i = -1; |
| do { |
| i = rawread(); |
| if (i == -1) { |
| return NE_IN_ERROR; |
| } else if (i == IAC) { |
| i = rawread(); |
| if (i == IAC) { |
| //duplicated IAC |
| sbuf.append((char) i); |
| } else if (i == SE) { |
| return NE_IN_END; |
| } else { |
| //Error should have been duplicated |
| return NE_IN_ERROR; |
| } |
| } else if (i == NE_ESC) { |
| i = rawread(); |
| if (i == NE_ESC || i == NE_VAR || i == NE_USERVAR || i == NE_VALUE) { |
| sbuf.append((char) i); |
| } else { |
| return NE_IN_ERROR; |
| } |
| } else if (i == NE_VAR || i == NE_USERVAR) { |
| return NE_VAR_UNDEFINED; |
| } else if (i == NE_VALUE) { |
| return NE_VAR_DEFINED; |
| } else { |
| //check maximum length to prevent overflow |
| if (sbuf.length() >= NE_VAR_NAME_MAXLENGTH) { |
| //TODO: Log Overflow |
| return NE_IN_ERROR; |
| } else { |
| sbuf.append((char) i); |
| } |
| } |
| } while (true); |
| }//readNEVariableName |
| |
| |
| /* |
| The characters following a VALUE up to the next |
| "type" specify the value of the variable. |
| If a VALUE is immediately |
| followed by a "type" or IAC, then the variable is defined, but has |
| no value. |
| If an IAC is contained between the IS and the IAC SE, |
| it must be sent as IAC IAC. |
| */ |
| private int readNEVariableValue(StringBuffer sbuf) throws IOException { |
| LOG.log(Level.FINE, "readNEVariableValue()"); |
| //check conditions for first character after VALUE |
| int i = rawread(); |
| if (i == -1) { |
| return NE_IN_ERROR; |
| } else if (i == IAC) { |
| i = rawread(); |
| if (i == IAC) { |
| //Double IAC |
| return NE_VAR_DEFINED_EMPTY; |
| } else if (i == SE) { |
| return NE_IN_END; |
| } else { |
| //according to rule IAC has to be duplicated |
| return NE_IN_ERROR; |
| } |
| } else if (i == NE_VAR || i == NE_USERVAR) { |
| return NE_VAR_DEFINED_EMPTY; |
| } else if (i == NE_ESC) { |
| //escaped value |
| i = rawread(); |
| if (i == NE_ESC || i == NE_VAR || i == NE_USERVAR || i == NE_VALUE) { |
| sbuf.append((char) i); |
| } else { |
| return NE_IN_ERROR; |
| } |
| } else { |
| //character |
| sbuf.append((char) i); |
| } |
| //loop until end of value (IAC SE or TYPE) |
| do { |
| i = rawread(); |
| if (i == -1) { |
| return NE_IN_ERROR; |
| } else if (i == IAC) { |
| i = rawread(); |
| if (i == IAC) { |
| //duplicated IAC |
| sbuf.append((char) i); |
| } else if (i == SE) { |
| return NE_IN_END; |
| } else { |
| //Error should have been duplicated |
| return NE_IN_ERROR; |
| } |
| } else if (i == NE_ESC) { |
| i = rawread(); |
| if (i == NE_ESC || i == NE_VAR || i == NE_USERVAR || i == NE_VALUE) { |
| sbuf.append((char) i); |
| } else { |
| return NE_IN_ERROR; |
| } |
| } else if (i == NE_VAR || i == NE_USERVAR) { |
| return NE_VAR_OK; |
| } else { |
| //check maximum length to prevent overflow |
| if (sbuf.length() > NE_VAR_VALUE_MAXLENGTH) { |
| //TODO: LOG Overflow |
| return NE_IN_ERROR; |
| } else { |
| sbuf.append((char) i); |
| } |
| } |
| } while (true); |
| }//readNEVariableValue |
| |
| |
| public void readNEVariables() throws IOException { |
| LOG.log(Level.FINE, "readNEVariables()"); |
| StringBuffer sbuf = new StringBuffer(50); |
| int i = rawread(); |
| if (i == IAC) { |
| //invalid or empty response |
| skipToSE(); |
| LOG.log(Level.FINE, "readNEVariables()::INVALID VARIABLE"); |
| return; |
| } |
| boolean cont = true; |
| if (i == NE_VAR || i == NE_USERVAR) { |
| do { |
| switch (readNEVariableName(sbuf)) { |
| case NE_IN_ERROR: |
| LOG.log(Level.FINE, "readNEVariables()::NE_IN_ERROR"); |
| return; |
| case NE_IN_END: |
| LOG.log(Level.FINE, "readNEVariables()::NE_IN_END"); |
| return; |
| case NE_VAR_DEFINED: |
| LOG.log(Level.FINE, "readNEVariables()::NE_VAR_DEFINED"); |
| String str = sbuf.toString(); |
| sbuf.delete(0, sbuf.length()); |
| switch (readNEVariableValue(sbuf)) { |
| case NE_IN_ERROR: |
| LOG.log(Level.FINE, "readNEVariables()::NE_IN_ERROR"); |
| return; |
| case NE_IN_END: |
| LOG.log(Level.FINE, "readNEVariables()::NE_IN_END"); |
| return; |
| case NE_VAR_DEFINED_EMPTY: |
| LOG.log(Level.FINE, "readNEVariables()::NE_VAR_DEFINED_EMPTY"); |
| break; |
| case NE_VAR_OK: |
| //add variable |
| LOG.log(Level.FINE, "readNEVariables()::NE_VAR_OK:VAR=" + str + " VAL=" + sbuf.toString()); |
| TelnetIO.this.connectionData.getEnvironment().put(str, sbuf.toString()); |
| sbuf.delete(0, sbuf.length()); |
| break; |
| } |
| break; |
| case NE_VAR_UNDEFINED: |
| LOG.log(Level.FINE, "readNEVariables()::NE_VAR_UNDEFINED"); |
| break; |
| } |
| } while (cont); |
| } |
| }//readVariables |
| |
| public void handleNEIs() throws IOException { |
| LOG.log(Level.FINE, "handleNEIs()"); |
| if (isEnabled(NEWENV)) { |
| readNEVariables(); |
| } |
| }//handleNEIs |
| |
| public void handleNEInfo() throws IOException { |
| LOG.log(Level.FINE, "handleNEInfo()"); |
| if (isEnabled(NEWENV)) { |
| readNEVariables(); |
| } |
| }//handleNEInfo |
| |
| /** |
| * Method that sends a TTYPE Subnegotiation Request. |
| * IAC SB TERMINAL-TYPE SEND |
| */ |
| public void getTTYPE() throws IOException { |
| if (isEnabled(TTYPE)) { |
| rawWrite(IAC); |
| rawWrite(SB); |
| rawWrite(TTYPE); |
| rawWrite(SEND); |
| rawWrite(IAC); |
| rawWrite(SE); |
| flush(); |
| } |
| }//getTTYPE |
| |
| /** |
| * Method that sends a LINEMODE MODE Subnegotiation request. |
| * IAC LINEMODE MODE MASK SE |
| */ |
| public void negotiateLineMode() throws IOException { |
| if (isEnabled(LINEMODE)) { |
| rawWrite(IAC); |
| rawWrite(SB); |
| rawWrite(LINEMODE); |
| rawWrite(LM_MODE); |
| rawWrite(LM_EDIT | LM_TRAPSIG); |
| rawWrite(IAC); |
| rawWrite(SE); |
| WAIT_LM_MODE_ACK = true; |
| |
| //dont forwardmask |
| rawWrite(IAC); |
| rawWrite(SB); |
| rawWrite(LINEMODE); |
| rawWrite(DONT); |
| rawWrite(LM_FORWARDMASK); |
| rawWrite(IAC); |
| rawWrite(SE); |
| WAIT_LM_DO_REPLY_FORWARDMASK = true; |
| flush(); |
| } |
| }//negotiateLineMode |
| |
| /** |
| * Method that sends a NEW-ENVIRON SEND subnegotiation request |
| * for default variables and user variables. |
| * IAC SB NEW-ENVIRON SEND VAR USERVAR IAC SE |
| */ |
| private void negotiateEnvironment() throws IOException { |
| //log.debug("negotiateEnvironment()"); |
| if (isEnabled(NEWENV)) { |
| rawWrite(IAC); |
| rawWrite(SB); |
| rawWrite(NEWENV); |
| rawWrite(SEND); |
| rawWrite(NE_VAR); |
| rawWrite(NE_USERVAR); |
| rawWrite(IAC); |
| rawWrite(SE); |
| WAIT_NE_SEND_REPLY = true; |
| flush(); |
| } |
| }//negotiateEnvironment |
| |
| /** |
| * Method that skips a subnegotiation response. |
| */ |
| private void skipToSE() throws IOException { |
| while (rawread() != SE) ; |
| }//skipSubnegotiation |
| |
| private boolean readTriple(int[] triple) throws IOException { |
| triple[0] = rawread(); |
| triple[1] = rawread(); |
| if ((triple[0] == IAC) && (triple[1] == SE)) { |
| return false; |
| } else { |
| triple[2] = rawread(); |
| return true; |
| } |
| }//readTriple |
| |
| /** |
| * Method that reads a subnegotiation String, |
| * one of those that end with a IAC SE combination. |
| * A maximum length is observed to prevent overflow. |
| * |
| * @return IAC SE terminated String |
| */ |
| private String readIACSETerminatedString(int maxlength) throws IOException { |
| int where = 0; |
| char[] cbuf = new char[maxlength]; |
| char b = ' '; |
| boolean cont = true; |
| |
| do { |
| int i; |
| i = rawread(); |
| switch (i) { |
| case IAC: |
| i = rawread(); |
| if (i == SE) { |
| cont = false; |
| } |
| break; |
| case -1: |
| return "default"; |
| default: |
| } |
| if (cont) { |
| b = (char) i; |
| //Fix for overflow wimpi (10/06/2004) |
| if (b == '\n' || b == '\r' || where == maxlength) { |
| cont = false; |
| } else { |
| cbuf[where++] = b; |
| } |
| } |
| } while (cont); |
| |
| return (new String(cbuf, 0, where)); |
| }//readIACSETerminatedString |
| |
| /** |
| * Method that informs internally about the supported Negotiation Options |
| * |
| * @param i int that represents requested the Option |
| * @return Boolean that represents support status |
| */ |
| private boolean supported(int i) { |
| switch (i) { |
| case SUPGA: |
| case ECHO: |
| case NAWS: |
| case TTYPE: |
| case NEWENV: |
| return true; |
| case LINEMODE: |
| return connectionData.isLineMode(); |
| default: |
| return false; |
| } |
| }//supported |
| |
| /** |
| * Method that sends a Telnet IAC String with TelnetIO.write(byte b) method. |
| * |
| * @param i int that represents requested Command Type (DO,DONT,WILL,WONT) |
| * @param j int that represents the Option itself (e.g. ECHO, NAWS) |
| */ |
| private void sendCommand(int i, int j, boolean westarted) throws IOException { |
| rawWrite(IAC); |
| rawWrite(i); |
| rawWrite(j); |
| // we started with DO OPTION and now wait for reply |
| if ((i == DO) && westarted) setWait(DO, j, true); |
| // we started with WILL OPTION and now wait for reply |
| if ((i == WILL) && westarted) setWait(WILL, j, true); |
| flush(); |
| }//sendCommand |
| |
| /** |
| * Method enables or disables a supported Option |
| * |
| * @param i int that represents the Option |
| */ |
| private void enable(int i) throws IOException { |
| switch (i) { |
| case SUPGA: |
| DO_SUPGA = !DO_SUPGA; |
| break; |
| case ECHO: |
| DO_ECHO = !DO_ECHO; |
| break; |
| case NAWS: |
| DO_NAWS = !DO_NAWS; |
| break; |
| case TTYPE: |
| if (DO_TTYPE) { |
| DO_TTYPE = false; |
| } else { |
| DO_TTYPE = true; |
| getTTYPE(); |
| } |
| break; |
| case LINEMODE: |
| if (DO_LINEMODE) { |
| DO_LINEMODE = false; |
| //set false in connection data, so the application knows. |
| connectionData.setLineMode(false); |
| } else { |
| DO_LINEMODE = true; |
| negotiateLineMode(); |
| } |
| break; |
| case NEWENV: |
| if (DO_NEWENV) { |
| DO_NEWENV = false; |
| } else { |
| DO_NEWENV = true; |
| negotiateEnvironment(); |
| } |
| break; |
| } |
| }//enable |
| |
| /** |
| * Method that informs internally about the status of the supported |
| * Negotiation Options. |
| * |
| * @param i int that represents requested the Option |
| * @return Boolean that represents the enabled status |
| */ |
| private boolean isEnabled(int i) { |
| switch (i) { |
| case SUPGA: |
| return DO_SUPGA; |
| case ECHO: |
| return DO_ECHO; |
| case NAWS: |
| return DO_NAWS; |
| case TTYPE: |
| return DO_TTYPE; |
| case LINEMODE: |
| return DO_LINEMODE; |
| case NEWENV: |
| return DO_NEWENV; |
| default: |
| return false; |
| } |
| }//isEnabled |
| |
| /** |
| * Method that informs internally about the WILL wait status |
| * of an option. |
| * |
| * @param i that represents requested the Option |
| * @return Boolean that represents WILL wait status of the Option |
| */ |
| private boolean waitWILLreply(int i) { |
| switch (i) { |
| case SUPGA: |
| return WAIT_WILL_REPLY_SUPGA; |
| case ECHO: |
| return WAIT_WILL_REPLY_ECHO; |
| case NAWS: |
| return WAIT_WILL_REPLY_NAWS; |
| case TTYPE: |
| return WAIT_WILL_REPLY_TTYPE; |
| default: |
| return false; |
| } |
| }//waitWILLreply |
| |
| /** |
| * Method that informs internally about the DO wait status |
| * of an option. |
| * |
| * @param i Integer that represents requested the Option |
| * @return Boolean that represents DO wait status of the Option |
| */ |
| private boolean waitDOreply(int i) { |
| switch (i) { |
| case SUPGA: |
| return WAIT_DO_REPLY_SUPGA; |
| case ECHO: |
| return WAIT_DO_REPLY_ECHO; |
| case NAWS: |
| return WAIT_DO_REPLY_NAWS; |
| case TTYPE: |
| return WAIT_DO_REPLY_TTYPE; |
| case LINEMODE: |
| return WAIT_DO_REPLY_LINEMODE; |
| case NEWENV: |
| return WAIT_DO_REPLY_NEWENV; |
| default: |
| return false; |
| } |
| }//waitDOreply |
| |
| /** |
| * Method that mutates the wait status of an option in |
| * negotiation. We need the wait status to keep track of |
| * negotiation in process. So we cant miss if we started out |
| * or the other and so on. |
| * |
| * @param WHAT Integer values of DO or WILL |
| * @param OPTION Integer that represents the Option |
| * @param WAIT Boolean that represents the status of wait that should be set |
| */ |
| private void setWait(int WHAT, int OPTION, boolean WAIT) { |
| switch (WHAT) { |
| case DO: |
| switch (OPTION) { |
| case SUPGA: |
| WAIT_DO_REPLY_SUPGA = WAIT; |
| break; |
| case ECHO: |
| WAIT_DO_REPLY_ECHO = WAIT; |
| break; |
| case NAWS: |
| WAIT_DO_REPLY_NAWS = WAIT; |
| break; |
| case TTYPE: |
| WAIT_DO_REPLY_TTYPE = WAIT; |
| break; |
| case LINEMODE: |
| WAIT_DO_REPLY_LINEMODE = WAIT; |
| break; |
| case NEWENV: |
| WAIT_DO_REPLY_NEWENV = WAIT; |
| break; |
| } |
| break; |
| case WILL: |
| switch (OPTION) { |
| case SUPGA: |
| WAIT_WILL_REPLY_SUPGA = WAIT; |
| break; |
| case ECHO: |
| WAIT_WILL_REPLY_ECHO = WAIT; |
| break; |
| case NAWS: |
| WAIT_WILL_REPLY_NAWS = WAIT; |
| break; |
| case TTYPE: |
| WAIT_WILL_REPLY_TTYPE = WAIT; |
| break; |
| } |
| break; |
| } |
| }//setWait |
| |
| }//inner class IACHandler |
| |
| /** end Constants declaration **************************************************/ |
| |
| }//class TelnetIO |