From 77a68fbe16246245937c5d692bb8c89dc14d7800 Mon Sep 17 00:00:00 2001
From: Roland Reichwein <mail@reichwein.it>
Date: Sat, 11 Apr 2020 16:56:36 +0200
Subject: Webbox (WIP)

---
 plugins/webbox/Makefile   |   4 +-
 plugins/webbox/webbox.cpp | 815 +++++++++++++++++++++++++++++++++++++++++++++-
 plugins/webbox/webbox.h   |   6 +-
 3 files changed, 820 insertions(+), 5 deletions(-)

(limited to 'plugins')

diff --git a/plugins/webbox/Makefile b/plugins/webbox/Makefile
index 666aede..ed66b50 100644
--- a/plugins/webbox/Makefile
+++ b/plugins/webbox/Makefile
@@ -52,8 +52,8 @@ LIBS+= \
 #-lstdc++fs
 else
 LIBS+= \
--lstdc++
-#-lstdc++fs
+-lstdc++ \
+-lstdc++fs
 endif
 
 PROGSRC=\
diff --git a/plugins/webbox/webbox.cpp b/plugins/webbox/webbox.cpp
index 7fe9fa7..6166895 100644
--- a/plugins/webbox/webbox.cpp
+++ b/plugins/webbox/webbox.cpp
@@ -19,8 +19,819 @@ webbox_plugin::~webbox_plugin()
  //std::cout << "Plugin destructor" << std::endl;
 }
 
-std::string webbox_plugin::generate_page(std::string path)
+std::string webbox_plugin::generate_page(
+  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
+)
 {
- return "Webbox "s + path;
+ return "Webbox";
 }
 
+#if 0
+#include <fcgiapp.h>
+
+#include <QString>
+#include <QStringList>
+#include <QHash>
+#include <QDir>
+#include <QFileInfo>
+#include <QXmlStreamReader>
+#include <QXmlStreamWriter>
+#include <QDateTime>
+#include <QProcess>
+#include <QTemporaryFile>
+#include <QUrlQuery>
+#include <QPair>
+
+#define BUFSIZE 1000000
+
+// XML special characters:
+// < : &lt;
+// > : &gt;
+// & : &amp;
+// " : &quot;
+// ' : &apos;
+//
+// here:replace &
+QString escapeXML(QString s) {
+	s.replace("&", "&amp;");
+	return s;
+}
+
+// revert escapeXML();
+QString unescapeXML(QString s) {
+	s.replace("&amp;", "&");
+	return s;
+}
+
+// supported httpStatusCode:
+// 400 Bad Request
+// 403 Forbidden
+// 404 Not Found
+// 500 Internal Server Error
+// message: additional message
+QString httpError(int httpStatusCode, QString message) {
+	QString description;
+
+	switch(httpStatusCode) {
+		case 400:
+			description = "Bad Request";
+			break;
+		case 403:
+			description = "Forbidden";
+			break;
+		case 404:
+			description = "Not Found";
+			break;
+		case 500:
+			description = "Internal Server Error";
+			break;
+		default:
+			message = QString("Bad error code: %1, message: %2").arg(httpStatusCode).arg(message);
+			httpStatusCode = 500;
+			description = "Internal Server Error";
+	}
+	return QString("Status: %1 %2\r\nContent-Type: text/html\r\n\r\n<html><body><h1>%1 %2</h1><p>%3</p></body></html>\r\n").arg(httpStatusCode).arg(description).arg(message);
+}
+
+struct CommandParameters {
+	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 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 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;
+			}
+
+			FCGX_PutS("Content-Type: text/html\r\n\r\n", p.request.out);
+
+			FCGX_PutS("<html><head><title>Params</title></head><body>\r\n", p.request.out);
+
+			FCGX_PutS(QString("Request no. %1<br/><br/>\r\n").arg(p.count).toUtf8().data(), p.request.out);
+
+			char** tmp = p.request.envp;
+
+			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);
+
+			FCGX_PutS(QString("<br/>URL Query=%1<br/>\r\n").arg(p.urlQuery.toString()).toUtf8().data(), p.request.out);
+
+			
+			FCGX_PutS("</body></html>\r\n", p.request.out);
+		}
+};
+
+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);
+
+			QDir dir(m_path);
+			QFileInfoList dirEntryList = dir.entryInfoList(QDir::NoDot | QDir::AllEntries, QDir::DirsFirst | QDir::Name | QDir::IgnoreCase);
+
+			QByteArray xmlData;
+			QXmlStreamWriter xmlWriter(&xmlData);
+			xmlWriter.writeStartDocument();
+			xmlWriter.writeStartElement("list");
+			foreach(QFileInfo i, dirEntryList) {
+				if (m_pathInfo != "/" || i.fileName() != "..") { // skip on ".." in "/"
+					xmlWriter.writeStartElement("listentry");
+					xmlWriter.writeAttribute("type", i.isDir() ? "dir" : "file");
+					xmlWriter.writeCharacters(i.fileName());
+					xmlWriter.writeEndElement();
+				}
+			}
+			xmlWriter.writeEndElement(); // list
+			xmlWriter.writeEndDocument();
+			FCGX_PutS(xmlData.data(), p.request.out);
+		}
+};
+
+// Retrieve from Server:
+//   Title
+//   ReadOnly flag
+class ServerInfoCommand: public GetCommand {
+	public:
+		ServerInfoCommand() {
+			m_commandName = "server-info";
+			m_isWriteCommand = false;
+		}
+
+	protected:
+		virtual void start(CommandParameters& p) {
+			FCGX_PutS("Content-Type: text/xml\r\n\r\n", p.request.out);
+
+			QByteArray xmlData;
+			QXmlStreamWriter xmlWriter(&xmlData);
+			xmlWriter.writeStartDocument();
+			xmlWriter.writeStartElement("serverinfo");
+
+			xmlWriter.writeTextElement("title", p.webboxName);
+			
+			xmlWriter.writeTextElement("readonly", p.webboxReadOnly ? "1" : "0");
+
+			xmlWriter.writeEndElement(); // serverinfo
+			xmlWriter.writeEndDocument();
+			FCGX_PutS(xmlData.data(), p.request.out);
+		}
+};
+
+class VersionCommand: public GetCommand {
+	public:
+		VersionCommand() {
+			m_commandName = "version";
+			m_isWriteCommand = false;
+		}
+
+	protected:
+		virtual void 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);
+		}
+};
+
+class NewDirCommand: public PostCommand {
+	public:
+		NewDirCommand() {
+			m_commandName = "newdir";
+			m_isWriteCommand = true;
+		}
+
+	protected:
+		virtual void start(CommandParameters& p) {
+			if (!readContent(p))
+				return;
+
+			FCGX_PutS("Content-Type: text/plain\r\n\r\n", p.request.out);
+			QXmlStreamReader xml(m_content);
+
+			while (!xml.atEnd()) {
+				while (xml.readNextStartElement()) {
+					if (xml.name() == "dirname") {
+						QString dirname = xml.readElementText();
+						QDir dir(m_path);
+						if (dir.mkdir(dirname)) {
+							FCGX_PutS("Successfully created directory", p.request.out);
+						} else {
+							FCGX_PutS("Error creating directory", p.request.out);
+						}
+					}
+				}
+			}
+		}
+};
+
+class InfoCommand: public PostCommand {
+	public:
+		InfoCommand() {
+			m_commandName = "info";
+			m_isWriteCommand = false;
+		}
+
+	protected:
+		virtual void start(CommandParameters& p) {
+			if (!readContent(p))
+				return;
+
+			FCGX_PutS("Content-Type: text/plain\r\n\r\n", p.request.out);
+			QXmlStreamReader xml(m_content);
+
+			while (!xml.atEnd()) {
+				while (xml.readNextStartElement()) {
+					if (xml.name() == "files") {
+						while (xml.readNextStartElement()) {
+							if (xml.name() == "file") {
+								QString filename = xml.readElementText();
+								QFileInfo fileInfo(m_path + "/" + filename);
+								qint64 size = fileInfo.size();
+								QString date = fileInfo.lastModified().toString();
+								if (fileInfo.isDir()) {
+									FCGX_PutS(QString("%1, %2 (folder)<br>")
+											.arg(filename)
+											.arg(date).toUtf8().data(), p.request.out);
+								} else {
+									FCGX_PutS(QString("%1, %2 bytes, %3 (file)<br>")
+											.arg(filename)
+											.arg(size)
+											.arg(date).toUtf8().data(), p.request.out);
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+};
+
+class DownloadZipCommand: public PostCommand {
+	public:
+		DownloadZipCommand() {
+			m_commandName = "download-zip";
+		}
+
+	protected:
+		virtual void start(CommandParameters& p) {
+			if (!readContent(p))
+				return;
+
+			QXmlStreamReader xml(m_content);
+
+			QByteArray zipData;
+			QStringList argumentList;
+			QTemporaryFile tempfile(QDir::tempPath() + "/webboxXXXXXX.zip");
+			tempfile.open();
+			QFileInfo fileInfo(tempfile);
+			QString tempfilePath = fileInfo.absolutePath();
+			QString tempfileName = fileInfo.fileName();
+			tempfile.close();
+			tempfile.remove();
+
+			argumentList << "-r"; // recursive packing
+			argumentList << tempfilePath + "/" + tempfileName; // zip filename
+
+			while (!xml.atEnd()) {
+				while (xml.readNextStartElement()) {
+					if (xml.name() == "files") {
+						while (xml.readNextStartElement()) {
+							if (xml.name() == "file") {
+								QString filename = xml.readElementText();
+
+								argumentList.append(filename); // add parts
+							}
+						}
+					}
+				}
+			}
+
+			QProcess process;
+			process.setWorkingDirectory(m_path);
+			process.setProgram("/usr/bin/zip");
+			process.setArguments(argumentList);
+			process.start();
+			process.waitForFinished();
+
+			QString debugText = process.readAll();
+			process.setReadChannel(QProcess::StandardError);
+			debugText += process.readAll();
+
+			if (process.state() != QProcess::NotRunning ||
+			    process.exitCode() != 0 ||
+			    process.exitStatus() != QProcess::NormalExit)
+			{
+				printHttpError(500, QString("Error running process: %1 %2 %3 %4 %5 %6 %7").
+					  arg(process.state()).
+					  arg(process.exitCode()).
+					  arg(process.exitStatus()).
+					  arg(tempfilePath).
+					  arg(tempfileName).
+					  arg(argumentList[0]).
+					  arg(debugText), p);
+			} else {
+
+				QFile tempfile(tempfilePath + "/" + tempfileName);
+				if (!tempfile.open(QIODevice::ReadOnly)) {
+					printHttpError(500, QString("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("Content-Type: application/octet-stream\r\n\r\n", p.request.out);
+					
+					FCGX_PutStr(zipData.data(), zipData.size(), p.request.out);
+				}
+
+				tempfile.close();
+				tempfile.remove();
+			}
+		}
+};
+
+class DeleteCommand: public PostCommand {
+	public:
+		DeleteCommand() {
+			m_commandName = "delete";
+			m_isWriteCommand = true;
+		}
+
+	protected:
+		virtual void start(CommandParameters& p) {
+			if (!readContent(p))
+				return;
+
+			QXmlStreamReader xml(m_content);
+			
+			QString response = "";
+
+			while (!xml.atEnd()) {
+				while (xml.readNextStartElement()) {
+					if (xml.name() == "files") {
+						while (xml.readNextStartElement()) {
+							if (xml.name() == "file") {
+								QString 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);
+									}
+								} else if (fileInfo.isFile()) {
+									QFile file(m_path + "/" + filename);
+									if (!file.remove()) {
+										response += QString("Error on removing file %1<br/>").arg(filename);
+									}
+								} else {
+									response += QString("Error: %1 is neither file nor directory.<br/>").arg(filename);
+								}
+							}
+						}
+					}
+				}
+			}
+
+			if (response == "") {
+				response = "OK";
+			}
+			
+			FCGX_PutS("Content-Type: text/plain\r\n\r\n", p.request.out);
+			FCGX_PutS(response.toUtf8().data(), p.request.out);
+		}
+};
+
+class MoveCommand: public PostCommand {
+	public:
+		MoveCommand() {
+			m_commandName = "move";
+			m_isWriteCommand = true;
+		}
+
+	protected:
+		virtual void start(CommandParameters& p) {
+			if (!readContent(p))
+				return;
+
+			QXmlStreamReader xml(m_content);
+			
+			QString response = "";
+			QString targetDir;
+
+			while (!xml.atEnd()) {
+				while (xml.readNextStartElement()) {
+					if (xml.name() == "request") {
+						while (xml.readNextStartElement()) {
+							if (xml.name() == "target") {
+								targetDir = xml.readElementText();
+							} else if (xml.name() == "file") {
+								QString 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);
+									}
+								} 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);
+									}
+								} else {
+									response += QString("Error: %1 is neither file nor directory.<br/>").arg(filename);
+								}
+							}
+						}
+					}
+				}
+			}
+
+			if (response == "") {
+				response = "OK";
+			}
+			
+			FCGX_PutS("Content-Type: text/plain\r\n\r\n", p.request.out);
+			FCGX_PutS(response.toUtf8().data(), p.request.out);
+		}
+};
+
+class RenameCommand: public PostCommand {
+	public:
+		RenameCommand() {
+			m_commandName = "rename";
+			m_isWriteCommand = true;
+		}
+
+	protected:
+		virtual void start(CommandParameters& p) {
+			if (!readContent(p))
+				return;
+
+			QXmlStreamReader xml(m_content);
+			
+			QString oldname;
+			QString newname;
+
+			while (!xml.atEnd()) {
+				while (xml.readNextStartElement()) {
+					if (xml.name() == "request") {
+						while (xml.readNextStartElement()) {
+							if (xml.name() == "oldname") {
+								oldname = xml.readElementText();
+							} else
+							if (xml.name() == "newname") {
+								newname = xml.readElementText();
+							}
+						}
+					}
+				}
+			}
+			
+			QDir dir(m_path);
+			QString response;
+			if (!dir.rename(oldname, newname)) {
+				response = QString("Error renaming %1 to %2<br/>").arg(oldname).arg(newname);
+			} else {
+				response = "OK";
+			}
+			
+			FCGX_PutS("Content-Type: text/plain\r\n\r\n", p.request.out);
+			FCGX_PutS(response.toUtf8().data(), p.request.out);
+		}
+};
+
+class UploadCommand: public PostCommand {
+	public:
+		UploadCommand() {
+			m_commandName = "upload";
+			m_isWriteCommand = true;
+		}
+
+	protected:
+		virtual void start(CommandParameters& p) {
+			if (!readContent(p))
+				return;
+
+			FCGX_PutS("Content-Type: text/plain\r\n\r\n", p.request.out);
+			QString contentType(FCGX_GetParam("CONTENT_TYPE", p.request.envp));
+
+			QString separator("boundary=");
+			if (!contentType.contains(separator)) {
+				FCGX_PutS(QString("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);
+				} else {
+					while (true) {
+						int start = m_content.indexOf(boundary) + boundary.size();
+						int end = m_content.indexOf(QByteArray("\r\n") + boundary, start);
+
+						if (end == -1) { // no further boundary found: all handled.
+							break;
+						}
+
+						QByteArray filecontent = m_content.mid(start, end - start);
+						int nextBoundaryIndex = end;
+
+						// Read filename
+						start = filecontent.indexOf("filename=\"");
+						if (start == -1) {
+							FCGX_PutS(QString("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);
+							} else {
+								QString filename = QString::fromUtf8(filecontent.mid(start, end - start));
+
+								if (filename.size() < 1) {
+									FCGX_PutS(QString("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);
+									} else {
+
+										filecontent = filecontent.mid(start + QString("\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);
+										} else {
+											qint64 written = file.write(filecontent);
+											if (written != filecontent.size()) {
+												FCGX_PutS(QString("Error writing file").toUtf8().data(), p.request.out);
+											}
+										}
+									}
+								}
+							}
+						}
+						m_content.remove(0, nextBoundaryIndex);
+					}
+				}
+			}
+		}
+};
+
+class DownloadCommand: public GetCommand {
+	public:
+		DownloadCommand() {
+			m_commandName = ""; // default command w/o explict "command=" query argument
+			m_isWriteCommand = false;
+		}
+
+	protected:
+		virtual void 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("Content-Type: application/octet-stream\r\n\r\n", p.request.out);
+				
+				while (!file.atEnd()) {
+					QByteArray ba = file.read(BUFSIZE);
+					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);
+			}
+		}
+};
+
+// 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;
+}
+#endif
diff --git a/plugins/webbox/webbox.h b/plugins/webbox/webbox.h
index 037a725..d6d26c1 100644
--- a/plugins/webbox/webbox.h
+++ b/plugins/webbox/webbox.h
@@ -8,7 +8,11 @@ public:
  webbox_plugin();
  ~webbox_plugin();
  std::string name();
- std::string generate_page(std::string path);
+ std::string generate_page(
+  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
+ );
 };
 
 extern "C" BOOST_SYMBOL_EXPORT webbox_plugin webserver_plugin;
-- 
cgit v1.2.3