Jelajahi Sumber

Add Facebook based login.

Paul Lietar 9 tahun lalu
induk
melakukan
4b4bc2f4e1
12 mengubah file dengan 383 tambahan dan 42 penghapusan
  1. 1 0
      .travis.yml
  2. 124 6
      Cargo.lock
  3. 7 4
      Cargo.toml
  4. 10 6
      README.md
  5. 4 5
      src/apresolve.rs
  6. 35 0
      src/data/spotilocal.cert
  7. 51 0
      src/data/spotilocal.key
  8. 112 0
      src/facebook.rs
  9. 3 0
      src/lib.in.rs
  10. 6 0
      src/lib.rs
  11. 29 14
      src/main.rs
  12. 1 7
      src/util/mod.rs

+ 1 - 0
.travis.yml

@@ -13,6 +13,7 @@ addons:
 script:
 script:
     - cargo build
     - cargo build
     - cargo build --features with-tremor
     - cargo build --features with-tremor
+    - cargo build --features facebook
     # Building without syntex only works on nightly
     # Building without syntex only works on nightly
     - if [[ $(rustc --version) == *"nightly"* ]]; then
     - if [[ $(rustc --version) == *"nightly"* ]]; then
         cargo build --no-default-features;
         cargo build --no-default-features;

+ 124 - 6
Cargo.lock

@@ -4,6 +4,7 @@ version = "0.1.0"
 dependencies = [
 dependencies = [
  "bit-set 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bit-set 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "clippy 0.0.53 (registry+https://github.com/rust-lang/crates.io-index)",
  "dns-sd 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "dns-sd 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "eventual 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "eventual 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
  "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -12,6 +13,7 @@ dependencies = [
  "lazy_static 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
  "librespot-protocol 0.1.0",
  "librespot-protocol 0.1.0",
  "num 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)",
  "num 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)",
+ "openssl 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "portaudio 0.2.0 (git+https://github.com/mvdnes/portaudio-rs)",
  "portaudio 0.2.0 (git+https://github.com/mvdnes/portaudio-rs)",
  "protobuf 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
  "protobuf 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
  "protobuf_macros 0.3.0 (git+https://github.com/plietar/rust-protobuf-macros)",
  "protobuf_macros 0.3.0 (git+https://github.com/plietar/rust-protobuf-macros)",
@@ -90,11 +92,24 @@ name = "chunked_transfer"
 version = "0.3.1"
 version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 
+[[package]]
+name = "clippy"
+version = "0.0.53"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "regex-syntax 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "toml 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 [[package]]
 name = "cookie"
 name = "cookie"
 version = "0.2.2"
 version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
 dependencies = [
+ "openssl 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 ]
@@ -180,6 +195,14 @@ name = "gcc"
 version = "0.3.25"
 version = "0.3.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 
+[[package]]
+name = "gdi32-sys"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 [[package]]
 name = "getopts"
 name = "getopts"
 version = "0.2.14"
 version = "0.2.14"
@@ -209,6 +232,7 @@ dependencies = [
  "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "mime 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "mime 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
  "num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "openssl 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
  "solicit 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "solicit 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -235,7 +259,7 @@ name = "kernel32-sys"
 version = "0.2.1"
 version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
 dependencies = [
- "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 ]
 
 
@@ -267,6 +291,14 @@ dependencies = [
  "protobuf_build 0.1.1 (git+https://github.com/plietar/rust-protobuf-build.git)",
  "protobuf_build 0.1.1 (git+https://github.com/plietar/rust-protobuf-build.git)",
 ]
 ]
 
 
+[[package]]
+name = "libressl-pnacl-sys"
+version = "2.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "pnacl-build-helper 1.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 [[package]]
 name = "log"
 name = "log"
 version = "0.3.5"
 version = "0.3.5"
@@ -289,6 +321,11 @@ dependencies = [
  "serde 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 ]
 
 
+[[package]]
+name = "nom"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 [[package]]
 name = "num"
 name = "num"
 version = "0.1.31"
 version = "0.1.31"
@@ -316,11 +353,54 @@ dependencies = [
  "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)",
 ]
 ]
 
 
+[[package]]
+name = "openssl"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gcc 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "openssl-sys 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "openssl-sys-extras 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "openssl-sys"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "gdi32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libressl-pnacl-sys 2.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "user32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "openssl-sys-extras"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "gcc 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "openssl-sys 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 [[package]]
 name = "pkg-config"
 name = "pkg-config"
 version = "0.3.8"
 version = "0.3.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 
+[[package]]
+name = "pnacl-build-helper"
+version = "1.4.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "tempdir 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 [[package]]
 name = "portaudio"
 name = "portaudio"
 version = "0.2.0"
 version = "0.2.0"
@@ -392,6 +472,11 @@ 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)",
 ]
 ]
 
 
+[[package]]
+name = "regex-syntax"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 [[package]]
 name = "rpassword"
 name = "rpassword"
 version = "0.1.3"
 version = "0.1.3"
@@ -400,7 +485,7 @@ dependencies = [
  "kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "kernel32-sys 0.2.1 (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)",
  "termios 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "termios 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 ]
 
 
 [[package]]
 [[package]]
@@ -433,6 +518,14 @@ name = "semver"
 version = "0.1.20"
 version = "0.1.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 
+[[package]]
+name = "semver"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "nom 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 [[package]]
 name = "serde"
 name = "serde"
 version = "0.6.15"
 version = "0.6.15"
@@ -499,6 +592,14 @@ dependencies = [
  "unicode-xid 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-xid 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 ]
 
 
+[[package]]
+name = "tempdir"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 [[package]]
 name = "tempfile"
 name = "tempfile"
 version = "2.0.1"
 version = "2.0.1"
@@ -507,7 +608,7 @@ dependencies = [
  "kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "kernel32-sys 0.2.1 (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)",
  "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 ]
 
 
 [[package]]
 [[package]]
@@ -516,7 +617,7 @@ version = "0.2.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
 dependencies = [
  "kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 ]
 
 
 [[package]]
 [[package]]
@@ -534,7 +635,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
 dependencies = [
  "kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "kernel32-sys 0.2.1 (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)",
- "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 ]
 
 
 [[package]]
 [[package]]
@@ -550,6 +651,14 @@ dependencies = [
  "url 0.2.38 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 0.2.38 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 ]
 
 
+[[package]]
+name = "toml"
+version = "0.1.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 [[package]]
 name = "traitobject"
 name = "traitobject"
 version = "0.0.1"
 version = "0.0.1"
@@ -627,6 +736,15 @@ dependencies = [
  "uuid 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
  "uuid 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 ]
 
 
+[[package]]
+name = "user32-sys"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 [[package]]
 name = "uuid"
 name = "uuid"
 version = "0.1.18"
 version = "0.1.18"
@@ -681,7 +799,7 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "winapi"
 name = "winapi"
-version = "0.2.5"
+version = "0.2.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 
 [[package]]
 [[package]]

+ 7 - 4
Cargo.toml

@@ -44,6 +44,8 @@ protobuf_macros = { git = "https://github.com/plietar/rust-protobuf-macros" }
 shannon         = { git = "https://github.com/plietar/rust-shannon" }
 shannon         = { git = "https://github.com/plietar/rust-shannon" }
 tremor          = { git = "https://github.com/plietar/rust-tremor", optional = true }
 tremor          = { git = "https://github.com/plietar/rust-tremor", optional = true }
 
 
+openssl         = { version = "0.7", optional = true }
+
 [build-dependencies]
 [build-dependencies]
 vergen          = "~0.1.0"
 vergen          = "~0.1.0"
 syntex          = { version = "*", optional = true }
 syntex          = { version = "*", optional = true }
@@ -51,8 +53,9 @@ protobuf_macros = { git = "https://github.com/plietar/rust-protobuf-macros" }
 json_macros     = { git = "https://github.com/plietar/json_macros" }
 json_macros     = { git = "https://github.com/plietar/json_macros" }
 
 
 [features]
 [features]
-discovery = ["dns-sd"]
-with-syntex = ["syntex", "protobuf_macros/with-syntex", "json_macros/with-syntex"]
-with-tremor = ["tremor"]
+discovery     = ["dns-sd"]
+with-syntex   = ["syntex", "protobuf_macros/with-syntex", "json_macros/with-syntex"]
+with-tremor   = ["tremor"]
+facebook      = ["hyper/ssl", "openssl"]
 static-appkey = []
 static-appkey = []
-default = ["with-syntex"]
+default       = ["with-syntex"]

+ 10 - 6
README.md

@@ -47,6 +47,16 @@ cargo build --release --features discovery
 
 
 When running *librespot* simply omit the `--username` argument.
 When running *librespot* simply omit the `--username` argument.
 
 
+## Facebook Accounts
+*librespot* can be built with Facebook authentication support. OpenSSL is required for this.
+
+```shell
+cargo build --release --features facebook
+target/release/librespot --appkey APPKEY --cache CACHEDIR --name DEVICENAME --facebook
+```
+
+This will print a link to the console, which must be visited on the same computer *librespot* is running on.
+
 ## Development
 ## Development
 When developing *librespot*, it is preferable to use Rust nightly, and build it using the following :
 When developing *librespot*, it is preferable to use Rust nightly, and build it using the following :
 ```shell
 ```shell
@@ -55,12 +65,6 @@ cargo build --no-default-features
 
 
 This produces better compilation error messages than with the default configuration.
 This produces better compilation error messages than with the default configuration.
 
 
-## Facebook Accounts
-If you connect using a facebook account, librespot will not show up among the
-devices in the Spotify app. What you need to do is apply for a
-[device password](http://www.spotify.com/account/set-device-password/) and
-use that to sign in instead.
-
 ## Disclaimer
 ## Disclaimer
 Using this code to connect to Spotify's API is probably forbidden by them, and
 Using this code to connect to Spotify's API is probably forbidden by them, and
 might result in you application key getting banned. Use at you own risk
 might result in you application key getting banned. Use at you own risk

+ 4 - 5
src/apresolve.rs

@@ -11,13 +11,12 @@ pub struct APResolveData {
 
 
 pub fn apresolve() -> Result<Vec<String>, ()> {
 pub fn apresolve() -> Result<Vec<String>, ()> {
     let client = hyper::client::Client::new();
     let client = hyper::client::Client::new();
-    let mut res = String::new();
     
     
-    client.get(APRESOLVE_ENDPOINT)
-          .send().unwrap()
-          .read_to_string(&mut res).unwrap();
+    let mut response = client.get(APRESOLVE_ENDPOINT).send().unwrap();
+    let mut data = String::new();
+    response.read_to_string(&mut data).unwrap();
 
 
-    let data : APResolveData = json::decode(&res).unwrap();
+    let data : APResolveData = json::decode(&data).unwrap();
 
 
     Ok(data.ap_list)
     Ok(data.ap_list)
 }
 }

+ 35 - 0
src/data/spotilocal.cert

@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIGGzCCBQOgAwIBAgIQCMsbyFVsBNk7vWlJ7YFryjANBgkqhkiG9w0BAQsFADBN
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMScwJQYDVQQDEx5E
+aWdpQ2VydCBTSEEyIFNlY3VyZSBTZXJ2ZXIgQ0EwHhcNMTQxMTI4MDAwMDAwWhcN
+MTcxMjA2MTIwMDAwWjBlMQswCQYDVQQGEwJTRTESMBAGA1UECBMJU3RvY2tob2xt
+MRIwEAYDVQQHEwlTdG9ja2hvbG0xEzARBgNVBAoTClNwb3RpZnkgQUIxGTAXBgNV
+BAMMECouc3BvdGlsb2NhbC5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
+AoICAQDwccCju1/MY/lVM+2j0Hiu/M4u8jbR5ctk11U3i3ugYF4xwyqzbh8ziQq6
+9atIDg33OTEGGD1m2LkOursIRng6UUth1wqdRlRylHG2p4JYW22WEa730hnysSDn
+AHLfFawfHh40BPwLY7UD/0c0J8dUTu7Xx3fNHcJATQAHO7KlhTj+t+axUvFj+AxK
+0YyGZKmyS4GATtdDuI7uFp188iDI3zZgSI7A4svoDUvn3yGA2G7sgOG1va8aYVRW
+ftD5f2minppMy3UKt8XVGM+CuFMbQbLaTnQCW6t3fj/luscX4mlBC7pWj+swfoC2
+q5LvV3vErA8q6jPKDZElps8KRqeLvqkSheKI9Zh7+foijkAkC1kJGbJiX7BnFhM1
+EKKS6xda4tbewDVBseguO5b37Xqvh3vexSC42q/lxGbo7Md2LlgkORoeGYw7wzDH
+OoVvYmgktmwEccNu6BtjT0ql9OW3/+PPxxBXL/85crFWrMieNBf3eLeiT5ncv/mR
+JD+EB4QTTiUvZokEbG8/haGNrJVxDImBIqc4B9dUdikNkwr+cFgjdJezM8FBy7vd
+5suQHfeLYeqkc+pN097jw6qWdIA2ZPr/96IhIb1Pg0rIC+NqFte5+DB3QKCgUZJR
+1SUyT1BuNgU65kaM3rTx71UwQ46DMvWwL6BHja8ujPhjh/UiJwIDAQABo4IB3TCC
+AdkwHwYDVR0jBBgwFoAUD4BhHIIxYdUvKOeNRji0LOHG2eIwHQYDVR0OBBYEFFGI
+qQzKfuxgQuJad85y1CRs++4ZMCsGA1UdEQQkMCKCECouc3BvdGlsb2NhbC5jb22C
+DnNwb3RpbG9jYWwuY29tMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEF
+BQcDAQYIKwYBBQUHAwIwawYDVR0fBGQwYjAvoC2gK4YpaHR0cDovL2NybDMuZGln
+aWNlcnQuY29tL3NzY2Etc2hhMi1nMy5jcmwwL6AtoCuGKWh0dHA6Ly9jcmw0LmRp
+Z2ljZXJ0LmNvbS9zc2NhLXNoYTItZzMuY3JsMEIGA1UdIAQ7MDkwNwYJYIZIAYb9
+bAEBMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMw
+fAYIKwYBBQUHAQEEcDBuMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2Vy
+dC5jb20wRgYIKwYBBQUHMAKGOmh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9E
+aWdpQ2VydFNIQTJTZWN1cmVTZXJ2ZXJDQS5jcnQwDAYDVR0TAQH/BAIwADANBgkq
+hkiG9w0BAQsFAAOCAQEAr4W8l4mhi1nZUIeRFUyaeFSuKF3wwmMTprkE8y1gZyj2
+9dReUR/3SsiWsiZhfoZVNDAuDx+poue3YnSq1oNBZvbff2mOZQmVFbMTCiDrfY0e
+5qCZDNNqoo8d93BHGmAtJq2mnhBLnGTX6s3uiQHrcqs8iSzgXfSn7T6og9GA5vCt
+qlNykfuVtbMdfVdgI3u2i4TZ/5xmJyRLvzhM4AtdwwHecbJc57/LN3Buif+Al6py
+l54mNPfvtisl0O7Otz1Tj3aqTDLG8BrLbMRccbgRmfNwAd4bW9jtL0NnKcFDbKeY
+d3Docj7OIYK7IjWfb4LKts3jUq1ZM1o/eGyp/B4G+w==
+-----END CERTIFICATE-----

+ 51 - 0
src/data/spotilocal.key

@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----

+ 112 - 0
src/facebook.rs

@@ -0,0 +1,112 @@
+use hyper;
+use hyper::net::Openssl;
+use hyper::server::Request;
+use hyper::server::Response;
+use hyper::uri::RequestUri;
+use hyper::header::AccessControlAllowOrigin;
+use openssl::ssl::{SslContext, SslMethod, SSL_VERIFY_NONE};
+use openssl::ssl::error::SslError;
+use openssl::crypto::pkey::PKey;
+use openssl::x509::X509;
+use rand::{self, Rng};
+use rustc_serialize::json;
+use std::collections::BTreeMap;
+use std::io::{Cursor, Read};
+use std::sync::{mpsc, Arc, Mutex};
+use url;
+
+use protocol::authentication::AuthenticationType;
+use authentication::Credentials;
+
+static SPOTILOCAL_CERT : &'static [u8] = include_bytes!("data/spotilocal.cert");
+static SPOTILOCAL_KEY : &'static [u8] = include_bytes!("data/spotilocal.key");
+
+fn spotilocal_ssl_context() -> Result<Openssl, SslError> {
+    let cert = try!(X509::from_pem(&mut Cursor::new(SPOTILOCAL_CERT)));
+    let key = try!(PKey::private_key_from_pem(&mut Cursor::new(SPOTILOCAL_KEY)));
+
+    let mut ctx = try!(SslContext::new(SslMethod::Sslv23));
+    try!(ctx.set_cipher_list("DEFAULT"));
+    try!(ctx.set_private_key(&key));
+    try!(ctx.set_certificate(&cert));
+    ctx.set_verify(SSL_VERIFY_NONE, None);
+    Ok(Openssl { context: Arc::new(ctx) })
+}
+
+struct ServerHandler {
+    token_tx: Mutex<mpsc::Sender<String>>,
+    csrf: String,
+}
+
+impl ServerHandler {
+    fn handle_login(&self, params: &BTreeMap<String, String>) -> hyper::status::StatusCode {
+        let token = params.get("access_token").unwrap();
+        let csrf = params.get("csrf").unwrap();
+
+        if *csrf == self.csrf {
+            self.token_tx.lock().unwrap().send(token.to_owned()).unwrap();
+            hyper::status::StatusCode::Ok
+        } else {
+            hyper::status::StatusCode::Forbidden
+        }
+    }
+}
+
+impl hyper::server::Handler for ServerHandler {
+    fn handle<'a, 'k>(&'a self, request: Request<'a, 'k>, mut response: Response<'a, hyper::net::Fresh>) {
+        response.headers_mut().set(AccessControlAllowOrigin::Value("https://login.spotify.com".to_owned()));
+        *response.status_mut() = if let RequestUri::AbsolutePath(path) = request.uri {
+            let (path, query, _) = url::parse_path(&path).unwrap();
+            let params = query.map_or(vec![], |q| url::form_urlencoded::parse(q.as_bytes()))
+                              .into_iter().collect::<BTreeMap<_,_>>();
+
+            if request.method == hyper::method::Method::Get && path == vec!["login", "facebook_login_sso.json"] {
+                self.handle_login(&params)
+            } else {
+                hyper::status::StatusCode::NotFound
+            }
+        } else {
+            hyper::status::StatusCode::NotFound
+        }
+    }
+}
+
+pub fn facebook_get_me_id(token: &str) -> Result<String, ()> {
+    let url = format!("https://graph.facebook.com/me?fields=id&access_token={}", token);
+
+    let client = hyper::Client::new();
+    let mut response = client.get(&url).send().unwrap();
+    let mut body = String::new();
+    response.read_to_string(&mut body).unwrap();
+
+    let mut result : BTreeMap<String, String> = json::decode(&body).unwrap();
+    Ok(result.remove("id").unwrap())
+}
+
+pub fn facebook_login() -> Result<Credentials, ()> {
+    let (tx, rx) = mpsc::channel();
+
+    let csrf = rand::thread_rng().gen_ascii_chars().take(32).collect::<String>();
+    let handler = ServerHandler {
+        token_tx: Mutex::new(tx),
+        csrf: csrf.clone()
+    };
+
+    let ssl = spotilocal_ssl_context().unwrap();
+    let mut server = hyper::Server::https("127.0.0.1:8001", ssl).unwrap().handle(handler).unwrap();
+
+    println!("Logging in using Facebook, please visit https://login.spotify.com/login-facebook-sso/?csrf={}&port={} in your browser.",
+             csrf, 8001);
+
+    //a2c27234068bbe05d22c1b930b3bc2f5
+    let token = rx.recv().unwrap();
+    let user_id = facebook_get_me_id(&token).unwrap();
+    let cred = Credentials {
+        username: user_id,
+        auth_type: AuthenticationType::AUTHENTICATION_FACEBOOK_TOKEN,
+        auth_data: token.as_bytes().to_owned(),
+    };
+
+    server.close().unwrap();
+    Ok(cred)
+}

+ 3 - 0
src/lib.in.rs

@@ -18,4 +18,7 @@ pub mod stream;
 pub mod apresolve;
 pub mod apresolve;
 mod zeroconf;
 mod zeroconf;
 
 
+#[cfg(feature = "facebook")]
+pub mod facebook;
+
 pub use album_cover::get_album_cover;
 pub use album_cover::get_album_cover;

+ 6 - 0
src/lib.rs

@@ -4,6 +4,9 @@
 #![cfg_attr(not(feature = "with-syntex"), plugin(protobuf_macros))]
 #![cfg_attr(not(feature = "with-syntex"), plugin(protobuf_macros))]
 #![cfg_attr(not(feature = "with-syntex"), plugin(json_macros))]
 #![cfg_attr(not(feature = "with-syntex"), plugin(json_macros))]
 
 
+#![cfg_attr(feature="clippy", feature(plugin))]
+#![cfg_attr(feature="clippy", plugin(clippy))]
+
 #[macro_use]
 #[macro_use]
 extern crate lazy_static;
 extern crate lazy_static;
 
 
@@ -31,6 +34,9 @@ extern crate tremor as vorbis;
 #[cfg(feature = "dns-sd")]
 #[cfg(feature = "dns-sd")]
 extern crate dns_sd;
 extern crate dns_sd;
 
 
+#[cfg(feature = "openssl")]
+extern crate openssl;
+
 extern crate librespot_protocol as protocol;
 extern crate librespot_protocol as protocol;
 
 
 #[cfg(feature = "with-syntex")]
 #[cfg(feature = "with-syntex")]

+ 29 - 14
src/main.rs

@@ -17,6 +17,13 @@ use librespot::session::{Bitrate, Config, Session};
 use librespot::spirc::SpircManager;
 use librespot::spirc::SpircManager;
 use librespot::util::version::version_string;
 use librespot::util::version::version_string;
 
 
+#[cfg(feature = "facebook")]
+use librespot::facebook::facebook_login;
+#[cfg(not(feature = "facebook"))]
+fn facebook_login() -> Result<Credentials, ()> {
+    Err(())
+}
+
 static PASSWORD_ENV_NAME: &'static str = "SPOTIFY_PASSWORD";
 static PASSWORD_ENV_NAME: &'static str = "SPOTIFY_PASSWORD";
 
 
 fn usage(program: &str, opts: &getopts::Options) -> String {
 fn usage(program: &str, opts: &getopts::Options) -> String {
@@ -46,6 +53,10 @@ fn main() {
         opts.optopt("a", "appkey", "Path to a spotify appkey", "APPKEY");
         opts.optopt("a", "appkey", "Path to a spotify appkey", "APPKEY");
     };
     };
 
 
+    if cfg!(feature = "facebook") {
+        opts.optflag("", "facebook", "Login with a Facebook account");
+    }
+
     let matches = match opts.parse(&args[1..]) {
     let matches = match opts.parse(&args[1..]) {
         Ok(m) => m,
         Ok(m) => m,
         Err(f) => {
         Err(f) => {
@@ -68,19 +79,6 @@ fn main() {
     let cache_location = matches.opt_str("c").map(PathBuf::from);
     let cache_location = matches.opt_str("c").map(PathBuf::from);
     let name = matches.opt_str("n").unwrap();
     let name = matches.opt_str("n").unwrap();
 
 
-    let credentials = username.map(|u| {
-        let password = matches.opt_str("p")
-                              .or_else(|| std::env::var(PASSWORD_ENV_NAME).ok())
-                              .unwrap_or_else(|| {
-                                  print!("Password: ");
-                                  stdout().flush().unwrap();
-                                  read_password().unwrap()
-                              });
-
-        (u, password)
-    });
-    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
 
 
@@ -102,8 +100,23 @@ fn main() {
 
 
     let credentials_path = cache_location.map(|c| c.join("credentials.json"));
     let credentials_path = cache_location.map(|c| c.join("credentials.json"));
 
 
-    let credentials = credentials.map(|(username, password)| {
+    let credentials = username.map(|username| {
+        let password = matches.opt_str("p")
+                              .or_else(|| std::env::var(PASSWORD_ENV_NAME).ok())
+                              .unwrap_or_else(|| {
+                                  print!("Password: ");
+                                  stdout().flush().unwrap();
+                                  read_password().unwrap()
+                              });
+
+
         Credentials::with_password(username, password)
         Credentials::with_password(username, password)
+    }).or_else(|| {
+        if cfg!(feature = "facebook") && matches.opt_present("facebook") {
+            Some(facebook_login().unwrap())
+        } else {
+            None
+        }
     }).or_else(|| {
     }).or_else(|| {
         credentials_path.as_ref()
         credentials_path.as_ref()
                         .and_then(|p| File::open(p).ok())
                         .and_then(|p| File::open(p).ok())
@@ -115,6 +128,8 @@ fn main() {
         discovery.run()
         discovery.run()
     });
     });
 
 
+    std::env::remove_var(PASSWORD_ENV_NAME);
+
     let reusable_credentials = session.login(credentials).unwrap();
     let reusable_credentials = session.login(credentials).unwrap();
     if let Some(path) = credentials_path {
     if let Some(path) = credentials_path {
         reusable_credentials.save_to_file(path);
         reusable_credentials.save_to_file(path);

+ 1 - 7
src/util/mod.rs

@@ -36,13 +36,7 @@ macro_rules! eprint(
 );
 );
 
 
 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> {
-    let mut vec = Vec::with_capacity(size);
-
-    for _ in 0..size {
-        vec.push(R::rand(rng));
-    }
-
-    vec
+    rng.gen_iter().take(size).collect()
 }
 }