Merge pull request #141 from awiouy/rustfmt

rustfmt
This commit is contained in:
Paul Liétar 2018-02-11 22:47:07 +01:00 committed by GitHub
commit 685c607c15
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 298 additions and 249 deletions

View file

@ -20,6 +20,10 @@ before_script:
- echo '[target.armv7-unknown-linux-gnueabihf]' > ~/.cargo/config - echo '[target.armv7-unknown-linux-gnueabihf]' > ~/.cargo/config
- echo 'linker = "arm-linux-gnueabihf-gcc"' >> ~/.cargo/config - echo 'linker = "arm-linux-gnueabihf-gcc"' >> ~/.cargo/config
- rustup target add armv7-unknown-linux-gnueabihf - rustup target add armv7-unknown-linux-gnueabihf
- if [[ $TRAVIS_RUST_VERSION == *"nightly"* ]]; then
rustup component add rustfmt-preview;
cargo fmt --package=librespot-core -- --write-mode=diff;
fi
script: script:
- cargo build --no-default-features - cargo build --no-default-features

View file

@ -1,36 +1,35 @@
extern crate vergen;
extern crate protobuf_macros; extern crate protobuf_macros;
extern crate rand; extern crate rand;
extern crate vergen;
use rand::Rng; use rand::Rng;
use std::env; use std::env;
use std::path::PathBuf;
use std::fs::OpenOptions; use std::fs::OpenOptions;
use std::io::Write; use std::io::Write;
use std::path::PathBuf;
fn main() { fn main() {
let out = PathBuf::from(env::var("OUT_DIR").unwrap()); let out = PathBuf::from(env::var("OUT_DIR").unwrap());
vergen::vergen(vergen::OutputFns::all()).unwrap(); vergen::vergen(vergen::OutputFns::all()).unwrap();
let build_id: String = rand::thread_rng() let build_id: String = rand::thread_rng().gen_ascii_chars().take(8).collect();
.gen_ascii_chars()
.take(8)
.collect();
let mut version_file = let mut version_file = OpenOptions::new()
OpenOptions::new()
.write(true) .write(true)
.append(true) .append(true)
.open(&out.join("version.rs")) .open(&out.join("version.rs"))
.unwrap(); .unwrap();
let build_id_fn = format!(" let build_id_fn = format!(
"
/// Generate a random build id. /// Generate a random build id.
pub fn build_id() -> &'static str {{ pub fn build_id() -> &'static str {{
\"{}\" \"{}\"
}} }}
", build_id); ",
build_id
);
if let Err(e) = version_file.write_all(build_id_fn.as_bytes()) { if let Err(e) = version_file.write_all(build_id_fn.as_bytes()) {
println!("{}", e); println!("{}", e);

View file

@ -1,17 +1,17 @@
const AP_FALLBACK: &'static str = "ap.spotify.com:80"; const AP_FALLBACK: &'static str = "ap.spotify.com:80";
const APRESOLVE_ENDPOINT: &'static str = "http://apresolve.spotify.com/"; const APRESOLVE_ENDPOINT: &'static str = "http://apresolve.spotify.com/";
use std::str::FromStr;
use futures::{Future, Stream}; use futures::{Future, Stream};
use hyper::{self, Uri, Client}; use hyper::{self, Client, Uri};
use serde_json; use serde_json;
use std::str::FromStr;
use tokio_core::reactor::Handle; use tokio_core::reactor::Handle;
error_chain!{} error_chain!{}
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct APResolveData { pub struct APResolveData {
ap_list: Vec<String> ap_list: Vec<String>,
} }
fn apresolve(handle: &Handle) -> Box<Future<Item = String, Error = Error>> { fn apresolve(handle: &Handle) -> Box<Future<Item = String, Error = Error>> {
@ -27,14 +27,10 @@ fn apresolve(handle: &Handle) -> Box<Future<Item=String, Error=Error>> {
}) })
}); });
let body = body.then(|result| result.chain_err(|| "HTTP error")); let body = body.then(|result| result.chain_err(|| "HTTP error"));
let body = body.and_then(|body| { let body = body.and_then(|body| String::from_utf8(body).chain_err(|| "invalid UTF8 in response"));
String::from_utf8(body).chain_err(|| "invalid UTF8 in response")
});
let data = body.and_then(|body| { let data =
serde_json::from_str::<APResolveData>(&body) body.and_then(|body| serde_json::from_str::<APResolveData>(&body).chain_err(|| "invalid JSON"));
.chain_err(|| "invalid JSON")
});
let ap = data.and_then(|data| { let ap = data.and_then(|data| {
let ap = data.ap_list.first().ok_or("empty AP List")?; let ap = data.ap_list.first().ok_or("empty AP List")?;
@ -44,9 +40,10 @@ fn apresolve(handle: &Handle) -> Box<Future<Item=String, Error=Error>> {
Box::new(ap) Box::new(ap)
} }
pub(crate) fn apresolve_or_fallback<E>(handle: &Handle) pub(crate) fn apresolve_or_fallback<E>(handle: &Handle) -> Box<Future<Item = String, Error = E>>
-> Box<Future<Item=String, Error=E>> where
where E: 'static { E: 'static,
{
let ap = apresolve(handle).or_else(|e| { let ap = apresolve(handle).or_else(|e| {
warn!("Failed to resolve Access Point: {}", e.description()); warn!("Failed to resolve Access Point: {}", e.description());
warn!("Using fallback \"{}\"", AP_FALLBACK); warn!("Using fallback \"{}\"", AP_FALLBACK);

View file

@ -1,12 +1,12 @@
use byteorder::{BigEndian, ByteOrder, WriteBytesExt}; use byteorder::{BigEndian, ByteOrder, WriteBytesExt};
use bytes::Bytes; use bytes::Bytes;
use futures::sync::oneshot;
use futures::{Async, Future, Poll}; use futures::{Async, Future, Poll};
use futures::sync::oneshot;
use std::collections::HashMap; use std::collections::HashMap;
use std::io::Write; use std::io::Write;
use util::{FileId, SpotifyId};
use util::SeqGenerator; use util::SeqGenerator;
use util::{SpotifyId, FileId};
#[derive(Debug, Hash, PartialEq, Eq, Copy, Clone)] #[derive(Debug, Hash, PartialEq, Eq, Copy, Clone)]
pub struct AudioKey(pub [u8; 16]); pub struct AudioKey(pub [u8; 16]);
@ -35,7 +35,11 @@ impl AudioKeyManager {
let _ = sender.send(Ok(AudioKey(key))); let _ = sender.send(Ok(AudioKey(key)));
} }
0xe => { 0xe => {
warn!("error audio key {:x} {:x}", data.as_ref()[0], data.as_ref()[1]); warn!(
"error audio key {:x} {:x}",
data.as_ref()[0],
data.as_ref()[1]
);
let _ = sender.send(Err(AudioKeyError)); let _ = sender.send(Err(AudioKeyError));
} }
_ => (), _ => (),
@ -81,4 +85,3 @@ impl <T> Future for AudioKeyFuture<T> {
} }
} }
} }

View file

@ -10,14 +10,13 @@ use protobuf::ProtobufEnum;
use rpassword; use rpassword;
use serde; use serde;
use serde_json; use serde_json;
use std::io::{self, stderr, Read, Write};
use std::fs::File; use std::fs::File;
use std::io::{self, stderr, Read, Write};
use std::path::Path; use std::path::Path;
use protocol::authentication::AuthenticationType; use protocol::authentication::AuthenticationType;
#[derive(Debug, Clone)] #[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Serialize, Deserialize)]
pub struct Credentials { pub struct Credentials {
pub username: String, pub username: String,
@ -89,12 +88,17 @@ impl Credentials {
let blob = { let blob = {
// Anyone know what this block mode is ? // Anyone know what this block mode is ?
let mut data = vec![0u8; encrypted_blob.len()]; let mut data = vec![0u8; encrypted_blob.len()];
let mut cipher = aes::ecb_decryptor(aes::KeySize::KeySize192, let mut cipher = aes::ecb_decryptor(
aes::KeySize::KeySize192,
&key, &key,
crypto::blockmodes::NoPadding); crypto::blockmodes::NoPadding,
cipher.decrypt(&mut crypto::buffer::RefReadBuffer::new(&encrypted_blob), );
cipher
.decrypt(
&mut crypto::buffer::RefReadBuffer::new(&encrypted_blob),
&mut crypto::buffer::RefWriteBuffer::new(&mut data), &mut crypto::buffer::RefWriteBuffer::new(&mut data),
true) true,
)
.unwrap(); .unwrap();
let l = encrypted_blob.len(); let l = encrypted_blob.len();
@ -112,7 +116,7 @@ impl Credentials {
let auth_type = read_int(&mut cursor).unwrap(); let auth_type = read_int(&mut cursor).unwrap();
let auth_type = AuthenticationType::from_i32(auth_type as i32).unwrap(); let auth_type = AuthenticationType::from_i32(auth_type as i32).unwrap();
read_u8(&mut cursor).unwrap(); read_u8(&mut cursor).unwrap();
let auth_data = read_bytes(&mut cursor).unwrap();; let auth_data = read_bytes(&mut cursor).unwrap();
Credentials { Credentials {
username: username, username: username,
@ -144,42 +148,49 @@ impl Credentials {
} }
fn serialize_protobuf_enum<T, S>(v: &T, ser: S) -> Result<S::Ok, S::Error> fn serialize_protobuf_enum<T, S>(v: &T, ser: S) -> Result<S::Ok, S::Error>
where T: ProtobufEnum, S: serde::Serializer { where
T: ProtobufEnum,
S: serde::Serializer,
{
serde::Serialize::serialize(&v.value(), ser) serde::Serialize::serialize(&v.value(), ser)
} }
fn deserialize_protobuf_enum<T, D>(de: D) -> Result<T, D::Error> fn deserialize_protobuf_enum<T, D>(de: D) -> Result<T, D::Error>
where T: ProtobufEnum, D: serde::Deserializer { where
T: ProtobufEnum,
D: serde::Deserializer,
{
let v: i32 = try!(serde::Deserialize::deserialize(de)); let v: i32 = try!(serde::Deserialize::deserialize(de));
T::from_i32(v).ok_or_else(|| serde::de::Error::custom("Invalid enum value")) T::from_i32(v).ok_or_else(|| serde::de::Error::custom("Invalid enum value"))
} }
fn serialize_base64<T, S>(v: &T, ser: S) -> Result<S::Ok, S::Error> fn serialize_base64<T, S>(v: &T, ser: S) -> Result<S::Ok, S::Error>
where T: AsRef<[u8]>, S: serde::Serializer { where
T: AsRef<[u8]>,
S: serde::Serializer,
{
serde::Serialize::serialize(&base64::encode(v.as_ref()), ser) serde::Serialize::serialize(&base64::encode(v.as_ref()), ser)
} }
fn deserialize_base64<D>(de: D) -> Result<Vec<u8>, D::Error> fn deserialize_base64<D>(de: D) -> Result<Vec<u8>, D::Error>
where D: serde::Deserializer { where
D: serde::Deserializer,
{
let v: String = try!(serde::Deserialize::deserialize(de)); let v: String = try!(serde::Deserialize::deserialize(de));
base64::decode(&v).map_err(|e| serde::de::Error::custom(e.to_string())) base64::decode(&v).map_err(|e| serde::de::Error::custom(e.to_string()))
} }
pub fn get_credentials(username: Option<String>, password: Option<String>, pub fn get_credentials(
cached_credentials: Option<Credentials>) username: Option<String>,
-> Option<Credentials> password: Option<String>,
{ cached_credentials: Option<Credentials>,
) -> Option<Credentials> {
match (username, password, cached_credentials) { match (username, password, cached_credentials) {
(Some(username), Some(password), _) => Some(Credentials::with_password(username, password)),
(Some(username), Some(password), _) (Some(ref username), _, Some(ref credentials)) if *username == credentials.username => {
=> Some(Credentials::with_password(username, password)), Some(credentials.clone())
}
(Some(ref username), _, Some(ref credentials))
if *username == credentials.username => Some(credentials.clone()),
(Some(username), None, _) => { (Some(username), None, _) => {
write!(stderr(), "Password for {}: ", username).unwrap(); write!(stderr(), "Password for {}: ", username).unwrap();
@ -188,8 +199,7 @@ pub fn get_credentials(username: Option<String>, password: Option<String>,
Some(Credentials::with_password(username.clone(), password)) Some(Credentials::with_password(username.clone(), password))
} }
(None, _, Some(credentials)) (None, _, Some(credentials)) => Some(credentials),
=> Some(credentials),
(None, _, None) => None, (None, _, None) => None,
} }

View file

@ -5,8 +5,8 @@ use std::io::Read;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use util::FileId;
use authentication::Credentials; use authentication::Credentials;
use util::FileId;
#[derive(Clone)] #[derive(Clone)]
pub struct Cache { pub struct Cache {
@ -31,7 +31,7 @@ impl Cache {
Cache { Cache {
root: location, root: location,
use_audio_cache: use_audio_cache use_audio_cache: use_audio_cache,
} }
} }
} }

View file

@ -1,7 +1,7 @@
use byteorder::{BigEndian, ByteOrder}; use byteorder::{BigEndian, ByteOrder};
use bytes::Bytes; use bytes::Bytes;
use futures::sync::{BiLock, mpsc}; use futures::{Async, Poll, Stream};
use futures::{Poll, Async, Stream}; use futures::sync::{mpsc, BiLock};
use std::collections::HashMap; use std::collections::HashMap;
use util::SeqGenerator; use util::SeqGenerator;

View file

@ -36,8 +36,8 @@ macro_rules! component {
} }
} }
use std::sync::Mutex;
use std::cell::UnsafeCell; use std::cell::UnsafeCell;
use std::sync::Mutex;
pub struct Lazy<T>(Mutex<bool>, UnsafeCell<Option<T>>); pub struct Lazy<T>(Mutex<bool>, UnsafeCell<Option<T>>);
unsafe impl<T: Sync> Sync for Lazy<T> {} unsafe impl<T: Sync> Sync for Lazy<T> {}

View file

@ -1,6 +1,6 @@
use uuid::Uuid;
use std::str::FromStr;
use std::fmt; use std::fmt;
use std::str::FromStr;
use uuid::Uuid;
use version; use version;
@ -20,7 +20,6 @@ impl Default for SessionConfig {
} }
} }
#[derive(Clone, Copy, Debug, Hash, PartialOrd, Ord, PartialEq, Eq)] #[derive(Clone, Copy, Debug, Hash, PartialOrd, Ord, PartialEq, Eq)]
pub enum Bitrate { pub enum Bitrate {
Bitrate96, Bitrate96,

View file

@ -1,5 +1,5 @@
use byteorder::{BigEndian, ByteOrder}; use byteorder::{BigEndian, ByteOrder};
use bytes::{Bytes, BytesMut, BufMut}; use bytes::{BufMut, Bytes, BytesMut};
use shannon::Shannon; use shannon::Shannon;
use std::io; use std::io;
use tokio_io::codec::{Decoder, Encoder}; use tokio_io::codec::{Decoder, Encoder};
@ -88,7 +88,8 @@ impl Decoder for APCodec {
let mut payload = buf.split_to(size + MAC_SIZE); let mut payload = buf.split_to(size + MAC_SIZE);
self.decode_cipher.decrypt(&mut payload.get_mut(..size).unwrap()); self.decode_cipher
.decrypt(&mut payload.get_mut(..size).unwrap());
let mac = payload.split_off(size); let mac = payload.split_off(size);
self.decode_cipher.check_mac(mac.as_ref())?; self.decode_cipher.check_mac(mac.as_ref())?;
@ -96,7 +97,6 @@ impl Decoder for APCodec {
} }
} }
Ok(None) Ok(None)
} }
} }

View file

@ -1,20 +1,21 @@
use crypto::sha1::Sha1; use byteorder::{BigEndian, ByteOrder, WriteBytesExt};
use crypto::hmac::Hmac; use crypto::hmac::Hmac;
use crypto::mac::Mac;use byteorder::{BigEndian, ByteOrder, WriteBytesExt}; use crypto::mac::Mac;
use crypto::sha1::Sha1;
use futures::{Async, Future, Poll};
use protobuf::{self, Message, MessageStatic}; use protobuf::{self, Message, MessageStatic};
use rand::thread_rng; use rand::thread_rng;
use std::io::{self, Read}; use std::io::{self, Read};
use std::marker::PhantomData; use std::marker::PhantomData;
use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::{AsyncRead, AsyncWrite};
use tokio_io::codec::Framed; use tokio_io::codec::Framed;
use tokio_io::io::{write_all, WriteAll, read_exact, ReadExact, Window}; use tokio_io::io::{read_exact, write_all, ReadExact, Window, WriteAll};
use futures::{Poll, Async, Future};
use super::codec::APCodec;
use diffie_hellman::DHLocalKeys; use diffie_hellman::DHLocalKeys;
use protocol; use protocol;
use protocol::keyexchange::{ClientHello, APResponseMessage, ClientResponsePlaintext}; use protocol::keyexchange::{APResponseMessage, ClientHello, ClientResponsePlaintext};
use util; use util;
use super::codec::APCodec;
pub struct Handshake<T> { pub struct Handshake<T> {
keys: DHLocalKeys, keys: DHLocalKeys,
@ -54,15 +55,15 @@ impl <T: AsyncRead + AsyncWrite> Future for Handshake<T> {
APResponse(ref mut read) => { APResponse(ref mut read) => {
let (connection, message, accumulator) = try_ready!(read.poll()); let (connection, message, accumulator) = try_ready!(read.poll());
let remote_key = message.get_challenge() let remote_key = message
.get_challenge()
.get_login_crypto_challenge() .get_login_crypto_challenge()
.get_diffie_hellman() .get_diffie_hellman()
.get_gs() .get_gs()
.to_owned(); .to_owned();
let shared_secret = self.keys.shared_secret(&remote_key); let shared_secret = self.keys.shared_secret(&remote_key);
let (challenge, send_key, recv_key) = compute_keys(&shared_secret, let (challenge, send_key, recv_key) = compute_keys(&shared_secret, &accumulator);
&accumulator);
let codec = APCodec::new(&send_key, &recv_key); let codec = APCodec::new(&send_key, &recv_key);
let write = client_response(connection, challenge); let write = client_response(connection, challenge);
@ -129,15 +130,17 @@ enum RecvPacket<T, M: MessageStatic> {
} }
fn recv_packet<T: AsyncRead, M>(connection: T, acc: Vec<u8>) -> RecvPacket<T, M> fn recv_packet<T: AsyncRead, M>(connection: T, acc: Vec<u8>) -> RecvPacket<T, M>
where T: Read, where
M: MessageStatic T: Read,
M: MessageStatic,
{ {
RecvPacket::Header(read_into_accumulator(connection, 4, acc), PhantomData) RecvPacket::Header(read_into_accumulator(connection, 4, acc), PhantomData)
} }
impl<T: AsyncRead, M> Future for RecvPacket<T, M> impl<T: AsyncRead, M> Future for RecvPacket<T, M>
where T: Read, where
M: MessageStatic T: Read,
M: MessageStatic,
{ {
type Item = (T, M, Vec<u8>); type Item = (T, M, Vec<u8>);
type Error = io::Error; type Error = io::Error;
@ -167,7 +170,11 @@ impl <T: AsyncRead, M> Future for RecvPacket<T, M>
} }
} }
fn read_into_accumulator<T: AsyncRead>(connection: T, size: usize, mut acc: Vec<u8>) -> ReadExact<T, Window<Vec<u8>>> { fn read_into_accumulator<T: AsyncRead>(
connection: T,
size: usize,
mut acc: Vec<u8>,
) -> ReadExact<T, Window<Vec<u8>>> {
let offset = acc.len(); let offset = acc.len();
acc.resize(offset + size, 0); acc.resize(offset + size, 0);
@ -191,5 +198,9 @@ fn compute_keys(shared_secret: &[u8], packets: &[u8]) -> (Vec<u8>, Vec<u8>, Vec<
mac = Hmac::new(Sha1::new(), &data[..0x14]); mac = Hmac::new(Sha1::new(), &data[..0x14]);
mac.input(packets); mac.input(packets);
(mac.result().code().to_vec(), data[0x14..0x34].to_vec(), data[0x34..0x54].to_vec()) (
mac.result().code().to_vec(),
data[0x14..0x34].to_vec(),
data[0x34..0x54].to_vec(),
)
} }

View file

@ -5,31 +5,34 @@ pub use self::codec::APCodec;
pub use self::handshake::handshake; pub use self::handshake::handshake;
use futures::{Future, Sink, Stream}; use futures::{Future, Sink, Stream};
use protobuf::{self, Message};
use std::io; use std::io;
use std::net::ToSocketAddrs; use std::net::ToSocketAddrs;
use tokio_core::net::TcpStream; use tokio_core::net::TcpStream;
use tokio_core::reactor::Handle; use tokio_core::reactor::Handle;
use tokio_io::codec::Framed; use tokio_io::codec::Framed;
use protobuf::{self, Message};
use authentication::Credentials; use authentication::Credentials;
use version; use version;
pub type Transport = Framed<TcpStream, APCodec>; pub type Transport = Framed<TcpStream, APCodec>;
pub fn connect<A: ToSocketAddrs>(addr: A, handle: &Handle) -> Box<Future<Item = Transport, Error = io::Error>> { pub fn connect<A: ToSocketAddrs>(
addr: A,
handle: &Handle,
) -> Box<Future<Item = Transport, Error = io::Error>> {
let addr = addr.to_socket_addrs().unwrap().next().unwrap(); let addr = addr.to_socket_addrs().unwrap().next().unwrap();
let socket = TcpStream::connect(&addr, handle); let socket = TcpStream::connect(&addr, handle);
let connection = socket.and_then(|socket| { let connection = socket.and_then(|socket| handshake(socket));
handshake(socket)
});
Box::new(connection) Box::new(connection)
} }
pub fn authenticate(transport: Transport, credentials: Credentials, device_id: String) pub fn authenticate(
-> Box<Future<Item = (Transport, Credentials), Error = io::Error>> transport: Transport,
{ credentials: Credentials,
device_id: String,
) -> Box<Future<Item = (Transport, Credentials), Error = io::Error>> {
use protocol::authentication::{APWelcome, ClientResponseEncrypted, CpuFamily, Os}; use protocol::authentication::{APWelcome, ClientResponseEncrypted, CpuFamily, Os};
let packet = protobuf_init!(ClientResponseEncrypted::new(), { let packet = protobuf_init!(ClientResponseEncrypted::new(), {
@ -50,13 +53,13 @@ pub fn authenticate(transport: Transport, credentials: Credentials, device_id: S
let cmd = 0xab; let cmd = 0xab;
let data = packet.write_to_bytes().unwrap(); let data = packet.write_to_bytes().unwrap();
Box::new(transport.send((cmd, data)).and_then(|transport| { Box::new(
transport.into_future().map_err(|(err, _stream)| err) transport
}).and_then(|(packet, transport)| { .send((cmd, data))
match packet { .and_then(|transport| transport.into_future().map_err(|(err, _stream)| err))
.and_then(|(packet, transport)| match packet {
Some((0xac, data)) => { Some((0xac, data)) => {
let welcome_data: APWelcome = let welcome_data: APWelcome = protobuf::parse_from_bytes(data.as_ref()).unwrap();
protobuf::parse_from_bytes(data.as_ref()).unwrap();
let reusable_credentials = Credentials { let reusable_credentials = Credentials {
username: welcome_data.get_canonical_username().to_owned(), username: welcome_data.get_canonical_username().to_owned(),
@ -70,6 +73,6 @@ pub fn authenticate(transport: Transport, credentials: Credentials, device_id: S
Some((0xad, _)) => panic!("Authentication failed"), Some((0xad, _)) => panic!("Authentication failed"),
Some((cmd, _)) => panic!("Unexpected packet {:?}", cmd), Some((cmd, _)) => panic!("Unexpected packet {:?}", cmd),
None => panic!("EOF"), None => panic!("EOF"),
} }),
})) )
} }

View file

@ -43,9 +43,11 @@ impl DHLocalKeys {
} }
pub fn shared_secret(&self, remote_key: &[u8]) -> Vec<u8> { pub fn shared_secret(&self, remote_key: &[u8]) -> Vec<u8> {
let shared_key = util::powm(&BigUint::from_bytes_be(remote_key), let shared_key = util::powm(
&BigUint::from_bytes_be(remote_key),
&self.private_key, &self.private_key,
&DH_PRIME); &DH_PRIME,
);
shared_key.to_bytes_be() shared_key.to_bytes_be()
} }
} }

View file

@ -13,9 +13,15 @@ pub struct Token {
pub scope: Vec<String>, pub scope: Vec<String>,
} }
pub fn get_token(session: &Session, client_id: &str, scopes: &str) -> Box<Future<Item = Token, Error = MercuryError>> { pub fn get_token(
let url = format!("hm://keymaster/token/authenticated?client_id={}&scope={}", session: &Session,
client_id, scopes); client_id: &str,
scopes: &str,
) -> Box<Future<Item = Token, Error = MercuryError>> {
let url = format!(
"hm://keymaster/token/authenticated?client_id={}&scope={}",
client_id, scopes
);
Box::new(session.mercury().get(url).map(move |response| { Box::new(session.mercury().get(url).map(move |response| {
let data = response.payload.first().expect("Empty payload"); let data = response.payload.first().expect("Empty payload");
let data = String::from_utf8(data.clone()).unwrap(); let data = String::from_utf8(data.clone()).unwrap();

View file

@ -1,10 +1,15 @@
#![cfg_attr(feature = "cargo-clippy", allow(unused_io_amount))] #![cfg_attr(feature = "cargo-clippy", allow(unused_io_amount))]
#[macro_use] extern crate error_chain; #[macro_use]
#[macro_use] extern crate futures; extern crate error_chain;
#[macro_use] extern crate lazy_static; #[macro_use]
#[macro_use] extern crate log; extern crate futures;
#[macro_use] extern crate serde_derive; #[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
#[macro_use]
extern crate serde_derive;
extern crate base64; extern crate base64;
extern crate byteorder; extern crate byteorder;
@ -26,7 +31,8 @@ extern crate uuid;
extern crate librespot_protocol as protocol; extern crate librespot_protocol as protocol;
#[macro_use] mod component; #[macro_use]
mod component;
mod apresolve; mod apresolve;
pub mod audio_key; pub mod audio_key;
pub mod authentication; pub mod authentication;

View file

@ -1,7 +1,7 @@
use byteorder::{BigEndian, ByteOrder}; use byteorder::{BigEndian, ByteOrder};
use bytes::Bytes; use bytes::Bytes;
use futures::sync::{oneshot, mpsc}; use futures::{Async, Future, Poll};
use futures::{Async, Poll, Future}; use futures::sync::{mpsc, oneshot};
use protobuf; use protobuf;
use protocol; use protocol;
use std::collections::HashMap; use std::collections::HashMap;
@ -51,9 +51,7 @@ impl MercuryManager {
seq seq
} }
pub fn request(&self, req: MercuryRequest) pub fn request(&self, req: MercuryRequest) -> MercuryFuture<MercuryResponse> {
-> MercuryFuture<MercuryResponse>
{
let (tx, rx) = oneshot::channel(); let (tx, rx) = oneshot::channel();
let pending = MercuryPending { let pending = MercuryPending {
@ -72,9 +70,7 @@ impl MercuryManager {
MercuryFuture(rx) MercuryFuture(rx)
} }
pub fn get<T: Into<String>>(&self, uri: T) pub fn get<T: Into<String>>(&self, uri: T) -> MercuryFuture<MercuryResponse> {
-> MercuryFuture<MercuryResponse>
{
self.request(MercuryRequest { self.request(MercuryRequest {
method: MercuryMethod::GET, method: MercuryMethod::GET,
uri: uri.into(), uri: uri.into(),
@ -83,9 +79,7 @@ impl MercuryManager {
}) })
} }
pub fn send<T: Into<String>>(&self, uri: T, data: Vec<u8>) pub fn send<T: Into<String>>(&self, uri: T, data: Vec<u8>) -> MercuryFuture<MercuryResponse> {
-> MercuryFuture<MercuryResponse>
{
self.request(MercuryRequest { self.request(MercuryRequest {
method: MercuryMethod::SEND, method: MercuryMethod::SEND,
uri: uri.into(), uri: uri.into(),
@ -98,9 +92,10 @@ impl MercuryManager {
MercurySender::new(self.clone(), uri.into()) MercurySender::new(self.clone(), uri.into())
} }
pub fn subscribe<T: Into<String>>(&self, uri: T) pub fn subscribe<T: Into<String>>(
-> Box<Future<Item = mpsc::UnboundedReceiver<MercuryResponse>, Error = MercuryError>> &self,
{ uri: T,
) -> Box<Future<Item = mpsc::UnboundedReceiver<MercuryResponse>, Error = MercuryError>> {
let uri = uri.into(); let uri = uri.into();
let request = self.request(MercuryRequest { let request = self.request(MercuryRequest {
method: MercuryMethod::SUB, method: MercuryMethod::SUB,
@ -118,8 +113,8 @@ impl MercuryManager {
if response.payload.len() > 0 { if response.payload.len() > 0 {
// Old subscription protocol, watch the provided list of URIs // Old subscription protocol, watch the provided list of URIs
for sub in response.payload { for sub in response.payload {
let mut sub : protocol::pubsub::Subscription let mut sub: protocol::pubsub::Subscription =
= protobuf::parse_from_bytes(&sub).unwrap(); protobuf::parse_from_bytes(&sub).unwrap();
let sub_uri = sub.take_uri(); let sub_uri = sub.take_uri();
debug!("subscribed sub_uri={}", sub_uri); debug!("subscribed sub_uri={}", sub_uri);
@ -147,13 +142,11 @@ impl MercuryManager {
let mut pending = match pending { let mut pending = match pending {
Some(pending) => pending, Some(pending) => pending,
None if cmd == 0xb5 => { None if cmd == 0xb5 => MercuryPending {
MercuryPending {
parts: Vec::new(), parts: Vec::new(),
partial: None, partial: None,
callback: None, callback: None,
} },
}
None => { None => {
warn!("Ignore seq {:?} cmd {:x}", seq, cmd); warn!("Ignore seq {:?} cmd {:x}", seq, cmd);
return; return;

View file

@ -1,5 +1,5 @@
use futures::{Async, AsyncSink, Future, Poll, Sink, StartSend};
use std::collections::VecDeque; use std::collections::VecDeque;
use futures::{Async, Poll, Future, Sink, StartSend, AsyncSink};
use super::*; use super::*;

View file

@ -37,8 +37,7 @@ impl ToString for MercuryMethod {
MercuryMethod::SUB => "SUB", MercuryMethod::SUB => "SUB",
MercuryMethod::UNSUB => "UNSUB", MercuryMethod::UNSUB => "UNSUB",
MercuryMethod::SEND => "SEND", MercuryMethod::SEND => "SEND",
} }.to_owned()
.to_owned()
} }
} }
@ -58,7 +57,9 @@ impl MercuryRequest {
packet.write_u16::<BigEndian>(seq.len() as u16).unwrap(); packet.write_u16::<BigEndian>(seq.len() as u16).unwrap();
packet.write_all(seq).unwrap(); packet.write_all(seq).unwrap();
packet.write_u8(1).unwrap(); // Flags: FINAL packet.write_u8(1).unwrap(); // Flags: FINAL
packet.write_u16::<BigEndian>(1 + self.payload.len() as u16).unwrap(); // Part count packet
.write_u16::<BigEndian>(1 + self.payload.len() as u16)
.unwrap(); // Part count
let mut header = protocol::mercury::Header::new(); let mut header = protocol::mercury::Header::new();
header.set_uri(self.uri.clone()); header.set_uri(self.uri.clone());
@ -68,7 +69,9 @@ impl MercuryRequest {
header.set_content_type(content_type.clone()); header.set_content_type(content_type.clone());
} }
packet.write_u16::<BigEndian>(header.compute_size() as u16).unwrap(); packet
.write_u16::<BigEndian>(header.compute_size() as u16)
.unwrap();
header.write_to_writer(&mut packet).unwrap(); header.write_to_writer(&mut packet).unwrap();
for p in &self.payload { for p in &self.payload {

View file

@ -1,19 +1,19 @@
use bytes::Bytes; use bytes::Bytes;
use crypto::digest::Digest; use crypto::digest::Digest;
use crypto::sha1::Sha1; use crypto::sha1::Sha1;
use futures::{Async, Future, IntoFuture, Poll, Stream};
use futures::sync::mpsc; use futures::sync::mpsc;
use futures::{Future, Stream, IntoFuture, Poll, Async};
use std::io; use std::io;
use std::sync::{RwLock, Arc, Weak}; use std::sync::{Arc, RwLock, Weak};
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
use tokio_core::reactor::{Handle, Remote}; use tokio_core::reactor::{Handle, Remote};
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
use apresolve::apresolve_or_fallback; use apresolve::apresolve_or_fallback;
use authentication::Credentials; use authentication::Credentials;
use cache::Cache; use cache::Cache;
use component::Lazy; use component::Lazy;
use connection;
use config::SessionConfig; use config::SessionConfig;
use connection;
use audio_key::AudioKeyManager; use audio_key::AudioKeyManager;
use channel::ChannelManager; use channel::ChannelManager;
@ -52,13 +52,14 @@ pub fn device_id(name: &str) -> String {
} }
impl Session { impl Session {
pub fn connect(config: SessionConfig, credentials: Credentials, pub fn connect(
cache: Option<Cache>, handle: Handle) config: SessionConfig,
-> Box<Future<Item=Session, Error=io::Error>> credentials: Credentials,
{ cache: Option<Cache>,
handle: Handle,
) -> Box<Future<Item = Session, Error = io::Error>> {
let access_point = apresolve_or_fallback::<io::Error>(&handle); let access_point = apresolve_or_fallback::<io::Error>(&handle);
let handle_ = handle.clone(); let handle_ = handle.clone();
let connection = access_point.and_then(move |addr| { let connection = access_point.and_then(move |addr| {
info!("Connecting to AP \"{}\"", addr); info!("Connecting to AP \"{}\"", addr);
@ -66,9 +67,8 @@ impl Session {
}); });
let device_id = config.device_id.clone(); let device_id = config.device_id.clone();
let authentication = connection.and_then(move |connection| { let authentication = connection
connection::authenticate(connection, credentials, device_id) .and_then(move |connection| connection::authenticate(connection, credentials, device_id));
});
let result = authentication.map(move |(transport, reusable_credentials)| { let result = authentication.map(move |(transport, reusable_credentials)| {
info!("Authenticated as \"{}\" !", reusable_credentials.username); info!("Authenticated as \"{}\" !", reusable_credentials.username);
@ -77,7 +77,11 @@ impl Session {
} }
let (session, task) = Session::create( let (session, task) = Session::create(
&handle, transport, config, cache, reusable_credentials.username.clone() &handle,
transport,
config,
cache,
reusable_credentials.username.clone(),
); );
handle.spawn(task.map_err(|e| panic!(e))); handle.spawn(task.map_err(|e| panic!(e)));
@ -88,10 +92,13 @@ impl Session {
Box::new(result) Box::new(result)
} }
fn create(handle: &Handle, transport: connection::Transport, fn create(
config: SessionConfig, cache: Option<Cache>, username: String) handle: &Handle,
-> (Session, Box<Future<Item = (), Error = io::Error>>) transport: connection::Transport,
{ config: SessionConfig,
cache: Option<Cache>,
username: String,
) -> (Session, Box<Future<Item = (), Error = io::Error>>) {
let (sink, stream) = transport.split(); let (sink, stream) = transport.split();
let (sender_tx, sender_rx) = mpsc::unbounded(); let (sender_tx, sender_rx) = mpsc::unbounded();
@ -121,11 +128,15 @@ impl Session {
let sender_task = sender_rx let sender_task = sender_rx
.map_err(|e| -> io::Error { panic!(e) }) .map_err(|e| -> io::Error { panic!(e) })
.forward(sink).map(|_| ()); .forward(sink)
.map(|_| ());
let receiver_task = DispatchTask(stream, session.weak()); let receiver_task = DispatchTask(stream, session.weak());
let task = Box::new((receiver_task, sender_task).into_future() let task = Box::new(
.map(|((), ())| ())); (receiver_task, sender_task)
.into_future()
.map(|((), ())| ()),
);
(session, task) (session, task)
} }
@ -143,16 +154,21 @@ impl Session {
} }
pub fn spawn<F, R>(&self, f: F) pub fn spawn<F, R>(&self, f: F)
where F: FnOnce(&Handle) -> R + Send + 'static, where
F: FnOnce(&Handle) -> R + Send + 'static,
R: IntoFuture<Item = (), Error = ()>, R: IntoFuture<Item = (), Error = ()>,
R::Future: 'static R::Future: 'static,
{ {
self.0.handle.spawn(f) self.0.handle.spawn(f)
} }
fn debug_info(&self) { fn debug_info(&self) {
debug!("Session[{}] strong={} weak={}", debug!(
self.0.session_id, Arc::strong_count(&self.0), Arc::weak_count(&self.0)); "Session[{}] strong={} weak={}",
self.0.session_id,
Arc::strong_count(&self.0),
Arc::weak_count(&self.0)
);
} }
#[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))] #[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
@ -161,7 +177,7 @@ impl Session {
0x4 => { 0x4 => {
self.debug_info(); self.debug_info();
self.send_packet(0x49, data.as_ref().to_owned()); self.send_packet(0x49, data.as_ref().to_owned());
}, }
0x4a => (), 0x4a => (),
0x1b => { 0x1b => {
let country = String::from_utf8(data.as_ref().to_owned()).unwrap(); let country = String::from_utf8(data.as_ref().to_owned()).unwrap();
@ -229,10 +245,12 @@ impl Drop for SessionInternal {
} }
struct DispatchTask<S>(S, SessionWeak) struct DispatchTask<S>(S, SessionWeak)
where S: Stream<Item = (u8, Bytes)>; where
S: Stream<Item = (u8, Bytes)>;
impl<S> Future for DispatchTask<S> impl<S> Future for DispatchTask<S>
where S: Stream<Item = (u8, Bytes)> where
S: Stream<Item = (u8, Bytes)>,
{ {
type Item = (); type Item = ();
type Error = S::Error; type Error = S::Error;
@ -240,9 +258,7 @@ impl <S> Future for DispatchTask<S>
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
let session = match self.1.try_upgrade() { let session = match self.1.try_upgrade() {
Some(session) => session, Some(session) => session,
None => { None => return Ok(Async::Ready(())),
return Ok(Async::Ready(()))
},
}; };
loop { loop {
@ -253,7 +269,8 @@ impl <S> Future for DispatchTask<S>
} }
impl<S> Drop for DispatchTask<S> impl<S> Drop for DispatchTask<S>
where S: Stream<Item = (u8, Bytes)> where
S: Stream<Item = (u8, Bytes)>,
{ {
fn drop(&mut self) { fn drop(&mut self) {
debug!("drop Dispatch"); debug!("drop Dispatch");

View file

@ -28,12 +28,7 @@ impl std::ops::Add<u128> for u128 {
type Output = u128; type Output = u128;
fn add(self, rhs: u128) -> u128 { fn add(self, rhs: u128) -> u128 {
let low = self.low + rhs.low; let low = self.low + rhs.low;
let high = self.high + rhs.high + let high = self.high + rhs.high + if low < self.low { 1 } else { 0 };
if low < self.low {
1
} else {
0
};
u128::from_parts(high, low) u128::from_parts(high, low)
} }
@ -43,12 +38,7 @@ impl<'a> std::ops::Add<&'a u128> for u128 {
type Output = u128; type Output = u128;
fn add(self, rhs: &'a u128) -> u128 { fn add(self, rhs: &'a u128) -> u128 {
let low = self.low + rhs.low; let low = self.low + rhs.low;
let high = self.high + rhs.high + let high = self.high + rhs.high + if low < self.low { 1 } else { 0 };
if low < self.low {
1
} else {
0
};
u128::from_parts(high, low) u128::from_parts(high, low)
} }
@ -60,20 +50,23 @@ impl std::convert::From<u8> for u128 {
} }
} }
impl std::ops::Mul<u128> for u128 { impl std::ops::Mul<u128> for u128 {
type Output = u128; type Output = u128;
fn mul(self, rhs: u128) -> u128 { fn mul(self, rhs: u128) -> u128 {
let top: [u64; 4] = [self.high >> 32, let top: [u64; 4] = [
self.high >> 32,
self.high & 0xFFFFFFFF, self.high & 0xFFFFFFFF,
self.low >> 32, self.low >> 32,
self.low & 0xFFFFFFFF]; self.low & 0xFFFFFFFF,
];
let bottom: [u64; 4] = [rhs.high >> 32, let bottom: [u64; 4] = [
rhs.high >> 32,
rhs.high & 0xFFFFFFFF, rhs.high & 0xFFFFFFFF,
rhs.low >> 32, rhs.low >> 32,
rhs.low & 0xFFFFFFFF]; rhs.low & 0xFFFFFFFF,
];
let mut rows = [u128::zero(); 16]; let mut rows = [u128::zero(); 16];
for i in 0..4 { for i in 0..4 {

View file

@ -1,7 +1,7 @@
use num_bigint::BigUint; use num_bigint::BigUint;
use num_traits::{Zero, One};
use num_integer::Integer; use num_integer::Integer;
use rand::{Rng, Rand}; use num_traits::{One, Zero};
use rand::{Rand, Rng};
use std::mem; use std::mem;
use std::ops::{Mul, Rem, Shr}; use std::ops::{Mul, Rem, Shr};
@ -9,7 +9,7 @@ mod int128;
mod spotify_id; mod spotify_id;
pub use util::int128::u128; pub use util::int128::u128;
pub use util::spotify_id::{SpotifyId, FileId}; pub use util::spotify_id::{FileId, SpotifyId};
pub fn rand_vec<G: Rng, R: Rand>(rng: &mut G, size: usize) -> Vec<R> { pub fn rand_vec<G: Rng, R: Rand>(rng: &mut G, size: usize) -> Vec<R> {
rng.gen_iter().take(size).collect() rng.gen_iter().take(size).collect()

View file

@ -1,7 +1,7 @@
use byteorder::{BigEndian, ByteOrder};
use std; use std;
use std::fmt; use std::fmt;
use util::u128; use util::u128;
use byteorder::{BigEndian, ByteOrder};
// Unneeded since 1.21 // Unneeded since 1.21
#[allow(unused_imports)] #[allow(unused_imports)]
use std::ascii::AsciiExt; use std::ascii::AsciiExt;
@ -9,8 +9,7 @@ use std::ascii::AsciiExt;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct SpotifyId(u128); pub struct SpotifyId(u128);
const BASE62_DIGITS: &'static [u8] = const BASE62_DIGITS: &'static [u8] = b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
const BASE16_DIGITS: &'static [u8] = b"0123456789abcdef"; const BASE16_DIGITS: &'static [u8] = b"0123456789abcdef";
impl SpotifyId { impl SpotifyId {

4
rustfmt.toml Normal file
View file

@ -0,0 +1,4 @@
max_width = 105
reorder_imports = true
reorder_imports_in_group = true
reorder_modules = true