Jelajahi Sumber

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.
Paul Lietar 9 tahun lalu
induk
melakukan
4b73f83c5e
4 mengubah file dengan 91 tambahan dan 17 penghapusan
  1. 10 5
      Cargo.lock
  2. 57 2
      src/authentication.rs
  3. 14 7
      src/main.rs
  4. 10 3
      src/session.rs

+ 10 - 5
Cargo.lock

@@ -71,6 +71,11 @@ name = "byteorder"
 version = "0.4.2"
 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]]
 name = "chrono"
 version = "0.2.20"
@@ -319,7 +324,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 [[package]]
 name = "portaudio"
 version = "0.2.0"
-source = "git+https://github.com/mvdnes/portaudio-rs#840b1096780c069fd54b4a425bcfebec99ef9c1a"
+source = "git+https://github.com/mvdnes/portaudio-rs#0b228f54a16814c52ba1ef449ac439af59f8cab0"
 dependencies = [
  "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)",
@@ -329,7 +334,7 @@ dependencies = [
 [[package]]
 name = "portaudio_sys"
 version = "0.1.1"
-source = "git+https://github.com/mvdnes/portaudio-rs#840b1096780c069fd54b4a425bcfebec99ef9c1a"
+source = "git+https://github.com/mvdnes/portaudio-rs#0b228f54a16814c52ba1ef449ac439af59f8cab0"
 dependencies = [
  "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)",
@@ -441,7 +446,7 @@ name = "shannon"
 version = "0.1.1"
 source = "git+https://github.com/plietar/rust-shannon#7000b3e49a53daaa890727ba2b2bd5a43cc4ffef"
 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)",
  "shannon-sys 0.1.0 (git+https://github.com/plietar/rust-shannon)",
 ]
@@ -555,7 +560,7 @@ name = "tremor"
 version = "0.1.0"
 source = "git+https://github.com/plietar/rust-tremor#5ced876f3cffb041fcf31238da7f3273178029fe"
 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)",
 ]
 
@@ -565,7 +570,7 @@ version = "0.1.0"
 source = "git+https://github.com/plietar/rust-tremor#5ced876f3cffb041fcf31238da7f3273178029fe"
 dependencies = [
  "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)",
 ]
 

+ 57 - 2
src/authentication.rs

@@ -7,17 +7,29 @@ use crypto::mac::Mac;
 use crypto::pbkdf2::pbkdf2;
 use crypto::sha1::Sha1;
 use protobuf::ProtobufEnum;
-use std::io::{self, Read};
-use rustc_serialize::base64::FromBase64;
+use std::io::{self, Read, Write};
+use std::fs::File;
+use std::path::Path;
+use rustc_serialize::base64::{self, FromBase64, ToBase64};
+use rustc_serialize::json;
 
 use protocol::authentication::AuthenticationType;
 
+#[derive(Debug, Clone)]
 pub struct Credentials {
     pub username: String,
     pub auth_type: AuthenticationType,
     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 {
     pub fn with_password(username: String, password: String) -> Credentials {
         Credentials {
@@ -108,4 +120,47 @@ impl Credentials {
             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(),
+        }
+    }
+}
+

+ 14 - 7
src/main.rs

@@ -55,7 +55,7 @@ fn main() {
     };
 
     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 credentials = username.map(|u| {
@@ -84,23 +84,30 @@ fn main() {
         application_key: appkey,
         user_agent: version_string(),
         device_name: name,
-        cache_location: PathBuf::from(cache_location),
+        cache_location: cache_location.clone(),
         bitrate: bitrate,
     };
 
     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());
         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 mut spirc = SpircManager::new(session.clone(), player);
+    let spirc = SpircManager::new(session.clone(), player);
     thread::spawn(move || spirc.run());
 
     loop {

+ 10 - 3
src/session.rs

@@ -180,7 +180,7 @@ impl Session {
                               &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(), {
             login_credentials => {
                 username: credentials.username,
@@ -213,12 +213,19 @@ impl Session {
                     protobuf::parse_from_bytes(&data).unwrap();
 
                 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.tx_connection.lock().unwrap() = Some(connection);
 
                 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 => {