diff options
| -rw-r--r-- | TODO | 11 | ||||
| -rw-r--r-- | html/webbox.js | 49 | ||||
| -rw-r--r-- | src/Makefile | 4 | ||||
| -rw-r--r-- | src/webbox.cpp | 879 | 
4 files changed, 565 insertions, 378 deletions
| @@ -1,20 +1,19 @@  Prio 1 (for next version)  ====== +handle writability +rename function +gallery +scroll in dialog/menu / handle too small popup window  Prio 2 (for future versions)  ====== -scroll in dialog/menu / handle too small popup window +links in path line, to dirs  google pagespeed insights https://developers.google.com/speed/pagespeed/insights/?url=http%3A%2F%2Fwww.kneipenband.com%2Fwebbox%2F&tab=mobile -rename function -handle errors from http -gallery  chromecast  player -handle writability -register GET/POST functions  sandclock during operations  i18n  forward/back button diff --git a/html/webbox.js b/html/webbox.js index 8612062..01738be 100644 --- a/html/webbox.js +++ b/html/webbox.js @@ -354,11 +354,14 @@ function createDir() {  		var xhr = new XMLHttpRequest();  		xhr.onreadystatechange = function() { -			if (this.readyState != 4 || this.status != 200) { +			if (this.readyState != 4) {  				return;  			} - -			document.getElementById("dialog").innerHTML = xhr.responseText; +			if (this.status != 200) { +				document.getElementById("dialog").innerHTML = "HTTP error"; +			} else { +				document.getElementById("dialog").innerHTML = xhr.responseText; +			}  			document.getElementById("okbutton").onclick = hideDialog;  			document.getElementById("okbutton").focus();  			loadContents(currentDir); // load new file list with new dir @@ -396,18 +399,22 @@ function onUploadFile() {  	var xhr = new XMLHttpRequest();  	xhr.onreadystatechange = function() { -		if (this.readyState != 4 || this.status != 200) { +		if (this.readyState != 4) {  			return;  		}  		showDialog();  		var message = ""; -		if (xhr.responseText == "OK") { -			message = "Upload successful."; -			loadContents(currentDir); // load new file list with uploaded file +		if (this.status != 200) { +			message = "HTTP error";  		} else { -			message = "Error: " + xhr.responseText; +			if (xhr.responseText == "OK") { +				message = "Upload successful."; +				loadContents(currentDir); // load new file list with uploaded file +			} else { +				message = "Error: " + xhr.responseText; +			}  		}  		document.getElementById("dialog").innerHTML = message; @@ -450,11 +457,14 @@ function deleteItems() {  		var xhr = new XMLHttpRequest();  		xhr.onreadystatechange = function() { -			if (this.readyState != 4 || this.status != 200) { +			if (this.readyState != 4) {  				return;  			} - -			document.getElementById("dialog").innerHTML = xhr.responseText; +			if (this.status != 200) { +				document.getElementById("dialog").innerHTML = "HTTP error"; +			} else { +				document.getElementById("dialog").innerHTML = xhr.responseText; +			}  			document.getElementById("okbutton").onclick = hideDialog;  			loadContents(currentDir); // load new file list with deleted items  		} @@ -505,11 +515,15 @@ function move() {  		var xhr = new XMLHttpRequest();  		xhr.onreadystatechange = function() { -			if (this.readyState != 4 || this.status != 200) { +			if (this.readyState != 4) {  				return;  			} +			if (this.status != 200) { +				document.getElementById("dialog").innerHTML = "HTTP error"; +			} else { +				document.getElementById("dialog").innerHTML = xhr.responseText; +			} -			document.getElementById("dialog").innerHTML = xhr.responseText;  			document.getElementById("okbutton").onclick = hideDialog;  			document.getElementById("okbutton").focus();  			loadContents(currentDir); // load new file list with deleted items @@ -556,11 +570,14 @@ function info() {  	var xhr = new XMLHttpRequest();  	xhr.onreadystatechange = function() { -		if (this.readyState != 4 || this.status != 200) { +		if (this.readyState != 4) {  			return;  		} - -		document.getElementById("dialog").innerHTML = xhr.responseText; +		if (this.status != 200) { +			document.getElementById("dialog").innerHTML = "HTTP error"; +		} else { +			document.getElementById("dialog").innerHTML = xhr.responseText; +		}  	}  	var parser = new DOMParser(); diff --git a/src/Makefile b/src/Makefile index d7f3847..0a8ab19 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,7 +1,7 @@  ARCH=$(shell dpkg-architecture -qDEB_HOST_MULTIARCH)  TARGET=query -CPPFLAGS=-Wall -O2 -fPIC -I/usr/include/$(ARCH)/qt5 -I/usr/include/$(ARCH)/qt5/QtCore -LDFLAGS=-Wall -O2 -fPIC -lstdc++ -lfcgi -lQt5Core +CPPFLAGS=-Wall -O2 -fPIC -std=gnu++17 -I/usr/include/$(ARCH)/qt5 -I/usr/include/$(ARCH)/qt5/QtCore +LDFLAGS=-Wall -O2 -fPIC -std=gnu++17 -lstdc++ -lfcgi -lQt5Core  OBJS=webbox.o  all: $(TARGET) diff --git a/src/webbox.cpp b/src/webbox.cpp index e991266..c3f03e2 100644 --- a/src/webbox.cpp +++ b/src/webbox.cpp @@ -10,8 +10,10 @@  #include <QDateTime>  #include <QProcess>  #include <QTemporaryFile> +#include <QUrlQuery> +#include <QPair> -#define PROGRAMVERSION "1.0" +#define PROGRAMVERSION "1.1"  #define BUFSIZE 1000000  // XML special characters: @@ -63,89 +65,154 @@ QString httpError(int httpStatusCode, QString message) {  	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);  } -void initlocale() { -	if (setenv("LC_ALL", "UTF-8", 1)) { -		exit(1); -	} -} +struct CommandParameters { +	FCGX_Request request; // the request -int main(int argc, char* argv[]) { -	initlocale(); +	QUrlQuery urlQuery; // derived from request +	QHash<QString, QString> paramHash; // derived from urlQuery -	int result = FCGX_Init(); -	if (result != 0) { -		return 1; // error on init -	} +	int count; // request count for this instance +}; -	FCGX_Request request; +class Command { +	public: +		// call interface +		void execute(CommandParameters& p) { +			QString requestMethod(FCGX_GetParam("REQUEST_METHOD", p.request.envp)); +			if (requestMethod != m_requestMethod) { +				printHttpError(403, QString("Bad request method"), p); +				return; +			} -	if (FCGX_InitRequest(&request, 0, 0) != 0) { -		return 1; // error on init -	} +			// process environment +			QString webboxPath(getenv("WEBBOX_PATH")); -	int count = 0; +			// 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; +			} -	while (FCGX_Accept_r(&request) == 0) { +			m_path = webboxPath + m_pathInfo; -		count++; +			this->start(p); +		} -		QString queryString(FCGX_GetParam("QUERY_STRING", request.envp)); +		QString getCommandName() { +			return m_commandName; +		} -		// URL parameters -		QStringList paramList = queryString.split("&"); -		QHash<QString, QString> paramHash; - -		foreach(QString keyValue, paramList) { -			QStringList keyValueList = keyValue.split("="); -			if (keyValueList.size() == 2) { -				QString key = keyValueList[0]; -				QString value = keyValueList[1]; -				paramHash[key] = value; -			} +	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; + +		// 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";  		} +}; -		QString command = paramHash["command"]; +class PostCommand: public Command { +	public: +		PostCommand() { +			m_requestMethod = "POST"; +		} -		// process environment -		QString webboxPath(getenv("WEBBOX_PATH")); +	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); -		// FastCGI request environment -		QString pathInfo(FCGX_GetParam("PATH_INFO", request.envp)); -		if (pathInfo == "") { -			pathInfo = "/"; +			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;  		} -		if (pathInfo.contains("..")) { -			FCGX_PutS(httpError(403, QString("Bad path: %1").arg(pathInfo)).toUtf8().data(), request.out); -			continue; + +		int m_contentLength; +		QByteArray m_content; +}; + +class DiagCommand: public GetCommand { +	public: +		DiagCommand() { +			m_commandName = "diag";  		} -		QString path = webboxPath + pathInfo; +	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; +			} -#if 0 -		// only for debugging -		if (command == "diag") { -			FCGX_PutS("Content-Type: text/html\r\n\r\n", request.out); +			FCGX_PutS("Content-Type: text/html\r\n\r\n", p.request.out); -			FCGX_PutS("<html><head><title>Params</title></head><body>\r\n", 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(count).toUtf8().data(), request.out); +			FCGX_PutS(QString("Request no. %1<br/><br/>\r\n").arg(p.count).toUtf8().data(), p.request.out); -			char** tmp = request.envp; +			char** tmp = p.request.envp;  			while (*tmp) { -				FCGX_PutS(QString("%1<br/>\r\n").arg(*tmp).toUtf8().data(), request.out); +				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(getenv("WEBBOX_PATH")).toUtf8().data(), request.out); +			FCGX_PutS(QString("<br/>WEBBOX_PATH=%1<br/>\r\n").arg(getenv("WEBBOX_PATH")).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", request.out); -		} else -#endif -		if (command == "list") { -			FCGX_PutS("Content-Type: text/xml\r\n\r\n", request.out); +			FCGX_PutS("</body></html>\r\n", p.request.out); +		} +}; -			QDir dir(path); +class ListCommand: public GetCommand { +	public: +		ListCommand() { +			m_commandName = "list"; +		} + +	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; @@ -153,7 +220,7 @@ int main(int argc, char* argv[]) {  			xmlWriter.writeStartDocument();  			xmlWriter.writeStartElement("list");  			foreach(QFileInfo i, dirEntryList) { -				if (pathInfo != "/" || i.fileName() != "..") { // skip on ".." in "/" +				if (m_pathInfo != "/" || i.fileName() != "..") { // skip on ".." in "/"  					xmlWriter.writeStartElement("listentry");  					xmlWriter.writeAttribute("type", i.isDir() ? "dir" : "file");  					xmlWriter.writeCharacters(i.fileName()); @@ -162,351 +229,352 @@ int main(int argc, char* argv[]) {  			}  			xmlWriter.writeEndElement(); // list  			xmlWriter.writeEndDocument(); -			FCGX_PutS(xmlData.data(), request.out); -		} else if (command == "title") { -			FCGX_PutS("Content-Type: text/plain\r\n\r\n", request.out); -			FCGX_PutS(getenv("WEBBOX_NAME"), request.out); -		} else if (command == "version") { -			FCGX_PutS("Content-Type: text/plain\r\n\r\n", request.out); -			FCGX_PutS(QString("webbox %1<br/>(C) 2017 <a href=\"https://www.reichwein.it/\">Reichwein.IT</a>\r\n").arg(PROGRAMVERSION).toUtf8().data(), request.out); -		} else if (command == "newdir") { // POST! -			QString contentLengthString(FCGX_GetParam("CONTENT_LENGTH", request.envp)); -			bool ok; -			int contentLength = contentLengthString.toInt(&ok); +			FCGX_PutS(xmlData.data(), p.request.out); +		} +}; -			FCGX_PutS("Content-Type: text/plain\r\n\r\n", request.out); -			if (!ok) { -				FCGX_PutS(QString("Bad content length").toUtf8().data(), request.out); -			} else { -				QByteArray content(contentLength, 0); -				 -				int result = FCGX_GetStr(content.data(), content.size(), request.in); -				if (result != content.size()) { -					FCGX_PutS(QString("Read error (%1/%2)").arg(result).arg(content.size()).toUtf8().data(), request.out); -				} else { -					QXmlStreamReader xml(content); +class TitleCommand: public GetCommand { +	public: +		TitleCommand() { +			m_commandName = "title"; +		} -					while (!xml.atEnd()) { -						while (xml.readNextStartElement()) { -							if (xml.name() == "dirname") { -								QString dirname = xml.readElementText(); -								QDir dir(path); -								if (dir.mkdir(dirname)) { -									FCGX_PutS("Successfully created directory", request.out); -								} else { -									FCGX_PutS("Error creating directory", request.out); -								} -							} +	protected: +		virtual void start(CommandParameters& p) { +			FCGX_PutS("Content-Type: text/plain\r\n\r\n", p.request.out); +			FCGX_PutS(getenv("WEBBOX_NAME"), p.request.out); +		} +}; + +class VersionCommand: public GetCommand { +	public: +		VersionCommand() { +			m_commandName = "version"; +		} + +	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) 2017 <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"; +		} + +	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);  						}  					}  				}  			} -		} else if (command == "info") { // POST! -			QString contentLengthString(FCGX_GetParam("CONTENT_LENGTH", request.envp)); -			bool ok; -			int contentLength = contentLengthString.toInt(&ok); +		} +}; -			FCGX_PutS("Content-Type: text/plain\r\n\r\n", request.out); -			if (!ok) { -				FCGX_PutS(QString("Bad content length").toUtf8().data(), request.out); -			} else { -				QByteArray content(contentLength, 0); -				 -				int result = FCGX_GetStr(content.data(), content.size(), request.in); -				if (result != content.size()) { -					FCGX_PutS(QString("Read error (%1/%2)").arg(result).arg(content.size()).toUtf8().data(), request.out); -				} else { -					QXmlStreamReader xml(content); +class InfoCommand: public PostCommand { +	public: +		InfoCommand() { +			m_commandName = "info"; +		} + +	protected: +		virtual void start(CommandParameters& p) { +			if (!readContent(p)) +				return; -					while (!xml.atEnd()) { +			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() == "files") { -								while (xml.readNextStartElement()) { -									if (xml.name() == "file") { -										QString filename = xml.readElementText(); -										QFileInfo fileInfo(path + "/" + filename); -										qint64 size = fileInfo.size(); -										QString date = fileInfo.lastModified().toString(); -										QString type = fileInfo.isDir() ? "directory" : "file"; -										FCGX_PutS(QString("%1, %2 bytes, %3 (%4)<br>") -												.arg(filename) -												.arg(size) -												.arg(date) -												.arg(type).toUtf8().data(), request.out); -									} -								} +							if (xml.name() == "file") { +								QString filename = xml.readElementText(); +								QFileInfo fileInfo(m_path + "/" + filename); +								qint64 size = fileInfo.size(); +								QString date = fileInfo.lastModified().toString(); +								QString type = fileInfo.isDir() ? "directory" : "file"; +								FCGX_PutS(QString("%1, %2 bytes, %3 (%4)<br>") +										.arg(filename) +										.arg(size) +										.arg(date) +										.arg(type).toUtf8().data(), p.request.out);  							}  						}  					}  				}  			} -		} else if (command == "download-zip") { // POST! -			QString contentLengthString(FCGX_GetParam("CONTENT_LENGTH", request.envp)); -			bool ok; -			int contentLength = contentLengthString.toInt(&ok); +		} +}; -			if (!ok) { -				FCGX_PutS("Content-Type: text/plain\r\n\r\n", request.out); -				FCGX_PutS(QString("Bad content length").toUtf8().data(), request.out); -			} else { -				QByteArray content(contentLength, 0); -				 -				int result = FCGX_GetStr(content.data(), content.size(), request.in); -				if (result != content.size()) { -					FCGX_PutS("Content-Type: text/plain\r\n\r\n", request.out); -					FCGX_PutS(QString("Read error (%1/%2)").arg(result).arg(content.size()).toUtf8().data(), request.out); -				} else { -					QXmlStreamReader xml(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()) { +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() == "files") { -								while (xml.readNextStartElement()) { -									if (xml.name() == "file") { -										QString filename = xml.readElementText(); +							if (xml.name() == "file") { +								QString filename = xml.readElementText(); -										argumentList.append(filename); // add parts -									} -								} +								argumentList.append(filename); // add parts  							}  						}  					} - -					QProcess process; -					process.setWorkingDirectory(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) -					{ -						FCGX_PutS("Content-Type: text/plain\r\n\r\n", request.out); -						FCGX_PutS(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).toUtf8().data(), request.out); -					} else { - -						QFile tempfile(tempfilePath + "/" + tempfileName); -						if (!tempfile.open(QIODevice::ReadOnly)) { -							FCGX_PutS("Content-Type: text/plain\r\n\r\n", request.out); -							FCGX_PutS(QString("Error reading file").toUtf8().data(), request.out); -						} else { -							zipData = tempfile.readAll(); - -							FCGX_PutS(QString("Content-Disposition: attachment; filename=\"%1\"\r\n").arg("webbox-download.zip").toUtf8().data(), request.out); -							FCGX_PutS("Content-Type: application/octet-stream\r\n\r\n", request.out); -							 -							FCGX_PutStr(zipData.data(), zipData.size(), request.out); -						} - -						tempfile.close(); -						tempfile.remove(); -					}  				}  			} -		} else if (command == "delete") { // POST! -			QString contentLengthString(FCGX_GetParam("CONTENT_LENGTH", request.envp)); -			bool ok; -			int contentLength = contentLengthString.toInt(&ok); -			if (!ok) { -				FCGX_PutS("Content-Type: text/plain\r\n\r\n", request.out); -				FCGX_PutS(QString("Bad content length").toUtf8().data(), request.out); +			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 { -				QByteArray content(contentLength, 0); -				 -				int result = FCGX_GetStr(content.data(), content.size(), request.in); -				if (result != content.size()) { -					FCGX_PutS("Content-Type: text/plain\r\n\r\n", request.out); -					FCGX_PutS(QString("Read error (%1/%2)").arg(result).arg(content.size()).toUtf8().data(), request.out); + +				QFile tempfile(tempfilePath + "/" + tempfileName); +				if (!tempfile.open(QIODevice::ReadOnly)) { +					printHttpError(500, QString("Error reading file"), p);  				} else { -					QXmlStreamReader xml(content); +					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); -					QString response = ""; +					FCGX_PutStr(zipData.data(), zipData.size(), p.request.out); +				} + +				tempfile.close(); +				tempfile.remove(); +			} +		} +}; -					while (!xml.atEnd()) { +class DeleteCommand: public PostCommand { +	public: +		DeleteCommand() { +			m_commandName = "delete"; +		} + +	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() == "files") { -								while (xml.readNextStartElement()) { -									if (xml.name() == "file") { -										QString filename = xml.readElementText(); - -										QFileInfo fileInfo(path + "/" + filename); -										if (fileInfo.isDir()) { -											QDir dir(path); -											if (!dir.rmdir(filename)) { -												response += QString("Error on removing directory %1<br/>").arg(filename); -											} -										} else if (fileInfo.isFile()) { -											QFile file(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 (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", request.out); -					FCGX_PutS(response.toUtf8().data(), request.out);  				}  			} -		} else if (command == "move") { // POST! -			QString contentLengthString(FCGX_GetParam("CONTENT_LENGTH", request.envp)); -			bool ok; -			int contentLength = contentLengthString.toInt(&ok); -			if (!ok) { -				FCGX_PutS("Content-Type: text/plain\r\n\r\n", request.out); -				FCGX_PutS(QString("Bad content length").toUtf8().data(), request.out); -			} else { -				QByteArray content(contentLength, 0); -				 -				int result = FCGX_GetStr(content.data(), content.size(), request.in); -				if (result != content.size()) { -					FCGX_PutS("Content-Type: text/plain\r\n\r\n", request.out); -					FCGX_PutS(QString("Read error (%1/%2)").arg(result).arg(content.size()).toUtf8().data(), request.out); -				} else { -					QXmlStreamReader xml(content); -					 -					QString response = ""; -					QString targetDir; +			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"; +		} + +	protected: +		virtual void start(CommandParameters& p) { +			if (!readContent(p)) +				return; -					while (!xml.atEnd()) { +			QXmlStreamReader xml(m_content); +			 +			QString response = ""; +			QString targetDir; + +			while (!xml.atEnd()) { +				while (xml.readNextStartElement()) { +					if (xml.name() == "request") {  						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(path + "/" + filename); -										if (fileInfo.isDir()) { -											QDir dir(path); -											if (!dir.rename(filename, targetDir + "/" + filename)) { -												response += QString("Error moving directory %1<br/>").arg(filename); -											} -										} else if (fileInfo.isFile()) { -											QFile file(path + "/" + filename); -											if (!file.rename(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 (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", request.out); -					FCGX_PutS(response.toUtf8().data(), request.out);  				}  			} -		} else if (command == "upload") { // POST! -			QString contentLengthString(FCGX_GetParam("CONTENT_LENGTH", request.envp)); -			bool ok; -			int contentLength = contentLengthString.toInt(&ok); -			FCGX_PutS("Content-Type: text/plain\r\n\r\n", request.out); -			if (!ok) { -				FCGX_PutS(QString("Bad content length").toUtf8().data(), request.out); + +			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 UploadCommand: public PostCommand { +	public: +		UploadCommand() { +			m_commandName = "upload"; +		} + +	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 content(contentLength, 0); -				 -				int result = FCGX_GetStr(content.data(), content.size(), request.in); -				if (result != content.size()) { -					FCGX_PutS(QString("Read error (%1/%2)").arg(result).arg(content.size()).toUtf8().data(), request.out); +				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 { -					QString contentType(FCGX_GetParam("CONTENT_TYPE", request.envp)); +					int start = m_content.indexOf(boundary) + boundary.size(); +					int end = m_content.indexOf(QByteArray("\r\n") + boundary, start); + +					m_content = m_content.mid(start, end - start); -					QString separator("boundary="); -					if (!contentType.contains(separator)) { -						FCGX_PutS(QString("No boundary defined").toUtf8().data(), request.out); +					// Read filename +					start = m_content.indexOf("filename=\""); +					if (start == -1) { +						FCGX_PutS(QString("Error reading filename / start").toUtf8().data(), p.request.out);  					} else { -						QByteArray boundary = QByteArray("--") + contentType.split(separator)[1].toUtf8(); -						int boundaryCount = content.count(boundary); -						if (boundaryCount != 2) { -							FCGX_PutS(QString("Bad boundary number found: %1").arg(boundaryCount).toUtf8().data(), request.out); -						} else { -							int start = content.indexOf(boundary) + boundary.size(); -							int end = content.indexOf(QByteArray("\r\n") + boundary, start); +						start += QByteArray("filename=\"").size(); -							content = content.mid(start, end - start); +						end = m_content.indexOf(QByteArray("\""), start); +						if (end == -1) { +							FCGX_PutS(QString("Error reading filename / end").toUtf8().data(), p.request.out); +						} else { +							QString filename = QString::fromUtf8(m_content.mid(start, end - start)); -							// Read filename -							start = content.indexOf("filename=\""); -							if (start == -1) { -								FCGX_PutS(QString("Error reading filename / start").toUtf8().data(), request.out); +							if (filename.size() < 1) { +								FCGX_PutS(QString("Bad filename").toUtf8().data(), p.request.out);  							} else { -								start += QByteArray("filename=\"").size(); - -								end = content.indexOf(QByteArray("\""), start); -								if (end == -1) { -									FCGX_PutS(QString("Error reading filename / end").toUtf8().data(), request.out); +								// Remove header +								start = m_content.indexOf(QByteArray("\r\n\r\n")); +								if (start == -1) { +									FCGX_PutS(QString("Error removing upload header").toUtf8().data(), p.request.out);  								} else { -									QString filename = QString::fromUtf8(content.mid(start, end - start)); -									if (filename.size() < 1) { -										FCGX_PutS(QString("Bad filename").toUtf8().data(), request.out); +									m_content = m_content.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 { -										// Remove header -										start = content.indexOf(QByteArray("\r\n\r\n")); -										if (start == -1) { -											FCGX_PutS(QString("Error removing upload header").toUtf8().data(), request.out); +										qint64 written = file.write(m_content); +										if (written != m_content.size()) { +											FCGX_PutS(QString("Error writing file").toUtf8().data(), p.request.out);  										} else { - -											content = content.mid(start + QString("\r\n\r\n").toUtf8().size()); - -											QFile file(path + "/" + filename); -											if (!file.open(QIODevice::WriteOnly)) { -												FCGX_PutS(QString("Error opening file").toUtf8().data(), request.out); -											} else { -												qint64 written = file.write(content); -												if (written != content.size()) { -													FCGX_PutS(QString("Error writing file").toUtf8().data(), request.out); -												} else { -													FCGX_PutS("OK", request.out); -												} -											} +											FCGX_PutS("OK", p.request.out);  										}  									}  								} @@ -515,21 +583,124 @@ int main(int argc, char* argv[]) {  					}  				}  			} -		} else { // default: download -			QFile file(path); +		} +}; + +class DownloadCommand: public GetCommand { +	public: +		DownloadCommand() { +			m_commandName = ""; // default command w/o explict "command=" query argument +		} + +	protected: +		virtual void start(CommandParameters& p) { +			QFile file(m_path);  			if (file.open(QIODevice::ReadOnly)) { -				QFileInfo fileInfo(path); -				FCGX_PutS(QString("Content-Disposition: attachment; filename=\"%1\"\r\n").arg(fileInfo.fileName()).toUtf8().data(), request.out); -				FCGX_PutS("Content-Type: application/octet-stream\r\n\r\n", request.out); +				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(), request.out); +					FCGX_PutStr(ba.data(), ba.size(), p.request.out);  				}  			} else { -				FCGX_PutS(httpError(500, QString("Bad file: %1").arg(pathInfo)).toUtf8().data(), request.out); +				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; + +	Commands commands; + +	DiagCommand diagCommand; +	commands.registerCommand(diagCommand); + +	ListCommand listCommand; +	commands.registerCommand(listCommand); + +	TitleCommand titleCommand; +	commands.registerCommand(titleCommand); + +	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); +	 +	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; | 
