2023-06-23 19:36:23 +02:00
use std ::sync ::{ Arc , Mutex } ;
use clap ::{ clap_derive ::ArgEnum , Parser } ;
use clap_complete ::Shell ;
2023-06-26 02:23:26 +02:00
use secrecy ::SecretString ;
2023-06-23 19:36:23 +02:00
use std ::str ::FromStr ;
2023-07-02 14:57:13 +02:00
use steamguard ::{ transport ::Transport , SteamGuardAccount } ;
2023-06-23 19:36:23 +02:00
use crate ::AccountManager ;
pub mod code ;
pub mod completions ;
2024-03-04 12:56:48 +01:00
pub mod confirm ;
2023-06-23 19:36:23 +02:00
pub mod debug ;
pub mod decrypt ;
pub mod encrypt ;
pub mod import ;
#[ cfg(feature = " qr " ) ]
pub mod qr ;
2023-06-24 19:45:03 +02:00
pub mod qr_login ;
2023-06-23 19:36:23 +02:00
pub mod remove ;
pub mod setup ;
pub use code ::CodeCommand ;
pub use completions ::CompletionsCommand ;
2024-03-04 12:56:48 +01:00
pub use confirm ::ConfirmCommand ;
2023-06-23 19:36:23 +02:00
pub use debug ::DebugCommand ;
pub use decrypt ::DecryptCommand ;
pub use encrypt ::EncryptCommand ;
pub use import ::ImportCommand ;
#[ cfg(feature = " qr " ) ]
pub use qr ::QrCommand ;
2023-06-24 19:45:03 +02:00
pub use qr_login ::QrLoginCommand ;
2023-06-23 19:36:23 +02:00
pub use remove ::RemoveCommand ;
pub use setup ::SetupCommand ;
/// A command that does not operate on the manifest or individual accounts.
pub ( crate ) trait ConstCommand {
fn execute ( & self ) -> anyhow ::Result < ( ) > ;
}
/// A command that operates the manifest as a whole
2023-07-02 14:57:13 +02:00
pub ( crate ) trait ManifestCommand < T >
where
T : Transport ,
{
2023-09-28 23:52:42 +02:00
fn execute (
& self ,
transport : T ,
manager : & mut AccountManager ,
args : & GlobalArgs ,
) -> anyhow ::Result < ( ) > ;
2023-06-23 19:36:23 +02:00
}
/// A command that operates on individual accounts.
2023-07-02 14:57:13 +02:00
pub ( crate ) trait AccountCommand < T >
where
T : Transport ,
{
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 > > > ,
2023-09-28 23:52:42 +02:00
args : & GlobalArgs ,
2023-06-23 19:36:23 +02:00
) -> anyhow ::Result < ( ) > ;
}
2023-07-02 14:57:13 +02:00
pub ( crate ) enum CommandType < T >
where
T : Transport ,
{
2023-06-23 19:36:23 +02:00
Const ( Box < dyn ConstCommand > ) ,
2023-07-02 14:57:13 +02:00
Manifest ( Box < dyn ManifestCommand < T > > ) ,
Account ( Box < dyn AccountCommand < T > > ) ,
2023-06-23 19:36:23 +02:00
}
#[ derive(Debug, Clone, Parser) ]
#[ clap(name= " steamguard-cli " , bin_name= " steamguard " , author, version, about = " Generate Steam 2FA codes and confirm Steam trades from the command line. " , long_about = None) ]
pub ( crate ) struct Args {
#[ clap(flatten) ]
pub global : GlobalArgs ,
#[ clap(subcommand) ]
pub sub : Option < Subcommands > ,
#[ clap(flatten) ]
pub code : CodeCommand ,
}
#[ derive(Debug, Clone, Parser) ]
pub ( crate ) struct GlobalArgs {
#[ clap(
short ,
long ,
conflicts_with = " all " ,
help = " Steam username, case-sensitive. " ,
long_help = " Select the account you want by steam username. Case-sensitive. By default, the first account in the manifest is selected. "
) ]
pub username : Option < String > ,
2023-09-28 23:52:42 +02:00
#[ clap(
long ,
conflicts_with = " all " ,
help = " Steam account password. You really shouldn't use this if you can avoid it. " ,
env = " STEAMGUARD_CLI_STEAM_PASSWORD "
) ]
pub password : Option < SecretString > ,
2023-06-23 19:36:23 +02:00
#[ clap(
short ,
long ,
conflicts_with = " username " ,
help = " Select all accounts in the manifest. "
) ]
pub all : bool ,
/// The path to the maFiles directory.
#[ clap(
short ,
long ,
2023-07-01 01:44:57 +02:00
env = " STEAMGUARD_CLI_MAFILES " ,
2023-06-23 19:36:23 +02:00
help = " Specify which folder your maFiles are in. This should be a path to a folder that contains manifest.json. Default: ~/.config/steamguard-cli/maFiles "
) ]
pub mafiles_path : Option < String > ,
#[ clap(
short ,
long ,
env = " STEAMGUARD_CLI_PASSKEY " ,
help = " Specify your encryption passkey. "
) ]
2023-06-26 02:23:26 +02:00
pub passkey : Option < SecretString > ,
2023-06-23 19:36:23 +02:00
#[ clap(short, long, arg_enum, default_value_t=Verbosity::Info, help = " Set the log level. Be warned, trace is capable of printing sensitive data. " ) ]
pub verbosity : Verbosity ,
2023-06-25 15:24:53 +02:00
#[ cfg(feature = " updater " ) ]
#[ clap(
long ,
help = " Disable checking for updates. " ,
long_help = " Disable checking for updates. By default, steamguard-cli will check for updates every now and then. This can be disabled with this flag. "
) ]
pub no_update_check : bool ,
2023-07-02 14:57:13 +02:00
#[ clap(
long ,
env = " HTTP_PROXY " ,
help = " Use a proxy for HTTP requests. " ,
long_help = " Use a proxy for HTTP requests. This is useful if you are behind a firewall and need to use a proxy to access the internet. "
) ]
pub http_proxy : Option < String > ,
2023-07-05 12:49:17 +02:00
#[ clap(
long ,
help = " Credentials to use for proxy authentication in the format username:password. "
) ]
pub proxy_credentials : Option < String > ,
2023-07-05 12:58:08 +02:00
#[ clap(
long ,
help = " Accept invalid TLS certificates. " ,
long_help = " Accept invalid TLS certificates. Be warned, this is insecure and enables man-in-the-middle attacks. "
) ]
pub danger_accept_invalid_certs : bool ,
2023-06-23 19:36:23 +02:00
}
#[ derive(Debug, Clone, Parser) ]
pub ( crate ) enum Subcommands {
Debug ( DebugCommand ) ,
Completion ( CompletionsCommand ) ,
Setup ( SetupCommand ) ,
Import ( ImportCommand ) ,
2024-03-04 12:56:48 +01:00
#[ clap(alias = " trade " ) ]
Confirm ( ConfirmCommand ) ,
2023-06-23 19:36:23 +02:00
Remove ( RemoveCommand ) ,
Encrypt ( EncryptCommand ) ,
Decrypt ( DecryptCommand ) ,
Code ( CodeCommand ) ,
#[ cfg(feature = " qr " ) ]
Qr ( QrCommand ) ,
2023-06-24 19:45:03 +02:00
QrLogin ( QrLoginCommand ) ,
2023-06-23 19:36:23 +02:00
}
#[ derive(Debug, Clone, Copy, ArgEnum) ]
pub ( crate ) enum Verbosity {
Error = 0 ,
Warn = 1 ,
Info = 2 ,
Debug = 3 ,
Trace = 4 ,
}
impl std ::fmt ::Display for Verbosity {
fn fmt ( & self , f : & mut std ::fmt ::Formatter < '_ > ) -> std ::fmt ::Result {
f . write_fmt ( format_args! (
" {} " ,
match self {
Verbosity ::Error = > " error " ,
Verbosity ::Warn = > " warn " ,
Verbosity ::Info = > " info " ,
Verbosity ::Debug = > " debug " ,
Verbosity ::Trace = > " trace " ,
}
) )
}
}
impl FromStr for Verbosity {
type Err = anyhow ::Error ;
fn from_str ( s : & str ) -> Result < Self , Self ::Err > {
match s {
" error " = > Ok ( Verbosity ::Error ) ,
" warn " = > Ok ( Verbosity ::Warn ) ,
" info " = > Ok ( Verbosity ::Info ) ,
" debug " = > Ok ( Verbosity ::Debug ) ,
" trace " = > Ok ( Verbosity ::Trace ) ,
_ = > Err ( anyhow! ( " Invalid verbosity level: {} " , s ) ) ,
}
}
}
// HACK: the derive API doesn't support default subcommands, so we are going to make it so that it'll be easier to switch over when it's implemented.
// See: https://github.com/clap-rs/clap/issues/3857
impl From < Args > for CodeCommand {
fn from ( args : Args ) -> Self {
args . code
}
}