mirror of
https://github.com/librespot-org/librespot.git
synced 2024-11-08 16:45:43 +00:00
Save reusable credentials to disk.
After the first login, credentials may be omitted from the command line and the stored ones will be used instead.
This commit is contained in:
parent
39af43728a
commit
4b73f83c5e
4 changed files with 91 additions and 17 deletions
15
Cargo.lock
generated
15
Cargo.lock
generated
|
@ -71,6 +71,11 @@ name = "byteorder"
|
||||||
version = "0.4.2"
|
version = "0.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chrono"
|
name = "chrono"
|
||||||
version = "0.2.20"
|
version = "0.2.20"
|
||||||
|
@ -319,7 +324,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "portaudio"
|
name = "portaudio"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/mvdnes/portaudio-rs#840b1096780c069fd54b4a425bcfebec99ef9c1a"
|
source = "git+https://github.com/mvdnes/portaudio-rs#0b228f54a16814c52ba1ef449ac439af59f8cab0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -329,7 +334,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "portaudio_sys"
|
name = "portaudio_sys"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
source = "git+https://github.com/mvdnes/portaudio-rs#840b1096780c069fd54b4a425bcfebec99ef9c1a"
|
source = "git+https://github.com/mvdnes/portaudio-rs#0b228f54a16814c52ba1ef449ac439af59f8cab0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -441,7 +446,7 @@ name = "shannon"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
source = "git+https://github.com/plietar/rust-shannon#7000b3e49a53daaa890727ba2b2bd5a43cc4ffef"
|
source = "git+https://github.com/plietar/rust-shannon#7000b3e49a53daaa890727ba2b2bd5a43cc4ffef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"shannon-sys 0.1.0 (git+https://github.com/plietar/rust-shannon)",
|
"shannon-sys 0.1.0 (git+https://github.com/plietar/rust-shannon)",
|
||||||
]
|
]
|
||||||
|
@ -555,7 +560,7 @@ name = "tremor"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/plietar/rust-tremor#5ced876f3cffb041fcf31238da7f3273178029fe"
|
source = "git+https://github.com/plietar/rust-tremor#5ced876f3cffb041fcf31238da7f3273178029fe"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"tremor-sys 0.1.0 (git+https://github.com/plietar/rust-tremor)",
|
"tremor-sys 0.1.0 (git+https://github.com/plietar/rust-tremor)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -565,7 +570,7 @@ version = "0.1.0"
|
||||||
source = "git+https://github.com/plietar/rust-tremor#5ced876f3cffb041fcf31238da7f3273178029fe"
|
source = "git+https://github.com/plietar/rust-tremor#5ced876f3cffb041fcf31238da7f3273178029fe"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gcc 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gcc 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ogg-sys 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ogg-sys 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -7,17 +7,29 @@ use crypto::mac::Mac;
|
||||||
use crypto::pbkdf2::pbkdf2;
|
use crypto::pbkdf2::pbkdf2;
|
||||||
use crypto::sha1::Sha1;
|
use crypto::sha1::Sha1;
|
||||||
use protobuf::ProtobufEnum;
|
use protobuf::ProtobufEnum;
|
||||||
use std::io::{self, Read};
|
use std::io::{self, Read, Write};
|
||||||
use rustc_serialize::base64::FromBase64;
|
use std::fs::File;
|
||||||
|
use std::path::Path;
|
||||||
|
use rustc_serialize::base64::{self, FromBase64, ToBase64};
|
||||||
|
use rustc_serialize::json;
|
||||||
|
|
||||||
use protocol::authentication::AuthenticationType;
|
use protocol::authentication::AuthenticationType;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct Credentials {
|
pub struct Credentials {
|
||||||
pub username: String,
|
pub username: String,
|
||||||
pub auth_type: AuthenticationType,
|
pub auth_type: AuthenticationType,
|
||||||
pub auth_data: Vec<u8>,
|
pub auth_data: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
#[derive(RustcDecodable, RustcEncodable)]
|
||||||
|
struct StoredCredentials {
|
||||||
|
pub username: String,
|
||||||
|
pub auth_type: i32,
|
||||||
|
pub auth_data: String,
|
||||||
|
}
|
||||||
|
|
||||||
impl Credentials {
|
impl Credentials {
|
||||||
pub fn with_password(username: String, password: String) -> Credentials {
|
pub fn with_password(username: String, password: String) -> Credentials {
|
||||||
Credentials {
|
Credentials {
|
||||||
|
@ -108,4 +120,47 @@ impl Credentials {
|
||||||
auth_data: auth_data,
|
auth_data: auth_data,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_reader<R: Read>(mut reader: R) -> Credentials {
|
||||||
|
let mut contents = String::new();
|
||||||
|
reader.read_to_string(&mut contents).unwrap();
|
||||||
|
|
||||||
|
json::decode::<StoredCredentials>(&contents).unwrap().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_file<P: AsRef<Path>>(path: P) -> Credentials {
|
||||||
|
let file = File::open(path).unwrap();
|
||||||
|
Credentials::from_reader(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save_to_writer<W: Write>(&self, writer: &mut W) {
|
||||||
|
let contents = json::encode::<StoredCredentials>(&self.clone().into()).unwrap();
|
||||||
|
writer.write_all(contents.as_bytes()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save_to_file<P: AsRef<Path>>(&self, path: P) {
|
||||||
|
let mut file = File::create(path).unwrap();
|
||||||
|
self.save_to_writer(&mut file)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Credentials> for StoredCredentials {
|
||||||
|
fn from(credentials: Credentials) -> StoredCredentials {
|
||||||
|
StoredCredentials {
|
||||||
|
username: credentials.username,
|
||||||
|
auth_type: credentials.auth_type.value(),
|
||||||
|
auth_data: credentials.auth_data.to_base64(base64::STANDARD),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<StoredCredentials> for Credentials {
|
||||||
|
fn from(credentials: StoredCredentials) -> Credentials {
|
||||||
|
Credentials {
|
||||||
|
username: credentials.username,
|
||||||
|
auth_type: AuthenticationType::from_i32(credentials.auth_type).unwrap(),
|
||||||
|
auth_data: credentials.auth_data.from_base64().unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
21
src/main.rs
21
src/main.rs
|
@ -55,7 +55,7 @@ fn main() {
|
||||||
};
|
};
|
||||||
|
|
||||||
let username = matches.opt_str("u");
|
let username = matches.opt_str("u");
|
||||||
let cache_location = matches.opt_str("c").unwrap();
|
let cache_location = PathBuf::from(matches.opt_str("c").unwrap());
|
||||||
let name = matches.opt_str("n").unwrap();
|
let name = matches.opt_str("n").unwrap();
|
||||||
|
|
||||||
let credentials = username.map(|u| {
|
let credentials = username.map(|u| {
|
||||||
|
@ -84,23 +84,30 @@ fn main() {
|
||||||
application_key: appkey,
|
application_key: appkey,
|
||||||
user_agent: version_string(),
|
user_agent: version_string(),
|
||||||
device_name: name,
|
device_name: name,
|
||||||
cache_location: PathBuf::from(cache_location),
|
cache_location: cache_location.clone(),
|
||||||
bitrate: bitrate,
|
bitrate: bitrate,
|
||||||
};
|
};
|
||||||
|
|
||||||
let session = Session::new(config);
|
let session = Session::new(config);
|
||||||
|
|
||||||
let credentials = credentials.map_or_else(|| {
|
let credentials_path = cache_location.join("credentials.json");
|
||||||
|
|
||||||
|
let credentials = credentials.map(|(username, password)| {
|
||||||
|
Credentials::with_password(username, password)
|
||||||
|
}).or_else(|| {
|
||||||
|
File::open(&credentials_path).map(|file| {
|
||||||
|
Credentials::from_reader(file)
|
||||||
|
}).ok()
|
||||||
|
}).unwrap_or_else(|| {
|
||||||
let mut discovery = DiscoveryManager::new(session.clone());
|
let mut discovery = DiscoveryManager::new(session.clone());
|
||||||
discovery.run()
|
discovery.run()
|
||||||
}, |(username, password)| {
|
|
||||||
Credentials::with_password(username, password)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
session.login(credentials).unwrap();
|
let reusable_credentials = session.login(credentials).unwrap();
|
||||||
|
reusable_credentials.save_to_file(credentials_path);
|
||||||
|
|
||||||
let player = Player::new(session.clone());
|
let player = Player::new(session.clone());
|
||||||
let mut spirc = SpircManager::new(session.clone(), player);
|
let spirc = SpircManager::new(session.clone(), player);
|
||||||
thread::spawn(move || spirc.run());
|
thread::spawn(move || spirc.run());
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
|
|
@ -180,7 +180,7 @@ impl Session {
|
||||||
&recv_key)
|
&recv_key)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn login(&self, credentials: Credentials) -> Result<(), ()> {
|
pub fn login(&self, credentials: Credentials) -> Result<Credentials, ()> {
|
||||||
let packet = protobuf_init!(protocol::authentication::ClientResponseEncrypted::new(), {
|
let packet = protobuf_init!(protocol::authentication::ClientResponseEncrypted::new(), {
|
||||||
login_credentials => {
|
login_credentials => {
|
||||||
username: credentials.username,
|
username: credentials.username,
|
||||||
|
@ -213,12 +213,19 @@ impl Session {
|
||||||
protobuf::parse_from_bytes(&data).unwrap();
|
protobuf::parse_from_bytes(&data).unwrap();
|
||||||
|
|
||||||
let username = welcome_data.get_canonical_username().to_owned();
|
let username = welcome_data.get_canonical_username().to_owned();
|
||||||
self.0.data.write().unwrap().canonical_username = username;
|
self.0.data.write().unwrap().canonical_username = username.clone();
|
||||||
*self.0.rx_connection.lock().unwrap() = Some(connection.clone());
|
*self.0.rx_connection.lock().unwrap() = Some(connection.clone());
|
||||||
*self.0.tx_connection.lock().unwrap() = Some(connection);
|
*self.0.tx_connection.lock().unwrap() = Some(connection);
|
||||||
|
|
||||||
eprintln!("Authenticated !");
|
eprintln!("Authenticated !");
|
||||||
Ok(())
|
|
||||||
|
let reusable_credentials = Credentials {
|
||||||
|
username: username,
|
||||||
|
auth_type: welcome_data.get_reusable_auth_credentials_type(),
|
||||||
|
auth_data: welcome_data.get_reusable_auth_credentials().to_owned(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(reusable_credentials)
|
||||||
}
|
}
|
||||||
|
|
||||||
0xad => {
|
0xad => {
|
||||||
|
|
Loading…
Reference in a new issue