package ssh;

import java.io.*;
import java.net.*;
import java.util.*;
import Tools.*;

/**
 * This class reads data from an ssh stream. It extracts
 * incoming bytes from the BinaryPacketIn packets, and buffers unused
 * ones to return on future read requests. One way to get your hands on
 * an instance of this class is by calling connect() and then
 * getInputStream() on an {@link ssh.ClientProtocol}.
 * 
 * @rcs $Id: SshInputStream.java,v 1.3 2000/05/22 01:36:01 jonh Exp $
 * @author Jon Howell <jonh@cs.dartmouth.edu>
 * @license This code is Copyright 1999 Jon Howell. It is available for
 * use under the GNU Public License, available at:
 * http://www.gnu.org/copyleft/gpl.html
 */
public class SshInputStream
	extends InputStream {

	BinaryPacketInputStream binaryIn;
	byte[] buf;
	int bufoff, buflen;
	byte[] oneByteBuffer;

	public SshInputStream(BinaryPacketInputStream binaryIn) {
		this.binaryIn = binaryIn;
		bufoff = 0;
		buflen = 0;
		oneByteBuffer = new byte[1];
	}

	public int read()
		throws IOException {
		int rc = read(oneByteBuffer, 0, 1);
		return (rc==1) ? (((int)oneByteBuffer[0])&0x0ff) : -1;
	}

	public int read(byte[] b, int off, int len)
		throws IOException {

		if (binaryIn==null) {
			throw new EOFException();
		}

		do {
			// copy as many bytes as available out of buffer
			if (buflen>0) {
				int copylen = Math.min(len, buflen);
				SecureRMIProtocol.log.log("verbose", "satisfied "
					+copylen+"/"+len+" bytes from buffer of "+buflen);
				System.arraycopy(buf, bufoff, b, off, copylen);
				bufoff += copylen;
				buflen -= copylen;
				return copylen;
			}
			// else wait for a network packet
			// (now buflen==0; we can replace buf)
			SecureRMIProtocol.log.log("verbose",
				"SshInputStream: looking for binaryIn packet on "
				+binaryIn.dataIn);
			// new Throwable(" ---trace").printStackTrace();
			BinaryPacketIn bpi = binaryIn.readPacket();
				// sometimes this will fail, and the ex gets caught by
				// sun.rmi.server.UnicastServerRef.dispatch
				// (UnicastServerRef.java:164), so that we only notice
				// something weird is going on when the stream acts broken
				// later.

			if (bpi.getType() == Protocol.SSH_MSG_DISCONNECT) {
				// end of this stream.
				binaryIn.close();
				binaryIn = null;
				return 0;
			}

			if (bpi.getType() == Protocol.SSH_CMSG_STDIN_DATA ||
				bpi.getType() == Protocol.SSH_SMSG_STDOUT_DATA ||
				bpi.getType() == Protocol.SSH_SMSG_STDERR_DATA) {
					// which one of these we should accept depends
					// on which end of an official ssh stream we're
					// on; for our purposes, take whatever is sent to us.

				buf = bpi.readStringAsBytes();
				bufoff = 0;
				buflen = buf.length;
				SecureRMIProtocol.log.log("verbose",
					"SshInputStream: received binaryIn packet, length "+buflen
					+", begins with "+buf[0]);
				// loop back to top, where buflen>0, and return data
				// to caller.
				continue;
			}
			throw new IOException("Unimplemented packet type: "+bpi.getType());
		} while (true);
	}
}
