mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-12 14:37:21 +03:00
big merge
This commit is contained in:
BIN
components/spotify/cspot/bell/src/.DS_Store
vendored
Normal file
BIN
components/spotify/cspot/bell/src/.DS_Store
vendored
Normal file
Binary file not shown.
88
components/spotify/cspot/bell/src/BaseHTTPServer.cpp0
Normal file
88
components/spotify/cspot/bell/src/BaseHTTPServer.cpp0
Normal file
@@ -0,0 +1,88 @@
|
||||
#include "BaseHTTPServer.h"
|
||||
#include <sstream>
|
||||
|
||||
unsigned char bell::BaseHTTPServer::h2int(char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9')
|
||||
{
|
||||
return ((unsigned char)c - '0');
|
||||
}
|
||||
if (c >= 'a' && c <= 'f')
|
||||
{
|
||||
return ((unsigned char)c - 'a' + 10);
|
||||
}
|
||||
if (c >= 'A' && c <= 'F')
|
||||
{
|
||||
return ((unsigned char)c - 'A' + 10);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
std::string bell::BaseHTTPServer::urlDecode(std::string str)
|
||||
{
|
||||
std::string encodedString = "";
|
||||
char c;
|
||||
char code0;
|
||||
char code1;
|
||||
for (int i = 0; i < str.length(); i++)
|
||||
{
|
||||
c = str[i];
|
||||
if (c == '+')
|
||||
{
|
||||
encodedString += ' ';
|
||||
}
|
||||
else if (c == '%')
|
||||
{
|
||||
i++;
|
||||
code0 = str[i];
|
||||
i++;
|
||||
code1 = str[i];
|
||||
c = (h2int(code0) << 4) | h2int(code1);
|
||||
encodedString += c;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
encodedString += c;
|
||||
}
|
||||
}
|
||||
|
||||
return encodedString;
|
||||
}
|
||||
|
||||
std::vector<std::string> bell::BaseHTTPServer::splitUrl(const std::string &url, char delimiter)
|
||||
{
|
||||
std::stringstream ssb(url);
|
||||
std::string segment;
|
||||
std::vector<std::string> seglist;
|
||||
|
||||
while (std::getline(ssb, segment, delimiter))
|
||||
{
|
||||
seglist.push_back(segment);
|
||||
}
|
||||
return seglist;
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> bell::BaseHTTPServer::parseQueryString(const std::string &queryString)
|
||||
{
|
||||
std::map<std::string, std::string> query;
|
||||
auto prefixedString = "&" + queryString;
|
||||
while (prefixedString.find("&") != std::string::npos)
|
||||
{
|
||||
auto keyStart = prefixedString.find("&");
|
||||
auto keyEnd = prefixedString.find("=");
|
||||
// Find second occurence of "&" in prefixedString
|
||||
auto valueEnd = prefixedString.find("&", keyStart + 1);
|
||||
if (valueEnd == std::string::npos)
|
||||
{
|
||||
valueEnd = prefixedString.size();
|
||||
}
|
||||
|
||||
auto key = prefixedString.substr(keyStart + 1, keyEnd - 1);
|
||||
auto value = prefixedString.substr(keyEnd + 1, valueEnd - keyEnd - 1);
|
||||
query[key] = urlDecode(value);
|
||||
prefixedString = prefixedString.substr(valueEnd);
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
11
components/spotify/cspot/bell/src/BellLogger.cpp
Normal file
11
components/spotify/cspot/bell/src/BellLogger.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#include "BellLogger.h"
|
||||
|
||||
std::shared_ptr<bell::AbstractLogger> bell::bellGlobalLogger;
|
||||
|
||||
void bell::setDefaultLogger() {
|
||||
bell::bellGlobalLogger = std::make_shared<bell::BellLogger>();
|
||||
}
|
||||
|
||||
void bell::enableSubmoduleLogging() {
|
||||
bell::bellGlobalLogger->enableSubmodule = true;
|
||||
}
|
||||
20
components/spotify/cspot/bell/src/BellUtils.cpp
Normal file
20
components/spotify/cspot/bell/src/BellUtils.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#include "BellUtils.h"
|
||||
|
||||
std::string bell::generateRandomUUID() {
|
||||
static std::random_device dev;
|
||||
static std::mt19937 rng(dev());
|
||||
|
||||
std::uniform_int_distribution<int> dist(0, 15);
|
||||
|
||||
const char *v = "0123456789abcdef";
|
||||
const bool dash[] = {0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0};
|
||||
|
||||
std::string res;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (dash[i])
|
||||
res += "-";
|
||||
res += v[dist(rng)];
|
||||
res += v[dist(rng)];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
69
components/spotify/cspot/bell/src/BinaryReader.cpp
Normal file
69
components/spotify/cspot/bell/src/BinaryReader.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
#include "BinaryReader.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
bell::BinaryReader::BinaryReader(std::shared_ptr<ByteStream> stream) {
|
||||
this->stream = stream;
|
||||
}
|
||||
|
||||
size_t bell::BinaryReader::position() {
|
||||
return stream->position();
|
||||
}
|
||||
|
||||
size_t bell::BinaryReader::size() {
|
||||
return stream->size();
|
||||
}
|
||||
|
||||
void bell::BinaryReader::close() {
|
||||
stream->close();
|
||||
}
|
||||
|
||||
void bell::BinaryReader::skip(size_t pos) {
|
||||
uint8_t b[pos];
|
||||
stream->read((uint8_t *)b, pos);
|
||||
}
|
||||
|
||||
int32_t bell::BinaryReader::readInt() {
|
||||
uint8_t b[4];
|
||||
stream->read((uint8_t *) b,4);
|
||||
|
||||
return static_cast<int32_t>(
|
||||
(b[3]) |
|
||||
(b[2] << 8) |
|
||||
(b[1] << 16)|
|
||||
(b[0] << 24) );
|
||||
}
|
||||
|
||||
int16_t bell::BinaryReader::readShort() {
|
||||
uint8_t b[2];
|
||||
stream->read((uint8_t *) b,2);
|
||||
|
||||
return static_cast<int16_t>(
|
||||
(b[1]) |
|
||||
(b[1] << 8));
|
||||
}
|
||||
|
||||
|
||||
uint32_t bell::BinaryReader::readUInt() {
|
||||
return readInt() & 0xffffffffL;
|
||||
}
|
||||
|
||||
uint8_t bell::BinaryReader::readByte() {
|
||||
uint8_t b[1];
|
||||
stream->read((uint8_t *) b,1);
|
||||
return b[0];
|
||||
}
|
||||
|
||||
std::vector<uint8_t> bell::BinaryReader::readBytes(size_t size) {
|
||||
std::vector<uint8_t> data(size);
|
||||
stream->read(&data[0], size);
|
||||
return data;
|
||||
}
|
||||
|
||||
long long bell::BinaryReader::readLong() {
|
||||
long high = readInt();
|
||||
long low = readInt();
|
||||
|
||||
return static_cast<long long>(
|
||||
(high << 32) | low );
|
||||
}
|
||||
|
||||
227
components/spotify/cspot/bell/src/CryptoMBedTLS.cpp
Normal file
227
components/spotify/cspot/bell/src/CryptoMBedTLS.cpp
Normal file
@@ -0,0 +1,227 @@
|
||||
#ifdef BELL_USE_MBEDTLS
|
||||
#include "CryptoMbedTLS.h"
|
||||
|
||||
CryptoMbedTLS::CryptoMbedTLS()
|
||||
{
|
||||
mbedtls_aes_init(&aesCtx);
|
||||
}
|
||||
|
||||
CryptoMbedTLS::~CryptoMbedTLS()
|
||||
{
|
||||
mbedtls_aes_free(&aesCtx);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> CryptoMbedTLS::base64Decode(const std::string &data)
|
||||
{
|
||||
// Calculate max decode length
|
||||
size_t requiredSize;
|
||||
|
||||
mbedtls_base64_encode(nullptr, 0, &requiredSize, (unsigned char *)data.c_str(), data.size());
|
||||
|
||||
std::vector<uint8_t> output(requiredSize);
|
||||
size_t outputLen = 0;
|
||||
mbedtls_base64_decode(output.data(), requiredSize, &outputLen, (unsigned char *)data.c_str(), data.size());
|
||||
|
||||
return std::vector<uint8_t>(output.begin(), output.begin() + outputLen);
|
||||
}
|
||||
|
||||
std::string CryptoMbedTLS::base64Encode(const std::vector<uint8_t> &data)
|
||||
{
|
||||
// Calculate max output length
|
||||
size_t requiredSize;
|
||||
mbedtls_base64_encode(nullptr, 0, &requiredSize, data.data(), data.size());
|
||||
|
||||
std::vector<uint8_t> output(requiredSize);
|
||||
size_t outputLen = 0;
|
||||
|
||||
mbedtls_base64_encode(output.data(), requiredSize, &outputLen, data.data(), data.size());
|
||||
|
||||
return std::string(output.begin(), output.begin() + outputLen);
|
||||
}
|
||||
|
||||
// Sha1
|
||||
void CryptoMbedTLS::sha1Init()
|
||||
{
|
||||
// Init mbedtls md context, pick sha1
|
||||
mbedtls_md_init(&sha1Context);
|
||||
mbedtls_md_setup(&sha1Context, mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), 1);
|
||||
mbedtls_md_starts(&sha1Context);
|
||||
}
|
||||
|
||||
void CryptoMbedTLS::sha1Update(const std::string &s)
|
||||
{
|
||||
sha1Update(std::vector<uint8_t>(s.begin(), s.end()));
|
||||
}
|
||||
void CryptoMbedTLS::sha1Update(const std::vector<uint8_t> &vec)
|
||||
{
|
||||
mbedtls_md_update(&sha1Context, vec.data(), vec.size());
|
||||
}
|
||||
|
||||
std::vector<uint8_t> CryptoMbedTLS::sha1FinalBytes()
|
||||
{
|
||||
std::vector<uint8_t> digest(20); // SHA1 digest size
|
||||
|
||||
mbedtls_md_finish(&sha1Context, digest.data());
|
||||
mbedtls_md_free(&sha1Context);
|
||||
|
||||
return digest;
|
||||
}
|
||||
|
||||
std::string CryptoMbedTLS::sha1Final()
|
||||
{
|
||||
auto digest = sha1FinalBytes();
|
||||
return std::string(digest.begin(), digest.end());
|
||||
}
|
||||
|
||||
// HMAC SHA1
|
||||
std::vector<uint8_t> CryptoMbedTLS::sha1HMAC(const std::vector<uint8_t> &inputKey, const std::vector<uint8_t> &message)
|
||||
{
|
||||
std::vector<uint8_t> digest(20); // SHA1 digest size
|
||||
|
||||
sha1Init();
|
||||
mbedtls_md_hmac_starts(&sha1Context, inputKey.data(), inputKey.size());
|
||||
mbedtls_md_hmac_update(&sha1Context, message.data(), message.size());
|
||||
mbedtls_md_hmac_finish(&sha1Context, digest.data());
|
||||
mbedtls_md_free(&sha1Context);
|
||||
|
||||
return digest;
|
||||
}
|
||||
|
||||
// AES CTR
|
||||
void CryptoMbedTLS::aesCTRXcrypt(const std::vector<uint8_t> &key, std::vector<uint8_t> &iv, std::vector<uint8_t> &data)
|
||||
{
|
||||
// needed for internal cache
|
||||
size_t off = 0;
|
||||
unsigned char streamBlock[16] = {0};
|
||||
|
||||
// set IV
|
||||
mbedtls_aes_setkey_enc(&aesCtx, key.data(), key.size() * 8);
|
||||
|
||||
// Perform decrypt
|
||||
mbedtls_aes_crypt_ctr(&aesCtx,
|
||||
data.size(),
|
||||
&off,
|
||||
iv.data(),
|
||||
streamBlock,
|
||||
data.data(),
|
||||
data.data());
|
||||
}
|
||||
|
||||
void CryptoMbedTLS::aesECBdecrypt(const std::vector<uint8_t> &key, std::vector<uint8_t> &data)
|
||||
{
|
||||
// Set 192bit key
|
||||
mbedtls_aes_setkey_dec(&aesCtx, key.data(), key.size() * 8);
|
||||
|
||||
// Mbedtls's decrypt only works on 16 byte blocks
|
||||
for (unsigned int x = 0; x < data.size() / 16; x++)
|
||||
{
|
||||
// Perform decrypt
|
||||
mbedtls_aes_crypt_ecb(&aesCtx,
|
||||
MBEDTLS_AES_DECRYPT,
|
||||
data.data() + (x * 16),
|
||||
data.data() + (x * 16));
|
||||
}
|
||||
}
|
||||
|
||||
// PBKDF2
|
||||
std::vector<uint8_t> CryptoMbedTLS::pbkdf2HmacSha1(const std::vector<uint8_t> &password, const std::vector<uint8_t> &salt, int iterations, int digestSize)
|
||||
{
|
||||
auto digest = std::vector<uint8_t>(digestSize);
|
||||
|
||||
// Init sha context
|
||||
sha1Init();
|
||||
mbedtls_pkcs5_pbkdf2_hmac(&sha1Context, password.data(), password.size(), salt.data(), salt.size(), iterations, digestSize, digest.data());
|
||||
|
||||
// Free sha context
|
||||
mbedtls_md_free(&sha1Context);
|
||||
|
||||
return digest;
|
||||
}
|
||||
|
||||
void CryptoMbedTLS::dhInit()
|
||||
{
|
||||
privateKey = generateVectorWithRandomData(DH_KEY_SIZE);
|
||||
|
||||
// initialize big num
|
||||
mbedtls_mpi prime, generator, res, privKey;
|
||||
mbedtls_mpi_init(&prime);
|
||||
mbedtls_mpi_init(&generator);
|
||||
mbedtls_mpi_init(&privKey);
|
||||
mbedtls_mpi_init(&res);
|
||||
|
||||
// Read bin into big num mpi
|
||||
mbedtls_mpi_read_binary(&prime, DHPrime, sizeof(DHPrime));
|
||||
mbedtls_mpi_read_binary(&generator, DHGenerator, sizeof(DHGenerator));
|
||||
mbedtls_mpi_read_binary(&privKey, privateKey.data(), DH_KEY_SIZE);
|
||||
|
||||
// perform diffie hellman G^X mod P
|
||||
mbedtls_mpi_exp_mod(&res, &generator, &privKey, &prime, NULL);
|
||||
|
||||
// Write generated public key to vector
|
||||
this->publicKey = std::vector<uint8_t>(DH_KEY_SIZE);
|
||||
mbedtls_mpi_write_binary(&res, publicKey.data(), DH_KEY_SIZE);
|
||||
|
||||
// Release memory
|
||||
mbedtls_mpi_free(&prime);
|
||||
mbedtls_mpi_free(&generator);
|
||||
mbedtls_mpi_free(&privKey);
|
||||
//mbedtls_mpi_free(&res);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> CryptoMbedTLS::dhCalculateShared(const std::vector<uint8_t> &remoteKey)
|
||||
{
|
||||
// initialize big num
|
||||
mbedtls_mpi prime, remKey, res, privKey;
|
||||
mbedtls_mpi_init(&prime);
|
||||
mbedtls_mpi_init(&remKey);
|
||||
mbedtls_mpi_init(&privKey);
|
||||
mbedtls_mpi_init(&res);
|
||||
|
||||
// Read bin into big num mpi
|
||||
mbedtls_mpi_read_binary(&prime, DHPrime, sizeof(DHPrime));
|
||||
mbedtls_mpi_read_binary(&remKey, remoteKey.data(), remoteKey.size());
|
||||
mbedtls_mpi_read_binary(&privKey, privateKey.data(), DH_KEY_SIZE);
|
||||
|
||||
// perform diffie hellman (G^Y)^X mod P (for shared secret)
|
||||
mbedtls_mpi_exp_mod(&res, &remKey, &privKey, &prime, NULL);
|
||||
|
||||
auto sharedKey = std::vector<uint8_t>(DH_KEY_SIZE);
|
||||
mbedtls_mpi_write_binary(&res, sharedKey.data(), DH_KEY_SIZE);
|
||||
|
||||
// Release memory
|
||||
mbedtls_mpi_free(&prime);
|
||||
mbedtls_mpi_free(&remKey);
|
||||
mbedtls_mpi_free(&privKey);
|
||||
mbedtls_mpi_free(&res);
|
||||
|
||||
return sharedKey;
|
||||
}
|
||||
|
||||
// Random stuff
|
||||
std::vector<uint8_t> CryptoMbedTLS::generateVectorWithRandomData(size_t length)
|
||||
{
|
||||
std::vector<uint8_t> randomVector(length);
|
||||
mbedtls_entropy_context entropy;
|
||||
mbedtls_ctr_drbg_context ctrDrbg;
|
||||
// Personification string
|
||||
const char *pers = "cspotGen";
|
||||
|
||||
// init entropy and random num generator
|
||||
mbedtls_entropy_init(&entropy);
|
||||
mbedtls_ctr_drbg_init(&ctrDrbg);
|
||||
|
||||
// Seed the generator
|
||||
mbedtls_ctr_drbg_seed(&ctrDrbg, mbedtls_entropy_func, &entropy,
|
||||
(const unsigned char *)pers,
|
||||
7);
|
||||
|
||||
// Generate random bytes
|
||||
mbedtls_ctr_drbg_random(&ctrDrbg, randomVector.data(), length);
|
||||
|
||||
// Release memory
|
||||
mbedtls_entropy_free(&entropy);
|
||||
mbedtls_ctr_drbg_free(&ctrDrbg);
|
||||
|
||||
return randomVector;
|
||||
}
|
||||
#endif
|
||||
183
components/spotify/cspot/bell/src/CryptoOpenSSL.cpp
Normal file
183
components/spotify/cspot/bell/src/CryptoOpenSSL.cpp
Normal file
@@ -0,0 +1,183 @@
|
||||
#include "CryptoOpenSSL.h"
|
||||
namespace
|
||||
{
|
||||
struct BIOFreeAll
|
||||
{
|
||||
void operator()(BIO *p) { BIO_free_all(p); }
|
||||
};
|
||||
} // namespace
|
||||
|
||||
CryptoOpenSSL::CryptoOpenSSL()
|
||||
{
|
||||
// OpenSSL init
|
||||
ENGINE_load_builtin_engines();
|
||||
ENGINE_register_all_complete();
|
||||
this->publicKey = std::vector<uint8_t>(DH_KEY_SIZE);
|
||||
this->privateKey = generateVectorWithRandomData(DH_KEY_SIZE);
|
||||
}
|
||||
|
||||
CryptoOpenSSL::~CryptoOpenSSL()
|
||||
{
|
||||
if (this->dhContext != nullptr)
|
||||
{
|
||||
DH_free(this->dhContext);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint8_t> CryptoOpenSSL::base64Decode(const std::string& data)
|
||||
{
|
||||
// base64 in openssl is an absolute mess lmao
|
||||
std::unique_ptr<BIO, BIOFreeAll> b64(BIO_new(BIO_f_base64()));
|
||||
BIO_set_flags(b64.get(), BIO_FLAGS_BASE64_NO_NL);
|
||||
BIO *source = BIO_new_mem_buf(data.c_str(), -1); // read-only source
|
||||
BIO_push(b64.get(), source);
|
||||
const int maxlen = data.size() / 4 * 3 + 1;
|
||||
std::vector<uint8_t> decoded(maxlen);
|
||||
const int len = BIO_read(b64.get(), decoded.data(), maxlen);
|
||||
decoded.resize(len);
|
||||
return decoded;
|
||||
}
|
||||
|
||||
std::string CryptoOpenSSL::base64Encode(const std::vector<uint8_t>& data)
|
||||
{
|
||||
// base64 in openssl is an absolute mess lmao x 2
|
||||
std::unique_ptr<BIO, BIOFreeAll> b64(BIO_new(BIO_f_base64()));
|
||||
|
||||
// No newline mode, put all the data into sink
|
||||
BIO_set_flags(b64.get(), BIO_FLAGS_BASE64_NO_NL);
|
||||
BIO *sink = BIO_new(BIO_s_mem());
|
||||
BIO_push(b64.get(), sink);
|
||||
BIO_write(b64.get(), data.data(), data.size());
|
||||
BIO_flush(b64.get());
|
||||
const char *encoded;
|
||||
const long len = BIO_get_mem_data(sink, &encoded);
|
||||
return std::string(encoded, len);
|
||||
}
|
||||
|
||||
// Sha1
|
||||
void CryptoOpenSSL::sha1Init()
|
||||
{
|
||||
SHA1_Init(&sha1Context);
|
||||
}
|
||||
|
||||
void CryptoOpenSSL::sha1Update(const std::string& s)
|
||||
{
|
||||
sha1Update(std::vector<uint8_t>(s.begin(), s.end()));
|
||||
}
|
||||
void CryptoOpenSSL::sha1Update(const std::vector<uint8_t>& vec)
|
||||
{
|
||||
SHA1_Update(&sha1Context, vec.data(), vec.size());
|
||||
}
|
||||
|
||||
std::vector<uint8_t> CryptoOpenSSL::sha1FinalBytes()
|
||||
{
|
||||
std::vector<uint8_t> digest(20); // 20 is 160 bits
|
||||
SHA1_Final(digest.data(), &sha1Context);
|
||||
return digest;
|
||||
}
|
||||
|
||||
std::string CryptoOpenSSL::sha1Final()
|
||||
{
|
||||
auto digest = sha1FinalBytes();
|
||||
return std::string(digest.begin(), digest.end());
|
||||
}
|
||||
|
||||
// HMAC SHA1
|
||||
std::vector<uint8_t> CryptoOpenSSL::sha1HMAC(const std::vector<uint8_t>& inputKey, const std::vector<uint8_t>& message)
|
||||
{
|
||||
std::vector<uint8_t> digest(20); // 20 is 160 bits
|
||||
auto hmacContext = HMAC_CTX_new();
|
||||
HMAC_Init_ex(hmacContext, inputKey.data(), inputKey.size(), EVP_sha1(), NULL);
|
||||
HMAC_Update(hmacContext, message.data(), message.size());
|
||||
|
||||
unsigned int resLen = 0;
|
||||
HMAC_Final(hmacContext, digest.data(), &resLen);
|
||||
|
||||
HMAC_CTX_free(hmacContext);
|
||||
|
||||
return digest;
|
||||
}
|
||||
|
||||
// AES CTR
|
||||
void CryptoOpenSSL::aesCTRXcrypt(const std::vector<uint8_t>& key, std::vector<uint8_t>& iv, std::vector<uint8_t> &data)
|
||||
{
|
||||
// Prepare AES_KEY
|
||||
auto cryptoKey = AES_KEY();
|
||||
AES_set_encrypt_key(key.data(), 128, &cryptoKey);
|
||||
|
||||
// Needed for openssl internal cache
|
||||
unsigned char ecountBuf[16] = {0};
|
||||
unsigned int offsetInBlock = 0;
|
||||
|
||||
CRYPTO_ctr128_encrypt(
|
||||
data.data(),
|
||||
data.data(),
|
||||
data.size(),
|
||||
&cryptoKey,
|
||||
iv.data(),
|
||||
ecountBuf,
|
||||
&offsetInBlock,
|
||||
block128_f(AES_encrypt));
|
||||
}
|
||||
|
||||
void CryptoOpenSSL::aesECBdecrypt(const std::vector<uint8_t>& key, std::vector<uint8_t>& data)
|
||||
{
|
||||
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
|
||||
EVP_CIPHER_CTX_init(ctx);
|
||||
int len = 0;
|
||||
|
||||
EVP_DecryptInit_ex(ctx, EVP_aes_192_ecb(), NULL, key.data(), NULL);
|
||||
EVP_DecryptUpdate(ctx, data.data(), &len, data.data(), data.size());
|
||||
EVP_DecryptFinal_ex(ctx, data.data() + len, &len);
|
||||
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
}
|
||||
|
||||
// PBKDF2
|
||||
std::vector<uint8_t> CryptoOpenSSL::pbkdf2HmacSha1(const std::vector<uint8_t>& password, const std::vector<uint8_t>& salt, int iterations, int digestSize)
|
||||
{
|
||||
std::vector<uint8_t> digest(digestSize);
|
||||
|
||||
// Generate PKDF2 digest
|
||||
PKCS5_PBKDF2_HMAC_SHA1((const char *)password.data(), password.size(),
|
||||
(const unsigned char *)salt.data(), salt.size(), iterations,
|
||||
digestSize, digest.data());
|
||||
return digest;
|
||||
}
|
||||
|
||||
void CryptoOpenSSL::dhInit()
|
||||
{
|
||||
// Free old context
|
||||
if (this->dhContext != nullptr)
|
||||
{
|
||||
DH_free(this->dhContext);
|
||||
}
|
||||
this->dhContext = DH_new();
|
||||
|
||||
// Set prime and the generator
|
||||
DH_set0_pqg(this->dhContext, BN_bin2bn(DHPrime, DH_KEY_SIZE, NULL), NULL, BN_bin2bn(DHGenerator, 1, NULL));
|
||||
|
||||
// Generate public and private keys and copy them to vectors
|
||||
DH_generate_key(this->dhContext);
|
||||
BN_bn2bin(DH_get0_pub_key(dhContext), this->publicKey.data());
|
||||
}
|
||||
|
||||
std::vector<uint8_t> CryptoOpenSSL::dhCalculateShared(const std::vector<uint8_t>& remoteKey)
|
||||
{
|
||||
auto sharedKey = std::vector<uint8_t>(DH_KEY_SIZE);
|
||||
// Convert remote key to bignum and compute shared key
|
||||
auto pubKey = BN_bin2bn(&remoteKey[0], DH_KEY_SIZE, NULL);
|
||||
DH_compute_key(sharedKey.data(), pubKey, this->dhContext);
|
||||
BN_free(pubKey);
|
||||
return sharedKey;
|
||||
}
|
||||
|
||||
// Random stuff
|
||||
std::vector<uint8_t> CryptoOpenSSL::generateVectorWithRandomData(size_t length)
|
||||
{
|
||||
std::vector<uint8_t> randomVec(length);
|
||||
if(RAND_bytes(randomVec.data(), length) == 0)
|
||||
{
|
||||
}
|
||||
return randomVec;
|
||||
}
|
||||
8
components/spotify/cspot/bell/src/DecoderGlobals.cpp
Normal file
8
components/spotify/cspot/bell/src/DecoderGlobals.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
#include "DecoderGlobals.h"
|
||||
|
||||
std::shared_ptr<bell::DecodersInstance> bell::decodersInstance;
|
||||
|
||||
void bell::createDecoders()
|
||||
{
|
||||
bell::decodersInstance = std::make_shared<bell::DecodersInstance>();
|
||||
}
|
||||
469
components/spotify/cspot/bell/src/HTTPServer.cpp
Normal file
469
components/spotify/cspot/bell/src/HTTPServer.cpp
Normal file
@@ -0,0 +1,469 @@
|
||||
#include "HTTPServer.h"
|
||||
|
||||
bell::HTTPServer::HTTPServer(int serverPort)
|
||||
{
|
||||
this->serverPort = serverPort;
|
||||
}
|
||||
|
||||
unsigned char bell::HTTPServer::h2int(char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9')
|
||||
{
|
||||
return ((unsigned char)c - '0');
|
||||
}
|
||||
if (c >= 'a' && c <= 'f')
|
||||
{
|
||||
return ((unsigned char)c - 'a' + 10);
|
||||
}
|
||||
if (c >= 'A' && c <= 'F')
|
||||
{
|
||||
return ((unsigned char)c - 'A' + 10);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
std::string bell::HTTPServer::urlDecode(std::string str)
|
||||
{
|
||||
std::string encodedString = "";
|
||||
char c;
|
||||
char code0;
|
||||
char code1;
|
||||
for (int i = 0; i < str.length(); i++)
|
||||
{
|
||||
c = str[i];
|
||||
if (c == '+')
|
||||
{
|
||||
encodedString += ' ';
|
||||
}
|
||||
else if (c == '%')
|
||||
{
|
||||
i++;
|
||||
code0 = str[i];
|
||||
i++;
|
||||
code1 = str[i];
|
||||
c = (h2int(code0) << 4) | h2int(code1);
|
||||
encodedString += c;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
encodedString += c;
|
||||
}
|
||||
}
|
||||
|
||||
return encodedString;
|
||||
}
|
||||
|
||||
std::vector<std::string> bell::HTTPServer::splitUrl(const std::string &url, char delimiter)
|
||||
{
|
||||
std::stringstream ssb(url);
|
||||
std::string segment;
|
||||
std::vector<std::string> seglist;
|
||||
|
||||
while (std::getline(ssb, segment, delimiter))
|
||||
{
|
||||
seglist.push_back(segment);
|
||||
}
|
||||
return seglist;
|
||||
}
|
||||
|
||||
void bell::HTTPServer::registerHandler(RequestType requestType, const std::string &routeUrl, httpHandler handler)
|
||||
{
|
||||
if (routes.find(routeUrl) == routes.end())
|
||||
{
|
||||
routes.insert({routeUrl, std::vector<HTTPRoute>()});
|
||||
}
|
||||
this->routes[routeUrl].push_back(HTTPRoute{
|
||||
.requestType = requestType,
|
||||
.handler = handler,
|
||||
});
|
||||
}
|
||||
|
||||
void bell::HTTPServer::listen()
|
||||
{
|
||||
BELL_LOG(info, "http", "Starting configuration server at port %d", this->serverPort);
|
||||
|
||||
// setup address
|
||||
struct addrinfo hints, *server;
|
||||
memset(&hints, 0, sizeof hints);
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
getaddrinfo(NULL, std::to_string(serverPort).c_str(), &hints, &server);
|
||||
|
||||
int sockfd = socket(server->ai_family,
|
||||
server->ai_socktype, server->ai_protocol);
|
||||
struct sockaddr_in clientname;
|
||||
socklen_t incomingSockSize;
|
||||
int i;
|
||||
int yes = true;
|
||||
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
|
||||
bind(sockfd, server->ai_addr, server->ai_addrlen);
|
||||
::listen(sockfd, 10);
|
||||
|
||||
FD_ZERO(&activeFdSet);
|
||||
FD_SET(sockfd, &activeFdSet);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
/* Block until input arrives on one or more active sockets. */
|
||||
readFdSet = activeFdSet;
|
||||
if (select(FD_SETSIZE, &readFdSet, NULL, NULL, NULL) < 0)
|
||||
{
|
||||
BELL_LOG(error, "http", "Error in select");
|
||||
perror("select");
|
||||
// exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Service all the sockets with input pending. */
|
||||
for (i = 0; i < FD_SETSIZE; ++i)
|
||||
if (FD_ISSET(i, &readFdSet))
|
||||
{
|
||||
if (i == sockfd)
|
||||
{
|
||||
/* Connection request on original socket. */
|
||||
int newFd;
|
||||
incomingSockSize = sizeof(clientname);
|
||||
newFd = accept(sockfd, (struct sockaddr *)&clientname, &incomingSockSize);
|
||||
if (newFd < 0)
|
||||
{
|
||||
perror("accept");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
FD_SET(newFd, &activeFdSet);
|
||||
|
||||
HTTPConnection conn = {
|
||||
.buffer = std::vector<uint8_t>(128),
|
||||
.httpMethod = ""};
|
||||
|
||||
this->connections.insert({newFd, conn});
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Data arriving on an already-connected socket. */
|
||||
readFromClient(i);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = this->connections.cbegin(); it != this->connections.cend() /* not hoisted */; /* no increment */)
|
||||
{
|
||||
if ((*it).second.toBeClosed)
|
||||
{
|
||||
close((*it).first);
|
||||
FD_CLR((*it).first, &activeFdSet);
|
||||
this->connections.erase(it++); // or "it = m.erase(it)" since C++11
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bell::HTTPServer::readFromClient(int clientFd)
|
||||
{
|
||||
HTTPConnection &conn = this->connections[clientFd];
|
||||
|
||||
int nbytes = recv(clientFd, &conn.buffer[0], conn.buffer.size(), 0);
|
||||
if (nbytes < 0)
|
||||
{
|
||||
BELL_LOG(error, "http", "Error reading from client");
|
||||
perror("recv");
|
||||
this->closeConnection(clientFd);
|
||||
}
|
||||
else if (nbytes == 0)
|
||||
{
|
||||
this->closeConnection(clientFd);
|
||||
}
|
||||
else
|
||||
{
|
||||
conn.currentLine += std::string(conn.buffer.data(), conn.buffer.data() + nbytes);
|
||||
READBODY:
|
||||
if (!conn.isReadingBody)
|
||||
{
|
||||
while (conn.currentLine.find("\r\n") != std::string::npos)
|
||||
{
|
||||
auto line = conn.currentLine.substr(0, conn.currentLine.find("\r\n"));
|
||||
conn.currentLine = conn.currentLine.substr(conn.currentLine.find("\r\n") + 2, conn.currentLine.size());
|
||||
if (line.find("GET ") != std::string::npos || line.find("POST ") != std::string::npos || line.find("OPTIONS ") != std::string::npos)
|
||||
{
|
||||
conn.httpMethod = line;
|
||||
}
|
||||
|
||||
if (line.find("Content-Length: ") != std::string::npos)
|
||||
{
|
||||
conn.contentLength = std::stoi(line.substr(16, line.size() - 1));
|
||||
BELL_LOG(info, "http", "Content-Length: %d", conn.contentLength);
|
||||
}
|
||||
if (line.size() == 0)
|
||||
{
|
||||
if (conn.contentLength != 0)
|
||||
{
|
||||
conn.isReadingBody = true;
|
||||
goto READBODY;
|
||||
}
|
||||
else
|
||||
{
|
||||
findAndHandleRoute(conn.httpMethod, conn.currentLine, clientFd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (conn.currentLine.size() >= conn.contentLength)
|
||||
{
|
||||
findAndHandleRoute(conn.httpMethod, conn.currentLine, clientFd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bell::HTTPServer::closeConnection(int connection)
|
||||
{
|
||||
|
||||
this->connections[connection].toBeClosed = true;
|
||||
}
|
||||
|
||||
void bell::HTTPServer::writeResponseEvents(int connFd)
|
||||
{
|
||||
std::lock_guard lock(this->responseMutex);
|
||||
|
||||
std::stringstream stream;
|
||||
stream << "HTTP/1.1 200 OK\r\n";
|
||||
stream << "Server: EUPHONIUM\r\n";
|
||||
stream << "Connection: keep-alive\r\n";
|
||||
stream << "Content-type: text/event-stream\r\n";
|
||||
stream << "Cache-Control: no-cache\r\n";
|
||||
stream << "Access-Control-Allow-Origin: *\r\n";
|
||||
stream << "Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS\r\n";
|
||||
stream << "Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token\r\n";
|
||||
stream << "\r\n";
|
||||
|
||||
auto responseStr = stream.str();
|
||||
|
||||
write(connFd, responseStr.c_str(), responseStr.size());
|
||||
this->connections[connFd].isEventConnection = true;
|
||||
}
|
||||
|
||||
void bell::HTTPServer::writeResponse(const HTTPResponse &response)
|
||||
{
|
||||
std::lock_guard lock(this->responseMutex);
|
||||
|
||||
auto fileSize = response.body.size();
|
||||
|
||||
if (response.responseReader != nullptr)
|
||||
{
|
||||
fileSize = response.responseReader->getTotalSize();
|
||||
}
|
||||
|
||||
std::stringstream stream;
|
||||
stream << "HTTP/1.1 " << response.status << " OK\r\n";
|
||||
stream << "Server: EUPHONIUM\r\n";
|
||||
stream << "Connection: close\r\n";
|
||||
stream << "Content-type: " << response.contentType << "\r\n";
|
||||
|
||||
if (response.useGzip)
|
||||
{
|
||||
stream << "Content-encoding: gzip"
|
||||
<< "\r\n";
|
||||
}
|
||||
|
||||
stream << "Content-length:" << fileSize << "\r\n";
|
||||
stream << "Access-Control-Allow-Origin: *\r\n";
|
||||
stream << "Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS\r\n";
|
||||
stream << "Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token\r\n";
|
||||
stream << "\r\n";
|
||||
|
||||
if (response.body.size() > 0)
|
||||
{
|
||||
stream << response.body;
|
||||
}
|
||||
|
||||
auto responseStr = stream.str();
|
||||
|
||||
write(response.connectionFd, responseStr.c_str(), responseStr.size());
|
||||
|
||||
if (response.responseReader != nullptr)
|
||||
{
|
||||
size_t read;
|
||||
do
|
||||
{
|
||||
read = response.responseReader->read(responseBuffer.data(), responseBuffer.size());
|
||||
if (read > 0)
|
||||
{
|
||||
write(response.connectionFd, responseBuffer.data(), read);
|
||||
}
|
||||
} while (read > 0);
|
||||
}
|
||||
this->closeConnection(response.connectionFd);
|
||||
}
|
||||
|
||||
void bell::HTTPServer::respond(const HTTPResponse &response)
|
||||
{
|
||||
writeResponse(response);
|
||||
}
|
||||
|
||||
void bell::HTTPServer::publishEvent(std::string eventName, std::string eventData)
|
||||
{
|
||||
std::lock_guard lock(this->responseMutex);
|
||||
BELL_LOG(info, "http", "Publishing event");
|
||||
|
||||
std::stringstream stream;
|
||||
stream << "event: " << eventName << "\n";
|
||||
stream << "data: " << eventData << "\n\n";
|
||||
auto responseStr = stream.str();
|
||||
|
||||
// Reply to all event-connections
|
||||
for (auto it = this->connections.cbegin(); it != this->connections.cend(); ++it)
|
||||
{
|
||||
if ((*it).second.isEventConnection)
|
||||
{
|
||||
write(it->first, responseStr.c_str(), responseStr.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> bell::HTTPServer::parseQueryString(const std::string &queryString)
|
||||
{
|
||||
std::map<std::string, std::string> query;
|
||||
auto prefixedString = "&" + queryString;
|
||||
while (prefixedString.find("&") != std::string::npos)
|
||||
{
|
||||
auto keyStart = prefixedString.find("&");
|
||||
auto keyEnd = prefixedString.find("=");
|
||||
// Find second occurence of "&" in prefixedString
|
||||
auto valueEnd = prefixedString.find("&", keyStart + 1);
|
||||
if (valueEnd == std::string::npos)
|
||||
{
|
||||
valueEnd = prefixedString.size();
|
||||
}
|
||||
|
||||
auto key = prefixedString.substr(keyStart + 1, keyEnd - 1);
|
||||
auto value = prefixedString.substr(keyEnd + 1, valueEnd - keyEnd - 1);
|
||||
query[key] = urlDecode(value);
|
||||
prefixedString = prefixedString.substr(valueEnd);
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
void bell::HTTPServer::findAndHandleRoute(std::string &url, std::string &body, int connectionFd)
|
||||
{
|
||||
std::map<std::string, std::string> pathParams;
|
||||
std::map<std::string, std::string> queryParams;
|
||||
|
||||
if (url.find("OPTIONS /") != std::string::npos)
|
||||
{
|
||||
std::stringstream stream;
|
||||
stream << "HTTP/1.1 200 OK\r\n";
|
||||
stream << "Server: EUPHONIUM\r\n";
|
||||
stream << "Allow: OPTIONS, GET, HEAD, POST\r\n";
|
||||
stream << "Connection: close\r\n";
|
||||
stream << "Access-Control-Allow-Origin: *\r\n";
|
||||
stream << "Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n";
|
||||
stream << "Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token\r\n";
|
||||
stream << "\r\n";
|
||||
|
||||
auto responseStr = stream.str();
|
||||
|
||||
write(connectionFd, responseStr.c_str(), responseStr.size());
|
||||
closeConnection(connectionFd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (url.find("GET /events") != std::string::npos)
|
||||
{
|
||||
// Handle SSE endpoint here
|
||||
writeResponseEvents(connectionFd);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto &routeSet : this->routes)
|
||||
{
|
||||
for (const auto &route : routeSet.second)
|
||||
{
|
||||
|
||||
std::string path = url;
|
||||
if (url.find("GET ") != std::string::npos && route.requestType == RequestType::GET)
|
||||
{
|
||||
path = path.substr(4);
|
||||
}
|
||||
else if (url.find("POST ") != std::string::npos && route.requestType == RequestType::POST)
|
||||
{
|
||||
path = path.substr(5);
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
path = path.substr(0, path.find(" "));
|
||||
|
||||
if (path.find("?") != std::string::npos)
|
||||
{
|
||||
auto urlEncodedSplit = splitUrl(path, '?');
|
||||
path = urlEncodedSplit[0];
|
||||
queryParams = this->parseQueryString(urlEncodedSplit[1]);
|
||||
}
|
||||
|
||||
auto routeSplit = splitUrl(routeSet.first, '/');
|
||||
auto urlSplit = splitUrl(path, '/');
|
||||
bool matches = true;
|
||||
|
||||
pathParams.clear();
|
||||
|
||||
if (routeSplit.size() == urlSplit.size())
|
||||
{
|
||||
for (int x = 0; x < routeSplit.size(); x++)
|
||||
{
|
||||
if (routeSplit[x] != urlSplit[x])
|
||||
{
|
||||
if (routeSplit[x][0] == ':')
|
||||
{
|
||||
pathParams.insert({routeSplit[x].substr(1), urlSplit[x]});
|
||||
}
|
||||
else
|
||||
{
|
||||
matches = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
matches = false;
|
||||
}
|
||||
|
||||
if (routeSplit.back().find("*") != std::string::npos && urlSplit[1] == routeSplit[1])
|
||||
{
|
||||
matches = true;
|
||||
}
|
||||
|
||||
if (matches)
|
||||
{
|
||||
if (body.find("&") != std::string::npos)
|
||||
{
|
||||
queryParams = this->parseQueryString(body);
|
||||
}
|
||||
|
||||
HTTPRequest req = {
|
||||
.urlParams = pathParams,
|
||||
.queryParams = queryParams,
|
||||
.body = body,
|
||||
.handlerId = 0,
|
||||
.connection = connectionFd};
|
||||
|
||||
route.handler(req);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
writeResponse(HTTPResponse{
|
||||
.connectionFd = connectionFd,
|
||||
.status = 404,
|
||||
.body = "Not found",
|
||||
});
|
||||
}
|
||||
186
components/spotify/cspot/bell/src/HTTPStream.cpp
Normal file
186
components/spotify/cspot/bell/src/HTTPStream.cpp
Normal file
@@ -0,0 +1,186 @@
|
||||
#include "HTTPStream.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <ctype.h>
|
||||
#include <cstring>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <netinet/tcp.h>
|
||||
|
||||
bell::HTTPStream::HTTPStream()
|
||||
{
|
||||
}
|
||||
|
||||
bell::HTTPStream::~HTTPStream()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
void bell::HTTPStream::close()
|
||||
{
|
||||
if (status != StreamStatus::CLOSED)
|
||||
{
|
||||
status = StreamStatus::CLOSED;
|
||||
BELL_LOG(info, "httpStream", "Closing socket");
|
||||
socket->close();
|
||||
BELL_LOG(info, "httpStream", "Closed socket");
|
||||
}
|
||||
}
|
||||
|
||||
void bell::HTTPStream::connectToUrl(std::string url, bool disableSSL)
|
||||
{
|
||||
std::string portString;
|
||||
// check if url contains "https"
|
||||
if (url.find("https") != std::string::npos && !disableSSL)
|
||||
{
|
||||
socket = std::make_unique<bell::TLSSocket>();
|
||||
portString = "443";
|
||||
}
|
||||
else
|
||||
{
|
||||
socket = std::make_unique<bell::TCPSocket>();
|
||||
portString = "80";
|
||||
}
|
||||
|
||||
socket->open(url);
|
||||
|
||||
// remove https or http from url
|
||||
url.erase(0, url.find("://") + 3);
|
||||
|
||||
// split by first "/" in url
|
||||
std::string hostUrl = url.substr(0, url.find('/'));
|
||||
std::string pathUrl = url.substr(url.find('/'));
|
||||
|
||||
// check if hostUrl contains ':'
|
||||
if (hostUrl.find(':') != std::string::npos)
|
||||
{
|
||||
// split by ':'
|
||||
std::string host = hostUrl.substr(0, hostUrl.find(':'));
|
||||
portString = hostUrl.substr(hostUrl.find(':') + 1);
|
||||
hostUrl = host;
|
||||
}
|
||||
|
||||
// Prepare HTTP get header
|
||||
std::stringstream ss;
|
||||
ss << "GET " << pathUrl << " HTTP/1.1\r\n"
|
||||
<< "Host: " << hostUrl << ":" << portString << "\r\n"
|
||||
<< "Accept: */*\r\n"
|
||||
<< "\r\n\r\n";
|
||||
|
||||
std::string request = ss.str();
|
||||
|
||||
// Send the request
|
||||
if (socket->write((uint8_t*)request.c_str(), request.length()) != (int)request.length())
|
||||
{
|
||||
close();
|
||||
BELL_LOG(error, "http", "Can't send request");
|
||||
throw std::runtime_error("Resolve failed");
|
||||
}
|
||||
|
||||
status = StreamStatus::READING_HEADERS;
|
||||
auto buffer = std::vector<uint8_t>(128);
|
||||
auto currentLine = std::string();
|
||||
auto statusOkay = false;
|
||||
auto readingData = false;
|
||||
// Read data on socket sockFd line after line
|
||||
int nbytes;
|
||||
|
||||
while (status == StreamStatus::READING_HEADERS)
|
||||
{
|
||||
nbytes = socket->read(&buffer[0], buffer.size());
|
||||
if (nbytes < 0)
|
||||
{
|
||||
BELL_LOG(error, "http", "Error reading from client");
|
||||
perror("recv");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
else if (nbytes == 0)
|
||||
{
|
||||
BELL_LOG(error, "http", "Client disconnected");
|
||||
close();
|
||||
}
|
||||
else
|
||||
{
|
||||
currentLine += std::string(buffer.data(), buffer.data() + nbytes);
|
||||
while (currentLine.find("\r\n") != std::string::npos)
|
||||
{
|
||||
auto line = currentLine.substr(0, currentLine.find("\r\n"));
|
||||
currentLine = currentLine.substr(currentLine.find("\r\n") + 2, currentLine.size());
|
||||
BELL_LOG(info, "http", "Line: %s", line.c_str());
|
||||
|
||||
// handle redirects:
|
||||
if (line.find("Location:") != std::string::npos)
|
||||
{
|
||||
auto newUrl = line.substr(10);
|
||||
BELL_LOG(info, "http", "Redirecting to %s", newUrl.c_str());
|
||||
|
||||
close();
|
||||
return connectToUrl(newUrl);
|
||||
}
|
||||
// handle content-length
|
||||
if (line.find("Content-Length:") != std::string::npos)
|
||||
{
|
||||
auto contentLengthStr = line.substr(16);
|
||||
BELL_LOG(info, "http", "Content size %s", contentLengthStr.c_str());
|
||||
|
||||
// convert contentLengthStr to size_t
|
||||
this->contentLength = std::stoi(contentLengthStr);
|
||||
hasFixedSize = true;
|
||||
}
|
||||
else if (line.find("200 OK") != std::string::npos)
|
||||
{
|
||||
statusOkay = true;
|
||||
}
|
||||
else if (line.size() == 0 && statusOkay)
|
||||
{
|
||||
BELL_LOG(info, "http", "Ready to receive data!");
|
||||
status = StreamStatus::READING_DATA;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t bell::HTTPStream::read(uint8_t *buf, size_t nbytes)
|
||||
{
|
||||
if (status != StreamStatus::READING_DATA)
|
||||
{
|
||||
BELL_LOG(error, "http", "Not ready to read data");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nread = socket->read(buf, nbytes);
|
||||
if (nread < 0)
|
||||
{
|
||||
BELL_LOG(error, "http", "Error reading from client");
|
||||
close();
|
||||
|
||||
perror("recv");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (this->hasFixedSize)
|
||||
{
|
||||
this->currentPos += nread;
|
||||
}
|
||||
|
||||
if (nread < nbytes)
|
||||
{
|
||||
return read(buf + nread, nbytes - nread);
|
||||
}
|
||||
return nread;
|
||||
}
|
||||
|
||||
size_t bell::HTTPStream::skip(size_t nbytes)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
45
components/spotify/cspot/bell/src/JSONObject.cpp
Normal file
45
components/spotify/cspot/bell/src/JSONObject.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#include "JSONObject.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
bell::JSONValue::JSONValue(cJSON *body, std::string key)
|
||||
{
|
||||
this->body = body;
|
||||
this->key = key;
|
||||
}
|
||||
|
||||
void bell::JSONValue::operator=(const std::string val)
|
||||
{
|
||||
this->operator=(val.c_str());
|
||||
}
|
||||
void bell::JSONValue::operator=(const char *val)
|
||||
{
|
||||
cJSON_AddStringToObject(this->body, this->key.c_str(), val);
|
||||
}
|
||||
void bell::JSONValue::operator=(int val)
|
||||
{
|
||||
cJSON_AddNumberToObject(this->body, this->key.c_str(), val);
|
||||
}
|
||||
|
||||
bell::JSONObject::JSONObject()
|
||||
{
|
||||
this->body = cJSON_CreateObject();
|
||||
}
|
||||
|
||||
bell::JSONObject::~JSONObject()
|
||||
{
|
||||
cJSON_Delete(this->body);
|
||||
}
|
||||
|
||||
bell::JSONValue bell::JSONObject::operator[](std::string index)
|
||||
{
|
||||
return bell::JSONValue(this->body, index);
|
||||
}
|
||||
|
||||
std::string bell::JSONObject::toString()
|
||||
{
|
||||
char *body = cJSON_Print(this->body);
|
||||
std::string retVal = std::string(body);
|
||||
free(body);
|
||||
return retVal;
|
||||
|
||||
}
|
||||
712
components/spotify/cspot/bell/src/MpegDashDemuxer.cpp
Normal file
712
components/spotify/cspot/bell/src/MpegDashDemuxer.cpp
Normal file
@@ -0,0 +1,712 @@
|
||||
#include "MpegDashDemuxer.h"
|
||||
|
||||
using namespace bell::mpeg;
|
||||
|
||||
MpegDashDemuxer::MpegDashDemuxer(std::shared_ptr<bell::ByteStream> stream)
|
||||
{
|
||||
this->reader = std::make_shared<bell::BinaryReader>(stream);
|
||||
}
|
||||
|
||||
int readIntFromVector(std::vector<uint8_t> c, size_t offset)
|
||||
{
|
||||
|
||||
return ((c[0] << 24) | (c[1] << 16) | (c[2] << 8) | (c[3])) & 0xffffffffL;
|
||||
}
|
||||
|
||||
void MpegDashDemuxer::close() {
|
||||
reader->close();
|
||||
}
|
||||
void MpegDashDemuxer::parse()
|
||||
{
|
||||
// Skip FTYP box
|
||||
lastBox = readBox();
|
||||
ensureBox(lastBox);
|
||||
|
||||
auto moov = std::make_unique<Moov>();
|
||||
|
||||
while (lastBox->type != ATOM_MOOF)
|
||||
{
|
||||
ensureBox(lastBox);
|
||||
lastBox = readBox();
|
||||
if (lastBox->type == ATOM_MOOV)
|
||||
{
|
||||
// parse moov
|
||||
moov = parseMoov(lastBox);
|
||||
}
|
||||
}
|
||||
|
||||
tracks = std::vector<Mp4Track>(moov->trak.size());
|
||||
|
||||
for (int i = 0; i < tracks.size(); i++)
|
||||
{
|
||||
tracks[i] = Mp4Track();
|
||||
tracks[i].trak = std::move(moov->trak[i]);
|
||||
}
|
||||
}
|
||||
|
||||
int MpegDashDemuxer::parseMfhd()
|
||||
{
|
||||
reader->skip(4);
|
||||
return reader->readInt();
|
||||
}
|
||||
|
||||
long MpegDashDemuxer::parseTfdt()
|
||||
{
|
||||
uint8_t version = reader->readByte();
|
||||
reader->skip(3);
|
||||
return version == 0 ? reader->readUInt() : reader->readLong();
|
||||
}
|
||||
|
||||
std::unique_ptr<Mp4DashSample> MpegDashDemuxer::getNextSample(std::unique_ptr<Chunk> &chunk)
|
||||
{
|
||||
auto sample = std::make_unique<Mp4DashSample>();
|
||||
if (chunk->size == 0)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (chunk->i >= chunk->moof->traf->trun->entryCount)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
sample->info = getAbsoluteTrunEntry(chunk->moof->traf->trun, chunk->i++, chunk->moof->traf->tfhd);
|
||||
sample->data = reader->readBytes(sample->info->sampleSize);
|
||||
chunk->sampleRead += sample->info->sampleSize;
|
||||
|
||||
return sample;
|
||||
}
|
||||
|
||||
std::unique_ptr<Chunk> MpegDashDemuxer::getNextChunk(bool infoOnly)
|
||||
{
|
||||
|
||||
while (reader->position() < reader->size())
|
||||
{
|
||||
if (chunkZero)
|
||||
{
|
||||
ensureBox(lastBox);
|
||||
lastBox = readBox();
|
||||
}
|
||||
else
|
||||
{
|
||||
chunkZero = true;
|
||||
}
|
||||
|
||||
if (lastBox->type == ATOM_MOOF)
|
||||
{
|
||||
lastMoof = parseMoof(lastBox, tracks[0].trak->tkhd->trackId);
|
||||
|
||||
if (lastMoof->traf != nullptr)
|
||||
{
|
||||
if (hasFlag(lastMoof->traf->trun->bFlags, 0x0001))
|
||||
{
|
||||
|
||||
lastMoof->traf->trun->dataOffset -= lastBox->size + 8;
|
||||
}
|
||||
}
|
||||
|
||||
if (lastMoof->traf->trun->chunkSize < 1)
|
||||
{
|
||||
if (hasFlag(lastMoof->traf->tfhd->bFlags, 0x10))
|
||||
{
|
||||
lastMoof->traf->trun->chunkSize = lastMoof->traf->tfhd->defaultSampleSize * lastMoof->traf->trun->entryCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
lastMoof->traf->trun->chunkSize = lastBox->size = 8;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasFlag(lastMoof->traf->trun->bFlags, 0x900) && lastMoof->traf->trun->chunkDuration == 0)
|
||||
{
|
||||
if (hasFlag(lastMoof->traf->tfhd->bFlags, 0x20))
|
||||
{
|
||||
lastMoof->traf->trun->chunkDuration = lastMoof->traf->tfhd->defaultSampleDuration * lastMoof->traf->trun->entryCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lastBox->type == ATOM_MDAT)
|
||||
{
|
||||
if (lastMoof->traf == nullptr)
|
||||
{
|
||||
lastMoof = nullptr;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto chunk = std::make_unique<Chunk>();
|
||||
chunk->moof = std::move(lastMoof);
|
||||
if (!infoOnly)
|
||||
{
|
||||
chunk->size = chunk->moof->traf->trun->chunkSize;
|
||||
}
|
||||
|
||||
lastMoof = nullptr;
|
||||
reader->skip(chunk->moof->traf->trun->dataOffset);
|
||||
return chunk;
|
||||
}
|
||||
}
|
||||
|
||||
return std::unique_ptr<Chunk>(nullptr);
|
||||
}
|
||||
|
||||
size_t MpegDashDemuxer::position() {
|
||||
return reader->position();
|
||||
}
|
||||
|
||||
std::unique_ptr<Moof> MpegDashDemuxer::parseMoof(std::unique_ptr<MpegBox> &ref, int trackId)
|
||||
{
|
||||
auto moof = std::make_unique<Moof>();
|
||||
auto box = readBox();
|
||||
moof->mfgdSequenceNumber = parseMfhd();
|
||||
ensureBox(box);
|
||||
|
||||
box = untilBox(ref, ATOM_TRAF);
|
||||
while (box)
|
||||
{
|
||||
moof->traf = parseTraf(box, trackId);
|
||||
ensureBox(box);
|
||||
|
||||
if (moof->traf->tfdt != -1)
|
||||
{
|
||||
return moof;
|
||||
}
|
||||
box = untilBox(ref, ATOM_TRAF);
|
||||
}
|
||||
|
||||
return moof;
|
||||
}
|
||||
|
||||
std::unique_ptr<Traf> MpegDashDemuxer::parseTraf(std::unique_ptr<MpegBox> &ref, int trackId)
|
||||
{
|
||||
auto traf = std::make_unique<Traf>();
|
||||
auto box = readBox();
|
||||
traf->tfhd = parseTfhd(trackId);
|
||||
ensureBox(box);
|
||||
|
||||
box = untilBox(ref, ATOM_TRUN, ATOM_TFDT);
|
||||
|
||||
if (box->type == ATOM_TFDT)
|
||||
{
|
||||
traf->tfdt = parseTfdt();
|
||||
ensureBox(box);
|
||||
box = readBox();
|
||||
}
|
||||
|
||||
traf->trun = parseTrun();
|
||||
ensureBox(box);
|
||||
|
||||
return traf;
|
||||
}
|
||||
|
||||
std::unique_ptr<Trun> MpegDashDemuxer::parseTrun()
|
||||
{
|
||||
auto trun = std::make_unique<Trun>();
|
||||
trun->bFlags = reader->readInt();
|
||||
trun->entryCount = reader->readInt();
|
||||
|
||||
trun->entriesRowSize = 0;
|
||||
|
||||
if (hasFlag(trun->bFlags, 0x0100))
|
||||
{
|
||||
trun->entriesRowSize += 4;
|
||||
}
|
||||
if (hasFlag(trun->bFlags, 0x0200))
|
||||
{
|
||||
trun->entriesRowSize += 4;
|
||||
}
|
||||
if (hasFlag(trun->bFlags, 0x0400))
|
||||
{
|
||||
trun->entriesRowSize += 4;
|
||||
}
|
||||
if (hasFlag(trun->bFlags, 0x0800))
|
||||
{
|
||||
trun->entriesRowSize += 4;
|
||||
}
|
||||
|
||||
if (hasFlag(trun->bFlags, 0x0001))
|
||||
{
|
||||
trun->dataOffset = reader->readInt();
|
||||
}
|
||||
|
||||
if (hasFlag(trun->bFlags, 0x0004))
|
||||
{
|
||||
trun->bFirstSampleFlags = reader->readInt();
|
||||
}
|
||||
|
||||
trun->bEntries = reader->readBytes(trun->entriesRowSize * trun->entryCount);
|
||||
trun->chunkSize = 0;
|
||||
|
||||
for (int i = 0; i < trun->entryCount; i++)
|
||||
{
|
||||
auto entry = getTrunEntry(trun, i);
|
||||
if (hasFlag(trun->bFlags, 0x0100))
|
||||
{
|
||||
trun->chunkDuration += entry->sampleDuration;
|
||||
}
|
||||
|
||||
if (hasFlag(trun->bFlags, 0x200))
|
||||
{
|
||||
|
||||
trun->chunkSize += entry->sampleSize;
|
||||
}
|
||||
|
||||
if (hasFlag(trun->bFlags, 0x0800))
|
||||
{
|
||||
if (!hasFlag(trun->bFlags, 0x0100))
|
||||
{
|
||||
trun->chunkDuration += entry->sampleCompositionTimeOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return trun;
|
||||
}
|
||||
|
||||
std::unique_ptr<TrunEntry> MpegDashDemuxer::getTrunEntry(std::unique_ptr<Trun> &trun, int i)
|
||||
{
|
||||
std::vector<uint8_t> subBuffer(
|
||||
trun->bEntries.begin() + (i * trun->entriesRowSize),
|
||||
trun->bEntries.begin() + ((i + 1) * trun->entriesRowSize));
|
||||
|
||||
auto entry = std::make_unique<TrunEntry>();
|
||||
|
||||
if (hasFlag(trun->bFlags, 0x0100))
|
||||
{
|
||||
entry->sampleDuration = readIntFromVector(subBuffer, 0);
|
||||
}
|
||||
|
||||
if (hasFlag(trun->bFlags, 0x0200))
|
||||
{
|
||||
entry->sampleSize = readIntFromVector(subBuffer, 0);
|
||||
}
|
||||
|
||||
if (hasFlag(trun->bFlags, 0x0400))
|
||||
{
|
||||
entry->sampleFlags = readIntFromVector(subBuffer, 0);
|
||||
}
|
||||
|
||||
if (hasFlag(trun->bFlags, 0x800))
|
||||
{
|
||||
entry->sampleCompositionTimeOffset = readIntFromVector(subBuffer, 0);
|
||||
}
|
||||
|
||||
entry->hasCompositionTimeOffset = hasFlag(trun->bFlags, 0x0800);
|
||||
entry->isKeyframe = !hasFlag(entry->sampleFlags, 0x10000);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
std::unique_ptr<TrunEntry> MpegDashDemuxer::getAbsoluteTrunEntry(std::unique_ptr<Trun> &trun, int i, std::unique_ptr<Tfhd> &header)
|
||||
{
|
||||
std::unique_ptr<TrunEntry> entry = getTrunEntry(trun, i);
|
||||
if (!hasFlag(trun->bFlags, 0x0100) && hasFlag(header->bFlags, 0x20))
|
||||
{
|
||||
entry->sampleFlags = header->defaultSampleFlags;
|
||||
}
|
||||
|
||||
if (!hasFlag(trun->bFlags, 0x0200) && hasFlag(header->bFlags, 0x10))
|
||||
{
|
||||
entry->sampleSize = header->defaultSampleSize;
|
||||
}
|
||||
|
||||
if (!hasFlag(trun->bFlags, 0x0100) && hasFlag(header->bFlags, 0x08))
|
||||
{
|
||||
entry->sampleDuration = header->defaultSampleDuration;
|
||||
}
|
||||
|
||||
if (i == 0 && hasFlag(trun->bFlags, 0x0004))
|
||||
{
|
||||
entry->sampleFlags = trun->bFirstSampleFlags;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
std::unique_ptr<Tfhd> MpegDashDemuxer::parseTfhd(int trackId)
|
||||
{
|
||||
auto tfhd = std::make_unique<Tfhd>();
|
||||
tfhd->bFlags = reader->readInt();
|
||||
tfhd->trackId = reader->readInt();
|
||||
|
||||
if (trackId != -1 && tfhd->trackId != trackId)
|
||||
{
|
||||
return tfhd;
|
||||
}
|
||||
|
||||
if (hasFlag(tfhd->bFlags, 0x01))
|
||||
{
|
||||
reader->skip(8);
|
||||
}
|
||||
|
||||
if (hasFlag(tfhd->bFlags, 0x02))
|
||||
{
|
||||
reader->skip(4);
|
||||
}
|
||||
|
||||
if (hasFlag(tfhd->bFlags, 0x08))
|
||||
{
|
||||
tfhd->defaultSampleDuration = reader->readInt();
|
||||
}
|
||||
|
||||
if (hasFlag(tfhd->bFlags, 0x10))
|
||||
{
|
||||
tfhd->defaultSampleSize = reader->readInt();
|
||||
}
|
||||
|
||||
if (hasFlag(tfhd->bFlags, 0x20))
|
||||
{
|
||||
tfhd->defaultSampleFlags = reader->readInt();
|
||||
}
|
||||
|
||||
return tfhd;
|
||||
}
|
||||
|
||||
bool MpegDashDemuxer::hasFlag(int flags, int mask)
|
||||
{
|
||||
return (flags & mask) == mask;
|
||||
}
|
||||
|
||||
std::unique_ptr<Moov> MpegDashDemuxer::parseMoov(std::unique_ptr<MpegBox> &ref)
|
||||
{
|
||||
auto moov = std::make_unique<Moov>();
|
||||
|
||||
auto box = readBox();
|
||||
|
||||
moov->mvhd = parseMvhd();
|
||||
moov->mvexTrex = std::vector<Trex>();
|
||||
ensureBox(box);
|
||||
|
||||
moov->trak = std::vector<std::unique_ptr<Trak>>();
|
||||
|
||||
box = untilBox(ref, ATOM_TRAK, ATOM_MVEX);
|
||||
while (box)
|
||||
{
|
||||
|
||||
if (box->type == ATOM_TRAK)
|
||||
{
|
||||
moov->trak.push_back(parseTrak(box));
|
||||
}
|
||||
|
||||
if (box->type == ATOM_MVEX)
|
||||
{
|
||||
moov->mvexTrex = parseMvex(box, moov->mvhd->nextTrackId);
|
||||
}
|
||||
|
||||
ensureBox(box);
|
||||
box = untilBox(ref, ATOM_TRAK, ATOM_MVEX);
|
||||
}
|
||||
|
||||
return moov;
|
||||
}
|
||||
|
||||
Trex MpegDashDemuxer::parseTrex()
|
||||
{
|
||||
reader->skip(4);
|
||||
Trex trex;
|
||||
trex.trackId = reader->readInt();
|
||||
trex.defaultSampleDescriptionIndex = reader->readInt();
|
||||
trex.defaultSampleDuration = reader->readInt();
|
||||
trex.defaultSampleSize = reader->readInt();
|
||||
trex.defaultSampleFlags = reader->readInt();
|
||||
|
||||
return trex;
|
||||
}
|
||||
|
||||
std::vector<Trex> MpegDashDemuxer::parseMvex(std::unique_ptr<MpegBox> &ref, int possibleTrackCount)
|
||||
{
|
||||
auto trexs = std::vector<Trex>();
|
||||
|
||||
auto box = untilBox(ref, ATOM_TREX);
|
||||
while (box)
|
||||
{
|
||||
trexs.push_back(parseTrex());
|
||||
ensureBox(box);
|
||||
box = untilBox(ref, ATOM_TREX);
|
||||
}
|
||||
|
||||
return trexs;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> MpegDashDemuxer::readFullBox(std::unique_ptr<MpegBox> &ref)
|
||||
{
|
||||
auto size = ref->size;
|
||||
std::vector<uint8_t> header(8);
|
||||
header.at(0) = *((uint8_t *)&ref->size + 0);
|
||||
header.at(1) = *((uint8_t *)&ref->size + 1);
|
||||
header.at(2) = *((uint8_t *)&ref->size + 2);
|
||||
header.at(3) = *((uint8_t *)&ref->size + 3);
|
||||
header.at(4) = *((uint8_t *)&ref->type + 0);
|
||||
header.at(5) = *((uint8_t *)&ref->type + 1);
|
||||
header.at(6) = *((uint8_t *)&ref->type + 2);
|
||||
header.at(7) = *((uint8_t *)&ref->type + 3);
|
||||
|
||||
std::vector<uint8_t> data = reader->readBytes(size - 8);
|
||||
|
||||
header.insert(header.end(), data.begin(), data.end());
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
std::unique_ptr<Tkhd> MpegDashDemuxer::parseTkhd()
|
||||
{
|
||||
auto tkhd = std::make_unique<Tkhd>();
|
||||
uint8_t version = reader->readByte();
|
||||
|
||||
// flags
|
||||
// creation entries_time
|
||||
// modification entries_time
|
||||
reader->skip(3 + (2 * (version == 0 ? 4 : 8)));
|
||||
tkhd->trackId = reader->readInt();
|
||||
reader->skip(4);
|
||||
tkhd->duration = version == 0 ? reader->readUInt() : reader->readLong();
|
||||
reader->skip(2 * 4);
|
||||
tkhd->bLayer = reader->readShort();
|
||||
tkhd->bAlternateGroup = reader->readShort();
|
||||
tkhd->bVolume = reader->readShort();
|
||||
|
||||
reader->skip(2);
|
||||
tkhd->matrix = reader->readBytes(9 * 4);
|
||||
tkhd->bWidth = reader->readInt();
|
||||
tkhd->bHeight = reader->readInt();
|
||||
|
||||
return tkhd;
|
||||
}
|
||||
|
||||
std::unique_ptr<Trak> MpegDashDemuxer::parseTrak(std::unique_ptr<MpegBox> &ref)
|
||||
{
|
||||
auto trak = std::make_unique<Trak>();
|
||||
auto box = readBox();
|
||||
trak->tkhd = parseTkhd();
|
||||
ensureBox(box);
|
||||
|
||||
box = untilBox(ref, ATOM_MDIA, ATOM_EDTS);
|
||||
while (box)
|
||||
{
|
||||
if (box->type == ATOM_MDIA)
|
||||
{
|
||||
trak->mdia = parseMdia(box);
|
||||
}
|
||||
|
||||
if (box->type == ATOM_EDTS)
|
||||
{
|
||||
trak->edstElst = parseEdts(box);
|
||||
}
|
||||
|
||||
ensureBox(box);
|
||||
box = untilBox(ref, ATOM_MDIA, ATOM_EDTS);
|
||||
}
|
||||
|
||||
return trak;
|
||||
}
|
||||
|
||||
std::unique_ptr<Minf> MpegDashDemuxer::parseMinf(std::unique_ptr<MpegBox> &ref)
|
||||
{
|
||||
auto minf = std::make_unique<Minf>();
|
||||
auto box = untilAnyBox(ref);
|
||||
while (box)
|
||||
{
|
||||
if (box->type == ATOM_DINF)
|
||||
{
|
||||
minf->dinf = readFullBox(box);
|
||||
}
|
||||
|
||||
if (box->type == ATOM_STBL)
|
||||
{
|
||||
minf->stblStsd = parseStbl(box);
|
||||
}
|
||||
if (box->type == ATOM_VMHD || box->type == ATOM_SMHD)
|
||||
{
|
||||
minf->mhd = readFullBox(box);
|
||||
}
|
||||
|
||||
ensureBox(box);
|
||||
box = untilAnyBox(ref);
|
||||
}
|
||||
|
||||
return minf;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> MpegDashDemuxer::parseStbl(std::unique_ptr<MpegBox> &ref)
|
||||
{
|
||||
auto box = untilBox(ref, ATOM_STSD);
|
||||
|
||||
return readFullBox(box);
|
||||
}
|
||||
|
||||
std::unique_ptr<Elst> MpegDashDemuxer::parseEdts(std::unique_ptr<MpegBox> &ref)
|
||||
{
|
||||
auto elst = std::make_unique<Elst>();
|
||||
auto box = untilBox(ref, ATOM_ELST);
|
||||
|
||||
bool v1 = reader->readByte() == 1;
|
||||
reader->skip(3); // flags
|
||||
|
||||
int entryCount = reader->readInt();
|
||||
if (entryCount < 1)
|
||||
{
|
||||
elst->bMediaRate = 0x00010000;
|
||||
return elst;
|
||||
}
|
||||
|
||||
if (v1)
|
||||
{
|
||||
reader->skip(8); // duration
|
||||
elst->mediaTime = reader->readLong();
|
||||
// ignore all entries
|
||||
reader->skip((entryCount - 1) * 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
reader->skip(4); // segment duration
|
||||
elst->mediaTime = reader->readInt();
|
||||
}
|
||||
|
||||
elst->bMediaRate = reader->readInt();
|
||||
return elst;
|
||||
}
|
||||
|
||||
std::unique_ptr<Hdlr> MpegDashDemuxer::parseHdlr(std::unique_ptr<MpegBox> &box)
|
||||
{
|
||||
auto hdlr = std::make_unique<Hdlr>();
|
||||
reader->skip(4);
|
||||
|
||||
hdlr->type = reader->readInt();
|
||||
hdlr->subType = reader->readInt();
|
||||
hdlr->bReserved = reader->readBytes(12);
|
||||
reader->skip((box->offset + box->size) - reader->position());
|
||||
return hdlr;
|
||||
}
|
||||
|
||||
std::unique_ptr<Mdia> MpegDashDemuxer::parseMdia(std::unique_ptr<MpegBox> &ref)
|
||||
{
|
||||
auto mdia = std::make_unique<Mdia>();
|
||||
|
||||
auto box = untilBox(ref, ATOM_MDHD, ATOM_HDLR, ATOM_MINF);
|
||||
while (box)
|
||||
{
|
||||
if (box->type == ATOM_MDHD)
|
||||
{
|
||||
mdia->mdhd = readFullBox(box);
|
||||
}
|
||||
|
||||
if (box->type == ATOM_HDLR)
|
||||
{
|
||||
mdia->hdlr = parseHdlr(box);
|
||||
}
|
||||
|
||||
if (box->type == ATOM_MINF)
|
||||
{
|
||||
mdia->minf = parseMinf(box);
|
||||
}
|
||||
|
||||
ensureBox(box);
|
||||
box = untilBox(ref, ATOM_MDHD, ATOM_HDLR, ATOM_MINF);
|
||||
}
|
||||
|
||||
return mdia;
|
||||
}
|
||||
|
||||
std::unique_ptr<Mvhd> MpegDashDemuxer::parseMvhd()
|
||||
{
|
||||
auto mvhd = std::make_unique<Mvhd>();
|
||||
uint8_t version = reader->readByte();
|
||||
reader->skip(3); // flags
|
||||
|
||||
// creation entries_time
|
||||
// modification entries_time
|
||||
reader->skip(2 * (version == 0 ? 4 : 8));
|
||||
|
||||
mvhd->timeScale = reader->readUInt();
|
||||
|
||||
// chunkDuration
|
||||
reader->skip(version == 0 ? 4 : 8);
|
||||
|
||||
// rate
|
||||
// volume
|
||||
// reserved
|
||||
// matrix array
|
||||
// predefined
|
||||
reader->skip(76);
|
||||
|
||||
mvhd->nextTrackId = reader->readUInt();
|
||||
|
||||
return mvhd;
|
||||
}
|
||||
|
||||
void MpegDashDemuxer::ensureBox(std::unique_ptr<MpegBox> &box)
|
||||
{
|
||||
reader->skip(box->offset + box->size - reader->position());
|
||||
}
|
||||
|
||||
std::unique_ptr<MpegBox> MpegDashDemuxer::readBox()
|
||||
{
|
||||
auto box = std::make_unique<MpegBox>();
|
||||
box->offset = reader->position();
|
||||
box->size = reader->readUInt();
|
||||
box->type = reader->readInt();
|
||||
if (box->size == 1)
|
||||
{
|
||||
box->size = reader->readLong();
|
||||
}
|
||||
|
||||
return box;
|
||||
}
|
||||
|
||||
std::unique_ptr<MpegBox> MpegDashDemuxer::untilBox(std::unique_ptr<MpegBox> &ref, int boxType1, int boxType2, int boxType3)
|
||||
{
|
||||
auto box = std::make_unique<MpegBox>();
|
||||
while (reader->position() < (ref->offset + ref->size))
|
||||
{
|
||||
box = readBox();
|
||||
if (box->type == boxType1 || box->type == boxType2 || box->type == boxType3)
|
||||
{
|
||||
return box;
|
||||
}
|
||||
ensureBox(box);
|
||||
}
|
||||
|
||||
return std::unique_ptr<MpegBox>(nullptr);
|
||||
}
|
||||
|
||||
std::unique_ptr<MpegBox> MpegDashDemuxer::untilBox(std::unique_ptr<MpegBox> &ref, int boxType1, int boxType2)
|
||||
{
|
||||
auto box = std::make_unique<MpegBox>();
|
||||
while (reader->position() < (ref->offset + ref->size))
|
||||
{
|
||||
box = readBox();
|
||||
if (box->type == boxType1 || box->type == boxType2)
|
||||
{
|
||||
return box;
|
||||
}
|
||||
ensureBox(box);
|
||||
}
|
||||
|
||||
return std::unique_ptr<MpegBox>(nullptr);
|
||||
}
|
||||
|
||||
std::unique_ptr<MpegBox> MpegDashDemuxer::untilBox(std::unique_ptr<MpegBox> &ref, int boxType1)
|
||||
{
|
||||
auto box = std::make_unique<MpegBox>();
|
||||
|
||||
while (reader->position() < (ref->offset + ref->size))
|
||||
{
|
||||
box = readBox();
|
||||
if (box->type == boxType1)
|
||||
{
|
||||
return box;
|
||||
}
|
||||
ensureBox(box);
|
||||
}
|
||||
|
||||
return std::unique_ptr<MpegBox>(nullptr);
|
||||
}
|
||||
|
||||
std::unique_ptr<MpegBox> MpegDashDemuxer::untilAnyBox(std::unique_ptr<MpegBox> &ref)
|
||||
{
|
||||
if (reader->position() >= (ref->offset + ref->size))
|
||||
{
|
||||
return std::unique_ptr<MpegBox>(nullptr);
|
||||
}
|
||||
|
||||
return readBox();
|
||||
}
|
||||
87
components/spotify/cspot/bell/src/asm/biquad_f32_ae32.S
Normal file
87
components/spotify/cspot/bell/src/asm/biquad_f32_ae32.S
Normal file
@@ -0,0 +1,87 @@
|
||||
// Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "esp_platform.h"
|
||||
|
||||
// This is bi quad filter form II for ESP32 processor.
|
||||
.text
|
||||
.align 4
|
||||
.global dsps_biquad_f32_ae32
|
||||
.type dsps_biquad_f32_ae32,@function
|
||||
// The function implements the following C code:
|
||||
//esp_err_t dsps_biquad_f32_ae32(const float* input, float* output, int len, float* coef, float* w)
|
||||
// {
|
||||
// for (int i=0 ; i< len ; i++)
|
||||
// {
|
||||
// float d0 = input[i] - coef[3]*w[0] - coef[4]*w[1]; (input[i] - a[1]*w[0] - a[2]*w[1];)
|
||||
// output[i] = coef[0]*d0 + coef[1]*w[0] + coef[2]*w[1];
|
||||
// w[1] = w[0];
|
||||
// w[0] = d0;
|
||||
// }
|
||||
// return ESP_OK;
|
||||
// }
|
||||
|
||||
dsps_biquad_f32_ae32:
|
||||
// input - a2
|
||||
// output - a3
|
||||
// len - a4
|
||||
// coeffs - a5
|
||||
// w- a6
|
||||
|
||||
// f0 - b0
|
||||
// f1 - b1
|
||||
// f2 - b2
|
||||
// f3 - a1
|
||||
// f4 - a2
|
||||
|
||||
// f5 - w0
|
||||
// f6 - w1
|
||||
|
||||
entry a1, 16
|
||||
// Array increment for floating point data should be 4
|
||||
lsi f0, a5, 0
|
||||
lsi f1, a5, 4
|
||||
lsi f2, a5, 8
|
||||
lsi f3, a5, 12
|
||||
lsi f4, a5, 16
|
||||
|
||||
|
||||
neg.s f5, f3 // -a[1]
|
||||
neg.s f6, f4 // -a[2]
|
||||
|
||||
lsi f7, a6, 0 // w[0]
|
||||
lsi f8, a6, 4 // w[1]
|
||||
|
||||
addi a3, a3, -4 // i-- // preset a3
|
||||
lsi f9, a2, 0 // f9 = x[i]
|
||||
loopnez a4, loop_bq_end_m_ae32
|
||||
madd.s f9, f7, f5 // f9 += -a1*w0
|
||||
addi a3, a3, 4 // out++;
|
||||
mul.s f10, f1, f7 // f10 = b1*w0
|
||||
madd.s f9, f8, f6 // f9 += -a2*w1
|
||||
madd.s f10, f9, f0 // f10 += b0*d0
|
||||
addi a2, a2, 4 // in++;
|
||||
madd.s f10, f2, f8 // f10+= b2*w1, f10 - result
|
||||
mov.s f8, f7 // w1 = w0
|
||||
mov.s f7, f9 // w0 = d0
|
||||
lsi f9, a2, 0 // f9 = x[i]
|
||||
ssi f10, a3, 0 // y[i] = result
|
||||
loop_bq_end_m_ae32:
|
||||
// Store delay line
|
||||
ssi f7, a6, 0
|
||||
ssi f8, a6, 4
|
||||
|
||||
movi.n a2, 0 // return status ESP_OK
|
||||
retw.n
|
||||
|
||||
BIN
components/spotify/cspot/bell/src/platform/.DS_Store
vendored
Normal file
BIN
components/spotify/cspot/bell/src/platform/.DS_Store
vendored
Normal file
Binary file not shown.
@@ -0,0 +1,29 @@
|
||||
#include "platform/WrappedSemaphore.h"
|
||||
|
||||
WrappedSemaphore::WrappedSemaphore(int count)
|
||||
{
|
||||
semaphoreHandle = dispatch_semaphore_create(0);
|
||||
}
|
||||
|
||||
WrappedSemaphore::~WrappedSemaphore()
|
||||
{
|
||||
dispatch_release(semaphoreHandle);
|
||||
}
|
||||
|
||||
int WrappedSemaphore::wait()
|
||||
{
|
||||
|
||||
return dispatch_semaphore_wait(semaphoreHandle, DISPATCH_TIME_FOREVER);
|
||||
}
|
||||
|
||||
int WrappedSemaphore::twait(long milliseconds)
|
||||
{
|
||||
dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (NSEC_PER_SEC / 1000) * milliseconds);
|
||||
|
||||
return dispatch_semaphore_wait(semaphoreHandle, timeout);
|
||||
}
|
||||
|
||||
void WrappedSemaphore::give()
|
||||
{
|
||||
dispatch_semaphore_signal(semaphoreHandle);
|
||||
}
|
||||
92
components/spotify/cspot/bell/src/platform/esp/TLSSocket.cpp
Normal file
92
components/spotify/cspot/bell/src/platform/esp/TLSSocket.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
#include "platform/TLSSocket.h"
|
||||
|
||||
/**
|
||||
* Platform TLSSocket implementation for the mbedtls
|
||||
*/
|
||||
bell::TLSSocket::TLSSocket()
|
||||
{
|
||||
mbedtls_net_init(&server_fd);
|
||||
mbedtls_ssl_init(&ssl);
|
||||
mbedtls_ssl_config_init(&conf);
|
||||
mbedtls_ctr_drbg_init(&ctr_drbg);
|
||||
mbedtls_entropy_init(&entropy);
|
||||
const char *pers = "euphonium";
|
||||
int ret;
|
||||
if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
|
||||
(const unsigned char *)pers,
|
||||
strlen(pers))) != 0)
|
||||
{
|
||||
BELL_LOG(error, "http_tls",
|
||||
"failed\n ! mbedtls_ctr_drbg_seed returned %d\n", ret);
|
||||
}
|
||||
}
|
||||
|
||||
void bell::TLSSocket::open(std::string url)
|
||||
{
|
||||
// initialize
|
||||
int ret;
|
||||
url.erase(0, url.find("://") + 3);
|
||||
std::string hostUrl = url.substr(0, url.find('/'));
|
||||
std::string pathUrl = url.substr(url.find('/'));
|
||||
|
||||
std::string portString = "443";
|
||||
// check if hostUrl contains ':'
|
||||
if (hostUrl.find(':') != std::string::npos)
|
||||
{
|
||||
// split by ':'
|
||||
std::string host = hostUrl.substr(0, hostUrl.find(':'));
|
||||
portString = hostUrl.substr(hostUrl.find(':') + 1);
|
||||
hostUrl = host;
|
||||
}
|
||||
|
||||
if ((ret = mbedtls_net_connect(&server_fd, hostUrl.c_str(), "443",
|
||||
MBEDTLS_NET_PROTO_TCP)) != 0)
|
||||
{
|
||||
BELL_LOG(error, "http_tls", "failed! connect returned %d\n", ret);
|
||||
}
|
||||
|
||||
if ((ret = mbedtls_ssl_config_defaults(&conf, MBEDTLS_SSL_IS_CLIENT,
|
||||
MBEDTLS_SSL_TRANSPORT_STREAM,
|
||||
MBEDTLS_SSL_PRESET_DEFAULT)) != 0)
|
||||
{
|
||||
|
||||
BELL_LOG(error, "http_tls", "failed! config returned %d\n", ret);
|
||||
}
|
||||
|
||||
mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_NONE);
|
||||
mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg);
|
||||
mbedtls_ssl_setup(&ssl, &conf);
|
||||
if ((ret = mbedtls_ssl_set_hostname(&ssl, "Mbed TLS Server 1")) != 0)
|
||||
{
|
||||
BELL_LOG(info, "oh", "kocz");
|
||||
}
|
||||
mbedtls_ssl_set_bio(&ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv,
|
||||
NULL);
|
||||
|
||||
while ((ret = mbedtls_ssl_handshake(&ssl)) != 0)
|
||||
{
|
||||
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE)
|
||||
{
|
||||
BELL_LOG(error, "http_tls", "failed! config returned %d\n", ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t bell::TLSSocket::read(uint8_t *buf, size_t len)
|
||||
{
|
||||
return mbedtls_ssl_read(&ssl, buf, len);
|
||||
}
|
||||
|
||||
size_t bell::TLSSocket::write(uint8_t *buf, size_t len)
|
||||
{
|
||||
return mbedtls_ssl_write(&ssl, buf, len);
|
||||
}
|
||||
|
||||
void bell::TLSSocket::close()
|
||||
{
|
||||
mbedtls_net_free(&server_fd);
|
||||
mbedtls_ssl_free(&ssl);
|
||||
mbedtls_ssl_config_free(&conf);
|
||||
mbedtls_ctr_drbg_free(&ctr_drbg);
|
||||
mbedtls_entropy_free(&entropy);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#include "platform/WrappedSemaphore.h"
|
||||
|
||||
/**
|
||||
* Platform semaphopre implementation for the esp-idf.
|
||||
*/
|
||||
|
||||
WrappedSemaphore::WrappedSemaphore(int count)
|
||||
{
|
||||
semaphoreHandle = xSemaphoreCreateCounting(count, 0);
|
||||
}
|
||||
|
||||
WrappedSemaphore::~WrappedSemaphore()
|
||||
{
|
||||
vSemaphoreDelete(semaphoreHandle);
|
||||
}
|
||||
|
||||
int WrappedSemaphore::wait()
|
||||
{
|
||||
if (xSemaphoreTake(semaphoreHandle, portMAX_DELAY) == pdTRUE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int WrappedSemaphore::twait(long milliseconds)
|
||||
{
|
||||
if (xSemaphoreTake(semaphoreHandle, milliseconds / portTICK_PERIOD_MS) == pdTRUE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void WrappedSemaphore::give()
|
||||
{
|
||||
|
||||
xSemaphoreGive(semaphoreHandle);
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
#include "platform/TLSSocket.h"
|
||||
|
||||
/**
|
||||
* Platform TLSSocket implementation for the openssl
|
||||
*/
|
||||
|
||||
bell::TLSSocket::TLSSocket() {
|
||||
ERR_load_crypto_strings();
|
||||
ERR_load_SSL_strings();
|
||||
OpenSSL_add_all_algorithms();
|
||||
ctx = SSL_CTX_new(SSLv23_client_method());
|
||||
}
|
||||
|
||||
void bell::TLSSocket::open(std::string url) {
|
||||
|
||||
/* We'd normally set some stuff like the verify paths and
|
||||
* mode here because as things stand this will connect to
|
||||
* any server whose certificate is signed by any CA.
|
||||
*/
|
||||
|
||||
sbio = BIO_new_ssl_connect(ctx);
|
||||
|
||||
BIO_get_ssl(sbio, &ssl);
|
||||
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
|
||||
|
||||
url.erase(0, url.find("://") + 3);
|
||||
std::string hostUrl = url.substr(0, url.find('/'));
|
||||
std::string pathUrl = url.substr(url.find('/'));
|
||||
|
||||
std::string portString = "443";
|
||||
// check if hostUrl contains ':'
|
||||
if (hostUrl.find(':') != std::string::npos) {
|
||||
// split by ':'
|
||||
std::string host = hostUrl.substr(0, hostUrl.find(':'));
|
||||
portString = hostUrl.substr(hostUrl.find(':') + 1);
|
||||
hostUrl = host;
|
||||
}
|
||||
|
||||
BELL_LOG(info, "http_tls", "Connecting with %s", hostUrl.c_str());
|
||||
BIO_set_conn_hostname(sbio, std::string(hostUrl + ":443").c_str());
|
||||
|
||||
out = BIO_new_fp(stdout, BIO_NOCLOSE);
|
||||
if (BIO_do_connect(sbio) <= 0) {
|
||||
BELL_LOG(error, "http_tls", "Error connecting with server");
|
||||
/* whatever ... */
|
||||
}
|
||||
|
||||
if (BIO_do_handshake(sbio) <= 0) {
|
||||
|
||||
BELL_LOG(error, "http_tls", "Error TLS connection");
|
||||
|
||||
/* whatever ... */
|
||||
} // remove https or http from url
|
||||
|
||||
// split by first "/" in url
|
||||
}
|
||||
|
||||
size_t bell::TLSSocket::read(uint8_t *buf, size_t len) {
|
||||
return BIO_read(sbio, buf, len);
|
||||
}
|
||||
|
||||
size_t bell::TLSSocket::write(uint8_t *buf, size_t len) {
|
||||
return BIO_write(sbio, buf, len);
|
||||
}
|
||||
|
||||
void bell::TLSSocket::close() {
|
||||
if (!isClosed) {
|
||||
BIO_free_all(sbio);
|
||||
BIO_free(out);
|
||||
isClosed = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "platform/WrappedSemaphore.h"
|
||||
|
||||
WrappedSemaphore::WrappedSemaphore(int count)
|
||||
{
|
||||
sem_init(&this->semaphoreHandle, 0, 0); // eek pointer
|
||||
}
|
||||
|
||||
WrappedSemaphore::~WrappedSemaphore()
|
||||
{
|
||||
sem_destroy(&this->semaphoreHandle);
|
||||
}
|
||||
|
||||
int WrappedSemaphore::wait()
|
||||
{
|
||||
sem_wait(&this->semaphoreHandle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int WrappedSemaphore::twait(long milliseconds)
|
||||
{
|
||||
// wait on semaphore with timeout
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
ts.tv_nsec += (milliseconds % 1000) * 1000000;
|
||||
return sem_timedwait(&this->semaphoreHandle, &ts);
|
||||
}
|
||||
|
||||
void WrappedSemaphore::give()
|
||||
{
|
||||
sem_post(&this->semaphoreHandle);
|
||||
}
|
||||
Reference in New Issue
Block a user