Review comments

This commit is contained in:
jpirnay
2026-04-23 17:08:08 +02:00
parent 9621dc1181
commit 6053ef4756
9 changed files with 200 additions and 53 deletions
+82 -17
View File
@@ -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 {