diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile | 17 | ||||
| -rw-r--r-- | src/webbox.cpp | 333 | 
2 files changed, 350 insertions, 0 deletions
| diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..e960f67 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,17 @@ +TARGET=query +CPPFLAGS=-Wall -O2 -fPIC -I/usr/include/x86_64-linux-gnu/qt5 -I/usr/include/x86_64-linux-gnu/qt5/QtCore +LDFLAGS=-Wall -O2 -fPIC -lstdc++ -lfcgi -lQt5Core +OBJS=webbox.o + +all: $(TARGET) + +$(TARGET): $(OBJS) +	gcc $(LDFLAGS) -o $@ $^ + +%.o: %.cpp +	gcc $(CPPFLAGS) -c -o $@ $< + +clean: +	-rm -rf $(TARGET) $(OBJS) + +.PHONY: clean all 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; +} + | 
