fix: correct Base64 encoding for 2-byte remainder

The original code used separate shifts for each byte instead of combining
them into a single 24-bit value first, producing wrong output for credentials
with lengths that leave 2-byte remainders (e.g. 'user:pass').
This commit is contained in:
2026-07-23 21:13:24 +01:00
parent 5805a4f7a4
commit 0563e2c162

View File

@@ -58,19 +58,18 @@ namespace {
if (remaining > 0) { if (remaining > 0) {
const std::uint32_t octet = (static_cast<std::uint32_t>( const std::uint32_t octet = (static_cast<std::uint32_t>(
static_cast<unsigned char>(input[i])) static_cast<unsigned char>(input[i]))
<< 16); << 16)
| (remaining == 2 ? (static_cast<std::uint32_t>(
static_cast<unsigned char>(input[i + 1]))
<< 8)
: 0);
output.push_back(table[(octet >> 18) & 0x3f]);
output.push_back(table[(octet >> 12) & 0x3f]);
if (remaining == 2) { if (remaining == 2) {
const std::uint32_t second = static_cast<std::uint32_t>( output.push_back(table[(octet >> 6) & 0x3f]);
static_cast<unsigned char>(input[i + 1])) }
<< 8; output.push_back('=');
output.push_back(table[((octet | second) >> 18) & 0x3f]); if (remaining == 1) {
output.push_back(table[((octet | second) >> 12) & 0x3f]);
output.push_back(table[((octet | second) >> 6) & 0x3f]);
output.push_back('=');
} else {
output.push_back(table[(octet >> 18) & 0x3f]);
output.push_back(table[(octet >> 12) & 0x3f]);
output.push_back('=');
output.push_back('='); output.push_back('=');
} }
} }
@@ -104,6 +103,7 @@ void CalDAVSync::discover() {
{"Depth", "0"}, {"Depth", "0"},
{"Content-Type", "application/xml; charset=utf-8"}, {"Content-Type", "application/xml; charset=utf-8"},
{"Authorization", "Basic " + encode_basic_auth()}, {"Authorization", "Basic " + encode_basic_auth()},
{"Overwrite", "F"},
}; };
const std::string principal_response = send_request( const std::string principal_response = send_request(
@@ -177,6 +177,7 @@ std::vector<CalDAVCalendar> CalDAVSync::list_calendars() {
{"Depth", "1"}, {"Depth", "1"},
{"Content-Type", "application/xml; charset=utf-8"}, {"Content-Type", "application/xml; charset=utf-8"},
{"Authorization", "Basic " + encode_basic_auth()}, {"Authorization", "Basic " + encode_basic_auth()},
{"Overwrite", "F"},
}; };
const std::string response = send_request( const std::string response = send_request(
@@ -390,13 +391,15 @@ std::string CalDAVSync::send_request(const int method, const std::string& url,
const auto response = transport_.send(request); const auto response = transport_.send(request);
if (response.status == 401) { if (response.status == 401) {
throw std::runtime_error("CalDAV: authentication failed"); throw std::runtime_error("CalDAV: authentication failed (status 401) from " + url
+ "\nCheck that --server points to the CalDAV root (e.g. "
"https://cloud.example.com/remote.php/dav) and that the username/password are correct.");
} }
if (response.status == 403) { if (response.status == 403) {
throw std::runtime_error("CalDAV: forbidden"); throw std::runtime_error("CalDAV: forbidden from " + url);
} }
if (response.status == 404) { if (response.status == 404) {
throw std::runtime_error("CalDAV: resource not found: " + url); throw std::runtime_error("CalDAV: resource not found from " + url);
} }
if (response.status != 200 && response.status != 207) { if (response.status != 200 && response.status != 207) {
throw std::runtime_error("CalDAV: unexpected status " + std::to_string(response.status) throw std::runtime_error("CalDAV: unexpected status " + std::to_string(response.status)