From 7f5df336e01f082ca312f7d2d013eb685115ff39 Mon Sep 17 00:00:00 2001 From: Daniel Brunner <0xFEEDC0DE64@gmail.com> Date: Thu, 4 Oct 2018 23:32:24 +0200 Subject: [PATCH] Implemented directory listing --- fileserverapplication.cpp | 121 +++++++++++++++++++++++++++++++-- fileserverapplication.h | 7 ++ fileserverplugin.pro | 2 +- fileserverplugin_resources.qrc | 7 ++ images/back.png | Bin 0 -> 308 bytes images/folder.png | Bin 0 -> 295 bytes images/text.png | Bin 0 -> 288 bytes 7 files changed, 131 insertions(+), 6 deletions(-) create mode 100644 fileserverplugin_resources.qrc create mode 100644 images/back.png create mode 100644 images/folder.png create mode 100644 images/text.png diff --git a/fileserverapplication.cpp b/fileserverapplication.cpp index 6819a6a..958c9d5 100644 --- a/fileserverapplication.cpp +++ b/fileserverapplication.cpp @@ -1,14 +1,36 @@ #include "fileserverapplication.h" +#include +#include +#include +#include +#include +#include +#include + +#include + #include "webserver.h" #include "httprequest.h" #include "httpresponse.h" #include "httpclientconnection.h" +#include "httpnotfoundexception.h" +#include "httpexception.h" + +const QString FileserverApplication::SERVER_NAME(QStringLiteral("Fileserver 1.0")); +const QDir FileserverApplication::PLUGIN_RESOURCES_DIR(QStringLiteral(":/webserver/plugins/fileserverplugin")); FileserverApplication::FileserverApplication(const QJsonObject &config, WebServer &webServer) : WebApplication(&webServer), m_webServer(webServer) { - Q_UNUSED(config) + if(!config.contains(QStringLiteral("rootPath"))) + throw std::runtime_error("Fileserver application does not contain rootPath"); + + const auto rootPathVal = config.value(QStringLiteral("rootPath")); + if(!rootPathVal.isString()) + throw std::runtime_error("Fileserver application rootPath is not a string"); + + m_rootPath = QDir(rootPathVal.toString()); } void FileserverApplication::start() @@ -17,8 +39,97 @@ void FileserverApplication::start() void FileserverApplication::handleRequest(HttpClientConnection *connection, const HttpRequest &request) { - HttpResponse response; - response.protocol = request.protocol; - response.statusCode = HttpResponse::StatusCode::OK; - connection->sendResponse(response, "Hello from FileserverApplication: " + request.path); + static const QString pluginPrefix(QStringLiteral("/:fileserverplugin/")); + const auto pluginResource = request.path.startsWith(pluginPrefix); + + const QFileInfo fileInfo((pluginResource ? PLUGIN_RESOURCES_DIR : m_rootPath) + .absoluteFilePath(request.path.mid(pluginResource ? pluginPrefix.length() : 1))); + + if(!fileInfo.exists()) + throw HttpNotFoundException(request); + + if(fileInfo.isDir()) + { + if(!request.path.endsWith(QLatin1Char('/'))) + { + const QString newUrl = request.path + '/'; + + HttpResponse response; + response.protocol = request.protocol; + response.statusCode = HttpResponse::StatusCode::MovedPermanently; + response.headers.insert(HttpResponse::HEADER_SERVER, SERVER_NAME); + response.headers.insert(HttpResponse::HEADER_LOCATION, newUrl); + connection->sendResponse(response, QStringLiteral("%1").arg(QString(QUrl::toPercentEncoding(newUrl)).toHtmlEscaped()).arg(tr("Moved."))); + return; + } + + const QDir dir(fileInfo.absoluteFilePath()); + + QString content; + static const QString line(QStringLiteral(" %2%3")); + + if(request.path != QStringLiteral("/")) + content.append(line.arg(QStringLiteral("back.png")).arg(QStringLiteral("..")).arg(tr("Parent directory")).arg(QString())); + + for(auto info : dir.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot)) + content.append(line + .arg(info.isDir() ? QStringLiteral("folder.png") : QStringLiteral("text.png")) + .arg(QString(QUrl::toPercentEncoding(info.fileName())).toHtmlEscaped()) + .arg(info.fileName().toHtmlEscaped()) + .arg(info.isDir() ? QString() : formatSize(info.size()))); + + HttpResponse response; + response.protocol = request.protocol; + response.statusCode = HttpResponse::StatusCode::OK; + response.headers.insert(HttpResponse::HEADER_SERVER, SERVER_NAME); + connection->sendResponse(response, QStringLiteral("" + "" + "" + "" + "" + "" + "" + "
" + "" + "" + "" + "" + "" + "" + "" + "" + "%0" + "" + "
FilenameSize
" + "
" + "" + "").arg(content)); + } + else if(fileInfo.isFile()) + { + std::unique_ptr file = std::make_unique(fileInfo.filePath()); + if(!file->open(QIODevice::ReadOnly)) + throw HttpException(request, tr("Could not open requested file \"%0\" because %1").arg(request.path, file->errorString()).toStdString()); + + HttpResponse response; + response.protocol = request.protocol; + response.statusCode = HttpResponse::StatusCode::OK; + response.headers.insert(HttpResponse::HEADER_SERVER, SERVER_NAME); + connection->sendResponse(response, std::move(file)); + } +} + +QString FileserverApplication::formatSize(qint64 size) +{ + static const QStringList suffixes { QStringLiteral("B"), QStringLiteral("kB"), QStringLiteral("MB"), QStringLiteral("GB"), QStringLiteral("TB") }; + static const int stepSize = 1024; + + for(const auto &suffix : suffixes) + { + if(size < stepSize) + return QStringLiteral("%0 %1").arg(size).arg(suffix); + size /= stepSize; + } + + return QStringLiteral("%0 %1").arg(size).arg(suffixes.last()); } diff --git a/fileserverapplication.h b/fileserverapplication.h index 46feb19..2ab3d3c 100644 --- a/fileserverapplication.h +++ b/fileserverapplication.h @@ -2,6 +2,8 @@ #include "webapplication.h" +#include + class QJsonObject; class WebServer; @@ -9,6 +11,8 @@ class WebServer; class FileserverApplication : public WebApplication { Q_OBJECT + static const QString SERVER_NAME; + static const QDir PLUGIN_RESOURCES_DIR; public: FileserverApplication(const QJsonObject &config, WebServer &webServer); @@ -18,5 +22,8 @@ public: void handleRequest(HttpClientConnection *connection, const HttpRequest &request) Q_DECL_OVERRIDE; private: + static QString formatSize(qint64 size); + WebServer &m_webServer; + QDir m_rootPath; }; diff --git a/fileserverplugin.pro b/fileserverplugin.pro index 060d2d2..d027e25 100644 --- a/fileserverplugin.pro +++ b/fileserverplugin.pro @@ -10,7 +10,7 @@ SOURCES += fileserverplugin.cpp \ FORMS += -RESOURCES += +RESOURCES += fileserverplugin_resources.qrc TRANSLATIONS += diff --git a/fileserverplugin_resources.qrc b/fileserverplugin_resources.qrc new file mode 100644 index 0000000..ae7049d --- /dev/null +++ b/fileserverplugin_resources.qrc @@ -0,0 +1,7 @@ + + + images/back.png + images/folder.png + images/text.png + + diff --git a/images/back.png b/images/back.png new file mode 100644 index 0000000000000000000000000000000000000000..a97b41e4c16a20aca70bf494e3731c3cc1112ced GIT binary patch literal 308 zcmeAS@N?(olHy`uVBq!ia0y~yU=U$oU=U+rW?*12%3o;8z`!6B;1lBd|Ns9p|NqaN zIWsLS&DhwOfq`LO_pIj(3=B*qL4Lvi8J=!8@M2(KU`+CMcVXyYmGxj?U`X(EaSY*z zc0GTQ^MC<|Yhd_>2Mn!x69j|e#5D^4hu)g*@>N+zuTJ8k%)`qQ_B70!+Q${*y1r|b z*~*x&u5XV!OKrdFdEMT3)_Xx--jhGPPDEde{{GIQa*6Jl>b-U;M`!T+>!}^c>3%N3 zz`zhz;u=wsoS&PUnpeUQl95@gkXTfrkXfvdnWs>ak*ZKonv|27tdNqQo0yrWr{JAh zmYJvEQJS8STCAgx4dP`uq-K`rCFkerC81lmGw# literal 0 HcmV?d00001 diff --git a/images/folder.png b/images/folder.png new file mode 100644 index 0000000000000000000000000000000000000000..df03153b8a2891dbc8f90ed000a694f824375ee2 GIT binary patch literal 295 zcmeAS@N?(olHy`uVBq!ia0y~yU=U$oU=U+rW?*12%3o;8z`!6B;1lBd|NsC0XJ(%H z|9@thv9Yl+0|Ub$pcvu|f6$rR63P|ieH1p89 z5HZm0~U|X!^Qi~J}EiFyUzP+Erz`(%Z>FVdQ&MBb@09MLd7ytkO literal 0 HcmV?d00001 diff --git a/images/text.png b/images/text.png new file mode 100644 index 0000000000000000000000000000000000000000..c43fb622fba6ccd1d69e660c43d1a4845617aab8 GIT binary patch literal 288 zcmeAS@N?(olHy`uVBq!ia0y~yU=U$oU=U+rW?*12%3o;8z`(#C;1lBd|Ns9p|NqaN zIn&tKn1O*IbffW91_lPEk|4j}{|ryJ8+b7=Ffb;0ySp%Su*!NcFfjOgx;Tb#L{B}j zk@tWC59`I?1jh9Z5)3K)4j;eI*L0QH`E1qY!*}o2&X(j}ur;bIcXg`tYVYOlE?Lv3 zoH9<+>iM~{MB{1LL%085KZ>?#ZWcc>NsK`$VQ)MG14CGeYeY$Mer|4RUI{};MrN@> zVo`}gX0bwMo8TBKlTX=z&a?fo1E1_lOCS3j3^P6