mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
Merge pull request #1230 from dsheets/cargo-replace-hex-with-data-encoding
Cargo replace hex with data-encoding
This commit is contained in:
commit
a245a3c458
6 changed files with 45 additions and 28 deletions
8
Cargo.lock
generated
8
Cargo.lock
generated
|
@ -389,9 +389,9 @@ checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "data-encoding"
|
name = "data-encoding"
|
||||||
version = "2.4.0"
|
version = "2.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
|
checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "der"
|
name = "der"
|
||||||
|
@ -1331,10 +1331,10 @@ dependencies = [
|
||||||
name = "librespot"
|
name = "librespot"
|
||||||
version = "0.5.0-dev"
|
version = "0.5.0-dev"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"data-encoding",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"getopts",
|
"getopts",
|
||||||
"hex",
|
|
||||||
"librespot-audio",
|
"librespot-audio",
|
||||||
"librespot-connect",
|
"librespot-connect",
|
||||||
"librespot-core",
|
"librespot-core",
|
||||||
|
@ -1398,13 +1398,13 @@ dependencies = [
|
||||||
"base64 0.21.5",
|
"base64 0.21.5",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
"data-encoding",
|
||||||
"dns-sd",
|
"dns-sd",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"form_urlencoded",
|
"form_urlencoded",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"governor",
|
"governor",
|
||||||
"hex",
|
|
||||||
"hmac",
|
"hmac",
|
||||||
"http",
|
"http",
|
||||||
"httparse",
|
"httparse",
|
||||||
|
|
|
@ -50,10 +50,10 @@ path = "protocol"
|
||||||
version = "0.5.0-dev"
|
version = "0.5.0-dev"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
data-encoding = "2.5"
|
||||||
env_logger = { version = "0.10", default-features = false, features = ["color", "humantime", "auto-color"] }
|
env_logger = { version = "0.10", default-features = false, features = ["color", "humantime", "auto-color"] }
|
||||||
futures-util = { version = "0.3", default_features = false }
|
futures-util = { version = "0.3", default_features = false }
|
||||||
getopts = "0.2"
|
getopts = "0.2"
|
||||||
hex = "0.4"
|
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
rpassword = "7.0"
|
rpassword = "7.0"
|
||||||
sha1 = "0.10"
|
sha1 = "0.10"
|
||||||
|
|
|
@ -23,7 +23,6 @@ form_urlencoded = "1.0"
|
||||||
futures-core = "0.3"
|
futures-core = "0.3"
|
||||||
futures-util = { version = "0.3", features = ["alloc", "bilock", "sink", "unstable"] }
|
futures-util = { version = "0.3", features = ["alloc", "bilock", "sink", "unstable"] }
|
||||||
governor = { version = "0.6", default-features = false, features = ["std", "jitter"] }
|
governor = { version = "0.6", default-features = false, features = ["std", "jitter"] }
|
||||||
hex = "0.4"
|
|
||||||
hmac = "0.12"
|
hmac = "0.12"
|
||||||
httparse = "1.7"
|
httparse = "1.7"
|
||||||
http = "0.2"
|
http = "0.2"
|
||||||
|
@ -57,6 +56,7 @@ tokio-tungstenite = { version = "*", default-features = false, features = ["rust
|
||||||
tokio-util = { version = "0.7", features = ["codec"] }
|
tokio-util = { version = "0.7", features = ["codec"] }
|
||||||
url = "2"
|
url = "2"
|
||||||
uuid = { version = "1", default-features = false, features = ["fast-rng", "v4"] }
|
uuid = { version = "1", default-features = false, features = ["fast-rng", "v4"] }
|
||||||
|
data-encoding = "2.5"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
|
|
|
@ -16,17 +16,17 @@ pub struct SessionConfig {
|
||||||
pub autoplay: Option<bool>,
|
pub autoplay: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for SessionConfig {
|
impl SessionConfig {
|
||||||
fn default() -> SessionConfig {
|
pub(crate) fn default_for_os(os: &str) -> Self {
|
||||||
let device_id = uuid::Uuid::new_v4().as_hyphenated().to_string();
|
let device_id = uuid::Uuid::new_v4().as_hyphenated().to_string();
|
||||||
let client_id = match std::env::consts::OS {
|
let client_id = match os {
|
||||||
"android" => ANDROID_CLIENT_ID,
|
"android" => ANDROID_CLIENT_ID,
|
||||||
"ios" => IOS_CLIENT_ID,
|
"ios" => IOS_CLIENT_ID,
|
||||||
_ => KEYMASTER_CLIENT_ID,
|
_ => KEYMASTER_CLIENT_ID,
|
||||||
}
|
}
|
||||||
.to_owned();
|
.to_owned();
|
||||||
|
|
||||||
SessionConfig {
|
Self {
|
||||||
client_id,
|
client_id,
|
||||||
device_id,
|
device_id,
|
||||||
proxy: None,
|
proxy: None,
|
||||||
|
@ -37,6 +37,12 @@ impl Default for SessionConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for SessionConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::default_for_os(std::env::consts::OS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Hash, PartialOrd, Ord, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, Hash, PartialOrd, Ord, PartialEq, Eq)]
|
||||||
pub enum DeviceType {
|
pub enum DeviceType {
|
||||||
Unknown = 0,
|
Unknown = 0,
|
||||||
|
|
|
@ -6,6 +6,7 @@ use std::{
|
||||||
|
|
||||||
use byteorder::{BigEndian, ByteOrder};
|
use byteorder::{BigEndian, ByteOrder};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
|
use data_encoding::HEXUPPER_PERMISSIVE;
|
||||||
use futures_util::future::IntoStream;
|
use futures_util::future::IntoStream;
|
||||||
use http::header::HeaderValue;
|
use http::header::HeaderValue;
|
||||||
use hyper::{
|
use hyper::{
|
||||||
|
@ -189,9 +190,10 @@ impl SpClient {
|
||||||
// on macOS and Windows. On Android and iOS we can send a platform-specific client ID and are
|
// on macOS and Windows. On Android and iOS we can send a platform-specific client ID and are
|
||||||
// then presented with a hash cash challenge. On Linux, we have to pass the old keymaster ID.
|
// then presented with a hash cash challenge. On Linux, we have to pass the old keymaster ID.
|
||||||
// We delegate most of this logic to `SessionConfig`.
|
// We delegate most of this logic to `SessionConfig`.
|
||||||
let client_id = match OS {
|
let os = OS;
|
||||||
|
let client_id = match os {
|
||||||
"macos" | "windows" => self.session().client_id(),
|
"macos" | "windows" => self.session().client_id(),
|
||||||
_ => SessionConfig::default().client_id,
|
os => SessionConfig::default_for_os(os).client_id,
|
||||||
};
|
};
|
||||||
client_data.client_id = client_id;
|
client_data.client_id = client_id;
|
||||||
|
|
||||||
|
@ -206,7 +208,7 @@ impl SpClient {
|
||||||
let os_version = sys.os_version().unwrap_or_else(|| String::from("0"));
|
let os_version = sys.os_version().unwrap_or_else(|| String::from("0"));
|
||||||
let kernel_version = sys.kernel_version().unwrap_or_else(|| String::from("0"));
|
let kernel_version = sys.kernel_version().unwrap_or_else(|| String::from("0"));
|
||||||
|
|
||||||
match OS {
|
match os {
|
||||||
"windows" => {
|
"windows" => {
|
||||||
let os_version = os_version.parse::<f32>().unwrap_or(10.) as i32;
|
let os_version = os_version.parse::<f32>().unwrap_or(10.) as i32;
|
||||||
let kernel_version = kernel_version.parse::<i32>().unwrap_or(21370);
|
let kernel_version = kernel_version.parse::<i32>().unwrap_or(21370);
|
||||||
|
@ -269,7 +271,10 @@ impl SpClient {
|
||||||
match ClientTokenResponseType::from_i32(message.response_type.value()) {
|
match ClientTokenResponseType::from_i32(message.response_type.value()) {
|
||||||
// depending on the platform, you're either given a token immediately
|
// depending on the platform, you're either given a token immediately
|
||||||
// or are presented a hash cash challenge to solve first
|
// or are presented a hash cash challenge to solve first
|
||||||
Some(ClientTokenResponseType::RESPONSE_GRANTED_TOKEN_RESPONSE) => break message,
|
Some(ClientTokenResponseType::RESPONSE_GRANTED_TOKEN_RESPONSE) => {
|
||||||
|
debug!("Received a granted token");
|
||||||
|
break message;
|
||||||
|
}
|
||||||
Some(ClientTokenResponseType::RESPONSE_CHALLENGES_RESPONSE) => {
|
Some(ClientTokenResponseType::RESPONSE_CHALLENGES_RESPONSE) => {
|
||||||
debug!("Received a hash cash challenge, solving...");
|
debug!("Received a hash cash challenge, solving...");
|
||||||
|
|
||||||
|
@ -279,20 +284,22 @@ impl SpClient {
|
||||||
let hash_cash_challenge = challenge.evaluate_hashcash_parameters();
|
let hash_cash_challenge = challenge.evaluate_hashcash_parameters();
|
||||||
|
|
||||||
let ctx = vec![];
|
let ctx = vec![];
|
||||||
let prefix = hex::decode(&hash_cash_challenge.prefix).map_err(|e| {
|
let prefix = HEXUPPER_PERMISSIVE
|
||||||
|
.decode(hash_cash_challenge.prefix.as_bytes())
|
||||||
|
.map_err(|e| {
|
||||||
Error::failed_precondition(format!(
|
Error::failed_precondition(format!(
|
||||||
"Unable to decode hash cash challenge: {e}"
|
"Unable to decode hash cash challenge: {e}"
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
let length = hash_cash_challenge.length;
|
let length = hash_cash_challenge.length;
|
||||||
|
|
||||||
let mut suffix = vec![0; 0x10];
|
let mut suffix = [0u8; 0x10];
|
||||||
let answer = Self::solve_hash_cash(&ctx, &prefix, length, &mut suffix);
|
let answer = Self::solve_hash_cash(&ctx, &prefix, length, &mut suffix);
|
||||||
|
|
||||||
match answer {
|
match answer {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
// the suffix must be in uppercase
|
// the suffix must be in uppercase
|
||||||
let suffix = hex::encode(suffix).to_uppercase();
|
let suffix = HEXUPPER_PERMISSIVE.encode(&suffix);
|
||||||
|
|
||||||
let mut answer_message = ClientTokenRequest::new();
|
let mut answer_message = ClientTokenRequest::new();
|
||||||
answer_message.request_type =
|
answer_message.request_type =
|
||||||
|
@ -302,7 +309,7 @@ impl SpClient {
|
||||||
let challenge_answers = answer_message.mut_challenge_answers();
|
let challenge_answers = answer_message.mut_challenge_answers();
|
||||||
|
|
||||||
let mut challenge_answer = ChallengeAnswer::new();
|
let mut challenge_answer = ChallengeAnswer::new();
|
||||||
challenge_answer.mut_hash_cash().suffix = suffix.to_string();
|
challenge_answer.mut_hash_cash().suffix = suffix;
|
||||||
challenge_answer.ChallengeType =
|
challenge_answer.ChallengeType =
|
||||||
ChallengeType::CHALLENGE_HASH_CASH.into();
|
ChallengeType::CHALLENGE_HASH_CASH.into();
|
||||||
|
|
||||||
|
@ -477,11 +484,14 @@ impl SpClient {
|
||||||
HeaderValue::from_str(&format!("{} {}", token.token_type, token.access_token,))?,
|
HeaderValue::from_str(&format!("{} {}", token.token_type, token.access_token,))?,
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Ok(client_token) = self.client_token().await {
|
match self.client_token().await {
|
||||||
headers_mut.insert(CLIENT_TOKEN, HeaderValue::from_str(&client_token)?);
|
Ok(client_token) => {
|
||||||
} else {
|
let _ = headers_mut.insert(CLIENT_TOKEN, HeaderValue::from_str(&client_token)?);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
// currently these endpoints seem to work fine without it
|
// currently these endpoints seem to work fine without it
|
||||||
warn!("Unable to get client token. Trying to continue without...");
|
warn!("Unable to get client token: {e} Trying to continue without...")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
last_response = self.session().http_client().request_body(request).await;
|
last_response = self.session().http_client().request_body(request).await;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use data_encoding::HEXLOWER;
|
||||||
use futures_util::StreamExt;
|
use futures_util::StreamExt;
|
||||||
use log::{debug, error, info, trace, warn};
|
use log::{debug, error, info, trace, warn};
|
||||||
use sha1::{Digest, Sha1};
|
use sha1::{Digest, Sha1};
|
||||||
|
@ -39,7 +40,7 @@ mod player_event_handler;
|
||||||
use player_event_handler::{run_program_on_sink_events, EventHandler};
|
use player_event_handler::{run_program_on_sink_events, EventHandler};
|
||||||
|
|
||||||
fn device_id(name: &str) -> String {
|
fn device_id(name: &str) -> String {
|
||||||
hex::encode(Sha1::digest(name.as_bytes()))
|
HEXLOWER.encode(&Sha1::digest(name.as_bytes()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(program: &str, opts: &getopts::Options) -> String {
|
fn usage(program: &str, opts: &getopts::Options) -> String {
|
||||||
|
|
Loading…
Reference in a new issue