Review comments
This commit is contained in:
+82
-17
@@ -4,16 +4,49 @@
|
||||
#include <JsonSettingsIO.h>
|
||||
#include <Logging.h>
|
||||
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
|
||||
#include "CrossPointSettings.h"
|
||||
#include "util/UrlUtils.h"
|
||||
|
||||
OpdsServerStore OpdsServerStore::instance;
|
||||
|
||||
namespace {
|
||||
constexpr char OPDS_FILE_JSON[] = "/.crosspoint/opds.json";
|
||||
|
||||
bool containsWhitespace(const std::string& value) {
|
||||
for (const unsigned char ch : value) {
|
||||
if (std::isspace(ch)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace OpdsServerValidation {
|
||||
|
||||
std::optional<std::string> normalizeUrl(const std::string& url) {
|
||||
if (url.empty() || containsWhitespace(url)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::string normalized = url;
|
||||
if (normalized.find("://") == std::string::npos) {
|
||||
normalized = "https://" + normalized;
|
||||
}
|
||||
|
||||
const bool hasHttpScheme = normalized.rfind("http://", 0) == 0 || normalized.rfind("https://", 0) == 0;
|
||||
if (!hasHttpScheme || UrlUtils::extractHostname(normalized).empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
} // namespace OpdsServerValidation
|
||||
|
||||
bool OpdsServerStore::saveToFile() const {
|
||||
Storage.mkdir("/.crosspoint");
|
||||
return JsonSettingsIO::saveOpds(*this, OPDS_FILE_JSON);
|
||||
@@ -22,16 +55,25 @@ bool OpdsServerStore::saveToFile() const {
|
||||
bool OpdsServerStore::loadFromFile() {
|
||||
if (Storage.exists(OPDS_FILE_JSON)) {
|
||||
String json = Storage.readFile(OPDS_FILE_JSON);
|
||||
if (!json.isEmpty()) {
|
||||
// resave flag is set when passwords were stored in plaintext and need re-obfuscation
|
||||
bool resave = false;
|
||||
bool result = JsonSettingsIO::loadOpds(*this, json.c_str(), &resave);
|
||||
if (result && resave) {
|
||||
LOG_DBG("OPS", "Resaving JSON with obfuscated passwords");
|
||||
saveToFile();
|
||||
}
|
||||
return result;
|
||||
if (json.isEmpty()) {
|
||||
LOG_ERR("OPS", "Failed to parse %s", OPDS_FILE_JSON);
|
||||
return false;
|
||||
}
|
||||
|
||||
// resave flag is set when passwords were stored in plaintext and need re-obfuscation
|
||||
bool resave = false;
|
||||
bool result = JsonSettingsIO::loadOpds(*this, json.c_str(), &resave);
|
||||
if (!result) {
|
||||
LOG_ERR("OPS", "Failed to parse %s", OPDS_FILE_JSON);
|
||||
return false;
|
||||
}
|
||||
if (resave) {
|
||||
LOG_DBG("OPS", "Resaving JSON with obfuscated passwords");
|
||||
if (!saveToFile()) {
|
||||
LOG_ERR("OPS", "Failed to resave %s after password migration", OPDS_FILE_JSON);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// No opds.json found — attempt one-time migration from the legacy single-server
|
||||
@@ -71,15 +113,23 @@ bool OpdsServerStore::migrateFromSettings() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OpdsServerStore::addServer(const OpdsServer& server) {
|
||||
std::optional<size_t> OpdsServerStore::addServer(const OpdsServer& server) {
|
||||
if (servers.size() >= MAX_SERVERS) {
|
||||
LOG_DBG("OPS", "Cannot add more servers, limit of %zu reached", MAX_SERVERS);
|
||||
return false;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto originalServers = servers;
|
||||
servers.push_back(server);
|
||||
LOG_DBG("OPS", "Added server: %s", server.name.c_str());
|
||||
return saveToFile();
|
||||
if (!saveToFile()) {
|
||||
servers = originalServers;
|
||||
LOG_ERR("OPS", "Failed to persist added server, rolled back in-memory state");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const size_t insertedIndex = servers.size() - 1;
|
||||
LOG_DBG("OPS", "Added server at index %zu: %s", insertedIndex, server.name.c_str());
|
||||
return insertedIndex;
|
||||
}
|
||||
|
||||
bool OpdsServerStore::updateServer(size_t index, const OpdsServer& server) {
|
||||
@@ -87,9 +137,16 @@ bool OpdsServerStore::updateServer(size_t index, const OpdsServer& server) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto originalServers = servers;
|
||||
servers[index] = server;
|
||||
LOG_DBG("OPS", "Updated server: %s", server.name.c_str());
|
||||
return saveToFile();
|
||||
if (!saveToFile()) {
|
||||
servers = originalServers;
|
||||
LOG_ERR("OPS", "Failed to persist updated server at index %zu, rolled back in-memory state", index);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_DBG("OPS", "Updated server at index %zu: %s", index, server.name.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpdsServerStore::removeServer(size_t index) {
|
||||
@@ -97,9 +154,17 @@ bool OpdsServerStore::removeServer(size_t index) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_DBG("OPS", "Removed server: %s", servers[index].name.c_str());
|
||||
const auto originalServers = servers;
|
||||
const std::string removedName = servers[index].name;
|
||||
servers.erase(servers.begin() + static_cast<ptrdiff_t>(index));
|
||||
return saveToFile();
|
||||
if (!saveToFile()) {
|
||||
servers = originalServers;
|
||||
LOG_ERR("OPS", "Failed to persist removed server at index %zu, rolled back in-memory state", index);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_DBG("OPS", "Removed server at index %zu: %s", index, removedName.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
const OpdsServer* OpdsServerStore::getServer(size_t index) const {
|
||||
|
||||
Reference in New Issue
Block a user