Преглед изворни кода

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 година
родитељ
комит
4b73f83c5e
4 измењених фајлова са 91 додато и 17 уклоњено
  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 => {