You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by gn...@apache.org on 2016/03/21 17:53:07 UTC
svn commit: r1735995 [4/4] - in /felix/trunk/gogo: ./ jline/ jline/doc/
jline/src/ jline/src/main/ jline/src/main/java/ jline/src/main/java/org/
jline/src/main/java/org/apache/ jline/src/main/java/org/apache/felix/
jline/src/main/java/org/apache/felix/...
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>