summaryrefslogtreecommitdiffhomepage
path: root/src/webbox.cpp
diff options
context:
space:
mode:
authorRoland Stigge <stigge@antcom.de>2018-01-05 21:03:31 +0100
committerRoland Stigge <stigge@antcom.de>2018-01-05 21:03:31 +0100
commitf939d19e9bcb0cc0cf048aa0c8037f1f9c5a8c8b (patch)
treec656bd35facfbf57959a9e5c3a6fcad0cdd6df0b /src/webbox.cpp
Initial commit, basic working webbox (WIP), see TODO
Diffstat (limited to 'src/webbox.cpp')
-rw-r--r--src/webbox.cpp333
1 files changed, 333 insertions, 0 deletions
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 <fcgiapp.h>
+
+#include <QString>
+#include <QStringList>
+#include <QHash>
+#include <QDir>
+#include <QFileInfo>
+#include <QXmlStreamReader>
+#include <QDateTime>
+
+#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<html><body><h1>%1 %2</h1><p>%3</p></body></html>\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<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;
+ }
+ }
+
+ 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("<html><head><title>Params</title></head><body>\r\n", request.out);
+
+ FCGX_PutS(QString("Request no. %1<br/><br/>\r\n").arg(count).toUtf8().data(), request.out);
+
+ char** tmp = request.envp;
+
+ while (*tmp) {
+ FCGX_PutS(QString("%1<br/>\r\n").arg(*tmp).toUtf8().data(), request.out);
+ tmp++;
+ }
+
+ FCGX_PutS(QString("<br/>WEBBOX_PATH=%1<br/>\r\n").arg(getenv("WEBBOX_PATH")).toUtf8().data(), request.out);
+
+ FCGX_PutS("</body></html>\r\n", request.out);
+ } else
+ if (command == "list") {
+ FCGX_PutS("Content-Type: text/xml\r\n\r\n", request.out);
+
+ FCGX_PutS("<list>\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("<listentry type=\"%1\">%2</listentry>\r\n").arg(i.isDir() ? "dir" : "file").arg(i.fileName()).toUtf8().data(), request.out);
+ }
+ }
+ FCGX_PutS("</list>\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<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("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)<br>")
+ .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;
+}
+