diff --git a/src/accountmanager.rs b/src/accountmanager.rs index 9726454..88c37ab 100644 --- a/src/accountmanager.rs +++ b/src/accountmanager.rs @@ -318,6 +318,12 @@ impl Manifest { self.entries.iter().any(|e| e.account_name.is_empty()) } + fn has_any_uppercase_in_account_names(&self) -> bool { + self.entries + .iter() + .any(|e| e.account_name != e.account_name.to_lowercase()) + } + /// Performs auto-upgrades on the manifest. Returns true if any upgrades were performed. pub fn auto_upgrade(&mut self) -> anyhow::Result { debug!("Performing auto-upgrade..."); @@ -331,6 +337,14 @@ impl Manifest { upgraded = true; } + if self.has_any_uppercase_in_account_names() { + debug!("Lowercasing account names"); + for i in 0..self.entries.len() { + self.entries[i].account_name = self.entries[i].account_name.to_lowercase(); + } + upgraded = true; + } + Ok(upgraded) } } diff --git a/src/cli.rs b/src/cli.rs index 30c2710..c9bd25b 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -116,10 +116,7 @@ pub(crate) struct ArgsCompletions { #[derive(Debug, Clone, Parser)] #[clap(about = "Set up a new account with steamguard-cli")] -pub(crate) struct ArgsSetup { - #[clap(short, long, from_global, help = "Steam username, case-sensitive.")] - pub username: Option, -} +pub(crate) struct ArgsSetup {} #[derive(Debug, Clone, Parser)] #[clap(about = "Import an account with steamguard already set up")] diff --git a/src/main.rs b/src/main.rs index 6148d87..22f874a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -275,7 +275,7 @@ fn do_login_impl( login.captcha_text = tui::prompt_captcha_text(&captcha_gid); } Err(LoginError::NeedEmail) => { - println!("You should have received an email with a code."); + println!("You should have received an email with a code. If you did not, check your spam folder, or abort and try again."); print!("Enter code: "); login.email_code = tui::prompt(); } @@ -349,18 +349,12 @@ fn do_subcmd_completion(args: cli::ArgsCompletions) -> Result<(), anyhow::Error> } fn do_subcmd_setup( - args: cli::ArgsSetup, + _args: cli::ArgsSetup, manifest: &mut accountmanager::Manifest, ) -> anyhow::Result<()> { println!("Log in to the account that you want to link to steamguard-cli"); print!("Username: "); - let username = if args.username.is_some() { - let u = args.username.unwrap(); - println!("{}", u); - u - } else { - tui::prompt() - }; + let username = tui::prompt().to_lowercase(); let account_name = username.clone(); if manifest.account_exists(&username) { bail!( @@ -368,8 +362,10 @@ fn do_subcmd_setup( username ); } + info!("Logging in to {}", username); let session = do_login_raw(username).expect("Failed to log in. Account has not been linked."); + info!("Adding authenticator..."); let mut linker = AccountLinker::new(session); let account: SteamGuardAccount; loop { diff --git a/steamguard/src/accountlinker.rs b/steamguard/src/accountlinker.rs index a7249fe..078adf6 100644 --- a/steamguard/src/accountlinker.rs +++ b/steamguard/src/accountlinker.rs @@ -62,6 +62,9 @@ impl AccountLinker { return Err(AccountLinkError::AuthenticatorPresent); } 2 => { + // If the user has no phone number on their account, it will always return this status code. + // However, this does not mean that this status just means "no phone number". It can also + // be literally anything else, so that's why we return GenericFailure here. return Err(AccountLinkError::GenericFailure); } 1 => { @@ -129,7 +132,7 @@ pub enum AccountLinkError { MustConfirmEmail, #[error("Authenticator is already present.")] AuthenticatorPresent, - #[error("Steam was unable to link the authenticator to the account. No additional information about this error is available. This is a Steam error, not a steamguard-cli error. Try adding a phone number to your Steam account, or try again later.")] + #[error("Steam was unable to link the authenticator to the account. No additional information about this error is available. This is a Steam error, not a steamguard-cli error. Try adding a phone number to your Steam account (which you can do here: https://store.steampowered.com/phone/add), or try again later.")] GenericFailure, #[error(transparent)] Unknown(#[from] anyhow::Error), diff --git a/steamguard/src/steamapi.rs b/steamguard/src/steamapi.rs index a779f41..5d002da 100644 --- a/steamguard/src/steamapi.rs +++ b/steamguard/src/steamapi.rs @@ -353,6 +353,8 @@ impl SteamApiClient { } } + /// Likely removed now + /// /// One of the endpoints that handles phone number things. Can check to see if phone is present on account, and maybe do some other stuff. It's not really super clear. /// /// Host: steamcommunity.com @@ -442,14 +444,26 @@ impl SteamApiClient { /// Provides lots of juicy information, like if the number is a VOIP number. /// Host: store.steampowered.com /// Endpoint: POST /phone/validate + /// Body format: form data + /// Example: + /// ```form + /// sessionID=FOO&phoneNumber=%2B1+1234567890 + /// ``` /// Found on page: https://store.steampowered.com/phone/add - pub fn phone_validate(&self, phone_number: String) -> anyhow::Result { + pub fn phone_validate(&self, phone_number: &String) -> anyhow::Result { let params = hashmap! { - "sessionID" => "", - "phoneNumber" => "", + "sessionID" => self.session.as_ref().unwrap().expose_secret().session_id.as_str(), + "phoneNumber" => phone_number.as_str(), }; - todo!(); + let resp = self + .client + .post("https://store.steampowered.com/phone/validate") + .form(¶ms) + .send()? + .json::()?; + + return Ok(resp); } /// Starts the authenticator linking process. @@ -648,6 +662,8 @@ pub struct AddAuthenticatorResponse { pub secret_1: String, /// Result code pub status: i32, + #[serde(default)] + pub phone_number_hint: Option, } impl AddAuthenticatorResponse { @@ -678,6 +694,16 @@ pub struct FinalizeAddAuthenticatorResponse { pub success: bool, } +#[derive(Debug, Clone, Deserialize)] +#[allow(dead_code)] +pub struct PhoneValidateResponse { + success: bool, + number: String, + is_valid: bool, + is_voip: bool, + is_fixed: bool, +} + #[derive(Debug, Clone, Deserialize)] pub struct RemoveAuthenticatorResponse { pub success: bool,