diff options
author | Roland Reichwein <mail@reichwein.it> | 2020-04-12 22:20:33 +0200 |
---|---|---|
committer | Roland Reichwein <mail@reichwein.it> | 2020-04-12 22:20:33 +0200 |
commit | 4732dc63657f4c6fc342f7674f7dc7c666b293dc (patch) | |
tree | da91a5dbbd62982284435d252dd89ac963952ee9 /plugins | |
parent | 3f778eecc705990598f1033e6245522f42e2fcb5 (diff) |
webbox (WIP)
Diffstat (limited to 'plugins')
-rw-r--r-- | plugins/static-files/static-files.cpp | 2 | ||||
-rw-r--r-- | plugins/webbox/Makefile | 1 | ||||
-rw-r--r-- | plugins/webbox/stringutil.cpp | 66 | ||||
-rw-r--r-- | plugins/webbox/stringutil.h | 10 | ||||
-rw-r--r-- | plugins/webbox/webbox.cpp | 586 | ||||
-rw-r--r-- | plugins/webbox/webbox.h | 7 |
6 files changed, 347 insertions, 325 deletions
diff --git a/plugins/static-files/static-files.cpp b/plugins/static-files/static-files.cpp index ad85f22..b137cb8 100644 --- a/plugins/static-files/static-files.cpp +++ b/plugins/static-files/static-files.cpp @@ -72,7 +72,7 @@ std::string static_files_plugin::generate_page( if (method != "GET" && method != "HEAD") return HttpStatus("400", "Unknown HTTP method", SetResponseHeader); - // Request path must be absolute and not contain "..". + // Request path must not contain "..". std::string rel_target{GetRequestParam("rel_target")}; if (rel_target.find("..") != std::string::npos) { std::string target{GetRequestParam("target")}; diff --git a/plugins/webbox/Makefile b/plugins/webbox/Makefile index e16171d..2ddec0e 100644 --- a/plugins/webbox/Makefile +++ b/plugins/webbox/Makefile @@ -58,6 +58,7 @@ endif PROGSRC=\ file.cpp \ + stringutil.cpp \ webbox.cpp TESTSRC=\ diff --git a/plugins/webbox/stringutil.cpp b/plugins/webbox/stringutil.cpp new file mode 100644 index 0000000..f87fa00 --- /dev/null +++ b/plugins/webbox/stringutil.cpp @@ -0,0 +1,66 @@ +#include "stringutil.h" + +#include <boost/algorithm/string/predicate.hpp> +#include <boost/algorithm/string/case_conv.hpp> + +#include <cstdarg> + +std::string strfmt(const char* fmt, ...) +{ + va_list args; + + va_start(args, fmt); + int size = std::vsnprintf(nullptr, 0, fmt, args); + va_end(args); + + std::string result(size, ' '); + + va_start(args, fmt); + std::vsnprintf(result.data(), size + 1, fmt, args); + va_end(args); + + return result; +} + +std::vector<std::string> split(std::string value, const std::string separators) +{ + std::vector<std::string> result; + + size_t pos0 = 0; + size_t pos1 = 0; + while (pos0 < value.size()) { + pos1 = value.find_first_of(separators, pos0); + if (pos1 == std::string::npos) + pos1 = value.size(); + std::string part = value.substr(pos0, pos1 - pos0); + //std::cout << "DEBUG: " << part << std::endl << std::flush; + if (part != "") + result.push_back(part); + pos0 = value.find_first_not_of(separators, pos1); + if (pos0 == std::string::npos) + pos0 = value.size(); + } + + return result; +} + +std::string join(std::vector<std::string> vs, std::string separator) +{ + std::string s; + for (const auto& line : vs) { + if (s.size() > 0) + s += separator; + s += line; + } + + return s; +} + +bool startsWithAnyOfLower(const std::string &s, const std::vector<std::string> &list) { + for (const std::string& element : list) { + if (boost::algorithm::starts_with(boost::algorithm::to_lower_copy(s), boost::algorithm::to_lower_copy(element))) + return true; + } + return false; +} + diff --git a/plugins/webbox/stringutil.h b/plugins/webbox/stringutil.h new file mode 100644 index 0000000..5110e2e --- /dev/null +++ b/plugins/webbox/stringutil.h @@ -0,0 +1,10 @@ +#pragma once + +#include <string> +#include <vector> + +std::string strfmt(const char* fmt, ...); + +std::vector<std::string> split(std::string value, const std::string separators = "\r\n "); +std::string join(std::vector<std::string> vs, std::string separator = "\n"); +bool startsWithAnyOfLower(const std::string &s, const std::vector<std::string> &list); diff --git a/plugins/webbox/webbox.cpp b/plugins/webbox/webbox.cpp index 5d3f64c..363df6c 100644 --- a/plugins/webbox/webbox.cpp +++ b/plugins/webbox/webbox.cpp @@ -1,15 +1,25 @@ #include "webbox.h" +#include "stringutil.h" + #include <boost/algorithm/string/replace.hpp> +#include <boost/property_tree/ptree.hpp> +#include <boost/property_tree/xml_parser.hpp> +#include <filesystem> #include <iostream> #include <string> #include <unordered_map> using namespace std::string_literals; +namespace fs = std::filesystem; +namespace pt = boost::property_tree; namespace { + void registerCommand(std::unordered_map<std::string, std::shared_ptr<Command>>& commands, std::shared_ptr<Command> command) { + commands[command.getCommandName()] = command; + }; unordered_map<std::string> status_map { { "400", "Bad Request"}, @@ -18,11 +28,57 @@ namespace { { "505", "Internal Server Error" }, }; +std::unordered_map<std::string, std::string> ParseQueryString(std::string s) +{ + std::unordered_map<std::string, std::string> result; + + size_t pos = s.find('?'); + if (pos != s.npos) { + auto list {split(s.substr(pos), "&")}; + for (auto i: list) { + pos = i.find('='); + if (pos != i.npos) { + result[i.substr(0, pos)] = i.substr(pos + 1); + } + } + } + + return result; +} + +struct CommandParameters +{ + std::function<std::string(const std::string& key)>& m_GetServerParam; + 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::unordered_map<std::string, std::string> paramHash; + + std::string webboxPath; + std::string webboxName; + bool webboxReadOnly; + + CommandParameters( + std::function<std::string(const std::string& key)>& GetServerParam, + std::function<std::string(const std::string& key)>& GetRequestParam, + std::function<void(const std::string& key, const std::string& value)>& SetResponseHeader + ) + : m_GetServerParam(GetServerParam) + , m_GetRequestParam(GetRequestParam) + , m_SetResponseHeader(SetResponseHeader) + , paramHash(ParseQueryString(GetRequestParam("rel_target"))) // rel_target contains query string + , webboxPath(m_GetRequestParam("doc_root")) + , webboxName(m_GetRequestParam("WEBBOX_NAME")) + , webboxReadOnly(m_GetRequestParam("WEBBOX_READONLY") == "1") + { + } +}; + // Used to return errors by generating response page and HTTP status code -std::string HttpStatus(std::string status, std::string message, std::function<plugin_interface_setter_type>& SetResponseHeader) +std::string HttpStatus(std::string status, std::string message, CommandParameters& commandParameters) { - SetResponseHeader("status", status); - SetResponseHeader("content_type", "text/html"); + commandParameters.m_SetResponseHeader("status", status); + commandParameters.m_SetResponseHeader("content_type", "text/html"); auto it{status_map.find(status)}; std::string description{"(Unknown)"}; @@ -32,169 +88,134 @@ std::string HttpStatus(std::string status, std::string message, std::function<pl return "<html><body><h1>"s + status + " "s + description + "</h1><p>"s + message + "</p></body></html>"; } - -struct CommandParameters { - std::function<std::string(const std::string& key)>& GetServerParam; - std::function<std::string(const std::string& key)>& GetRequestParam; // request including body (POST...) - std::function<void(const std::string& key, const std::string& value)>& SetResponseHeader; // to be added to result string - FCGX_Request request; // the request - - QUrlQuery urlQuery; // derived from request - QHash<QString, QString> paramHash; // derived from urlQuery - - int count; // request count for this instance - - // Webbox parameters, constant and set globally in Web server config - QString webboxPath; - QString webboxName; - bool webboxReadOnly; -}; - -class Command { - public: - // call interface - void execute(CommandParameters& p) { - // check if this webbox is writable and enforce this - if (p.webboxReadOnly && m_isWriteCommand) { - printHttpError(400, QString("Webbox is Read-Only"), p); - return; - } - - // check for correct method GET/POST - QString requestMethod(FCGX_GetParam("REQUEST_METHOD", p.request.envp)); - if (requestMethod != m_requestMethod) { - printHttpError(403, QString("Bad request method"), p); - return; - } - - // Set parameters from FastCGI request environment - m_pathInfo = FCGX_GetParam("PATH_INFO", p.request.envp); - if (m_pathInfo == "") { - m_pathInfo = "/"; - } - if (m_pathInfo.contains("..")) { - printHttpError(403, QString("Bad path: %1"), p); - return; - } - - m_path = p.webboxPath + m_pathInfo; - - this->start(p); - } - - QString getCommandName() { - return m_commandName; - } - - protected: - // helper function for writing http error - void printHttpError(int httpStatusCode, QString message, CommandParameters& p) { - FCGX_PutS(httpError(httpStatusCode, message).toUtf8().data(), p.request.out); - } - - // implemented in implementation classes - virtual void start(CommandParameters& p) = 0; - - // Implementation class constants - QString m_commandName; - QString m_requestMethod; - bool m_isWriteCommand; // if true, command must be prevented if p.webboxReadOnly - - // calculated during start of execute() - QString m_pathInfo; // path inside webbox, derived from request - QString m_path; // complete path +class Command +{ +public: + // call interface + std::string execute(CommandParameters& p) + { + // check if this webbox is writable and enforce this + if (p.webboxReadOnly && m_isWriteCommand) { + return HttpStatus("400", "Webbox is Read-Only", p); + } + + // check for correct method GET/POST + std::string requestMethod{p.m_GetRequestParam("method")}; + if (requestMethod != m_requestMethod) { + return HttpStatus("403", "Bad request method", p); + } + + // Set parameters from FastCGI request environment + m_pathInfo = p.m_GetRequestParam("rel_path"); + if (m_pathInfo == "") { + m_pathInfo = "/"; + } + if (m_pathInfo.find("..") != m_pathInfo.npos) { + return HttpStatus("403", "Bad path: "s + m_pathInfo, p); + } + + m_path = p.webboxPath + m_pathInfo; + + return this->start(p); + } + + std::string getCommandName() + { + return m_commandName; + } + +protected: + // implemented in implementation classes + virtual std::string start(CommandParameters& p) = 0; + + // Implementation class constants + std::string m_commandName; + 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 + std::string m_path; // complete path }; -class GetCommand: public Command { - public: - GetCommand() { - m_requestMethod = "GET"; - } +class GetCommand: public Command +{ +public: + GetCommand() { + m_requestMethod = "GET"; + } }; -class PostCommand: public Command { - public: - PostCommand() { - m_requestMethod = "POST"; - } - - protected: - // prepare POST handler implementation: read m_contentLength and m_content - // needs to be called at beginning of post implementations start() - // returns true on success - bool readContent(CommandParameters& p) { - QString contentLengthString(FCGX_GetParam("CONTENT_LENGTH", p.request.envp)); - bool ok; - m_contentLength = contentLengthString.toInt(&ok); - - if (!ok) { - printHttpError(400, QString("Bad content length"), p); - return false; - } else { - m_content.resize(m_contentLength); - - int result = FCGX_GetStr(m_content.data(), m_content.size(), p.request.in); - if (result != m_content.size()) { - printHttpError(400, QString("Read error (%1/%2)").arg(result).arg(m_content.size()), p); - return false; - } - } - return true; - } - - int m_contentLength; - QByteArray m_content; +class PostCommand: public Command +{ +public: + PostCommand() + { + m_requestMethod = "POST"; + } + +protected: + // prepare POST handler implementation: read m_contentLength and m_content + // needs to be called at beginning of post implementations start() + // returns true on success + void readContent(CommandParameters& p) + { + m_content = p.m_GetRequestParam("body"); + m_contentLength = m_content.size(); + } + + int m_contentLength; + std::string m_content; }; -class DiagCommand: public GetCommand { - public: - DiagCommand() { - m_commandName = "diag"; - m_isWriteCommand = false; - } - - protected: - virtual void start(CommandParameters& p) { - QString serverName(FCGX_GetParam("SERVER_NAME", p.request.envp)); - // provide diag only on "localhost" - if (serverName != "localhost") { - printHttpError(403, QString("Command not available"), p); - return; - } +class DiagCommand: public GetCommand +{ +public: + DiagCommand() + { + m_commandName = "diag"; + m_isWriteCommand = false; + } - FCGX_PutS("Content-Type: text/html\r\n\r\n", p.request.out); +protected: + virtual std::string start(CommandParameters& p) + { + std::string serverName(p.m_GetRequestParam("host")); + + // provide diag only on "localhost" + if (serverName != "localhost") + throw std::runtime_error("Command not available"); - FCGX_PutS("<html><head><title>Params</title></head><body>\r\n", p.request.out); + p.m_SetRequestParam("content_type", "text/html"); - FCGX_PutS(QString("Request no. %1<br/><br/>\r\n").arg(p.count).toUtf8().data(), p.request.out); + std::string result {"<html><head><title>Params</title></head><body>\r\n"}; - char** tmp = p.request.envp; + result += "WEBBOX_PATH="s + p.webboxPath + "<br/>\r\n"s; - while (*tmp) { - FCGX_PutS(QString("%1<br/>\r\n").arg(*tmp).toUtf8().data(), p.request.out); - tmp++; - } - - FCGX_PutS(QString("<br/>WEBBOX_PATH=%1<br/>\r\n").arg(p.webboxPath).toUtf8().data(), p.request.out); + result += "<br/>URL Query="s + p.m_GetRequestParam("rel_target") + "<br/>\r\n";; - FCGX_PutS(QString("<br/>URL Query=%1<br/>\r\n").arg(p.urlQuery.toString()).toUtf8().data(), p.request.out); + result += "</body></html>\r\n"; - - FCGX_PutS("</body></html>\r\n", p.request.out); - } + return result; + } }; -class ListCommand: public GetCommand { - public: - ListCommand() { - m_commandName = "list"; - m_isWriteCommand = false; - } - - protected: - virtual void start(CommandParameters& p) { - FCGX_PutS("Content-Type: text/xml\r\n\r\n", p.request.out); - +class ListCommand: public GetCommand +{ +public: + ListCommand() + { + m_commandName = "list"; + m_isWriteCommand = false; + } + +protected: + virtual std::string start(CommandParameters& p) { + FCGX_PutS("Content-Type: text/xml\r\n\r\n", p.request.out); + + fs::directory_iterator dir(m_path); + for (auto it& + pt::ptree QDir dir(m_path); QFileInfoList dirEntryList = dir.entryInfoList(QDir::NoDot | QDir::AllEntries, QDir::DirsFirst | QDir::Name | QDir::IgnoreCase); @@ -227,7 +248,7 @@ class ServerInfoCommand: public GetCommand { } protected: - virtual void start(CommandParameters& p) { + virtual std::string start(CommandParameters& p) { FCGX_PutS("Content-Type: text/xml\r\n\r\n", p.request.out); QByteArray xmlData; @@ -253,9 +274,9 @@ class VersionCommand: public GetCommand { } protected: - virtual void start(CommandParameters& p) { + virtual std::string start(CommandParameters& p) { FCGX_PutS("Content-Type: text/plain\r\n\r\n", p.request.out); - FCGX_PutS(QString("webbox %1<br/>(C) 2018 <a href=\"https://www.reichwein.it/\">Reichwein.IT</a>\r\n").arg(PROGRAMVERSION).toUtf8().data(), p.request.out); + FCGX_PutS(std::string("webbox %1<br/>(C) 2018 <a href=\"https://www.reichwein.it/\">Reichwein.IT</a>\r\n").arg(PROGRAMVERSION).toUtf8().data(), p.request.out); } }; @@ -267,9 +288,8 @@ class NewDirCommand: public PostCommand { } protected: - virtual void start(CommandParameters& p) { - if (!readContent(p)) - return; + virtual std::string start(CommandParameters& p) { + readContent(p); FCGX_PutS("Content-Type: text/plain\r\n\r\n", p.request.out); QXmlStreamReader xml(m_content); @@ -277,7 +297,7 @@ class NewDirCommand: public PostCommand { while (!xml.atEnd()) { while (xml.readNextStartElement()) { if (xml.name() == "dirname") { - QString dirname = xml.readElementText(); + std::string dirname = xml.readElementText(); QDir dir(m_path); if (dir.mkdir(dirname)) { FCGX_PutS("Successfully created directory", p.request.out); @@ -298,9 +318,8 @@ class InfoCommand: public PostCommand { } protected: - virtual void start(CommandParameters& p) { - if (!readContent(p)) - return; + virtual std::string start(CommandParameters& p) { + readContent(p); FCGX_PutS("Content-Type: text/plain\r\n\r\n", p.request.out); QXmlStreamReader xml(m_content); @@ -310,16 +329,16 @@ class InfoCommand: public PostCommand { if (xml.name() == "files") { while (xml.readNextStartElement()) { if (xml.name() == "file") { - QString filename = xml.readElementText(); + std::string filename = xml.readElementText(); QFileInfo fileInfo(m_path + "/" + filename); qint64 size = fileInfo.size(); - QString date = fileInfo.lastModified().toString(); + std::string date = fileInfo.lastModified().toString(); if (fileInfo.isDir()) { - FCGX_PutS(QString("%1, %2 (folder)<br>") + FCGX_PutS(std::string("%1, %2 (folder)<br>") .arg(filename) .arg(date).toUtf8().data(), p.request.out); } else { - FCGX_PutS(QString("%1, %2 bytes, %3 (file)<br>") + FCGX_PutS(std::string("%1, %2 bytes, %3 (file)<br>") .arg(filename) .arg(size) .arg(date).toUtf8().data(), p.request.out); @@ -339,19 +358,18 @@ class DownloadZipCommand: public PostCommand { } protected: - virtual void start(CommandParameters& p) { - if (!readContent(p)) - return; + virtual std::string start(CommandParameters& p) { + readContent(p); QXmlStreamReader xml(m_content); QByteArray zipData; - QStringList argumentList; + std::stringList argumentList; QTemporaryFile tempfile(QDir::tempPath() + "/webboxXXXXXX.zip"); tempfile.open(); QFileInfo fileInfo(tempfile); - QString tempfilePath = fileInfo.absolutePath(); - QString tempfileName = fileInfo.fileName(); + std::string tempfilePath = fileInfo.absolutePath(); + std::string tempfileName = fileInfo.fileName(); tempfile.close(); tempfile.remove(); @@ -363,7 +381,7 @@ class DownloadZipCommand: public PostCommand { if (xml.name() == "files") { while (xml.readNextStartElement()) { if (xml.name() == "file") { - QString filename = xml.readElementText(); + std::string filename = xml.readElementText(); argumentList.append(filename); // add parts } @@ -379,7 +397,7 @@ class DownloadZipCommand: public PostCommand { process.start(); process.waitForFinished(); - QString debugText = process.readAll(); + std::string debugText = process.readAll(); process.setReadChannel(QProcess::StandardError); debugText += process.readAll(); @@ -387,7 +405,7 @@ class DownloadZipCommand: public PostCommand { process.exitCode() != 0 || process.exitStatus() != QProcess::NormalExit) { - printHttpError(500, QString("Error running process: %1 %2 %3 %4 %5 %6 %7"). + printHttpError(500, std::string("Error running process: %1 %2 %3 %4 %5 %6 %7"). arg(process.state()). arg(process.exitCode()). arg(process.exitStatus()). @@ -399,11 +417,11 @@ class DownloadZipCommand: public PostCommand { QFile tempfile(tempfilePath + "/" + tempfileName); if (!tempfile.open(QIODevice::ReadOnly)) { - printHttpError(500, QString("Error reading file"), p); + printHttpError(500, std::string("Error reading file"), p); } else { zipData = tempfile.readAll(); - FCGX_PutS(QString("Content-Disposition: attachment; filename=\"%1\"\r\n").arg("webbox-download.zip").toUtf8().data(), p.request.out); + FCGX_PutS(std::string("Content-Disposition: attachment; filename=\"%1\"\r\n").arg("webbox-download.zip").toUtf8().data(), p.request.out); FCGX_PutS("Content-Type: application/octet-stream\r\n\r\n", p.request.out); FCGX_PutStr(zipData.data(), zipData.size(), p.request.out); @@ -423,34 +441,33 @@ class DeleteCommand: public PostCommand { } protected: - virtual void start(CommandParameters& p) { - if (!readContent(p)) - return; + virtual std::string start(CommandParameters& p) { + readContent(p); QXmlStreamReader xml(m_content); - QString response = ""; + std::string response = ""; while (!xml.atEnd()) { while (xml.readNextStartElement()) { if (xml.name() == "files") { while (xml.readNextStartElement()) { if (xml.name() == "file") { - QString filename = xml.readElementText(); + std::string filename = xml.readElementText(); QFileInfo fileInfo(m_path + "/" + filename); if (fileInfo.isDir()) { QDir dir(m_path); if (!dir.rmdir(filename)) { - response += QString("Error on removing directory %1<br/>").arg(filename); + response += std::string("Error on removing directory %1<br/>").arg(filename); } } else if (fileInfo.isFile()) { QFile file(m_path + "/" + filename); if (!file.remove()) { - response += QString("Error on removing file %1<br/>").arg(filename); + response += std::string("Error on removing file %1<br/>").arg(filename); } } else { - response += QString("Error: %1 is neither file nor directory.<br/>").arg(filename); + response += std::string("Error: %1 is neither file nor directory.<br/>").arg(filename); } } } @@ -475,14 +492,13 @@ class MoveCommand: public PostCommand { } protected: - virtual void start(CommandParameters& p) { - if (!readContent(p)) - return; + virtual std::string start(CommandParameters& p) { + readContent(p); QXmlStreamReader xml(m_content); - QString response = ""; - QString targetDir; + std::string response = ""; + std::string targetDir; while (!xml.atEnd()) { while (xml.readNextStartElement()) { @@ -491,21 +507,21 @@ class MoveCommand: public PostCommand { if (xml.name() == "target") { targetDir = xml.readElementText(); } else if (xml.name() == "file") { - QString filename = xml.readElementText(); + std::string filename = xml.readElementText(); QFileInfo fileInfo(m_path + "/" + filename); if (fileInfo.isDir()) { QDir dir(m_path); if (!dir.rename(filename, targetDir + "/" + filename)) { - response += QString("Error moving directory %1<br/>").arg(filename); + response += std::string("Error moving directory %1<br/>").arg(filename); } } else if (fileInfo.isFile()) { QFile file(m_path + "/" + filename); if (!file.rename(m_path + "/" + targetDir + "/" + filename)) { - response += QString("Error on moving file %1<br/>").arg(filename); + response += std::string("Error on moving file %1<br/>").arg(filename); } } else { - response += QString("Error: %1 is neither file nor directory.<br/>").arg(filename); + response += std::string("Error: %1 is neither file nor directory.<br/>").arg(filename); } } } @@ -530,14 +546,13 @@ class RenameCommand: public PostCommand { } protected: - virtual void start(CommandParameters& p) { - if (!readContent(p)) - return; + virtual std::string start(CommandParameters& p) { + readContent(p); QXmlStreamReader xml(m_content); - QString oldname; - QString newname; + std::string oldname; + std::string newname; while (!xml.atEnd()) { while (xml.readNextStartElement()) { @@ -555,9 +570,9 @@ class RenameCommand: public PostCommand { } QDir dir(m_path); - QString response; + std::string response; if (!dir.rename(oldname, newname)) { - response = QString("Error renaming %1 to %2<br/>").arg(oldname).arg(newname); + response = std::string("Error renaming %1 to %2<br/>").arg(oldname).arg(newname); } else { response = "OK"; } @@ -575,21 +590,20 @@ class UploadCommand: public PostCommand { } protected: - virtual void start(CommandParameters& p) { - if (!readContent(p)) - return; + virtual std::string start(CommandParameters& p) { + readContent(p); FCGX_PutS("Content-Type: text/plain\r\n\r\n", p.request.out); - QString contentType(FCGX_GetParam("CONTENT_TYPE", p.request.envp)); + std::string contentType(FCGX_GetParam("CONTENT_TYPE", p.request.envp)); - QString separator("boundary="); + std::string separator("boundary="); if (!contentType.contains(separator)) { - FCGX_PutS(QString("No boundary defined").toUtf8().data(), p.request.out); + FCGX_PutS(std::string("No boundary defined").toUtf8().data(), p.request.out); } else { QByteArray boundary = QByteArray("--") + contentType.split(separator)[1].toUtf8(); int boundaryCount = m_content.count(boundary); if (boundaryCount < 2) { - FCGX_PutS(QString("Bad boundary number found: %1").arg(boundaryCount).toUtf8().data(), p.request.out); + FCGX_PutS(std::string("Bad boundary number found: %1").arg(boundaryCount).toUtf8().data(), p.request.out); } else { while (true) { int start = m_content.indexOf(boundary) + boundary.size(); @@ -605,34 +619,34 @@ class UploadCommand: public PostCommand { // Read filename start = filecontent.indexOf("filename=\""); if (start == -1) { - FCGX_PutS(QString("Error reading filename / start").toUtf8().data(), p.request.out); + FCGX_PutS(std::string("Error reading filename / start").toUtf8().data(), p.request.out); } else { start += QByteArray("filename=\"").size(); end = filecontent.indexOf(QByteArray("\""), start); if (end == -1) { - FCGX_PutS(QString("Error reading filename / end").toUtf8().data(), p.request.out); + FCGX_PutS(std::string("Error reading filename / end").toUtf8().data(), p.request.out); } else { - QString filename = QString::fromUtf8(filecontent.mid(start, end - start)); + std::string filename = std::string::fromUtf8(filecontent.mid(start, end - start)); if (filename.size() < 1) { - FCGX_PutS(QString("Bad filename").toUtf8().data(), p.request.out); + FCGX_PutS(std::string("Bad filename").toUtf8().data(), p.request.out); } else { // Remove header start = filecontent.indexOf(QByteArray("\r\n\r\n")); if (start == -1) { - FCGX_PutS(QString("Error removing upload header").toUtf8().data(), p.request.out); + FCGX_PutS(std::string("Error removing upload header").toUtf8().data(), p.request.out); } else { - filecontent = filecontent.mid(start + QString("\r\n\r\n").toUtf8().size()); + filecontent = filecontent.mid(start + std::string("\r\n\r\n").toUtf8().size()); QFile file(m_path + "/" + filename); if (!file.open(QIODevice::WriteOnly)) { - FCGX_PutS(QString("Error opening file").toUtf8().data(), p.request.out); + FCGX_PutS(std::string("Error opening file").toUtf8().data(), p.request.out); } else { qint64 written = file.write(filecontent); if (written != filecontent.size()) { - FCGX_PutS(QString("Error writing file").toUtf8().data(), p.request.out); + FCGX_PutS(std::string("Error writing file").toUtf8().data(), p.request.out); } } } @@ -654,11 +668,11 @@ class DownloadCommand: public GetCommand { } protected: - virtual void start(CommandParameters& p) { + virtual std::string start(CommandParameters& p) { QFile file(m_path); if (file.open(QIODevice::ReadOnly)) { QFileInfo fileInfo(m_path); - FCGX_PutS(QString("Content-Disposition: attachment; filename=\"%1\"\r\n").arg(fileInfo.fileName()).toUtf8().data(), p.request.out); + FCGX_PutS(std::string("Content-Disposition: attachment; filename=\"%1\"\r\n").arg(fileInfo.fileName()).toUtf8().data(), p.request.out); FCGX_PutS("Content-Type: application/octet-stream\r\n\r\n", p.request.out); while (!file.atEnd()) { @@ -666,116 +680,11 @@ class DownloadCommand: public GetCommand { FCGX_PutStr(ba.data(), ba.size(), p.request.out); } } else { - FCGX_PutS(httpError(500, QString("Bad file: %1").arg(m_pathInfo)).toUtf8().data(), p.request.out); + FCGX_PutS(httpError(500, std::string("Bad file: %1").arg(m_pathInfo)).toUtf8().data(), p.request.out); } } }; -// Hash of commands for fast access -class Commands: public QHash<QString, Command*> { - public: - void registerCommand(Command& command) { - (*this)[command.getCommandName()] = &command; - } -}; - -void initlocale() { - if (setenv("LC_ALL", "UTF-8", 1)) { - exit(1); - } -} - -int main(int argc, char* argv[]) { - initlocale(); - - int result = FCGX_Init(); - if (result != 0) { - return 1; // error on init - } - - CommandParameters commandParameters; - - FCGX_Request& request = commandParameters.request; - - if (FCGX_InitRequest(&request, 0, 0) != 0) { - return 1; // error on init - } - - commandParameters.count = 0; - - // values constant to this instance: - commandParameters.webboxPath = getenv("WEBBOX_PATH"); - commandParameters.webboxName = getenv("WEBBOX_NAME"); - char* WEBBOX_READONLY = getenv("WEBBOX_READONLY"); - commandParameters.webboxReadOnly = ((WEBBOX_READONLY != NULL) && !strcmp(WEBBOX_READONLY, "On")); - - Commands commands; - - DiagCommand diagCommand; - commands.registerCommand(diagCommand); - - ListCommand listCommand; - commands.registerCommand(listCommand); - - ServerInfoCommand serverInfoCommand; - commands.registerCommand(serverInfoCommand); - - VersionCommand versionCommand; - commands.registerCommand(versionCommand); - - NewDirCommand newDirCommand; - commands.registerCommand(newDirCommand); - - InfoCommand infoCommand; - commands.registerCommand(infoCommand); - - DownloadZipCommand downloadZipCommand; - commands.registerCommand(downloadZipCommand); - - DeleteCommand deleteCommand; - commands.registerCommand(deleteCommand); - - MoveCommand moveCommand; - commands.registerCommand(moveCommand); - - RenameCommand renameCommand; - commands.registerCommand(renameCommand); - - UploadCommand uploadCommand; - commands.registerCommand(uploadCommand); - - DownloadCommand downloadCommand; - commands.registerCommand(downloadCommand); - - while (FCGX_Accept_r(&request) == 0) { - - commandParameters.count++; - - QString queryString(FCGX_GetParam("QUERY_STRING", request.envp)); - - // URL parameters - commandParameters.urlQuery.setQuery(queryString); - - QList<QPair<QString, QString> > items = commandParameters.urlQuery.queryItems(); - commandParameters.paramHash.clear(); - - for (int i = 0; i < items.size(); i++) { - commandParameters.paramHash[items[i].first] = items[i].second; - } - - QString commandName = commandParameters.paramHash["command"]; - if (commands.contains(commandName)) { - Command* command = commands[commandName]; - - command->execute(commandParameters); - } else { - FCGX_PutS(httpError(400, QString("Bad command: %1").arg(commandName)).toUtf8().data(), request.out); - } - } - - return 0; -} - } // anonymous namespace std::string webbox_plugin::name() @@ -786,6 +695,18 @@ std::string webbox_plugin::name() webbox_plugin::webbox_plugin() { //std::cout << "Plugin constructor" << std::endl; + registerCommand(m_commands, std::make_shared<DiagCommand>()); + registerCommand(m_commands, std::make_shared<ListCommand>()); + registerCommand(m_commands, std::make_shared<ServerInfoCommand>()); + registerCommand(m_commands, std::make_shared<VersionCommand>()); + registerCommand(m_commands, std::make_shared<NewDirCommand>()); + registerCommand(m_commands, std::make_shared<InfoCommand>()); + registerCommand(m_commands, std::make_shared<DownloadZipCommand>()); + registerCommand(m_commands, std::make_shared<DeleteCommand>()); + registerCommand(m_commands, std::make_shared<MoveCommand>()); + registerCommand(m_commands, std::make_shared<RenameCommand>()); + registerCommand(m_commands, std::make_shared<UploadCommand>()); + registerCommand(m_commands, std::make_shared<DownloadCommand>()); } webbox_plugin::~webbox_plugin() @@ -799,5 +720,22 @@ std::string webbox_plugin::generate_page( std::function<void(const std::string& key, const std::string& value)>& SetResponseHeader // to be added to result string ) { - return "Webbox"; + CommandParameters commandParameters(GetServerParam, GetRequestParam, SetResponseHeader); + + auto it {commandParameters.paramHash.find("command")}; + if (it != commandParameters.paramHash.end()) { + std::string& commandName{it->second}; + + auto commands_it{commands.find(commandName)}; + if (commands_it != commands.end()) { + try { + return commands_it->second.execute(commandParameters); + } catch (const std::exception& ex) { + return HttpStatus("500", "Processing command: "s + commandName, commandParameters); + } + } else + return HttpStatus("400", "Bad command: "s + commandName, commandParameters); + } else + return HttpStatus("400", "No command specified"s, commandParameters); } + diff --git a/plugins/webbox/webbox.h b/plugins/webbox/webbox.h index d6d26c1..e2644f3 100644 --- a/plugins/webbox/webbox.h +++ b/plugins/webbox/webbox.h @@ -2,8 +2,15 @@ #include "../../plugin_interface.h" +#include <memory> +#include <string> +#include <unordered_map> + class webbox_plugin: public webserver_plugin_interface { +private: + std::unordered_map<std::string, std::shared_ptr<Command>> m_commands; + public: webbox_plugin(); ~webbox_plugin(); |