Added: felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/Telnet.java URL: http://svn.apache.org/viewvc/felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/Telnet.java?rev=1735995&view=auto ============================================================================== --- felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/Telnet.java (added) +++ felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/Telnet.java Mon Mar 21 16:53:06 2016 @@ -0,0 +1,202 @@ +/* + * 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. + */ +package org.apache.felix.gogo.jline.telnet; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.List; + +import org.apache.felix.gogo.jline.Shell; +import org.apache.felix.gogo.jline.Shell.Context; +import org.apache.felix.service.command.CommandProcessor; +import org.apache.felix.service.command.CommandSession; +import org.jline.builtins.Options; +import org.jline.terminal.Size; +import org.jline.terminal.Terminal; +import org.jline.terminal.Terminal.Signal; +import org.jline.terminal.TerminalBuilder; + +/* + * a very simple Telnet server. + * real remote access should be via ssh. + */ +public class Telnet { + public static final String[] functions = {"telnetd"}; + + private static final int defaultPort = 2019; + private final CommandProcessor processor; + private PortListener portListener; + private int port; + private String ip; + + public Telnet(CommandProcessor procesor) { + this.processor = procesor; + } + + public void telnetd(CommandSession session, String[] argv) throws IOException { + final String[] usage = {"telnetd - start simple telnet server", + "Usage: telnetd [-i ip] [-p port] start | stop | status", + " -i --ip=INTERFACE listen interface (default=127.0.0.1)", + " -p --port=PORT listen port (default=" + defaultPort + ")", + " -? --help show help"}; + + Options opt = Options.compile(usage).parse(argv); + List<String> args = opt.args(); + + if (opt.isSet("help") || args.isEmpty()) { + opt.usage(System.err); + return; + } + + String command = args.get(0); + + if ("start".equals(command)) { + if (portListener != null) { + throw new IllegalStateException("telnetd is already running on port " + port); + } + ip = opt.get("ip"); + port = opt.getNumber("port"); + start(session); + status(); + } else if ("stop".equals(command)) { + if (portListener == null) { + throw new IllegalStateException("telnetd is not running."); + } + stop(); + } else if ("status".equals(command)) { + status(); + } else { + throw opt.usageError("bad command: " + command); + } + } + + private void status() { + if (portListener != null) { + System.out.println("telnetd is running on " + ip + ":" + port); + } else { + System.out.println("telnetd is not running."); + } + } + + private void start(CommandSession session) throws IOException { + ConnectionManager connectionManager = new ConnectionManager(1000, 5 * 60 * 1000, 5 * 60 * 1000, 60 * 1000, null, null, false) { + @Override + protected Connection createConnection(ThreadGroup threadGroup, ConnectionData newCD) { + return new Connection(threadGroup, newCD) { + TelnetIO telnetIO; + + @Override + protected void doRun() throws Exception { + telnetIO = new TelnetIO(); + telnetIO.setConnection(this); + telnetIO.initIO(); + + InputStream in = new InputStream() { + @Override + public int read() throws IOException { + return telnetIO.read(); + } + @Override + public int read(byte[] b, int off, int len) throws IOException { + int r = read(); + if (r >= 0) { + b[off] = (byte) r; + return 1; + } else { + return -1; + } + } + }; + PrintStream out = new PrintStream(new OutputStream() { + @Override + public void write(int b) throws IOException { + telnetIO.write(b); + } + @Override + public void flush() throws IOException { + telnetIO.flush(); + } + }); + Terminal terminal = TerminalBuilder.builder() + .type(getConnectionData().getNegotiatedTerminalType().toLowerCase()) + .streams(in, out) + .system(false) + .name("telnet") + .build(); + terminal.setSize(new Size(getConnectionData().getTerminalColumns(), getConnectionData().getTerminalRows())); + terminal.setAttributes(Shell.getTerminal(session).getAttributes()); + addConnectionListener(new ConnectionListener() { + @Override + public void connectionIdle(ConnectionEvent ce) { + } + + @Override + public void connectionTimedOut(ConnectionEvent ce) { + } + + @Override + public void connectionLogoutRequest(ConnectionEvent ce) { + } + + @Override + public void connectionSentBreak(ConnectionEvent ce) { + } + + @Override + public void connectionTerminalGeometryChanged(ConnectionEvent ce) { + terminal.setSize(new Size(getConnectionData().getTerminalColumns(), getConnectionData().getTerminalRows())); + terminal.raise(Signal.WINCH); + } + }); + PrintStream pout = new PrintStream(terminal.output()); + CommandSession session = processor.createSession(terminal.input(), pout, pout); + Context context = new Context() { + @Override + public String getProperty(String name) { + return System.getProperty(name); + } + @Override + public void exit() throws Exception { + close(); + } + }; + new Shell(context, processor, terminal).gosh(session, new String[]{"--login"}); + } + + @Override + protected void doClose() throws Exception { + telnetIO.closeOutput(); + telnetIO.closeInput(); + } + }; + } + }; + portListener = new PortListener("gogo", port, 10); + portListener.setConnectionManager(connectionManager); + portListener.start(); + } + + private void stop() throws IOException { + portListener.stop(); + portListener = null; + } + +}
Added: felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/TelnetIO.java URL: http://svn.apache.org/viewvc/felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/TelnetIO.java?rev=1735995&view=auto ============================================================================== --- felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/TelnetIO.java (added) +++ felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/TelnetIO.java Mon Mar 21 16:53:06 2016 @@ -0,0 +1,1548 @@ +/* + * 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 + 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); + + if (b == 13) { + crFlag = true; + } else { + crFlag = false; + } + }//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; + 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 (new String("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: + if (DO_SUPGA) { + DO_SUPGA = false; + } else { + DO_SUPGA = true; + } + break; + case ECHO: + if (DO_ECHO) { + DO_ECHO = false; + } else { + DO_ECHO = true; + } + break; + case NAWS: + if (DO_NAWS) { + DO_NAWS = false; + } else { + DO_NAWS = true; + } + 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 Added: felix/trunk/gogo/jline/src/main/resources/gosh_profile URL: http://svn.apache.org/viewvc/felix/trunk/gogo/jline/src/main/resources/gosh_profile?rev=1735995&view=auto ============================================================================== --- felix/trunk/gogo/jline/src/main/resources/gosh_profile (added) +++ felix/trunk/gogo/jline/src/main/resources/gosh_profile Mon Mar 21 16:53:06 2016 @@ -0,0 +1,247 @@ +# +# 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. +# +# default gosh_profile +# only read if etc/gosh_profile doesn't exist relative to the System property +# gosh.home or failing that the current directory. + +# catch all exceptions from this script to avoid it aborting startup +try { + + # ensure gogo commands are found first + SCOPE = gogo:* + + # add methods on BundleContext object as commands + addcommand context ${.context} + + # add methods on System object as commands + #addcommand system (((${.context} getClass) getClassLoader) loadClass java.lang.System) + + # alias to print full stack trace + e = { $exception printStackTrace } + + ## disable console auto-formatting of each result + # you will then need to explicitly use the 'format' command + # to print the result of commands that don't write to stdout. + #.Gogo.format = false + + ## disable printing the formatted result of a command into pipelines + #.Format.Pipe = false + + # set prompt + prompt = 'g! ' + \#rprompt = { (new java.text.SimpleDateFormat "HH:mm:ss") format (new Date) } + + + __option_not_present = { + res = true + opts = ([ $args ] get 0) + each $opts { + arg = $it + each ($.commandLine words) { + if { ($it toString) contentEquals ($arg toString) } { + res = false + } + } + } + $res + } + + __load_class = { + (($.reader class) classLoader) loadClass $1 + } + + __as_list = { + (__load_class java.util.Arrays) asList $1 + } + + __set_unset_arguments = { + is_setopt = (($.commandLine words) get 0) equals "setopt" + enums = ((__load_class 'org.jline.ConsoleReader$Option') enumConstants) + candidates = new ArrayList + each (__as_list $enums) { + name = ((($it name) toLowerCase) replace '_' '-') + is_set = ($.reader isSet $it) + neg = %(( if(is_setopt, is_set, not(is_set)) )) + if { $neg } { + name = "no-${name}" + } + if { not { (($.commandLine words) subList 1 ($.commandLine wordIndex)) contains $name } } { + $candidates add (new org.jline.Candidate $name $name (if { $neg } { "unset" } { "set" }) null null null true) + } + } + $candidates + } + + setopt pad-prompts + setopt group + + complete -c gogo:complete -e + complete -c gogo:complete -d "Edit command specific completions" + complete -c gogo:complete -s c -l command --description "Command to add completion to" -n '__option_not_present -c --command' -a '$.commands' + complete -c gogo:complete -s s -l short-option --description "Posix-style option to complete" -n '__option_not_present -s --short-option' + complete -c gogo:complete -s l -l long-option --description "GNU-style option to complete" -n '__option_not_present -l --long-option' + complete -c gogo:complete -s a -l arguments --description "A list of possible arguments" -n '__option_not_present -a --argument' + complete -c gogo:complete -s d -l description --description "Description of this completions" -n '__option_not_present -d --description' + complete -c gogo:complete -s h -l help --description "Display help and exit" -n '__option_not_present -h --help' + complete -c gogo:complete -s n -l condition --description "The completion should only be used if the specified command has a zero exit status" -n '__option_not_present -n --condition' + complete -c gogo:complete -s e -l erase --description "Remove completion" -n '__option_not_present -e --erase' + + complete -c gogo:history -e + complete -c gogo:history -d "Show and manipulate command history" + complete -c gogo:history -l clear --description "Clear history" -n '__option_not_present --clear' + complete -c gogo:history -l save --description "Save history" -n '__option_not_present --save' + + complete -c gogo:setopt -e + complete -c gogo:setopt -d "Set or view set shell options" + complete -c gogo:setopt -a '__set_unset_arguments' + + complete -c gogo:unsetopt -e + complete -c gogo:unsetopt -d "Unset or view unset shell options" + complete -c gogo:unsetopt -a '__set_unset_arguments' + + complete -c gogo:cat -e + complete -c gogo:cat -d "Concatenate and print files" + complete -c gogo:cat -s n "Number the output lines, starting at 1" + complete -c gogo:cat -a '__files' + + complete -c gogo:pwd -e + complete -c gogo:pwd -d "Get current directory" + + complete -c gogo:ls -e + complete -c gogo:ls -d "List files" + + complete -c gogo:cd -e + complete -c gogo:cd -d "Change current directory" + complete -c gogo:cd -a '__directories' + + complete -c gogo:sleep -e + complete -c gogo:sleep -d "Pause execution for the specified amount of time" + + complete -c gogo:echo -e + complete -c gogo:echo -d "Write arguments to the standard output" + complete -c gogo:echo -s n -d "No trailing new line" + + complete -c gogo:grep -e + complete -c gogo:grep -d "File pattern searcher" + # TODO + + complete -c gogo:sort -e + complete -c gogo:sort -d "Sort lines of text files" + # TODO + + complete -c gogo:gosh -e + complete -c gogo:gosh -d "Execute script with arguments in a new session" + # TODO + + complete -c gogo:sh -e + complete -c gogo:sh -d "Execute script with arguments in a new session" + # TODO + + complete -c gogo:source -e + complete -c gogo:source -d "Execute script with arguments" + # TODO + + # TODO: format getopt new set tac type addcommand removeCommand eval + + complete -c gogo:each -e + complete -c gogo:each -d "Loop and execute script on the specified elements" + + complete -c gogo:if -e + complete -c gogo:if -d "Conditionaly execute a script" + + complete -c gogo:not -e + complete -c gogo:not -d "Negates the result of a script" + + complete -c gogo:throw -e + complete -c gogo:throw -d "Throws an exception" + + complete -c gogo:try -e + complete -c gogo:try -d "Try executing a script and catch any exception" + + complete -c gogo:until -e + complete -c gogo:until -d "Loop and execute script until a condition is satisfied" + + complete -c gogo:while -e + complete -c gogo:while -d "Loop and execute script while a condition is satisfied" + + complete -c gogo:less -e + complete -c gogo:less -d "File pager" + complete -c gogo:less -s e -l quit-at-eof --description "Exit on second EOF" + complete -c gogo:less -s E -l QUIT-AT-EOF --description "Exit on EOF" + complete -c gogo:less -s q -l quiet -l silent --description "Silent mode" + complete -c gogo:less -s Q -l QUIET -l SILENT --description "Completely silent" + complete -c gogo:less -s S -l chop-long-lines --description "Do not fold long lines" + complete -c gogo:less -s i -l ignore-case --description "Search ignores lowercase case" + complete -c gogo:less -s I -l IGNORE-CASE --description "Search ignores all case" + complete -c gogo:less -s x -l tabs --description "Set tab stops" + complete -c gogo:less -s N -l LINE-NUMBERS --description "Display line number for each line" + complete -c gogo:less -a '__files' + + complete -c gogo:nano -e + complete -c gogo:nano -d "File editor" + complete -c gogo:nano -a '__files' + + complete -c gogo:keymap -e + complete -c gogo:keymap -d "Manipulate keymaps" + complete -c gogo:keymap -s N --description "Create a new keymap" -n '__option_not_present -N -d -D -l -r -s -A' + complete -c gogo:keymap -s d --description "Delete existing keymaps and reset to default state" -n '__option_not_present -N -d -D -l -r -s -A' + complete -c gogo:keymap -s D --description "Delete named keymaps" -n '__option_not_present -N -d -D -l -r -s -A' + complete -c gogo:keymap -s l --description "List existing keymap names" -n '__option_not_present -N -d -D -l -r -s -A' + complete -c gogo:keymap -s r --description "Unbind specified in-strings" -n '__option_not_present -N -d -D -l -r -s -A' + complete -c gogo:keymap -s s --description "Bind each in-string to each out-string" -n '__option_not_present -N -d -D -l -r -s -A' + complete -c gogo:keymap -s A --description "Create alias to keymap" -n '__option_not_present -N -d -D -l -r -s -A' + complete -c gogo:keymap -s e --description "Select emacs keymap and bind it to main" -n '__option_not_present -e -a -v -M' + complete -c gogo:keymap -s v --description "Select viins keymap and bind it to main" -n '__option_not_present -e -a -v -M' + complete -c gogo:keymap -s a --description "Select vicmd keymap" -n '__option_not_present -e -a -v -M' + complete -c gogo:keymap -s M --description "Specify keymap to select" -n '__option_not_present -e -a -v -M' -a '(keymap -l | tac) split " "' + complete -c gogo:keymap -s R --description "Interpret in-strings as ranges" + complete -c gogo:keymap -s p --description "List bindings which have given key sequence as a a prefix" + complete -c gogo:keymap -s L --description "Output in form of keymap commands" + + complete -c gogo:widget -e + complete -c gogo:widget -d "Manipulate widgets" + complete -c gogo:widget -s N --description "Create a new widget" -n '__option_not_present -N -A -D -U -l' + complete -c gogo:widget -s A --description "Create alias to widget" -n '__option_not_present -N -A -D -U -l' + complete -c gogo:widget -s D --description "Delete widgets" -n '__option_not_present -N -A -D -U -l' + complete -c gogo:widget -s U --description "Push characters to the stack" -n '__option_not_present -N -A -D -U -l' + complete -c gogo:widget -s l --description "List user-defined widgets" -n '__option_not_present -N -A -D -U -l' + complete -c gogo:widget -s a --description "With -l, list all widgets" -n '__option_not_present -l' + + complete -c gogo:telnetd -e + complete -c gogo:telnetd -d "Telnet daemon" + complete -c gogo:telnetd -s i -l ip --description "Listening IP interface" -n '__option_not_present -i --ip' + complete -c gogo:telnetd -s p -l port --description "Listening IP port" -n '__option_not_present -p --port' + complete -c gogo:telnetd -a '[start stop status]' + + complete -c gogo:sshd -e + complete -c gogo:sshd -d "SSH daemon" + complete -c gogo:sshd -s i -l ip --description "Listening IP interface" -n '__option_not_present -i --ip' + complete -c gogo:sshd -s p -l port --description "Listening IP port" -n '__option_not_present -p --port' + complete -c gogo:sshd -a '[start stop status]' + + complete -c gogo:tmux -e + complete -c gogo:tmux -d "Terminal multiplexer" + + # print welcome message + cat ($0 resolve motd) +} { + echo "$0: ERROR: $exception" +} + +# end Added: felix/trunk/gogo/jline/src/main/resources/motd URL: http://svn.apache.org/viewvc/felix/trunk/gogo/jline/src/main/resources/motd?rev=1735995&view=auto ============================================================================== --- felix/trunk/gogo/jline/src/main/resources/motd (added) +++ felix/trunk/gogo/jline/src/main/resources/motd Mon Mar 21 16:53:06 2016 @@ -0,0 +1,3 @@ +____________________________ +Welcome to Apache Felix Gogo + Modified: felix/trunk/gogo/pom.xml URL: http://svn.apache.org/viewvc/felix/trunk/gogo/pom.xml?rev=1735995&r1=1735994&r2=1735995&view=diff ============================================================================== --- felix/trunk/gogo/pom.xml (original) +++ felix/trunk/gogo/pom.xml Mon Mar 21 16:53:06 2016 @@ -40,6 +40,7 @@ <modules> <module>gogo-parent</module> <module>runtime</module> + <module>jline</module> <module>shell</module> <module>command</module> </modules>
