Add Facebook based login.

This commit is contained in:
Paul Lietar 2016-03-16 00:05:05 +00:00
parent 4d712efb48
commit 4b4bc2f4e1
12 changed files with 383 additions and 42 deletions

View file

@ -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;

130
Cargo.lock generated
View file

@ -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]]

View file

@ -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"] discovery = ["dns-sd"]
with-syntex = ["syntex", "protobuf_macros/with-syntex", "json_macros/with-syntex"] with-syntex = ["syntex", "protobuf_macros/with-syntex", "json_macros/with-syntex"]
with-tremor = ["tremor"] with-tremor = ["tremor"]
facebook = ["hyper/ssl", "openssl"]
static-appkey = [] static-appkey = []
default = ["with-syntex"] default = ["with-syntex"]

View file

@ -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

View file

@ -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) let mut response = client.get(APRESOLVE_ENDPOINT).send().unwrap();
.send().unwrap() let mut data = String::new();
.read_to_string(&mut res).unwrap(); 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
src/data/spotilocal.cert Normal file
View file

@ -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
src/data/spotilocal.key Normal file
View file

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

112
src/facebook.rs Normal file
View file

@ -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)
}

View file

@ -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;

View file

@ -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")]

View file

@ -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);

View file

@ -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); rng.gen_iter().take(size).collect()
for _ in 0..size {
vec.push(R::rand(rng));
}
vec
} }