/*
 * @(#)HTTPServer.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, 18.04.2002
 */

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

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

HTTPServer::~HTTPServer() {
}

void HTTPServer::logResponse(class HTTPRequest &request,
		class HTTPResponse &response) {
	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());
}

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

StringBuffer &HTTPServer::replace(StringBuffer &buf,
		const char *key, String &by) {
	log.debug("HTTPServer::replace", S.e + 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::replace", S.e + buf.length());
	}
	return buf;
}

void HTTPServer::run() {
	log.put(6, 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();

		bool GET = request.method.equalsIgnoreCase("GET");
		bool HEAD, POST;
		if (!GET) {
			HEAD = request.method.equalsIgnoreCase("HEAD");
			if (!HEAD)
				POST = request.method.equalsIgnoreCase("POST");
		}

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

			if (request.uri.indexOf("..") >= 0
					|| !request.uri.startsWith("/")) {
				HTTPResponse response = HTTPResponse::notFound();
				response.write(writer);
				logResponse(request, response);
				break;
			}
		}

		if (GET || HEAD) {
			if (request.uri.endsWith(".html")) {
				int forNetscape4 = 0;
				if (request.uri.equals("/webCDcreator/4netscape.html")) {
					forNetscape4 = 1;
					File file = new File(config.getHttpDir(),
						"/webCDcreator/4netscape/swing.jar");
					if (!file.exists())
						request.uri = "/webCDcreator/4netscape/swing.html";
				}

				if (request.uri.endsWith("/4MJR.html")
					|| forNetscape4
					|| request.uri.endsWith("/4pluginIE.html")
					|| request.uri.endsWith("/4pluginRSA.html")) {
					File file = new File(config.getHttpDir(),
						"/webCDcreator/4pluginRSA/webCDcreator.jar");
					String str = "/webCDcreator/4pluginRSA/version-";
					String version = Version.version;
					int len = version.length();
					if (len > 0) {
						char ch = version.charAt(len - 1);
						if (ch >= 'a' && ch <= 'z')
							version = version.substring(0, len - 1);
					}
					str = str + version;
					File versionFile = new File(config.getHttpDir(), str);
					bool OK = versionFile.exists();
					for (char ch = 'a'; !OK && ch <= 'z'; ch++) {
						String str2 = str;
						str2 = str2 + ch;
						versionFile = new File(config.getHttpDir(), str2);
						OK = versionFile.exists();
					}
					if (!file.exists() || !OK)
						request.uri = "/webCDcreator/4pluginRSA/info.html";
				}
			}

			try {
				serve(request, writer);
				log.put(6, "HTTPServer::run() OK");
			} catch (Exception *e) {
				HTTPResponse response = HTTPResponse::notFound();
				response.write(writer);
				logResponse(request, response);
				delete e;
			}
			break;
		}
		else if (request.method.equalsIgnoreCase("CDWP")) {
			ServerPro server(socket);
			server.run();
			break;
		}
		else if (POST && request.uri.startsWith("/config/")) {
			log.put(2, S.e + "POST " + request.uri);
			static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
			try {
				pthread_mutex_lock(&mutex);
				HTTPResponse response = new HTTPResponse();
				const char LF = '\n', CR = '\r';
				StringBuffer sb = new StringBuffer(request.contentLength);
				for (int i = 0; i < request.contentLength; i++) {
					char ch = socket.getInputStream().read();
					if (ch == CR) {
						// CRLF -> LF
						if (i < request.contentLength - 1) {
							ch = socket.getInputStream().read();
							i++;
							if (ch != LF)
								sb.append(LF);
						}
						else
							ch = LF;
					}
					else
					if (ch == '&')
						ch = LF;
					else
					if (ch == '+')
						ch = ' ';
					else
					if (ch == '%' && i < request.contentLength - 2) {
						char hex[2];
						hex[0] = socket.getInputStream().read();
						hex[1] = socket.getInputStream().read();
						i += 2;
						ch = Server::hextoint(hex, 2);
						if (ch < 0)
							ch = '_';
					}

					sb.append(ch);
				}

				bool isAdmin = false, isConfig = false;
				try {
					String str = new String(sb);
					config.updateEditor(str);
					isAdmin = true;
					if (request.uri.indexOf("/config/") >= 0
							&& request.uri.indexOf("/support.html") >= 0)
						config.resetWarning();
					else {
						String passwdKey = PASSWORD;
						String password = config.getEditorValue(passwdKey);
						Server::activateConfig();
						config.setEditor(PASSWORD, password);
					}
				}
				catch (Exception *e) {
					log.put(5, S.e
						+ "HTTPServer::run(): " + e->toString());
					if (request.uri.indexOf("/support.html") >= 0) {
						request.uri = "/support.html";
						isConfig = true;
					}
					delete e;
				}

				serve(request, writer, isAdmin, isConfig);
				config.resetEditor();
				pthread_mutex_unlock(&mutex);
			}
			catch (Exception *e) {
				config.resetEditor();
				pthread_mutex_unlock(&mutex);
				HTTPResponse response = HTTPResponse::notImplemented();
				response.write(writer);
				logResponse(request, response);
				delete e;
			}
			break;
		}
		else if (POST && request.uri.startsWith("/cgi-bin/")) {
			log.put(2, S.e + "POST2 " + request.uri);
			File file = new File(httpDir, request.uri.getBytes());
			if (file.exists()) {
				Process p(file.getPath());
				p.show();
				p.start();
				StringBuffer sb = new StringBuffer(4 * 1024);
				sb.append("HTTP/1.0 200 OK\n");

				int i = 0, j = 0;
				while (true) {
					log.debug("POST2", S.e + "i = " + i + " " + (j++));
					fd_set inFdSet, outFdSet;
					FD_ZERO(&inFdSet);
					FD_SET(p.getInputStream(), &inFdSet);
					FD_ZERO(&outFdSet);
					if (i < request.contentLength)
						FD_SET(p.getOutputStream(), &outFdSet);
					struct timeval tv;
					tv.tv_sec = 60;
					tv.tv_usec = 0;
					int n = ::select(
						FD_SETSIZE, &inFdSet, &outFdSet, NULL, &tv);
					if (n <= 0)
						break;
					if (FD_ISSET(p.getInputStream(), &inFdSet)) {
						char buf[4 * 1024];
						int n = read(p.getInputStream(), buf, sizeof buf);
						log.debug("POST2", S.e + "n = " + n);
						if (n <= 0)
							break;
//						writer.write(buf, 0, n);
						sb.append(buf, 0, n);
						log.debug("POST2", S.e + "*");
					}
					if (i < request.contentLength
							&& FD_ISSET(p.getOutputStream(), &outFdSet)) {
						char ch = socket.getInputStream().read();
						int n = write(p.getOutputStream(), &ch, 1);
						i++;
						if (i == request.contentLength)
							p.closePipeToChild();
					}
				}

				HTTPResponse response = new HTTPResponse();
				String entity = new String(sb);
				String entityType = "text/html";
				response.setEntity(entity, entityType);
//				response.write(writer);
				writer.write(sb);
				writer.flush();
				logResponse(request, response);
			}
			else {
				HTTPResponse response = HTTPResponse::notImplemented();
				response.write(writer);
				logResponse(request, response);
			}
			log.put(2, S.e + "POST2 end");
			break;
		}
		else {
			HTTPResponse response = HTTPResponse::notImplemented();
			response.write(writer);
			logResponse(request, response);
			break;
		}
	}

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

void HTTPServer::serve(HTTPRequest &request,
			PrintWriter &writer,
			bool isAdmin, bool isConfig) {
	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";
	}

	String address = socket.getInetAddress();
	HTTPServerPro::parseURI(address, uri);

	String httpDir = config.getHttpDir();

	// virtual host?
	File file = new File(httpDir, host.getBytes());
	if (file.exists())
		httpDir = file.getPath();

	file = new File(httpDir, uri.getBytes());
	if (uri.endsWith("/")) {
		file = new File(file, "index.html");
		if (file.exists())
			uri = uri + "index.html";
		else {
			file = new File(httpDir, uri.getBytes());
			file = new File(file, "toc.html");
			if (file.exists())
				uri = uri + "toc.html";
			else
				uri = uri + "index.html";
		}
	}

	if (!file.exists() && uri.equals("/index.html")) {
		uri = "/status.html";
		file =  new File(httpDir, uri.getBytes());
	}

	HTTPResponse response = new HTTPResponse();
	bool html = file.getPath().endsWith(".html")
		|| file.getPath().endsWith(".htm");
	if (html || file.getPath().endsWith(".jnlp")) {
		int lastModified = file.lastModified();
		StringBuffer workBuf = request.languages.load(file);
		bool upToDate = request.languages.getFileLastModified()
			>= lastModified;

		if (uri.equals("/about.html")) {
			String license = Server::getLicense();
			if (!license.startsWith("GNU")) {
				Socket socket;
				ServerPro server(socket);
			}
			workBuf.delete_(0, workBuf.length());
			String str = status.getAbout();
			workBuf.append(str.getBytes());
		}

		if (uri.equals("/status.html")) {
			if (config.getFirstErrorPriority() >= 100)
				Server::activateConfig();

			String license = Server::getLicense();
			if (license.length() == 0) {
				Socket socket;
				ServerPro server(socket);
			}

			StringBuffer b;
			String no = new String(b + status.getStatusNo());
			replace(workBuf, "@STATUSNO@", no);

			String str = status.getStatus();
//			if (str.length() > 0)
//				replace(workBuf, "<!-- status -->", str);

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

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

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

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

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

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

		if (uri.startsWith("/config/") || isConfig) {
			if (workBuf.indexOf("<!-- firstErrorHelp -->") >= 0) {
				StringBuffer sb = config.getFirstErrorHelp();
				String str = "\n<table bgcolor=#E0E0E0 "
					"cellpadding=10 width=\"100%\"><tr><td>\n";
				replace(sb, "[", str);
				str = "\n</tr></td></table>\n";
				replace(sb, "]", str);
				if (sb.length() > 0) {
					String str = new String(sb);
					replace(workBuf, "<!-- firstErrorHelp -->", str);
				}
			}

			if (workBuf.indexOf("<!-- menu -->") >= 0) {
				File f1 = new File(httpDir, "config");
				File f2 = new File(f1, "menu");
				StringBuffer sb = request.languages.load(f2);
				if (sb.length() > 0) {
					String str = new String(sb);
					replace(workBuf, "<!-- menu -->", str);
				}
			}

			if (isAdmin && workBuf.indexOf("<!-- log -->") >= 0) {
				String str = log.getSupportLog();
				replace(workBuf, "<!-- log -->", str);
			}

			if (workBuf.indexOf("<!-- writeBox -->") >= 0) {
				File f1 = new File(httpDir, "config");
				File f2 = new File(f1, "writeBox");
				StringBuffer sb = request.languages.load(f2);
				if (sb.length() > 0) {
					String str = new String(sb);
					replace(workBuf, "<!-- writeBox -->", str);
				}
			}

			String str = config.getWritersForm();
			replace(workBuf, "<!-- writers -->", str);

			if (workBuf.indexOf("@GROUP@") >= 0) {
				String str = config.getGroup();
				replace(workBuf, "@GROUP@", str);
			}

			if (workBuf.indexOf("@USER@") >= 0) {
				String str = config.getUser();
				replace(workBuf, "@USER@", str);
			}

			int p0 = 0;
			while (true) {
				p0 = workBuf.indexOf("@", p0);
				if (p0 < 0)
					break;
				int p = workBuf.indexOf("@", p0 + 1);
				if (p < 0)
					break;
				if (p - p0 < 24) {
					String str0 = workBuf;
					String str = str0.substring(p0 + 1, p);
					try {
						String value = config.getEditorValue(str);
						String str = str0.substring(p0, p + 1);
						replace(workBuf, str.getBytes(), value);
						p = p + value.length() - str.length();
					}
					catch (Exception *e) {
						log.debug("HTTPServer::serve",
							S.e + "replace error: " + e->toString());
						delete e;
					}
				}
				p0 = p + 1;
			}

			p0 = 0;
			while (true) {
				p0 = workBuf.indexOf("<!-- ", p0);
				if (p0 < 0)
					break;
				int p = workBuf.indexOf(" -->", p0 + 1);
				if (p < 0)
					break;
				if (p - p0 < 40) {
					String str0 = workBuf;
					String str = str0.substring(p0 + 5, p);
					try {
						String input = "";
						if (!str.startsWith("SELECT")) {
							String checked = "";
							if (config.getEditorBoolean(str))
								checked = " checked=\"checked\"";
							input = input
								+ "<input type=\"hidden\""
									+ " name=\"" + str + "\""
									+ " value=\"off\">"
								+ "<input type=\"checkbox\""
									+ " name=\"" + str + "\""
									+ " value=\"on\""
									+ checked + ">"
									+ " " + str;
						}
						else {
							// integer <select ...>...</select>
							str = str.substring(6);
							String value = config.getEditorValue(str);
							input = input
							+ "<select name=\"" + str + "\""
							+ " size=\"1\">\n";
							char *options[] = {
								"<i18n>Never</i18n>",
								"<i18n>Option</i18n> (<i18n>off</i18n>)",
								"<i18n>Option</i18n> (<i18n>on</i18n>)",
								"<i18n>Always</i18n>" };
							for (int i = 0; i < 4; i++) {
								String iStr = "";
								iStr = iStr + i;
								input = input + "\t<option "
									+ (iStr.equals(value)? "selected ": "")
									+ "value=\"" + i + "\">"
									+ options[i] + "</option>\n";
							}
							input = input + "</select>";
						}
						String str = str0.substring(p0, p + 4);
						replace(workBuf, str.getBytes(), input);
					}
					catch (Exception *e) {
						delete e;
						if (str.endsWith("Error"))
						try {
							String str2 = str.substring(0, str.length() - 5);
							String error = config.getError(str2.getBytes());
							String str = str0.substring(p0, p + 4);
							replace(workBuf, str.getBytes(), error);
						}
						catch (Exception *e2) {
							delete e2;
						}
					}
				}
				p0 = p + 1;
			}
		}

		const char *directory = "<!-- directory -->";
		if (workBuf.indexOf(directory) >= 0) {
			String dirStr = file.getParent();
			DIR *dir = opendir(dirStr.getBytes());
			if (dir != NULL) {
				StringBuffer sb = new StringBuffer(64 * 1024, 16 * 1024);
				sb.append("<h1>Directory ");
				String str = uri;
				int p0 = str.lastIndexOf("/");
				if (p0 >= 0)
					str = str.substring(0, p0 + 1);
				sb.append(str.getBytes());
				sb.append("</h1>\n");
				sb.append("<table>\n");
				sb.append("<tr><th><i18n>Name</i18n></th>");
				sb.append("<th><i18n>Date</i18n></th>");
				sb.append("<th><i18n>Size</i18n></th></tr>\n");
				struct dirent *dirEntry;
				while ((dirEntry = readdir(dir)) != NULL) {
					String name = dirEntry->d_name;
					if (name.equals(".") || name.equals("toc.html"))
						continue;
					File f2 = new File(dirStr, dirEntry->d_name);
					sb.append("<tr><td><a href=\"");
					sb.append(dirEntry->d_name);
					if (f2.isDirectory())
						sb.append("/");
					sb.append("\">");
					sb.append(dirEntry->d_name);
					if (f2.isDirectory())
						sb.append("/");
					sb.append("</a></td><td align=\"right\">");
					str = Server::timeToString(f2.lastModified());
					sb.append(str.getBytes());
					sb.append("</td><td align=\"right\">");
					sb.append(f2.length());
					sb.append("</td></tr>\n");
				}
				closedir(dir);
				sb.append("</table>\n");
				str = new String(sb);
				replace(workBuf, directory, str);
			}
		}

		if (workBuf.indexOf("<head>") >= 0) {
			File f = new File(httpDir, "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);
			}
		}

		{
			String str = "href=\"";
			str = str + config.getActiveValue("CSSFile") + "\"";
			replace(workBuf, "href=\"/default.css\"", str);
		}

		if (workBuf.indexOf("<body>") >= 0) {
			File f = new File(httpDir, "header");
			StringBuffer strBuf = request.languages.load(f);
			if (strBuf.length() > 0) {
				String str = new String(strBuf);
				if (config.getFirstError().length() > 0) {
					String solveLink = "";
					if (!uri.startsWith("/config/error"))
						solveLink = solveLink
							+ "&nbsp;&nbsp;<a href=\""
								+ config.getFirstErrorURL()
							+ "\"><i18n>Solve</i18n>...</a>";
					String bgcolor = "\"#FF8080\"";
					String type = "Error";
					if (config.getFirstErrorPriority() < 100) {
						bgcolor = "\"#FFFF80\"";
						type = "Warning";
					}
					str = str
						+ "<table bgcolor=" + bgcolor
							+ " cellspacing=5 width=\"100%\">\n"
						+ "<tr><td><b>Server Configuration " + type
							+ "</b></td></tr>\n"
						+ "<tr><td>" + config.getFirstError()
							+ solveLink + "</td></tr>\n"
						+ "</table>\n<br>\n";
				}
				if (!upToDate) {
					String name = file.getName();
					int p0 = name.lastIndexOf(".");
					if (p0 > 0)
					str = str
						+ "<table bgcolor=\"#FFC0C0\""
							+ " cellspacing=5 width=\"100%\">\n"
						+ "<tr><td><b>"
						+ "<i18n>Translation outdated</i18n>"
						+ ". <i18n>Compare with the</i18n> <a href=\""
						+ name.substring(0, p0) + "_en"
							+ name.substring(p0)
						+ "\"><i18n>original</i18n></a></td></tr>\n"
						+ "</table>\n<br>\n";
				}
				replace(workBuf, "<body>", str);
			}

			f = new File(httpDir, "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 (workBuf.indexOf("@CONFIGDIR@") >= 0) {
			String configDir = config.getConfigDir();
			if (!configDir.endsWith("/"))
				configDir = configDir + '/';
			replace(workBuf, "@CONFIGDIR@/", configDir);
		}

		if (workBuf.indexOf("@HTTPDIR@") >= 0) {
			String httpDir = config.getHttpDir();
			if (!httpDir.endsWith("/"))
				httpDir = httpDir + '/';
			replace(workBuf, "@HTTPDIR@/", httpDir);
		}

		if (config.getLogoURL().length() > 0) {
			String logo = "";
			logo = logo
				+ "<img align=\"right\" src="
				+ "\"" + config.getLogoURL() + "\" alt=\"Logo\">";
			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);
		}

		HTTPServerPro::process(address, file, workBuf);

		String entity = new String(workBuf);
		String type;
		if (html)
			type = "text/html";
		else
			type = "application/x-java-jnlp-file";
		response.setEntity(entity, type);
	}
	else {
		response.setEntity(file, request.ifModifiedSince);
		if (request.ifModifiedSince > 0)
			log.debug("HTTPRequest2", S.e + "ifModifiedSince = "
				+ Server::timeToString(request.ifModifiedSince));
	}

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

	logResponse(request, response);
}

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

	return new String(str);
}

pthread_mutex_t HTTPServerPro::mutex = PTHREAD_MUTEX_INITIALIZER;
String HTTPServerPro::address0 = "";
String HTTPServerPro::args = "";
time_t HTTPServerPro::t0 = 0;
