From f939d19e9bcb0cc0cf048aa0c8037f1f9c5a8c8b Mon Sep 17 00:00:00 2001 From: Roland Stigge Date: Fri, 5 Jan 2018 21:03:31 +0100 Subject: Initial commit, basic working webbox (WIP), see TODO --- src/webbox.cpp | 333 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 333 insertions(+) create mode 100644 src/webbox.cpp (limited to 'src/webbox.cpp') diff --git a/src/webbox.cpp b/src/webbox.cpp new file mode 100644 index 0000000..ab65529 --- /dev/null +++ b/src/webbox.cpp @@ -0,0 +1,333 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + +#define PROGRAMVERSION "1.0" +#define BUFSIZE 1000000 + +// 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

%1 %2

%3

\r\n").arg(httpStatusCode).arg(description).arg(message); +} + +int main(int argc, char* argv[]) { + int result = FCGX_Init(); + if (result != 0) { + return 1; // error on init + } + + FCGX_Request request; + + if (FCGX_InitRequest(&request, 0, 0) != 0) { + return 1; // error on init + } + + int count = 0; + + while (FCGX_Accept_r(&request) == 0) { + + count++; + + QString queryString(FCGX_GetParam("QUERY_STRING", request.envp)); + + // 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; + } + } + + QString command = paramHash["command"]; + + // process environment + QString webboxPath(getenv("WEBBOX_PATH")); + + // FastCGI request environment + QString pathInfo(FCGX_GetParam("PATH_INFO", request.envp)); + if (pathInfo == "") { + pathInfo = "/"; + } + if (pathInfo.contains("..")) { + FCGX_PutS(httpError(403, QString("Bad path: %1").arg(pathInfo)).toUtf8().data(), request.out); + continue; + } + + QString path = webboxPath + pathInfo; + + if (command == "diag") { + FCGX_PutS("Content-Type: text/html\r\n\r\n", request.out); + + FCGX_PutS("Params\r\n", request.out); + + FCGX_PutS(QString("Request no. %1

\r\n").arg(count).toUtf8().data(), request.out); + + char** tmp = request.envp; + + while (*tmp) { + FCGX_PutS(QString("%1
\r\n").arg(*tmp).toUtf8().data(), request.out); + tmp++; + } + + FCGX_PutS(QString("
WEBBOX_PATH=%1
\r\n").arg(getenv("WEBBOX_PATH")).toUtf8().data(), request.out); + + FCGX_PutS("\r\n", request.out); + } else + if (command == "list") { + FCGX_PutS("Content-Type: text/xml\r\n\r\n", request.out); + + FCGX_PutS("\r\n", request.out); + + QDir dir(path); + QFileInfoList dirEntryList = dir.entryInfoList(QDir::NoDot | QDir::AllEntries, QDir::DirsFirst | QDir::Name); + foreach(QFileInfo i, dirEntryList) { + if (pathInfo != "/" || i.fileName() != "..") { // skip on ".." in "/" + FCGX_PutS(QString("%2\r\n").arg(i.isDir() ? "dir" : "file").arg(i.fileName()).toUtf8().data(), request.out); + } + } + FCGX_PutS("\r\n", 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("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); + + 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); + } + } + } + } + } + } + } 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); + + while (!xml.atEnd()) { + 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); + } + } + } + } + } + } + } + } 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; + + while (!xml.atEnd()) { + while (xml.readNextStartElement()) { + if (xml.name() == "files") { + while (xml.readNextStartElement()) { + if (xml.name() == "file") { + QString filename = xml.readElementText(); + + zipData.append(filename.toUtf8()); // TBD + } + } + } + } + } + + 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); + } + } + } 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); + } 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 { + QString contentType(FCGX_GetParam("CONTENT_TYPE", request.envp)); + + if (!contentType.contains("boundary=--")) { + FCGX_PutS(QString("No boundary defined").toUtf8().data(), request.out); + } else { + QByteArray boundary = contentType.split("boundary=--")[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(boundary, start); + + content = 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); + } else { + start += QString("filename=\"").size(); + + end = content.indexOf("\"", start); + if (end == -1) { + FCGX_PutS(QString("Error reading filename / end").toUtf8().data(), request.out); + } else { + QString filename = content.mid(start, end - start); + + if (filename.size() < 1) { + FCGX_PutS(QString("Bad filename").toUtf8().data(), request.out); + } else { + // Remove header + start = content.indexOf("\r\n\r\n"); + if (start == -1) { + FCGX_PutS(QString("Error removing upload header").toUtf8().data(), request.out); + } else { + + content = content.mid(start + QString("\r\n\r\n").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); + } + } + } + } + } + } + } + } + } + } + } else { // default: download + QFile file(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); + + while (!file.atEnd()) { + QByteArray ba = file.read(BUFSIZE); + FCGX_PutStr(ba.data(), ba.size(), request.out); + } + } else { + FCGX_PutS(httpError(500, QString("Bad file: %1").arg(pathInfo)).toUtf8().data(), request.out); + } + } + } + + return 0; +} + -- cgit v1.2.3