/*
 * @(#)HTTPRequest.cpp
 *
 * This file is part of webCDwriter - Network CD Writing.
 *
 * Copyright (C) 2002-2004 Jörg P. M. Haeger
 *
 * webCDwriter is free software. See CDWserver.cpp for details.
 *
 * Jörg Haeger, 30.04.2002
 */

#include "BufferedReader.h"
#include "Error.h"
#include "HTTPRequest.h"
#include "Log.h"
#include "StreamTokenizer.h"

HTTPRequest::HTTPRequest(BufferedReader &reader) {
	contentLength = 0;
	ifModifiedSince = 0;

	StreamTokenizer tokenizer(reader);

	if (tokenizer.nextToken() != tokenizer.TT_WORD)
		throw new Exception("method expected");
	method = tokenizer.sval;

	if (!method.equalsIgnoreCase("GET")
			&& !method.equalsIgnoreCase("HEAD")
			&& !method.equalsIgnoreCase("POST"))
		return;

	if (tokenizer.nextToken() != tokenizer.TT_WORD)
		throw new Exception("URI expected");
	uri = tokenizer.sval;

	int res;
	while (true) {
		// deal with ":" in URIs
		res = tokenizer.nextToken();
		if (res != tokenizer.TT_WORD
				|| !tokenizer.sval.equals(":"))
			break;
		uri = uri + ":";
		res = tokenizer.nextToken();
		if (res != tokenizer.TT_WORD)
			break;
		uri = uri + tokenizer.sval;
	}

	if (res != tokenizer.TT_WORD)
		throw new Exception("protocol expected");
	protocol = tokenizer.sval;

	if (tokenizer.nextToken() != tokenizer.TT_EOL)
		throw new Exception("EOL expected");

	// parse fields
	while (1) {
		if (tokenizer.nextToken() == tokenizer.TT_EOL)
			break;
		String fieldName = tokenizer.sval;

		if (tokenizer.nextToken() != tokenizer.TT_WORD
			|| !tokenizer.sval.equals(":"))
			throw new Exception(": expected");

		String field = "";
		if (tokenizer.nextToken() != tokenizer.TT_EOL) {
			field = field + tokenizer.sval;

			if (fieldName.equalsIgnoreCase("Host"))
				host = new String(field);

			int acceptLanguage = fieldName.equalsIgnoreCase(
				"Accept-Language");
			if (acceptLanguage)
				languages.add(tokenizer.sval);
			else
			if (fieldName.equalsIgnoreCase("If-Modified-Since")) {
				if (tokenizer.sval.length() == 3) {
					// drop day of week
					if (tokenizer.nextToken() == tokenizer.TT_EOL)
						continue;
					field = field + " " + tokenizer.sval;
					if (tokenizer.nextToken() == tokenizer.TT_EOL)
						continue;
					field = field + " " + tokenizer.sval;
				}

				int day = Integer::getInt(tokenizer.sval.getBytes());
				if (tokenizer.nextToken() == tokenizer.TT_EOL)
					continue;
				field = field + " " + tokenizer.sval;

				const char *months[] = { "Jan", "Feb", "Mar", "Apr",
					"May", "Jun", "Jul", "Aug",
					"Sep", "Oct", "Nov", "Dec" };
				int month;
				for (month = 0; month < 11; month++)
					if (tokenizer.sval.equals(months[month]))
						break;

				if (tokenizer.nextToken() == tokenizer.TT_EOL)
					continue;
				field = field + " " + tokenizer.sval;
				int year = Integer::getInt(tokenizer.sval.getBytes());

				if (tokenizer.nextToken() == tokenizer.TT_EOL)
					continue;
				field = field + " " + tokenizer.sval;
				int hour = Integer::getInt(tokenizer.sval.getBytes());

				if (tokenizer.nextToken() == tokenizer.TT_EOL)
					continue;
				field = field + " " + tokenizer.sval;
				if (tokenizer.nextToken() == tokenizer.TT_EOL)
					continue;
				field = field + " " + tokenizer.sval;
				int min = Integer::getInt(tokenizer.sval.getBytes());

				if (tokenizer.nextToken() == tokenizer.TT_EOL)
					continue;
				field = field + " " + tokenizer.sval;
				if (tokenizer.nextToken() == tokenizer.TT_EOL)
					continue;
				field = field + " " + tokenizer.sval;
				int sec = Integer::getInt(tokenizer.sval.getBytes());

				if (year >= 1970) {
					int days0[] = { 0, 31, 59, 90, 120, 151, 181, 212,
						243, 273, 304, 334, 365 };
					int years = year - 1970;
					int days = years * 365 + (years + 1) / 4
						+ days0[month]
						+ (month > 1 && (year + 2) % 4 == 0? 1 : 0)
						+ (day - 1);
					ifModifiedSince = ((days * 24 + hour) * 60
						+ min) * 60 + sec;
				}
			}

			int i = 0;
			while (tokenizer.nextToken() != tokenizer.TT_EOL)
				if (i++ < 1000) {
					field = field + " " + tokenizer.sval;
					if (acceptLanguage && !tokenizer.sval.equals(","))
						languages.add(tokenizer.sval);
				}

			if (fieldName.equalsIgnoreCase("Content-Length"))
				contentLength = Integer::getInt(field.getBytes());
		}

		log.debug("HTTPRequest", "- <%s> <%s>",
			fieldName.getBytes(), field.getBytes());
	}
}

HTTPRequest::HTTPRequest(HTTPRequest *request) {
	method = request->method;
	uri = request->uri;
	protocol = request->protocol;
	host = request->host;
	languages = request->languages;
	contentLength = request->contentLength;
	ifModifiedSince = request->ifModifiedSince;
	delete request;
}

HTTPRequest::~HTTPRequest() {
}
