Something went wrong. Try again.
A fork of https://github.com/crosspoint-reader/crosspoint-reader
Something went wrong. Try again.
5.6 kB · 153 lines
C++
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154#include "KOReaderCredentialStore.h"
#include <Logging.h>#include <MD5Builder.h>#include <ObfuscationUtils.h>
namespace {// Default sync server URL. crosspoint-sync speaks the full KOSync protocol, so// pointing at any other kosync server (e.g. https://sync.koreader.rocks:443)// still works via the custom server URL setting.constexpr char DEFAULT_SERVER_URL[] = "https://sync.crosspointreader.com";
// Default before config version 2. Configs saved without a version stamp and an// empty serverUrl were implicitly syncing here — they get pinned on upgrade.constexpr char LEGACY_DEFAULT_SERVER_URL[] = "https://sync.koreader.rocks:443";
// Bumped when a change to defaults would alter behavior for existing configs.constexpr uint8_t CONFIG_VERSION = 2;} // namespace
void KOReaderCredentialStore::toJson(JsonDocument& doc) const { doc["cfgVersion"] = CONFIG_VERSION; doc["username"] = getUsername(); doc["password_obf"] = obfuscation::obfuscateToBase64(getPassword()); doc["serverUrl"] = getServerUrl(); doc["matchMethod"] = static_cast<uint8_t>(getMatchMethod()); doc["sendMetadata"] = getSendMetadata(); doc["syncBehavior"] = static_cast<uint8_t>(getSyncBehavior());}
bool KOReaderCredentialStore::fromJson(JsonVariantConst doc) { std::string user = doc["username"] | "";
bool needsResave = false; std::string pass = extractPassword(doc, needsResave);
setCredentials(user, pass); setServerUrl(doc["serverUrl"] | "");
// The default server changed in config v2 (sync.koreader.rocks -> crosspoint-sync). // A pre-v2 config with credentials and no explicit URL was actively syncing // against the old default — pin that URL so the upgrade doesn't switch servers // out from under the user. Fresh setups get the new default. const uint8_t cfgVersion = doc["cfgVersion"] | (uint8_t)1; if (cfgVersion < CONFIG_VERSION) { if (getServerUrl().empty() && hasCredentials()) { LOG_DBG("KRS", "Pre-v2 config used the old default server; pinning %s", LEGACY_DEFAULT_SERVER_URL); setServerUrl(LEGACY_DEFAULT_SERVER_URL); } needsResave = true; // stamp cfgVersion so this migration runs once }
uint8_t method = doc["matchMethod"] | (uint8_t)0; if (method <= static_cast<uint8_t>(DocumentMatchMethod::BINARY)) { setMatchMethod(static_cast<DocumentMatchMethod>(method)); } else { LOG_DBG("KRS", "Invalid matchMethod %u in JSON, resetting to FILENAME", method); setMatchMethod(DocumentMatchMethod::FILENAME); } setSendMetadata(doc["sendMetadata"] | false);
const JsonVariantConst behaviorValue = doc["syncBehavior"]; const bool missingBehavior = behaviorValue.isNull(); uint8_t behavior = behaviorValue | static_cast<uint8_t>(KOReaderSyncBehavior::ASK_EVERY_TIME); if (behavior <= static_cast<uint8_t>(KOReaderSyncBehavior::SMART)) { setSyncBehavior(static_cast<KOReaderSyncBehavior>(behavior)); needsResave = needsResave || missingBehavior; } else { LOG_DBG("KRS", "Invalid syncBehavior %u in JSON, resetting to ASK_EVERY_TIME", behavior); setSyncBehavior(KOReaderSyncBehavior::ASK_EVERY_TIME); needsResave = true; }
if (needsResave) { LOG_DBG("KRS", "Resaving KOReader credentials to update format"); requestResave(); }
return true;}
void KOReaderCredentialStore::setCredentials(const std::string& user, const std::string& pass) { username = user; password = pass; LOG_DBG("KRS", "Set credentials for user: %s", user.c_str());}
std::string KOReaderCredentialStore::getMd5Password() const { if (password.empty()) { return ""; }
// Calculate MD5 hash of password using ESP32's MD5Builder MD5Builder md5; md5.begin(); md5.add(password.c_str()); md5.calculate();
return md5.toString().c_str();}
bool KOReaderCredentialStore::hasCredentials() const { return !username.empty() && !password.empty(); }
void KOReaderCredentialStore::clearCredentials() { username.clear(); password.clear(); saveToFile(); LOG_DBG("KRS", "Cleared KOReader credentials");}
void KOReaderCredentialStore::setServerUrl(const std::string& url) { serverUrl = url; LOG_DBG("KRS", "Set server URL: %s", url.empty() ? "(default)" : url.c_str());}
std::string KOReaderCredentialStore::getBaseUrl() const { std::string url; if (serverUrl.empty()) { url = DEFAULT_SERVER_URL; } else if (serverUrl.find("://") == std::string::npos) { // Normalize URL: add http:// if no protocol specified (local servers typically don't have SSL) url = "http://" + serverUrl; } else { url = serverUrl; }
// Strip trailing slashes to avoid double-slash in API paths while (!url.empty() && url.back() == '/') { url.pop_back(); }
return url;}
bool KOReaderCredentialStore::usesCrossPointSyncServer() const { return getBaseUrl() == DEFAULT_SERVER_URL; }
void KOReaderCredentialStore::setMatchMethod(DocumentMatchMethod method) { matchMethod = method; LOG_DBG("KRS", "Set match method: %s", method == DocumentMatchMethod::FILENAME ? "Filename" : "Binary");}
void KOReaderCredentialStore::setSendMetadata(bool enabled) { sendMetadata = enabled; LOG_DBG("KRS", "Set send metadata: %s", enabled ? "true" : "false");}
void KOReaderCredentialStore::setSyncBehavior(KOReaderSyncBehavior behavior) { if (static_cast<uint8_t>(behavior) > static_cast<uint8_t>(KOReaderSyncBehavior::SMART)) { behavior = KOReaderSyncBehavior::ASK_EVERY_TIME; } syncBehavior = behavior; LOG_DBG("KRS", "Set sync behavior: %s", behavior == KOReaderSyncBehavior::SMART ? "Smart" : "Ask");}