aboutsummaryrefslogtreecommitdiff
path: root/backends/networking/sdl_net
diff options
context:
space:
mode:
Diffstat (limited to 'backends/networking/sdl_net')
-rw-r--r--backends/networking/sdl_net/client.cpp64
-rw-r--r--backends/networking/sdl_net/client.h13
-rw-r--r--backends/networking/sdl_net/handlers/uploadfilehandler.cpp138
-rw-r--r--backends/networking/sdl_net/handlers/uploadfilehandler.h11
-rw-r--r--backends/networking/sdl_net/handlerutils.cpp121
-rw-r--r--backends/networking/sdl_net/handlerutils.h44
-rw-r--r--backends/networking/sdl_net/reader.cpp115
-rw-r--r--backends/networking/sdl_net/reader.h17
-rw-r--r--backends/networking/sdl_net/uploadfileclienthandler.cpp176
-rw-r--r--backends/networking/sdl_net/uploadfileclienthandler.h57
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