/*
* @(#)HTTPServer.cpp
*
* This file is part of webCDwriter - Network CD/DVD Writing.
*
* Copyright (C) 2002-2006 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::appendOutdatedWarning(String &str, File &file) {
String name = file.getName();
int p0 = name.lastIndexOf(".");
if (p0 > 0)
str = str
+ "\n
\n"
+ ""
+ "Translation outdated"
+ ". Compare with the original |
\n"
+ "
\n";
}
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;
String version = "2.6.8";
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 (request.method.equalsIgnoreCase("CDWPS")) {
const char str[] = "SSL: not available\n";
socket.write(str, strlen(str));
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);
}
config.setResult(PASSWORD,
"OK");
}
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.endsWith(".jnlp")) {
request.uri = "/config/error/oneClick.html";
serve(request, writer);
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();
HTTPServer::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 (file.isDirectory()) {
if (!uri.endsWith("/")) {
String url = uri;
url = url + "/";
HTTPResponse response = HTTPResponse::movedPermanently(url);
response.write(writer);
logResponse(request, response);
return;
}
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()) {
if (uri.equals("/index.html")) {
uri = "/status.html";
file = new File(httpDir, uri.getBytes());
}
else
if (uri.startsWith("/webCDcreator/doc/")) {
String url = "http://JoergHaeger.de";
url = url + uri;
HTTPResponse response = HTTPResponse::movedPermanently(url);
response.write(writer);
logResponse(request, response);
return;
}
}
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());
}
bool index_html = uri.equals("/index.html");
bool status_html = uri.equals("/status.html");
if (index_html || status_html) {
if (config.getFirstErrorPriority() >= 100)
Server::activateConfig();
String license = Server::getLicense();
if (license.length() == 0) {
Socket socket;
ServerPro server(socket);
}
}
if (status_html) {
StringBuffer b;
String no = new String(b + status.getStatusNo());
replace(workBuf, "@STATUSNO@", no);
String str = status.getStatus();
// if (str.length() > 0)
// replace(workBuf, "", str);
str = status.getConnects();
replace(workBuf, "", str);
str = status.getSessions();
replace(workBuf, "", str);
str = status.getStatistics();
replace(workBuf, "", str);
str = status.getStore();
replace(workBuf, "", str);
str = status.getWriters();
replace(workBuf, "", str);
}
if (uri.equals("/config.html")) {
String str = config.toHTML();
replace(workBuf, "", str);
}
if (uri.startsWith("/config/") || isConfig) {
if (workBuf.indexOf("") >= 0) {
StringBuffer sb = config.getFirstErrorHelp();
String str = "\n\n";
replace(sb, "[", str);
str = "\n |
\n";
replace(sb, "]", str);
if (sb.length() > 0) {
String str = new String(sb);
replace(workBuf, "", str);
}
}
if (workBuf.indexOf("") >= 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, "", str);
}
}
if (isAdmin && workBuf.indexOf("") >= 0) {
String str = log.getSupportLog();
replace(workBuf, "", str);
}
if (workBuf.indexOf("") >= 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, "", str);
}
}
String str = config.getWritersForm();
replace(workBuf, "", 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 + 5);
if (p < 0)
break;
if (p - p0 <= 5 + 40) {
String str0 = workBuf;
String str;
int p1 = p;
while (p1 > p0 + 5 && str0.charAt(p1 - 1) == ' ')
p1--;
str = str0.substring(p0 + 5, p1);
try {
String input = "";
if (!str.startsWith("SELECT")) {
String checked = "";
if (config.getEditorBoolean(str))
checked = " checked=\"checked\"";
input = input
+ ""
+ ""
+ " " + str;
}
else {
// integer
str = str.substring(6);
String value = config.getEditorValue(str);
input = input
+ "";
}
String str = str0.substring(p0, p + 3);
replace(workBuf, str.getBytes(), input);
p0 += input.length();
}
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 + 3);
replace(workBuf, str.getBytes(), error);
p0 += error.length();
}
catch (Exception *e2) {
delete e2;
p0 = p + 3;
}
}
else
p0 = p + 3;
}
}
else
p0 = p + 3;
}
}
const char *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("Directory ");
String str = uri;
int p0 = str.lastIndexOf("/");
if (p0 >= 0)
str = str.substring(0, p0 + 1);
sb.append(str.getBytes());
sb.append("
\n");
sb.append("\n");
sb.append("Name | ");
sb.append("Size | ");
sb.append("Date |
\n");
const int namesMax = 400;
String *names[namesMax];
int i = 0;
struct dirent *dirEntry;
while ((dirEntry = readdir(dir)) != NULL) {
String name = dirEntry->d_name;
if (name.equals(".") || name.equals("toc.html"))
continue;
int j;
for (j = i; j > 0; j--) {
if (names[j - 1]->compareToIgnoreCase(
dirEntry->d_name) <= 0)
break;
names[j] = names[j - 1];
}
names[j] = new String(dirEntry->d_name);
i++;
if (i == namesMax)
break;
}
closedir(dir);
for (int j = 0; j < i; j++) {
String name = names[j];
File f2 = new File(dirStr, name.getBytes());
sb.append("");
sb.append(name.getBytes());
if (f2.isDirectory())
sb.append("/");
sb.append(" | ");
sb.append(f2.length());
sb.append(" | ");
str = Server::timeToString(f2.lastModified());
sb.append(str.getBytes());
sb.append(" |
\n");
}
sb.append("
\n");
str = new String(sb);
replace(workBuf, directory, str);
}
}
if (workBuf.indexOf("") >= 0) {
File f = new File(httpDir, "head");
StringBuffer strBuf = request.languages.load(f);
if (strBuf.length() > 0) {
String str = new String(strBuf);
replace(workBuf, "", str);
}
if (refresh > 0) {
String str;
str = str
+ "\n"
+ "";
replace(workBuf, "", str);
}
}
{
String str = "href=\"";
str = str + config.getActiveValue("CSSFile") + "\"";
replace(workBuf, "href=\"/default.css\"", str);
}
if (workBuf.indexOf("") >= 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 url = config.getFirstErrorURL();
int p = url.indexOf("#");
if (p > 0)
url = url.substring(0, p);
String solveLink = "";
if (!uri.startsWith("/config/error") && !uri.equals(url))
solveLink = solveLink
+ " Solve...";
String bgcolor = "\"#FF8080\"";
String type = "Error";
if (config.getFirstErrorPriority() < 100) {
bgcolor = "\"#FFFF80\"";
type = "Warning";
}
str = str
+ "\n"
+ "Server Configuration " + type
+ " |
\n"
+ "" + config.getFirstError()
+ solveLink + " |
\n"
+ "
\n
\n";
}
if (!upToDate) {
appendOutdatedWarning(str, file);
str = str + "
\n";
}
replace(workBuf, "", str);
}
f = new File(httpDir, "footer");
strBuf = request.languages.load(f);
if (strBuf.length() > 0) {
String str = new String(strBuf);
replace(workBuf, "", str);
}
}
else
if (!upToDate) {
int pos = workBuf.indexOf("= 0) {
pos = workBuf.indexOf(">", pos);
if (pos >= 0 && pos < workBuf.length()) {
String str = "";
appendOutdatedWarning(str, file);
workBuf.insert(pos + 1, str.getBytes());
}
}
}
String author = Version.author;
replace(workBuf, "@AUTHOR@", author);
String authorHome = Version.authorHome;
replace(workBuf, "@AUTHORHOME@", authorHome);
String writerType = "CD";
replace(workBuf, "@CD@", writerType);
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
+ "";
replace(workBuf, "", 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("") >= 0)
request.languages.i18n(workBuf);
if (workBuf.indexOf("") >= 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, "", year);
replace(workBuf, "", month);
replace(workBuf, "", day);
}
if (workBuf.indexOf("") >= 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);
}
if (workBuf.indexOf("utf-8") >= 0) {
String str = "ö";
replace(workBuf, "ö", str);
}
HTTPServer::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);
}