diff options
Diffstat (limited to 'backends/networking/sdl_net')
-rw-r--r-- | backends/networking/sdl_net/client.cpp | 64 | ||||
-rw-r--r-- | backends/networking/sdl_net/client.h | 13 | ||||
-rw-r--r-- | backends/networking/sdl_net/handlers/uploadfilehandler.cpp | 138 | ||||
-rw-r--r-- | backends/networking/sdl_net/handlers/uploadfilehandler.h | 11 | ||||
-rw-r--r-- | backends/networking/sdl_net/handlerutils.cpp | 121 | ||||
-rw-r--r-- | backends/networking/sdl_net/handlerutils.h | 44 | ||||
-rw-r--r-- | backends/networking/sdl_net/reader.cpp | 115 | ||||
-rw-r--r-- | backends/networking/sdl_net/reader.h | 17 | ||||
-rw-r--r-- | backends/networking/sdl_net/uploadfileclienthandler.cpp | 176 | ||||
-rw-r--r-- | backends/networking/sdl_net/uploadfileclienthandler.h | 57 |
10 files changed, 594 insertions, 162 deletions
diff --git a/backends/networking/sdl_net/client.cpp b/backends/networking/sdl_net/client.cpp index 13543a6c44..89b50faf86 100644 --- a/backends/networking/sdl_net/client.cpp +++ b/backends/networking/sdl_net/client.cpp @@ -26,12 +26,13 @@ #include "backends/networking/sdl_net/localwebserver.h" #include "common/textconsole.h" #include <SDL/SDL_net.h> +#include <common/memstream.h> namespace Networking { -Client::Client() : _state(INVALID), _set(nullptr), _socket(nullptr), _handler(nullptr) {} +Client::Client() : _state(INVALID), _set(nullptr), _socket(nullptr), _handler(nullptr), _previousHandler(nullptr), _stream(nullptr) {} -Client::Client(SDLNet_SocketSet set, TCPsocket socket) : _state(INVALID), _set(nullptr), _socket(nullptr), _handler(nullptr) { +Client::Client(SDLNet_SocketSet set, TCPsocket socket) : _state(INVALID), _set(nullptr), _socket(nullptr), _handler(nullptr), _previousHandler(nullptr), _stream(nullptr) { open(set, socket); } @@ -46,7 +47,11 @@ void Client::open(SDLNet_SocketSet set, TCPsocket socket) { _set = set; Reader cleanReader; _reader = cleanReader; + if (_handler) delete _handler; _handler = nullptr; + if (_previousHandler) delete _previousHandler; + _previousHandler = nullptr; + _stream = new Common::MemoryReadWriteStream(DisposeAfterUse::YES); if (set) { int numused = SDLNet_TCP_AddSocket(set, socket); if (numused == -1) { @@ -55,9 +60,11 @@ void Client::open(SDLNet_SocketSet set, TCPsocket socket) { } } -void Client::readHeaders() { - if (!_socket) return; - if (!SDLNet_SocketReady(_socket)) return; +bool Client::readMoreIfNeeded() { + if (_stream == nullptr) return false; //nothing to read into + if (_stream->size() - _stream->pos() > 0) return true; //not needed, some data left in the stream + if (!_socket) return false; + if (!SDLNet_SocketReady(_socket)) return false; const uint32 BUFFER_SIZE = 16 * 1024; byte buffer[BUFFER_SIZE]; @@ -65,16 +72,48 @@ void Client::readHeaders() { if (bytes <= 0) { warning("Client::readHeaders recv fail"); close(); - return; + return false; + } + + if (_stream->write(buffer, bytes) != bytes) { + warning("failed to write() into MemoryReadWriteStream"); + close(); + return false; } - - _reader.setContent(buffer, bytes); - if (_reader.readRequest()) + + return true; +} + +void Client::readHeaders() { + if (!readMoreIfNeeded()) return; + _reader.setContent(_stream); + if (_reader.readFirstHeaders()) _state = (_reader.badRequest() ? BAD_REQUEST : READ_HEADERS); } +bool Client::readContent(Common::WriteStream *stream) { + if (!readMoreIfNeeded()) return false; + _reader.setContent(_stream); + return _reader.readFirstContent(stream); +} + +bool Client::readBlockHeaders(Common::WriteStream *stream) { + if (!readMoreIfNeeded()) return false; + _reader.setContent(_stream); + return _reader.readBlockHeaders(stream); +} + +bool Client::readBlockContent(Common::WriteStream *stream) { + if (!readMoreIfNeeded()) return false; + _reader.setContent(_stream); + return _reader.readBlockContent(stream); +} + void Client::setHandler(ClientHandler *handler) { - if (_handler) delete _handler; + if (_handler) { + if (_previousHandler) delete _previousHandler; + _previousHandler = _handler; //can't just delete it, as setHandler() could've been called by handler itself + } _state = BEING_HANDLED; _handler = handler; } @@ -100,6 +139,11 @@ void Client::close() { _socket = nullptr; } + if (_stream) { + delete _stream; + _stream = nullptr; + } + _state = INVALID; } diff --git a/backends/networking/sdl_net/client.h b/backends/networking/sdl_net/client.h index cd10adc780..6a0dea32fe 100644 --- a/backends/networking/sdl_net/client.h +++ b/backends/networking/sdl_net/client.h @@ -27,6 +27,10 @@ #include "common/str.h" #include "reader.h" +namespace Common { +class MemoryReadWriteStream; +} + typedef struct _SDLNet_SocketSet *SDLNet_SocketSet; typedef struct _TCPsocket *TCPsocket; @@ -53,7 +57,10 @@ class Client { SDLNet_SocketSet _set; TCPsocket _socket; Reader _reader; - ClientHandler *_handler; + ClientHandler *_handler, *_previousHandler; + Common::MemoryReadWriteStream *_stream; + + bool readMoreIfNeeded(); public: Client(); @@ -62,7 +69,11 @@ public: void open(SDLNet_SocketSet set, TCPsocket socket); void readHeaders(); + bool readContent(Common::WriteStream *stream); + bool readBlockHeaders(Common::WriteStream *stream); + bool readBlockContent(Common::WriteStream *stream); void setHandler(ClientHandler *handler); + void dropHandler(); void handle(); void close(); diff --git a/backends/networking/sdl_net/handlers/uploadfilehandler.cpp b/backends/networking/sdl_net/handlers/uploadfilehandler.cpp index 1eabf1d926..eeb4fad1ac 100644 --- a/backends/networking/sdl_net/handlers/uploadfilehandler.cpp +++ b/backends/networking/sdl_net/handlers/uploadfilehandler.cpp @@ -21,161 +21,49 @@ */ #include "backends/networking/sdl_net/handlers/uploadfilehandler.h" +#include "backends/networking/sdl_net/handlerutils.h" #include "backends/networking/sdl_net/localwebserver.h" +#include "backends/networking/sdl_net/uploadfileclienthandler.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<br/><a href=\"files?path=%s\">%s</a>", - errorMessage.c_str(), - "%2F", //that's encoded "/" - _("Back to the files manager") - ) - ); - return; - } - - Common::String response = "<html><head><title>ScummVM</title></head><body>{message}</body></html>"; - - // 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<br/><a href=\"files?path=%s\">%s</a>", - _("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 = "<html><head><title>ScummVM</title></head><body>{message}</body></html>"; - - // 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 <path> is not an absolute root if (path == "" || path == "/" || path == "\\") { - errorMessage = _("Invalid path!"); - return false; + HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!")); + return; } // 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; + HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!")); + return; } // check that <path> exists and is directory AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(path); if (!node->exists()) { - errorMessage = _("The parent directory doesn't exist!"); - return false; + HandlerUtils::setFilesManagerErrorMessageHandler(client, _("The parent directory doesn't exist!")); + return; } if (!node->isDirectory()) { - errorMessage = _("Can't upload into a file!"); - return false; - } - - // check that <path>/<originalFilename> 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 <tempFilename> exists - AbstractFSNode *tempNode = g_system->getFilesystemFactory()->makeFileNodePath(tempFilename); - if (!tempNode->exists() || tempNode->isDirectory()) { - errorMessage = _("Failed to upload the file!"); - return false; - } - - // copy <tempFilename> into <path>/<originalFilename> - // 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; + HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Can't upload into a file!")); + return; } - - delete tempStream; - delete fileStream; - return true; + + // if all OK, set special handler + client.setHandler(new UploadFileClientHandler(path)); } /// public diff --git a/backends/networking/sdl_net/handlers/uploadfilehandler.h b/backends/networking/sdl_net/handlers/uploadfilehandler.h index 6300a13e8d..bba7fdfc02 100644 --- a/backends/networking/sdl_net/handlers/uploadfilehandler.h +++ b/backends/networking/sdl_net/handlers/uploadfilehandler.h @@ -29,16 +29,7 @@ namespace Networking { class UploadFileHandler: public FilesBaseHandler { void handle(Client &client); - void handleErrorMessage(Client &client, Common::String message); - - /** - * Uploads file. - * - * Fills <errorMessage> on failure. - * - * Returns true on success. - */ - bool uploadFile(Client &client, Common::String &errorMessage); + public: UploadFileHandler(); virtual ~UploadFileHandler(); diff --git a/backends/networking/sdl_net/handlerutils.cpp b/backends/networking/sdl_net/handlerutils.cpp new file mode 100644 index 0000000000..f249ee1e29 --- /dev/null +++ b/backends/networking/sdl_net/handlerutils.cpp @@ -0,0 +1,121 @@ +/* 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/handlerutils.h" +#include "backends/networking/sdl_net/localwebserver.h" +#include "common/archive.h" +#include "common/config-manager.h" +#include "common/file.h" +#include "common/translation.h" +#include "common/unzip.h" + +namespace Networking { + +#define ARCHIVE_NAME "wwwroot.zip" + +#define INDEX_PAGE_NAME ".index.html" + +Common::Archive *HandlerUtils::getZipArchive() { + // first search in themepath + if (ConfMan.hasKey("themepath")) { + const Common::FSNode &node = Common::FSNode(ConfMan.get("themepath")); + if (!node.exists() || !node.isReadable() || !node.isDirectory()) + return nullptr; + + Common::FSNode fileNode = node.getChild(ARCHIVE_NAME); + if (fileNode.exists() && fileNode.isReadable() && !fileNode.isDirectory()) { + Common::SeekableReadStream *const stream = fileNode.createReadStream(); + Common::Archive *zipArchive = Common::makeZipArchive(stream); + if (zipArchive) return zipArchive; + } + } + + // then use SearchMan to find it + Common::ArchiveMemberList fileList; + SearchMan.listMatchingMembers(fileList, ARCHIVE_NAME); + for (Common::ArchiveMemberList::iterator it = fileList.begin(); it != fileList.end(); ++it) { + Common::ArchiveMember const &m = **it; + Common::SeekableReadStream *const stream = m.createReadStream(); + Common::Archive *zipArchive = Common::makeZipArchive(stream); + if (zipArchive) return zipArchive; + } + + return nullptr; +} + +Common::ArchiveMemberList HandlerUtils::listArchive() { + Common::ArchiveMemberList resultList; + Common::Archive *zipArchive = getZipArchive(); + if (zipArchive) { + zipArchive->listMembers(resultList); + delete zipArchive; + } + return resultList; +} + +Common::SeekableReadStream *HandlerUtils::getArchiveFile(Common::String name) { + Common::SeekableReadStream *result = nullptr; + Common::Archive *zipArchive = getZipArchive(); + if (zipArchive) { + const Common::ArchiveMemberPtr ptr = zipArchive->getMember(name); + if (ptr.get() == nullptr) return nullptr; + result = ptr->createReadStream(); + delete zipArchive; + } + return result; +} + +Common::String HandlerUtils::readEverythingFromStream(Common::SeekableReadStream *const stream) { + Common::String result; + char buf[1024]; + uint32 readBytes; + while (!stream->eos()) { + readBytes = stream->read(buf, 1024); + result += Common::String(buf, readBytes); + } + return result; +} + +void HandlerUtils::setMessageHandler(Client &client, Common::String message, Common::String redirectTo) { + Common::String response = "<html><head><title>ScummVM</title></head><body>{message}</body></html>"; + + // load stylish response page from the archive + Common::SeekableReadStream *const stream = getArchiveFile(INDEX_PAGE_NAME); + if (stream) response = readEverythingFromStream(stream); + + replace(response, "{message}", message); + if (redirectTo.empty()) LocalWebserver::setClientGetHandler(client, response); + else LocalWebserver::setClientRedirectHandler(client, response, redirectTo); +} + +void HandlerUtils::setFilesManagerErrorMessageHandler(Client &client, Common::String message, Common::String redirectTo) { + setMessageHandler(client, + Common::String::format( + "%s<br/><a href=\"files?path=%s\">%s</a>", + message.c_str(), + "%2F", //that's encoded "/" + _("Back to the files manager") + ), redirectTo + ); +} + +} // End of namespace Networking diff --git a/backends/networking/sdl_net/handlerutils.h b/backends/networking/sdl_net/handlerutils.h new file mode 100644 index 0000000000..c6722688e8 --- /dev/null +++ b/backends/networking/sdl_net/handlerutils.h @@ -0,0 +1,44 @@ +/* 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_HANDLERUTILS_H +#define BACKENDS_NETWORKING_SDL_NET_HANDLERUTILS_H + +#include "backends/networking/sdl_net/client.h" +#include "common/archive.h" + +namespace Networking { + +class HandlerUtils { +public: + static Common::Archive *getZipArchive(); + static Common::ArchiveMemberList listArchive(); + static Common::SeekableReadStream *getArchiveFile(Common::String name); + static Common::String readEverythingFromStream(Common::SeekableReadStream *const stream); + + static void setMessageHandler(Client &client, Common::String message, Common::String redirectTo = ""); + static void setFilesManagerErrorMessageHandler(Client &client, Common::String message, Common::String redirectTo = ""); +}; + +} // End of namespace Networking + +#endif diff --git a/backends/networking/sdl_net/reader.cpp b/backends/networking/sdl_net/reader.cpp index e08d5db566..d61eff2e68 100644 --- a/backends/networking/sdl_net/reader.cpp +++ b/backends/networking/sdl_net/reader.cpp @@ -135,14 +135,14 @@ void Reader::cleanup() { } } -bool Reader::readRequest() { +bool Reader::readWholeRequest() { if (_state == RS_NONE) _state = RS_READING_HEADERS; while (true) { if (!bytesLeft()) return false; if (_state == RS_READING_HEADERS) - if (!readHeaders()) + if (!readWholeHeaders()) return false; if (_boundary.empty()) return true; //not POST multipart @@ -150,7 +150,7 @@ bool Reader::readRequest() { if (!bytesLeft()) return false; if (_state == RS_READING_CONTENT) - if (!readContent()) + if (!readWholeContent()) return false; if (_availableBytes >= 2) { @@ -171,7 +171,69 @@ bool Reader::readRequest() { return true; } -bool Reader::readHeaders() { +bool Reader::readFirstHeaders() { + if (_state == RS_NONE) _state = RS_READING_HEADERS; + + if (!bytesLeft()) return false; + + if (_state == RS_READING_HEADERS) + return readWholeHeaders(); + + warning("Reader::readFirstHeaders(): bad state"); + return false; +} + +bool Reader::readFirstContent(Common::WriteStream *stream) { + if (_state != RS_READING_CONTENT) { + warning("Reader::readFirstContent(): bad state"); + return false; + } + + if (!bytesLeft()) return false; + + return readWholeContentIntoStream(stream); +} + +bool Reader::readBlockHeaders(Common::WriteStream *stream) { + if (_state != RS_READING_HEADERS) { + warning("Reader::readBlockHeaders(): bad state"); + return false; + } + + if (!bytesLeft()) return false; + + return readWholeHeadersIntoStream(stream); +} + +bool Reader::readBlockContent(Common::WriteStream *stream) { + if (_state != RS_READING_CONTENT) { + warning("Reader::readBlockContent(): bad state"); + return false; + } + + if (!bytesLeft()) return false; + + if (!readWholeContentIntoStream(stream)) + return false; + + /* + if (_availableBytes >= 2) { + Common::String bts; + bts += readOne(); + bts += readOne(); + if (bts == "--") _isOver = true; + else if (bts != "\r\n") + warning("strange bytes: \"%s\"", bts.c_str()); + } else { + warning("strange ending"); + _isOver = true; + } + */ + + return true; +} + +bool Reader::readWholeHeaders() { Common::String boundary = "\r\n\r\n"; if (_window == nullptr) { makeWindow(boundary.size()); @@ -188,6 +250,20 @@ bool Reader::readHeaders() { return true; } +bool Reader::readWholeHeadersIntoStream(Common::WriteStream *stream) { + Common::String boundary = "\r\n\r\n"; + if (_window == nullptr) makeWindow(boundary.size()); + + while (readOneByteInStream(stream, boundary)) { + if (!bytesLeft()) return false; + } + if (stream) stream->flush(); + + freeWindow(); + _state = RS_READING_CONTENT; + return true; +} + namespace { void readFromThatUntilLineEnd(const char *cstr, Common::String needle, Common::String &result) { const char *position = strstr(cstr, needle.c_str()); @@ -383,7 +459,7 @@ Common::String generateTempFileName(Common::String originalFilename, Common::Ran } } -bool Reader::readContent() { +bool Reader::readWholeContent() { Common::String boundary = "--" + _boundary; if (!_firstBlock) boundary = "\r\n" + boundary; if (_window == nullptr) { @@ -435,6 +511,23 @@ bool Reader::readContent() { return true; } +bool Reader::readWholeContentIntoStream(Common::WriteStream *stream) { + Common::String boundary = "--" + _boundary; + if (!_firstBlock) boundary = "\r\n" + boundary; + if (_window == nullptr) makeWindow(boundary.size()); + + while (readOneByteInStream(stream, boundary)) { + if (!bytesLeft()) return false; + } + + _firstBlock = false; + if (stream) stream->flush(); + + freeWindow(); + _state = RS_READING_HEADERS; + return true; +} + void Reader::handleFileContent(Common::String filename) { debug("\nHANDLE FILE CONTENT:\nFILE >>%s<< SAVED INTO >>%s<<", _currentFileName.c_str(), filename.c_str()); _attachedFiles[_currentFileName] = filename; @@ -469,7 +562,7 @@ bool Reader::readOneByteInStream(Common::WriteStream *stream, const Common::Stri return false; //if not, add the first byte of the window to the string - stream->writeByte(_window[0]); + if (stream) stream->writeByte(_window[0]); for (uint32 i = 1; i < _windowSize; ++i) _window[i - 1] = _window[i]; --_windowUsed; @@ -494,8 +587,8 @@ bool Reader::readOneByteInString(Common::String &buffer, const Common::String &b } byte Reader::readOne() { - byte b = _content[0]; - ++_content; + byte b; + _content->read(&b, 1); --_availableBytes; --_bytesLeft; return b; @@ -503,9 +596,9 @@ byte Reader::readOne() { uint32 Reader::bytesLeft() { return _bytesLeft; } -void Reader::setContent(byte *buffer, uint32 size) { - _content = buffer; - _bytesLeft = size; +void Reader::setContent(Common::MemoryReadWriteStream *stream) { + _content = stream; + _bytesLeft = stream->size() - stream->pos(); } bool Reader::badRequest() { return _isBadRequest; } diff --git a/backends/networking/sdl_net/reader.h b/backends/networking/sdl_net/reader.h index 244d619f5f..5f951a39b1 100644 --- a/backends/networking/sdl_net/reader.h +++ b/backends/networking/sdl_net/reader.h @@ -30,6 +30,7 @@ #include "common/random.h" namespace Common { +class MemoryReadWriteStream; class WriteStream; } @@ -45,7 +46,7 @@ class Reader { Common::RandomSource _randomSource; //for temp file names ReaderState _state; - byte *_content; + Common::MemoryReadWriteStream *_content; uint32 _bytesLeft; byte *_window; @@ -68,8 +69,10 @@ class Reader { void cleanup(); - bool readHeaders(); //true when ended reading - bool readContent(); //true when ended reading + bool readWholeHeaders(); //true when ended reading + bool readWholeContent(); //true when ended reading + bool readWholeHeadersIntoStream(Common::WriteStream *stream); //true when ended reading + bool readWholeContentIntoStream(Common::WriteStream *stream); //true when ended reading void handleHeaders(Common::String headers); void handleFileContent(Common::String filename); void handleValueContent(Common::String value); @@ -92,8 +95,12 @@ public: Reader &operator=(Reader &r); - bool readRequest(); //true when ended reading - void setContent(byte *buffer, uint32 size); + bool readWholeRequest(); //true when ended reading + bool readFirstHeaders(); //true when ended reading + bool readFirstContent(Common::WriteStream *stream); //true when ended reading + bool readBlockHeaders(Common::WriteStream *stream); //true when ended reading + bool readBlockContent(Common::WriteStream *stream); //true when ended reading + void setContent(Common::MemoryReadWriteStream *stream); bool badRequest(); Common::String method() const; diff --git a/backends/networking/sdl_net/uploadfileclienthandler.cpp b/backends/networking/sdl_net/uploadfileclienthandler.cpp new file mode 100644 index 0000000000..ae795da25c --- /dev/null +++ b/backends/networking/sdl_net/uploadfileclienthandler.cpp @@ -0,0 +1,176 @@ +/* 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/uploadfileclienthandler.h" +#include "common/textconsole.h" +#include <common/memstream.h> +#include <common/translation.h> +#include <common/system.h> +#include <backends/fs/fs-factory.h> +#include <common/debug.h> +#include "handlerutils.h" +#include "localwebserver.h" + +namespace Networking { + +UploadFileClientHandler::UploadFileClientHandler(Common::String parentDirectoryPath): + _state(UFH_READING_CONTENT), _headersStream(nullptr), _contentStream(nullptr), + _parentDirectoryPath(parentDirectoryPath) {} + +UploadFileClientHandler::~UploadFileClientHandler() { + delete _headersStream; + delete _contentStream; +} + +namespace { +void readFromThatUntilLineEnd(const char *cstr, Common::String needle, Common::String &result) { + const char *position = strstr(cstr, needle.c_str()); + + if (position) { + char c; + for (const char *i = position + needle.size(); c = *i, c != 0; ++i) { + if (c == '\n' || c == '\r') break; + result += c; + } + } +} + +void readFromThatUntilDoubleQuote(const char *cstr, Common::String needle, Common::String &result) { + const char *position = strstr(cstr, needle.c_str()); + + if (position) { + char c; + for (const char *i = position + needle.size(); c = *i, c != 0; ++i) { + if (c == '"') break; + result += c; + } + } +} + +Common::String readEverythingFromMemoryStream(Common::MemoryReadWriteStream *stream) { + Common::String result; + char buf[1024]; + uint32 readBytes; + while (true) { + readBytes = stream->read(buf, 1024); + if (readBytes == 0) break; + result += Common::String(buf, readBytes); + } + return result; +} +} + +void UploadFileClientHandler::handleBlockHeaders(Client *client) { + // search for "upload_file" field + Common::String headers = readEverythingFromMemoryStream(_headersStream); + Common::String fieldName = ""; + readFromThatUntilDoubleQuote(headers.c_str(), "name=\"", fieldName); + if (fieldName != "upload_file") return; + + Common::String filename = ""; + readFromThatUntilDoubleQuote(headers.c_str(), "filename=\"", filename); + + // check that <filename> is not empty + if (filename.empty()) { + setErrorMessageHandler(*client, _("Invalid filename!")); + return; + } + + // check that <path>/<filename> doesn't exist + Common::String path = _parentDirectoryPath; + if (path.lastChar() != '/' && path.lastChar() != '\\') path += '/'; + AbstractFSNode *originalNode = g_system->getFilesystemFactory()->makeFileNodePath(path + filename); + if (originalNode->exists()) { + setErrorMessageHandler(*client, _("There is a file with that name in the parent directory!")); + return; + } + + // create file stream + _contentStream = originalNode->createWriteStream(); + if (_contentStream == nullptr) { + setErrorMessageHandler(*client, _("Failed to upload the file!")); + return; + } +} + +void UploadFileClientHandler::handle(Client *client) { + if (client == nullptr) { + warning("UploadFileClientHandler::handle(): empty client pointer"); + return; + } + + while (true) { + switch (_state) { + case UFH_READING_CONTENT: + if (client->readContent(nullptr)) { + _state = UFH_READING_BLOCK_HEADERS; + continue; + } + break; + + case UFH_READING_BLOCK_HEADERS: + if (_headersStream == nullptr) + _headersStream = new Common::MemoryReadWriteStream(DisposeAfterUse::YES); + + if (client->readBlockHeaders(_headersStream)) { + handleBlockHeaders(client); + _state = UFH_READING_BLOCK_CONTENT; + continue; + } + break; + + case UFH_READING_BLOCK_CONTENT: + if (client->readBlockContent(_contentStream)) { + if (_contentStream) { + _contentStream->flush(); + // success - redirect back to directory listing + HandlerUtils::setMessageHandler(*client, + Common::String::format( + "%s<br/><a href=\"files?path=%s\">%s</a>", + _("Uploaded successfully!"), + client->queryParameter("path").c_str(), + _("Back to parent directory") + ), + "/files?path=" + LocalWebserver::urlEncodeQueryParameterValue(client->queryParameter("path")) + ); + return; + } + _state = UFH_READING_BLOCK_HEADERS; + continue; + } + break; + + case UFH_ERROR: + return; + } + + break; + } +} + + +void UploadFileClientHandler::setErrorMessageHandler(Client &client, Common::String message) { + HandlerUtils::setFilesManagerErrorMessageHandler(client, message); + _state = UFH_ERROR; +} + +} // End of namespace Networking diff --git a/backends/networking/sdl_net/uploadfileclienthandler.h b/backends/networking/sdl_net/uploadfileclienthandler.h new file mode 100644 index 0000000000..4f2ce621fb --- /dev/null +++ b/backends/networking/sdl_net/uploadfileclienthandler.h @@ -0,0 +1,57 @@ +/* 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_UPLOADFILECLIENTHANDLER_H +#define BACKENDS_NETWORKING_SDL_NET_UPLOADFILECLIENTHANDLER_H + +#include "backends/networking/sdl_net/client.h" +#include "common/stream.h" + +namespace Networking { + +enum UploadFileHandlerState { + UFH_READING_CONTENT, + UFH_READING_BLOCK_HEADERS, + UFH_READING_BLOCK_CONTENT, + UFH_ERROR +}; + +class UploadFileClientHandler: public ClientHandler { + UploadFileHandlerState _state; + Common::MemoryReadWriteStream *_headersStream; + Common::WriteStream *_contentStream; + Common::String _parentDirectoryPath; + + void handleBlockHeaders(Client *client); + bool validFilename(Client &client, Common::String filename, Common::String &errorMessage); + void setErrorMessageHandler(Client &client, Common::String message); + +public: + UploadFileClientHandler(Common::String parentDirectoryPath); + virtual ~UploadFileClientHandler(); + + virtual void handle(Client *client); +}; + +} // End of namespace Networking + +#endif |