This commit is contained in:
Robin E.R. Davies
2026-05-20 09:32:10 -04:00
19 changed files with 840 additions and 493 deletions
+40
View File
@@ -0,0 +1,40 @@
#include "AesDigest.hpp"
#include <array>
#include <openssl/evp.h>
#include <stdexcept>
using namespace pipedal;
std::string pipedal::AesDigest(const std::string &text)
{
std::array<unsigned char, EVP_MAX_MD_SIZE> digest{};
size_t digestLength = 0;
if (EVP_Q_digest(
nullptr,
"SHA256",
nullptr,
reinterpret_cast<const unsigned char *>(text.data()),
text.size(),
digest.data(),
&digestLength)
!= 1)
{
throw std::runtime_error("EVP_Q_digest(SHA256) failed.");
}
static constexpr char hexDigits[] = "0123456789abcdef";
std::string result(digestLength * 2, '\0');
for (size_t i = 0; i < digestLength; ++i)
{
unsigned char value = digest[i];
result[2 * i] = hexDigits[(value >> 4) & 0x0F];
result[2 * i + 1] = hexDigits[value & 0x0F];
}
return result;
}
+25
View File
@@ -0,0 +1,25 @@
// Copyright (c) 2026 Robin E. R. Davies
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#pragma once
#include <string>
namespace pipedal {
std::string AesDigest(const std::string &text);
}
+13 -8
View File
@@ -1,6 +1,13 @@
set(VERBOSE true)
cmake_minimum_required(VERSION 3.16.0)
configure_file(config.hpp.in config.hpp)
file(GENERATE
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/T3kConfig.h"
CONTENT "#define PIPEDAL_T3K_PUBLISHABLE_KEY \"${PIPEDAL_T3K_PUBLISHABLE_KEY}\"\n")
set (CMAKE_INSTALL_PREFIX "/usr/")
set (USE_PCH 1)
@@ -10,19 +17,13 @@ set (ENABLE_BACKTRACE 0)
set (USE_SANITIZE OFF) # seems to be broken on Ubuntu 24.10
set(CXX_STANDARD 20)
find_package(PkgConfig REQUIRED)
pkg_check_modules(PipeWire REQUIRED libpipewire-0.3)
message(STATUS "PipeWire_INCLUDE_DIRS: ${PipeWire_INCLUDE_DIRS}"
"PipeWire_LIBRARIES: ${PipeWire_LIBRARIES}"
"PipeWire_CFLAGS: ${PipeWire_CFLAGS}"
"PipeWire_DEFINES: ${PipeWire_DEFINES}"
"PipeWire_VERSION: ${PipeWire_VERSION}")
include(FetchContent)
# FOR FTXUI
@@ -58,6 +59,8 @@ include(FindPkgConfig)
find_package(OpenSSL REQUIRED)
find_package(ICU REQUIRED COMPONENTS uc i18n)
message(STATUS "ICU_LIBRARIES: ${ICU_LIBRARIES}")
@@ -204,6 +207,7 @@ else()
endif()
set (PIPEDAL_SOURCES
AesDigest.cpp AesDigest.hpp
ChannelRouterSettings.cpp ChannelRouterSettings.hpp
Curl.cpp Curl.hpp
Tone3000DownloadType.cpp Tone3000DownloadType.hpp
@@ -324,7 +328,7 @@ set (PIPEDAL_SOURCES
)
configure_file(config.hpp.in config.hpp)
include_directories(
${pipedald_SOURCE_DIR}/.
../build/src
@@ -364,6 +368,7 @@ target_link_libraries(libpipedald
PiPedalCommon
PRIVATE
SQLiteCpp
OpenSSL::Crypto
${PipeWire_LIBRARIES}
)
+24 -1
View File
@@ -456,7 +456,7 @@ public:
JSON_MAP_BEGIN(RenameBankBody)
JSON_MAP_REFERENCE(RenameBankBody, bankId)
JSON_MAP_REFERENCE(RenameBankBody, newName)
JSON_MAP_END()
JSON_MAP_END();
class RenamePresetBody
{
@@ -1181,6 +1181,29 @@ public:
}
REGISTER_MESSAGE_HANDLER(setControl)
void handle_makeTone3000Pkce(int replyTo, json_reader *pReader)
{
std::string redirectUrl;
pReader->read(&redirectUrl);
Tone3000PkceParams result {redirectUrl};
this->Reply(replyTo, "makeTone3000Pkce", result);
}
REGISTER_MESSAGE_HANDLER(makeTone3000Pkce);
void handle_sha256Base64url(int replyTo, json_reader *pReader)
{
std::string input;
pReader->read(&input);
std::string result = Sha256Base64Url(input);
this->Reply(replyTo, "sha256Base64url", result);
}
REGISTER_MESSAGE_HANDLER(sha256Base64url);
void handle_previewControl(int replyTo, json_reader *pReader)
{
ControlChangedBody message;
+116
View File
@@ -28,9 +28,12 @@
#include <functional>
#include "HtmlHelper.hpp"
#include "Curl.hpp"
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <charconv>
#include <limits>
#include <type_traits>
#include <array>
#include <filesystem>
#include <fstream>
#include "TemporaryFile.hpp"
@@ -39,6 +42,102 @@
using namespace pipedal;
namespace fs = std::filesystem;
// Pulls in the define for T3kConfig.h
#define PIPEDAL_T3K_PUBLISHABLE_KEY "t3k_pub_bHrH8btdwXXTtxz5ryEU8sNLF-2TGRT9"
namespace {
std::string Base64UrlEncode(const unsigned char* data, size_t size)
{
static constexpr char base64Table[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
std::string result;
result.reserve(((size + 2) / 3) * 4);
for (size_t i = 0; i + 3 <= size; i += 3)
{
uint32_t value = (static_cast<uint32_t>(data[i]) << 16)
| (static_cast<uint32_t>(data[i + 1]) << 8)
| static_cast<uint32_t>(data[i + 2]);
result.push_back(base64Table[(value >> 18) & 0x3F]);
result.push_back(base64Table[(value >> 12) & 0x3F]);
result.push_back(base64Table[(value >> 6) & 0x3F]);
result.push_back(base64Table[value & 0x3F]);
}
size_t remainder = size % 3;
if (remainder == 1)
{
uint32_t value = static_cast<uint32_t>(data[size - 1]) << 16;
result.push_back(base64Table[(value >> 18) & 0x3F]);
result.push_back(base64Table[(value >> 12) & 0x3F]);
result.push_back('=');
result.push_back('=');
}
else if (remainder == 2)
{
uint32_t value = (static_cast<uint32_t>(data[size - 2]) << 16)
| (static_cast<uint32_t>(data[size - 1]) << 8);
result.push_back(base64Table[(value >> 18) & 0x3F]);
result.push_back(base64Table[(value >> 12) & 0x3F]);
result.push_back(base64Table[(value >> 6) & 0x3F]);
result.push_back('=');
}
for (char &ch : result)
{
if (ch == '+')
{
ch = '-';
}
else if (ch == '/')
{
ch = '_';
}
}
while (!result.empty() && result.back() == '=')
{
result.pop_back();
}
return result;
}
std::string RandomBase64Url(size_t size)
{
std::string bytes(size, '\0');
if (RAND_bytes(reinterpret_cast<unsigned char*>(bytes.data()), static_cast<int>(bytes.size())) != 1)
{
throw std::runtime_error("RAND_bytes failed.");
}
return Base64UrlEncode(reinterpret_cast<const unsigned char*>(bytes.data()), bytes.size());
}
}
std::string pipedal::Sha256Base64Url(const std::string &text)
{
std::array<unsigned char, EVP_MAX_MD_SIZE> digest{};
size_t digestLength = 0;
if (EVP_Q_digest(
nullptr,
"SHA256",
nullptr,
reinterpret_cast<const unsigned char *>(text.data()),
text.size(),
digest.data(),
&digestLength)
!= 1)
{
throw std::runtime_error("EVP_Q_digest(SHA256) failed.");
}
return Base64UrlEncode(digest.data(), digestLength);
}
static const std::filesystem::path WEB_TEMP_DIR{"/var/pipedal/web_temp"};
static const std::filesystem::path TONE3000_THUMBNAIL_PATH{"/var/pipedal/tone3000_thumbnails"};
@@ -1168,6 +1267,23 @@ std::shared_ptr<Tone3000Download> Tone3000DownloaderImpl::GetTone3000Tone(
return tone;
}
Tone3000PkceParams::Tone3000PkceParams(
const std::string &redirectUrl)
{
this->publishableKey_ = PIPEDAL_T3K_PUBLISHABLE_KEY;
this->redirectUrl_ = redirectUrl;
this->codeVerifier_ = RandomBase64Url(32);
this->codeChallenge_ = Sha256Base64Url(this->codeVerifier_);
this->state_ = RandomBase64Url(16);
}
//////////////////////////////////////////////////////////////////////////////////
JSON_MAP_BEGIN(Tone3000PkceParams)
+9 -8
View File
@@ -33,14 +33,19 @@
namespace pipedal
{
class Tone3000PkceParams {
class Tone3000PkceParams
{
private:
std::string publishableKey_;
std::string redirectUrl_;
std::string codeVerifier_;
std::string codeChallenge_;
std::string state_;
public:
Tone3000PkceParams() = default;
Tone3000PkceParams(const std::string &redirectUrl);
JSON_GETTER_SETTER_REF(publishableKey);
JSON_GETTER_SETTER_REF(redirectUrl);
JSON_GETTER_SETTER_REF(codeVerifier);
@@ -62,6 +67,8 @@ namespace pipedal
DECLARE_JSON_MAP(Tone3000ModelInfo);
};
std::string Sha256Base64Url(const std::string&input);
// class Tone3000DownloadRequest
// {
// private:
@@ -108,7 +115,6 @@ namespace pipedal
// JSON_GETTER_SETTER_REF(license);
// JSON_GETTER_SETTER_REF(links);
// DECLARE_JSON_MAP(Tone3000DownloadRequest);
// };
@@ -144,10 +150,8 @@ namespace pipedal
using self = Tone3000Downloader;
static std::shared_ptr<self> Create();
virtual void SetListener(Listener *listener) = 0;
virtual void CancelDownload(
handle_t handle) = 0;
@@ -155,12 +159,9 @@ namespace pipedal
const uri &uri,
const Tone3000PkceParams &pkceParams,
const std::string &downloadPath,
Tone3000DownloadType downloadType)
= 0;
Tone3000DownloadType downloadType) = 0;
virtual void Close() = 0;
virtual Tone3000DownloadProgress GetDownloadStatus() = 0;
};
}