mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-07 08:07:40 +03:00
Added support for authentication in Calibre
This commit is contained in:
parent
79dc134b78
commit
da3abbacd5
@ -12,9 +12,9 @@
|
|||||||
CrossPointSettings CrossPointSettings::instance;
|
CrossPointSettings CrossPointSettings::instance;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr uint8_t SETTINGS_FILE_VERSION = 1;
|
constexpr uint8_t SETTINGS_FILE_VERSION = 2;
|
||||||
// Increment this when adding new persisted settings fields
|
// Increment this when adding new persisted settings fields
|
||||||
constexpr uint8_t SETTINGS_COUNT = 17;
|
constexpr uint8_t SETTINGS_COUNT = 19;
|
||||||
constexpr char SETTINGS_FILE[] = "/.crosspoint/settings.bin";
|
constexpr char SETTINGS_FILE[] = "/.crosspoint/settings.bin";
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@ -47,6 +47,8 @@ bool CrossPointSettings::saveToFile() const {
|
|||||||
serialization::writeString(outputFile, std::string(opdsServerUrl));
|
serialization::writeString(outputFile, std::string(opdsServerUrl));
|
||||||
serialization::writePod(outputFile, textAntiAliasing);
|
serialization::writePod(outputFile, textAntiAliasing);
|
||||||
serialization::writePod(outputFile, hideBatteryPercentage);
|
serialization::writePod(outputFile, hideBatteryPercentage);
|
||||||
|
serialization::writeString(outputFile, std::string(calibreUsername));
|
||||||
|
serialization::writeString(outputFile, std::string(calibrePassword));
|
||||||
outputFile.close();
|
outputFile.close();
|
||||||
|
|
||||||
Serial.printf("[%lu] [CPS] Settings saved to file\n", millis());
|
Serial.printf("[%lu] [CPS] Settings saved to file\n", millis());
|
||||||
@ -113,6 +115,20 @@ bool CrossPointSettings::loadFromFile() {
|
|||||||
if (++settingsRead >= fileSettingsCount) break;
|
if (++settingsRead >= fileSettingsCount) break;
|
||||||
serialization::readPod(inputFile, hideBatteryPercentage);
|
serialization::readPod(inputFile, hideBatteryPercentage);
|
||||||
if (++settingsRead >= fileSettingsCount) break;
|
if (++settingsRead >= fileSettingsCount) break;
|
||||||
|
{
|
||||||
|
std::string usernameStr;
|
||||||
|
serialization::readString(inputFile, usernameStr);
|
||||||
|
strncpy(calibreUsername, usernameStr.c_str(), sizeof(calibreUsername) - 1);
|
||||||
|
calibreUsername[sizeof(calibreUsername) - 1] = '\0';
|
||||||
|
}
|
||||||
|
if (++settingsRead >= fileSettingsCount) break;
|
||||||
|
{
|
||||||
|
std::string passwordStr;
|
||||||
|
serialization::readString(inputFile, passwordStr);
|
||||||
|
strncpy(calibrePassword, passwordStr.c_str(), sizeof(calibrePassword) - 1);
|
||||||
|
calibrePassword[sizeof(calibrePassword) - 1] = '\0';
|
||||||
|
}
|
||||||
|
if (++settingsRead >= fileSettingsCount) break;
|
||||||
} while (false);
|
} while (false);
|
||||||
|
|
||||||
inputFile.close();
|
inputFile.close();
|
||||||
|
|||||||
@ -88,6 +88,8 @@ class CrossPointSettings {
|
|||||||
uint8_t screenMargin = 5;
|
uint8_t screenMargin = 5;
|
||||||
// OPDS browser settings
|
// OPDS browser settings
|
||||||
char opdsServerUrl[128] = "";
|
char opdsServerUrl[128] = "";
|
||||||
|
char calibreUsername[64] = "";
|
||||||
|
char calibrePassword[64] = "";
|
||||||
// Hide battery percentage
|
// Hide battery percentage
|
||||||
uint8_t hideBatteryPercentage = HIDE_NEVER;
|
uint8_t hideBatteryPercentage = HIDE_NEVER;
|
||||||
|
|
||||||
|
|||||||
@ -41,6 +41,10 @@ inline std::vector<SettingInfo> getSettingsList() {
|
|||||||
{"1 page", "5 pages", "10 pages", "15 pages", "30 pages"}),
|
{"1 page", "5 pages", "10 pages", "15 pages", "30 pages"}),
|
||||||
SettingInfo::String("opdsServerUrl", "Calibre Web URL", SETTINGS.opdsServerUrl,
|
SettingInfo::String("opdsServerUrl", "Calibre Web URL", SETTINGS.opdsServerUrl,
|
||||||
sizeof(SETTINGS.opdsServerUrl) - 1),
|
sizeof(SETTINGS.opdsServerUrl) - 1),
|
||||||
|
SettingInfo::String("calibreUsername", "Calibre Username", SETTINGS.calibreUsername,
|
||||||
|
sizeof(SETTINGS.calibreUsername) - 1),
|
||||||
|
SettingInfo::String("calibrePassword", "Calibre Password", SETTINGS.calibrePassword,
|
||||||
|
sizeof(SETTINGS.calibrePassword) - 1),
|
||||||
SettingInfo::Action("Calibre Settings"),
|
SettingInfo::Action("Calibre Settings"),
|
||||||
SettingInfo::Action("Check for updates"),
|
SettingInfo::Action("Check for updates"),
|
||||||
};
|
};
|
||||||
|
|||||||
@ -13,8 +13,8 @@
|
|||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr int MENU_ITEMS = 2;
|
constexpr int MENU_ITEMS = 4;
|
||||||
const char* menuNames[MENU_ITEMS] = {"Calibre Web URL", "Connect as Wireless Device"};
|
const char* menuNames[MENU_ITEMS] = {"Calibre Web URL", "Username", "Password", "Connect as Wireless Device"};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void CalibreSettingsActivity::taskTrampoline(void* param) {
|
void CalibreSettingsActivity::taskTrampoline(void* param) {
|
||||||
@ -98,6 +98,42 @@ void CalibreSettingsActivity::handleSelection() {
|
|||||||
updateRequired = true;
|
updateRequired = true;
|
||||||
}));
|
}));
|
||||||
} else if (selectedIndex == 1) {
|
} else if (selectedIndex == 1) {
|
||||||
|
// Username
|
||||||
|
exitActivity();
|
||||||
|
enterNewActivity(new KeyboardEntryActivity(
|
||||||
|
renderer, mappedInput, "Username", SETTINGS.calibreUsername, 10,
|
||||||
|
63, // maxLength
|
||||||
|
false, // not password
|
||||||
|
[this](const std::string& username) {
|
||||||
|
strncpy(SETTINGS.calibreUsername, username.c_str(), sizeof(SETTINGS.calibreUsername) - 1);
|
||||||
|
SETTINGS.calibreUsername[sizeof(SETTINGS.calibreUsername) - 1] = '\0';
|
||||||
|
SETTINGS.saveToFile();
|
||||||
|
exitActivity();
|
||||||
|
updateRequired = true;
|
||||||
|
},
|
||||||
|
[this]() {
|
||||||
|
exitActivity();
|
||||||
|
updateRequired = true;
|
||||||
|
}));
|
||||||
|
} else if (selectedIndex == 2) {
|
||||||
|
// Password
|
||||||
|
exitActivity();
|
||||||
|
enterNewActivity(new KeyboardEntryActivity(
|
||||||
|
renderer, mappedInput, "Password", SETTINGS.calibrePassword, 10,
|
||||||
|
63, // maxLength
|
||||||
|
true, // is password
|
||||||
|
[this](const std::string& password) {
|
||||||
|
strncpy(SETTINGS.calibrePassword, password.c_str(), sizeof(SETTINGS.calibrePassword) - 1);
|
||||||
|
SETTINGS.calibrePassword[sizeof(SETTINGS.calibrePassword) - 1] = '\0';
|
||||||
|
SETTINGS.saveToFile();
|
||||||
|
exitActivity();
|
||||||
|
updateRequired = true;
|
||||||
|
},
|
||||||
|
[this]() {
|
||||||
|
exitActivity();
|
||||||
|
updateRequired = true;
|
||||||
|
}));
|
||||||
|
} else if (selectedIndex == 3) {
|
||||||
// Wireless Device - launch the activity (handles WiFi connection internally)
|
// Wireless Device - launch the activity (handles WiFi connection internally)
|
||||||
exitActivity();
|
exitActivity();
|
||||||
if (WiFi.status() != WL_CONNECTED) {
|
if (WiFi.status() != WL_CONNECTED) {
|
||||||
@ -153,11 +189,19 @@ void CalibreSettingsActivity::render() {
|
|||||||
|
|
||||||
renderer.drawText(UI_10_FONT_ID, 20, settingY, menuNames[i], !isSelected);
|
renderer.drawText(UI_10_FONT_ID, 20, settingY, menuNames[i], !isSelected);
|
||||||
|
|
||||||
// Draw status for URL setting
|
// Draw status for URL, username, and password settings
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
const char* status = (strlen(SETTINGS.opdsServerUrl) > 0) ? "[Set]" : "[Not Set]";
|
const char* status = (strlen(SETTINGS.opdsServerUrl) > 0) ? "[Set]" : "[Not Set]";
|
||||||
const auto width = renderer.getTextWidth(UI_10_FONT_ID, status);
|
const auto width = renderer.getTextWidth(UI_10_FONT_ID, status);
|
||||||
renderer.drawText(UI_10_FONT_ID, pageWidth - 20 - width, settingY, status, !isSelected);
|
renderer.drawText(UI_10_FONT_ID, pageWidth - 20 - width, settingY, status, !isSelected);
|
||||||
|
} else if (i == 1) {
|
||||||
|
const char* status = (strlen(SETTINGS.calibreUsername) > 0) ? "[Set]" : "[Not Set]";
|
||||||
|
const auto width = renderer.getTextWidth(UI_10_FONT_ID, status);
|
||||||
|
renderer.drawText(UI_10_FONT_ID, pageWidth - 20 - width, settingY, status, !isSelected);
|
||||||
|
} else if (i == 2) {
|
||||||
|
const char* status = (strlen(SETTINGS.calibrePassword) > 0) ? "[Set]" : "[Not Set]";
|
||||||
|
const auto width = renderer.getTextWidth(UI_10_FONT_ID, status);
|
||||||
|
renderer.drawText(UI_10_FONT_ID, pageWidth - 20 - width, settingY, status, !isSelected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
#include "UrlUtils.h"
|
#include "UrlUtils.h"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
namespace UrlUtils {
|
namespace UrlUtils {
|
||||||
|
|
||||||
std::string ensureProtocol(const std::string& url) {
|
std::string ensureProtocol(const std::string& url) {
|
||||||
@ -38,4 +41,76 @@ std::string buildUrl(const std::string& serverUrl, const std::string& path) {
|
|||||||
return urlWithProtocol + "/" + path;
|
return urlWithProtocol + "/" + path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string urlEncode(const std::string& value) {
|
||||||
|
std::ostringstream escaped;
|
||||||
|
escaped.fill('0');
|
||||||
|
escaped << std::hex;
|
||||||
|
|
||||||
|
for (char c : value) {
|
||||||
|
// Keep alphanumeric and other safe characters intact
|
||||||
|
if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
|
||||||
|
escaped << c;
|
||||||
|
} else {
|
||||||
|
// Encode special characters
|
||||||
|
escaped << std::uppercase;
|
||||||
|
escaped << '%' << std::setw(2) << int((unsigned char)c);
|
||||||
|
escaped << std::nouppercase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return escaped.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string buildUrlWithAuth(const std::string& serverUrl, const std::string& path,
|
||||||
|
const std::string& username, const std::string& password) {
|
||||||
|
// If no credentials, use regular buildUrl
|
||||||
|
if (username.empty() && password.empty()) {
|
||||||
|
return buildUrl(serverUrl, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string urlWithProtocol = ensureProtocol(serverUrl);
|
||||||
|
|
||||||
|
// Find protocol end
|
||||||
|
const size_t protocolEnd = urlWithProtocol.find("://");
|
||||||
|
if (protocolEnd == std::string::npos) {
|
||||||
|
return buildUrl(serverUrl, path); // Fallback if no protocol
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract protocol and host parts
|
||||||
|
std::string protocol = urlWithProtocol.substr(0, protocolEnd + 3); // Include ://
|
||||||
|
std::string hostAndPath = urlWithProtocol.substr(protocolEnd + 3);
|
||||||
|
|
||||||
|
// Check if auth already exists in URL
|
||||||
|
const size_t atPos = hostAndPath.find('@');
|
||||||
|
if (atPos != std::string::npos) {
|
||||||
|
// Auth already in URL, remove it
|
||||||
|
hostAndPath = hostAndPath.substr(atPos + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build auth string with URL encoding
|
||||||
|
std::string auth;
|
||||||
|
if (!username.empty() || !password.empty()) {
|
||||||
|
auth = urlEncode(username) + ":" + urlEncode(password) + "@";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reconstruct URL with auth
|
||||||
|
std::string authenticatedUrl = protocol + auth + hostAndPath;
|
||||||
|
|
||||||
|
// Now apply path logic
|
||||||
|
if (path.empty()) {
|
||||||
|
return authenticatedUrl;
|
||||||
|
}
|
||||||
|
if (path[0] == '/') {
|
||||||
|
// Absolute path - extract just protocol + auth + host
|
||||||
|
const size_t firstSlash = hostAndPath.find('/');
|
||||||
|
std::string hostOnly = (firstSlash == std::string::npos) ? hostAndPath : hostAndPath.substr(0, firstSlash);
|
||||||
|
return protocol + auth + hostOnly + path;
|
||||||
|
}
|
||||||
|
// Relative path
|
||||||
|
if (authenticatedUrl.back() == '/') {
|
||||||
|
return authenticatedUrl + path;
|
||||||
|
}
|
||||||
|
return authenticatedUrl + "/" + path;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace UrlUtils
|
} // namespace UrlUtils
|
||||||
|
|||||||
@ -20,4 +20,17 @@ std::string extractHost(const std::string& url);
|
|||||||
*/
|
*/
|
||||||
std::string buildUrl(const std::string& serverUrl, const std::string& path);
|
std::string buildUrl(const std::string& serverUrl, const std::string& path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URL encode a string (percent encoding for special characters)
|
||||||
|
*/
|
||||||
|
std::string urlEncode(const std::string& value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build URL with basic authentication embedded.
|
||||||
|
* If username and password are provided, adds them to the URL.
|
||||||
|
* Example: https://username:password@example.com/path
|
||||||
|
*/
|
||||||
|
std::string buildUrlWithAuth(const std::string& serverUrl, const std::string& path,
|
||||||
|
const std::string& username, const std::string& password);
|
||||||
|
|
||||||
} // namespace UrlUtils
|
} // namespace UrlUtils
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user