diff options
Diffstat (limited to 'plugins/webbox/webbox.cpp')
-rw-r--r-- | plugins/webbox/webbox.cpp | 164 |
1 files changed, 69 insertions, 95 deletions
diff --git a/plugins/webbox/webbox.cpp b/plugins/webbox/webbox.cpp index c58bd0c..53322b5 100644 --- a/plugins/webbox/webbox.cpp +++ b/plugins/webbox/webbox.cpp @@ -3,6 +3,8 @@ #include "file.h" #include "stringutil.h" +#include "libcommon/mime.h" + #include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/replace.hpp> #include <boost/algorithm/string/split.hpp> @@ -127,6 +129,9 @@ namespace { std::function<std::string(const std::string& key)>& m_GetRequestParam; // request including body (POST...) std::function<void(const std::string& key, const std::string& value)>& m_SetResponseHeader; // to be added to result string + std::string m_pathInfo; // path inside webbox, derived from request + fs::path m_path; // local filesystem path + std::unordered_map<std::string, std::string> paramHash; std::string webboxPath; @@ -150,14 +155,27 @@ namespace { { if (webboxStaticHtml == "") webboxStaticHtml = STATIC_HTML_DOC_ROOT; + + m_pathInfo = urlDecode(GetRequestParam("rel_target")); + size_t pos {m_pathInfo.find('?')}; + if (pos != m_pathInfo.npos) { + m_pathInfo = m_pathInfo.substr(0, pos); + } + + if (m_pathInfo.find("..") != m_pathInfo.npos) { + throw std::runtime_error("Bad path: "s + m_pathInfo); + } + + m_path = webboxPath; + if (!m_pathInfo.empty()) + m_path /= m_pathInfo; } }; - // Used to return errors by generating response page and HTTP status code - std::string HttpStatus(std::string status, std::string message, CommandParameters& commandParameters) + std::string HttpStatus(std::string status, std::string message, std::function<void(const std::string& key, const std::string& value)>& SetResponseHeader) { - commandParameters.m_SetResponseHeader("status", status); - commandParameters.m_SetResponseHeader("content_type", "text/html"); + SetResponseHeader("status", status); + SetResponseHeader("content_type", "text/html"); auto it{status_map.find(status)}; std::string description{"(Unknown)"}; @@ -167,6 +185,12 @@ namespace { return "<html><body><h1>"s + status + " "s + description + "</h1><p>"s + message + "</p></body></html>"; } + // Used to return errors by generating response page and HTTP status code + std::string HttpStatus(std::string status, std::string message, CommandParameters& commandParameters) + { + return HttpStatus(status, message, commandParameters.m_SetResponseHeader); + } + } // anonymous namespace class Command @@ -186,21 +210,6 @@ public: return HttpStatus("403", "Bad request method", p); } - // Set parameters from FastCGI request environment - m_pathInfo = urlDecode(p.m_GetRequestParam("rel_target")); - size_t pos {m_pathInfo.find('?')}; - if (pos != m_pathInfo.npos) { - m_pathInfo = m_pathInfo.substr(0, pos); - } - - if (m_pathInfo.find("..") != m_pathInfo.npos) { - return HttpStatus("403", "Bad path: "s + m_pathInfo, p); - } - - m_path = p.webboxPath; - if (!m_pathInfo.empty()) - m_path /= m_pathInfo; - return this->start(p); } @@ -220,9 +229,6 @@ protected: std::string m_requestMethod; bool m_isWriteCommand; // if true, command must be prevented if p.webboxReadOnly - // calculated during start of execute() - std::string m_pathInfo; // path inside webbox, derived from request - fs::path m_path; // local filesystem path }; class GetCommand: public Command @@ -306,13 +312,13 @@ protected: pt::ptree list; pt::ptree entry; - if (m_pathInfo != ""s) { // Add ".." if not in top directory of this webbox + if (p.m_pathInfo != ""s) { // Add ".." if not in top directory of this webbox entry.put_value(".."); entry.put("<xmlattr>.type", "dir"); list.push_back(pt::ptree::value_type("listentry", entry)); } - fs::directory_iterator dir(m_path); + fs::directory_iterator dir(p.m_path); std::vector<std::string> files; std::vector<std::string> dirs; @@ -412,7 +418,7 @@ protected: std::string dirname = tree.get<std::string>("dirname"); try { - if (fs::create_directory(m_path / dirname)) + if (fs::create_directory(p.m_path / dirname)) return "Successfully created directory"; else return "Error creating directory"; @@ -449,7 +455,7 @@ protected: for (const auto& element: elements) { if (element.first == "file"s) { std::string filename{element.second.data()}; - fs::path path {m_path / filename}; + fs::path path {p.m_path / filename}; auto filesize {fs::file_size(path)}; @@ -514,7 +520,7 @@ protected: return HttpStatus("400", "No files found", p); try { - fs::current_path(m_path); + fs::current_path(p.m_path); } catch (const std::exception& ex) { return HttpStatus("500", "Change path error: "s + ex.what(), p); } @@ -566,7 +572,7 @@ protected: if (element.first == "file"s) { std::string filename{element.second.data()}; - fs::path path{m_path / filename}; + fs::path path{p.m_path / filename}; if (fs::is_directory(path)) { try { @@ -624,10 +630,10 @@ protected: auto elements {tree.get_child("request")}; for (const auto& element: elements) { if (element.first == "target") { - targetDir = m_path / element.second.data(); + targetDir = p.m_path / element.second.data(); } else if (element.first == "file") { std::string filename{element.second.data()}; - fs::path old_path{m_path / filename}; + fs::path old_path{p.m_path / filename}; fs::path new_path{targetDir / filename}; try { fs::rename(old_path, new_path); @@ -674,8 +680,8 @@ protected: std::string oldname{tree.get<std::string>("request.oldname")}; std::string newname{tree.get<std::string>("request.newname")}; - fs::path oldpath{m_path / oldname}; - fs::path newpath{m_path / newname}; + fs::path oldpath{p.m_path / oldname}; + fs::path newpath{p.m_path / newname}; try { fs::rename(oldpath, newpath); @@ -753,7 +759,7 @@ protected: } else { filecontent = filecontent.substr(start + "\r\n\r\n"s.size()); - fs::path path{ m_path / filename}; + fs::path path{ p.m_path / filename}; try { File::setFile(path, filecontent); } catch (const std::exception& ex) { @@ -784,54 +790,18 @@ protected: virtual std::string start(CommandParameters& p) { try { - std::string result{File::getFile(m_path)}; + std::string result{File::getFile(p.m_path)}; - p.m_SetResponseHeader("content_disposition", "attachment; filename=\""s + m_path.filename().string() + "\""s); + p.m_SetResponseHeader("content_disposition", "attachment; filename=\""s + p.m_path.filename().string() + "\""s); p.m_SetResponseHeader("content_type", "application/octet-stream"); return result; } catch (const std::exception& ex) { - return HttpStatus("404", "Bad file: "s + m_path.filename().string(), p); + return HttpStatus("404", "Bad file: "s + p.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: @@ -846,7 +816,7 @@ protected: { // redirect to xyz/ if xyz was requested std::string target = p.m_GetRequestParam("target"); - if (m_pathInfo == "" && !target.empty() && target.back() != '/') { + if (p.m_pathInfo == "" && !target.empty() && target.back() != '/') { p.m_SetResponseHeader("location", target + "/"); return HttpStatus("301", "Use correct index: /"s, p); } @@ -854,20 +824,20 @@ protected: try { fs::path file_path; - if (m_pathInfo == "/" || m_pathInfo == "") { + if (p.m_pathInfo == "/" || p.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 if (boost::algorithm::starts_with(p.m_pathInfo, STATIC_HTML_TARGET)) { + file_path = p.webboxStaticHtml / p.m_pathInfo.substr(STATIC_HTML_TARGET.size()); } else { - return HttpStatus("500", "Bad request: "s + m_pathInfo, p); + return HttpStatus("500", "Bad request: "s + p.m_pathInfo, p); } - p.m_SetResponseHeader("content_type", mime_type(file_path)); + p.m_SetResponseHeader("content_type", mime_type(file_path.string())); std::string result{File::getFile(file_path)}; return result; } catch (const std::exception& ex) { - return HttpStatus("500", "Server error: "s + m_pathInfo, p); + return HttpStatus("500", "Server error: "s + p.m_pathInfo, p); } } }; @@ -908,23 +878,27 @@ std::string webbox_plugin::generate_page( { // 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")}; - if (it != commandParameters.paramHash.end()) - commandName = it->second; + try { + CommandParameters commandParameters(GetServerParam, GetRequestParam, SetResponseHeader); + + std::string commandName; + + auto it {commandParameters.paramHash.find("command")}; + if (it != commandParameters.paramHash.end()) + commandName = it->second; - auto commands_it{m_commands.find(commandName)}; - if (commands_it != m_commands.end()) { - try { - return commands_it->second->execute(commandParameters); - } catch (const std::exception& ex) { - return HttpStatus("500", "Processing command: "s + commandName + ", "s + ex.what(), commandParameters); - } - } else - return HttpStatus("400", "Bad command: "s + commandName, commandParameters); + auto commands_it{m_commands.find(commandName)}; + if (commands_it != m_commands.end()) { + try { + return commands_it->second->execute(commandParameters); + } catch (const std::exception& ex) { + return HttpStatus("500", "Processing command: "s + commandName + ", "s + ex.what(), commandParameters); + } + } else + return HttpStatus("400", "Bad command: "s + commandName, commandParameters); + } catch (const std::exception& ex) { + return HttpStatus("500", ex.what(), SetResponseHeader); + } } void webbox_plugin::registerCommand(std::shared_ptr<Command> command) |