mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-09 13:07:03 +03:00
big merge
This commit is contained in:
21
components/spotify/cspot/include/ApResolve.h
Normal file
21
components/spotify/cspot/include/ApResolve.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef APRESOLVE_H
|
||||
#define APRESOLVE_H
|
||||
|
||||
#include <string>
|
||||
|
||||
class ApResolve {
|
||||
private:
|
||||
std::string getApList();
|
||||
|
||||
public:
|
||||
ApResolve();
|
||||
|
||||
/**
|
||||
* @brief Connects to spotify's servers and returns first valid ap address
|
||||
*
|
||||
* @return std::string Address in form of url:port
|
||||
*/
|
||||
std::string fetchFirstApAddress();
|
||||
};
|
||||
|
||||
#endif
|
||||
74
components/spotify/cspot/include/AudioChunk.h
Normal file
74
components/spotify/cspot/include/AudioChunk.h
Normal file
@@ -0,0 +1,74 @@
|
||||
#ifndef AUDIOCHUNK_H
|
||||
#define AUDIOCHUNK_H
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include "pthread.h"
|
||||
#include "platform/WrappedSemaphore.h"
|
||||
#include "Crypto.h"
|
||||
#include "Utils.h"
|
||||
#include <mutex>
|
||||
|
||||
class AudioChunk {
|
||||
private:
|
||||
/**
|
||||
* @brief Calculates a correct IV by performing bignum addition.
|
||||
*
|
||||
* @param num Number to add to IV.
|
||||
* @return std::vector<uint8_t>
|
||||
*/
|
||||
std::vector<uint8_t> getIVSum(uint32_t num);
|
||||
|
||||
public:
|
||||
std::unique_ptr<Crypto> crypto;
|
||||
std::vector<uint8_t> decryptedData;
|
||||
std::vector<uint8_t> audioKey;
|
||||
bool keepInMemory = false;
|
||||
pthread_mutex_t loadingMutex;
|
||||
std::mutex dataAccessMutex;
|
||||
uint32_t startPosition;
|
||||
uint32_t endPosition;
|
||||
uint16_t seqId;
|
||||
|
||||
size_t headerFileSize = -1;
|
||||
bool isLoaded = false;
|
||||
bool isFailed = false;
|
||||
|
||||
/**
|
||||
* @brief Triggered when audiochunk is fully downloaded and decrypted.
|
||||
*/
|
||||
std::unique_ptr<WrappedSemaphore> isLoadedSemaphore;
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*/
|
||||
std::unique_ptr<WrappedSemaphore> isHeaderFileSizeLoadedSemaphore;
|
||||
|
||||
|
||||
/**
|
||||
* @brief AudioChunk handles all audiochunk related operations.
|
||||
*
|
||||
* @param seqId Sequence id of requested chunk
|
||||
* @param audioKey Audio key used for decryption of audio data
|
||||
* @param startPosition Start position of current chunk in audio file
|
||||
* @param predictedEndPosition Predicted end position of given chunk. This is not final positon.
|
||||
*/
|
||||
AudioChunk(uint16_t seqId, std::vector<uint8_t> &audioKey, uint32_t startPosition, uint32_t predictedEndPosition);
|
||||
~AudioChunk();
|
||||
|
||||
/**
|
||||
* @brief Appends incoming chunked data to local cache.
|
||||
*
|
||||
* @param data encrypted binary audio data.
|
||||
*/
|
||||
void appendData(std::vector<uint8_t> &data);
|
||||
|
||||
/**
|
||||
* @brief Performs AES CTR decryption of received data.
|
||||
*
|
||||
*/
|
||||
void decrypt();
|
||||
};
|
||||
|
||||
#endif
|
||||
57
components/spotify/cspot/include/AudioChunkManager.h
Normal file
57
components/spotify/cspot/include/AudioChunkManager.h
Normal file
@@ -0,0 +1,57 @@
|
||||
#ifndef AUDIOCHUNKMANAGER_H
|
||||
#define AUDIOCHUNKMANAGER_H
|
||||
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
#include "Utils.h"
|
||||
#include "AudioChunk.h"
|
||||
#include "Queue.h"
|
||||
#include "Task.h"
|
||||
|
||||
#define DATA_SIZE_HEADER 24
|
||||
#define DATA_SIZE_FOOTER 2
|
||||
|
||||
class AudioChunkManager : public bell::Task {
|
||||
std::vector<std::shared_ptr<AudioChunk>> chunks;
|
||||
bell::Queue<std::pair<std::vector<uint8_t>, bool>> audioChunkDataQueue;
|
||||
void runTask();
|
||||
public:
|
||||
AudioChunkManager();
|
||||
std::atomic<bool> isRunning = false;
|
||||
std::mutex runningMutex;
|
||||
/**
|
||||
* @brief Registers a new audio chunk request.
|
||||
*
|
||||
* Registering an audiochunk will trigger a request to spotify servers.
|
||||
* All the incoming data will be redirected to this given audiochunk.
|
||||
*
|
||||
* @param seqId sequence identifier of given audio chunk.
|
||||
* @param audioKey audio key of given file, used for decryption.
|
||||
* @param startPos start position of audio chunk
|
||||
* @param endPos end position of audio chunk. end - pos % 4 must be 0.
|
||||
* @return std::shared_ptr<AudioChunk> registered audio chunk. Does not contain the data yet.
|
||||
*/
|
||||
std::shared_ptr<AudioChunk> registerNewChunk(uint16_t seqId, std::vector<uint8_t> &audioKey, uint32_t startPos, uint32_t endPos);
|
||||
|
||||
/**
|
||||
* @brief Pushes binary data from spotify's servers containing audio chunks.
|
||||
*
|
||||
* This method pushes received data to a queue that is then received by manager's thread.
|
||||
* That thread parses the data and passes it to a matching audio chunk.
|
||||
*
|
||||
* @param data binary data received from spotify's servers
|
||||
* @param failed whenever given chunk request failed
|
||||
*/
|
||||
void handleChunkData(std::vector<uint8_t>& data, bool failed = false);
|
||||
|
||||
/**
|
||||
* @brief Fails all requested chunks, used for reconnection.
|
||||
*/
|
||||
void failAllChunks();
|
||||
|
||||
void close();
|
||||
};
|
||||
|
||||
#endif
|
||||
18
components/spotify/cspot/include/AudioSink.h
Normal file
18
components/spotify/cspot/include/AudioSink.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef AUDIOSINK_H
|
||||
#define AUDIOSINK_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
class AudioSink
|
||||
{
|
||||
public:
|
||||
AudioSink() {}
|
||||
virtual ~AudioSink() {}
|
||||
virtual void feedPCMFrames(std::vector<uint8_t> &data) = 0;
|
||||
virtual void volumeChanged(uint16_t volume) {}
|
||||
bool softwareVolumeControl = true;
|
||||
bool usign = false;
|
||||
};
|
||||
|
||||
#endif
|
||||
75
components/spotify/cspot/include/ChunkedAudioStream.h
Normal file
75
components/spotify/cspot/include/ChunkedAudioStream.h
Normal file
@@ -0,0 +1,75 @@
|
||||
#ifndef CHUNKEDAUDIOSTREAM_H
|
||||
#define CHUNKEDAUDIOSTREAM_H
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
#include <array>
|
||||
#include <unistd.h>
|
||||
#include <atomic>
|
||||
#include "ivorbisfile.h"
|
||||
#include "MercuryManager.h"
|
||||
#include "AudioSink.h"
|
||||
#include "AudioChunk.h"
|
||||
#include "platform/WrappedMutex.h"
|
||||
|
||||
#define SPOTIFY_HEADER_SIZE 167
|
||||
#define BUFFER_SIZE 0x20000 * 1.5
|
||||
typedef std::function<void(std::vector<uint8_t>&)> pcmDataCallback;
|
||||
|
||||
enum class Whence
|
||||
{
|
||||
START,
|
||||
CURRENT,
|
||||
END
|
||||
};
|
||||
|
||||
class ChunkedAudioStream
|
||||
{
|
||||
private:
|
||||
// Vorbis related
|
||||
OggVorbis_File vorbisFile;
|
||||
ov_callbacks vorbisCallbacks;
|
||||
int currentSection;
|
||||
|
||||
// Audio chunking
|
||||
std::vector<uint8_t> audioKey;
|
||||
std::vector<std::shared_ptr<AudioChunk>> chunks;
|
||||
|
||||
// Audio data
|
||||
uint32_t duration;
|
||||
|
||||
bool loadingChunks = false;
|
||||
uint16_t lastSequenceId = 0;
|
||||
|
||||
std::shared_ptr<MercuryManager> manager;
|
||||
std::vector<uint8_t> fileId;
|
||||
uint32_t startPositionMs;
|
||||
|
||||
std::shared_ptr<AudioChunk> requestChunk(size_t chunkIndex);
|
||||
void fetchTraillingPacket();
|
||||
std::shared_ptr<AudioChunk> findChunkForPosition(size_t position);
|
||||
|
||||
public:
|
||||
ChunkedAudioStream(std::vector<uint8_t> fileId, std::vector<uint8_t> audioKey, uint32_t duration, std::shared_ptr<MercuryManager> manager, uint32_t startPositionMs, bool isPaused);
|
||||
~ChunkedAudioStream();
|
||||
int requestedChunkIndex = 0;
|
||||
std::function<void()> streamFinishedCallback;
|
||||
size_t pos = SPOTIFY_HEADER_SIZE; // size of some spotify header
|
||||
uint32_t fileSize;
|
||||
uint32_t readBeforeSeek = 0;
|
||||
bool loadingMeta = true;
|
||||
std::atomic<bool> isPaused = false;
|
||||
std::atomic<bool> isRunning = false;
|
||||
std::atomic<bool> finished = false;
|
||||
pcmDataCallback pcmCallback;
|
||||
std::shared_ptr<AudioSink> audioSink;
|
||||
WrappedMutex seekMutex;
|
||||
|
||||
std::vector<uint8_t> read(size_t bytes);
|
||||
void seekMs(uint32_t positionMs);
|
||||
void seek(size_t pos, Whence whence);
|
||||
void startPlaybackLoop();
|
||||
};
|
||||
|
||||
#endif
|
||||
26
components/spotify/cspot/include/ConfigJSON.h
Normal file
26
components/spotify/cspot/include/ConfigJSON.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef CONFIGJSON_H
|
||||
#define CONFIGJSON_H
|
||||
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include "FileHelper.h"
|
||||
#include "ProtoHelper.h"
|
||||
|
||||
class ConfigJSON
|
||||
{
|
||||
private:
|
||||
std::shared_ptr<FileHelper> _file;
|
||||
std::string _jsonFileName;
|
||||
public:
|
||||
ConfigJSON(std::string jsonFileName, std::shared_ptr<FileHelper> file);
|
||||
bool load();
|
||||
bool save();
|
||||
|
||||
uint16_t volume;
|
||||
std::string deviceName;
|
||||
AudioFormat format;
|
||||
};
|
||||
|
||||
extern std::shared_ptr<ConfigJSON> configMan;
|
||||
|
||||
#endif
|
||||
15
components/spotify/cspot/include/ConstantParameters.h
Normal file
15
components/spotify/cspot/include/ConstantParameters.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef CONSTANTPARAMETERS_H
|
||||
#define CONSTANTPARAMETERS_H
|
||||
|
||||
#define MAX_VOLUME 65536
|
||||
|
||||
// Hardcoded information sent to spotify servers
|
||||
const char * const deviceId = "162137fd329622137a14901634264e6f332e2422";
|
||||
const char * const informationString = "cspot";
|
||||
const char * const brandName = "corn";
|
||||
const char * const versionString = "cspot-1.0";
|
||||
const char * const protocolVersion = "2.7.1";
|
||||
const char * const defaultDeviceName = "CSpot";
|
||||
const char * const swVersion = "1.0.0";
|
||||
|
||||
#endif
|
||||
16
components/spotify/cspot/include/CspotAssert.h
Normal file
16
components/spotify/cspot/include/CspotAssert.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef CSPOT_ASSERT_H
|
||||
#define CSPOT_ASSERT_H
|
||||
#include <stdio.h>
|
||||
#include <cassert>
|
||||
|
||||
#define CSPOT_ASSERT(CONDITION, MESSAGE) \
|
||||
do \
|
||||
{ \
|
||||
if (!(CONDITION)) \
|
||||
{ \
|
||||
printf("At %s in %s:%d\n Assertion %s failed: %s", __func__, __FILE__, __LINE__, #CONDITION, MESSAGE); \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
16
components/spotify/cspot/include/FileHelper.h
Normal file
16
components/spotify/cspot/include/FileHelper.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef FILEHELPER_H
|
||||
#define FILEHELPER_H
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
class FileHelper
|
||||
{
|
||||
public:
|
||||
FileHelper() {}
|
||||
virtual ~FileHelper() {}
|
||||
virtual bool readFile(std::string filename, std::string &fileContent) = 0;
|
||||
virtual bool writeFile(std::string filename, std::string fileContent) = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
465
components/spotify/cspot/include/Keyexchange.h
Normal file
465
components/spotify/cspot/include/Keyexchange.h
Normal file
@@ -0,0 +1,465 @@
|
||||
// AUTOGENERATED FILE, DO NOT EDIT BY HAND
|
||||
#ifndef PB_KEYEXCHANGE_H
|
||||
#define PB_KEYEXCHANGE_H
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <PbCommon.h>
|
||||
#include <PbWriter.h>
|
||||
#include <PbReader.h>
|
||||
|
||||
enum class Product : uint32_t {
|
||||
PRODUCT_CLIENT = 0,
|
||||
PRODUCT_LIBSPOTIFY = 1,
|
||||
PRODUCT_MOBILE = 2,
|
||||
PRODUCT_PARTNER = 3,
|
||||
PRODUCT_LIBSPOTIFY_EMBEDDED = 5
|
||||
};
|
||||
|
||||
enum class Platform : uint32_t {
|
||||
PLATFORM_WIN32_X86 = 0,
|
||||
PLATFORM_OSX_X86 = 1,
|
||||
PLATFORM_LINUX_X86 = 2,
|
||||
PLATFORM_IPHONE_ARM = 3,
|
||||
PLATFORM_S60_ARM = 4,
|
||||
PLATFORM_OSX_PPC = 5,
|
||||
PLATFORM_ANDROID_ARM = 6,
|
||||
PLATFORM_WINDOWS_CE_ARM = 7,
|
||||
PLATFORM_LINUX_X86_64 = 8,
|
||||
PLATFORM_OSX_X86_64 = 9,
|
||||
PLATFORM_PALM_ARM = 10,
|
||||
PLATFORM_LINUX_SH = 11,
|
||||
PLATFORM_FREEBSD_X86 = 12,
|
||||
PLATFORM_FREEBSD_X86_64 = 13,
|
||||
PLATFORM_BLACKBERRY_ARM = 14,
|
||||
PLATFORM_SONOS = 15,
|
||||
PLATFORM_LINUX_MIPS = 16,
|
||||
PLATFORM_LINUX_ARM = 17,
|
||||
PLATFORM_LOGITECH_ARM = 18,
|
||||
PLATFORM_LINUX_BLACKFIN = 19,
|
||||
PLATFORM_WP7_ARM = 20,
|
||||
PLATFORM_ONKYO_ARM = 21,
|
||||
PLATFORM_QNXNTO_ARM = 22,
|
||||
PLATFORM_BCO_ARM = 23
|
||||
};
|
||||
|
||||
enum class Cryptosuite : uint32_t {
|
||||
CRYPTO_SUITE_SHANNON = 0,
|
||||
CRYPTO_SUITE_RC4_SHA1_HMAC = 1
|
||||
};
|
||||
|
||||
class LoginCryptoDiffieHellmanChallenge : public BaseProtobufMessage {
|
||||
private:
|
||||
public:
|
||||
LoginCryptoDiffieHellmanChallenge() {};
|
||||
std::vector<uint8_t> gs;
|
||||
|
||||
bool decodeField(std::shared_ptr<PbReader> reader) {
|
||||
switch (reader->currentTag)
|
||||
{
|
||||
case 10:
|
||||
reader->decodeVector(gs);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void encodeWithWriter(std::shared_ptr<PbWriter> writer) {
|
||||
writer->addVector(10, gs);
|
||||
}
|
||||
};
|
||||
|
||||
class LoginCryptoChallengeUnion : public BaseProtobufMessage {
|
||||
private:
|
||||
public:
|
||||
LoginCryptoChallengeUnion() {};
|
||||
LoginCryptoDiffieHellmanChallenge diffie_hellman;
|
||||
|
||||
bool decodeField(std::shared_ptr<PbReader> reader) {
|
||||
switch (reader->currentTag)
|
||||
{
|
||||
case 10:
|
||||
lastMessagePosition = reader->pos + reader->decodeVarInt<uint32_t>();
|
||||
diffie_hellman.parseWithReader(reader);
|
||||
reader->maxPosition = lastMessagePosition;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void encodeWithWriter(std::shared_ptr<PbWriter> writer) {
|
||||
lastMessagePosition = writer->startMessage();
|
||||
diffie_hellman.encodeWithWriter(writer);
|
||||
writer->finishMessage(10, lastMessagePosition);
|
||||
}
|
||||
};
|
||||
|
||||
class LoginCryptoDiffieHellmanHello : public BaseProtobufMessage {
|
||||
private:
|
||||
public:
|
||||
LoginCryptoDiffieHellmanHello() {};
|
||||
std::vector<uint8_t> gc;
|
||||
uint32_t server_keys_known;
|
||||
|
||||
bool decodeField(std::shared_ptr<PbReader> reader) {
|
||||
switch (reader->currentTag)
|
||||
{
|
||||
case 10:
|
||||
reader->decodeVector(gc);
|
||||
break;
|
||||
case 20:
|
||||
server_keys_known = reader->decodeVarInt<uint32_t>();
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void encodeWithWriter(std::shared_ptr<PbWriter> writer) {
|
||||
writer->addVector(10, gc);
|
||||
writer->addVarInt(20, server_keys_known);
|
||||
}
|
||||
};
|
||||
|
||||
class LoginCryptoHelloUnion : public BaseProtobufMessage {
|
||||
private:
|
||||
public:
|
||||
LoginCryptoHelloUnion() {};
|
||||
LoginCryptoDiffieHellmanHello diffie_hellman;
|
||||
|
||||
bool decodeField(std::shared_ptr<PbReader> reader) {
|
||||
switch (reader->currentTag)
|
||||
{
|
||||
case 10:
|
||||
lastMessagePosition = reader->pos + reader->decodeVarInt<uint32_t>();
|
||||
diffie_hellman.parseWithReader(reader);
|
||||
reader->maxPosition = lastMessagePosition;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void encodeWithWriter(std::shared_ptr<PbWriter> writer) {
|
||||
lastMessagePosition = writer->startMessage();
|
||||
diffie_hellman.encodeWithWriter(writer);
|
||||
writer->finishMessage(10, lastMessagePosition);
|
||||
}
|
||||
};
|
||||
|
||||
class BuildInfo : public BaseProtobufMessage {
|
||||
private:
|
||||
public:
|
||||
BuildInfo() {};
|
||||
Product product;
|
||||
Platform platform;
|
||||
uint64_t version;
|
||||
|
||||
bool decodeField(std::shared_ptr<PbReader> reader) {
|
||||
switch (reader->currentTag)
|
||||
{
|
||||
case 10:
|
||||
product = static_cast<Product>(reader->decodeVarInt<uint32_t>());
|
||||
break;
|
||||
case 30:
|
||||
platform = static_cast<Platform>(reader->decodeVarInt<uint32_t>());
|
||||
break;
|
||||
case 40:
|
||||
version = reader->decodeVarInt<uint64_t>();
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void encodeWithWriter(std::shared_ptr<PbWriter> writer) {
|
||||
writer->addVarInt(10, static_cast<uint32_t>(product));
|
||||
writer->addVarInt(30, static_cast<uint32_t>(platform));
|
||||
writer->addVarInt(40, version);
|
||||
}
|
||||
};
|
||||
|
||||
class FeatureSet : public BaseProtobufMessage {
|
||||
private:
|
||||
public:
|
||||
FeatureSet() {};
|
||||
bool autoupdate2;
|
||||
|
||||
bool decodeField(std::shared_ptr<PbReader> reader) {
|
||||
switch (reader->currentTag)
|
||||
{
|
||||
case 1:
|
||||
autoupdate2 = reader->decodeVarInt<bool>();
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void encodeWithWriter(std::shared_ptr<PbWriter> writer) {
|
||||
writer->addVarInt(1, autoupdate2);
|
||||
}
|
||||
};
|
||||
|
||||
class APChallenge : public BaseProtobufMessage {
|
||||
private:
|
||||
public:
|
||||
APChallenge() {};
|
||||
LoginCryptoChallengeUnion login_crypto_challenge;
|
||||
|
||||
bool decodeField(std::shared_ptr<PbReader> reader) {
|
||||
switch (reader->currentTag)
|
||||
{
|
||||
case 10:
|
||||
lastMessagePosition = reader->pos + reader->decodeVarInt<uint32_t>();
|
||||
login_crypto_challenge.parseWithReader(reader);
|
||||
reader->maxPosition = lastMessagePosition;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void encodeWithWriter(std::shared_ptr<PbWriter> writer) {
|
||||
lastMessagePosition = writer->startMessage();
|
||||
login_crypto_challenge.encodeWithWriter(writer);
|
||||
writer->finishMessage(10, lastMessagePosition);
|
||||
}
|
||||
};
|
||||
|
||||
class APResponseMessage : public BaseProtobufMessage {
|
||||
private:
|
||||
public:
|
||||
APResponseMessage() {};
|
||||
APChallenge challenge;
|
||||
|
||||
bool decodeField(std::shared_ptr<PbReader> reader) {
|
||||
switch (reader->currentTag)
|
||||
{
|
||||
case 10:
|
||||
lastMessagePosition = reader->pos + reader->decodeVarInt<uint32_t>();
|
||||
challenge.parseWithReader(reader);
|
||||
reader->maxPosition = lastMessagePosition;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void encodeWithWriter(std::shared_ptr<PbWriter> writer) {
|
||||
lastMessagePosition = writer->startMessage();
|
||||
challenge.encodeWithWriter(writer);
|
||||
writer->finishMessage(10, lastMessagePosition);
|
||||
}
|
||||
};
|
||||
|
||||
class LoginCryptoDiffieHellmanResponse : public BaseProtobufMessage {
|
||||
private:
|
||||
public:
|
||||
LoginCryptoDiffieHellmanResponse() {};
|
||||
std::vector<uint8_t> hmac;
|
||||
|
||||
bool decodeField(std::shared_ptr<PbReader> reader) {
|
||||
switch (reader->currentTag)
|
||||
{
|
||||
case 10:
|
||||
reader->decodeVector(hmac);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void encodeWithWriter(std::shared_ptr<PbWriter> writer) {
|
||||
writer->addVector(10, hmac);
|
||||
}
|
||||
};
|
||||
|
||||
class LoginCryptoResponseUnion : public BaseProtobufMessage {
|
||||
private:
|
||||
public:
|
||||
LoginCryptoResponseUnion() {};
|
||||
LoginCryptoDiffieHellmanResponse diffie_hellman;
|
||||
|
||||
bool decodeField(std::shared_ptr<PbReader> reader) {
|
||||
switch (reader->currentTag)
|
||||
{
|
||||
case 10:
|
||||
lastMessagePosition = reader->pos + reader->decodeVarInt<uint32_t>();
|
||||
diffie_hellman.parseWithReader(reader);
|
||||
reader->maxPosition = lastMessagePosition;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void encodeWithWriter(std::shared_ptr<PbWriter> writer) {
|
||||
lastMessagePosition = writer->startMessage();
|
||||
diffie_hellman.encodeWithWriter(writer);
|
||||
writer->finishMessage(10, lastMessagePosition);
|
||||
}
|
||||
};
|
||||
|
||||
class CryptoResponseUnion : public BaseProtobufMessage {
|
||||
private:
|
||||
public:
|
||||
CryptoResponseUnion() {};
|
||||
|
||||
bool decodeField(std::shared_ptr<PbReader> reader) {
|
||||
switch (reader->currentTag)
|
||||
{
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void encodeWithWriter(std::shared_ptr<PbWriter> writer) {
|
||||
}
|
||||
};
|
||||
|
||||
class PoWResponseUnion : public BaseProtobufMessage {
|
||||
private:
|
||||
public:
|
||||
PoWResponseUnion() {};
|
||||
|
||||
bool decodeField(std::shared_ptr<PbReader> reader) {
|
||||
switch (reader->currentTag)
|
||||
{
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void encodeWithWriter(std::shared_ptr<PbWriter> writer) {
|
||||
}
|
||||
};
|
||||
|
||||
class ClientResponsePlaintext : public BaseProtobufMessage {
|
||||
private:
|
||||
public:
|
||||
ClientResponsePlaintext() {};
|
||||
LoginCryptoResponseUnion login_crypto_response;
|
||||
PoWResponseUnion pow_response;
|
||||
CryptoResponseUnion crypto_response;
|
||||
|
||||
bool decodeField(std::shared_ptr<PbReader> reader) {
|
||||
switch (reader->currentTag)
|
||||
{
|
||||
case 10:
|
||||
lastMessagePosition = reader->pos + reader->decodeVarInt<uint32_t>();
|
||||
login_crypto_response.parseWithReader(reader);
|
||||
reader->maxPosition = lastMessagePosition;
|
||||
break;
|
||||
case 20:
|
||||
lastMessagePosition = reader->pos + reader->decodeVarInt<uint32_t>();
|
||||
pow_response.parseWithReader(reader);
|
||||
reader->maxPosition = lastMessagePosition;
|
||||
break;
|
||||
case 30:
|
||||
lastMessagePosition = reader->pos + reader->decodeVarInt<uint32_t>();
|
||||
crypto_response.parseWithReader(reader);
|
||||
reader->maxPosition = lastMessagePosition;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void encodeWithWriter(std::shared_ptr<PbWriter> writer) {
|
||||
lastMessagePosition = writer->startMessage();
|
||||
login_crypto_response.encodeWithWriter(writer);
|
||||
writer->finishMessage(10, lastMessagePosition);
|
||||
lastMessagePosition = writer->startMessage();
|
||||
pow_response.encodeWithWriter(writer);
|
||||
writer->finishMessage(20, lastMessagePosition);
|
||||
lastMessagePosition = writer->startMessage();
|
||||
crypto_response.encodeWithWriter(writer);
|
||||
writer->finishMessage(30, lastMessagePosition);
|
||||
}
|
||||
};
|
||||
|
||||
class ClientHello : public BaseProtobufMessage {
|
||||
private:
|
||||
public:
|
||||
ClientHello() {};
|
||||
BuildInfo build_info;
|
||||
LoginCryptoHelloUnion login_crypto_hello;
|
||||
std::vector<Cryptosuite> cryptosuites_supported;
|
||||
std::vector<uint8_t> client_nonce;
|
||||
std::vector<uint8_t> padding;
|
||||
FeatureSet feature_set;
|
||||
|
||||
bool decodeField(std::shared_ptr<PbReader> reader) {
|
||||
if (firstField) {
|
||||
cryptosuites_supported.clear();
|
||||
}
|
||||
switch (reader->currentTag)
|
||||
{
|
||||
case 10:
|
||||
lastMessagePosition = reader->pos + reader->decodeVarInt<uint32_t>();
|
||||
build_info.parseWithReader(reader);
|
||||
reader->maxPosition = lastMessagePosition;
|
||||
break;
|
||||
case 50:
|
||||
lastMessagePosition = reader->pos + reader->decodeVarInt<uint32_t>();
|
||||
login_crypto_hello.parseWithReader(reader);
|
||||
reader->maxPosition = lastMessagePosition;
|
||||
break;
|
||||
case 30:
|
||||
cryptosuites_supported.push_back(Cryptosuite());
|
||||
cryptosuites_supported[cryptosuites_supported.size()-1] = static_cast<Cryptosuite>(reader->decodeVarInt<uint32_t>());
|
||||
break;
|
||||
case 60:
|
||||
reader->decodeVector(client_nonce);
|
||||
break;
|
||||
case 70:
|
||||
reader->decodeVector(padding);
|
||||
break;
|
||||
case 80:
|
||||
lastMessagePosition = reader->pos + reader->decodeVarInt<uint32_t>();
|
||||
feature_set.parseWithReader(reader);
|
||||
reader->maxPosition = lastMessagePosition;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void encodeWithWriter(std::shared_ptr<PbWriter> writer) {
|
||||
lastMessagePosition = writer->startMessage();
|
||||
build_info.encodeWithWriter(writer);
|
||||
writer->finishMessage(10, lastMessagePosition);
|
||||
lastMessagePosition = writer->startMessage();
|
||||
login_crypto_hello.encodeWithWriter(writer);
|
||||
writer->finishMessage(50, lastMessagePosition);
|
||||
lastMessagePosition = writer->startMessage();
|
||||
for (int i = 0; i < cryptosuites_supported.size(); i++) {
|
||||
writer->encodeVarInt(static_cast<uint32_t>(cryptosuites_supported[i]));
|
||||
}
|
||||
writer->finishMessage(30, lastMessagePosition);
|
||||
writer->addVector(60, client_nonce);
|
||||
writer->addVector(70, padding);
|
||||
lastMessagePosition = writer->startMessage();
|
||||
feature_set.encodeWithWriter(writer);
|
||||
writer->finishMessage(80, lastMessagePosition);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
12
components/spotify/cspot/include/Logger.h
Normal file
12
components/spotify/cspot/include/Logger.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef LOGGER_H
|
||||
#define LOGGER_H
|
||||
|
||||
#include <BellLogger.h>
|
||||
|
||||
#define CSPOT_LOG(type, ...) \
|
||||
do \
|
||||
{ \
|
||||
bell::bellGlobalLogger->type(__FILE__, __LINE__, "cspot", __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
35
components/spotify/cspot/include/LoginBlob.h
Normal file
35
components/spotify/cspot/include/LoginBlob.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#ifndef LOGINBLOB_H
|
||||
#define LOGINBLOB_H
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include "Crypto.h"
|
||||
#include "ProtoHelper.h"
|
||||
|
||||
class LoginBlob
|
||||
{
|
||||
private:
|
||||
int blobSkipPosition = 0;
|
||||
std::unique_ptr<Crypto> crypto;
|
||||
|
||||
|
||||
uint32_t readBlobInt(const std::vector<uint8_t>& loginData);
|
||||
std::vector<uint8_t> decodeBlob(const std::vector<uint8_t>& blob, const std::vector<uint8_t>& sharedKey);
|
||||
std::vector<uint8_t> decodeBlobSecondary(const std::vector<uint8_t>& blob, const std::string& username, const std::string& deviceId);
|
||||
|
||||
public:
|
||||
LoginBlob();
|
||||
std::vector<uint8_t> authData;
|
||||
std::string username;
|
||||
int authType;
|
||||
|
||||
// Loading
|
||||
void loadZeroconf(const std::vector<uint8_t>& blob, const std::vector<uint8_t>& sharedKey, const std::string& deviceId, const std::string& username);
|
||||
void loadUserPass(const std::string& username, const std::string& password);
|
||||
void loadJson(const std::string& json);
|
||||
|
||||
std::string toJson();
|
||||
};
|
||||
|
||||
#endif
|
||||
100
components/spotify/cspot/include/MercuryManager.h
Normal file
100
components/spotify/cspot/include/MercuryManager.h
Normal file
@@ -0,0 +1,100 @@
|
||||
#ifndef MERCURY_MANAGER_H
|
||||
#define MERCURY_MANAGER_H
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include "ShannonConnection.h"
|
||||
#include "MercuryResponse.h"
|
||||
#include "Packet.h"
|
||||
#include "Utils.h"
|
||||
#include "ProtoHelper.h"
|
||||
#include "MercuryManager.h"
|
||||
#include "AudioChunk.h"
|
||||
#include "AudioChunkManager.h"
|
||||
#include <atomic>
|
||||
#include "Task.h"
|
||||
#include "platform/WrappedSemaphore.h"
|
||||
#include "TimeProvider.h"
|
||||
#include "Session.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <memory>
|
||||
|
||||
#define AUDIOCHUNK_TIMEOUT_MS 5 * 1000
|
||||
#define RECONNECTION_RETRY_MS 5 * 1000
|
||||
#define PING_TIMEOUT_MS 2 * 60 * 1000 + 5000
|
||||
|
||||
typedef std::function<void(std::unique_ptr<MercuryResponse>)> mercuryCallback;
|
||||
typedef std::function<void(bool, std::vector<uint8_t>)> audioKeyCallback;
|
||||
typedef std::function<void()> voidCallback;
|
||||
|
||||
#define AUDIO_CHUNK_SIZE 0x20000
|
||||
|
||||
enum class MercuryType : uint8_t
|
||||
{
|
||||
SUB = 0xb3,
|
||||
UNSUB = 0xb4,
|
||||
SUBRES = 0xb5,
|
||||
SEND = 0xb2,
|
||||
GET = 0xFF, // Shitty workaround, it's value is actually same as SEND
|
||||
PING = 0x04,
|
||||
PONG_ACK = 0x4a,
|
||||
AUDIO_CHUNK_REQUEST_COMMAND = 0x08,
|
||||
AUDIO_CHUNK_SUCCESS_RESPONSE = 0x09,
|
||||
AUDIO_CHUNK_FAILURE_RESPONSE = 0x0A,
|
||||
AUDIO_KEY_REQUEST_COMMAND = 0x0C,
|
||||
AUDIO_KEY_SUCCESS_RESPONSE = 0x0D,
|
||||
AUDIO_KEY_FAILURE_RESPONSE = 0x0E,
|
||||
COUNTRY_CODE_RESPONSE = 0x1B,
|
||||
};
|
||||
|
||||
extern std::map<MercuryType, std::string> MercuryTypeMap;
|
||||
|
||||
class MercuryManager : public bell::Task
|
||||
{
|
||||
private:
|
||||
std::map<uint64_t, mercuryCallback> callbacks;
|
||||
std::mutex reconnectionMutex;
|
||||
std::mutex runningMutex;
|
||||
std::map<std::string, mercuryCallback> subscriptions;
|
||||
std::unique_ptr<Session> session;
|
||||
std::shared_ptr<LoginBlob> lastAuthBlob;
|
||||
std::unique_ptr<AudioChunkManager> audioChunkManager;
|
||||
std::vector<std::unique_ptr<Packet>> queue;
|
||||
std::unique_ptr<WrappedSemaphore> queueSemaphore;
|
||||
unsigned long long lastRequestTimestamp = -1;
|
||||
unsigned long long lastPingTimestamp = -1;
|
||||
std::atomic<bool> isRunning = false;
|
||||
uint64_t sequenceId;
|
||||
uint32_t audioKeySequence;
|
||||
audioKeyCallback keyCallback;
|
||||
|
||||
void runTask();
|
||||
public:
|
||||
MercuryManager(std::unique_ptr<Session> session);
|
||||
voidCallback reconnectedCallback;
|
||||
uint16_t audioChunkSequence;
|
||||
std::shared_ptr<TimeProvider> timeProvider;
|
||||
std::string countryCode;
|
||||
|
||||
bool timeoutHandler();
|
||||
uint64_t execute(MercuryType method, std::string uri, mercuryCallback &callback, mercuryCallback &subscription, mercuryParts &payload);
|
||||
uint64_t execute(MercuryType method, std::string uri, mercuryCallback &callback, mercuryCallback &subscription);
|
||||
uint64_t execute(MercuryType method, std::string uri, mercuryCallback &callback, mercuryParts &payload);
|
||||
uint64_t execute(MercuryType method, std::string uri, mercuryCallback &callback);
|
||||
void updateQueue();
|
||||
void stop();
|
||||
void handleQueue();
|
||||
void requestAudioKey(std::vector<uint8_t> trackId, std::vector<uint8_t> fileId, audioKeyCallback &audioCallback);
|
||||
std::shared_ptr<AudioChunk> fetchAudioChunk(std::vector<uint8_t> fileId, std::vector<uint8_t> &audioKey, uint16_t index);
|
||||
std::shared_ptr<AudioChunk> fetchAudioChunk(std::vector<uint8_t> fileId, std::vector<uint8_t> &audioKey, uint32_t startPos, uint32_t endPos);
|
||||
void unregisterAudioCallback(uint16_t seqId);
|
||||
void unregisterMercuryCallback(uint64_t seqId);
|
||||
void freeAudioKeyCallback();
|
||||
void reconnect();
|
||||
};
|
||||
|
||||
#endif
|
||||
28
components/spotify/cspot/include/MercuryResponse.h
Normal file
28
components/spotify/cspot/include/MercuryResponse.h
Normal file
@@ -0,0 +1,28 @@
|
||||
|
||||
#ifndef MERCURYRESPONSE_H
|
||||
#define MERCURYRESPONSE_H
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include "ProtoHelper.h"
|
||||
#include "Utils.h"
|
||||
|
||||
typedef std::vector<std::vector<uint8_t>> mercuryParts;
|
||||
|
||||
class MercuryResponse
|
||||
{
|
||||
private:
|
||||
void parseResponse(std::vector<uint8_t> &data);
|
||||
std::vector<uint8_t> data;
|
||||
public:
|
||||
MercuryResponse(std::vector<uint8_t> &data);
|
||||
void decodeHeader();
|
||||
Header mercuryHeader;
|
||||
uint8_t flags;
|
||||
mercuryParts parts;
|
||||
uint64_t sequenceId;
|
||||
};
|
||||
|
||||
#endif
|
||||
17
components/spotify/cspot/include/Packet.h
Normal file
17
components/spotify/cspot/include/Packet.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef PACKET_H
|
||||
#define PACKET_H
|
||||
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
class Packet
|
||||
{
|
||||
private:
|
||||
|
||||
public:
|
||||
Packet(uint8_t command, std::vector<uint8_t> &data);
|
||||
uint8_t command;
|
||||
std::vector<uint8_t> data;
|
||||
};
|
||||
|
||||
#endif
|
||||
42
components/spotify/cspot/include/PbCommon.h
Normal file
42
components/spotify/cspot/include/PbCommon.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#ifndef PBCOMMON_H
|
||||
#define PBCOMMON_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <PbWriter.h>
|
||||
#include <optional>
|
||||
#include <PbReader.h>
|
||||
class BaseProtobufMessage
|
||||
{
|
||||
private:
|
||||
public:
|
||||
bool firstField = true;
|
||||
uint32_t lastMessagePosition;
|
||||
void parseFromVector(std::vector<uint8_t> const &rawData)
|
||||
{
|
||||
auto reader = std::make_shared<PbReader>(rawData);
|
||||
parseWithReader(reader);
|
||||
}
|
||||
void encodeToVector(std::vector<uint8_t> &rawData)
|
||||
{
|
||||
auto writer = std::make_shared<PbWriter>(rawData);
|
||||
encodeWithWriter(writer);
|
||||
}
|
||||
|
||||
void parseWithReader(std::shared_ptr<PbReader> reader)
|
||||
{
|
||||
firstField = true;
|
||||
while (reader->next())
|
||||
{
|
||||
if (!decodeField(reader))
|
||||
{
|
||||
reader->skip();
|
||||
} else {
|
||||
firstField = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void encodeWithWriter(std::shared_ptr<PbWriter> writer) = 0;
|
||||
virtual bool decodeField(std::shared_ptr<PbReader> reader) = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
42
components/spotify/cspot/include/PbReader.h
Normal file
42
components/spotify/cspot/include/PbReader.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#ifndef PBREADER_H
|
||||
#define PBREADER_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <PbWireType.h>
|
||||
|
||||
class PbReader
|
||||
{
|
||||
private:
|
||||
std::vector<uint8_t> const &rawData;
|
||||
uint32_t currentWireValue = 0;
|
||||
uint64_t skipVarIntDump = 0;
|
||||
uint32_t nextFieldLength = 0;
|
||||
int64_t decodeZigzag(uint64_t value);
|
||||
|
||||
public:
|
||||
PbReader(std::vector<uint8_t> const &rawData);
|
||||
uint32_t maxPosition = 0;
|
||||
|
||||
PbWireType currentWireType = PbWireType::unknown;
|
||||
uint32_t currentTag = 0;
|
||||
uint32_t pos = 0;
|
||||
|
||||
template <typename T>
|
||||
T decodeVarInt();
|
||||
|
||||
template <typename T>
|
||||
T decodeFixed();
|
||||
|
||||
template <typename T>
|
||||
T decodeSVarInt();
|
||||
void decodeString(std::string &target);
|
||||
void decodeVector(std::vector<uint8_t> &target);
|
||||
|
||||
bool next();
|
||||
void skip();
|
||||
void resetMaxPosition();
|
||||
};
|
||||
|
||||
#endif
|
||||
15
components/spotify/cspot/include/PbWireType.h
Normal file
15
components/spotify/cspot/include/PbWireType.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef PBWIRETYPE_H
|
||||
#define PBWIRETYPE_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
enum class PbWireType : uint32_t
|
||||
{
|
||||
varint = 0, // int32/64, uint32/64, sint32/64, bool, enum
|
||||
fixed64 = 1, // fixed64, sfixed64, double
|
||||
length_delimited = 2, // string, bytes, nested messages, packed repeated fields
|
||||
fixed32 = 5, // fixed32, sfixed32, float
|
||||
unknown = 99
|
||||
};
|
||||
|
||||
#endif
|
||||
39
components/spotify/cspot/include/PbWriter.h
Normal file
39
components/spotify/cspot/include/PbWriter.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifndef PBWRITER_H
|
||||
#define PBWRITER_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <PbWireType.h>
|
||||
|
||||
class PbWriter
|
||||
{
|
||||
private:
|
||||
std::vector<uint8_t> &rawData;
|
||||
uint32_t pos;
|
||||
uint32_t msgStartPos = 0;
|
||||
void encodeVarInt(uint32_t low, uint32_t high, int32_t atIndex = 0);
|
||||
uint32_t encodeZigzag32(int32_t value);
|
||||
uint64_t encodeZigzag64(int64_t value);
|
||||
public:
|
||||
PbWriter(std::vector<uint8_t> &rawData);
|
||||
|
||||
template <typename T>
|
||||
void encodeVarInt(T, int32_t atIndex = 0);
|
||||
template <typename T>
|
||||
void encodeFixed(T);
|
||||
void addSVarInt32(uint32_t tag, int32_t);
|
||||
void addSVarInt64(uint32_t tag, int64_t);
|
||||
void addString(uint32_t tag, std::string &target);
|
||||
void addVector(uint32_t tag, std::vector<uint8_t> &target);
|
||||
|
||||
template <typename T>
|
||||
void addVarInt(uint32_t tag, T intType);
|
||||
void addBool(uint32_t tag, bool value);
|
||||
|
||||
void addField(uint32_t tag, PbWireType wiretype);
|
||||
uint32_t startMessage();
|
||||
void finishMessage(uint32_t tag, uint32_t pos);
|
||||
};
|
||||
|
||||
#endif
|
||||
31
components/spotify/cspot/include/PlainConnection.h
Normal file
31
components/spotify/cspot/include/PlainConnection.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef PLAINCONNECTION_H
|
||||
#define PLAINCONNECTION_H
|
||||
|
||||
#include "sys/socket.h"
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#include "Packet.h"
|
||||
#include "Utils.h"
|
||||
|
||||
typedef std::function<bool()> timeoutCallback;
|
||||
|
||||
class PlainConnection
|
||||
{
|
||||
public:
|
||||
PlainConnection();
|
||||
~PlainConnection();
|
||||
int apSock;
|
||||
void connectToAp(std::string apAddress);
|
||||
void closeSocket();
|
||||
timeoutCallback timeoutHandler;
|
||||
std::vector<uint8_t> sendPrefixPacket(const std::vector<uint8_t> &prefix, const std::vector<uint8_t> &data);
|
||||
std::vector<uint8_t> recvPacket();
|
||||
std::vector<uint8_t> readBlock(size_t size);
|
||||
size_t writeBlock(const std::vector<uint8_t> &data);
|
||||
};
|
||||
|
||||
#endif
|
||||
50
components/spotify/cspot/include/Player.h
Normal file
50
components/spotify/cspot/include/Player.h
Normal file
@@ -0,0 +1,50 @@
|
||||
#ifndef PLAYER_H
|
||||
#define PLAYER_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
#include "Utils.h"
|
||||
#include "MercuryManager.h"
|
||||
#include "TrackReference.h"
|
||||
#include "Session.h"
|
||||
#include "SpotifyTrack.h"
|
||||
#include "AudioSink.h"
|
||||
#include <mutex>
|
||||
#include "Queue.h"
|
||||
#include "Task.h"
|
||||
|
||||
class Player : public bell::Task {
|
||||
private:
|
||||
std::shared_ptr<MercuryManager> manager;
|
||||
std::shared_ptr<SpotifyTrack> currentTrack = nullptr;
|
||||
std::shared_ptr<AudioSink> audioSink;
|
||||
std::mutex loadTrackMutex;
|
||||
// @TODO: Use some actual structure here
|
||||
bell::Queue<std::shared_ptr<SpotifyTrack>> trackQueue;
|
||||
void runTask();
|
||||
|
||||
public:
|
||||
Player(std::shared_ptr<MercuryManager> manager, std::shared_ptr<AudioSink> audioSink);
|
||||
std::function<void()> endOfFileCallback;
|
||||
int volume = 255;
|
||||
uint32_t logVolume;
|
||||
std::atomic<bool> isRunning = false;
|
||||
trackChangedCallback trackChanged;
|
||||
std::mutex runningMutex;
|
||||
|
||||
void setVolume(uint32_t volume);
|
||||
void handleLoad(std::shared_ptr<TrackReference> track, std::function<void()> &trackLoadedCallback, uint32_t position_ms, bool isPaused);
|
||||
void pause();
|
||||
void cancelCurrentTrack();
|
||||
void seekMs(size_t positionMs);
|
||||
void feedPCM(std::vector<uint8_t> &data);
|
||||
void play();
|
||||
void stop();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
135
components/spotify/cspot/include/PlayerState.h
Normal file
135
components/spotify/cspot/include/PlayerState.h
Normal file
@@ -0,0 +1,135 @@
|
||||
#ifndef PLAYERSTATE_H
|
||||
#define PLAYERSTATE_H
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "ProtoHelper.h"
|
||||
#include "Utils.h"
|
||||
#include "TimeProvider.h"
|
||||
#include "ConstantParameters.h"
|
||||
#include "CspotAssert.h"
|
||||
#include "TrackReference.h"
|
||||
#include "ConfigJSON.h"
|
||||
|
||||
enum class PlaybackState {
|
||||
Playing,
|
||||
Stopped,
|
||||
Loading,
|
||||
Paused
|
||||
};
|
||||
|
||||
class PlayerState
|
||||
{
|
||||
private:
|
||||
uint32_t seqNum = 0;
|
||||
uint8_t capabilityIndex = 0;
|
||||
std::vector<uint8_t> frameData;
|
||||
std::shared_ptr<TimeProvider> timeProvider;
|
||||
std::shared_ptr<ConfigJSON> config;
|
||||
|
||||
void addCapability(CapabilityType typ, int intValue = -1, std::vector<std::string> stringsValue = std::vector<std::string>());
|
||||
public:
|
||||
Frame innerFrame = Frame();
|
||||
Frame remoteFrame = Frame();
|
||||
|
||||
/**
|
||||
* @brief Player state represents the current state of player.
|
||||
*
|
||||
* Responsible for keeping track of player's state. Doesn't control the playback itself.
|
||||
*
|
||||
* @param timeProvider synced time provider
|
||||
*/
|
||||
PlayerState(std::shared_ptr<TimeProvider> timeProvider);
|
||||
|
||||
/**
|
||||
* @brief Updates state according to current playback state.
|
||||
*
|
||||
* @param state playback state
|
||||
*/
|
||||
void setPlaybackState(const PlaybackState state);
|
||||
|
||||
/**
|
||||
* @brief Sets player activity
|
||||
*
|
||||
* @param isActive activity status
|
||||
*/
|
||||
void setActive(bool isActive);
|
||||
|
||||
/**
|
||||
* @brief Simple getter
|
||||
*
|
||||
* @return true player is active
|
||||
* @return false player is inactive
|
||||
*/
|
||||
bool isActive();
|
||||
|
||||
/**
|
||||
* @brief Updates local track position.
|
||||
*
|
||||
* @param position position in milliseconds
|
||||
*/
|
||||
void updatePositionMs(uint32_t position);
|
||||
|
||||
/**
|
||||
* @brief Sets local volume on internal state.
|
||||
*
|
||||
* @param volume volume between 0 and UINT16 max
|
||||
*/
|
||||
void setVolume(uint32_t volume);
|
||||
|
||||
/**
|
||||
* @brief Enables queue shuffling.
|
||||
*
|
||||
* Sets shuffle parameter on local frame, and in case shuffling is enabled,
|
||||
* it will randomize the entire local queue.
|
||||
*
|
||||
* @param shuffle whenever should shuffle
|
||||
*/
|
||||
void setShuffle(bool shuffle);
|
||||
|
||||
/**
|
||||
* @brief Enables repeat
|
||||
*
|
||||
* @param repeat should repeat param
|
||||
*/
|
||||
void setRepeat(bool repeat);
|
||||
|
||||
/**
|
||||
* @brief Updates local track queue from remote data.
|
||||
*/
|
||||
void updateTracks();
|
||||
|
||||
/**
|
||||
* @brief Changes playback to next queued track.
|
||||
*
|
||||
* Will go back to first track if current track is last track in queue.
|
||||
* In that case, it will pause if repeat is disabled.
|
||||
*/
|
||||
bool nextTrack();
|
||||
|
||||
/**
|
||||
* @brief Changes playback to previous queued track.
|
||||
*
|
||||
* Will stop if current track is the first track in queue and repeat is disabled.
|
||||
* If repeat is enabled, it will loop back to the last track in queue.
|
||||
*/
|
||||
void prevTrack();
|
||||
|
||||
/**
|
||||
* @brief Gets the current track reference.
|
||||
*
|
||||
* @return std::shared_ptr<TrackReference> pointer to track reference
|
||||
*/
|
||||
std::shared_ptr<TrackReference> getCurrentTrack();
|
||||
|
||||
/**
|
||||
* @brief Encodes current frame into binary data via protobuf.
|
||||
*
|
||||
* @param typ message type to include in frame type
|
||||
* @return std::vector<uint8_t> binary frame data
|
||||
*/
|
||||
std::vector<uint8_t> encodeCurrentFrame(MessageType typ);
|
||||
};
|
||||
|
||||
#endif
|
||||
37
components/spotify/cspot/include/ProtoHelper.h
Normal file
37
components/spotify/cspot/include/ProtoHelper.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef PROTOBUF_H
|
||||
#define PROTOBUF_H
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include "protobuf.h"
|
||||
#include <PbReader.h>
|
||||
#include <PbCommon.h>
|
||||
|
||||
std::optional<AnyRef> findFieldWithProtobufTag(AnyRef ref, uint32_t tag);
|
||||
void decodeField(std::shared_ptr<PbReader> reader, AnyRef any);
|
||||
void decodeProtobuf(std::shared_ptr<PbReader> reader, AnyRef any);
|
||||
void encodeProtobuf(std::shared_ptr<PbWriter> writer, AnyRef any, uint32_t protobufTag = 0);
|
||||
|
||||
template <typename T>
|
||||
std::vector<uint8_t> encodePb(T & data)
|
||||
{
|
||||
auto ref = AnyRef::of(&data);
|
||||
std::vector<uint8_t> rawData;;
|
||||
auto writer = std::make_shared<PbWriter>(rawData);
|
||||
encodeProtobuf(writer, ref);
|
||||
|
||||
return rawData;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
T decodePb(std::vector<uint8_t> & bytes)
|
||||
{
|
||||
T data = {};
|
||||
auto ref = AnyRef::of(&data);
|
||||
auto writer = std::make_shared<PbReader>(bytes);
|
||||
decodeProtobuf(writer, ref);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
#endif
|
||||
49
components/spotify/cspot/include/Session.h
Normal file
49
components/spotify/cspot/include/Session.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#ifndef SESSION_H
|
||||
#define SESSION_H
|
||||
|
||||
#include <vector>
|
||||
#include <random>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <climits>
|
||||
#include <algorithm>
|
||||
#include "Utils.h"
|
||||
#include "stdlib.h"
|
||||
#include "ShannonConnection.h"
|
||||
#include "LoginBlob.h"
|
||||
#include "ApResolve.h"
|
||||
#include "PlainConnection.h"
|
||||
#include "Packet.h"
|
||||
#include "ConstantParameters.h"
|
||||
#include "Crypto.h"
|
||||
#include "ProtoHelper.h"
|
||||
|
||||
#define SPOTIFY_VERSION 0x10800000000
|
||||
#define LOGIN_REQUEST_COMMAND 0xAB
|
||||
#define AUTH_SUCCESSFUL_COMMAND 0xAC
|
||||
#define AUTH_DECLINED_COMMAND 0xAD
|
||||
|
||||
class Session
|
||||
{
|
||||
private:
|
||||
ClientResponseEncrypted authRequest;
|
||||
ClientResponsePlaintext clientResPlaintext;
|
||||
ClientHello clientHello;
|
||||
APResponseMessage apResponse;
|
||||
|
||||
std::shared_ptr<PlainConnection> conn;
|
||||
std::unique_ptr<Crypto> crypto;
|
||||
std::vector<uint8_t> sendClientHelloRequest();
|
||||
void processAPHelloResponse(std::vector<uint8_t> &helloPacket);
|
||||
|
||||
public:
|
||||
Session();
|
||||
std::shared_ptr<ShannonConnection> shanConn;
|
||||
std::shared_ptr<LoginBlob> authBlob;
|
||||
void connect(std::unique_ptr<PlainConnection> connection);
|
||||
void connectWithRandomAp();
|
||||
void close();
|
||||
std::vector<uint8_t> authenticate(std::shared_ptr<LoginBlob> blob);
|
||||
};
|
||||
|
||||
#endif
|
||||
43
components/spotify/cspot/include/Shannon.h
Normal file
43
components/spotify/cspot/include/Shannon.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#ifndef SHANNON_H
|
||||
#define SHANNON_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
class Shannon
|
||||
{
|
||||
public:
|
||||
static constexpr unsigned int N = 16;
|
||||
|
||||
void key(const std::vector<uint8_t> &key); /* set key */
|
||||
void nonce(const std::vector<uint8_t> &nonce); /* set Init Vector */
|
||||
void stream(std::vector<uint8_t> &buf); /* stream cipher */
|
||||
void maconly(std::vector<uint8_t> &buf); /* accumulate MAC */
|
||||
void encrypt(std::vector<uint8_t> &buf); /* encrypt + MAC */
|
||||
void decrypt(std::vector<uint8_t> &buf); /* decrypt + MAC */
|
||||
void finish(std::vector<uint8_t> &buf); /* finalise MAC */
|
||||
|
||||
private:
|
||||
static constexpr unsigned int FOLD = Shannon::N;
|
||||
static constexpr unsigned int INITKONST = 0x6996c53a;
|
||||
static constexpr unsigned int KEYP = 13;
|
||||
uint32_t R[Shannon::N];
|
||||
uint32_t CRC[Shannon::N];
|
||||
uint32_t initR[Shannon::N];
|
||||
uint32_t konst;
|
||||
uint32_t sbuf;
|
||||
uint32_t mbuf;
|
||||
int nbuf;
|
||||
static uint32_t sbox1(uint32_t w);
|
||||
static uint32_t sbox2(uint32_t w);
|
||||
void cycle();
|
||||
void crcfunc(uint32_t i);
|
||||
void macfunc(uint32_t i);
|
||||
void initState();
|
||||
void saveState();
|
||||
void reloadState();
|
||||
void genkonst();
|
||||
void diffuse();
|
||||
void loadKey(const std::vector<uint8_t> &key);
|
||||
};
|
||||
|
||||
#endif
|
||||
41
components/spotify/cspot/include/ShannonConnection.h
Normal file
41
components/spotify/cspot/include/ShannonConnection.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#ifndef SHANNONCONNECTION_H
|
||||
#define SHANNONCONNECTION_H
|
||||
|
||||
#include <vector>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <string>
|
||||
#include <netdb.h>
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include "platform/WrappedMutex.h"
|
||||
#include "Utils.h"
|
||||
#include "Shannon.h"
|
||||
#include "PlainConnection.h"
|
||||
#include "Packet.h"
|
||||
|
||||
#define MAC_SIZE 4
|
||||
|
||||
|
||||
class ShannonConnection
|
||||
{
|
||||
private:
|
||||
std::unique_ptr<Shannon> sendCipher;
|
||||
std::unique_ptr<Shannon> recvCipher;
|
||||
uint32_t sendNonce = 0;
|
||||
uint32_t recvNonce = 0;
|
||||
std::vector<uint8_t> cipherPacket(uint8_t cmd, std::vector<uint8_t> &data);
|
||||
WrappedMutex writeMutex;
|
||||
WrappedMutex readMutex;
|
||||
|
||||
public:
|
||||
ShannonConnection();
|
||||
~ShannonConnection();
|
||||
void wrapConnection(std::shared_ptr<PlainConnection> conn, std::vector<uint8_t> &sendKey, std::vector<uint8_t> &recvKey);
|
||||
void sendPacket(uint8_t cmd, std::vector<uint8_t> &data);
|
||||
std::shared_ptr<PlainConnection> conn;
|
||||
std::unique_ptr<Packet> recvPacket();
|
||||
};
|
||||
|
||||
#endif
|
||||
104
components/spotify/cspot/include/SpircController.h
Normal file
104
components/spotify/cspot/include/SpircController.h
Normal file
@@ -0,0 +1,104 @@
|
||||
#ifndef SPIRCCONTROLLER_H
|
||||
#define SPIRCCONTROLLER_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include "Utils.h"
|
||||
#include "MercuryManager.h"
|
||||
#include "ProtoHelper.h"
|
||||
#include "Session.h"
|
||||
#include "PlayerState.h"
|
||||
#include "SpotifyTrack.h"
|
||||
#include "ConstantParameters.h"
|
||||
#include "Player.h"
|
||||
#include "ConfigJSON.h"
|
||||
#include <cassert>
|
||||
#include <variant>
|
||||
|
||||
enum class CSpotEventType {
|
||||
PLAY_PAUSE,
|
||||
VOLUME,
|
||||
TRACK_INFO,
|
||||
DISC,
|
||||
NEXT,
|
||||
PREV,
|
||||
SEEK,
|
||||
};
|
||||
|
||||
struct CSpotEvent {
|
||||
CSpotEventType eventType;
|
||||
std::variant<TrackInfo, int, bool> data;
|
||||
};
|
||||
|
||||
typedef std::function<void(CSpotEvent&)> cspotEventHandler;
|
||||
|
||||
class SpircController {
|
||||
private:
|
||||
std::shared_ptr<MercuryManager> manager;
|
||||
std::string username;
|
||||
std::unique_ptr<Player> player;
|
||||
std::unique_ptr<PlayerState> state;
|
||||
std::shared_ptr<AudioSink> audioSink;
|
||||
std::shared_ptr<ConfigJSON> config;
|
||||
|
||||
cspotEventHandler eventHandler;
|
||||
void sendCmd(MessageType typ);
|
||||
void notify();
|
||||
void sendEvent(CSpotEventType eventType, std::variant<TrackInfo, int, bool> data = 0);
|
||||
void handleFrame(std::vector<uint8_t> &data);
|
||||
void loadTrack(uint32_t position_ms = 0, bool isPaused = 0);
|
||||
public:
|
||||
SpircController(std::shared_ptr<MercuryManager> manager, std::string username, std::shared_ptr<AudioSink> audioSink);
|
||||
void subscribe();
|
||||
|
||||
/**
|
||||
* @brief Pauses / Plays current song
|
||||
*
|
||||
* Calling this function will pause or resume playback, setting the
|
||||
* necessary state value and notifying spotify SPIRC.
|
||||
*
|
||||
* @param pause if true pause content, play otherwise
|
||||
*/
|
||||
void setPause(bool pause, bool notifyPlayer = true);
|
||||
|
||||
/**
|
||||
* @brief Toggle Play/Pause
|
||||
*/
|
||||
void playToggle();
|
||||
|
||||
/**
|
||||
* @brief Notifies spotify servers about volume change
|
||||
*
|
||||
* @param volume int between 0 and `MAX_VOLUME`
|
||||
*/
|
||||
void setRemoteVolume(int volume);
|
||||
|
||||
/**
|
||||
* @brief Set device volume and notifies spotify
|
||||
*
|
||||
* @param volume int between 0 and `MAX_VOLUME`
|
||||
*/
|
||||
void setVolume(int volume);
|
||||
|
||||
/**
|
||||
* @brief change volume by a given value
|
||||
*
|
||||
* @param volume int between 0 and `MAX_VOLUME`
|
||||
*/
|
||||
void adjustVolume(int by);
|
||||
|
||||
/**
|
||||
* @brief Goes back to previous track and notfies spotify SPIRC
|
||||
*/
|
||||
void prevSong();
|
||||
|
||||
/**
|
||||
* @brief Skips to next track and notfies spotify SPIRC
|
||||
*/
|
||||
void nextSong();
|
||||
void setEventHandler(cspotEventHandler handler);
|
||||
void stopPlayer();
|
||||
};
|
||||
|
||||
#endif
|
||||
54
components/spotify/cspot/include/SpotifyTrack.h
Normal file
54
components/spotify/cspot/include/SpotifyTrack.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#ifndef SPOTIFYTRACK_H
|
||||
#define SPOTIFYTRACK_H
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include "MercuryManager.h"
|
||||
#include "ProtoHelper.h"
|
||||
#include "Utils.h"
|
||||
#include "MercuryResponse.h"
|
||||
#include <fstream>
|
||||
#include "Crypto.h"
|
||||
#include <functional>
|
||||
#include "ChunkedAudioStream.h"
|
||||
#include "TrackReference.h"
|
||||
#include <cassert>
|
||||
|
||||
struct TrackInfo {
|
||||
std::string name;
|
||||
std::string album;
|
||||
std::string artist;
|
||||
std::string imageUrl;
|
||||
};
|
||||
|
||||
typedef std::function<void(TrackInfo&)> trackChangedCallback;
|
||||
|
||||
class SpotifyTrack
|
||||
{
|
||||
private:
|
||||
std::shared_ptr<MercuryManager> manager;
|
||||
void trackInformationCallback(std::unique_ptr<MercuryResponse> response, uint32_t position_ms, bool isPaused);
|
||||
void episodeInformationCallback(std::unique_ptr<MercuryResponse> response, uint32_t position_ms, bool isPaused);
|
||||
void requestAudioKey(std::vector<uint8_t> fileId, std::vector<uint8_t> trackId, int32_t trackDuration, uint32_t position_ms, bool isPaused);
|
||||
bool countryListContains(std::string countryList, std::string country);
|
||||
bool canPlayTrack(std::vector<Restriction> &restrictions);
|
||||
Track trackInfo;
|
||||
Episode episodeInfo;
|
||||
|
||||
std::vector<uint8_t> fileId;
|
||||
std::vector<uint8_t> currentChunkData;
|
||||
std::vector<uint8_t> currentChunkHeader;
|
||||
public:
|
||||
SpotifyTrack(std::shared_ptr<MercuryManager> manager, std::shared_ptr<TrackReference> trackRef, uint32_t position_ms, bool isPaused);
|
||||
~SpotifyTrack();
|
||||
uint64_t reqSeqNum = -1;
|
||||
std::function<void()> loadedTrackCallback;
|
||||
std::unique_ptr<ChunkedAudioStream> audioStream;
|
||||
trackChangedCallback trackInfoReceived;
|
||||
audioKeyCallback audioKeyLambda;
|
||||
mercuryCallback responseLambda;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
34
components/spotify/cspot/include/TimeProvider.h
Normal file
34
components/spotify/cspot/include/TimeProvider.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#ifndef TIMEPROVIDER_H
|
||||
#define TIMEPROVIDER_H
|
||||
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
|
||||
class TimeProvider
|
||||
{
|
||||
private:
|
||||
unsigned long long timestampDiff;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Bypasses the need for NTP server sync by syncing with spotify's servers
|
||||
*
|
||||
*/
|
||||
TimeProvider();
|
||||
|
||||
/**
|
||||
* @brief Syncs the TimeProvider with spotify server's timestamp
|
||||
*
|
||||
* @param pongPacket pong packet containing timestamp
|
||||
*/
|
||||
void syncWithPingPacket(const std::vector<uint8_t>& pongPacket);
|
||||
|
||||
/**
|
||||
* @brief Get current timestamp synced with spotify servers
|
||||
*
|
||||
* @return unsigned long long timestamp
|
||||
*/
|
||||
unsigned long long getSyncedTimestamp();
|
||||
};
|
||||
|
||||
#endif
|
||||
34
components/spotify/cspot/include/TrackReference.h
Normal file
34
components/spotify/cspot/include/TrackReference.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#ifndef TRACKREFERENCE_H
|
||||
#define TRACKREFERENCE_H
|
||||
|
||||
#include <vector>
|
||||
#include "Utils.h"
|
||||
#include "ProtoHelper.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
class TrackReference
|
||||
{
|
||||
private:
|
||||
std::string alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
std::vector<uint8_t> base62Decode(std::string uri);
|
||||
|
||||
public:
|
||||
TrackReference(TrackRef *ref);
|
||||
~TrackReference();
|
||||
|
||||
TrackRef* ref;
|
||||
|
||||
std::vector<uint8_t> gid;
|
||||
|
||||
bool isEpisode = false;
|
||||
|
||||
/**
|
||||
* @brief Returns an uri that can be allowed to query track information.
|
||||
*
|
||||
* @return std::string
|
||||
*/
|
||||
std::string getMercuryRequestUri();
|
||||
};
|
||||
|
||||
#endif
|
||||
98
components/spotify/cspot/include/Utils.h
Normal file
98
components/spotify/cspot/include/Utils.h
Normal file
@@ -0,0 +1,98 @@
|
||||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
#include "sys/socket.h"
|
||||
#include <cstdint>
|
||||
#include <netdb.h>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
#define HMAC_SHA1_BLOCKSIZE 64
|
||||
|
||||
/**
|
||||
* @brief Returns current timestamp
|
||||
*
|
||||
* @return unsigned long long resulting timestamp in milliseconds from unix time zero
|
||||
*/
|
||||
unsigned long long getCurrentTimestamp();
|
||||
|
||||
/**
|
||||
* @brief portable 64bit equivalent of htons / htonl. aka endianess swap
|
||||
*
|
||||
* @param value input value to swap
|
||||
* @return uint64_t swapped result
|
||||
*/
|
||||
uint64_t hton64(uint64_t value);
|
||||
|
||||
/**
|
||||
* @brief Performs big number multiplication on two numbers
|
||||
*
|
||||
* @param num big num in vector format
|
||||
* @param n secondary number
|
||||
* @return std::vector<uint8_t> resulting number
|
||||
*/
|
||||
std::vector<uint8_t> bigNumMultiply(std::vector<uint8_t> num, int n);
|
||||
|
||||
/**
|
||||
* @brief Performs big number addition on two numbers
|
||||
*
|
||||
* @param num big num in vector format
|
||||
* @param n secondary number
|
||||
* @return std::vector<uint8_t> resulting number
|
||||
*/
|
||||
std::vector<uint8_t> bigNumAdd(std::vector<uint8_t> num, int n);
|
||||
|
||||
unsigned char h2int(char c);
|
||||
|
||||
|
||||
std::string urlDecode(std::string str);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Converts provided bytes into a human readable hex string
|
||||
*
|
||||
* @param bytes vector containing binary data
|
||||
* @return std::string string containing hex representation of inputted data
|
||||
*/
|
||||
std::string bytesToHexString(std::vector<uint8_t> &bytes);
|
||||
|
||||
/**
|
||||
* @brief Extracts given type from binary data
|
||||
*
|
||||
* @tparam T type to extract
|
||||
* @param v vector containing binary data to extract from
|
||||
* @param pos position offset
|
||||
* @return T extracted type
|
||||
*/
|
||||
template <typename T>
|
||||
T extract(const std::vector<unsigned char> &v, int pos)
|
||||
{
|
||||
T value;
|
||||
memcpy(&value, &v[pos], sizeof(T));
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Packs given type into binary data
|
||||
*
|
||||
* @tparam T type of data to pack
|
||||
* @param data data to pack
|
||||
* @return std::vector<uint8_t> resulting vector containing binary data
|
||||
*/
|
||||
template <typename T>
|
||||
std::vector<uint8_t> pack(T data)
|
||||
{
|
||||
std::vector<std::uint8_t> rawData( (std::uint8_t*)&data, (std::uint8_t*)&(data) + sizeof(T));
|
||||
|
||||
return rawData;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
49
components/spotify/cspot/include/ZeroconfAuthenticator.h
Normal file
49
components/spotify/cspot/include/ZeroconfAuthenticator.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#ifndef ZEROCONFAUTHENTICATOR_H
|
||||
#define ZEROCONFAUTHENTICATOR_H
|
||||
|
||||
#include <vector>
|
||||
#include <unistd.h>
|
||||
#include <string>
|
||||
#include <BaseHTTPServer.h>
|
||||
#include <cstdlib>
|
||||
#include "Utils.h"
|
||||
#include "LoginBlob.h"
|
||||
#include "Crypto.h"
|
||||
#include "Task.h"
|
||||
#include "ConstantParameters.h"
|
||||
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
#include "mdns.h"
|
||||
#else
|
||||
#include "dns_sd.h"
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifndef SOCK_NONBLOCK
|
||||
#define SOCK_NONBLOCK O_NONBLOCK
|
||||
#endif
|
||||
|
||||
#define SERVER_PORT_MAX 65535 // Max usable tcp port
|
||||
#define SERVER_PORT_MIN 1024 // 0-1024 services ports
|
||||
|
||||
typedef std::function<void(std::shared_ptr<LoginBlob>)> authCallback;
|
||||
|
||||
class ZeroconfAuthenticator {
|
||||
private:
|
||||
int serverPort;
|
||||
bool authorized = false;
|
||||
std::unique_ptr<Crypto> crypto;
|
||||
std::shared_ptr<bell::BaseHTTPServer> server;
|
||||
authCallback gotBlobCallback;
|
||||
void startServer();
|
||||
std::string buildJsonInfo();
|
||||
void handleAddUser(std::map<std::string, std::string>& queryMap);
|
||||
void registerZeroconf();
|
||||
std::string getParameterFromUrlEncoded(std::string data, std::string param);
|
||||
public:
|
||||
ZeroconfAuthenticator(authCallback callback, std::shared_ptr<bell::BaseHTTPServer> httpServer);
|
||||
void registerHandlers();
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user