#include "KOReaderCredentialStore.h" #include #include #include namespace { // Default sync server URL constexpr char DEFAULT_SERVER_URL[] = "https://sync.koreader.rocks:443"; } // namespace void KOReaderCredentialStore::toJson(JsonDocument& doc) const { doc["username"] = getUsername(); doc["password_obf"] = obfuscation::obfuscateToBase64(getPassword()); doc["serverUrl"] = getServerUrl(); doc["matchMethod"] = static_cast(getMatchMethod()); doc["sendMetadata"] = getSendMetadata(); } 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"] | ""); uint8_t method = doc["matchMethod"] | (uint8_t)0; if (method <= static_cast(DocumentMatchMethod::BINARY)) { setMatchMethod(static_cast(method)); } else { LOG_DBG("KRS", "Invalid matchMethod %u in JSON, resetting to FILENAME", method); setMatchMethod(DocumentMatchMethod::FILENAME); } setSendMetadata(doc["sendMetadata"] | false); if (needsResave) { LOG_DBG("KRS", "Resaved KOReader credentials to update format"); saveToFile(); } 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; } 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"); }