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..
}
}