/******************************************************************
*
*	CyberHTTP for Java
*
*	Copyright (C) Satoshi Konno 2002-2004
*
*	File: HTTPConnection.java
*
*	Revision;
*
*	11/18/02
*		- first revision.
*	09/02/03
*		- Giordano Sassaroli <sassarol@cefriel.it>
*		- Problem : The API is unable to receive responses from the Microsoft UPnP stack
*		- Error : the Microsoft UPnP stack is based on ISAPI on IIS, and whenever IIS
*                 receives a post request, it answers with two responses: the first one has no 
*		          body and it is a code 100 (continue) response, which has to be ignored. The
*		          second response is the actual one and should be parsed as the response.
*	02/09/04
*		- Ralf G. R. Bergs" <Ralf@Ber.gs>
*		- Why do you strip leading and trailing white space from the response body?
*		- Disabled to trim the content string.
*	03/11/04
*		- Added some methods about InputStream content.
*		  setContentInputStream(), getContentInputStream() and hasContentInputStream().
*	03/16/04
*		- Thanks for Darrell Young
*		- Added setVersion() and getVersion();
*	03/17/04
*		- Added hasFirstLine();
*	05/26/04
*		- Jan Newmarch <jan.newmarch@infotech.monash.edu.au> (05/26/04)
*		- Changed setCacheControl() and getChcheControl();
*
*******************************************************************/

package org.cybergarage.http;

import java.io.*;
import java.util.*;

import org.cybergarage.net.*;
import org.cybergarage.util.*;
import java.util.Calendar;

public class HTTPPacket 
{
	////////////////////////////////////////////////
	//	Constructor
	////////////////////////////////////////////////
	
	public HTTPPacket()
	{
		setVersion(HTTP.VERSION);
		setContentInputStream(null);
	}

	public HTTPPacket(HTTPPacket httpPacket)
	{
		setVersion(HTTP.VERSION);
		set(httpPacket);
		setContentInputStream(null);
	}

	public HTTPPacket(InputStream in)
	{
		setVersion(HTTP.VERSION);
		set(in);
		setContentInputStream(null);
	}

	////////////////////////////////////////////////
	//	Version
	////////////////////////////////////////////////
	
	private String version;
	
	public void setVersion(String ver)
	{
		version = ver;
	}
	
	public String getVersion()
	{
		return version;
	}
	
	////////////////////////////////////////////////
	//	set
	////////////////////////////////////////////////
	
	protected void set(InputStream in)
	{
 		try {
			BufferedReader reader = new BufferedReader(new InputStreamReader(in));
			
			String firstLine = reader.readLine();
			setFirstLine(firstLine);
			
			// Thanks for Giordano Sassaroli <sassarol@cefriel.it> (09/03/03)
			HTTPStatus httpStatus = new HTTPStatus(firstLine);
			int statCode = httpStatus.getStatusCode();
			if (statCode == HTTPStatus.CONTINUE_STATUS){
				//ad hoc code for managing iis non-standard behaviour
				//iis sends 100 code response and a 200 code response in the same
				//stream, so the code should check the presence of the actual
				//response in the stream.
				//skip all header lines
				String headerLine = reader.readLine();
				while ((headerLine != null) && (0 < headerLine.length()) ) {
					HTTPHeader header = new HTTPHeader(headerLine);
					if (header.hasName() == true)
						setHeader(header);
					headerLine = reader.readLine();
				}
				//look forward another first line
				String actualFirstLine = reader.readLine();
				if ((actualFirstLine != null) && (0 < actualFirstLine.length()) ) {
					//this is the actual first line
					setFirstLine(actualFirstLine);
				}else{
					return;
				}
			}
			
			String headerLine = reader.readLine();
			while ((headerLine != null) && (0 < headerLine.length()) ) {
				HTTPHeader header = new HTTPHeader(headerLine);
				if (header.hasName() == true)
					setHeader(header);
				headerLine = reader.readLine();
			}
			
			long conLen = getContentLength();
			if (0 < conLen) {
				int chunkSize = HTTP.getChunkSize();
				char readBuf[] = new char[chunkSize];
				long readCnt = 0;
				StringBuffer conBuf = new StringBuffer();
				while (readCnt < conLen) {
					try {
						int len = reader.read(readBuf);
						if (len < 0)
							break;
						conBuf.append(new String(readBuf, 0, len));
						readCnt += len;
					}
					catch (Exception e)
					{
						Debug.warning(e);
						break;
					}
				}
				// Thanks for Ralf G. R. Bergs" <Ralf@Ber.gs>
				String conStr = conBuf.toString();
				//String trimStr = conStr.trim();
				//setContent(trimStr.getBytes());
				setContent(conStr.getBytes());
			}
		}
		catch (Exception e) {
			Debug.warning(e);
		}
	}

	protected void set(HTTPSocket httpSock)
	{
		set(httpSock.getInputStream());
	}

	protected void set(HTTPPacket httpPacket)
	{
		setFirstLine(httpPacket.getFirstLine());
		
		clearHeaders();
		int nHeaders = httpPacket.getNHeaders();
		for (int n=0; n<nHeaders; n++) {
			HTTPHeader header = httpPacket.getHeader(n);
			addHeader(header);
		}
		setContent(httpPacket.getContent());
	}

	////////////////////////////////////////////////
	//	String
	////////////////////////////////////////////////

	private String firstLine = "";
	
	private void setFirstLine(String value)
	{
			firstLine = value;
	}
	
	protected String getFirstLine()
	{
		return firstLine;
	}

	protected String getFirstLineToken(int num)
	{
		StringTokenizer st = new StringTokenizer(firstLine, HTTP.REQEST_LINE_DELIM);
		String lastToken = "";
		for (int n=0; n<=num; n++) {
			if (st.hasMoreTokens() == false)
				return "";
			lastToken = st.nextToken();
		}
		return lastToken;
     }
	
	public boolean hasFirstLine()
	{
		return (0 < firstLine.length()) ? true : false;
	}
	
	////////////////////////////////////////////////
	//	Header
	////////////////////////////////////////////////

	private Vector httpHeaderList = new Vector();
	
	public int getNHeaders()
	{
		return httpHeaderList.size();
	}

	public void addHeader(HTTPHeader header)
	{
		httpHeaderList.add(header);
	}

	public void addHeader(String name, String value)
	{
		HTTPHeader header = new HTTPHeader(name, value);
		httpHeaderList.add(header);
	}

	public HTTPHeader getHeader(int n)
	{
		return (HTTPHeader)httpHeaderList.get(n);
	}
	
	public HTTPHeader getHeader(String name)
	{
		int nHeaders = getNHeaders();
		for (int n=0; n<nHeaders; n++) {
			HTTPHeader header = getHeader(n);
			String headerName = header.getName();
			if (headerName.equalsIgnoreCase(name) == true)
				return header;			
		}
		return null;
	}

	public void clearHeaders()
	{
		httpHeaderList.clear();
		httpHeaderList = new Vector();
	}
	
	public boolean hasHeader(String name)
	{
		return (getHeader(name) != null) ? true : false;
	}

	public void setHeader(String name, String value)
	{
		HTTPHeader header = getHeader(name);
		if (header != null) {
			header.setValue(value);
			return;
		}
		addHeader(name, value);
	}

	public void setHeader(String name, int value)
	{
		setHeader(name, Integer.toString(value));
	}

	public void setHeader(String name, long value)
	{
		setHeader(name, Long.toString(value));
	}
	
	public void setHeader(HTTPHeader header)
	{
		setHeader(header.getName(), header.getValue());
	}

	public String getHeaderValue(String name)
	{
		HTTPHeader header = getHeader(name);
		if (header == null)
			return "";
		return header.getValue();
	}

	////////////////////////////////////////////////
	// set*Value
	////////////////////////////////////////////////

	public void setStringHeader(String name, String value, String startWidth, String endWidth)
	{
		String headerValue = value;
		if (headerValue.startsWith(startWidth) == false)
			headerValue = startWidth + headerValue;
		if (headerValue.endsWith(endWidth) == false)
			headerValue = headerValue + endWidth;
		setHeader(name, headerValue);
	}

	public void setStringHeader(String name, String value)
	{
		setStringHeader(name, value, "\"", "\"");
	}
	
	public String getStringHeaderValue(String name, String startWidth, String endWidth)
	{
		String headerValue = getHeaderValue(name);
		if (headerValue.startsWith(startWidth) == true)
			headerValue = headerValue.substring(1, headerValue.length());
		if (headerValue.endsWith(endWidth) == true)
			headerValue = headerValue.substring(0, headerValue.length()-1);
		return headerValue;
	}
	
	public String getStringHeaderValue(String name)
	{
		return getStringHeaderValue(name, "\"", "\"");
	}

	public void setIntegerHeader(String name, int value)
	{
		setHeader(name, Integer.toString(value));
	}
	
	public void setLongHeader(String name, long value)
	{
		setHeader(name, Long.toString(value));
	}
	
	public int getIntegerHeaderValue(String name)
	{
		HTTPHeader header = getHeader(name);
		if (header == null)
			return 0;
		return StringUtil.toInteger(header.getValue());
	}

	public long getLongHeaderValue(String name)
	{
		HTTPHeader header = getHeader(name);
		if (header == null)
			return 0;
		return StringUtil.toLong(header.getValue());
	}

	////////////////////////////////////////////////
	//	getHeader
	////////////////////////////////////////////////
	
	public String getHeaderString()
	{
		StringBuffer str = new StringBuffer();
	
		int nHeaders = getNHeaders();
		for (int n=0; n<nHeaders; n++) {
			HTTPHeader header = getHeader(n);
			str.append(header.getName() + ": " + header.getValue() + HTTP.CRLF);
		}
		
		return str.toString();
	}

	////////////////////////////////////////////////
	//	Contents
	////////////////////////////////////////////////

	private byte content[] = new byte[0];
	
	public void setContent(byte data[])
	{
		content = data;
		setContentLength(data.length);
	}

	public void setContent(String data)
	{
		setContent(data.getBytes());
	}

	public  byte []getContent()
	{
		return content;
	}

	public  String getContentString()
	{
		return new String(content);
	}
	
	public boolean hasContent()
	{
		return (content.length > 0) ? true : false;
	}

	////////////////////////////////////////////////
	//	Contents (InputStream)
	////////////////////////////////////////////////

	private InputStream contentInput = null;
	
	public void setContentInputStream(InputStream in)
	{
		contentInput = in;
	}

	public InputStream getContentInputStream()
	{
		return contentInput;
	}

	public boolean hasContentInputStream()
	{
		return (contentInput != null) ? true : false;
	}

	////////////////////////////////////////////////
	//	ContentType
	////////////////////////////////////////////////

	public void setContentType(String type)
	{
		setHeader(HTTP.CONTENT_TYPE, type);
	}

	public String getContentType()
	{
		return getHeaderValue(HTTP.CONTENT_TYPE);
	}

	////////////////////////////////////////////////
	//	ContentLength
	////////////////////////////////////////////////

	public void setContentLength(long len)
	{
		setLongHeader(HTTP.CONTENT_LENGTH, len);
	}

	public long getContentLength()
	{
		return getLongHeaderValue(HTTP.CONTENT_LENGTH);
	}

	////////////////////////////////////////////////
	//	CacheControl
	////////////////////////////////////////////////

	public void setCacheControl(String directive)
	{
		setHeader(HTTP.CACHE_CONTROL, directive);
	}
	
	public void setCacheControl(String directive, int value)
	{
		String strVal = directive + "=" + Integer.toString(value);
		setHeader(HTTP.CACHE_CONTROL, strVal);
	}
	
	public void setCacheControl(int value)
	{
		setCacheControl(HTTP.MAX_AGE, value);
	}

	public String getCacheControl()
	{
		return getHeaderValue(HTTP.CACHE_CONTROL);
	}

	////////////////////////////////////////////////
	//	Server
	////////////////////////////////////////////////

	public void setServer(String name)
	{
		setHeader(HTTP.SERVER, name);
	}

	public String getServer()
	{
		return getHeaderValue(HTTP.SERVER);
	}

	////////////////////////////////////////////////
	//	Server
	////////////////////////////////////////////////

	public void setHost(String host, int port)
	{
		String hostAddr = host;
		if (HostInterface.isIPv6Address(host) == true)
			hostAddr = "[" + host + "]";
		setHeader(HTTP.HOST, hostAddr + ":" + Integer.toString(port));
	}

	public String getHost()
	{
		return getHeaderValue(HTTP.HOST);
	}


	////////////////////////////////////////////////
	//	Server
	////////////////////////////////////////////////

	public void setDate(Calendar cal)
	{
		Date date = new Date(cal);
		setHeader(HTTP.DATE, date.getDateString());
	}

	public String getDate()
	{
		return getHeaderValue(HTTP.DATE);
	}

	////////////////////////////////////////////////
	//	set
	////////////////////////////////////////////////

/*
	public final static boolean parse(HTTPPacket httpPacket, InputStream in)
	{
 		try {
			BufferedReader reader = new BufferedReader(new InputStreamReader(in));
			return parse(httpPacket, reader);
		}
		catch (Exception e) {
			Debug.warning(e);
		}
		return false;
	}
*/
}

