package systemobject.terminal;
/* License
*
* Copyright 1994-2004 Sun Microsystems, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistribution 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.
*
* Neither the name of Sun Microsystems, Inc. or the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
* ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN")
* AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
* AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
* REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
* INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
* OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
* EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or intended
* for use in the design, construction, operation or maintenance of any
* nuclear facility.
*/
//package net.mpowers.telnet;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
/**
* A minimal telnet protocol filter that handles only
* TERMINAL_TYPE and NAWS (window size) options.
*/
public class TelnetInputStream extends InOutInputStream
{
private OutputStream output;
private int width, height;
private String terminal;
// used for replies
private final byte[] reply = { IAC, (byte) 0, (byte) 0 };
/**
* Constructor specifying the input stream to filter,
* and the output stream with which the telnet protocol
* is negotiated.
*/
public TelnetInputStream(
InputStream inInput, OutputStream inOutput )
{
this( inInput, inOutput, 0, 0, null );
}
/**
* Constructor specifying the input stream to filter,
* the output stream with which the telnet protocol
* is negotiated, and terminal emulation settings.
*/
public TelnetInputStream(
InputStream inInput, OutputStream inOutput,
int inWidth, int inHeight, String inTermType )
{
setInputStream(inInput);
output = inOutput;
width = inWidth;
height = inHeight;
terminal = inTermType;
if ( terminal == null ) terminal = "dumb";
}
public int read() throws IOException
{
byte b;
b = (byte) in.read();
if ( b != IAC ) return b; // not an IAC, skip.
b = (byte) in.read();
if ( b == IAC ) return b; // two IACs isn't.
if ( b != SB ) // handle command
{
switch ( b )
{
// basic commands
case GA:
case NOP:
case DAT:
case BRK:
case IP:
case AO:
case AYT:
case EC:
case EL:
// not implemented: ignore for now
/*System.err.println(
"Ignored command: " + b + " : " + reply[2] );*/
if(available() > 0){
return read();
} else {
return -1;
}
// option prefixes
case DO:
case DONT:
case WILL:
case WONT:
// read next byte to determine option
reply[2] = (byte) in.read();
switch ( reply[2] )
{
case TERMINAL_TYPE:
// do allow terminal type subnegotiation
if ( b == DO )
{
reply[1] = WILL;
write( reply );
break;
}
case WINDOW_SIZE:
// do allow and reply with window size
if ( b == DO && width > 0 && height > 0 )
{
reply[1] = WILL;
write( reply );
reply[1] = SB;
write( reply );
byte[] bytes = new byte[6];
bytes[0] = (byte) (width >> 8);
bytes[1] = (byte) (width & 0xff);
bytes[2] = (byte) (height >> 8);
bytes[3] = (byte) (height & 0xff);
bytes[4] = IAC;
bytes[5] = SE;
write( bytes );
break;
}
default:
// unsupported option: refuse and break
/*System.err.println(
"Unsupported option: " + b + " : " + reply[2] );*/
reply[1] = WONT;
if ( b == WILL )
{
//reply[1] = DONT;
reply[1] = DO;
}
write( reply );
break;
}
break;
default:
// unsupported option: suppress and exit
//System.err.println( "Unsupported command: " + b );
}
}
else // handle begin-sub
{
b = (byte) in.read();
reply[2] = b;
switch ( b )
{
case TERMINAL_TYPE:
if ( (b = (byte) in.read()) != TERMINAL_SEND ) return b;
if ( (b = (byte) in.read()) != IAC ) return b;
if ( (b = (byte) in.read()) != SE ) return b;
reply[1] = SB;
write( reply );
char[] c = terminal.toCharArray();
byte[] bytes = new byte[c.length+3];
int i = 0;
bytes[i++] = TERMINAL_IS;
for ( ; i < c.length+1; i++ ) bytes[i] = (byte) c[i-1];
bytes[i++] = IAC;
bytes[i++] = SE;
write( bytes );
break;
default:
reply[1] = WONT;
write( reply );
}
}
if(available() > 0){
return read();
} else {
return -1;
}
}
public void close() throws IOException
{
in.close();
}
private void write( byte inByte ) throws IOException
{
output.write( inByte );
output.flush();
}
private void write( byte[] inBytes ) throws IOException
{
output.write( inBytes );
output.flush();
}
public int available() throws IOException {
return in.available();
}
// iac commands
private final static byte SE = (byte) 240; // -16
private final static byte NOP = (byte) 241;
private final static byte DAT = (byte) 242;
private final static byte BRK = (byte) 243;
private final static byte IP = (byte) 244;
private final static byte AO = (byte) 245;
private final static byte AYT = (byte) 246;
private final static byte EC = (byte) 247;
private final static byte EL = (byte) 248;
private final static byte GA = (byte) 249;
private final static byte SB = (byte) 250; // -6
private final static byte WILL = (byte) 251; // -5
private final static byte WONT = (byte) 252; // -4
private final static byte DO = (byte) 253; // -3
private final static byte DONT = (byte) 254; // -2
private final static byte IAC = (byte) 255; // -1
// options
private final static byte TRANSMIT_BINARY = (byte) 0;
private final static byte ECHO = (byte) 1;
private final static byte SUPPRESS_GO_AHEAD = (byte) 3;
private final static byte STATUS = (byte) 5;
private final static byte TIMING_MARK = (byte) 6;
private final static byte TERMINAL_TYPE = (byte) 24;
private final static byte END_OF_RECORD = (byte) 25;
private final static byte WINDOW_SIZE = (byte) 31;
// used with END_OF_RECORD
private final static byte EOR = (byte) 239;
// used with TERMINAL_TYPE
private final static byte TERMINAL_IS = (byte) 0;
private final static byte TERMINAL_SEND = (byte) 1;
}