diff --git a/components/spotify/cspot/include/AccessKeyFetcher.h b/components/spotify/cspot/include/AccessKeyFetcher.h index 00a8afcf..11e5a73c 100644 --- a/components/spotify/cspot/include/AccessKeyFetcher.h +++ b/components/spotify/cspot/include/AccessKeyFetcher.h @@ -1,12 +1,12 @@ #pragma once -#include -#include - -#include "CSpotContext.h" -#include "Utils.h" +#include // for function +#include // for shared_ptr +#include // for string namespace cspot { +struct Context; + class AccessKeyFetcher { public: AccessKeyFetcher(std::shared_ptr ctx); diff --git a/components/spotify/cspot/include/ApResolve.h b/components/spotify/cspot/include/ApResolve.h index 6ebc7755..13b282a9 100644 --- a/components/spotify/cspot/include/ApResolve.h +++ b/components/spotify/cspot/include/ApResolve.h @@ -1,13 +1,9 @@ #pragma once -#include -#include - -#include "HTTPClient.h" +#include // for string #ifdef BELL_ONLY_CJSON #include "cJSON.h" #else -#include "nlohmann/json.hpp" #endif namespace cspot { diff --git a/components/spotify/cspot/include/AuthChallenges.h b/components/spotify/cspot/include/AuthChallenges.h index a14186b1..38d5a161 100644 --- a/components/spotify/cspot/include/AuthChallenges.h +++ b/components/spotify/cspot/include/AuthChallenges.h @@ -1,20 +1,14 @@ #pragma once -#include -#include -#include -#include -#include -#include +#include // for uint8_t +#include // for unique_ptr +#include // for string +#include // for vector -#include "Crypto.h" -#include "Logger.h" -#include "NanoPBHelper.h" -#include "Utils.h" - -#include "protobuf/authentication.pb.h" -#include "protobuf/keyexchange.pb.h" +#include "Crypto.h" // for Crypto +#include "protobuf/authentication.pb.h" // for ClientResponseEncrypted +#include "protobuf/keyexchange.pb.h" // for APResponseMessage, ClientHello namespace cspot { class AuthChallenges { diff --git a/components/spotify/cspot/include/CDNTrackStream.h b/components/spotify/cspot/include/CDNTrackStream.h index 3375a578..be7c2ca6 100644 --- a/components/spotify/cspot/include/CDNTrackStream.h +++ b/components/spotify/cspot/include/CDNTrackStream.h @@ -1,16 +1,20 @@ #pragma once -#include -#include -#include "Crypto.h" -#include "WrappedSemaphore.h" +#include // for size_t +#include // for uint8_t +#include // for shared_ptr, unique_ptr +#include // for string +#include // for vector -#include "Logger.h" -#include "Utils.h" -#include "CSpotContext.h" -#include "AccessKeyFetcher.h" +#include "Crypto.h" // for Crypto +#include "HTTPClient.h" // for HTTPClient + +namespace bell { +class WrappedSemaphore; +} // namespace bell namespace cspot { +class AccessKeyFetcher; class CDNTrackStream { diff --git a/components/spotify/cspot/include/CSpotContext.h b/components/spotify/cspot/include/CSpotContext.h index 65fca8e7..0237da85 100644 --- a/components/spotify/cspot/include/CSpotContext.h +++ b/components/spotify/cspot/include/CSpotContext.h @@ -5,6 +5,7 @@ #include "MercurySession.h" #include "TimeProvider.h" #include "protobuf/metadata.pb.h" +#include "LoginBlob.h" namespace cspot { struct Context { diff --git a/components/spotify/cspot/include/LoginBlob.h b/components/spotify/cspot/include/LoginBlob.h index e6a539ba..2a1f3611 100644 --- a/components/spotify/cspot/include/LoginBlob.h +++ b/components/spotify/cspot/include/LoginBlob.h @@ -1,17 +1,12 @@ #pragma once -#include -#include -#include -#ifndef BELL_ONLY_CJSON -#include -#endif -#include +#include // for uint8_t, uint32_t +#include // for map +#include // for unique_ptr +#include // for string +#include // for vector -#include "ConstantParameters.h" -#include "Crypto.h" - -#include "protobuf/authentication.pb.h" +#include "Crypto.h" // for CryptoMbedTLS, Crypto namespace cspot { class LoginBlob { diff --git a/components/spotify/cspot/include/MercurySession.h b/components/spotify/cspot/include/MercurySession.h index ea9c16ab..4f4126e7 100644 --- a/components/spotify/cspot/include/MercurySession.h +++ b/components/spotify/cspot/include/MercurySession.h @@ -1,22 +1,23 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include "BellTask.h" -#include "Logger.h" -#include "NanoPBHelper.h" -#include "Packet.h" -#include "Queue.h" -#include "Session.h" -#include "TimeProvider.h" -#include "Utils.h" -#include "protobuf/mercury.pb.h" +#include // for atomic +#include // for uint8_t, uint64_t, uint32_t +#include // for function +#include // for shared_ptr +#include // for mutex +#include // for string +#include // for unordered_map +#include // for vector + +#include "BellTask.h" // for Task +#include "Packet.h" // for Packet +#include "Queue.h" // for Queue +#include "Session.h" // for Session +#include "protobuf/mercury.pb.h" // for Header namespace cspot { +class TimeProvider; + class MercurySession : public bell::Task, public cspot::Session { public: MercurySession(std::shared_ptr timeProvider); diff --git a/components/spotify/cspot/include/PlainConnection.h b/components/spotify/cspot/include/PlainConnection.h index d52afa9d..6bb37b47 100644 --- a/components/spotify/cspot/include/PlainConnection.h +++ b/components/spotify/cspot/include/PlainConnection.h @@ -3,19 +3,15 @@ #ifdef _WIN32 #include #include + #include "win32shim.h" #else -#include -#include -#include "sys/socket.h" -#include +#include // for size_t #endif -#include -#include -#include -#include -#include "Packet.h" -#include "Utils.h" +#include // for uint8_t +#include // for function +#include // for string +#include // for vector typedef std::function timeoutCallback; diff --git a/components/spotify/cspot/include/PlaybackState.h b/components/spotify/cspot/include/PlaybackState.h index 48152273..b13fd71e 100644 --- a/components/spotify/cspot/include/PlaybackState.h +++ b/components/spotify/cspot/include/PlaybackState.h @@ -1,18 +1,14 @@ #pragma once -#include -#include -#include -#include -#include "CSpotContext.h" -#include "ConstantParameters.h" -#include "CspotAssert.h" -#include "TimeProvider.h" -#include "Utils.h" +#include // for uint8_t, uint32_t +#include // for shared_ptr +#include // for string +#include // for vector -#include "protobuf/spirc.pb.h" +#include "protobuf/spirc.pb.h" // for Frame, TrackRef, CapabilityType, Mess... namespace cspot { +struct Context; class PlaybackState { private: diff --git a/components/spotify/cspot/include/Session.h b/components/spotify/cspot/include/Session.h index 1cb9e573..df7f03c8 100644 --- a/components/spotify/cspot/include/Session.h +++ b/components/spotify/cspot/include/Session.h @@ -1,20 +1,16 @@ #pragma once -#include -#include -#include -#include +#include // for uint8_t +#include // for shared_ptr, unique_ptr +#include // for string +#include // for vector -#include "ApResolve.h" -#include "AuthChallenges.h" -#include "ConstantParameters.h" -#include "Logger.h" -#include "LoginBlob.h" -#include "Packet.h" -#include "PlainConnection.h" -#include "ShannonConnection.h" -#include "Utils.h" -#include "protobuf/mercury.pb.h" +namespace cspot { +class AuthChallenges; +class LoginBlob; +class PlainConnection; +class ShannonConnection; +} // namespace cspot #define LOGIN_REQUEST_COMMAND 0xAB #define AUTH_SUCCESSFUL_COMMAND 0xAC diff --git a/components/spotify/cspot/include/Shannon.h b/components/spotify/cspot/include/Shannon.h index 34107bf2..aa19bdb8 100644 --- a/components/spotify/cspot/include/Shannon.h +++ b/components/spotify/cspot/include/Shannon.h @@ -1,8 +1,9 @@ #ifndef SHANNON_H #define SHANNON_H -#include -#include +#include // for uint32_t, uint8_t +#include // for vector + class Shannon { public: diff --git a/components/spotify/cspot/include/ShannonConnection.h b/components/spotify/cspot/include/ShannonConnection.h index 0ebce671..b1a1fd01 100644 --- a/components/spotify/cspot/include/ShannonConnection.h +++ b/components/spotify/cspot/include/ShannonConnection.h @@ -1,18 +1,18 @@ #ifndef SHANNONCONNECTION_H #define SHANNONCONNECTION_H -#include -#include -#include -#include -#include +#include // for uint8_t, uint32_t +#include // for shared_ptr, unique_ptr +#include // for mutex +#include // for vector -#include "Packet.h" -#include "PlainConnection.h" -#include "Shannon.h" -#include -#include "Utils.h" -#include "Logger.h" +#include "Packet.h" // for Packet + +class Shannon; +namespace cspot { + +class PlainConnection; +} // namespace cspot #define MAC_SIZE 4 namespace cspot { diff --git a/components/spotify/cspot/include/SpircHandler.h b/components/spotify/cspot/include/SpircHandler.h index 9e52110e..5401088b 100644 --- a/components/spotify/cspot/include/SpircHandler.h +++ b/components/spotify/cspot/include/SpircHandler.h @@ -1,16 +1,20 @@ #pragma once -#include -#include "BellTask.h" +#include // for uint32_t, uint8_t +#include // for function +#include // for shared_ptr, unique_ptr +#include // for string +#include // for variant +#include // for vector -#include "CDNTrackStream.h" -#include "CSpotContext.h" -#include "PlaybackState.h" -#include "TrackPlayer.h" -#include "TrackProvider.h" -#include "protobuf/spirc.pb.h" +#include "CDNTrackStream.h" // for CDNTrackStream, CDNTrackStream::Track... +#include "PlaybackState.h" // for PlaybackState +#include "protobuf/spirc.pb.h" // for MessageType namespace cspot { +class TrackPlayer; +struct Context; + class SpircHandler { public: SpircHandler(std::shared_ptr ctx); diff --git a/components/spotify/cspot/include/TimeProvider.h b/components/spotify/cspot/include/TimeProvider.h index f131ab1d..b1f9b55d 100644 --- a/components/spotify/cspot/include/TimeProvider.h +++ b/components/spotify/cspot/include/TimeProvider.h @@ -1,9 +1,7 @@ #pragma once -#include -#include - -#include "Utils.h" +#include // for uint8_t +#include // for vector namespace cspot { class TimeProvider { diff --git a/components/spotify/cspot/include/TrackPlayer.h b/components/spotify/cspot/include/TrackPlayer.h index a96e5343..5eaf9f50 100644 --- a/components/spotify/cspot/include/TrackPlayer.h +++ b/components/spotify/cspot/include/TrackPlayer.h @@ -1,22 +1,31 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include "CDNTrackStream.h" -#include "CSpotContext.h" -#include "TrackProvider.h" -#include "TrackReference.h" +#include // for atomic +#include // for uint8_t, int64_t +#include // for size_t, time +#include // for function +#include // for shared_ptr, unique_ptr +#include // for mutex +#include // for string_view +#include // for vector + +#include "BellTask.h" // for Task +#include "CDNTrackStream.h" // for CDNTrackStream, CDNTrackStream::TrackInfo + +namespace bell { +class WrappedSemaphore; +} // namespace bell #ifdef BELL_VORBIS_FLOAT #include "vorbis/vorbisfile.h" #else -#include "ivorbisfile.h" +#include "ivorbisfile.h" // for OggVorbis_File, ov_callbacks #endif namespace cspot { +class TrackProvider; +struct Context; +struct TrackReference; + class TrackPlayer : bell::Task { public: typedef std::function TrackLoadedCallback; diff --git a/components/spotify/cspot/include/TrackProvider.h b/components/spotify/cspot/include/TrackProvider.h index fa8810fb..de2900fe 100644 --- a/components/spotify/cspot/include/TrackProvider.h +++ b/components/spotify/cspot/include/TrackProvider.h @@ -1,15 +1,18 @@ #pragma once -#include +#include // for uint8_t +#include // for shared_ptr, unique_ptr, weak_ptr +#include // for vector -#include "AccessKeyFetcher.h" -#include "CDNTrackStream.h" -#include "CSpotContext.h" -#include "TrackReference.h" -#include "protobuf/metadata.pb.h" -#include "protobuf/spirc.pb.h" +#include "MercurySession.h" // for MercurySession +#include "TrackReference.h" // for TrackReference +#include "protobuf/metadata.pb.h" // for Episode, Restriction, Track namespace cspot { +class AccessKeyFetcher; +class CDNTrackStream; +struct Context; + class TrackProvider { public: TrackProvider(std::shared_ptr ctx); @@ -23,11 +26,13 @@ class TrackProvider { std::unique_ptr cdnStream; Track trackInfo; + Episode episodeInfo; std::weak_ptr currentTrackReference; TrackReference trackIdInfo; void queryMetadata(); void onMetadataResponse(MercurySession::Response& res); + bool doRestrictionsApply(Restriction* restrictions, int count); void fetchFile(const std::vector& fileId, const std::vector& trackId); bool canPlayTrack(int index); diff --git a/components/spotify/cspot/include/TrackReference.h b/components/spotify/cspot/include/TrackReference.h index e257438f..ec87e16c 100644 --- a/components/spotify/cspot/include/TrackReference.h +++ b/components/spotify/cspot/include/TrackReference.h @@ -28,12 +28,11 @@ struct TrackReference { // Episode GID is being fetched via base62 encoded URI auto uri = std::string(ref->uri); auto idString = uri.substr(uri.find_last_of(":") + 1, uri.size()); - trackRef.gid = {0}; std::string_view alphabet(base62Alphabet); - for (int x = 0; x < uri.size(); x++) { - size_t d = alphabet.find(uri[x]); + for (int x = 0; x < idString.size(); x++) { + size_t d = alphabet.find(idString[x]); trackRef.gid = bigNumMultiply(trackRef.gid, 62); trackRef.gid = bigNumAdd(trackRef.gid, d); } diff --git a/components/spotify/cspot/include/Utils.h b/components/spotify/cspot/include/Utils.h index 052f62fa..95dcce11 100644 --- a/components/spotify/cspot/include/Utils.h +++ b/components/spotify/cspot/include/Utils.h @@ -1,27 +1,20 @@ #ifndef UTILS_H #define UTILS_H -#include +#include // for snprintf, size_t +#include // for vector #ifdef _WIN32 #include #include + #include "win32shim.h" #else -#include -#include "sys/socket.h" -#include -#include + #endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include // for uint8_t, uint64_t +#include // for memcpy +#include // for unique_ptr +#include // for runtime_error +#include // for string #define HMAC_SHA1_BLOCKSIZE 64 diff --git a/components/spotify/cspot/protobuf/metadata.options b/components/spotify/cspot/protobuf/metadata.options index 3758c5f6..a8314bfa 100644 --- a/components/spotify/cspot/protobuf/metadata.options +++ b/components/spotify/cspot/protobuf/metadata.options @@ -12,7 +12,8 @@ Album.name type: FT_POINTER Episode.gid type: FT_POINTER Episode.name type: FT_POINTER ImageGroup.image type: FT_POINTER -Episode.audio type: FT_POINTER +Episode.file type: FT_POINTER +Episode.restriction type: FT_POINTER Episode.covers type: FT_POINTER Restriction.countries_allowed type: FT_POINTER Restriction.countries_forbidden type: FT_POINTER \ No newline at end of file diff --git a/components/spotify/cspot/protobuf/metadata.proto b/components/spotify/cspot/protobuf/metadata.proto index 2e5ae222..6dc8a7c3 100644 --- a/components/spotify/cspot/protobuf/metadata.proto +++ b/components/spotify/cspot/protobuf/metadata.proto @@ -44,7 +44,8 @@ message Episode { optional bytes gid = 1; optional string name = 2; optional sint32 duration = 7; - repeated AudioFile audio = 12; + repeated AudioFile file = 12; + repeated Restriction restriction = 0x4B; optional ImageGroup covers = 0x44; } diff --git a/components/spotify/cspot/src/AccessKeyFetcher.cpp b/components/spotify/cspot/src/AccessKeyFetcher.cpp index 4856610f..d3d32237 100644 --- a/components/spotify/cspot/src/AccessKeyFetcher.cpp +++ b/components/spotify/cspot/src/AccessKeyFetcher.cpp @@ -1,7 +1,24 @@ #include "AccessKeyFetcher.h" -#include -#include "Logger.h" -#include "Utils.h" + +#include // for strrchr +#include // for initializer_list +#include // for operator!=, operator== +#include // for remove_extent_t +#include // for vector + +#include "BellLogger.h" // for AbstractLogger +#include "CSpotContext.h" // for Context +#include "Logger.h" // for CSPOT_LOG +#include "MercurySession.h" // for MercurySession, MercurySession::Res... +#include "Packet.h" // for cspot +#include "TimeProvider.h" // for TimeProvider +#include "Utils.h" // for string_format +#ifdef BELL_ONLY_CJSON +#include "cJSON.h" +#else +#include "nlohmann/json.hpp" // for basic_json<>::object_t, basic_json +#include "nlohmann/json_fwd.hpp" // for json +#endif using namespace cspot; @@ -38,18 +55,21 @@ void AccessKeyFetcher::getAccessKey(AccessKeyFetcher::Callback callback) { ctx->session->execute( MercurySession::RequestType::GET, url, [this, timeProvider, callback](MercurySession::Response& res) { - if (res.fail) return; + if (res.fail) + return; char* accessKeyJson = (char*)res.parts[0].data(); - auto accessJSON = std::string(accessKeyJson, strrchr(accessKeyJson, '}') - accessKeyJson + 1); + auto accessJSON = std::string( + accessKeyJson, strrchr(accessKeyJson, '}') - accessKeyJson + 1); #ifdef BELL_ONLY_CJSON cJSON* jsonBody = cJSON_Parse(accessJSON.c_str()); - this->accessKey = cJSON_GetObjectItem(jsonBody, "accessToken")->valuestring; + this->accessKey = + cJSON_GetObjectItem(jsonBody, "accessToken")->valuestring; int expiresIn = cJSON_GetObjectItem(jsonBody, "expiresIn")->valueint; #else auto jsonBody = nlohmann::json::parse(accessJSON); this->accessKey = jsonBody["accessToken"]; int expiresIn = jsonBody["expiresIn"]; -#endif +#endif expiresIn = expiresIn / 2; // Refresh token before it expires this->expiresAt = @@ -57,8 +77,8 @@ void AccessKeyFetcher::getAccessKey(AccessKeyFetcher::Callback callback) { #ifdef BELL_ONLY_CJSON callback(cJSON_GetObjectItem(jsonBody, "accessToken")->valuestring); cJSON_Delete(jsonBody); -#else +#else callback(jsonBody["accessToken"]); -#endif +#endif }); } diff --git a/components/spotify/cspot/src/ApResolve.cpp b/components/spotify/cspot/src/ApResolve.cpp index 277fed84..011f1c23 100644 --- a/components/spotify/cspot/src/ApResolve.cpp +++ b/components/spotify/cspot/src/ApResolve.cpp @@ -1,5 +1,19 @@ #include "ApResolve.h" +#include // for initializer_list +#include // for operator!=, operator== +#include // for allocator, unique_ptr +#include // for string_view +#include // for vector + +#include "HTTPClient.h" // for HTTPClient, HTTPClient::Response +#ifdef BELL_ONLY_CJSON +#include "cJSON.h" +#else +#include "nlohmann/json.hpp" // for basic_json<>::object_t, basic_json +#include "nlohmann/json_fwd.hpp" // for json +#endif + using namespace cspot; ApResolve::ApResolve(std::string apOverride) @@ -18,7 +32,7 @@ std::string ApResolve::fetchFirstApAddress() std::string_view responseStr = request->body(); // parse json with nlohmann -#if BELL_ONLY_CJSON +#ifdef BELL_ONLY_CJSON cJSON* json = cJSON_Parse(responseStr.data()); auto ap_string = std::string(cJSON_GetArrayItem(cJSON_GetObjectItem(json, "ap_list"), 0)->valuestring); cJSON_Delete(json); diff --git a/components/spotify/cspot/src/AuthChallenges.cpp b/components/spotify/cspot/src/AuthChallenges.cpp index 945c50f8..b1b48509 100644 --- a/components/spotify/cspot/src/AuthChallenges.cpp +++ b/components/spotify/cspot/src/AuthChallenges.cpp @@ -1,5 +1,13 @@ #include "AuthChallenges.h" +#include // for copy +#include // for CHAR_BIT +#include // for default_random_engine, independent_bits_en... + +#include "NanoPBHelper.h" // for pbPutString, pbEncode, pbDecode +#include "pb.h" // for pb_byte_t +#include "pb_decode.h" // for pb_release + using namespace cspot; using random_bytes_engine = std::independent_bits_engine; diff --git a/components/spotify/cspot/src/CDNTrackStream.cpp b/components/spotify/cspot/src/CDNTrackStream.cpp index 638a78d3..e38c6f13 100644 --- a/components/spotify/cspot/src/CDNTrackStream.cpp +++ b/components/spotify/cspot/src/CDNTrackStream.cpp @@ -1,5 +1,26 @@ #include "CDNTrackStream.h" +#include // for memcpy +#include // for __base +#include // for initializer_list +#include // for operator!=, operator== +#include // for string_view +#include // for remove_extent_t + +#include "AccessKeyFetcher.h" // for AccessKeyFetcher +#include "BellLogger.h" // for AbstractLogger +#include "Logger.h" // for CSPOT_LOG +#include "Packet.h" // for cspot +#include "SocketStream.h" // for SocketStream +#include "Utils.h" // for bigNumAdd, bytesToHexString, string... +#include "WrappedSemaphore.h" // for WrappedSemaphore +#ifdef BELL_ONLY_CJSON +#include "cJSON.h" +#else +#include "nlohmann/json.hpp" // for basic_json<>::object_t, basic_json +#include "nlohmann/json_fwd.hpp" // for json +#endif + using namespace cspot; CDNTrackStream::CDNTrackStream( @@ -10,8 +31,7 @@ CDNTrackStream::CDNTrackStream( this->crypto = std::make_unique(); } -CDNTrackStream::~CDNTrackStream() { -} +CDNTrackStream::~CDNTrackStream() {} void CDNTrackStream::fail() { this->status = Status::FAILED; @@ -40,7 +60,9 @@ void CDNTrackStream::fetchFile(const std::vector& trackId, #ifdef BELL_ONLY_CJSON cJSON* jsonResult = cJSON_Parse(result.data()); - std::string cdnUrl = cJSON_GetArrayItem(cJSON_GetObjectItem(jsonResult, "cdnurl"), 0)->valuestring; + std::string cdnUrl = + cJSON_GetArrayItem(cJSON_GetObjectItem(jsonResult, "cdnurl"), 0) + ->valuestring; cJSON_Delete(jsonResult); #else auto jsonResult = nlohmann::json::parse(result); diff --git a/components/spotify/cspot/src/LoginBlob.cpp b/components/spotify/cspot/src/LoginBlob.cpp index 411fdae6..4a1d8ca7 100644 --- a/components/spotify/cspot/src/LoginBlob.cpp +++ b/components/spotify/cspot/src/LoginBlob.cpp @@ -1,8 +1,18 @@ #include "LoginBlob.h" -#include "ConstantParameters.h" -#include "Logger.h" + +#include // for sprintf +#include // for initializer_list + +#include "BellLogger.h" // for AbstractLogger +#include "ConstantParameters.h" // for brandName, cspot, protoc... +#include "Logger.h" // for CSPOT_LOG +#include "protobuf/authentication.pb.h" // for AuthenticationType_AUTHE... #ifdef BELL_ONLY_CJSON #include "cJSON.h" +#else +#include "nlohmann/detail/json_pointer.hpp" // for json_pointer<>::string_t +#include "nlohmann/json.hpp" // for basic_json<>::object_t +#include "nlohmann/json_fwd.hpp" // for json #endif using namespace cspot; diff --git a/components/spotify/cspot/src/MercurySession.cpp b/components/spotify/cspot/src/MercurySession.cpp index 164a6dd4..6778cb76 100644 --- a/components/spotify/cspot/src/MercurySession.cpp +++ b/components/spotify/cspot/src/MercurySession.cpp @@ -1,11 +1,25 @@ #include "MercurySession.h" -#include -#include -#include "BellLogger.h" -#include "BellTask.h" -#include "BellUtils.h" -#include "CSpotContext.h" -#include "Logger.h" + +#include // for memcpy +#include // for shared_ptr +#include // for scoped_lock +#include // for runtime_error +#include // for remove_extent_t, __underlying_type_impl<>:... +#include // for pair + +#ifndef _WIN32 +#include +#endif + +#include "BellLogger.h" // for AbstractLogger +#include "BellTask.h" // for Task +#include "BellUtils.h" // for BELL_SLEEP_MS +#include "Logger.h" // for CSPOT_LOG +#include "NanoPBHelper.h" // for pbPutString, pbDecode, pbEncode +#include "PlainConnection.h" // for PlainConnection +#include "ShannonConnection.h" // for ShannonConnection +#include "TimeProvider.h" // for TimeProvider +#include "Utils.h" // for extract, pack, hton64 using namespace cspot; @@ -167,7 +181,7 @@ void MercurySession::handlePacket() { } void MercurySession::failAllPending() { - Response response = { }; + Response response = {}; response.fail = true; // Fail all callbacks diff --git a/components/spotify/cspot/src/PlainConnection.cpp b/components/spotify/cspot/src/PlainConnection.cpp index 7fbc6646..4ccb4eff 100644 --- a/components/spotify/cspot/src/PlainConnection.cpp +++ b/components/spotify/cspot/src/PlainConnection.cpp @@ -1,12 +1,24 @@ #include "PlainConnection.h" -#include + +#ifndef _WIN32 +#include // for addrinfo, freeaddrinfo, getaddrinfo +#include // for IPPROTO_IP, IPPROTO_TCP +#include // for EAGAIN, EINTR, ETIMEDOUT, errno +#include // for setsockopt, connect, recv, send, shutdown +#include // for timeval +#endif +#include // for memset +#include // for runtime_error #ifdef _WIN32 #include #else -#include +#include // for TCP_NODELAY +#include #endif -#include -#include "Logger.h" +#include "BellLogger.h" // for AbstractLogger +#include "Logger.h" // for CSPOT_LOG +#include "Packet.h" // for cspot +#include "Utils.h" // for extract, pack using namespace cspot; diff --git a/components/spotify/cspot/src/PlaybackState.cpp b/components/spotify/cspot/src/PlaybackState.cpp index 7ebc899f..fd8d7a41 100644 --- a/components/spotify/cspot/src/PlaybackState.cpp +++ b/components/spotify/cspot/src/PlaybackState.cpp @@ -1,7 +1,20 @@ #include "PlaybackState.h" -#include -#include "CSpotContext.h" -#include "Logger.h" + +#include // for strdup, memcpy, strcpy, strlen +#include // for uint8_t +#include // for free, NULL, realloc, rand +#include // for shared_ptr +#include // for remove_extent_t +#include // for swap + +#include "BellLogger.h" // for AbstractLogger +#include "CSpotContext.h" // for Context::ConfigState, Context (ptr o... +#include "ConstantParameters.h" // for protocolVersion, swVersion +#include "Logger.h" // for CSPOT_LOG +#include "NanoPBHelper.h" // for pbEncode, pbPutString +#include "Packet.h" // for cspot +#include "pb.h" // for pb_bytes_array_t, PB_BYTES_ARRAY_T_A... +#include "pb_decode.h" // for pb_release using namespace cspot; diff --git a/components/spotify/cspot/src/Session.cpp b/components/spotify/cspot/src/Session.cpp index 86a0ca03..74c3f18a 100644 --- a/components/spotify/cspot/src/Session.cpp +++ b/components/spotify/cspot/src/Session.cpp @@ -1,6 +1,21 @@ #include "Session.h" -#include -#include "AuthChallenges.h" + +#include // for CHAR_BIT +#include // for uint8_t +#include // for __base +#include // for shared_ptr, unique_ptr, make_unique +#include // for default_random_engine, independent_bi... +#include // for remove_extent_t +#include // for move + +#include "ApResolve.h" // for ApResolve, cspot +#include "AuthChallenges.h" // for AuthChallenges +#include "BellLogger.h" // for AbstractLogger +#include "Logger.h" // for CSPOT_LOG +#include "LoginBlob.h" // for LoginBlob +#include "Packet.h" // for Packet +#include "PlainConnection.h" // for PlainConnection, timeoutCallback +#include "ShannonConnection.h" // for ShannonConnection using random_bytes_engine = std::independent_bits_engine; diff --git a/components/spotify/cspot/src/Shannon.cpp b/components/spotify/cspot/src/Shannon.cpp index 7e289415..37615eee 100644 --- a/components/spotify/cspot/src/Shannon.cpp +++ b/components/spotify/cspot/src/Shannon.cpp @@ -1,9 +1,7 @@ #include "Shannon.h" -// #include -#include // for uint32_t -#include // for CHAR_BIT -// #define NDEBUG -#include + +#include // for CHAR_BIT +#include // for size_t using std::size_t; diff --git a/components/spotify/cspot/src/ShannonConnection.cpp b/components/spotify/cspot/src/ShannonConnection.cpp index 2b2f7029..173ebfbb 100644 --- a/components/spotify/cspot/src/ShannonConnection.cpp +++ b/components/spotify/cspot/src/ShannonConnection.cpp @@ -1,5 +1,17 @@ #include "ShannonConnection.h" -#include "Packet.h" + +#include // for remove_extent_t + +#ifndef _WIN32 +#include +#endif + +#include "BellLogger.h" // for AbstractLogger +#include "Logger.h" // for CSPOT_LOG +#include "Packet.h" // for Packet, cspot +#include "PlainConnection.h" // for PlainConnection +#include "Shannon.h" // for Shannon +#include "Utils.h" // for pack, extract using namespace cspot; diff --git a/components/spotify/cspot/src/SpircHandler.cpp b/components/spotify/cspot/src/SpircHandler.cpp index 002cd565..814a990b 100644 --- a/components/spotify/cspot/src/SpircHandler.cpp +++ b/components/spotify/cspot/src/SpircHandler.cpp @@ -1,14 +1,22 @@ #include "SpircHandler.h" -#include -#include "AccessKeyFetcher.h" -#include "BellUtils.h" -#include "CSpotContext.h" -#include "Logger.h" -#include "MercurySession.h" -#include "PlaybackState.h" -#include "TrackPlayer.h" -#include "TrackReference.h" -#include "protobuf/spirc.pb.h" + +#include // for uint8_t +#include // for shared_ptr, make_unique, unique_ptr +#include // for remove_extent_t +#include // for move + +#include "BellLogger.h" // for AbstractLogger +#include "CSpotContext.h" // for Context::ConfigState, Context (ptr only) +#include "Logger.h" // for CSPOT_LOG +#include "MercurySession.h" // for MercurySession, MercurySession::Response +#include "NanoPBHelper.h" // for pbDecode +#include "Packet.h" // for cspot +#include "PlaybackState.h" // for PlaybackState, PlaybackState::State +#include "TrackPlayer.h" // for TrackPlayer +#include "TrackReference.h" // for TrackReference +#include "Utils.h" // for stringHexToBytes +#include "pb_decode.h" // for pb_release +#include "protobuf/spirc.pb.h" // for Frame, State, Frame_fields, MessageTy... using namespace cspot; @@ -155,9 +163,9 @@ void SpircHandler::handleFrame(std::vector& data) { * when last track has been reached, we has to restart as we can't tell the difference */ if ((!isNextTrackPreloaded && this->playbackState.getNextTrackRef()) || isRequestedFromLoad) { CSPOT_LOG(debug, "Seek command while streaming current"); - sendEvent(EventType::SEEK, (int)playbackState.remoteFrame.position); playbackState.updatePositionMs(playbackState.remoteFrame.position); trackPlayer->seekMs(playbackState.remoteFrame.position); + sendEvent(EventType::SEEK, (int)playbackState.remoteFrame.position); } else { CSPOT_LOG(debug, "Seek command while streaming next or before started"); isRequestedFromLoad = true; diff --git a/components/spotify/cspot/src/TimeProvider.cpp b/components/spotify/cspot/src/TimeProvider.cpp index bda239bd..5e952449 100644 --- a/components/spotify/cspot/src/TimeProvider.cpp +++ b/components/spotify/cspot/src/TimeProvider.cpp @@ -1,5 +1,12 @@ #include "TimeProvider.h" -#include "Logger.h" + +#ifndef _WIN32 +#include +#endif + +#include "BellLogger.h" // for AbstractLogger +#include "Logger.h" // for CSPOT_LOG +#include "Utils.h" // for extract, getCurrentTimestamp using namespace cspot; diff --git a/components/spotify/cspot/src/TrackPlayer.cpp b/components/spotify/cspot/src/TrackPlayer.cpp index 2acd4f1b..99de08d7 100644 --- a/components/spotify/cspot/src/TrackPlayer.cpp +++ b/components/spotify/cspot/src/TrackPlayer.cpp @@ -1,12 +1,22 @@ #include "TrackPlayer.h" -#include -#include -#include -#include -#include -#include "CDNTrackStream.h" -#include "Logger.h" -#include "TrackReference.h" + +#include // for mutex, scoped_lock +#include // for string +#include // for remove_extent_t +#include // for vector, vector<>::value_type + +#include "BellLogger.h" // for AbstractLogger +#include "BellUtils.h" // for BELL_SLEEP_MS +#include "CDNTrackStream.h" // for CDNTrackStream, CDNTrackStream::TrackInfo +#include "Logger.h" // for CSPOT_LOG +#include "Packet.h" // for cspot +#include "TrackProvider.h" // for TrackProvider +#include "WrappedSemaphore.h" // for WrappedSemaphore + +namespace cspot { +struct Context; +struct TrackReference; +} // namespace cspot using namespace cspot; diff --git a/components/spotify/cspot/src/TrackProvider.cpp b/components/spotify/cspot/src/TrackProvider.cpp index 32abdc82..14b6ffd7 100644 --- a/components/spotify/cspot/src/TrackProvider.cpp +++ b/components/spotify/cspot/src/TrackProvider.cpp @@ -1,12 +1,26 @@ #include "TrackProvider.h" -#include -#include "AccessKeyFetcher.h" -#include "CDNTrackStream.h" -#include "Logger.h" -#include "MercurySession.h" -#include "TrackReference.h" -#include "Utils.h" -#include "protobuf/metadata.pb.h" + +#include // for assert +#include // for strlen +#include // for uint8_t +#include // for __base +#include // for shared_ptr, weak_ptr, make_shared +#include // for string, operator+ +#include // for remove_extent_t + +#include "AccessKeyFetcher.h" // for AccessKeyFetcher +#include "BellLogger.h" // for AbstractLogger +#include "CDNTrackStream.h" // for CDNTrackStream, CDNTrackStream::Tr... +#include "CSpotContext.h" // for Context::ConfigState, Context (ptr... +#include "Logger.h" // for CSPOT_LOG +#include "MercurySession.h" // for MercurySession, MercurySession::Da... +#include "NanoPBHelper.h" // for pbArrayToVector, pbDecode +#include "Packet.h" // for cspot +#include "TrackReference.h" // for TrackReference, TrackReference::Type +#include "Utils.h" // for bytesToHexString, string_format +#include "WrappedSemaphore.h" // for WrappedSemaphore +#include "pb_decode.h" // for pb_release +#include "protobuf/metadata.pb.h" // for Track, _Track, AudioFile, Episode using namespace cspot; @@ -21,9 +35,11 @@ TrackProvider::TrackProvider(std::shared_ptr ctx) { TrackProvider::~TrackProvider() { pb_release(Track_fields, &trackInfo); + pb_release(Episode_fields, &trackInfo); } -std::shared_ptr TrackProvider::loadFromTrackRef(TrackReference& trackRef) { +std::shared_ptr TrackProvider::loadFromTrackRef( + TrackReference& trackRef) { auto track = std::make_shared(this->accessKeyFetcher); this->currentTrackReference = track; this->trackIdInfo = trackRef; @@ -34,7 +50,8 @@ std::shared_ptr TrackProvider::loadFromTrackRef(TrackRefe void TrackProvider::queryMetadata() { std::string requestUrl = string_format( - "hm://metadata/3/%s/%s", trackIdInfo.type == TrackReference::Type::TRACK ? "track" : "episode", + "hm://metadata/3/%s/%s", + trackIdInfo.type == TrackReference::Type::TRACK ? "track" : "episode", bytesToHexString(trackIdInfo.gid).c_str()); CSPOT_LOG(debug, "Requesting track metadata from %s", requestUrl.c_str()); @@ -50,54 +67,40 @@ void TrackProvider::queryMetadata() { void TrackProvider::onMetadataResponse(MercurySession::Response& res) { CSPOT_LOG(debug, "Got track metadata response"); - pb_release(Track_fields, &trackInfo); - pbDecode(trackInfo, Track_fields, res.parts[0]); + int alternativeCount, filesCount = 0; + bool canPlay = false; + AudioFile* selectedFiles; + std::vector trackId, fileId; - CSPOT_LOG(info, "Track name: %s", trackInfo.name); - CSPOT_LOG(info, "Track duration: %d", trackInfo.duration); + if (trackIdInfo.type == TrackReference::Type::TRACK) { + pb_release(Track_fields, &trackInfo); + assert(res.parts.size() > 0); + pbDecode(trackInfo, Track_fields, res.parts[0]); + CSPOT_LOG(info, "Track name: %s", trackInfo.name); + CSPOT_LOG(info, "Track duration: %d", trackInfo.duration); - CSPOT_LOG(debug, "trackInfo.restriction.size() = %d", - trackInfo.restriction_count); + CSPOT_LOG(debug, "trackInfo.restriction.size() = %d", + trackInfo.restriction_count); - int altIndex = -1; - while (!canPlayTrack(altIndex)) { - altIndex++; - CSPOT_LOG(info, "Trying alternative %d", altIndex); - - if (altIndex >= trackInfo.alternative_count) { - // no alternatives for song - if (!this->currentTrackReference.expired()) { - auto trackRef = this->currentTrackReference.lock(); - trackRef->status = CDNTrackStream::Status::FAILED; - trackRef->trackReady->give(); + if (doRestrictionsApply(trackInfo.restriction, + trackInfo.restriction_count)) { + // Go through alternatives + for (int x = 0; x < trackInfo.alternative_count; x++) { + if (!doRestrictionsApply(trackInfo.alternative[x].restriction, + trackInfo.alternative[x].restriction_count)) { + selectedFiles = trackInfo.alternative[x].file; + filesCount = trackInfo.alternative[x].file_count; + trackId = pbArrayToVector(trackInfo.alternative[x].gid); + break; + } } - return; + } else { + selectedFiles = trackInfo.file; + filesCount = trackInfo.file_count; + trackId = pbArrayToVector(trackInfo.gid); } - } - std::vector trackId; - std::vector fileId; - - if (altIndex < 0) { - trackId = pbArrayToVector(trackInfo.gid); - for (int x = 0; x < trackInfo.file_count; x++) { - if (trackInfo.file[x].format == ctx->config.audioFormat) { - fileId = pbArrayToVector(trackInfo.file[x].file_id); - break; // If file found stop searching - } - } - } else { - trackId = pbArrayToVector(trackInfo.alternative[altIndex].gid); - for (int x = 0; x < trackInfo.alternative[altIndex].file_count; x++) { - if (trackInfo.alternative[altIndex].file[x].format == ctx->config.audioFormat) { - fileId = - pbArrayToVector(trackInfo.alternative[altIndex].file[x].file_id); - break; // If file found stop searching - } - } - } - - if (!this->currentTrackReference.expired()) { + // Set track's metadata auto trackRef = this->currentTrackReference.lock(); auto imageId = @@ -110,6 +113,60 @@ void TrackProvider::onMetadataResponse(MercurySession::Response& res) { trackRef->trackInfo.imageUrl = "https://i.scdn.co/image/" + bytesToHexString(imageId); trackRef->trackInfo.duration = trackInfo.duration; + } else { + pb_release(Episode_fields, &episodeInfo); + assert(res.parts.size() > 0); + pbDecode(episodeInfo, Episode_fields, res.parts[0]); + + CSPOT_LOG(info, "Episode name: %s", episodeInfo.name); + CSPOT_LOG(info, "Episode duration: %d", episodeInfo.duration); + + CSPOT_LOG(debug, "episodeInfo.restriction.size() = %d", + episodeInfo.restriction_count); + if (!doRestrictionsApply(episodeInfo.restriction, + episodeInfo.restriction_count)) { + selectedFiles = episodeInfo.file; + filesCount = episodeInfo.file_count; + trackId = pbArrayToVector(episodeInfo.gid); + } + + auto trackRef = this->currentTrackReference.lock(); + + auto imageId = pbArrayToVector(episodeInfo.covers->image[0].file_id); + + trackRef->trackInfo.trackId = bytesToHexString(trackIdInfo.gid); + trackRef->trackInfo.name = std::string(episodeInfo.name); + trackRef->trackInfo.album = ""; + trackRef->trackInfo.artist = "", + trackRef->trackInfo.imageUrl = + "https://i.scdn.co/image/" + bytesToHexString(imageId); + trackRef->trackInfo.duration = episodeInfo.duration; + } + + for (int x = 0; x < filesCount; x++) { + CSPOT_LOG(debug, "File format: %d", selectedFiles[x].format); + if (selectedFiles[x].format == ctx->config.audioFormat) { + fileId = pbArrayToVector(selectedFiles[x].file_id); + break; // If file found stop searching + } + + // Fallback to OGG Vorbis 96kbps + if (fileId.size() == 0 && + selectedFiles[x].format == AudioFormat_OGG_VORBIS_96) { + fileId = pbArrayToVector(selectedFiles[x].file_id); + } + } + + // No viable files found for playback + if (fileId.size() == 0) { + CSPOT_LOG(info, "File not available for playback"); + // no alternatives for song + if (!this->currentTrackReference.expired()) { + auto trackRef = this->currentTrackReference.lock(); + trackRef->status = CDNTrackStream::Status::FAILED; + trackRef->trackReady->give(); + } + return; } this->fetchFile(fileId, trackId); @@ -147,20 +204,25 @@ bool countryListContains(char* countryList, char* country) { return false; } +bool TrackProvider::doRestrictionsApply(Restriction* restrictions, int count) { + for (int x = 0; x < count; x++) { + if (restrictions[x].countries_allowed != nullptr) { + return !countryListContains(restrictions[x].countries_allowed, + (char*)ctx->config.countryCode.c_str()); + } + + if (restrictions[x].countries_forbidden != nullptr) { + return countryListContains(restrictions[x].countries_forbidden, + (char*)ctx->config.countryCode.c_str()); + } + } + + return false; +} + bool TrackProvider::canPlayTrack(int altIndex) { if (altIndex < 0) { - for (int x = 0; x < trackInfo.restriction_count; x++) { - if (trackInfo.restriction[x].countries_allowed != nullptr) { - return countryListContains(trackInfo.restriction[x].countries_allowed, - (char*)ctx->config.countryCode.c_str()); - } - if (trackInfo.restriction[x].countries_forbidden != nullptr) { - return !countryListContains( - trackInfo.restriction[x].countries_forbidden, - (char*)ctx->config.countryCode.c_str()); - } - } } else { for (int x = 0; x < trackInfo.alternative[altIndex].restriction_count; x++) { diff --git a/components/spotify/cspot/src/Utils.cpp b/components/spotify/cspot/src/Utils.cpp index a006b2a3..6983e3ae 100644 --- a/components/spotify/cspot/src/Utils.cpp +++ b/components/spotify/cspot/src/Utils.cpp @@ -1,168 +1,154 @@ #include "Utils.h" -#include -#include -#include -#include -#include -#include -#include -unsigned long long getCurrentTimestamp() -{ - return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); +#include // for strtol +#include // for operator<<, setfill, setw +#include // for basic_ostream, hex +#include // for stringstream +#include // for string +#include // for enable_if<>::type +#include +#ifndef _WIN32 +#include +#endif + +unsigned long long getCurrentTimestamp() { + return std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); } uint64_t hton64(uint64_t value) { - int num = 42; - if (*(char *)&num == 42) { - uint32_t high_part = htonl((uint32_t)(value >> 32)); - uint32_t low_part = htonl((uint32_t)(value & 0xFFFFFFFFLL)); - return (((uint64_t)low_part) << 32) | high_part; - } else { - return value; - } + int num = 42; + if (*(char*)&num == 42) { + uint32_t high_part = htonl((uint32_t)(value >> 32)); + uint32_t low_part = htonl((uint32_t)(value & 0xFFFFFFFFLL)); + return (((uint64_t)low_part) << 32) | high_part; + } else { + return value; + } } -std::vector stringHexToBytes(const std::string & s) { - std::vector v; - v.reserve(s.length() / 2); +std::vector stringHexToBytes(const std::string& s) { + std::vector v; + v.reserve(s.length() / 2); - for (std::string::size_type i = 0; i < s.length(); i += 2) { - std::string byteString = s.substr(i, 2); - uint8_t byte = (uint8_t) strtol(byteString.c_str(), NULL, 16); - v.push_back(byte); - } + for (std::string::size_type i = 0; i < s.length(); i += 2) { + std::string byteString = s.substr(i, 2); + uint8_t byte = (uint8_t)strtol(byteString.c_str(), NULL, 16); + v.push_back(byte); + } - return v; + return v; } std::string bytesToHexString(const std::vector& v) { - std::stringstream ss; - ss << std::hex << std::setfill('0'); - std::vector::const_iterator it; + std::stringstream ss; + ss << std::hex << std::setfill('0'); + std::vector::const_iterator it; - for (it = v.begin(); it != v.end(); it++) { - ss << std::setw(2) << static_cast(*it); - } + for (it = v.begin(); it != v.end(); it++) { + ss << std::setw(2) << static_cast(*it); + } - return ss.str(); + return ss.str(); } -std::vector bigNumAdd(std::vector num, int n) -{ - auto carry = n; - for (int x = num.size() - 1; x >= 0; x--) - { - int res = num[x] + carry; - if (res < 256) - { - carry = 0; - num[x] = res; - } - else - { - // Carry the rest of the division - carry = res / 256; - num[x] = res % 256; +std::vector bigNumAdd(std::vector num, int n) { + auto carry = n; + for (int x = num.size() - 1; x >= 0; x--) { + int res = num[x] + carry; + if (res < 256) { + carry = 0; + num[x] = res; + } else { + // Carry the rest of the division + carry = res / 256; + num[x] = res % 256; - // extend the vector at the last index - if (x == 0) - { - num.insert(num.begin(), carry); - return num; - } - } - } - - return num; -} - -std::vector bigNumDivide(std::vector num, int n) -{ - auto carry = 0; - for (int x = 0; x < num.size(); x++) - { - int res = num[x] + carry * 256; - if (res < n) - { - carry = res; - num[x] = 0; - } - else - { - // Carry the rest of the division - carry = res % n; - num[x] = res / n; - } - } - - return num; -} - -std::vector bigNumMultiply(std::vector num, int n) -{ - auto carry = 0; - for (int x = num.size() - 1; x >= 0; x--) - { - int res = num[x] * n + carry; - if (res < 256) - { - carry = 0; - num[x] = res; - } - else - { - // Carry the rest of the division - carry = res / 256; - num[x] = res % 256; - - // extend the vector at the last index - if (x == 0) - { - num.insert(num.begin(), carry); - return num; - } - } - } - - return num; -} -unsigned char 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 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; + // extend the vector at the last index + if (x == 0) { + num.insert(num.begin(), carry); + return num; } } - - return encodedString; + } + + return num; +} + +std::vector bigNumDivide(std::vector num, int n) { + auto carry = 0; + for (int x = 0; x < num.size(); x++) { + int res = num[x] + carry * 256; + if (res < n) { + carry = res; + num[x] = 0; + } else { + // Carry the rest of the division + carry = res % n; + num[x] = res / n; + } + } + + return num; +} + +std::vector bigNumMultiply(std::vector num, int n) { + auto carry = 0; + for (int x = num.size() - 1; x >= 0; x--) { + int res = num[x] * n + carry; + if (res < 256) { + carry = 0; + num[x] = res; + } else { + // Carry the rest of the division + carry = res / 256; + num[x] = res % 256; + + // extend the vector at the last index + if (x == 0) { + num.insert(num.begin(), carry); + return num; + } + } + } + + return num; +} +unsigned char 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 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; } \ No newline at end of file