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

#include "Config.h"
#include "File.h"
#include "HTTPRequest.h"
#include "HTTPResponse.h"
#include "HTTPServer.h"
#include "Log.h"
#include "PrintWriter.h"
#include "Server.h"
#include "Socket.h"
#include "Status.h"
#include "String.h"
#include "StringBuffer.h"
#include "Version.h"

HTTPServer::HTTPServer(Socket &socket): socket(socket) {
}

HTTPServer::~HTTPServer() {
}

HTTPRequest *HTTPServer::nextRequest() {
	return new HTTPRequest(socket.getInputStream());
}

StringBuffer &HTTPServer::replace(StringBuffer &buf,
		const char *key, String &by) {
	log.debug("HTTPServer", "replace %d", buf.length());
	int pos = 0;
	while (1) {
		pos = buf.indexOf(key, pos);
		if (pos < 0)
			break;
		buf.delete_(pos, pos + strlen(key));
		buf.insert(pos, by.getBytes());
		pos += by.length();
		log.debug("HTTPServer", "replace2 %d", buf.length());
	}
	return buf;
}

void HTTPServer::run() {
	log.put(4, S.e + "HTTPServer::run() start "
		+ socket.getOutputStream());
/*	try {
		socket.setKeepAlive(true);
		socket.setSoLinger(false, 10);
	}
	catch (Exception *e) {
		log.put(1, S.e + e->getMessage());
		delete e;
	}
*/
	PrintWriter writer(socket.getOutputStream());

	while (1) {
		HTTPRequest request = nextRequest();

		if (request.method.equalsIgnoreCase("GET")
			|| request.method.equalsIgnoreCase("HEAD")) {
			log.put(4, S.e + socket.getInetAddress()
				+ " <" + request.method
				+ "> <" + request.uri
				+ "> <" + request.protocol + ">");

			if (request.uri.equals("/webCDcreator/4netscape.html")) {
				File file = new File(config.getConfigDir(),
					"/webCDcreator/4netscape/swing.jar");
				if (!file.exists())
					request.uri = "/webCDcreator/4netscape/swing.html";
			}

/*			if (request.uri.endsWith("/4MJR.html")
				|| request.uri.endsWith("/4netscape.html")
				|| request.uri.endsWith("/4pluginIE.html")
				|| request.uri.endsWith("/4pluginRSA.html")) {
				File file = new File(config.getConfigDir(),
					"/webCDcreator/4pluginRSA/webCDcreator.jar");
				if (!file.exists())
					request.uri = "/webCDcreator/4pluginRSA/info.html";
			}
*/
			if (request.uri.equals("/")
				|| request.uri.equals("/index.html"))
				request.uri = "/status.html";

			if (request.uri.indexOf("..") == -1
				&& request.uri.startsWith("/")
				&& !request.uri.startsWith("/accounts")
				&& (!request.uri.startsWith("/config")
					|| request.uri.equals("/config.html"))
				&& !request.uri.startsWith("/export")
				&& !request.uri.startsWith("/messages"))
			try {
				serve(request, writer);
				log.put(4, "HTTPServer::run() OK");
				break;
			} catch (Exception *e) {
				HTTPResponse response = HTTPResponse::notFound();
				response.write(writer);
				delete e;
				break;
			}

			HTTPResponse response = HTTPResponse::notFound();
			response.write(writer);
			break;
		}
		else if (request.method.equalsIgnoreCase("CDWP")) {
			Server server(socket.getOutputStream());
			server.run();
			break;
		}
		else {
			HTTPResponse response = HTTPResponse::notImplemented();
			response.write(writer);
			break;
		}
	}

	log.put(4, "HTTPServer::run() end");
}

void HTTPServer::serve(HTTPRequest &request, PrintWriter &writer) {
	String &method = request.method;
	String &uri = request.uri;
	String &host = request.host;

	int refresh = 0;
	if (uri.startsWith("/status.html?interval=")) {
		for (int i = uri.indexOf("=") + 1; i < uri.length(); i++)
			refresh = 10 * refresh + (uri.charAt(i) - '0');
		uri = "/status.html";
	}

	File file = new File(config.getConfigDir(), uri.getBytes());
	if (file.isDirectory())
		file = new File(file, "index.html");

	HTTPResponse response = new HTTPResponse();
	if (file.getPath().endsWith(".html")
			|| file.getPath().endsWith(".jnlp")) {
		StringBuffer workBuf = request.languages.load(file);

		if (uri.equals("/about.html")) {
			workBuf.delete_(0, workBuf.length());
			String str = status.getAbout();
			workBuf.append(str.getBytes());
		}

		if (uri.equals("/status.html")) {
			StringBuffer b;
			String no = new String(b + status.getStatusNo());
			replace(workBuf, "@STATUSNO@", no);

			String str = status.getStatus();
			if (!str.equals(""))
				replace(workBuf, "<!-- status -->", str);
			else {
				str = status.getCDROMs();
				replace(workBuf, "<!-- CDROMs -->", str);

				str = status.getConnects();
				replace(workBuf, "<!-- connects -->", str);

				str = status.getSessions();
				replace(workBuf, "<!-- sessions -->", str);

				str = status.getStore();
				replace(workBuf, "<!-- store -->", str);

				str = status.getStatistics();
				replace(workBuf, "<!-- statistics -->", str);

				str = status.getWriters();
				replace(workBuf, "<!-- writers -->", str);
			}
		}

		if (uri.equals("/config.html")) {
			String str = config.toHTML();
			replace(workBuf, "<!-- config -->", str);
		}

		String configDir = config.getConfigDir();
		replace(workBuf, "@CONFIGDIR@", configDir);

		if (workBuf.indexOf("<head>") >= 0) {
			File f = new File(config.getConfigDir(), "head");
			StringBuffer strBuf = request.languages.load(f);
			if (strBuf.length() > 0) {
				String str = new String(strBuf);
				replace(workBuf, "<head>", str);
			}

			if (refresh > 0) {
				String str;
				str = str
					+ "<head>\n"
					+ "<meta http-equiv=\"Refresh\""
					+ " content=\"" + refresh
					+ "; url=http://@SERVER@:@PORT@"
					+ "/status.html?interval="
					+ refresh + "\">";
				replace(workBuf, "<head>", str);
			}
		}

		if (workBuf.indexOf("<body>") >= 0) {
			File f = new File(config.getConfigDir(), "header");
			StringBuffer strBuf = request.languages.load(f);
			if (strBuf.length() > 0) {
				String str = new String(strBuf);
				replace(workBuf, "<body>", str);
			}

			f = new File(config.getConfigDir(), "footer");
			strBuf = request.languages.load(f);
			if (strBuf.length() > 0) {
				String str = new String(strBuf);
				replace(workBuf, "</body>", str);
			}
		}

		String author = Version.author;
		replace(workBuf, "@AUTHOR@", author);

		String authorHome = Version.authorHome;
		replace(workBuf, "@AUTHORHOME@", authorHome);

		if (config.getLogoURL() != NULL) {
			String logo = "";
			logo = logo
				+ "<img align=\"right\" src="
				+ "\"" + config.getLogoURL() + "\">";
			replace(workBuf, "<!-- logo -->", logo);
		}

		if (workBuf.indexOf("@LANGUAGE@") >= 0) {
			replace(workBuf, "@LANGUAGE@",
				request.languages.getWebCDcreatorLanguage());
			replace(workBuf, "@COUNTRY@",
				request.languages.getWebCDcreatorCountry());
		}

		if (workBuf.indexOf("@LOCALES@") >= 0) {
			String str = request.languages.getLocales();
			replace(workBuf, "@LOCALES@", str);
		}

		if (host.length() > 0)
			replace(workBuf, "@SERVER@", host);

		if (workBuf.indexOf("@PORT@") >= 0) {
			StringBuffer b;
			String port = new String(b + config.getPortNo());
			replace(workBuf, "@PORT@", port);
		}

		String product = Version.product;
		replace(workBuf, "@PRODUCT@", product);

		String projectHome = Version.projectHome;
		replace(workBuf, "@PROJECTHOME@", projectHome);

		if (workBuf.indexOf("<!-- StringBufferExisting -->") >= 0) {
			StringBuffer b;
			String n = new String(b + b.getExisting());
			replace(workBuf, "<!-- StringBufferExisting -->", n);
		}

		if (workBuf.indexOf("<!-- StringBufferNo -->") >= 0) {
			StringBuffer b;
			String no = new String(b + b.getNo());
			replace(workBuf, "<!-- StringBufferNo -->", no);
		}

		String version = Version.version;
		replace(workBuf, "@VERSION@", version);

		if (workBuf.indexOf("<i18n>") >= 0)
			request.languages.i18n(workBuf);

		if (workBuf.indexOf("<dd/>") >= 0) {
			time_t t;
			time(&t);
			struct tm *tm = localtime(&t);
			String year = toString(1900 + tm->tm_year, 4);
			String month = toString(tm->tm_mon + 1, 2);
			String day = toString(tm->tm_mday, 2);
			replace(workBuf, "<yyyy/>", year);
			replace(workBuf, "<mm/>", month);
			replace(workBuf, "<dd/>", day);
		}

		if (workBuf.indexOf("<time/>") >= 0) {
			time_t t;
			time(&t);
			struct tm *tm = localtime(&t);
			String hour = toString(tm->tm_hour, 2);
			String min = toString(tm->tm_min, 2);
			String sec = toString(tm->tm_sec, 2);
			String time = "";
			time = time + hour + ":" + min + ":" + sec;
			replace(workBuf, "<time/>", time);
		}

		if (workBuf.indexOf("utf-8") >= 0) {
			String str = "Ã¶";
			replace(workBuf, "ö", str);
		}

		String entity = new String(workBuf);
		String type;
		if (file.getPath().endsWith(".html"))
			type = "text/html";
		else
			type = "application/x-java-jnlp-file";
		response.setEntity(entity, type);
	} else
		response.setEntity(file);

	try {
		if (method.equalsIgnoreCase("HEAD"))
			response.writeHead(writer);
		else
			response.write(writer);
	}
	catch (Exception *e) {
		log.put(2, S.e + e->getMessage());
		delete e;
	}

	time_t t;
	time(&t);
	struct tm *tm = localtime(&t);
	log.put(1, S.e + socket.getInetAddress() + " - - ["
		+ (1900 + tm->tm_year)
		+ "-" + (tm->tm_mon < 9? "0": "") + (tm->tm_mon + 1)
		+ "-" + (tm->tm_mday < 10? "0": "") + tm->tm_mday
		+ " " + (tm->tm_hour < 10? "0": "") + tm->tm_hour
		+ ":" + (tm->tm_min < 10? "0": "") + tm->tm_min
		+ ":" + (tm->tm_sec < 10? "0": "") + tm->tm_sec
		+ "] \"" + request.method + " " + request.uri
		+ " " + request.protocol
		+ "\" " + response.getStatusCode()
		+ " " + response.getEntityBytesTransferred());
}

String *HTTPServer::toString(int n, int digits) {
	StringBuffer str;
	str.append(n);
	while (str.length() < digits)
		str.insert(0, '0');

	return new String(str);
}
