From 188a87d1a66f09dc7b224b89e6c1c16d77681423 Mon Sep 17 00:00:00 2001 From: Roland Stigge Date: Wed, 10 Jan 2018 20:49:03 +0100 Subject: Handle errors from HTTP, separated out commands as classes --- TODO | 11 +- html/webbox.js | 49 ++-- src/Makefile | 4 +- src/webbox.cpp | 879 ++++++++++++++++++++++++++++++++++----------------------- 4 files changed, 565 insertions(+), 378 deletions(-) diff --git a/TODO b/TODO index 03d1a79..e803457 100644 --- a/TODO +++ b/TODO @@ -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 #include #include +#include +#include -#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

%1 %2

%3

\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 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 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("Params\r\n", request.out); + FCGX_PutS("Params\r\n", p.request.out); - FCGX_PutS(QString("Request no. %1

\r\n").arg(count).toUtf8().data(), request.out); + FCGX_PutS(QString("Request no. %1

\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
\r\n").arg(*tmp).toUtf8().data(), request.out); + FCGX_PutS(QString("%1
\r\n").arg(*tmp).toUtf8().data(), p.request.out); tmp++; } - FCGX_PutS(QString("
WEBBOX_PATH=%1
\r\n").arg(getenv("WEBBOX_PATH")).toUtf8().data(), request.out); + FCGX_PutS(QString("
WEBBOX_PATH=%1
\r\n").arg(getenv("WEBBOX_PATH")).toUtf8().data(), p.request.out); + + FCGX_PutS(QString("
URL Query=%1
\r\n").arg(p.urlQuery.toString()).toUtf8().data(), p.request.out); + - FCGX_PutS("\r\n", request.out); - } else -#endif - if (command == "list") { - FCGX_PutS("Content-Type: text/xml\r\n\r\n", request.out); + FCGX_PutS("\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
(C) 2017 Reichwein.IT\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
(C) 2017 Reichwein.IT\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)
") - .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)
") + .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
").arg(filename); - } - } else if (fileInfo.isFile()) { - QFile file(path + "/" + filename); - if (!file.remove()) { - response += QString("Error on removing file %1
").arg(filename); - } - } else { - response += QString("Error: %1 is neither file nor directory.
").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
").arg(filename); } + } else if (fileInfo.isFile()) { + QFile file(m_path + "/" + filename); + if (!file.remove()) { + response += QString("Error on removing file %1
").arg(filename); + } + } else { + response += QString("Error: %1 is neither file nor directory.
").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
").arg(filename); - } - } else if (fileInfo.isFile()) { - QFile file(path + "/" + filename); - if (!file.rename(path + "/" + targetDir + "/" + filename)) { - response += QString("Error on moving file %1
").arg(filename); - } - } else { - response += QString("Error: %1 is neither file nor directory.
").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
").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
").arg(filename); + } + } else { + response += QString("Error: %1 is neither file nor directory.
").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 { + 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 > 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; -- cgit v1.2.3