diff --git a/Cargo.lock b/Cargo.lock index 1d687112..d8f80b48 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1435,7 +1435,6 @@ dependencies = [ "bytes", "cfg-if 1.0.0", "env_logger", - "error-chain", "futures-core", "futures-sink", "futures-util", @@ -1455,6 +1454,7 @@ dependencies = [ "serde_json", "sha-1 0.9.4", "shannon", + "thiserror", "tokio", "tokio-stream", "tokio-util", diff --git a/core/Cargo.toml b/core/Cargo.toml index 85f3be62..0ab4e398 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -18,7 +18,6 @@ base64 = "0.13" byteorder = "1.4" bytes = "1.0" cfg-if = "1" -error-chain = { version = "0.12", default-features = false } futures-core = { version = "0.3", default-features = false } futures-sink = { version = "0.3", default-features = false } futures-util = { version = "0.3", default-features = false, features = ["alloc", "bilock", "unstable", "sink"] } @@ -37,6 +36,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" sha-1 = "0.9" shannon = "0.2.0" +thiserror = "1" tokio = { version = "1.0", features = ["io-util", "net", "rt", "sync"] } tokio-stream = "0.1" tokio-util = { version = "0.6", features = ["codec"] } diff --git a/core/src/authentication.rs b/core/src/authentication.rs index ff477df5..544dda4c 100644 --- a/core/src/authentication.rs +++ b/core/src/authentication.rs @@ -1,5 +1,4 @@ use std::io::{self, Read}; -use std::ops::FnOnce; use aes::Aes192; use byteorder::{BigEndian, ByteOrder}; @@ -10,7 +9,6 @@ use serde::{Deserialize, Serialize}; use sha1::{Digest, Sha1}; use crate::protocol::authentication::AuthenticationType; -use crate::protocol::keyexchange::{APLoginFailed, ErrorCode}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Credentials { @@ -144,37 +142,3 @@ where let v: String = serde::Deserialize::deserialize(de)?; base64::decode(&v).map_err(|e| serde::de::Error::custom(e.to_string())) } - -error_chain! { - types { - AuthenticationError, AuthenticationErrorKind, AuthenticationResultExt, AuthenticationResult; - } - - foreign_links { - Io(::std::io::Error); - } - - errors { - BadCredentials { - description("Bad credentials") - display("Authentication failed with error: Bad credentials") - } - PremiumAccountRequired { - description("Premium account required") - display("Authentication failed with error: Premium account required") - } - } -} - -impl From for AuthenticationError { - fn from(login_failure: APLoginFailed) -> Self { - let error_code = login_failure.get_error_code(); - match error_code { - ErrorCode::BadCredentials => Self::from_kind(AuthenticationErrorKind::BadCredentials), - ErrorCode::PremiumAccountRequired => { - Self::from_kind(AuthenticationErrorKind::PremiumAccountRequired) - } - _ => format!("Authentication failed with error: {:?}", error_code).into(), - } - } -} diff --git a/core/src/connection/mod.rs b/core/src/connection/mod.rs index 6bdbde6a..a07f9a2d 100644 --- a/core/src/connection/mod.rs +++ b/core/src/connection/mod.rs @@ -4,21 +4,60 @@ mod handshake; pub use self::codec::APCodec; pub use self::handshake::handshake; -use futures_util::{SinkExt, StreamExt}; -use protobuf::{self, Message}; -use std::io; +use std::io::{self, ErrorKind}; use std::net::ToSocketAddrs; + +use futures_util::{SinkExt, StreamExt}; +use protobuf::{self, Message, ProtobufError}; +use thiserror::Error; use tokio::net::TcpStream; use tokio_util::codec::Framed; use url::Url; -use crate::authentication::{AuthenticationError, Credentials}; +use crate::authentication::Credentials; +use crate::protocol::keyexchange::{APLoginFailed, ErrorCode}; +use crate::proxytunnel; use crate::version; -use crate::proxytunnel; - pub type Transport = Framed; +fn login_error_message(code: &ErrorCode) -> &'static str { + pub use ErrorCode::*; + match code { + ProtocolError => "Protocol error", + TryAnotherAP => "Try another AP", + BadConnectionId => "Bad connection id", + TravelRestriction => "Travel restriction", + PremiumAccountRequired => "Premium account required", + BadCredentials => "Bad credentials", + CouldNotValidateCredentials => "Could not validate credentials", + AccountExists => "Account exists", + ExtraVerificationRequired => "Extra verification required", + InvalidAppKey => "Invalid app key", + ApplicationBanned => "Application banned", + } +} + +#[derive(Debug, Error)] +pub enum AuthenticationError { + #[error("Login failed with reason: {}", login_error_message(.0))] + LoginFailed(ErrorCode), + #[error("Authentication failed: {0}")] + IoError(#[from] io::Error), +} + +impl From for AuthenticationError { + fn from(e: ProtobufError) -> Self { + io::Error::new(ErrorKind::InvalidData, e).into() + } +} + +impl From for AuthenticationError { + fn from(login_failure: APLoginFailed) -> Self { + Self::LoginFailed(login_failure.get_error_code()) + } +} + pub async fn connect(addr: String, proxy: &Option) -> io::Result { let socket = if let Some(proxy) = proxy { info!("Using proxy \"{}\"", proxy); @@ -66,7 +105,6 @@ pub async fn authenticate( device_id: &str, ) -> Result { use crate::protocol::authentication::{APWelcome, ClientResponseEncrypted, CpuFamily, Os}; - use crate::protocol::keyexchange::APLoginFailed; let mut packet = ClientResponseEncrypted::new(); packet @@ -101,7 +139,7 @@ pub async fn authenticate( let (cmd, data) = transport.next().await.expect("EOF")?; match cmd { 0xac => { - let welcome_data: APWelcome = protobuf::parse_from_bytes(data.as_ref()).unwrap(); + let welcome_data: APWelcome = protobuf::parse_from_bytes(data.as_ref())?; let reusable_credentials = Credentials { username: welcome_data.get_canonical_username().to_owned(), @@ -111,12 +149,13 @@ pub async fn authenticate( Ok(reusable_credentials) } - 0xad => { - let error_data: APLoginFailed = protobuf::parse_from_bytes(data.as_ref()).unwrap(); + let error_data: APLoginFailed = protobuf::parse_from_bytes(data.as_ref())?; Err(error_data.into()) } - - _ => panic!("Unexpected packet {:?}", cmd), + _ => { + let msg = format!("Received invalid packet: {}", cmd); + Err(io::Error::new(ErrorKind::InvalidData, msg).into()) + } } } diff --git a/core/src/lib.rs b/core/src/lib.rs index 54f83f17..4ebe8581 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -4,8 +4,6 @@ extern crate log; #[macro_use] extern crate cfg_if; -#[macro_use] -extern crate error_chain; use librespot_protocol as protocol; diff --git a/core/src/session.rs b/core/src/session.rs index 858a0b69..9eaff3ed 100644 --- a/core/src/session.rs +++ b/core/src/session.rs @@ -12,6 +12,7 @@ use bytes::Bytes; use futures_core::TryStream; use futures_util::{FutureExt, StreamExt, TryStreamExt}; use once_cell::sync::OnceCell; +use thiserror::Error; use tokio::sync::mpsc; use tokio_stream::wrappers::UnboundedReceiverStream; @@ -21,10 +22,16 @@ use crate::authentication::Credentials; use crate::cache::Cache; use crate::channel::ChannelManager; use crate::config::SessionConfig; -use crate::connection; +use crate::connection::{self, AuthenticationError}; use crate::mercury::MercuryManager; -pub use crate::authentication::{AuthenticationError, AuthenticationErrorKind}; +#[derive(Debug, Error)] +pub enum SessionError { + #[error(transparent)] + AuthenticationError(#[from] AuthenticationError), + #[error("Cannot create session: {0}")] + IoError(#[from] io::Error), +} struct SessionData { country: String, @@ -59,7 +66,7 @@ impl Session { config: SessionConfig, credentials: Credentials, cache: Option, - ) -> Result { + ) -> Result { let ap = apresolve_or_fallback(&config.proxy, &config.ap_port).await; info!("Connecting to AP \"{}\"", ap);