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 --- Makefile | 12 ++ TODO | 22 +++ apache/localhost.conf | 47 ++++++ html/index.html | 57 +++++++ html/webbox.css | 138 ++++++++++++++++ html/webbox.js | 446 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/Makefile | 17 ++ src/webbox.cpp | 333 +++++++++++++++++++++++++++++++++++++ 8 files changed, 1072 insertions(+) create mode 100644 Makefile create mode 100644 TODO create mode 100644 apache/localhost.conf create mode 100644 html/index.html create mode 100644 html/webbox.css create mode 100644 html/webbox.js create mode 100644 src/Makefile create mode 100644 src/webbox.cpp diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..cae9e7c --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +all: + make -C src + +install: all + sudo service apache2 stop + sudo cp -r html/* /var/www/webbox/ + sudo mkdir -p /usr/lib/webbox + sudo cp src/query /usr/lib/webbox/ + sudo service apache2 start + +clean: + make -C src clean diff --git a/TODO b/TODO new file mode 100644 index 0000000..ba7f43c --- /dev/null +++ b/TODO @@ -0,0 +1,22 @@ +Prio 1 (for next version) +====== + +Icons: menu, file, directory +debian/ +implement TBD features +handle spaces/umlauts +download zip +Upload bug +Refresh + +Prio 2 (for future versions) +====== + +gallery +chromecast +player +handle writability +register GET/POST functions +handle too small window +sandclock during operations +i18n diff --git a/apache/localhost.conf b/apache/localhost.conf new file mode 100644 index 0000000..160dbe3 --- /dev/null +++ b/apache/localhost.conf @@ -0,0 +1,47 @@ + + # The ServerName directive sets the request scheme, hostname and port that + # the server uses to identify itself. This is used when creating + # redirection URLs. In the context of virtual hosts, the ServerName + # specifies what hostname must appear in the request's Host: header to + # match this virtual host. For the default virtual host (this file) this + # value is not decisive as it is used as a last resort host regardless. + # However, you must set it for any further virtual host explicitly. + ServerName localhost + + ServerAdmin webmaster@localhost + DocumentRoot /var/www/webbox + + FcgidInitialEnv WEBBOX_PATH /home/stigge/testbox + FcgidInitialEnv WEBBOX_NAME Testbox + FcgidMaxRequestLen 100000000 + + ScriptAlias /bin/ /usr/lib/webbox/ + + SetHandler fcgid-script + + AllowOverride None + Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch + # Order allow,deny + # Allow from all + Require all granted + + + + # Available loglevels: trace8, ..., trace1, debug, info, notice, warn, + # error, crit, alert, emerg. + # It is also possible to configure the loglevel for particular + # modules, e.g. + #LogLevel info ssl:warn + + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + + # For most configuration files from conf-available/, which are + # enabled or disabled at a global level, it is possible to + # include a line for only one particular virtual host. For example the + # following line enables the CGI configuration for this host only + # after it has been globally disabled with "a2disconf". + #Include conf-available/serve-cgi-bin.conf + + +# vim: syntax=apache ts=4 sw=4 sts=4 sr noet diff --git a/html/index.html b/html/index.html new file mode 100644 index 0000000..023b8bc --- /dev/null +++ b/html/index.html @@ -0,0 +1,57 @@ + + + + + Webbox + + + + +
+

+ + + + + + + + + + + + + +
+
+
+ +
+
+ + + + + + + + + + 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