This commit is contained in:
Paul Lietar 2016-01-02 16:19:39 +01:00
parent 5464647164
commit 90eeed3f80
22 changed files with 380 additions and 342 deletions

View file

@ -6,21 +6,18 @@ use std::ops::Add;
use audio_key::AudioKey; use audio_key::AudioKey;
const AUDIO_AESIV : &'static [u8] = &[ const AUDIO_AESIV: &'static [u8] = &[0x72, 0xe0, 0x67, 0xfb, 0xdd, 0xcb, 0xcf, 0x77, 0xeb, 0xe8,
0x72,0xe0,0x67,0xfb,0xdd,0xcb,0xcf,0x77,0xeb,0xe8,0xbc,0x64,0x3f,0x63,0x0d,0x93, 0xbc, 0x64, 0x3f, 0x63, 0x0d, 0x93];
];
pub struct AudioDecrypt<T : io::Read> { pub struct AudioDecrypt<T: io::Read> {
cipher: Box<SynchronousStreamCipher + 'static>, cipher: Box<SynchronousStreamCipher + 'static>,
key: AudioKey, key: AudioKey,
reader: T, reader: T,
} }
impl <T : io::Read> AudioDecrypt<T> { impl<T: io::Read> AudioDecrypt<T> {
pub fn new(key: AudioKey, reader: T) -> AudioDecrypt<T> { pub fn new(key: AudioKey, reader: T) -> AudioDecrypt<T> {
let cipher = aes::ctr(aes::KeySize::KeySize128, let cipher = aes::ctr(aes::KeySize::KeySize128, &key, AUDIO_AESIV);
&key,
AUDIO_AESIV);
AudioDecrypt { AudioDecrypt {
cipher: cipher, cipher: cipher,
key: key, key: key,
@ -29,7 +26,7 @@ impl <T : io::Read> AudioDecrypt<T> {
} }
} }
impl <T : io::Read> io::Read for AudioDecrypt<T> { impl<T: io::Read> io::Read for AudioDecrypt<T> {
fn read(&mut self, output: &mut [u8]) -> io::Result<usize> { fn read(&mut self, output: &mut [u8]) -> io::Result<usize> {
let mut buffer = vec![0u8; output.len()]; let mut buffer = vec![0u8; output.len()];
let len = try!(self.reader.read(&mut buffer)); let len = try!(self.reader.read(&mut buffer));
@ -40,17 +37,15 @@ impl <T : io::Read> io::Read for AudioDecrypt<T> {
} }
} }
impl <T : io::Read + io::Seek> io::Seek for AudioDecrypt<T> { impl<T: io::Read + io::Seek> io::Seek for AudioDecrypt<T> {
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> { fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
let newpos = try!(self.reader.seek(pos)); let newpos = try!(self.reader.seek(pos));
let skip = newpos % 16; let skip = newpos % 16;
let iv = BigUint::from_bytes_be(AUDIO_AESIV) let iv = BigUint::from_bytes_be(AUDIO_AESIV)
.add(BigUint::from_u64(newpos / 16).unwrap()) .add(BigUint::from_u64(newpos / 16).unwrap())
.to_bytes_be(); .to_bytes_be();
self.cipher = aes::ctr(aes::KeySize::KeySize128, self.cipher = aes::ctr(aes::KeySize::KeySize128, &self.key, &iv);
&self.key,
&iv);
let buf = vec![0u8; skip as usize]; let buf = vec![0u8; skip as usize];
let mut buf2 = vec![0u8; skip as usize]; let mut buf2 = vec![0u8; skip as usize];
@ -59,5 +54,3 @@ impl <T : io::Read + io::Seek> io::Seek for AudioDecrypt<T> {
Ok(newpos as u64) Ok(newpos as u64)
} }
} }

View file

@ -13,11 +13,11 @@ use util::{FileId, IgnoreExt, ZeroFile, mkdir_existing};
use session::Session; use session::Session;
use stream::StreamEvent; use stream::StreamEvent;
const CHUNK_SIZE : usize = 0x20000; const CHUNK_SIZE: usize = 0x20000;
pub enum AudioFile { pub enum AudioFile {
Direct(fs::File), Direct(fs::File),
Loading(AudioFileLoading) Loading(AudioFileLoading),
} }
pub struct AudioFileLoading { pub struct AudioFileLoading {
@ -43,15 +43,18 @@ impl AudioFileLoading {
let read_file = files_iter.next().unwrap(); let read_file = files_iter.next().unwrap();
let mut write_file = files_iter.next().unwrap(); let mut write_file = files_iter.next().unwrap();
let size = session.stream(file_id, 0, 1).into_iter() let size = session.stream(file_id, 0, 1)
.filter_map(|event| { .into_iter()
match event { .filter_map(|event| {
StreamEvent::Header(id, ref data) if id == 0x3 => { match event {
Some(BigEndian::read_u32(data) as usize * 4) StreamEvent::Header(id, ref data) if id == 0x3 => {
} Some(BigEndian::read_u32(data) as usize * 4)
_ => None }
} _ => None,
}).next().unwrap(); }
})
.next()
.unwrap();
let chunk_count = (size + CHUNK_SIZE - 1) / CHUNK_SIZE; let chunk_count = (size + CHUNK_SIZE - 1) / CHUNK_SIZE;
@ -70,9 +73,7 @@ impl AudioFileLoading {
let _shared = shared.clone(); let _shared = shared.clone();
let _session = session.clone(); let _session = session.clone();
thread::spawn(move || { thread::spawn(move || AudioFileLoading::fetch(&_session, _shared, write_file, seek_rx));
AudioFileLoading::fetch(&_session, _shared, write_file, seek_rx)
});
AudioFileLoading { AudioFileLoading {
read_file: read_file, read_file: read_file,
@ -80,12 +81,14 @@ impl AudioFileLoading {
position: 0, position: 0,
seek: seek_tx, seek: seek_tx,
shared: shared shared: shared,
} }
} }
fn fetch(session: &Session, shared: Arc<AudioFileShared>, fn fetch(session: &Session,
mut write_file: TempFile, seek_rx: mpsc::Receiver<u64>) { shared: Arc<AudioFileShared>,
mut write_file: TempFile,
seek_rx: mpsc::Receiver<u64>) {
let mut index = 0; let mut index = 0;
loop { loop {
@ -113,12 +116,14 @@ impl AudioFileLoading {
} }
} }
fn fetch_chunk(session: &Session, shared: &Arc<AudioFileShared>, fn fetch_chunk(session: &Session,
write_file: &mut TempFile, index: usize) { shared: &Arc<AudioFileShared>,
write_file: &mut TempFile,
index: usize) {
let rx = session.stream(shared.file_id, let rx = session.stream(shared.file_id,
(index * CHUNK_SIZE / 4) as u32, (index * CHUNK_SIZE / 4) as u32,
(CHUNK_SIZE / 4) as u32); (CHUNK_SIZE / 4) as u32);
println!("Chunk {}", index); println!("Chunk {}", index);
@ -133,7 +138,7 @@ impl AudioFileLoading {
size += data.len(); size += data.len();
if size >= CHUNK_SIZE { if size >= CHUNK_SIZE {
break break;
} }
} }
} }
@ -150,7 +155,8 @@ impl AudioFileLoading {
mkdir_existing(&AudioFileManager::cache_dir(session, shared.file_id)).unwrap(); mkdir_existing(&AudioFileManager::cache_dir(session, shared.file_id)).unwrap();
let mut f = fs::File::create(AudioFileManager::cache_path(session, shared.file_id)).unwrap(); let mut f = fs::File::create(AudioFileManager::cache_path(session, shared.file_id))
.unwrap();
io::copy(write_file, &mut f).unwrap(); io::copy(write_file, &mut f).unwrap();
} }
} }
@ -159,7 +165,7 @@ impl Read for AudioFileLoading {
fn read(&mut self, output: &mut [u8]) -> io::Result<usize> { fn read(&mut self, output: &mut [u8]) -> io::Result<usize> {
let index = self.position as usize / CHUNK_SIZE; let index = self.position as usize / CHUNK_SIZE;
let offset = self.position as usize % CHUNK_SIZE; let offset = self.position as usize % CHUNK_SIZE;
let len = min(output.len(), CHUNK_SIZE-offset); let len = min(output.len(), CHUNK_SIZE - offset);
let mut bitmap = self.shared.bitmap.lock().unwrap(); let mut bitmap = self.shared.bitmap.lock().unwrap();
while !bitmap.contains(&index) { while !bitmap.contains(&index) {
@ -179,11 +185,9 @@ impl Seek for AudioFileLoading {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
self.position = try!(self.read_file.seek(pos)); self.position = try!(self.read_file.seek(pos));
/* // Notify the fetch thread to get the correct block
* Notify the fetch thread to get the correct block // This can fail if fetch thread has completed, in which case the
* This can fail if fetch thread has completed, in which case the // block is ready. Just ignore the error.
* block is ready. Just ignore the error.
*/
self.seek.send(self.position).ignore(); self.seek.send(self.position).ignore();
Ok(self.position as u64) Ok(self.position as u64)
} }
@ -223,11 +227,10 @@ impl AudioFileManager {
AudioFileManager::cache_dir(session, file_id).join(&name[2..]) AudioFileManager::cache_dir(session, file_id).join(&name[2..])
} }
pub fn request (&mut self, session: &Session, file_id: FileId) -> AudioFile { pub fn request(&mut self, session: &Session, file_id: FileId) -> AudioFile {
match fs::File::open(AudioFileManager::cache_path(session, file_id)) { match fs::File::open(AudioFileManager::cache_path(session, file_id)) {
Ok(f) => AudioFile::Direct(f), Ok(f) => AudioFile::Direct(f),
Err(..) => AudioFile::Loading(AudioFileLoading::new(session, file_id)) Err(..) => AudioFile::Loading(AudioFileLoading::new(session, file_id)),
} }
} }
} }

View file

@ -18,7 +18,7 @@ struct AudioKeyId(SpotifyId, FileId);
enum AudioKeyStatus { enum AudioKeyStatus {
Loading(Vec<eventual::Complete<AudioKey, AudioKeyError>>), Loading(Vec<eventual::Complete<AudioKey, AudioKeyError>>),
Loaded(AudioKey), Loaded(AudioKey),
Failed(AudioKeyError) Failed(AudioKeyError),
} }
pub struct AudioKeyManager { pub struct AudioKeyManager {
@ -32,44 +32,48 @@ impl AudioKeyManager {
AudioKeyManager { AudioKeyManager {
next_seq: 1, next_seq: 1,
pending: HashMap::new(), pending: HashMap::new(),
cache: HashMap::new() cache: HashMap::new(),
} }
} }
pub fn request(&mut self, session: &Session, track: SpotifyId, file: FileId) pub fn request(&mut self,
-> eventual::Future<AudioKey, AudioKeyError> { session: &Session,
track: SpotifyId,
file: FileId)
-> eventual::Future<AudioKey, AudioKeyError> {
let id = AudioKeyId(track, file); let id = AudioKeyId(track, file);
self.cache.get_mut(&id).map(|status| match *status { self.cache
AudioKeyStatus::Failed(error) => { .get_mut(&id)
eventual::Future::error(error) .map(|status| {
} match *status {
AudioKeyStatus::Loaded(key) => { AudioKeyStatus::Failed(error) => eventual::Future::error(error),
eventual::Future::of(key) AudioKeyStatus::Loaded(key) => eventual::Future::of(key),
} AudioKeyStatus::Loading(ref mut req) => {
AudioKeyStatus::Loading(ref mut req) => { let (tx, rx) = eventual::Future::pair();
req.push(tx);
rx
}
}
})
.unwrap_or_else(|| {
let seq = self.next_seq;
self.next_seq += 1;
let mut data: Vec<u8> = Vec::new();
data.write(&file.0).unwrap();
data.write(&track.to_raw()).unwrap();
data.write_u32::<BigEndian>(seq).unwrap();
data.write_u16::<BigEndian>(0x0000).unwrap();
session.send_packet(0xc, &data).unwrap();
self.pending.insert(seq, id.clone());
let (tx, rx) = eventual::Future::pair(); let (tx, rx) = eventual::Future::pair();
req.push(tx); self.cache.insert(id, AudioKeyStatus::Loading(vec![tx]));
rx rx
} })
}).unwrap_or_else(|| {
let seq = self.next_seq;
self.next_seq += 1;
let mut data : Vec<u8> = Vec::new();
data.write(&file.0).unwrap();
data.write(&track.to_raw()).unwrap();
data.write_u32::<BigEndian>(seq).unwrap();
data.write_u16::<BigEndian>(0x0000).unwrap();
session.send_packet(0xc, &data).unwrap();
self.pending.insert(seq, id.clone());
let (tx, rx) = eventual::Future::pair();
self.cache.insert(id, AudioKeyStatus::Loading(vec!{ tx }));
rx
})
} }
} }
@ -78,7 +82,7 @@ impl PacketHandler for AudioKeyManager {
let mut data = Cursor::new(data); let mut data = Cursor::new(data);
let seq = data.read_u32::<BigEndian>().unwrap(); let seq = data.read_u32::<BigEndian>().unwrap();
if let Some(status) = self.pending.remove(&seq).and_then(|id| { self.cache.get_mut(&id) }) { if let Some(status) = self.pending.remove(&seq).and_then(|id| self.cache.get_mut(&id)) {
if cmd == 0xd { if cmd == 0xd {
let mut key = [0u8; 16]; let mut key = [0u8; 16];
data.read_exact(&mut key).unwrap(); data.read_exact(&mut key).unwrap();
@ -103,4 +107,3 @@ impl PacketHandler for AudioKeyManager {
} }
} }
} }

View file

@ -24,7 +24,7 @@ fn read_u8<R: Read>(stream: &mut R) -> io::Result<u8> {
fn read_int<R: Read>(stream: &mut R) -> io::Result<u32> { fn read_int<R: Read>(stream: &mut R) -> io::Result<u32> {
let lo = try!(read_u8(stream)) as u32; let lo = try!(read_u8(stream)) as u32;
if lo & 0x80 == 0 { if lo & 0x80 == 0 {
return Ok(lo) return Ok(lo);
} }
let hi = try!(read_u8(stream)) as u32; let hi = try!(read_u8(stream)) as u32;
@ -40,7 +40,11 @@ fn read_bytes<R: Read>(stream: &mut R) -> io::Result<Vec<u8>> {
} }
impl Session { impl Session {
fn login(&self, username: String, auth_data: Vec<u8>, typ: AuthenticationType) -> Result<(), ()> { fn login(&self,
username: String,
auth_data: Vec<u8>,
typ: AuthenticationType)
-> Result<(), ()> {
let packet = protobuf_init!(protocol::authentication::ClientResponseEncrypted::new(), { let packet = protobuf_init!(protocol::authentication::ClientResponseEncrypted::new(), {
login_credentials => { login_credentials => {
username: username, username: username,
@ -68,7 +72,7 @@ impl Session {
let (cmd, data) = self.recv(); let (cmd, data) = self.recv();
match cmd { match cmd {
0xac => { 0xac => {
let welcome_data : protocol::authentication::APWelcome = let welcome_data: protocol::authentication::APWelcome =
protobuf::parse_from_bytes(&data).unwrap(); protobuf::parse_from_bytes(&data).unwrap();
self.0.data.write().unwrap().canonical_username = self.0.data.write().unwrap().canonical_username =
welcome_data.get_canonical_username().to_string(); welcome_data.get_canonical_username().to_string();
@ -78,8 +82,8 @@ impl Session {
} }
0xad => { 0xad => {
let msg : protocol::keyexchange::APLoginFailed = let msg: protocol::keyexchange::APLoginFailed = protobuf::parse_from_bytes(&data)
protobuf::parse_from_bytes(&data).unwrap(); .unwrap();
eprintln!("Authentication failed, {:?}", msg); eprintln!("Authentication failed, {:?}", msg);
Err(()) Err(())
} }
@ -91,7 +95,8 @@ impl Session {
} }
pub fn login_password(&self, username: String, password: String) -> Result<(), ()> { pub fn login_password(&self, username: String, password: String) -> Result<(), ()> {
self.login(username, password.into_bytes(), self.login(username,
password.into_bytes(),
AuthenticationType::AUTHENTICATION_USER_PASS) AuthenticationType::AUTHENTICATION_USER_PASS)
} }
@ -121,14 +126,16 @@ impl Session {
let blob = { let blob = {
// Anyone know what this block mode is ? // Anyone know what this block mode is ?
let mut data = vec![0u8; blob.len()]; let mut data = vec![0u8; blob.len()];
let mut cipher = aes::ecb_decryptor( let mut cipher = aes::ecb_decryptor(aes::KeySize::KeySize192,
aes::KeySize::KeySize192, &key, crypto::blockmodes::NoPadding); &key,
crypto::blockmodes::NoPadding);
cipher.decrypt(&mut crypto::buffer::RefReadBuffer::new(&blob), cipher.decrypt(&mut crypto::buffer::RefReadBuffer::new(&blob),
&mut crypto::buffer::RefWriteBuffer::new(&mut data), &mut crypto::buffer::RefWriteBuffer::new(&mut data),
true).unwrap(); true)
.unwrap();
let l = blob.len(); let l = blob.len();
for i in 0..l-0x10 { for i in 0..l - 0x10 {
data[l - i - 1] ^= data[l - i - 0x11]; data[l - i - 1] ^= data[l - i - 0x11];
} }

View file

@ -9,7 +9,7 @@ use std::result;
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
IoError(io::Error), IoError(io::Error),
Other Other,
} }
pub type Result<T> = result::Result<T, Error>; pub type Result<T> = result::Result<T, Error>;
@ -24,13 +24,13 @@ impl convert::From<byteorder::Error> for Error {
fn from(err: byteorder::Error) -> Error { fn from(err: byteorder::Error) -> Error {
match err { match err {
byteorder::Error::Io(e) => Error::IoError(e), byteorder::Error::Io(e) => Error::IoError(e),
_ => Error::Other _ => Error::Other,
} }
} }
} }
pub struct PlainConnection { pub struct PlainConnection {
stream: TcpStream stream: TcpStream,
} }
#[derive(Clone)] #[derive(Clone)]
@ -79,13 +79,12 @@ impl PlainConnection {
impl CipherConnection { impl CipherConnection {
pub fn new(stream: TcpStream, recv_key: &[u8], send_key: &[u8]) -> CipherConnection { pub fn new(stream: TcpStream, recv_key: &[u8], send_key: &[u8]) -> CipherConnection {
CipherConnection { CipherConnection { stream: ShannonStream::new(stream, recv_key, send_key) }
stream: ShannonStream::new(stream, recv_key, send_key)
}
} }
pub fn send_packet(&mut self, cmd: u8, data: &[u8]) -> Result<()> { pub fn send_packet(&mut self, cmd: u8, data: &[u8]) -> Result<()> {
try!(self.stream.write_u8(cmd)); try!(self.stream.write_u16::<BigEndian>(data.len() as u16)); try!(self.stream.write_u8(cmd));
try!(self.stream.write_u16::<BigEndian>(data.len() as u16));
try!(self.stream.write(data)); try!(self.stream.write(data));
try!(self.stream.finish_send()); try!(self.stream.finish_send());
@ -110,4 +109,3 @@ impl CipherConnection {
pub trait PacketHandler { pub trait PacketHandler {
fn handle(&mut self, cmd: u8, data: Vec<u8>); fn handle(&mut self, cmd: u8, data: Vec<u8>);
} }

View file

@ -48,4 +48,3 @@ impl DHLocalKeys {
shared_key.to_bytes_be() shared_key.to_bytes_be()
} }
} }

View file

@ -15,7 +15,7 @@ use util;
pub struct DiscoveryManager { pub struct DiscoveryManager {
session: Session, session: Session,
private_key: BigUint, private_key: BigUint,
public_key: BigUint public_key: BigUint,
} }
fn not_found() -> ResponseBox { fn not_found() -> ResponseBox {
@ -55,9 +55,9 @@ impl DiscoveryManager {
} }
fn add_user(&self, params: &[(String, String)]) -> ResponseBox { fn add_user(&self, params: &[(String, String)]) -> ResponseBox {
let &(_, ref username) = params.iter().find(|& &(ref key, _)| key == "userName").unwrap(); let &(_, ref username) = params.iter().find(|&&(ref key, _)| key == "userName").unwrap();
let &(_, ref encrypted_blob) = params.iter().find(|& &(ref key, _)| key == "blob").unwrap(); let &(_, ref encrypted_blob) = params.iter().find(|&&(ref key, _)| key == "blob").unwrap();
let &(_, ref client_key) = params.iter().find(|& &(ref key, _)| key == "clientKey").unwrap(); let &(_, ref client_key) = params.iter().find(|&&(ref key, _)| key == "clientKey").unwrap();
let encrypted_blob = encrypted_blob.from_base64().unwrap(); let encrypted_blob = encrypted_blob.from_base64().unwrap();
@ -67,8 +67,8 @@ impl DiscoveryManager {
let shared_key = util::powm(&client_key, &self.private_key, &DH_PRIME); let shared_key = util::powm(&client_key, &self.private_key, &DH_PRIME);
let iv = &encrypted_blob[0..16]; let iv = &encrypted_blob[0..16];
let encrypted = &encrypted_blob[16..encrypted_blob.len()-20]; let encrypted = &encrypted_blob[16..encrypted_blob.len() - 20];
let cksum = &encrypted_blob[encrypted_blob.len()-20..encrypted_blob.len()]; let cksum = &encrypted_blob[encrypted_blob.len() - 20..encrypted_blob.len()];
let base_key = { let base_key = {
let mut data = [0u8; 20]; let mut data = [0u8; 20];
@ -100,7 +100,9 @@ impl DiscoveryManager {
let decrypted = { let decrypted = {
let mut data = vec![0u8; encrypted.len()]; let mut data = vec![0u8; encrypted.len()];
let mut cipher = crypto::aes::ctr(crypto::aes::KeySize::KeySize128, &encryption_key[0..16], &iv); let mut cipher = crypto::aes::ctr(crypto::aes::KeySize::KeySize128,
&encryption_key[0..16],
&iv);
cipher.process(&encrypted, &mut data); cipher.process(&encrypted, &mut data);
String::from_utf8(data).unwrap() String::from_utf8(data).unwrap()
}; };
@ -126,7 +128,8 @@ impl DiscoveryManager {
for mut request in server.incoming_requests() { for mut request in server.incoming_requests() {
let (_, query, _) = url::parse_path(request.url()).unwrap(); let (_, query, _) = url::parse_path(request.url()).unwrap();
let mut params = query.map(|q| url::form_urlencoded::parse(q.as_bytes())).unwrap_or(Vec::new()); let mut params = query.map(|q| url::form_urlencoded::parse(q.as_bytes()))
.unwrap_or(Vec::new());
if *request.method() == Method::Post { if *request.method() == Method::Post {
let mut body = Vec::new(); let mut body = Vec::new();
@ -137,7 +140,7 @@ impl DiscoveryManager {
println!("{:?}", params); println!("{:?}", params);
let &(_, ref action) = params.iter().find(|& &(ref key, _)| key == "action").unwrap(); let &(_, ref action) = params.iter().find(|&&(ref key, _)| key == "action").unwrap();
match action.as_ref() { match action.as_ref() {
"getInfo" => request.respond(self.get_info()).unwrap(), "getInfo" => request.respond(self.get_info()).unwrap(),
"addUser" => { "addUser" => {
@ -151,4 +154,3 @@ impl DiscoveryManager {
drop(svc); drop(svc);
} }
} }

View file

@ -4,7 +4,8 @@
#![plugin(protobuf_macros)] #![plugin(protobuf_macros)]
#![plugin(json_macros)] #![plugin(json_macros)]
#[macro_use] extern crate lazy_static; #[macro_use]
extern crate lazy_static;
extern crate bit_set; extern crate bit_set;
extern crate byteorder; extern crate byteorder;
@ -27,7 +28,7 @@ extern crate dns_sd;
extern crate librespot_protocol; extern crate librespot_protocol;
#[macro_use] pub mod util; #[macro_use]pub mod util;
mod audio_decrypt; mod audio_decrypt;
mod audio_file; mod audio_file;
mod audio_key; mod audio_key;
@ -42,4 +43,3 @@ pub mod session;
pub mod spirc; pub mod spirc;
mod stream; mod stream;
mod zeroconf; mod zeroconf;

View file

@ -36,17 +36,16 @@ fn main() {
.optopt("b", "bitrate", "Bitrate (96, 160 or 320). Defaults to 160", "BITRATE"); .optopt("b", "bitrate", "Bitrate (96, 160 or 320). Defaults to 160", "BITRATE");
let matches = match opts.parse(&args[1..]) { let matches = match opts.parse(&args[1..]) {
Ok(m) => { m }, Ok(m) => m,
Err(f) => { Err(f) => {
print!("Error: {}\n{}", f.to_string(), usage(&*program, &opts)); print!("Error: {}\n{}", f.to_string(), usage(&*program, &opts));
return; return;
} }
}; };
let appkey = { let appkey = {
let mut file = File::open( let mut file = File::open(Path::new(&*matches.opt_str("a").unwrap()))
Path::new(&*matches.opt_str("a").unwrap()) .expect("Could not open app key.");
).expect("Could not open app key.");
let mut data = Vec::new(); let mut data = Vec::new();
file.read_to_end(&mut data).unwrap(); file.read_to_end(&mut data).unwrap();
@ -59,25 +58,25 @@ fn main() {
let name = matches.opt_str("n").unwrap(); let name = matches.opt_str("n").unwrap();
let credentials = username.map(|u| { let credentials = username.map(|u| {
let password = matches.opt_str("p").or_else(|| { let password = matches.opt_str("p")
std::env::var(PASSWORD_ENV_NAME).ok() .or_else(|| std::env::var(PASSWORD_ENV_NAME).ok())
}).unwrap_or_else(|| { .unwrap_or_else(|| {
print!("Password: "); print!("Password: ");
stdout().flush().unwrap(); stdout().flush().unwrap();
read_password().unwrap() read_password().unwrap()
}); });
(u, password) (u, password)
}); });
std::env::remove_var(PASSWORD_ENV_NAME); std::env::remove_var(PASSWORD_ENV_NAME);
let bitrate = match matches.opt_str("b").as_ref().map(String::as_ref) { let bitrate = match matches.opt_str("b").as_ref().map(String::as_ref) {
None => Bitrate::Bitrate160, // default value None => Bitrate::Bitrate160, // default value
Some("96") => Bitrate::Bitrate96, Some("96") => Bitrate::Bitrate96,
Some("160") => Bitrate::Bitrate160, Some("160") => Bitrate::Bitrate160,
Some("320") => Bitrate::Bitrate320, Some("320") => Bitrate::Bitrate320,
Some(b) => panic!("Invalid bitrate {}", b), Some(b) => panic!("Invalid bitrate {}", b),
}; };
let config = Config { let config = Config {
@ -85,7 +84,7 @@ fn main() {
user_agent: version_string(), user_agent: version_string(),
device_name: name, device_name: name,
cache_location: PathBuf::from(cache_location), cache_location: PathBuf::from(cache_location),
bitrate: bitrate bitrate: bitrate,
}; };
let session = Session::new(config); let session = Session::new(config);
@ -99,12 +98,9 @@ fn main() {
let player = Player::new(session.clone()); let player = Player::new(session.clone());
let mut spirc = SpircManager::new(session.clone(), player); let mut spirc = SpircManager::new(session.clone(), player);
thread::spawn(move || { thread::spawn(move || spirc.run());
spirc.run()
});
loop { loop {
session.poll(); session.poll();
} }
} }

View file

@ -22,13 +22,13 @@ pub struct MercuryRequest {
pub method: MercuryMethod, pub method: MercuryMethod,
pub uri: String, pub uri: String,
pub content_type: Option<String>, pub content_type: Option<String>,
pub payload: Vec<Vec<u8>> pub payload: Vec<Vec<u8>>,
} }
#[derive(Debug)] #[derive(Debug)]
pub struct MercuryResponse { pub struct MercuryResponse {
pub uri: String, pub uri: String,
pub payload: Vec<Vec<u8>> pub payload: Vec<Vec<u8>>,
} }
enum MercuryCallback { enum MercuryCallback {
@ -40,7 +40,7 @@ enum MercuryCallback {
pub struct MercuryPending { pub struct MercuryPending {
parts: Vec<Vec<u8>>, parts: Vec<Vec<u8>>,
partial: Option<Vec<u8>>, partial: Option<Vec<u8>>,
callback: MercuryCallback callback: MercuryCallback,
} }
pub struct MercuryManager { pub struct MercuryManager {
@ -55,8 +55,9 @@ impl ToString for MercuryMethod {
MercuryMethod::GET => "GET", MercuryMethod::GET => "GET",
MercuryMethod::SUB => "SUB", MercuryMethod::SUB => "SUB",
MercuryMethod::UNSUB => "UNSUB", MercuryMethod::UNSUB => "UNSUB",
MercuryMethod::SEND => "SEND" MercuryMethod::SEND => "SEND",
}.to_owned() }
.to_owned()
} }
} }
@ -86,30 +87,34 @@ impl MercuryManager {
session.send_packet(cmd, &data).unwrap(); session.send_packet(cmd, &data).unwrap();
self.pending.insert(seq.to_vec(), MercuryPending{ self.pending.insert(seq.to_vec(),
parts: Vec::new(), MercuryPending {
partial: None, parts: Vec::new(),
callback: cb, partial: None,
}); callback: cb,
});
} }
pub fn request(&mut self, session: &Session, req: MercuryRequest) pub fn request(&mut self,
-> eventual::Future<MercuryResponse, ()> { session: &Session,
req: MercuryRequest)
-> eventual::Future<MercuryResponse, ()> {
let (tx, rx) = eventual::Future::pair(); let (tx, rx) = eventual::Future::pair();
self.request_with_callback(session, req, MercuryCallback::Future(tx)); self.request_with_callback(session, req, MercuryCallback::Future(tx));
rx rx
} }
pub fn subscribe(&mut self, session: &Session, uri: String) pub fn subscribe(&mut self, session: &Session, uri: String) -> mpsc::Receiver<MercuryResponse> {
-> mpsc::Receiver<MercuryResponse> {
let (tx, rx) = mpsc::channel(); let (tx, rx) = mpsc::channel();
self.request_with_callback(session, MercuryRequest{ self.request_with_callback(session,
method: MercuryMethod::SUB, MercuryRequest {
uri: uri, method: MercuryMethod::SUB,
content_type: None, uri: uri,
payload: Vec::new() content_type: None,
}, MercuryCallback::Subscription(tx)); payload: Vec::new(),
},
MercuryCallback::Subscription(tx));
rx rx
} }
@ -126,7 +131,8 @@ impl MercuryManager {
response: MercuryResponse, response: MercuryResponse,
tx: mpsc::Sender<MercuryResponse>) { tx: mpsc::Sender<MercuryResponse>) {
for sub_data in response.payload { for sub_data in response.payload {
if let Ok(mut sub) = protobuf::parse_from_bytes::<protocol::pubsub::Subscription>(&sub_data) { if let Ok(mut sub) =
protobuf::parse_from_bytes::<protocol::pubsub::Subscription>(&sub_data) {
self.subscriptions.insert(sub.take_uri(), tx.clone()); self.subscriptions.insert(sub.take_uri(), tx.clone());
} }
} }
@ -134,12 +140,11 @@ impl MercuryManager {
fn complete_request(&mut self, mut pending: MercuryPending) { fn complete_request(&mut self, mut pending: MercuryPending) {
let header_data = pending.parts.remove(0); let header_data = pending.parts.remove(0);
let header : protocol::mercury::Header = let header: protocol::mercury::Header = protobuf::parse_from_bytes(&header_data).unwrap();
protobuf::parse_from_bytes(&header_data).unwrap();
let response = MercuryResponse { let response = MercuryResponse {
uri: header.get_uri().to_owned(), uri: header.get_uri().to_owned(),
payload: pending.parts payload: pending.parts,
}; };
match pending.callback { match pending.callback {
@ -203,7 +208,7 @@ impl PacketHandler for MercuryManager {
} }
} else { } else {
println!("Ignore seq {:?} cmd {}", seq, cmd); println!("Ignore seq {:?} cmd {}", seq, cmd);
return return;
}; };
for i in 0..count { for i in 0..count {
@ -227,4 +232,3 @@ impl PacketHandler for MercuryManager {
} }
} }
} }

View file

@ -13,12 +13,14 @@ fn countrylist_contains(list: &str, country: &str) -> bool {
} }
fn parse_restrictions<'s, I>(restrictions: I, country: &str, catalogue: &str) -> bool fn parse_restrictions<'s, I>(restrictions: I, country: &str, catalogue: &str) -> bool
where I : Iterator<Item=&'s protocol::metadata::Restriction> { where I: Iterator<Item = &'s protocol::metadata::Restriction>
restrictions {
.filter(|r| r.get_catalogue_str().contains(&catalogue.to_owned())) restrictions.filter(|r| r.get_catalogue_str().contains(&catalogue.to_owned()))
.all(|r| !countrylist_contains(r.get_countries_forbidden(), country) .all(|r| {
&& (!r.has_countries_allowed() !countrylist_contains(r.get_countries_forbidden(), country) &&
|| countrylist_contains(r.get_countries_allowed(), country))) (!r.has_countries_allowed() ||
countrylist_contains(r.get_countries_allowed(), country))
})
} }
pub trait MetadataTrait : Send + 'static { pub trait MetadataTrait : Send + 'static {
@ -43,7 +45,7 @@ pub struct Album {
pub id: SpotifyId, pub id: SpotifyId,
pub name: String, pub name: String,
pub artists: Vec<SpotifyId>, pub artists: Vec<SpotifyId>,
pub covers: Vec<FileId> pub covers: Vec<FileId>,
} }
#[derive(Debug)] #[derive(Debug)]
@ -69,20 +71,21 @@ impl MetadataTrait for Track {
id: SpotifyId::from_raw(msg.get_gid()), id: SpotifyId::from_raw(msg.get_gid()),
name: msg.get_name().to_owned(), name: msg.get_name().to_owned(),
album: SpotifyId::from_raw(msg.get_album().get_gid()), album: SpotifyId::from_raw(msg.get_album().get_gid()),
files: msg.get_file().iter() files: msg.get_file()
.map(|file| { .iter()
let mut dst = [0u8; 20]; .map(|file| {
dst.clone_from_slice(&file.get_file_id()); let mut dst = [0u8; 20];
(FileId(dst), file.get_format()) dst.clone_from_slice(&file.get_file_id());
}) (FileId(dst), file.get_format())
.collect(), })
alternatives: msg.get_alternative().iter() .collect(),
.map(|alt| SpotifyId::from_raw(alt.get_gid())) alternatives: msg.get_alternative()
.collect(), .iter()
available: parse_restrictions( .map(|alt| SpotifyId::from_raw(alt.get_gid()))
msg.get_restriction().iter(), .collect(),
&session.0.data.read().unwrap().country, available: parse_restrictions(msg.get_restriction().iter(),
"premium"), &session.0.data.read().unwrap().country,
"premium"),
} }
} }
} }
@ -98,16 +101,19 @@ impl MetadataTrait for Album {
Album { Album {
id: SpotifyId::from_raw(msg.get_gid()), id: SpotifyId::from_raw(msg.get_gid()),
name: msg.get_name().to_owned(), name: msg.get_name().to_owned(),
artists: msg.get_artist().iter() artists: msg.get_artist()
.map(|a| SpotifyId::from_raw(a.get_gid())) .iter()
.collect(), .map(|a| SpotifyId::from_raw(a.get_gid()))
covers: msg.get_cover_group().get_image().iter() .collect(),
.map(|image| { covers: msg.get_cover_group()
let mut dst = [0u8; 20]; .get_image()
dst.clone_from_slice(&image.get_file_id()); .iter()
FileId(dst) .map(|image| {
}) let mut dst = [0u8; 20];
.collect(), dst.clone_from_slice(&image.get_file_id());
FileId(dst)
})
.collect(),
} }
} }
} }
@ -135,21 +141,22 @@ impl MetadataManager {
MetadataManager MetadataManager
} }
pub fn get<T: MetadataTrait>(&mut self, session: &Session, id: SpotifyId) pub fn get<T: MetadataTrait>(&mut self, session: &Session, id: SpotifyId) -> MetadataRef<T> {
-> MetadataRef<T> {
let _session = session.clone(); let _session = session.clone();
session.mercury(MercuryRequest { session.mercury(MercuryRequest {
method: MercuryMethod::GET, method: MercuryMethod::GET,
uri: format!("{}/{}", T::base_url(), id.to_base16()), uri: format!("{}/{}", T::base_url(), id.to_base16()),
content_type: None, content_type: None,
payload: Vec::new() payload: Vec::new(),
}).and_then(move |response| { })
let msg : T::Message = protobuf::parse_from_bytes( .and_then(move |response| {
response.payload.first().unwrap()).unwrap(); let msg: T::Message = protobuf::parse_from_bytes(response.payload
.first()
.unwrap())
.unwrap();
Ok(T::parse(&msg, &_session)) Ok(T::parse(&msg, &_session))
}) })
} }
} }

View file

@ -22,7 +22,7 @@ pub struct PlayerState {
position_measured_at: i64, position_measured_at: i64,
update_time: i64, update_time: i64,
end_of_track: bool end_of_track: bool,
} }
struct PlayerInternal { struct PlayerInternal {
@ -36,7 +36,7 @@ enum PlayerCommand {
Play, Play,
Pause, Pause,
Stop, Stop,
Seek(u32) Seek(u32),
} }
impl Player { impl Player {
@ -49,17 +49,16 @@ impl Player {
position_measured_at: 0, position_measured_at: 0,
update_time: util::now_ms(), update_time: util::now_ms(),
end_of_track: false, end_of_track: false,
}), Condvar::new())); }),
Condvar::new()));
let internal = PlayerInternal { let internal = PlayerInternal {
session: session, session: session,
commands: cmd_rx, commands: cmd_rx,
state: state.clone() state: state.clone(),
}; };
thread::spawn(move || { thread::spawn(move || internal.run());
internal.run()
});
Player { Player {
commands: cmd_tx, commands: cmd_tx,
@ -113,11 +112,17 @@ impl PlayerInternal {
let mut track = self.session.metadata::<Track>(track_id).await().unwrap(); let mut track = self.session.metadata::<Track>(track_id).await().unwrap();
if !track.available { if !track.available {
let alternatives = track.alternatives.iter() let alternatives = track.alternatives
.map(|alt_id| self.session.metadata::<Track>(*alt_id)) .iter()
.collect::<Vec<TrackRef>>(); .map(|alt_id| {
self.session.metadata::<Track>(*alt_id)
})
.collect::<Vec<TrackRef>>();
track = eventual::sequence(alternatives.into_iter()).iter().find(|alt| alt.available).unwrap(); track = eventual::sequence(alternatives.into_iter())
.iter()
.find(|alt| alt.available)
.unwrap();
} }
let format = match self.session.0.config.bitrate { let format = match self.session.0.config.bitrate {
@ -152,11 +157,12 @@ impl PlayerInternal {
Some(PlayerCommand::Seek(ms)) => { Some(PlayerCommand::Seek(ms)) => {
decoder.as_mut().unwrap().time_seek(ms as f64 / 1000f64).unwrap(); decoder.as_mut().unwrap().time_seek(ms as f64 / 1000f64).unwrap();
self.update(|state| { self.update(|state| {
state.position_ms = (decoder.as_mut().unwrap().time_tell().unwrap() * 1000f64) as u32; state.position_ms =
(decoder.as_mut().unwrap().time_tell().unwrap() * 1000f64) as u32;
state.position_measured_at = util::now_ms(); state.position_measured_at = util::now_ms();
return true; return true;
}); });
}, }
Some(PlayerCommand::Play) => { Some(PlayerCommand::Play) => {
self.update(|state| { self.update(|state| {
state.status = PlayStatus::kPlayStatusPlay; state.status = PlayStatus::kPlayStatusPlay;
@ -164,7 +170,7 @@ impl PlayerInternal {
}); });
stream.start().unwrap(); stream.start().unwrap();
}, }
Some(PlayerCommand::Pause) => { Some(PlayerCommand::Pause) => {
self.update(|state| { self.update(|state| {
state.status = PlayStatus::kPlayStatusPause; state.status = PlayStatus::kPlayStatusPause;
@ -173,7 +179,7 @@ impl PlayerInternal {
}); });
stream.stop().unwrap(); stream.stop().unwrap();
}, }
Some(PlayerCommand::Stop) => { Some(PlayerCommand::Stop) => {
self.update(|state| { self.update(|state| {
if state.status == PlayStatus::kPlayStatusPlay { if state.status == PlayStatus::kPlayStatusPlay {
@ -184,7 +190,7 @@ impl PlayerInternal {
stream.stop().unwrap(); stream.stop().unwrap();
decoder = None; decoder = None;
}, }
None => (), None => (),
} }
@ -193,11 +199,10 @@ impl PlayerInternal {
Some(Ok(packet)) => { Some(Ok(packet)) => {
match stream.write(&packet.data) { match stream.write(&packet.data) {
Ok(_) => (), Ok(_) => (),
Err(portaudio::PaError::OutputUnderflowed) Err(portaudio::PaError::OutputUnderflowed) => eprintln!("Underflow"),
=> eprintln!("Underflow"), Err(e) => panic!("PA Error {}", e),
Err(e) => panic!("PA Error {}", e)
}; };
}, }
Some(Err(vorbis::VorbisError::Hole)) => (), Some(Err(vorbis::VorbisError::Hole)) => (),
Some(Err(e)) => panic!("Vorbis error {:?}", e), Some(Err(e)) => panic!("Vorbis error {:?}", e),
None => { None => {
@ -216,7 +221,8 @@ impl PlayerInternal {
let now = util::now_ms(); let now = util::now_ms();
if now - state.position_measured_at > 5000 { if now - state.position_measured_at > 5000 {
state.position_ms = (decoder.as_mut().unwrap().time_tell().unwrap() * 1000f64) as u32; state.position_ms =
(decoder.as_mut().unwrap().time_tell().unwrap() * 1000f64) as u32;
state.position_measured_at = now; state.position_measured_at = now;
return true; return true;
} else { } else {
@ -232,7 +238,8 @@ impl PlayerInternal {
} }
fn update<F>(&self, f: F) fn update<F>(&self, f: F)
where F: FnOnce(&mut MutexGuard<PlayerState>) -> bool { where F: FnOnce(&mut MutexGuard<PlayerState>) -> bool
{
let mut guard = self.state.0.lock().unwrap(); let mut guard = self.state.0.lock().unwrap();
let update = f(&mut guard); let update = f(&mut guard);
if update { if update {
@ -245,8 +252,7 @@ impl PlayerInternal {
impl SpircDelegate for Player { impl SpircDelegate for Player {
type State = PlayerState; type State = PlayerState;
fn load(&self, track: SpotifyId, fn load(&self, track: SpotifyId, start_playing: bool, position_ms: u32) {
start_playing: bool, position_ms: u32) {
self.command(PlayerCommand::Load(track, start_playing, position_ms)); self.command(PlayerCommand::Load(track, start_playing, position_ms));
} }
@ -308,4 +314,3 @@ impl SpircState for PlayerState {
return self.end_of_track; return self.end_of_track;
} }
} }

View file

@ -25,7 +25,7 @@ use util;
pub enum Bitrate { pub enum Bitrate {
Bitrate96, Bitrate96,
Bitrate160, Bitrate160,
Bitrate320 Bitrate320,
} }
pub struct Config { pub struct Config {
@ -123,12 +123,12 @@ impl Session {
} }
}); });
let init_client_packet = let init_client_packet = connection.send_packet_prefix(&[0, 4],
connection.send_packet_prefix(&[0,4], &request.write_to_bytes().unwrap()).unwrap(); &request.write_to_bytes().unwrap())
let init_server_packet = .unwrap();
connection.recv_packet().unwrap(); let init_server_packet = connection.recv_packet().unwrap();
let response : protocol::keyexchange::APResponseMessage = let response: protocol::keyexchange::APResponseMessage =
protobuf::parse_from_bytes(&init_server_packet[4..]).unwrap(); protobuf::parse_from_bytes(&init_server_packet[4..]).unwrap();
let remote_key = response.get_challenge() let remote_key = response.get_challenge()
@ -169,10 +169,9 @@ impl Session {
connection.send_packet(&packet.write_to_bytes().unwrap()).unwrap(); connection.send_packet(&packet.write_to_bytes().unwrap()).unwrap();
let cipher_connection = CipherConnection::new( let cipher_connection = CipherConnection::new(connection.into_stream(),
connection.into_stream(), &send_key,
&send_key, &recv_key);
&recv_key);
*self.0.rx_connection.lock().unwrap() = Some(cipher_connection.clone()); *self.0.rx_connection.lock().unwrap() = Some(cipher_connection.clone());
*self.0.tx_connection.lock().unwrap() = Some(cipher_connection); *self.0.tx_connection.lock().unwrap() = Some(cipher_connection);
@ -184,14 +183,13 @@ impl Session {
match cmd { match cmd {
0x4 => self.send_packet(0x49, &data).unwrap(), 0x4 => self.send_packet(0x49, &data).unwrap(),
0x4a => (), 0x4a => (),
0x9 => self.0.stream.lock().unwrap().handle(cmd, data), 0x9 => self.0.stream.lock().unwrap().handle(cmd, data),
0xd | 0xe => self.0.audio_key.lock().unwrap().handle(cmd, data), 0xd | 0xe => self.0.audio_key.lock().unwrap().handle(cmd, data),
0x1b => { 0x1b => {
self.0.data.write().unwrap().country = self.0.data.write().unwrap().country = String::from_utf8(data).unwrap();
String::from_utf8(data).unwrap(); }
},
0xb2...0xb6 => self.0.mercury.lock().unwrap().handle(cmd, data), 0xb2...0xb6 => self.0.mercury.lock().unwrap().handle(cmd, data),
_ => () _ => (),
} }
} }
@ -227,4 +225,3 @@ impl Session {
self.0.mercury.lock().unwrap().subscribe(self, uri) self.0.mercury.lock().unwrap().subscribe(self, uri)
} }
} }

View file

@ -34,14 +34,13 @@ pub struct SpircManager<D: SpircDelegate> {
last_command_msgid: u32, last_command_msgid: u32,
tracks: Vec<SpotifyId>, tracks: Vec<SpotifyId>,
index: u32 index: u32,
} }
pub trait SpircDelegate { pub trait SpircDelegate {
type State : SpircState; type State : SpircState;
fn load(&self, track: SpotifyId, fn load(&self, track: SpotifyId, start_playing: bool, position_ms: u32);
start_playing: bool, position_ms: u32);
fn play(&self); fn play(&self);
fn pause(&self); fn pause(&self);
fn seek(&self, position_ms: u32); fn seek(&self, position_ms: u32);
@ -58,9 +57,8 @@ pub trait SpircState {
fn end_of_track(&self) -> bool; fn end_of_track(&self) -> bool;
} }
impl <D: SpircDelegate> SpircManager<D> { impl<D: SpircDelegate> SpircManager<D> {
pub fn new(session: Session, delegate: D) pub fn new(session: Session, delegate: D) -> SpircManager<D> {
-> SpircManager<D> {
let ident = session.0.data.read().unwrap().device_id.clone(); let ident = session.0.data.read().unwrap().device_id.clone();
let name = session.0.config.device_name.clone(); let name = session.0.config.device_name.clone();
@ -88,13 +86,19 @@ impl <D: SpircDelegate> SpircManager<D> {
last_command_msgid: 0, last_command_msgid: 0,
tracks: Vec::new(), tracks: Vec::new(),
index: 0 index: 0,
} }
} }
pub fn run(&mut self) { pub fn run(&mut self) {
let rx = self.session.mercury_sub(format!("hm://remote/user/{}/", let rx = self.session.mercury_sub(format!("hm://remote/user/{}/",
self.session.0.data.read().unwrap().canonical_username.clone())); self.session
.0
.data
.read()
.unwrap()
.canonical_username
.clone()));
let updates = self.delegate.updates(); let updates = self.delegate.updates();
self.notify(true, None); self.notify(true, None);
@ -149,9 +153,11 @@ impl <D: SpircDelegate> SpircManager<D> {
self.index = frame.get_state().get_playing_track_index(); self.index = frame.get_state().get_playing_track_index();
self.tracks = frame.get_state().get_track().iter() self.tracks = frame.get_state()
.map(|track| SpotifyId::from_raw(track.get_gid())) .get_track()
.collect(); .iter()
.map(|track| SpotifyId::from_raw(track.get_gid()))
.collect();
let play = frame.get_state().get_status() == PlayStatus::kPlayStatusPlay; let play = frame.get_state().get_status() == PlayStatus::kPlayStatusPlay;
let track = self.tracks[self.index as usize]; let track = self.tracks[self.index as usize];
@ -173,7 +179,7 @@ impl <D: SpircDelegate> SpircManager<D> {
self.delegate.stop(); self.delegate.stop();
} }
} }
_ => () _ => (),
} }
} }
@ -200,13 +206,16 @@ impl <D: SpircDelegate> SpircManager<D> {
pkt.set_state(self.spirc_state()); pkt.set_state(self.spirc_state());
} }
self.session.mercury(MercuryRequest{ self.session
method: MercuryMethod::SEND, .mercury(MercuryRequest {
uri: format!("hm://remote/user/{}", method: MercuryMethod::SEND,
self.session.0.data.read().unwrap().canonical_username.clone()), uri: format!("hm://remote/user/{}",
content_type: None, self.session.0.data.read().unwrap().canonical_username.clone()),
payload: vec![ pkt.write_to_bytes().unwrap() ] content_type: None,
}).await().unwrap(); payload: vec![pkt.write_to_bytes().unwrap()],
})
.await()
.unwrap();
} }
fn spirc_state(&self) -> protocol::spirc::State { fn spirc_state(&self) -> protocol::spirc::State {

View file

@ -17,12 +17,12 @@ type ChannelId = u16;
enum ChannelMode { enum ChannelMode {
Header, Header,
Data Data,
} }
struct Channel { struct Channel {
mode: ChannelMode, mode: ChannelMode,
callback: mpsc::Sender<StreamEvent> callback: mpsc::Sender<StreamEvent>,
} }
pub struct StreamManager { pub struct StreamManager {
@ -38,16 +38,19 @@ impl StreamManager {
} }
} }
pub fn request(&mut self, session: &Session, pub fn request(&mut self,
file: FileId, offset: u32, size: u32) session: &Session,
-> mpsc::Receiver<StreamEvent> { file: FileId,
offset: u32,
size: u32)
-> mpsc::Receiver<StreamEvent> {
let (tx, rx) = mpsc::channel(); let (tx, rx) = mpsc::channel();
let channel_id = self.next_id; let channel_id = self.next_id;
self.next_id += 1; self.next_id += 1;
let mut data : Vec<u8> = Vec::new(); let mut data: Vec<u8> = Vec::new();
data.write_u16::<BigEndian>(channel_id).unwrap(); data.write_u16::<BigEndian>(channel_id).unwrap();
data.write_u8(0).unwrap(); data.write_u8(0).unwrap();
data.write_u8(1).unwrap(); data.write_u8(1).unwrap();
@ -61,10 +64,11 @@ impl StreamManager {
session.send_packet(0x8, &data).unwrap(); session.send_packet(0x8, &data).unwrap();
self.channels.insert(channel_id, Channel { self.channels.insert(channel_id,
mode: ChannelMode::Header, Channel {
callback: tx mode: ChannelMode::Header,
}); callback: tx,
});
rx rx
} }
@ -75,12 +79,14 @@ impl PacketHandler for StreamManager {
let data = ArcVec::new(data); let data = ArcVec::new(data);
let mut packet = Cursor::new(&data as &[u8]); let mut packet = Cursor::new(&data as &[u8]);
let id : ChannelId = packet.read_u16::<BigEndian>().unwrap(); let id: ChannelId = packet.read_u16::<BigEndian>().unwrap();
let mut close = false; let mut close = false;
{ {
let channel = match self.channels.get_mut(&id) { let channel = match self.channels.get_mut(&id) {
Some(ch) => ch, Some(ch) => ch,
None => { return; } None => {
return;
}
}; };
match channel.mode { match channel.mode {
@ -114,10 +120,11 @@ impl PacketHandler for StreamManager {
ChannelMode::Data => { ChannelMode::Data => {
if packet.position() < data.len() as u64 { if packet.position() < data.len() as u64 {
channel.callback channel.callback
.send(StreamEvent::Data(data.clone().offset(packet.position() as usize))) .send(StreamEvent::Data(data.clone()
.unwrap_or_else(|_| { .offset(packet.position() as usize)))
close = true; .unwrap_or_else(|_| {
}); close = true;
});
} else { } else {
close = true; close = true;
} }
@ -130,4 +137,3 @@ impl PacketHandler for StreamManager {
} }
} }
} }

View file

@ -9,13 +9,13 @@ pub struct ArcVec<T> {
length: usize, length: usize,
} }
impl <T> ArcVec<T> { impl<T> ArcVec<T> {
pub fn new(data: Vec<T>) -> ArcVec<T> { pub fn new(data: Vec<T>) -> ArcVec<T> {
let length = data.len(); let length = data.len();
ArcVec { ArcVec {
data: Arc::new(data), data: Arc::new(data),
offset: 0, offset: 0,
length: length length: length,
} }
} }
@ -40,13 +40,12 @@ impl<T> Deref for ArcVec<T> {
type Target = [T]; type Target = [T];
fn deref(&self) -> &[T] { fn deref(&self) -> &[T] {
&self.data[self.offset..self.offset+self.length] &self.data[self.offset..self.offset + self.length]
} }
} }
impl<T : fmt::Debug> fmt::Debug for ArcVec<T> { impl<T: fmt::Debug> fmt::Debug for ArcVec<T> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
self.deref().fmt(formatter) self.deref().fmt(formatter)
} }
} }

View file

@ -4,12 +4,15 @@ use std;
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
pub struct u128 { pub struct u128 {
high: u64, high: u64,
low: u64 low: u64,
} }
impl u128 { impl u128 {
pub fn from_parts(high: u64, low: u64) -> u128 { pub fn from_parts(high: u64, low: u64) -> u128 {
u128 { high: high, low: low } u128 {
high: high,
low: low,
}
} }
pub fn parts(&self) -> (u64, u64) { pub fn parts(&self) -> (u64, u64) {
@ -28,18 +31,26 @@ impl std::ops::Add<u128> for 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)
} }
} }
impl <'a> std::ops::Add<&'a u128> for u128 { 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)
} }
@ -56,19 +67,21 @@ 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] = let top: [u64; 4] = [self.high >> 32,
[self.high >> 32, self.high & 0xFFFFFFFF, self.high & 0xFFFFFFFF,
self.low >> 32, self.low & 0xFFFFFFFF]; self.low >> 32,
self.low & 0xFFFFFFFF];
let bottom : [u64; 4] = let bottom: [u64; 4] = [rhs.high >> 32,
[rhs.high >> 32, rhs.high & 0xFFFFFFFF, rhs.high & 0xFFFFFFFF,
rhs.low >> 32, rhs.low & 0xFFFFFFFF]; rhs.low >> 32,
rhs.low & 0xFFFFFFFF];
let mut rows = [std::num::Zero::zero(); 16]; let mut rows = [std::num::Zero::zero(); 16];
for i in 0..4 { for i in 0..4 {
for j in 0..4 { for j in 0..4 {
let shift = i + j; let shift = i + j;
let product = top[3-i] * bottom[3-j]; let product = top[3 - i] * bottom[3 - j];
let (high, low) = match shift { let (high, low) = match shift {
0 => (0, product), 0 => (0, product),
1 => (product >> 32, product << 32), 1 => (product >> 32, product << 32),
@ -76,8 +89,7 @@ impl std::ops::Mul<u128> for u128 {
3 => (product << 32, 0), 3 => (product << 32, 0),
_ => { _ => {
if product != 0 { if product != 0 {
panic!("Overflow on mul {:?} {:?} ({} {})", panic!("Overflow on mul {:?} {:?} ({} {})", self, rhs, i, j)
self, rhs, i, j)
} else { } else {
(0, 0) (0, 0)
} }
@ -90,5 +102,3 @@ impl std::ops::Mul<u128> for u128 {
rows.iter().sum::<u128>() rows.iter().sum::<u128>()
} }
} }

View file

@ -1,5 +1,5 @@
use num::{BigUint, Integer, Zero, One}; use num::{BigUint, Integer, Zero, One};
use rand::{Rng,Rand}; use rand::{Rng, Rand};
use std::io; use std::io;
use std::ops::{Mul, Rem, Shr}; use std::ops::{Mul, Rem, Shr};
use std::fs; use std::fs;
@ -44,7 +44,7 @@ pub fn rand_vec<G: Rng, R: Rand>(rng: &mut G, size: usize) -> Vec<R> {
vec.push(R::rand(rng)); vec.push(R::rand(rng));
} }
return vec return vec;
} }
pub mod version { pub mod version {
@ -66,10 +66,10 @@ pub trait IgnoreExt {
fn ignore(self); fn ignore(self);
} }
impl <T, E> IgnoreExt for Result<T, E> { impl<T, E> IgnoreExt for Result<T, E> {
fn ignore(self) { fn ignore(self) {
match self { match self {
Ok(_) => (), Ok(_) => (),
Err(_) => (), Err(_) => (),
} }
} }
@ -81,18 +81,19 @@ pub fn now_ms() -> i64 {
} }
pub fn mkdir_existing(path: &Path) -> io::Result<()> { pub fn mkdir_existing(path: &Path) -> io::Result<()> {
fs::create_dir(path) fs::create_dir(path).or_else(|err| {
.or_else(|err| if err.kind() == io::ErrorKind::AlreadyExists { if err.kind() == io::ErrorKind::AlreadyExists {
Ok(()) Ok(())
} else { } else {
Err(err) Err(err)
}) }
})
} }
pub fn powm(base: &BigUint, exp: &BigUint, modulus: &BigUint) -> BigUint { pub fn powm(base: &BigUint, exp: &BigUint, modulus: &BigUint) -> BigUint {
let mut base = base.clone(); let mut base = base.clone();
let mut exp = exp.clone(); let mut exp = exp.clone();
let mut result : BigUint = One::one(); let mut result: BigUint = One::one();
while !exp.is_zero() { while !exp.is_zero() {
if exp.is_odd() { if exp.is_odd() {
@ -117,7 +118,7 @@ impl StrChunksExt for str {
} }
} }
impl <'s> Iterator for StrChunks<'s> { impl<'s> Iterator for StrChunks<'s> {
type Item = &'s str; type Item = &'s str;
fn next(&mut self) -> Option<&'s str> { fn next(&mut self) -> Option<&'s str> {
let &mut StrChunks(data, size) = self; let &mut StrChunks(data, size) = self;
@ -130,4 +131,3 @@ impl <'s> Iterator for StrChunks<'s> {
} }
} }
} }

View file

@ -1,6 +1,6 @@
use std; use std;
use util::u128; use util::u128;
use byteorder::{BigEndian,ByteOrder}; use byteorder::{BigEndian, ByteOrder};
use std::ascii::AsciiExt; use std::ascii::AsciiExt;
#[derive(Debug,Copy,Clone,PartialEq,Eq,Hash)] #[derive(Debug,Copy,Clone,PartialEq,Eq,Hash)]
@ -9,7 +9,8 @@ pub struct SpotifyId(u128);
#[derive(Debug,Copy,Clone,PartialEq,Eq,Hash)] #[derive(Debug,Copy,Clone,PartialEq,Eq,Hash)]
pub struct FileId(pub [u8; 20]); pub struct FileId(pub [u8; 20]);
const BASE62_DIGITS: &'static [u8] = b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; const BASE62_DIGITS: &'static [u8] =
b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
const BASE16_DIGITS: &'static [u8] = b"0123456789abcdef"; const BASE16_DIGITS: &'static [u8] = b"0123456789abcdef";
impl SpotifyId { impl SpotifyId {
@ -17,7 +18,7 @@ impl SpotifyId {
assert!(id.is_ascii()); assert!(id.is_ascii());
let data = id.as_bytes(); let data = id.as_bytes();
let mut n : u128 = std::num::Zero::zero(); let mut n: u128 = std::num::Zero::zero();
for c in data { for c in data {
let d = BASE16_DIGITS.iter().position(|e| e == c).unwrap() as u8; let d = BASE16_DIGITS.iter().position(|e| e == c).unwrap() as u8;
n = n * u128::from(16); n = n * u128::from(16);
@ -31,7 +32,7 @@ impl SpotifyId {
assert!(id.is_ascii()); assert!(id.is_ascii());
let data = id.as_bytes(); let data = id.as_bytes();
let mut n : u128 = std::num::Zero::zero(); let mut n: u128 = std::num::Zero::zero();
for c in data { for c in data {
let d = BASE62_DIGITS.iter().position(|e| e == c).unwrap() as u8; let d = BASE62_DIGITS.iter().position(|e| e == c).unwrap() as u8;
n = n * u128::from(62); n = n * u128::from(62);
@ -56,10 +57,10 @@ impl SpotifyId {
let mut data = [0u8; 32]; let mut data = [0u8; 32];
for i in 0..16 { for i in 0..16 {
data[31-i] = BASE16_DIGITS[(low.wrapping_shr(4 * i as u32) & 0xF) as usize]; data[31 - i] = BASE16_DIGITS[(low.wrapping_shr(4 * i as u32) & 0xF) as usize];
} }
for i in 0..16 { for i in 0..16 {
data[15-i] = BASE16_DIGITS[(high.wrapping_shr(4 * i as u32) & 0xF) as usize]; data[15 - i] = BASE16_DIGITS[(high.wrapping_shr(4 * i as u32) & 0xF) as usize];
} }
std::str::from_utf8(&data).unwrap().to_owned() std::str::from_utf8(&data).unwrap().to_owned()
@ -71,7 +72,7 @@ impl SpotifyId {
let mut data = [0u8; 16]; let mut data = [0u8; 16];
BigEndian::write_u64(&mut data[0..8], high); BigEndian::write_u64(&mut data[0..8], high);
BigEndian::write_u64(&mut data[8..16], low); BigEndian::write_u64(&mut data[8..16], low);
data data
@ -80,10 +81,10 @@ impl SpotifyId {
impl FileId { impl FileId {
pub fn to_base16(&self) -> String { pub fn to_base16(&self) -> String {
self.0.iter() self.0
.iter()
.map(|b| format!("{:02x}", b)) .map(|b| format!("{:02x}", b))
.collect::<Vec<String>>() .collect::<Vec<String>>()
.concat() .concat()
} }
} }

View file

@ -2,39 +2,38 @@ use std::io::{Read, Seek, SeekFrom, Result};
pub struct Subfile<T: Read + Seek> { pub struct Subfile<T: Read + Seek> {
stream: T, stream: T,
offset: u64 offset: u64,
} }
impl <T: Read + Seek> Subfile<T> { impl<T: Read + Seek> Subfile<T> {
pub fn new(mut stream: T, offset: u64) -> Subfile<T> { pub fn new(mut stream: T, offset: u64) -> Subfile<T> {
stream.seek(SeekFrom::Start(offset)).unwrap(); stream.seek(SeekFrom::Start(offset)).unwrap();
Subfile { Subfile {
stream: stream, stream: stream,
offset: offset offset: offset,
} }
} }
} }
impl <T: Read + Seek> Read for Subfile<T> { impl<T: Read + Seek> Read for Subfile<T> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> { fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
self.stream.read(buf) self.stream.read(buf)
} }
} }
impl <T: Read + Seek> Seek for Subfile<T> { impl<T: Read + Seek> Seek for Subfile<T> {
fn seek(&mut self, mut pos: SeekFrom) -> Result<u64> { fn seek(&mut self, mut pos: SeekFrom) -> Result<u64> {
pos = match pos { pos = match pos {
SeekFrom::Start(offset) => SeekFrom::Start(offset + self.offset), SeekFrom::Start(offset) => SeekFrom::Start(offset + self.offset),
x => x x => x,
}; };
let newpos = try!(self.stream.seek(pos)); let newpos = try!(self.stream.seek(pos));
if newpos > self.offset { if newpos > self.offset {
return Ok(newpos - self.offset) return Ok(newpos - self.offset);
} else { } else {
return Ok(0) return Ok(0);
} }
} }
} }

View file

@ -3,14 +3,14 @@ use std::cmp::{min, max};
pub struct ZeroFile { pub struct ZeroFile {
position: u64, position: u64,
size: u64 size: u64,
} }
impl ZeroFile { impl ZeroFile {
pub fn new(size: u64) -> ZeroFile { pub fn new(size: u64) -> ZeroFile {
ZeroFile { ZeroFile {
position: 0, position: 0,
size: size size: size,
} }
} }
} }
@ -41,4 +41,3 @@ impl io::Read for ZeroFile {
Ok(len) Ok(len)
} }
} }

View file

@ -22,7 +22,8 @@ pub mod stub {
_: &[&str]) _: &[&str])
-> std::result::Result<DNSService, DNSError> { -> std::result::Result<DNSService, DNSError> {
writeln!(&mut std::io::stderr(), writeln!(&mut std::io::stderr(),
"WARNING: dns-sd is not enabled. Service will probably not be visible").unwrap(); "WARNING: dns-sd is not enabled. Service will probably not be visible")
.unwrap();
Ok(DNSService) Ok(DNSService)
} }
} }