From 4b4bc2f4e1c2368e359ef25a76f41fdfe3446c8c Mon Sep 17 00:00:00 2001 From: Paul Lietar Date: Wed, 16 Mar 2016 00:05:05 +0000 Subject: [PATCH] Add Facebook based login. --- .travis.yml | 1 + Cargo.lock | 130 +++++++++++++++++++++++++++++++++++++-- Cargo.toml | 11 ++-- README.md | 16 +++-- src/apresolve.rs | 9 ++- src/data/spotilocal.cert | 35 +++++++++++ src/data/spotilocal.key | 51 +++++++++++++++ src/facebook.rs | 112 +++++++++++++++++++++++++++++++++ src/lib.in.rs | 3 + src/lib.rs | 6 ++ src/main.rs | 43 ++++++++----- src/util/mod.rs | 8 +-- 12 files changed, 383 insertions(+), 42 deletions(-) create mode 100644 src/data/spotilocal.cert create mode 100644 src/data/spotilocal.key create mode 100644 src/facebook.rs diff --git a/.travis.yml b/.travis.yml index 69a43626..0e5bb78a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,7 @@ addons: script: - cargo build - cargo build --features with-tremor + - cargo build --features facebook # Building without syntex only works on nightly - if [[ $(rustc --version) == *"nightly"* ]]; then cargo build --no-default-features; diff --git a/Cargo.lock b/Cargo.lock index 4c807534..22bf842e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,6 +4,7 @@ version = "0.1.0" dependencies = [ "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)", + "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)", "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)", @@ -12,6 +13,7 @@ dependencies = [ "lazy_static 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", "librespot-protocol 0.1.0", "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)", "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)", @@ -90,11 +92,24 @@ name = "chunked_transfer" version = "0.3.1" 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]] name = "cookie" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" 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)", "url 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -180,6 +195,14 @@ name = "gcc" version = "0.3.25" 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]] name = "getopts" version = "0.2.14" @@ -209,6 +232,7 @@ dependencies = [ "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)", "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)", "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)", @@ -235,7 +259,7 @@ name = "kernel32-sys" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" 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)", ] @@ -267,6 +291,14 @@ dependencies = [ "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]] name = "log" version = "0.3.5" @@ -289,6 +321,11 @@ dependencies = [ "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]] name = "num" version = "0.1.31" @@ -316,11 +353,54 @@ dependencies = [ "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]] name = "pkg-config" version = "0.3.8" 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]] name = "portaudio" version = "0.2.0" @@ -392,6 +472,11 @@ dependencies = [ "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]] name = "rpassword" version = "0.1.3" @@ -400,7 +485,7 @@ dependencies = [ "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)", "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]] @@ -433,6 +518,14 @@ name = "semver" version = "0.1.20" 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]] name = "serde" version = "0.6.15" @@ -499,6 +592,14 @@ dependencies = [ "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]] name = "tempfile" version = "2.0.1" @@ -507,7 +608,7 @@ dependencies = [ "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)", "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]] @@ -516,7 +617,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "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]] @@ -534,7 +635,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "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)", - "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]] @@ -550,6 +651,14 @@ dependencies = [ "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]] name = "traitobject" version = "0.0.1" @@ -627,6 +736,15 @@ dependencies = [ "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]] name = "uuid" version = "0.1.18" @@ -681,7 +799,7 @@ dependencies = [ [[package]] name = "winapi" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] diff --git a/Cargo.toml b/Cargo.toml index 9e97984e..c0e3220b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,6 +44,8 @@ protobuf_macros = { git = "https://github.com/plietar/rust-protobuf-macros" } shannon = { git = "https://github.com/plietar/rust-shannon" } tremor = { git = "https://github.com/plietar/rust-tremor", optional = true } +openssl = { version = "0.7", optional = true } + [build-dependencies] vergen = "~0.1.0" 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" } [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 = [] -default = ["with-syntex"] +default = ["with-syntex"] diff --git a/README.md b/README.md index db259645..f401d6c5 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,16 @@ cargo build --release --features discovery 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 When developing *librespot*, it is preferable to use Rust nightly, and build it using the following : ```shell @@ -55,12 +65,6 @@ cargo build --no-default-features 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 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 diff --git a/src/apresolve.rs b/src/apresolve.rs index fd71d047..ca01a066 100644 --- a/src/apresolve.rs +++ b/src/apresolve.rs @@ -11,13 +11,12 @@ pub struct APResolveData { pub fn apresolve() -> Result, ()> { 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) } diff --git a/src/data/spotilocal.cert b/src/data/spotilocal.cert new file mode 100644 index 00000000..26781a94 --- /dev/null +++ b/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----- diff --git a/src/data/spotilocal.key b/src/data/spotilocal.key new file mode 100644 index 00000000..e5e38f62 --- /dev/null +++ b/src/data/spotilocal.key @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/src/facebook.rs b/src/facebook.rs new file mode 100644 index 00000000..9523a61c --- /dev/null +++ b/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 { + 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>, + csrf: String, +} + +impl ServerHandler { + fn handle_login(&self, params: &BTreeMap) -> 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::>(); + + if request.method == hyper::method::Method::Get && path == vec!["login", "facebook_login_sso.json"] { + self.handle_login(¶ms) + } else { + hyper::status::StatusCode::NotFound + } + } else { + hyper::status::StatusCode::NotFound + } + } +} + +pub fn facebook_get_me_id(token: &str) -> Result { + 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 = json::decode(&body).unwrap(); + Ok(result.remove("id").unwrap()) +} + +pub fn facebook_login() -> Result { + let (tx, rx) = mpsc::channel(); + + let csrf = rand::thread_rng().gen_ascii_chars().take(32).collect::(); + 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) +} diff --git a/src/lib.in.rs b/src/lib.in.rs index c1fa32b4..e2eab057 100644 --- a/src/lib.in.rs +++ b/src/lib.in.rs @@ -18,4 +18,7 @@ pub mod stream; pub mod apresolve; mod zeroconf; +#[cfg(feature = "facebook")] +pub mod facebook; + pub use album_cover::get_album_cover; diff --git a/src/lib.rs b/src/lib.rs index 26c57966..8e8d8fcb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,9 @@ #![cfg_attr(not(feature = "with-syntex"), plugin(protobuf_macros))] #![cfg_attr(not(feature = "with-syntex"), plugin(json_macros))] +#![cfg_attr(feature="clippy", feature(plugin))] +#![cfg_attr(feature="clippy", plugin(clippy))] + #[macro_use] extern crate lazy_static; @@ -31,6 +34,9 @@ extern crate tremor as vorbis; #[cfg(feature = "dns-sd")] extern crate dns_sd; +#[cfg(feature = "openssl")] +extern crate openssl; + extern crate librespot_protocol as protocol; #[cfg(feature = "with-syntex")] diff --git a/src/main.rs b/src/main.rs index 4f8cd766..372630b7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,6 +17,13 @@ use librespot::session::{Bitrate, Config, Session}; use librespot::spirc::SpircManager; use librespot::util::version::version_string; +#[cfg(feature = "facebook")] +use librespot::facebook::facebook_login; +#[cfg(not(feature = "facebook"))] +fn facebook_login() -> Result { + Err(()) +} + static PASSWORD_ENV_NAME: &'static str = "SPOTIFY_PASSWORD"; fn usage(program: &str, opts: &getopts::Options) -> String { @@ -46,6 +53,10 @@ fn main() { 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..]) { Ok(m) => m, Err(f) => { @@ -68,19 +79,6 @@ fn main() { let cache_location = matches.opt_str("c").map(PathBuf::from); 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) { None => Bitrate::Bitrate160, // default value @@ -102,8 +100,23 @@ fn main() { 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) + }).or_else(|| { + if cfg!(feature = "facebook") && matches.opt_present("facebook") { + Some(facebook_login().unwrap()) + } else { + None + } }).or_else(|| { credentials_path.as_ref() .and_then(|p| File::open(p).ok()) @@ -115,6 +128,8 @@ fn main() { discovery.run() }); + std::env::remove_var(PASSWORD_ENV_NAME); + let reusable_credentials = session.login(credentials).unwrap(); if let Some(path) = credentials_path { reusable_credentials.save_to_file(path); diff --git a/src/util/mod.rs b/src/util/mod.rs index 332a9fc9..27694587 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -36,13 +36,7 @@ macro_rules! eprint( ); pub fn rand_vec(rng: &mut G, size: usize) -> Vec { - let mut vec = Vec::with_capacity(size); - - for _ in 0..size { - vec.push(R::rand(rng)); - } - - vec + rng.gen_iter().take(size).collect() }