scummvm/backends/networking/sdl_net/handlerutils.cpp
2021-12-26 18:48:43 +01:00

204 lines
6.2 KiB
C++

/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "backends/networking/sdl_net/handlerutils.h"
#include "backends/networking/sdl_net/localwebserver.h"
#include "backends/saves/default/default-saves.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()) {
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;
}
Common::String HandlerUtils::normalizePath(const Common::String &path) {
Common::String normalized;
bool slash = false;
for (uint32 i = 0; i < path.size(); ++i) {
char c = path[i];
if (c == '\\' || c == '/') {
slash = true;
continue;
}
if (slash) {
normalized += '/';
slash = false;
}
if ('A' <= c && c <= 'Z') {
normalized += c - 'A' + 'a';
} else {
normalized += c;
}
}
if (slash) normalized += '/';
return normalized;
}
bool HandlerUtils::hasForbiddenCombinations(const Common::String &path) {
return (path.contains("/../") || path.contains("\\..\\") || path.contains("\\../") || path.contains("/..\\"));
}
bool HandlerUtils::isBlacklisted(const Common::String &path) {
const char *blacklist[] = {
"/etc",
"/bin",
"c:/windows" // just saying: I know guys who install windows on another drives
};
// normalize path
Common::String normalized = normalizePath(path);
uint32 size = sizeof(blacklist) / sizeof(const char *);
for (uint32 i = 0; i < size; ++i)
if (normalized.hasPrefix(blacklist[i]))
return true;
return false;
}
bool HandlerUtils::hasPermittedPrefix(const Common::String &path) {
// normalize path
Common::String normalized = normalizePath(path);
// prefix for /root/
Common::String prefix;
if (ConfMan.hasKey("rootpath", "cloud")) {
prefix = normalizePath(ConfMan.get("rootpath", "cloud"));
if (prefix == "/" || normalized.hasPrefix(prefix))
return true;
}
// prefix for /saves/
#ifdef USE_LIBCURL
DefaultSaveFileManager *manager = dynamic_cast<DefaultSaveFileManager *>(g_system->getSavefileManager());
prefix = (manager ? manager->concatWithSavesPath("") : ConfMan.get("savepath"));
#else
prefix = ConfMan.get("savepath");
#endif
return normalized.hasPrefix(normalizePath(prefix))
|| normalizePath(prefix).compareTo(normalized + "/") == 0;
}
bool HandlerUtils::permittedPath(const Common::String path) {
return hasPermittedPrefix(path) && !isBlacklisted(path);
}
void HandlerUtils::setMessageHandler(Client &client, Common::String message, Common::String redirectTo) {
Common::String response = "<html><head><title>ScummVM</title><meta charset=\"utf-8\"/></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%s?path=%s\">%s</a>",
message.c_str(),
client.queryParameter("ajax") == "true" ? "AJAX" : "",
"%2F", //that's encoded "/"
Common::convertFromU32String(_("Back to the files manager")).c_str()
),
redirectTo
);
}
} // End of namespace Networking