2023-06-23 19:36:23 +02:00
|
|
|
use std::sync::{Arc, Mutex};
|
|
|
|
|
|
|
|
use log::*;
|
2023-07-02 14:57:13 +02:00
|
|
|
use steamguard::{steamapi::TwoFactorClient, transport::TransportError, RemoveAuthenticatorError};
|
2023-06-23 19:36:23 +02:00
|
|
|
|
|
|
|
use crate::{errors::UserError, tui, AccountManager};
|
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Parser)]
|
|
|
|
#[clap(about = "Remove the authenticator from an account.")]
|
|
|
|
pub struct RemoveCommand;
|
|
|
|
|
2023-07-02 14:57:13 +02:00
|
|
|
impl<T> AccountCommand<T> for RemoveCommand
|
|
|
|
where
|
|
|
|
T: Transport + Clone,
|
|
|
|
{
|
2023-06-23 19:36:23 +02:00
|
|
|
fn execute(
|
|
|
|
&self,
|
2023-07-02 14:57:13 +02:00
|
|
|
transport: T,
|
2023-06-23 19:36:23 +02:00
|
|
|
manager: &mut AccountManager,
|
|
|
|
accounts: Vec<Arc<Mutex<SteamGuardAccount>>>,
|
|
|
|
) -> anyhow::Result<()> {
|
|
|
|
eprintln!(
|
|
|
|
"This will remove the mobile authenticator from {} accounts: {}",
|
|
|
|
accounts.len(),
|
|
|
|
accounts
|
|
|
|
.iter()
|
|
|
|
.map(|a| a.lock().unwrap().account_name.clone())
|
|
|
|
.collect::<Vec<String>>()
|
|
|
|
.join(", ")
|
|
|
|
);
|
|
|
|
|
|
|
|
match tui::prompt_char("Do you want to continue?", "yN") {
|
|
|
|
'y' => {}
|
|
|
|
_ => {
|
|
|
|
info!("Aborting!");
|
|
|
|
return Err(UserError::Aborted.into());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut successful = vec![];
|
|
|
|
for a in accounts {
|
|
|
|
let mut account = a.lock().unwrap();
|
2023-07-02 14:57:13 +02:00
|
|
|
let client = TwoFactorClient::new(transport.clone());
|
2023-06-23 19:36:23 +02:00
|
|
|
|
2023-06-30 16:53:05 +02:00
|
|
|
let mut revocation: Option<String> = None;
|
|
|
|
loop {
|
2023-07-02 14:57:13 +02:00
|
|
|
match account.remove_authenticator(&client, revocation.as_ref()) {
|
2023-06-30 16:53:05 +02:00
|
|
|
Ok(_) => {
|
|
|
|
info!("Removed authenticator from {}", account.account_name);
|
2023-06-23 19:36:23 +02:00
|
|
|
successful.push(account.account_name.clone());
|
2023-06-30 16:53:05 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
Err(RemoveAuthenticatorError::TransportError(TransportError::Unauthorized)) => {
|
|
|
|
error!("Account {} is not logged in", account.account_name);
|
2023-07-02 14:57:13 +02:00
|
|
|
crate::do_login(transport.clone(), &mut account)?;
|
2023-06-30 16:53:05 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
Err(RemoveAuthenticatorError::IncorrectRevocationCode {
|
|
|
|
attempts_remaining,
|
|
|
|
}) => {
|
|
|
|
error!(
|
|
|
|
"Revocation code was incorrect for {} ({} attempts remaining)",
|
|
|
|
account.account_name, attempts_remaining
|
2023-06-23 19:36:23 +02:00
|
|
|
);
|
2023-06-30 16:53:05 +02:00
|
|
|
if attempts_remaining == 0 {
|
|
|
|
error!("No attempts remaining, aborting!");
|
|
|
|
break;
|
2023-06-23 19:36:23 +02:00
|
|
|
}
|
2023-06-30 16:53:05 +02:00
|
|
|
eprint!("Enter the revocation code for {}: ", account.account_name);
|
|
|
|
let code = tui::prompt();
|
|
|
|
revocation = Some(code);
|
|
|
|
}
|
|
|
|
Err(RemoveAuthenticatorError::MissingRevocationCode) => {
|
|
|
|
error!(
|
|
|
|
"Account {} does not have a revocation code",
|
|
|
|
account.account_name
|
|
|
|
);
|
|
|
|
eprint!("Enter the revocation code for {}: ", account.account_name);
|
|
|
|
let code = tui::prompt();
|
|
|
|
revocation = Some(code);
|
|
|
|
}
|
|
|
|
Err(err) => {
|
|
|
|
error!(
|
|
|
|
"Unexpected error when removing authenticator from {}: {}",
|
|
|
|
account.account_name, err
|
|
|
|
);
|
|
|
|
break;
|
2023-06-23 19:36:23 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for account_name in successful {
|
|
|
|
manager.remove_account(account_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
manager.save()?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|