/*
 * @(#)HTTPResponse.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 <time.h>
#include "Config.h"
#include "File.h"
#include "HTTPResponse.h"
#include "Log.h"
#include "PrintWriter.h"
#include "Version.h"

const char CRLF[] = "\r\n";

HTTPResponse::HTTPResponse() {
	setStatusCode(200);
	contentType = "application/octet-stream";
	entityBytesTransferred = 0;
	lastModified = "";
}

HTTPResponse::HTTPResponse(HTTPResponse *response) {
	*this = *response;
	delete response;
}

HTTPResponse::~HTTPResponse() {
}

String *operator+(const char *str1, String &str2) {
	String *str = new String(str1);
	*str + str2;
	return str;
}

String *HTTPResponse::getErrorHTML() {
	String str = "";
	str = str + "<html>\n"
		+ "<head><title>" + reasonPhrase + "</title></head>\n"
		+ "<body><h1>Error " + statusCode + ": "
		+ reasonPhrase + "</h1></body>\n"
		+ "</html>\n";
	return new String(str);
}

HTTPResponse *HTTPResponse::movedPermanently(String &location) {
	HTTPResponse *response = new HTTPResponse();
	response->setStatusCode(301);
	response->setLocation(location);
	String entity = response->getErrorHTML();
	String type = "text/html";
	response->setEntity(entity, type);
	return response;
}

HTTPResponse *HTTPResponse::notFound() {
	HTTPResponse *response = new HTTPResponse();
	response->setStatusCode(404);
	String entity = response->getErrorHTML();
	String type = "text/html";
	response->setEntity(entity, type);
	return response;
}

HTTPResponse *HTTPResponse::notImplemented() {
	HTTPResponse *response = new HTTPResponse();
	response->setStatusCode(501);
	String entity = response->getErrorHTML();
	String type = "text/html";
	response->setEntity(entity, type);
	return response;
}

void HTTPResponse::setEntity(File &file, int ifModifiedSince) {
	if (file.exists() && ifModifiedSince >= file.lastModified()) {
		setStatusCode(304);
		contentLength = "";
		contentType = "";
		entityBody = "";
		return;
	}

	entityStream = new FileInputStream(file);

	contentLength = "Content-Length: ";
	contentLength = contentLength + file.getSize() + CRLF;

	String type = "";
	if (file.getPath().endsWith(".html"))
		type = "text/html";
	else if (file.getPath().endsWith(".css"))
		type = "text/css";
	else if (file.getPath().endsWith(".jar"))
		type = "application/x-jar";
	else if (file.getPath().endsWith(".jnlp"))
		type = "application/x-java-jnlp-file";
	else if (file.getPath().endsWith(".properties"))
		type = "text/plain";
	else if (file.getPath().indexOf(".") < 0)
		type = "text/plain";
	else
		type = config.getMimeType(file);

	contentType = "Content-Type: ";
	contentType = contentType + type + CRLF;

	time_t t = file.lastModified();
	struct tm *tm = gmtime(&t);
	const char *days[] = { "Sun", "Mon", "Tue", "Wed", "Thu",
			"Fri", "Sat" };
	const char *months[] = { "Jan", "Feb", "Mar", "Apr",
			"May", "Jun", "Jul", "Aug",
			"Sep", "Oct", "Nov", "Dec" };
	lastModified = "Last-Modified: ";
	lastModified = lastModified
		+ days[tm->tm_wday]
		+ ", " + (tm->tm_mday < 10? "0": "") + tm->tm_mday
		+ " " + months[tm->tm_mon]
		+ " " + (1900 + tm->tm_year)
		+ " " + (tm->tm_hour < 10? "0": "") + tm->tm_hour
		+ ":" + (tm->tm_min < 10? "0": "") + tm->tm_min
		+ ":" + (tm->tm_sec < 10? "0": "") + tm->tm_sec
		+ " GMT"
		+ CRLF;
}

void HTTPResponse::setEntity(String &body, String &type) {
	contentLength = "Content-Length: ";
	contentLength = contentLength + body.length() + CRLF;
	contentType = "Content-Type: ";
	contentType = contentType + type + CRLF;
	entityBody = body;
}

void HTTPResponse::setLocation(String &aLocation) {
	location = "Location: ";
	location = location + aLocation + CRLF;
}

void HTTPResponse::setStatusCode(int code) {
	statusCode = code;
	switch (code) {
		case 200:
			reasonPhrase = "OK";
		break;
		case 301:
			reasonPhrase = "Moved Permanently";
		break;
		case 304:
			reasonPhrase = "Not Modified";
		break;
		case 404:
			reasonPhrase = "Not Found";
		break;
		case 501:
			reasonPhrase = "Not Implemented";
		break;
	}
}

void HTTPResponse::write(class PrintWriter &writer) {
	writeHead(writer);
	writer.write(entityBody);

	entityBytesTransferred = entityBody.length();
	while (1) {
		char buf[4096];
		int n = entityStream.read(buf, 0, sizeof buf);
		if (n <= 0)
			break;
		writer.write(buf, 0, n);
		entityBytesTransferred += n;
	}

	writer.flush();
}

void HTTPResponse::writeHead(class PrintWriter &writer) {
	String s = "";
	s = s + "HTTP/1.0 " + statusCode + " " + reasonPhrase + CRLF
	// Response-Header
		+ location
		+ "Server: " + Version.product + "/" + Version.version + CRLF
		+ "Connection: close" + CRLF
	// Entity Header Fields
		+ contentLength
		+ contentType
		+ lastModified
		+ CRLF;

	writer.write(s);
	writer.flush();
}
