package net.sourceforge.pain.network.console.telnet; import net.sourceforge.pain.network.console.*; import net.sourceforge.pain.util.*; import java.io.*; import java.net.*; import java.util.*; public class TelnetConsoleAdapter extends ConsoleAdapter implements Runnable { /** * IAC - init sequence for telnet negotiation. */ private final static int IAC = 255; /** [IAC] End Of Record */ // private final static int EOR = 239; not used /** * [IAC] WILL */ private final static int WILL = 251; /** * [IAC] WONT */ private final static int WONT = 252; /** * [IAC] DO */ private final static int DO = 253; /** * [IAC] DONT */ private final static int DONT = 254; /** * [AYT] are you there? */ // private final static int AYT = 246; /** * no op */ // private final static int NOP = 241; private final byte[] IACWONT = {(byte) IAC, (byte) WONT, 0}; private final byte[] IACDONT = {(byte) IAC, (byte) DONT, 0}; // private final byte[] IACAYT = {(byte) IAC, (byte) AYT}; private TelnetConsoleServer server; Socket socket; private InputStream is; private OutputStream os; private boolean forcedClose; private boolean lostLink; private final Object monitor = new Object(); private final Object flushMonitor = new Object(); private ArrayList flushBlocks = new ArrayList(); private ArrayList flushBlocksShadow = new ArrayList(); private int currentColor = AnsiColor.COLOR_CLEAR; private StringBuffer out = new StringBuffer(); private boolean wantFlush = false; long lastFlushTime; protected TelnetConsoleAdapter(TelnetConsoleServer server, Socket socket) throws Exception { super(); this.server = server; this.socket = socket; socket.setSoTimeout(10000); is = socket.getInputStream(); os = socket.getOutputStream(); init(); } public void run() { startService(); } public void startService() { forcedClose = false; try { StringBuffer buf = new StringBuffer(); int c = 0; do { try { c = read(is); if (c > 0) { if (c == IAC) { //Interpret As Command // Log.debug("TELNET:IAC"); c = read(is); if (c != IAC) { // its really command if (c == WILL) { // if command is WILL echo DON'T c = read(is); // Log.debug("TELNET:will:" + c); IACDONT[2] = (byte) c; sendRaw((byte[]) IACDONT.clone()); // we do not want any command (do not support) } else if (c == DO) {// if command is DO echo WON'T c = read(is); // Log.debug("TELNET:do:" + c); IACWONT[2] = (byte) c; sendRaw((byte[]) IACWONT.clone()); } else { // Log.debug("TELNET: skipping command:" + c); } continue; } } if (c == '\r') { continue; } if (c == '\n') { if (buf.length() > 0) { lineReceived(buf.substring(0)); buf.delete(0, buf.length()); } else { lineReceived(""); } } else { buf.append((char) c); } } } catch (InterruptedIOException e) { break; } catch (IOException e) { if (!forcedClose) { Log.error(e.getMessage(), e); lostLink = true; } break; } catch (Exception e) { Log.error(e.getMessage(), e); } } while (c > -1 && !forcedClose && !lostLink); } catch (Exception e) { e.printStackTrace(); } finally { server.onClose(this); try { socket.close(); } catch (Exception ignored) { } if (!forcedClose) { closedRemote(); } } } private int read(InputStream is) throws Exception { if (is.available() > 0) { return is.read(); } do { synchronized (monitor) { monitor.wait(10); } if (forcedClose) { throw new IOException("forced close!"); } if (lostLink) { throw new InterruptedIOException("lost link!"); } if (wantFlush) { ArrayList blocks; synchronized (flushMonitor) { blocks = flushBlocks; flushBlocks = flushBlocksShadow; flushBlocksShadow = blocks; wantFlush = false; } int len = blocks.size(); if (len > 0) { for (int i = 0; i < len; i++) { byte[] block = (byte[]) blocks.get(i); os.write(block); } // os.write(0); lastFlushTime = System.currentTimeMillis(); os.flush(); flushBlocksShadow.clear(); } } } while (is.available() <= 0); return is.read(); } public void outText(String text) { // special symbol color parsing for (int i = 0; i < text.length(); i++) { char c = text.charAt(i); if (c == '{') { i++; if (i < text.length()) { // explicit mapping inherited from old version -> todo: int newColor = AnsiColor.toColor(text.charAt(i)); if (newColor != currentColor) { appendColorMask(newColor); currentColor = newColor; } } } else if (c == '\n') { out.append("\r\n"); } else { out.append(c); } } } private synchronized void sendRaw(byte[] data) { flushBlocks.add(data); flush(); } public void flush() { if (out.length() == 0) { wantFlush = flushBlocks.size() > 0; return; } synchronized (flushMonitor) { flushBlocks.add(out.substring(0).getBytes()); wantFlush = true; } out.delete(0, out.length()); } public void forceClose() { forcedClose = true; synchronized (monitor) { monitor.notify(); } } public void appendColorMask(int colorCode) { String color; switch (colorCode) { case AnsiColor.COLOR_CLEAR: color = "\033[m"; break; case AnsiColor.COLOR_BLACK: color = "\033[0;30m"; break; case AnsiColor.COLOR_RED: color = "\033[0;31m"; break; case AnsiColor.COLOR_GREEN: color = "\033[0;32m"; break; case AnsiColor.COLOR_YELLOW: color = "\033[0;33m"; break; case AnsiColor.COLOR_BLUE: color = "\033[0;34m"; break; case AnsiColor.COLOR_MAGENTA: color = "\033[0;35m"; break; case AnsiColor.COLOR_CYAN: color = "\033[0;36m"; break; case AnsiColor.COLOR_WHITE: color = "\033[0;37m"; break; // brighter case AnsiColor.COLOR_DARK_GREY: color = "\033[1;30m"; break; case AnsiColor.COLOR_BRIGHT_RED: color = "\033[1;31m"; break; case AnsiColor.COLOR_BRIGHT_GREEN: color = "\033[1;32m"; break; case AnsiColor.COLOR_BRIGHT_YELLOW: color = "\033[1;33m"; break; case AnsiColor.COLOR_BRIGHT_BLUE: color = "\033[1;34m"; break; case AnsiColor.COLOR_BRIGHT_MAGENTA: color = "\033[1;35m"; break; case AnsiColor.COLOR_BRIGHT_CYAN: color = "\033[1;36m"; break; case AnsiColor.COLOR_BRIGHT_WHITE: color = "\033[1;37m"; break; // special case AnsiColor.COLOR_BEEP: color = "\007"; break; default: Log.warn("Unknown color:" + colorCode); return; } currentColor = colorCode; out.append(color); } protected void ping() { // sendRaw(IACAYT); sendRaw(new byte[]{0});//windows telnet does not like IACs.. } }