From f0fc18d2ee02c61524486aa1e73b781a6f64ef23 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Fri, 8 Jul 2016 16:00:11 +0600 Subject: CLOUD: Add UploadFileHandler --- backends/module.mk | 1 + backends/networking/sdl_net/client.cpp | 2 + backends/networking/sdl_net/client.h | 1 + .../sdl_net/handlers/uploadfilehandler.cpp | 187 +++++++++++++++++++++ .../sdl_net/handlers/uploadfilehandler.h | 51 ++++++ backends/networking/sdl_net/localwebserver.cpp | 1 + backends/networking/sdl_net/localwebserver.h | 2 + backends/networking/sdl_net/reader.cpp | 6 + backends/networking/sdl_net/reader.h | 2 + 9 files changed, 253 insertions(+) create mode 100644 backends/networking/sdl_net/handlers/uploadfilehandler.cpp create mode 100644 backends/networking/sdl_net/handlers/uploadfilehandler.h diff --git a/backends/module.mk b/backends/module.mk index 3c6f5e5c39..0647d36b4f 100644 --- a/backends/module.mk +++ b/backends/module.mk @@ -65,6 +65,7 @@ MODULE_OBJS += \ networking/sdl_net/handlers/filespagehandler.o \ networking/sdl_net/handlers/indexpagehandler.o \ networking/sdl_net/handlers/resourcehandler.o \ + networking/sdl_net/handlers/uploadfilehandler.o \ networking/sdl_net/localwebserver.o \ networking/sdl_net/reader.o endif diff --git a/backends/networking/sdl_net/client.cpp b/backends/networking/sdl_net/client.cpp index 32aa1e4bef..13543a6c44 100644 --- a/backends/networking/sdl_net/client.cpp +++ b/backends/networking/sdl_net/client.cpp @@ -116,6 +116,8 @@ Common::String Client::query() const { return _reader.query(); } Common::String Client::queryParameter(Common::String name) const { return _reader.queryParameter(name); } +Common::String Client::attachedFile(Common::String name) const { return _reader.attachedFile(name); } + Common::String Client::anchor() const { return _reader.anchor(); } bool Client::socketIsReady() { return SDLNet_SocketReady(_socket); } diff --git a/backends/networking/sdl_net/client.h b/backends/networking/sdl_net/client.h index 9764dacb51..cd10adc780 100644 --- a/backends/networking/sdl_net/client.h +++ b/backends/networking/sdl_net/client.h @@ -72,6 +72,7 @@ public: Common::String path() const; Common::String query() const; Common::String queryParameter(Common::String name) const; + Common::String attachedFile(Common::String name) const; Common::String anchor() const; /** diff --git a/backends/networking/sdl_net/handlers/uploadfilehandler.cpp b/backends/networking/sdl_net/handlers/uploadfilehandler.cpp new file mode 100644 index 0000000000..1eabf1d926 --- /dev/null +++ b/backends/networking/sdl_net/handlers/uploadfilehandler.cpp @@ -0,0 +1,187 @@ +/* ScummVM - Graphic Adventure Engine +* +* ScummVM is the legal property of its developers, whose names +* are too numerous to list here. Please refer to the COPYRIGHT +* file distributed with this source distribution. +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +*/ + +#include "backends/networking/sdl_net/handlers/uploadfilehandler.h" +#include "backends/networking/sdl_net/localwebserver.h" +#include "backends/fs/fs-factory.h" +#include "common/file.h" +#include "common/translation.h" + +namespace Networking { + +#define INDEX_PAGE_NAME ".index.html" + +UploadFileHandler::UploadFileHandler() {} + +UploadFileHandler::~UploadFileHandler() {} + +void UploadFileHandler::handle(Client &client) { + Common::String errorMessage = ""; + + // show an error message if failed to upload the file + if (!uploadFile(client, errorMessage)) { + handleErrorMessage( + client, + Common::String::format( + "%s
%s", + errorMessage.c_str(), + "%2F", //that's encoded "/" + _("Back to the files manager") + ) + ); + return; + } + + Common::String response = "ScummVM{message}"; + + // load stylish response page from the archive + Common::SeekableReadStream *const stream = getArchiveFile(INDEX_PAGE_NAME); + if (stream) response = readEverythingFromStream(stream); + + replace(response, "{message}", Common::String::format( + "%s
%s", + _("Uploaded successfully!"), + client.queryParameter("path").c_str(), + _("Back to parent directory") + ) + ); + LocalWebserver::setClientRedirectHandler( + client, response, + "/files?path=" + LocalWebserver::urlEncodeQueryParameterValue(client.queryParameter("path")) + ); +} + +void UploadFileHandler::handleErrorMessage(Client &client, Common::String message) { + Common::String response = "ScummVM{message}"; + + // load stylish response page from the archive + Common::SeekableReadStream *const stream = getArchiveFile(INDEX_PAGE_NAME); + if (stream) response = readEverythingFromStream(stream); + + replace(response, "{message}", message); + LocalWebserver::setClientGetHandler(client, response); +} + +namespace { +bool copyStream(Common::ReadStream *from, Common::WriteStream *into) { + assert(from); + assert(into); + const uint32 BUFFER_SIZE = 1 * 1024 * 1024; + void *buffer = malloc(BUFFER_SIZE); + bool success = true; + assert(buffer); + + while (!from->eos()) { + uint32 readBytes = from->read(buffer, BUFFER_SIZE); + + if (from->err()) { + warning("copyStream: failed to read bytes from the stream"); + success = false; + break; + } + + if (readBytes != 0) + if (into->write(buffer, readBytes) != readBytes || into->err()) { + warning("copyStream: failed to write all bytes into the file"); + success = false; + break; + } + } + + free(buffer); + return success; +} +} + +bool UploadFileHandler::uploadFile(Client &client, Common::String &errorMessage) { + Common::String path = client.queryParameter("path"); + Common::String originalFilename = client.queryParameter("upload_file"); + Common::String tempFilename = client.attachedFile(originalFilename); + debug("path = <%s>", path.c_str()); + debug("filename = <%s>", originalFilename.c_str()); + debug("tempfile = <%s>", tempFilename.c_str()); + + // check that is not an absolute root + if (path == "" || path == "/" || path == "\\") { + errorMessage = _("Invalid path!"); + return false; + } + + // transform virtual path to actual file system one + Common::String prefixToRemove = "", prefixToAdd = ""; + if (!transformPath(path, prefixToRemove, prefixToAdd, false) || path.empty()) { + errorMessage = _("Invalid path!"); + return false; + } + + // check that exists and is directory + AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(path); + if (!node->exists()) { + errorMessage = _("The parent directory doesn't exist!"); + return false; + } + if (!node->isDirectory()) { + errorMessage = _("Can't upload into a file!"); + return false; + } + + // check that / doesn't exist + if (path.lastChar() != '/' && path.lastChar() != '\\') path += '/'; + AbstractFSNode *originalNode = g_system->getFilesystemFactory()->makeFileNodePath(path + originalFilename); + if (originalNode->exists()) { + errorMessage = _("There is a file with that name in the parent directory!"); + return false; + } + + // check that exists + AbstractFSNode *tempNode = g_system->getFilesystemFactory()->makeFileNodePath(tempFilename); + if (!tempNode->exists() || tempNode->isDirectory()) { + errorMessage = _("Failed to upload the file!"); + return false; + } + + // copy into / + // FIXME: I think we should move/rename file with some system call + // even though that might be less portable, that is much better than + // making an actual copy of data (because user might had enough place + // for one copy of the file, but not for two of them) + Common::ReadStream *tempStream = tempNode->createReadStream(); + Common::WriteStream *fileStream = originalNode->createWriteStream(); + if (tempStream == nullptr || fileStream == nullptr || !copyStream(tempStream, fileStream)) { + delete tempStream; + delete fileStream; + errorMessage = _("Failed to upload the file!"); + return false; + } + + delete tempStream; + delete fileStream; + return true; +} + +/// public + +ClientHandlerCallback UploadFileHandler::getHandler() { + return new Common::Callback(this, &UploadFileHandler::handle); +} + +} // End of namespace Networking diff --git a/backends/networking/sdl_net/handlers/uploadfilehandler.h b/backends/networking/sdl_net/handlers/uploadfilehandler.h new file mode 100644 index 0000000000..6300a13e8d --- /dev/null +++ b/backends/networking/sdl_net/handlers/uploadfilehandler.h @@ -0,0 +1,51 @@ +/* ScummVM - Graphic Adventure Engine +* +* ScummVM is the legal property of its developers, whose names +* are too numerous to list here. Please refer to the COPYRIGHT +* file distributed with this source distribution. +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +*/ + +#ifndef BACKENDS_NETWORKING_SDL_NET_UPLOADFILEHANDLER_H +#define BACKENDS_NETWORKING_SDL_NET_UPLOADFILEHANDLER_H + +#include "backends/networking/sdl_net/handlers/filesbasehandler.h" + +namespace Networking { + +class UploadFileHandler: public FilesBaseHandler { + void handle(Client &client); + void handleErrorMessage(Client &client, Common::String message); + + /** + * Uploads file. + * + * Fills on failure. + * + * Returns true on success. + */ + bool uploadFile(Client &client, Common::String &errorMessage); +public: + UploadFileHandler(); + virtual ~UploadFileHandler(); + + virtual ClientHandlerCallback getHandler(); +}; + +} // End of namespace Networking + +#endif diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp index 8f9c30d069..86a1d1a3f5 100644 --- a/backends/networking/sdl_net/localwebserver.cpp +++ b/backends/networking/sdl_net/localwebserver.cpp @@ -47,6 +47,7 @@ LocalWebserver::LocalWebserver(): _set(nullptr), _serverSocket(nullptr), _timerS addPathHandler("/files", _filesPageHandler.getHandler()); addPathHandler("/create", _createDirectoryHandler.getHandler()); addPathHandler("/download", _downloadFileHandler.getHandler()); + addPathHandler("/upload", _uploadFileHandler.getHandler()); _defaultHandler = _resourceHandler.getHandler(); } diff --git a/backends/networking/sdl_net/localwebserver.h b/backends/networking/sdl_net/localwebserver.h index 24a4ecf6c3..124b1b3d8d 100644 --- a/backends/networking/sdl_net/localwebserver.h +++ b/backends/networking/sdl_net/localwebserver.h @@ -30,6 +30,7 @@ #include "backends/networking/sdl_net/handlers/filespagehandler.h" #include "backends/networking/sdl_net/handlers/indexpagehandler.h" #include "backends/networking/sdl_net/handlers/resourcehandler.h" +#include "backends/networking/sdl_net/handlers/uploadfilehandler.h" #include "common/hash-str.h" #include "common/mutex.h" #include "common/singleton.h" @@ -63,6 +64,7 @@ class LocalWebserver : public Common::Singleton { FilesPageHandler _filesPageHandler; CreateDirectoryHandler _createDirectoryHandler; DownloadFileHandler _downloadFileHandler; + UploadFileHandler _uploadFileHandler; ResourceHandler _resourceHandler; uint32 _idlingFrames; Common::Mutex _handleMutex; diff --git a/backends/networking/sdl_net/reader.cpp b/backends/networking/sdl_net/reader.cpp index 698c20919e..e08d5db566 100644 --- a/backends/networking/sdl_net/reader.cpp +++ b/backends/networking/sdl_net/reader.cpp @@ -51,6 +51,7 @@ Reader::Reader(): _randomSource("Networking::Reader") { _headers = ""; _stream = nullptr; + _firstBlock = true; _contentLength = 0; _availableBytes = 0; @@ -98,6 +99,7 @@ Reader &Reader::operator=(Reader &r) { _headers = r._headers; _stream = r._stream; + _firstBlock = r._firstBlock; r._stream = nullptr; _headers = r._headers; @@ -383,6 +385,7 @@ Common::String generateTempFileName(Common::String originalFilename, Common::Ran bool Reader::readContent() { Common::String boundary = "--" + _boundary; + if (!_firstBlock) boundary = "\r\n" + boundary; if (_window == nullptr) { makeWindow(boundary.size()); @@ -403,6 +406,7 @@ bool Reader::readContent() { if (!bytesLeft()) return false; } + _firstBlock = false; if (_isFileField) { if (_stream != nullptr) { _stream->flush(); @@ -514,6 +518,8 @@ Common::String Reader::query() const { return _query; } Common::String Reader::queryParameter(Common::String name) const { return _queryParameters[name]; } +Common::String Reader::attachedFile(Common::String name) const { return _attachedFiles[name]; } + Common::String Reader::anchor() const { return _anchor; } } // End of namespace Networking diff --git a/backends/networking/sdl_net/reader.h b/backends/networking/sdl_net/reader.h index de0f3b668f..244d619f5f 100644 --- a/backends/networking/sdl_net/reader.h +++ b/backends/networking/sdl_net/reader.h @@ -53,6 +53,7 @@ class Reader { Common::String _headers; Common::WriteStream *_stream; + bool _firstBlock; ///Common::String _headers; Common::String _method, _path, _query, _anchor; @@ -99,6 +100,7 @@ public: Common::String path() const; Common::String query() const; Common::String queryParameter(Common::String name) const; + Common::String attachedFile(Common::String name) const; Common::String anchor() const; }; -- cgit v1.2.3