w3c.model.tools.basicweb.Message

w3c.model.tools.basicweb.Message

/* basicweb.Message.java
 * $Id, $Date eric Exp $
 * (c) COPYRIGHT MIT and INRIA, 1997.
 * Please first read the full copyright statement in file COPYRIGHT.html
 *
 * Light fuse and get away.
 */

package w3c.model.tools.basicweb;

import java.io.*;
import java.net.*;
import java.util.*;
import w3c.model.www.pep.PEPAgent;
import w3c.model.www.pep.PEPExtension; // for construction states
import w3c.model.www.pep.PEPMessage;
import w3c.model.www.pep.InstanceContext;
import w3c.model.www.pep.altlib.ErrorContext;

/*
Message - "The basic unit of HTTP communications" - rfc2068
extended by Request, ProxyRequest and Reply.
*/
abstract public class Message implements PEPMessage

    boolean WATCH = true;
    String startLine;
    Hashtable headers;
    Socket socket;
    InputStream in;
    OutputStream out;
    String URI;
    PEPAgent pepAgent;
    InstanceContext instanceContext;
    String path = null; /* real file path to get or put */

/*
The header list extends the interface in PEPMessage.
*/
    public static final int ACCEPT = 4;
    public static final int CONNECTION = 5;
    public static final int CONTENT_LENGTH = 6;
    public static final int CONTENT_TYPE = 7;
    public static final int DATE = 8;
    public static final int HOST = 9;
    public static final int LAST_MODIFIED = 10;
    public static final int USER_AGENT = 11;
    public static final int _HEADER_NAME_COUNT = 12;
    final static String headerNames[] = {
	"PEP",
	"C-PEP",
	"PEP-Info",
	"C-PEP-Info",
	"Accept",
	"Connection",
	"Content-Length",
	"Content-Type",
	"Date",
	"Host",
	"Last-Modified",
	"User-Agent",
    };

    public final int TEXT_HTML = 0;
    public final int TEXT_PLAIN = 1;
    public final int _MEDIA_TYPES_COUNT = 2;
    final String mediaTypes[] = {
	"text/html",
	"text/plain"
    };

    final String extensions[] = {
	".html",
	".txt"
    };

    boolean writable = false;

    ErrorContext errorContext;

/* constructor for Request */
    Message (Socket socket, InputStream in, OutputStream out, String URI, PEPAgent pepAgent, ErrorContext errorContext) {
	headers =  Hashtable(5, 3);
	this.socket = socket;
	this.in = in;
	this.out = out;
	this.URI = URI;
	this.pepAgent = pepAgent;
	this.errorContext = errorContext;
	instanceContext =  InstanceContext(pepAgent, this, errorContext);
    }

/* constructor for Reply */
    Message (Socket socket, InputStream in, OutputStream out, String URI, Message old) {
	headers =  Hashtable(5, 3);
	this.socket = socket;
	this.in = in;
	this.out = out;
	this.URI = URI;
	pepAgent = old.pepAgent;
	errorContext = old.errorContext;
	instanceContext =  InstanceContext(old.instanceContext, this);
    }

    public Socket getSocket () {return socket;}
    public InputStream getIn () {return in;}
    public void setIn (InputStream in) {this.in = in;}
    public OutputStream getOut () {return out;}
    public void setOut (OutputStream out) {this.out = out;}
    public String getURI () {return URI;}
    public void setURI (String URI) {this.URI = URI;}
    public InstanceContext getInstanceContext () {return instanceContext;}

    public void useHeaders (Message old) {
	headers = old.headers;
    }

    public void setHeaderValue (String name, String value) {
	headers.put(name, value);
    }

    public void setHeaderValue (int name, String value) {
	headers.put(headerNames[name], value);
    }

    public void clearHeader (String name) {
	headers.remove(name);
    }

    public void clearHeader (int name) {
	headers.remove(headerNames[name]);
    }

    public String getHeaderValue (String name) {
	return ( (String)headers.get(name);
    }

    public String getHeaderValue (int name) {
	return ( (String)headers.get(headerNames[name]);
    }

    public Vector getMatchingHeaders(String name){
	Vector ret =  Vector();
    	if (name.endsWith("*")))
    	    name = name.substring(0, name.length() - 1);
	Enumeration e = headers.keys();
	while (e.hasMoreElements())) {
	    String header = (String)e.nextElement();
	    if (header.startsWith(name)))
		ret.addElement(header);
	}
	return ret;
    }

    /* header support for specific fields
     */

    void setMediaType(int type) {
	setHeaderValue(CONTENT_TYPE, mediaTypes[type]);
    }

    void setMediaTypeFor (String name) {
	int i;
	for (i = 0; i < extensions.length; i++)) {
	    if (name.endsWith(extensions[i]))) {
		setMediaType(i);
		return;
	    }
	}
	setMediaType(TEXT_PLAIN);
    }

    private static String days[] = {"Sun", "Mon", "Tue", "Wed", "Thu" , "Fri", "Sat"};
    private static String months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
    void setHeaderDate() {
	long now = System.currentTimeMillis();
        int tz =  Date().getTimezoneOffset();
	Date d =  Date(now+(tz*60*1000));
	setHeaderValue("Date", days[d.getDay()] + ", " + d.getDate() + " " + months[d.getMonth()] + " " + (d.getYear()+1900) + " " + d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds() +" GMT");
    }

    /* M A P P I N G */

    static URL ref = null;
    URL url = null;
    static static {
	try {
	    ref =  URL("http://localhost/");
	} catch (MalformedURLException f)) {
	}
    }

    public String getPath () {
	if (path != null))
	    return path;
	try {
	    url =  URL(URI);
	    path = url.getFile();
	    return path;
	} catch (MalformedURLException e)) {
	    try {
		url =  URL(ref, URI);
		path = url.getFile();
		return path;
	    } catch (MalformedURLException f)) {
		System.out.println("MalformedURLException: "+f.getMessage());
	    }
	}
	return null;
    }

    public String getMappedFile () {
	if (url == null))
	    return URI;
	try {
	    return   URL (url, path).getFile();
	} catch (MalformedURLException f)) {
	    System.out.println("MalformedURLException: "+f.getMessage());
	}
	return null;
    }

    public String getMappedURI () {
	if (url == null))
	    return URI;
	try {
	    return   URL (url, path).toExternalForm();
	} catch (MalformedURLException f)) {
	    System.out.println("MalformedURLException: "+f.getMessage());
	}
	return null;
    }

    public void mapURI (String from, String to) {
    	try {
    	    URL fromUrl =  URL(from);
    	    from = fromUrl.getFile();
    	} catch (MalformedURLException f)) {	// leave from alone
	}
	String path = getPath();
	if (!path.startsWith(from)))
	    return;
	path = to + path.substring(from.length());
	this.path = path;
    }

    /* R E A D I N G */

    abstract boolean parseStartLine();

    String readLine () {
	byte b[] =  byte[1024];
	byte c = -1;
	int i = 0;
	try {
	    while ((c = (byte)in.read()) != 'n' && c != -1))
		if (c != 'r'))
		    b[i++] = c;
	} catch (IOException e)) {
	}
	if (c == -1 && i == 0))
	    return null;
	return  String(b, 0, 0, i);
    }

    boolean read () throws IOException{
	/* PEP incoming CONNECT */
	pepAgent.handleHeaders(instanceContext, PEPExtension.CONNECT);

	while (true)) {
	    String input = readLine();
//			System.out.println("<!- reading \"" + input + "\" ->");
	    if (input == null))
		throw  IOException("fini");
	    if ("".equals(input)))
		if (startLine == null)) /* rfc2068#4.1 */
		    continue;
		else {
		    /* PEP incoming HEADERS */
		    pepAgent.handleHeaders(instanceContext, PEPExtension.HEADERS);
		    return true;
		}
	    elseif (startLine == null)) {
		startLine = input;
		if (!parseStartLine()))
		    return false;
		System.out.println("<!- reading "" + startLine + "" ->");
		/* PEP incoming STARTLINE */
		pepAgent.handleHeaders(instanceContext, PEPExtension.STARTLINE);
	    } elseif (!parseMessageHeader(input)))
		    return false;
	}
    }

    boolean parseMessageHeader (String input) {
	String value = null;
	int i = input.indexOf(':');
	if (i < 0))
	    return false;
	if (i == 0))
	    setHeaderValue(input.trim(), null);
	elsesetHeaderValue(input.substring(0, i).trim(), input.substring(i+1).trim());
	return true;
    }

    /* W R I T I N G */

    private void writeLine (String data) throws IOException{
	byte b[] =  byte[data.length() + 1];
	data.getBytes(0, b.length - 1, b, 0);
	b[b.length-1] = 'n';
	try {
	    out.write(b);
	} catch (IOException e)) {
	    System.err.println("<!- error writing "" + data  + "" to "+out.toString()+" ->");
	    e.printStackTrace();
	}
	if (WATCH)) System.out.println("<!- writing line ->" + data);
    }

    void write (String data) throws IOException{
	writeHeaders();
	byte b[] =  byte[data.length()];
	data.getBytes(0, b.length, b, 0);
	out.write(b);
    //	if (WATCH) System.out.println("<!- writing data ->" + data);
    }

    void write (byte data[]) throws IOException{
	writeHeaders();
	out.write(data);
//		if (WATCH) System.out.println("<!- writing data ->" + new String(data, 0, 0, data.length));
    }

    protected void prepStartLine () {
    }

    void writeHeaders () throws IOException{
	if (writable))
	    return;
	/* PEP outgoing CONNECT */
	pepAgent.generateHeaders(instanceContext, PEPExtension.CONNECT);
	prepStartLine();    // request sets PEP- method for required extensions
	if (getHeaderValue(DATE) == null))
	    setHeaderDate();
	writeLine(startLine);
	/* PEP outgoing STARTLINE */
	pepAgent.generateHeaders(instanceContext, PEPExtension.STARTLINE);

//	System.out.println("<!- writing \"" + startLine + "\" ->");
	for (Enumeration en = headers.keys(); en.hasMoreElements();)) {
	    String name = (String)en.nextElement();
	    String value = (String)headers.get(name);
	    writeLine(name + ": " + value);
//	    System.out.println("<!- writing \"" + name + ": " + value + "\" ->");
        }
	/* PEP outgoing HEADERS */
	pepAgent.generateHeaders(instanceContext, PEPExtension.NON_PEP_HEADERS);
	Hashtable h = instanceContext.mayIGetYouBags();
	for (Enumeration e = h.keys(); e.hasMoreElements();)) {
	    Object o = e.nextElement();
	    Integer i = (Integer)o;
	    String value = (String)h.get(i);
	    writeLine(headerNames[i.intValue()] + ": " + value);
	}
			
	writeLine("");
	pepAgent.generateHeaders(instanceContext, PEPExtension.HEADERS);
	writable = true;
    }

    void getFile (File file) throws IOException{
	RandomAccessFile fileInput =  RandomAccessFile(file, "r");
	setHeaderValue(CONTENT_LENGTH, "" + (int)file.length());
	byte b[] =  byte[(int)file.length()];
	fileInput.read(b);
	write(b);
    }

    void putFile (File file) throws IOException{
	int len = Integer.parseInt(getHeaderValue(CONTENT_LENGTH));
	byte b[] =  byte[len];
	RandomAccessFile fileOutput =  RandomAccessFile(file, "rw");
	setHeaderValue(CONTENT_LENGTH, "" + (int)file.length());
	in.read(b);
	fileOutput.write(b);
    }

    void readRest () throws IOException{
	String valueStr = getHeaderValue(CONTENT_LENGTH);
	int len;
	if (valueStr == null || (len = Integer.parseInt(valueStr)) <= 0))
	    return;
	byte b[] =  byte[len];
	in.read(b);
	if (out != null))
	    out.write(b);
    }

    /* @@@ this function must exist somewhere in java already */
    static int arrayFind (String ray[], String lookFor) {
	int i;
	for (i = 0; i < ray.length; i++))
	    if (ray[i].equals(lookFor)))
		break;
	return i == ray.length ? -1 : i;
    }

    public InputStream getInputStream () {
    	return in;
    }

    public void setInputStream (InputStream in) {
    	this.in = in;
    }

    public OutputStream getOutputStream () {
    	return out;
    }

    public void setOutputStream (OutputStream out) {
    	this.out = out;
    }