diff --git a/Cargo.lock b/Cargo.lock index ab1ad08..f109ac8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1625,6 +1625,7 @@ dependencies = [ "serde", "serde_json", "standback", + "thiserror", "uuid", ] @@ -1781,6 +1782,26 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" +[[package]] +name = "thiserror" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "thread_local" version = "0.3.4" diff --git a/src/main.rs b/src/main.rs index aba5c58..6e1d17d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,8 +9,8 @@ use std::{ sync::{Arc, Mutex}, }; use steamguard::{ - steamapi, AccountLinker, Confirmation, ConfirmationType, LoginError, SteamGuardAccount, - UserLogin, FinalizeLinkError + steamapi, AccountLinker, Confirmation, ConfirmationType, FinalizeLinkError, LoginError, + SteamGuardAccount, UserLogin, }; use termion::{ event::{Event, Key}, @@ -172,7 +172,7 @@ fn main() { let sms_code = prompt(); let mut tries = 0; loop { - match linker.finalize(&mut account, sms_code) { + match linker.finalize(&mut account, sms_code.clone()) { Ok(_) => break, Err(FinalizeLinkError::WantMore) => { debug!("steam wants more 2fa codes (tries: {})", tries); @@ -182,7 +182,7 @@ fn main() { break; } continue; - }, + } Err(err) => { error!("Failed to finalize: {}", err); break; diff --git a/steamguard/Cargo.toml b/steamguard/Cargo.toml index 466dbfe..f7e91d6 100644 --- a/steamguard/Cargo.toml +++ b/steamguard/Cargo.toml @@ -23,3 +23,4 @@ uuid = { version = "0.8", features = ["v4"] } log = "0.4.14" scraper = "0.12.0" maplit = "1.0.2" +thiserror = "1.0.26" diff --git a/steamguard/src/accountlinker.rs b/steamguard/src/accountlinker.rs index 8bb1061..69174d4 100644 --- a/steamguard/src/accountlinker.rs +++ b/steamguard/src/accountlinker.rs @@ -1,9 +1,9 @@ use crate::{ - steamapi::{AddAuthenticatorResponse, Session, SteamApiClient}, + steamapi::{AddAuthenticatorResponse, Session, SteamApiClient, FinalizeAddAuthenticatorResponse}, SteamGuardAccount, }; use log::*; -use std::error::Error; +use thiserror::Error; use std::fmt::Display; #[derive(Debug)] @@ -30,28 +30,27 @@ impl AccountLinker { }; } - pub fn link(&mut self) -> anyhow::Result { - ensure!(!self.finalized); + pub fn link(&mut self) -> anyhow::Result { let has_phone = self.client.has_phone()?; if has_phone && !self.phone_number.is_empty() { - bail!(AccountLinkError::MustRemovePhoneNumber); + return Err(AccountLinkError::MustRemovePhoneNumber); } if !has_phone && self.phone_number.is_empty() { - bail!(AccountLinkError::MustProvidePhoneNumber); + return Err(AccountLinkError::MustProvidePhoneNumber); } if !has_phone { if self.sent_confirmation_email { if !self.client.check_email_confirmation()? { - bail!("Failed email confirmation check"); + return Err(anyhow!("Failed email confirmation check"))?; } } else if !self.client.add_phone_number(self.phone_number.clone())? { - bail!("Failed to add phone number"); + return Err(anyhow!("Failed to add phone number"))?; } else { self.sent_confirmation_email = true; - bail!(AccountLinkError::MustConfirmEmail); + return Err(AccountLinkError::MustConfirmEmail); } } @@ -60,7 +59,7 @@ impl AccountLinker { match resp.status { 29 => { - bail!(AccountLinkError::AuthenticatorPresent); + return Err(AccountLinkError::AuthenticatorPresent); } 1 => { let mut account = resp.to_steam_guard_account(); @@ -69,7 +68,7 @@ impl AccountLinker { return Ok(account); } status => { - bail!("Unknown add authenticator status code: {}", status); + return Err(anyhow!("Unknown add authenticator status code: {}", status))?; } } } @@ -79,30 +78,27 @@ impl AccountLinker { &mut self, account: &mut SteamGuardAccount, sms_code: String, - ) -> anyhow::Result<()> { - ensure!(!account.fully_enrolled); - ensure!(!self.finalized); - + ) -> anyhow::Result<(), FinalizeLinkError> { let time = crate::steamapi::get_server_time(); let code = account.generate_code(time); - let resp = self + let resp: FinalizeAddAuthenticatorResponse = self .client .finalize_authenticator(sms_code.clone(), code, time)?; info!("finalize response status: {}", resp.status); match resp.status { 89 => { - bail!(FinalizeLinkError::BadSmsCode); + return Err(FinalizeLinkError::BadSmsCode); } _ => {} } if !resp.success { - bail!("Failed to finalize authenticator. Status: {}", resp.status); + return Err(FinalizeLinkError::Failure { status: resp.status })?; } if resp.want_more { - bail!(FinalizeLinkError::WantMore); + return Err(FinalizeLinkError::WantMore); } self.finalized = true; @@ -115,38 +111,35 @@ fn generate_device_id() -> String { return format!("android:{}", uuid::Uuid::new_v4().to_string()); } -#[derive(Debug)] +#[derive(Error, Debug)] pub enum AccountLinkError { /// No phone number on the account + #[error("A phone number is needed, but not already present on the account.")] MustProvidePhoneNumber, /// A phone number is already on the account + #[error("A phone number was provided, but one is already present on the account.")] MustRemovePhoneNumber, /// User need to click link from confirmation email + #[error("An email has been sent to the user's email, click the link in that email.")] MustConfirmEmail, /// Must provide an SMS code + #[error("Awaiting finalization")] AwaitingFinalization, + #[error("Authenticator is already present.")] AuthenticatorPresent, + #[error(transparent)] + Unknown(#[from] anyhow::Error), } -impl Display for AccountLinkError { - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { - write!(f, "{:?}", self) - } -} - -impl Error for AccountLinkError {} - -#[derive(Debug)] +#[derive(Error, Debug)] pub enum FinalizeLinkError { + #[error("Provided SMS code was incorrect.")] BadSmsCode, /// Steam wants more 2fa codes to verify that we can generate valid codes. Call finalize again. + #[error("Steam wants more 2fa codes for verification.")] WantMore, + #[error("Finalization was not successful. Status code {status:?}")] + Failure{ status: i32 }, + #[error(transparent)] + Unknown(#[from] anyhow::Error), } - -impl Display for FinalizeLinkError { - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { - write!(f, "{:?}", self) - } -} - -impl Error for FinalizeLinkError {} diff --git a/steamguard/src/lib.rs b/steamguard/src/lib.rs index 8ea4f83..b240f3e 100644 --- a/steamguard/src/lib.rs +++ b/steamguard/src/lib.rs @@ -1,4 +1,4 @@ -pub use accountlinker::{AccountLinkError, AccountLinker}; +pub use accountlinker::{AccountLinkError, AccountLinker, FinalizeLinkError}; use anyhow::Result; pub use confirmation::{Confirmation, ConfirmationType}; use hmacsha1::hmac_sha1; @@ -11,7 +11,6 @@ use reqwest::{ use scraper::{Html, Selector}; use serde::{Deserialize, Serialize}; use std::{collections::HashMap, convert::TryInto}; -pub use steamapi::AddAuthenticatorResponse; pub use userlogin::{LoginError, UserLogin}; #[macro_use] extern crate lazy_static;