diff options
| -rw-r--r-- | README.txt | 7 | ||||
| -rw-r--r-- | plugins/fcgi/fcgi.cpp | 11 | ||||
| -rw-r--r-- | plugins/webbox/html/index.html | 9 | ||||
| -rw-r--r-- | plugins/webbox/html/webbox.js | 33 | ||||
| -rw-r--r-- | plugins/webbox/webbox.cpp | 94 | ||||
| -rw-r--r-- | webserver.conf | 12 | 
6 files changed, 125 insertions, 41 deletions
@@ -172,16 +172,9 @@ Configuration example:  Plugin: webbox  ~~~~~~~~~~~~~~ -This plugin needs a combination of static-files (for statically served parts) -and webbox plugins (for dynamic contents). -  Configuration example:     <path requested="/webbox1"> -    <plugin>static-files</plugin> -    <target>/usr/lib/webbox/html</target> -   </path> -   <path requested="/webbox1/bin">      <plugin>webbox</plugin>      <target>/home/rr/testbox</target>      <WEBBOX_NAME>Testbox1</WEBBOX_NAME> diff --git a/plugins/fcgi/fcgi.cpp b/plugins/fcgi/fcgi.cpp index f9d8a4d..a97f1c0 100644 --- a/plugins/fcgi/fcgi.cpp +++ b/plugins/fcgi/fcgi.cpp @@ -314,16 +314,6 @@ namespace {    return status + " " + message;   } - void DumpAppValues(const std::unordered_map<std::string, std::string>& app_values) - { -  std::cout << "App properties:" << std::endl; -  if (app_values.size() == 0) -   std::cout << " (empty)" << std::endl; -  else for (auto&[key, value]: app_values) { -   std::cout << " " << key << "=" << value << std::endl; -  } - } -  } // anonymous namespace  std::string fcgi_plugin::fcgiQuery(FCGIContext& context) @@ -448,7 +438,6 @@ std::string fcgi_plugin::fcgiQuery(FCGIContext& context)       std::cerr << "FCGI STDERR: " << r.getContent() << std::endl;      } else if (r.getType() == FCGI_GET_VALUES_RESULT) {       FCGI_DecodeEnv(r.getContent(), app_values); -     //DumpAppValues(app_values);      } else       throw std::runtime_error("Unhandled FCGI type: "s + std::to_string(r.getType()));     } catch (const std::length_error& ex) { diff --git a/plugins/webbox/html/index.html b/plugins/webbox/html/index.html index e9ea819..cc1cf12 100644 --- a/plugins/webbox/html/index.html +++ b/plugins/webbox/html/index.html @@ -4,22 +4,23 @@  		<meta charset="utf-8"/>  		<meta name="viewport" content="width=device-width, initial-scale=1">  		<title>Webbox</title> -		<link rel="stylesheet" type="text/css" href="webbox.css"/> -		<script src="webbox.js"></script> +		<link rel="stylesheet" type="text/css" href="webbox-html/webbox.css"/> +		<link rel="shortcut icon" href="webbox-html/favicon.ico" type="image/x-icon"/> +		<script src="webbox-html/webbox.js"></script>  	</head>  	<body onload="initMainpage();">  		<div>  			<table>  				<tr>  				<td class="title">Webbox</td> -				<td class="menusymbol" onclick="showMenu();"><img src="menu.png" alt=""/></td> +				<td class="menusymbol" onclick="showMenu();"><img src="webbox-html/menu.png" alt=""/></td>  				</tr>  			</table>  			<input id="uploadfile" type="file" onchange="onUploadFile();" multiple hidden/>  			<table class="menu">  				<tr>  					<td class="firsttd"></td> -					<td class="entry" onclick="refresh();"><img src="refresh-inverted.png" alt=""/></td> +					<td class="entry" onclick="refresh();"><img src="webbox-html/refresh-inverted.png" alt=""/></td>  				</tr>  			</table>  		</div> diff --git a/plugins/webbox/html/webbox.js b/plugins/webbox/html/webbox.js index 9a38051..602f76d 100644 --- a/plugins/webbox/html/webbox.js +++ b/plugins/webbox/html/webbox.js @@ -4,6 +4,13 @@ var numberOfSelectedRows = 0;  var username = "notaname";  var password = "password"; +function relativePath(path) { + if (path.startsWith("/")) +  return path.substr(1); + else +  return path; +} +  function clearContents() {  	var result = "<table class=\"list\">";  	// empty list @@ -35,9 +42,9 @@ function loadContents(dir) {  			for (var i = 0; i < listElements.length; i++) {  				var type = listElements[i].getAttribute("type");  				if (type == "file") { -					type = "<img src=\"file.png\"/>"; +					type = "<img src=\"webbox-html/file.png\"/>";  				} else if (type == "dir") { -					type = "<img src=\"directory.png\"/>"; +					type = "<img src=\"webbox-html/directory.png\"/>";  				} else {  					type = "";  				} @@ -57,7 +64,7 @@ function loadContents(dir) {  	} -	xhr.open("GET", "bin" + dir + "?command=list", true); +	xhr.open("GET", relativePath(dir) + "?command=list", true);  	xhr.setRequestHeader("Authorization", "Basic " + btoa(username + ":" + password));  	xhr.send();  } @@ -329,7 +336,7 @@ function initMainpage() {  		initMainpage2();  	} -	xhrTitle.open("GET", "bin?command=server-info", true); +	xhrTitle.open("GET", "?command=server-info", true);  	xhrTitle.setRequestHeader("Authorization", "Basic " + btoa(username + ":" + password));  	xhrTitle.send(); @@ -350,7 +357,7 @@ function initMainpage2() {  		document.getElementsByClassName("footer")[0].innerHTML = xhrFooter.responseText;  	} -	xhrFooter.open("GET", "bin?command=version", true); +	xhrFooter.open("GET", "?command=version", true);  	xhrFooter.setRequestHeader("Authorization", "Basic " + btoa(username + ":" + password));  	xhrFooter.send(); @@ -431,7 +438,7 @@ function download(filename) {  				filesElement.appendChild(fileElement);  			} -			xhr.open("POST", "bin" + currentDir + "?command=download-zip", true); +			xhr.open("POST", relativePath(currentDir) + "?command=download-zip", true);  			xhr.setRequestHeader("Authorization", "Basic " + btoa(username + ":" + password));  			xhr.setRequestHeader("Content-type", "text/xml");  			xhr.responseType = 'blob'; @@ -458,7 +465,7 @@ function download(filename) {  			dir += "/"  		}  		progressOn(); -		xhr.open("GET", "bin" + dir + filename, true); +		xhr.open("GET", relativePath(dir) + filename, true);  		xhr.setRequestHeader("Authorization", "Basic " + btoa(username + ":" + password));  		xhr.responseType = 'blob';  		xhr.send(); @@ -493,7 +500,7 @@ function createDir() {  		dirElement.appendChild(document.createTextNode(document.getElementById("newdir").value)); -		xhr.open("POST", "bin" + currentDir + "?command=newdir", true); +		xhr.open("POST", relativePath(currentDir) + "?command=newdir", true);  		xhr.setRequestHeader("Authorization", "Basic " + btoa(username + ":" + password));  		xhr.setRequestHeader("Content-type", "text/xml");  		xhr.send(xmlDocument); @@ -557,7 +564,7 @@ function onUploadFile() {          	formData.append("uploadfile", uploadfile.files[i]);  	} -        xhr.open("POST", "bin" + currentDir + "?command=upload", true); +        xhr.open("POST", relativePath(currentDir) + "?command=upload", true);  	xhr.setRequestHeader("Authorization", "Basic " + btoa(username + ":" + password));          xhr.send(formData);  } @@ -607,7 +614,7 @@ function deleteItems() {  			filesElement.appendChild(fileElement);  		} -		xhr.open("POST", "bin" + currentDir + "?command=delete", true); +		xhr.open("POST", relativePath(currentDir) + "?command=delete", true);  		xhr.setRequestHeader("Authorization", "Basic " + btoa(username + ":" + password));  		xhr.setRequestHeader("Content-type", "text/xml");  		xhr.send(xmlDocument); @@ -672,7 +679,7 @@ function move() {  			filesElement.appendChild(fileElement);  		} -		xhr.open("POST", "bin" + currentDir + "?command=move", true); +		xhr.open("POST", relativePath(currentDir) + "?command=move", true);  		xhr.setRequestHeader("Authorization", "Basic " + btoa(username + ":" + password));  		xhr.setRequestHeader("Content-type", "text/xml");  		xhr.send(xmlDocument); @@ -736,7 +743,7 @@ function rename() {  		newnameElement.appendChild(document.createTextNode(document.getElementById("renamenew").value));  		filesElement.appendChild(newnameElement); -		xhr.open("POST", "bin" + currentDir + "?command=rename", true); +		xhr.open("POST", relativePath(currentDir) + "?command=rename", true);  		xhr.setRequestHeader("Authorization", "Basic " + btoa(username + ":" + password));  		xhr.setRequestHeader("Content-type", "text/xml");  		xhr.send(xmlDocument); @@ -784,7 +791,7 @@ function info() {  		filesElement.appendChild(fileElement);  	} -	xhr.open("POST", "bin" + currentDir + "?command=info", true); +	xhr.open("POST", relativePath(currentDir) + "?command=info", true);  	xhr.setRequestHeader("Authorization", "Basic " + btoa(username + ":" + password));  	xhr.setRequestHeader("Content-type", "text/xml");  	xhr.send(xmlDocument); diff --git a/plugins/webbox/webbox.cpp b/plugins/webbox/webbox.cpp index 7d15ee9..c58bd0c 100644 --- a/plugins/webbox/webbox.cpp +++ b/plugins/webbox/webbox.cpp @@ -30,6 +30,11 @@ namespace {   static const std::string PROGRAMVERSION{"Webbox 2.0"};   static const std::string DOWNLOAD_FILENAME{"webbox-download.zip"}; + // STATIC_HTML_TARGET: no leading slash because comparision is done with relative path; + // trailing slash because of path comparison + static const std::string STATIC_HTML_TARGET{"webbox-html/"}; + static const fs::path STATIC_HTML_DOC_ROOT{"/usr/lib/webbox/html"}; +   // TODO: separate out   class Tempfile   { @@ -109,6 +114,8 @@ namespace {       result[urlDecode(i.substr(0, pos))] = urlDecode(i.substr(pos + 1));      }     } +  } else if (s == "/" || s == "" || boost::algorithm::starts_with(s, STATIC_HTML_TARGET)) { +   result["command"] = "static-html";    }    return result; @@ -125,6 +132,7 @@ namespace {     std::string webboxPath;     std::string webboxName;     bool webboxReadOnly; +   fs::path webboxStaticHtml;     CommandParameters(       std::function<std::string(const std::string& key)>& GetServerParam, @@ -138,7 +146,10 @@ namespace {      , webboxPath(m_GetRequestParam("doc_root"))      , webboxName(m_GetRequestParam("WEBBOX_NAME"))      , webboxReadOnly(m_GetRequestParam("WEBBOX_READONLY") == "1") +    , webboxStaticHtml(m_GetRequestParam("WEBBOX_STATIC_HTML"))     { +    if (webboxStaticHtml == "") +     webboxStaticHtml = STATIC_HTML_DOC_ROOT;     }   }; @@ -780,7 +791,83 @@ protected:     return result;    } catch (const std::exception& ex) { -   return HttpStatus("500", "Bad file: "s + m_path.filename().string(), p); +   return HttpStatus("404", "Bad file: "s + m_path.filename().string(), p); +  } + } +}; + +// Return a reasonable mime type based on the extension of a file. +static std::string +mime_type(fs::path path) +{ + using boost::algorithm::iequals; + auto const ext = [&path] + { +  size_t pos = path.string().rfind("."); +  if (pos == std::string::npos) +   return std::string{}; +  return path.string().substr(pos); + }(); + if(iequals(ext, ".htm"))  return "text/html"; // TODO: unordered_map + if(iequals(ext, ".html")) return "text/html"; + if(iequals(ext, ".php"))  return "text/html"; + if(iequals(ext, ".css"))  return "text/css"; + if(iequals(ext, ".txt"))  return "text/plain"; + if(iequals(ext, ".js"))   return "application/javascript"; + if(iequals(ext, ".json")) return "application/json"; + if(iequals(ext, ".xml"))  return "application/xml"; + if(iequals(ext, ".swf"))  return "application/x-shockwave-flash"; + if(iequals(ext, ".flv"))  return "video/x-flv"; + if(iequals(ext, ".png"))  return "image/png"; + if(iequals(ext, ".jpe"))  return "image/jpeg"; + if(iequals(ext, ".jpeg")) return "image/jpeg"; + if(iequals(ext, ".jpg"))  return "image/jpeg"; + if(iequals(ext, ".gif"))  return "image/gif"; + if(iequals(ext, ".bmp"))  return "image/bmp"; + if(iequals(ext, ".ico"))  return "image/vnd.microsoft.icon"; + if(iequals(ext, ".tiff")) return "image/tiff"; + if(iequals(ext, ".tif"))  return "image/tiff"; + if(iequals(ext, ".svg"))  return "image/svg+xml"; + if(iequals(ext, ".svgz")) return "image/svg+xml"; + return "application/text"; +} + +class StaticHtmlCommand: public GetCommand +{ +public: + StaticHtmlCommand() + { +  m_commandName = "static-html"; +  m_isWriteCommand = false; + } + +protected: + virtual std::string start(CommandParameters& p) + { +  // redirect to xyz/ if xyz was requested +  std::string target = p.m_GetRequestParam("target"); +  if (m_pathInfo == "" && !target.empty() && target.back() != '/') { +   p.m_SetResponseHeader("location", target + "/"); +   return HttpStatus("301", "Use correct index: /"s, p); +  }  + +  try { + +   fs::path file_path; +   if (m_pathInfo == "/" || m_pathInfo == "") { +    file_path = p.webboxStaticHtml / "index.html"; +   } else if (boost::algorithm::starts_with(m_pathInfo, STATIC_HTML_TARGET)) { +    file_path = p.webboxStaticHtml / m_pathInfo.substr(STATIC_HTML_TARGET.size()); +   } else { +    return HttpStatus("500", "Bad request: "s + m_pathInfo, p); +   } +    +   p.m_SetResponseHeader("content_type", mime_type(file_path)); +   std::string result{File::getFile(file_path)}; +   return result; + +  } catch (const std::exception& ex) { +   return HttpStatus("500", "Server error: "s + m_pathInfo, p);    }   }  }; @@ -805,6 +892,7 @@ webbox_plugin::webbox_plugin()   registerCommand(std::make_shared<RenameCommand>());   registerCommand(std::make_shared<UploadCommand>());   registerCommand(std::make_shared<DownloadCommand>()); + registerCommand(std::make_shared<StaticHtmlCommand>());  }  webbox_plugin::~webbox_plugin() @@ -818,8 +906,10 @@ std::string webbox_plugin::generate_page(    std::function<void(const std::string& key, const std::string& value)>& SetResponseHeader // to be added to result string  )  { - CommandParameters commandParameters(GetServerParam, GetRequestParam, SetResponseHeader); + // Queries under STATIC_HTML_TARGET will be served statically from STATIC_HTML_DOC_ROOT + CommandParameters commandParameters(GetServerParam, GetRequestParam, SetResponseHeader); +    std::string commandName;   auto it {commandParameters.paramHash.find("command")}; diff --git a/webserver.conf b/webserver.conf index efc1a69..4901578 100644 --- a/webserver.conf +++ b/webserver.conf @@ -23,16 +23,20 @@     </path>     <path requested="/webbox1"> -    <plugin>static-files</plugin> -    <target>/home/ernie/code/webserver/plugins/webbox/html</target> -   </path> -   <path requested="/webbox1/bin">      <plugin>webbox</plugin>      <target>/home/ernie/testbox</target>      <WEBBOX_NAME>Testbox1</WEBBOX_NAME>      <WEBBOX_READONLY>0</WEBBOX_READONLY> +    <WEBBOX_STATIC_HTML>/home/ernie/code/webserver/plugins/webbox/html</WEBBOX_STATIC_HTML>      <auth login="abc" password="p3p0Jka3YM5Fk"/>     </path> +   <path requested="/webbox2"> +    <plugin>webbox</plugin> +    <target>/home/ernie/testbox</target> +    <WEBBOX_NAME>Testbox1</WEBBOX_NAME> +    <WEBBOX_READONLY>1</WEBBOX_READONLY> +    <WEBBOX_STATIC_HTML>/home/ernie/code/webserver/plugins/webbox/html</WEBBOX_STATIC_HTML> +   </path>     <path requested="/blog">      <plugin>weblog</plugin>      <target>/home/ernie/testblog</target>  | 
