diff src/de/mud/telnet/TelnetProtocolHandler.java @ 0:0ce5cc452d02

initial version
author Carl Byington <carl@five-ten-sg.com>
date Thu, 22 May 2014 10:41:19 -0700
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/de/mud/telnet/TelnetProtocolHandler.java	Thu May 22 10:41:19 2014 -0700
@@ -0,0 +1,789 @@
+/*
+ * This file is part of "JTA - Telnet/SSH for the JAVA(tm) platform".
+ *
+ * (c) Matthias L. Jugel, Marcus Meißner 1996-2005. All Rights Reserved.
+ *
+ * Please visit http://javatelnet.org/ for updates and contact.
+ *
+ * --LICENSE NOTICE--
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * --LICENSE NOTICE--
+ *
+ */
+
+package de.mud.telnet;
+
+import java.io.IOException;
+/**
+ * This is a telnet protocol handler. The handler needs implementations
+ * for several methods to handle the telnet options and to be able to
+ * read and write the buffer.
+ * <P>
+ * <B>Maintainer:</B> Marcus Meissner
+ *
+ * @version $Id: TelnetProtocolHandler.java 503 2005-10-24 07:34:13Z marcus $
+ * @author  Matthias L. Jugel, Marcus Meissner
+ */
+public abstract class TelnetProtocolHandler {
+    /** contains the current revision id */
+    public final static String ID = "$Id: TelnetProtocolHandler.java 503 2005-10-24 07:34:13Z marcus $";
+
+    /** debug level */
+    private final static int debug = 0;
+
+    /** temporary buffer for data-telnetstuff-data transformation */
+    private byte[] tempbuf = new byte[0];
+
+    /** the data sent on pressing <RETURN>  \n */
+    private byte[] crlf = new byte[2];
+    /** the data sent on pressing <LineFeed>  \r */
+    private byte[] cr = new byte[2];
+
+    /**
+     * Create a new telnet protocol handler.
+     */
+    public TelnetProtocolHandler() {
+        reset();
+        crlf[0] = 13; crlf[1] = 10;
+        cr[0] = 13; cr[1] = 0;
+    }
+
+    /**
+     * Get the current terminal type for TTYPE telnet option.
+     * @return the string id of the terminal
+     */
+    protected abstract String getTerminalType();
+
+    /**
+     * Get the current window size of the terminal for the
+     * NAWS telnet option.
+     * @return the size of the terminal as Dimension
+     */
+    protected abstract int[] getWindowSize();
+
+    /**
+     * Set the local echo option of telnet.
+     * @param echo true for local echo, false for no local echo
+     */
+    protected abstract void setLocalEcho(boolean echo);
+
+    /**
+     * Generate an EOR (end of record) request. For use by prompt displaying.
+     */
+    protected abstract void notifyEndOfRecord();
+
+    /**
+     * Send data to the remote host.
+     * @param b array of bytes to send
+     */
+    protected abstract void write(byte[] b) throws IOException;
+
+    /**
+     * Read the charset name from terminal.
+     */
+    protected abstract String getCharsetName();
+
+    /**
+     * Send one byte to the remote host.
+     * @param b the byte to be sent
+     * @see #write(byte[] b)
+     */
+    private static byte[] one = new byte[1];
+    private void write(byte b) throws IOException {
+        one[0] = b;
+        write(one);
+    }
+
+    /**
+     * Reset the protocol handler. This may be necessary after the
+     * connection was closed or some other problem occured.
+     */
+    public void reset() {
+        neg_state = 0;
+        receivedDX = new byte[256];
+        sentDX = new byte[256];
+        receivedWX = new byte[256];
+        sentWX = new byte[256];
+    }
+
+    // ===================================================================
+    // the actual negotiation handling for the telnet protocol follows:
+    // ===================================================================
+
+    /** state variable for telnet negotiation reader */
+    private byte neg_state = 0;
+
+    /** constants for the negotiation state */
+    private final static byte STATE_DATA  = 0;
+    private final static byte STATE_IAC   = 1;
+    private final static byte STATE_IACSB = 2;
+    private final static byte STATE_IACWILL       = 3;
+    private final static byte STATE_IACDO = 4;
+    private final static byte STATE_IACWONT       = 5;
+    private final static byte STATE_IACDONT       = 6;
+    private final static byte STATE_IACSBIAC      = 7;
+    private final static byte STATE_IACSBDATA     = 8;
+    private final static byte STATE_IACSBDATAIAC  = 9;
+
+    /** What IAC SB <xx> we are handling right now */
+    private byte current_sb;
+
+    /** current SB negotiation buffer */
+    private byte[] sbbuf;
+
+    /** IAC - init sequence for telnet negotiation. */
+    private final static byte IAC  = (byte)255;
+    /** [IAC] End Of Record */
+    private final static byte EOR  = (byte)239;
+    /** [IAC] WILL */
+    private final static byte WILL  = (byte)251;
+    /** [IAC] WONT */
+    private final static byte WONT  = (byte)252;
+    /** [IAC] DO */
+    private final static byte DO    = (byte)253;
+    /** [IAC] DONT */
+    private final static byte DONT  = (byte)254;
+    /** [IAC] Sub Begin */
+    private final static byte SB  = (byte)250;
+    /** [IAC] Sub End */
+    private final static byte SE  = (byte)240;
+    /** Telnet option: binary mode */
+    private final static byte TELOPT_BINARY = (byte)0; /* binary mode */
+    /** Telnet option: echo text */
+    private final static byte TELOPT_ECHO  = (byte)1;  /* echo on/off */
+    /** Telnet option: sga */
+    private final static byte TELOPT_SGA   = (byte)3;  /* supress go ahead */
+    /** Telnet option: End Of Record */
+    private final static byte TELOPT_EOR   = (byte)25;  /* end of record */
+    /** Telnet option: Negotiate About Window Size */
+    private final static byte TELOPT_NAWS  = (byte)31;  /* NA-WindowSize*/
+    /** Telnet option: Terminal Type */
+    private final static byte TELOPT_TTYPE  = (byte)24;  /* terminal type */
+    /** Telnet option: CHARSET */
+    private final static byte TELOPT_CHARSET = (byte)42; /* charset */
+
+    private final static byte[] IACWILL  = { IAC, WILL };
+    private final static byte[] IACWONT  = { IAC, WONT };
+    private final static byte[] IACDO    = { IAC, DO      };
+    private final static byte[] IACDONT  = { IAC, DONT };
+    private final static byte[] IACSB  = { IAC, SB };
+    private final static byte[] IACSE  = { IAC, SE };
+
+    private final static byte CHARSET_ACCEPTED = (byte)2;
+    private final static byte CHARSET_REJECTED = (byte)3;
+
+    /** Telnet option qualifier 'IS' */
+    private final static byte TELQUAL_IS = (byte)0;
+    /** Telnet option qualifier 'SEND' */
+    private final static byte TELQUAL_SEND = (byte)1;
+
+    /** What IAC DO(NT) request do we have received already ? */
+    private byte[] receivedDX;
+    /** What IAC WILL/WONT request do we have received already ? */
+    private byte[] receivedWX;
+    /** What IAC DO/DONT request do we have sent already ? */
+    private byte[] sentDX;
+    /** What IAC WILL/WONT request do we have sent already ? */
+    private byte[] sentWX;
+
+    /**
+     * Send a Telnet Escape character (IAC <code>)
+     */
+    public void sendTelnetControl(byte code)
+    throws IOException {
+        byte[] b = new byte[2];
+        b[0] = IAC;
+        b[1] = code;
+        write(b);
+    }
+
+    /**
+     * Send the new Window Size (via NAWS)
+     */
+    public void setWindowSize(int columns, int rows)
+    throws IOException {
+        if (debug > 2) System.err.println("sending NAWS");
+
+        if (receivedDX[TELOPT_NAWS] != DO) {
+            System.err.println("not allowed to send NAWS? (DONT NAWS)");
+            return;
+        }
+
+        write(IAC); write(SB); write(TELOPT_NAWS);
+        write((byte)(columns >> 8));
+        write((byte)(columns & 0xff));
+        write((byte)(rows >> 8));
+        write((byte)(rows & 0xff));
+        write(IAC); write(SE);
+    }
+
+
+    /**
+     * Handle an incoming IAC SB &lt;type&gt; &lt;bytes&gt; IAC SE
+     * @param type type of SB
+     * @param sbata byte array as &lt;bytes&gt;
+     */
+    private void handle_sb(byte type, byte[] sbdata)
+    throws IOException {
+        if (debug > 1)
+            System.err.println("TelnetIO.handle_sb(" + type + ")");
+
+        switch (type) {
+            case TELOPT_TTYPE:
+                if (sbdata.length > 0 && sbdata[0] == TELQUAL_SEND) {
+                    write(IACSB); write(TELOPT_TTYPE); write(TELQUAL_IS);
+                    /* FIXME: need more logic here if we use
+                     * more than one terminal type
+                     */
+                    String ttype = getTerminalType();
+
+                    if (ttype == null) ttype = "dumb";
+
+                    write(ttype.getBytes());
+                    write(IACSE);
+                }
+
+                break;
+
+            case TELOPT_CHARSET:
+                System.out.println("Got SB CHARSET");
+                String charsetStr = new String(sbdata, "US-ASCII");
+
+                if (charsetStr.startsWith("TTABLE ")) {
+                    charsetStr = charsetStr.substring(7);
+                }
+
+                String[] charsets = charsetStr.split(charsetStr.substring(0, 0));
+                String myCharset = getCharsetName();
+
+                for (String charset : charsets) {
+                    if (charset.equals(myCharset)) {
+                        write(IACSB); write(TELOPT_CHARSET); write(CHARSET_ACCEPTED);
+                        write(charset.getBytes());
+                        write(IACSE);
+                        System.out.println("Sent our charset!");
+                        return;
+                    }
+                }
+
+                write(IACSB); write(TELOPT_CHARSET); write(CHARSET_REJECTED);
+                write(IACSE);
+                break;
+        }
+    }
+
+    /**
+     * Do not send any notifications at startup. We do not know,
+     * whether the remote client understands telnet protocol handling,
+     * so we are silent.
+     * (This used to send IAC WILL SGA, but this is false for a compliant
+     *  client.)
+     */
+    public void startup() throws IOException {
+    }
+    /**
+     * Transpose special telnet codes like 0xff or newlines to values
+     * that are compliant to the protocol. This method will also send
+     * the buffer immediately after transposing the data.
+     * @param buf the data buffer to be sent
+     */
+    public void transpose(byte[] buf) throws IOException {
+        int i;
+        byte[] nbuf, xbuf;
+        int nbufptr = 0;
+        nbuf = new byte[buf.length * 2]; // FIXME: buffer overflows possible
+
+        for (i = 0; i < buf.length ; i++) {
+            switch (buf[i]) {
+                // Escape IAC twice in stream ... to be telnet protocol compliant
+                // this is there in binary and non-binary mode.
+                case IAC:
+                    nbuf[nbufptr++] = IAC;
+                    nbuf[nbufptr++] = IAC;
+                    break;
+
+                // We need to heed RFC 854. LF (\n) is 10, CR (\r) is 13
+                // we assume that the Terminal sends \n for lf+cr and \r for just cr
+                // linefeed+carriage return is CR LF */
+                case 10:  // \n
+                    if (receivedDX[TELOPT_BINARY + 128 ] != DO) {
+                        while (nbuf.length - nbufptr < crlf.length) {
+                            xbuf = new byte[nbuf.length * 2];
+                            System.arraycopy(nbuf, 0, xbuf, 0, nbufptr);
+                            nbuf = xbuf;
+                        }
+
+                        for (int j = 0; j < crlf.length; j++)
+                            nbuf[nbufptr++] = crlf[j];
+
+                        break;
+                    }
+                    else {
+                        // copy verbatim in binary mode.
+                        nbuf[nbufptr++] = buf[i];
+                    }
+
+                    break;
+
+                // carriage return is CR NUL */
+                case 13:  // \r
+                    if (receivedDX[TELOPT_BINARY + 128 ] != DO) {
+                        while (nbuf.length - nbufptr < cr.length) {
+                            xbuf = new byte[nbuf.length * 2];
+                            System.arraycopy(nbuf, 0, xbuf, 0, nbufptr);
+                            nbuf = xbuf;
+                        }
+
+                        for (int j = 0; j < cr.length; j++)
+                            nbuf[nbufptr++] = cr[j];
+                    }
+                    else {
+                        // copy verbatim in binary mode.
+                        nbuf[nbufptr++] = buf[i];
+                    }
+
+                    break;
+
+                // all other characters are just copied
+                default:
+                    nbuf[nbufptr++] = buf[i];
+                    break;
+            }
+        }
+
+        xbuf = new byte[nbufptr];
+        System.arraycopy(nbuf, 0, xbuf, 0, nbufptr);
+        write(xbuf);
+    }
+
+    public void setCRLF(String xcrlf) { crlf = xcrlf.getBytes(); }
+    public void setCR(String xcr) { cr = xcr.getBytes(); }
+
+    /**
+     * Handle telnet protocol negotiation. The buffer will be parsed
+     * and necessary actions are taken according to the telnet protocol.
+     * See <A HREF="RFC-Telnet-URL">RFC-Telnet</A>
+     * @param nbuf the byte buffer put out after negotiation
+     * @return number of bytes processed, 0 for none, and -1 for end of buffer.
+     */
+    public int negotiate(byte nbuf[], int offset)
+    throws IOException {
+        int count = tempbuf.length;
+        byte[] buf = tempbuf;
+        byte sendbuf[] = new byte[3];
+        byte b, reply;
+        int boffset = 0, noffset = offset;
+        boolean dobreak = false;
+
+        if (count == 0)     // buffer is empty.
+            return -1;
+
+        while (!dobreak && (boffset < count) && (noffset < nbuf.length)) {
+            b = buf[boffset++];
+
+            // of course, byte is a signed entity (-128 -> 127)
+            // but apparently the SGI Netscape 3.0 doesn't seem
+            // to care and provides happily values up to 255
+            if (b >= 128)
+                b = (byte)(b - 256);
+
+            if (debug > 2) {
+                Byte B = new Byte(b);
+                System.err.print("byte: " + B.intValue() + " ");
+            }
+
+            switch (neg_state) {
+                case STATE_DATA:
+                    if (b == IAC) {
+                        neg_state = STATE_IAC;
+                        dobreak = true; // leave the loop so we can sync.
+                    }
+                    else
+                        nbuf[noffset++] = b;
+
+                    break;
+
+                case STATE_IAC:
+                    switch (b) {
+                        case IAC:
+                            if (debug > 2) System.err.print("IAC ");
+
+                            neg_state = STATE_DATA;
+                            nbuf[noffset++] = IAC;
+                            break;
+
+                        case WILL:
+                            if (debug > 2) System.err.print("WILL ");
+
+                            neg_state = STATE_IACWILL;
+                            break;
+
+                        case WONT:
+                            if (debug > 2) System.err.print("WONT ");
+
+                            neg_state = STATE_IACWONT;
+                            break;
+
+                        case DONT:
+                            if (debug > 2) System.err.print("DONT ");
+
+                            neg_state = STATE_IACDONT;
+                            break;
+
+                        case DO:
+                            if (debug > 2) System.err.print("DO ");
+
+                            neg_state = STATE_IACDO;
+                            break;
+
+                        case EOR:
+                            if (debug > 1) System.err.print("EOR ");
+
+                            notifyEndOfRecord();
+                            dobreak = true; // leave the loop so we can sync.
+                            neg_state = STATE_DATA;
+                            break;
+
+                        case SB:
+                            if (debug > 2) System.err.print("SB ");
+
+                            neg_state = STATE_IACSB;
+                            break;
+
+                        default:
+                            if (debug > 2) System.err.print("<UNKNOWN " + b + " > ");
+
+                            neg_state = STATE_DATA;
+                            break;
+                    }
+
+                    break;
+
+                case STATE_IACWILL:
+                    switch (b) {
+                        case TELOPT_ECHO:
+                            if (debug > 2) System.err.println("ECHO");
+
+                            reply = DO;
+                            setLocalEcho(false);
+                            break;
+
+                        case TELOPT_SGA:
+                            if (debug > 2) System.err.println("SGA");
+
+                            reply = DO;
+                            break;
+
+                        case TELOPT_EOR:
+                            if (debug > 2) System.err.println("EOR");
+
+                            reply = DO;
+                            break;
+
+                        case TELOPT_BINARY:
+                            if (debug > 2) System.err.println("BINARY");
+
+                            reply = DO;
+                            break;
+
+                        default:
+                            if (debug > 2) System.err.println("<UNKNOWN," + b + ">");
+
+                            reply = DONT;
+                            break;
+                    }
+
+                    if (debug > 1) System.err.println("<" + b + ", WILL =" + WILL + ">");
+
+                    if (reply != sentDX[b + 128] || WILL != receivedWX[b + 128]) {
+                        sendbuf[0] = IAC;
+                        sendbuf[1] = reply;
+                        sendbuf[2] = b;
+                        write(sendbuf);
+                        sentDX[b + 128] = reply;
+                        receivedWX[b + 128] = WILL;
+                    }
+
+                    neg_state = STATE_DATA;
+                    break;
+
+                case STATE_IACWONT:
+                    switch (b) {
+                        case TELOPT_ECHO:
+                            if (debug > 2) System.err.println("ECHO");
+
+                            setLocalEcho(true);
+                            reply = DONT;
+                            break;
+
+                        case TELOPT_SGA:
+                            if (debug > 2) System.err.println("SGA");
+
+                            reply = DONT;
+                            break;
+
+                        case TELOPT_EOR:
+                            if (debug > 2) System.err.println("EOR");
+
+                            reply = DONT;
+                            break;
+
+                        case TELOPT_BINARY:
+                            if (debug > 2) System.err.println("BINARY");
+
+                            reply = DONT;
+                            break;
+
+                        default:
+                            if (debug > 2) System.err.println("<UNKNOWN," + b + ">");
+
+                            reply = DONT;
+                            break;
+                    }
+
+                    if (reply != sentDX[b + 128] || WONT != receivedWX[b + 128]) {
+                        sendbuf[0] = IAC;
+                        sendbuf[1] = reply;
+                        sendbuf[2] = b;
+                        write(sendbuf);
+                        sentDX[b + 128] = reply;
+                        receivedWX[b + 128] = WILL;
+                    }
+
+                    neg_state = STATE_DATA;
+                    break;
+
+                case STATE_IACDO:
+                    switch (b) {
+                        case TELOPT_ECHO:
+                            if (debug > 2) System.err.println("ECHO");
+
+                            reply = WILL;
+                            setLocalEcho(true);
+                            break;
+
+                        case TELOPT_SGA:
+                            if (debug > 2) System.err.println("SGA");
+
+                            reply = WILL;
+                            break;
+
+                        case TELOPT_TTYPE:
+                            if (debug > 2) System.err.println("TTYPE");
+
+                            reply = WILL;
+                            break;
+
+                        case TELOPT_BINARY:
+                            if (debug > 2) System.err.println("BINARY");
+
+                            reply = WILL;
+                            break;
+
+                        case TELOPT_NAWS:
+                            if (debug > 2) System.err.println("NAWS");
+
+                            int[] size = getWindowSize();
+                            receivedDX[b] = DO;
+
+                            if (size == null) {
+                                // this shouldn't happen
+                                write(IAC);
+                                write(WONT);
+                                write(TELOPT_NAWS);
+                                reply = WONT;
+                                sentWX[b] = WONT;
+                                break;
+                            }
+
+                            reply = WILL;
+                            sentWX[b] = WILL;
+                            sendbuf[0] = IAC;
+                            sendbuf[1] = WILL;
+                            sendbuf[2] = TELOPT_NAWS;
+                            write(sendbuf);
+                            write(IAC); write(SB); write(TELOPT_NAWS);
+                            write((byte)(size[0] >> 8));
+                            write((byte)(size[0] & 0xff));
+                            write((byte)(size[1] >> 8));
+                            write((byte)(size[1] & 0xff));
+                            write(IAC); write(SE);
+                            break;
+
+                        default:
+                            if (debug > 2) System.err.println("<UNKNOWN," + b + ">");
+
+                            reply = WONT;
+                            break;
+                    }
+
+                    if (reply != sentWX[128 + b] || DO != receivedDX[128 + b]) {
+                        sendbuf[0] = IAC;
+                        sendbuf[1] = reply;
+                        sendbuf[2] = b;
+                        write(sendbuf);
+                        sentWX[b + 128] = reply;
+                        receivedDX[b + 128] = DO;
+                    }
+
+                    neg_state = STATE_DATA;
+                    break;
+
+                case STATE_IACDONT:
+                    switch (b) {
+                        case TELOPT_ECHO:
+                            if (debug > 2) System.err.println("ECHO");
+
+                            reply = WONT;
+                            setLocalEcho(false);
+                            break;
+
+                        case TELOPT_SGA:
+                            if (debug > 2) System.err.println("SGA");
+
+                            reply = WONT;
+                            break;
+
+                        case TELOPT_NAWS:
+                            if (debug > 2) System.err.println("NAWS");
+
+                            reply = WONT;
+                            break;
+
+                        case TELOPT_BINARY:
+                            if (debug > 2) System.err.println("BINARY");
+
+                            reply = WONT;
+                            break;
+
+                        default:
+                            if (debug > 2) System.err.println("<UNKNOWN," + b + ">");
+
+                            reply = WONT;
+                            break;
+                    }
+
+                    if (reply != sentWX[b + 128] || DONT != receivedDX[b + 128]) {
+                        write(IAC); write(reply); write(b);
+                        sentWX[b + 128] = reply;
+                        receivedDX[b + 128] = DONT;
+                    }
+
+                    neg_state = STATE_DATA;
+                    break;
+
+                case STATE_IACSBIAC:
+                    if (debug > 2) System.err.println("" + b + " ");
+
+                    if (b == IAC) {
+                        sbbuf = new byte[0];
+                        current_sb = b;
+                        neg_state = STATE_IACSBDATA;
+                    }
+                    else {
+                        System.err.println("(bad) " + b + " ");
+                        neg_state = STATE_DATA;
+                    }
+
+                    break;
+
+                case STATE_IACSB:
+                    if (debug > 2) System.err.println("" + b + " ");
+
+                    switch (b) {
+                        case IAC:
+                            neg_state = STATE_IACSBIAC;
+                            break;
+
+                        default:
+                            current_sb = b;
+                            sbbuf = new byte[0];
+                            neg_state = STATE_IACSBDATA;
+                            break;
+                    }
+
+                    break;
+
+                case STATE_IACSBDATA:
+                    if (debug > 2) System.err.println("" + b + " ");
+
+                    switch (b) {
+                        case IAC:
+                            neg_state = STATE_IACSBDATAIAC;
+                            break;
+
+                        default:
+                            byte[] xsb = new byte[sbbuf.length + 1];
+                            System.arraycopy(sbbuf, 0, xsb, 0, sbbuf.length);
+                            sbbuf = xsb;
+                            sbbuf[sbbuf.length - 1] = b;
+                            break;
+                    }
+
+                    break;
+
+                case STATE_IACSBDATAIAC:
+                    if (debug > 2) System.err.println("" + b + " ");
+
+                    switch (b) {
+                        case IAC:
+                            neg_state = STATE_IACSBDATA;
+                            byte[] xsb = new byte[sbbuf.length + 1];
+                            System.arraycopy(sbbuf, 0, xsb, 0, sbbuf.length);
+                            sbbuf = xsb;
+                            sbbuf[sbbuf.length - 1] = IAC;
+                            break;
+
+                        case SE:
+                            handle_sb(current_sb, sbbuf);
+                            current_sb = 0;
+                            neg_state = STATE_DATA;
+                            break;
+
+                        case SB:
+                            handle_sb(current_sb, sbbuf);
+                            neg_state = STATE_IACSB;
+                            break;
+
+                        default:
+                            neg_state = STATE_DATA;
+                            break;
+                    }
+
+                    break;
+
+                default:
+                    if (debug > 1)
+                        System.err.println("This should not happen: " + neg_state + " ");
+
+                    neg_state = STATE_DATA;
+                    break;
+            }
+        }
+
+        // shrink tempbuf to new processed size.
+        byte[] xb = new byte[count - boffset];
+        System.arraycopy(tempbuf, boffset, xb, 0, count - boffset);
+        tempbuf = xb;
+        return noffset - offset;
+    }
+
+    public void inputfeed(byte[] b, int offset, int len) {
+        byte[] xb = new byte[tempbuf.length + len];
+        System.arraycopy(tempbuf, 0, xb, 0, tempbuf.length);
+        System.arraycopy(b, offset, xb, tempbuf.length, len);
+        tempbuf = xb;
+    }
+}