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 0000000..a97b41e Binary files /dev/null and b/images/back.png differ diff --git a/images/folder.png b/images/folder.png new file mode 100644 index 0000000..df03153 Binary files /dev/null and b/images/folder.png differ diff --git a/images/text.png b/images/text.png new file mode 100644 index 0000000..c43fb62 Binary files /dev/null and b/images/text.png differ