/* * @(#)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" + "\n" + "
" + "Translation outdated" + ". Compare with the original
\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(""); sb.append(""); sb.append("\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("\n"); } sb.append("
NameSizeDate
"); 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"); 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" + "\n" + "\n" + "
Server Configuration " + type + "
" + config.getFirstError() + solveLink + "
\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 + "\"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("