2016-08-23 16:38:53 +02:00
using Newtonsoft.Json ;
2016-08-21 20:17:04 +02:00
using SteamAuth ;
2016-08-21 17:29:27 +02:00
using System ;
2016-08-21 20:17:04 +02:00
using System.Collections.Generic ;
2016-08-21 19:03:43 +02:00
using System.IO ;
2016-08-21 20:17:04 +02:00
using System.Linq ;
2016-08-24 19:01:49 +02:00
using System.Reflection ;
2016-08-28 00:33:01 +02:00
using System.Runtime.InteropServices ;
2016-08-21 20:17:04 +02:00
using System.Text ;
using System.Threading.Tasks ;
2016-08-21 17:29:27 +02:00
2016-08-28 00:42:43 +02:00
namespace SteamGuard
2016-08-21 17:29:27 +02:00
{
2016-08-28 00:42:43 +02:00
public static class Program
2016-08-25 23:23:16 +02:00
{
2016-08-28 00:42:43 +02:00
public const string defaultSteamGuardPath = "~/maFiles" ;
public static string SteamGuardPath { get ; set ; } = defaultSteamGuardPath ;
public static Manifest Manifest { get ; set ; }
public static SteamGuardAccount [ ] SteamGuardAccounts { get ; set ; }
public static bool Verbose { get ; set ; } = false ;
/// <summary>
/// The main entry point for the application
/// </summary>
[STAThread]
public static void Main ( string [ ] args )
2016-08-26 02:11:56 +02:00
{
2016-08-28 00:42:43 +02:00
string action = "" ;
string user = "" ;
2017-05-23 00:07:12 +02:00
string passkey = "" ;
2016-08-28 00:42:43 +02:00
// Parse cli arguments
for ( int i = 0 ; i < args . Length ; i + + )
2016-08-26 03:15:47 +02:00
{
2016-08-28 00:42:43 +02:00
if ( args [ i ] . StartsWith ( "-" ) )
2016-08-26 03:15:47 +02:00
{
2017-05-23 00:07:12 +02:00
// TODO: there's gotta be some framework or tool or something for this
2016-08-28 00:42:43 +02:00
if ( args [ i ] = = "-v" | | args [ i ] = = "--verbose" )
{
Verbose = true ;
}
else if ( args [ i ] = = "-m" | | args [ i ] = = "--mafiles-path" )
{
i + + ;
if ( i < args . Length )
2017-05-22 23:56:54 +02:00
{
2016-08-28 00:42:43 +02:00
SteamGuardPath = args [ i ] ;
2017-05-22 23:56:54 +02:00
}
2016-08-28 00:42:43 +02:00
else
{
Console . WriteLine ( $"Expected path after {args[i-1]}" ) ;
return ;
}
}
2017-05-23 00:07:12 +02:00
else if ( args [ i ] = = "-p" | | args [ i ] = = "--passkey" )
{
i + + ;
if ( i < args . Length )
{
passkey = args [ i ] ;
}
else
{
Console . WriteLine ( $"Expected encryption passkey after {args[i-1]}" ) ;
return ;
}
}
2016-08-28 00:42:43 +02:00
else if ( args [ i ] = = "--help" | | args [ i ] = = "-h" )
{
ShowHelp ( ) ;
return ;
}
2016-08-26 03:15:47 +02:00
}
2016-08-28 00:42:43 +02:00
else // Parse as action or username
2016-08-26 03:15:47 +02:00
{
2016-08-28 00:42:43 +02:00
if ( string . IsNullOrEmpty ( action ) )
{
if ( args [ i ] = = "add" | | args [ i ] = = "setup" )
{
action = "setup" ;
}
else if ( args [ i ] = = "trade" )
{
action = "trade" ;
}
else if ( args [ i ] = = "encrypt" )
{
action = "encrypt" ;
}
else if ( args [ i ] = = "decrypt" )
{
action = "decrypt" ;
}
else if ( args [ i ] = = "remove" )
{
action = "remove" ;
}
else if ( args [ i ] = = "2fa" | | args [ i ] = = "code" | | args [ i ] = = "generate-code" )
{
action = "generate-code" ;
}
2017-09-27 06:38:56 +02:00
else if ( args [ i ] = = "accept-all" )
{
action = "accept-all" ;
}
2016-08-28 00:42:43 +02:00
else if ( string . IsNullOrEmpty ( user ) )
2017-05-22 23:56:54 +02:00
{
2016-08-28 00:42:43 +02:00
user = args [ i ] ;
2017-05-22 23:56:54 +02:00
}
2016-08-28 00:42:43 +02:00
}
else if ( string . IsNullOrEmpty ( user ) )
2017-05-22 23:56:54 +02:00
{
2016-08-28 00:42:43 +02:00
user = args [ i ] ;
2017-05-22 23:56:54 +02:00
}
2016-08-26 03:15:47 +02:00
}
}
2016-08-28 00:37:25 +02:00
2016-08-28 00:42:43 +02:00
if ( string . IsNullOrEmpty ( action ) )
2017-05-22 23:56:54 +02:00
{
2016-08-28 00:42:43 +02:00
action = "generate-code" ;
2017-05-22 23:56:54 +02:00
}
2016-08-28 00:37:25 +02:00
2016-08-28 00:42:43 +02:00
// Do some configuring
SteamGuardPath = SteamGuardPath . Replace ( "~" , Environment . GetEnvironmentVariable ( "HOME" ) ) ;
if ( ! Directory . Exists ( SteamGuardPath ) )
{
if ( SteamGuardPath = = defaultSteamGuardPath . Replace ( "~" , Environment . GetEnvironmentVariable ( "HOME" ) ) )
{
2018-03-27 23:48:22 +02:00
Utils . Verbose ( "warn: {0} does not exist, creating..." , SteamGuardPath ) ;
2016-08-28 00:42:43 +02:00
Directory . CreateDirectory ( SteamGuardPath ) ;
}
else
{
Console . WriteLine ( "error: {0} does not exist." , SteamGuardPath ) ;
return ;
}
}
2016-08-28 00:37:25 +02:00
2018-03-28 01:16:15 +02:00
Utils . Verbose ( $"Action: {action}" ) ;
if ( user . Length > 0 )
2017-05-22 23:56:54 +02:00
{
2018-03-28 01:16:15 +02:00
Utils . Verbose ( $"User: {user}" ) ;
2017-05-22 23:56:54 +02:00
}
2018-03-28 01:16:15 +02:00
Utils . Verbose ( $"Passkey: {passkey.Length > 0 ? " [ specified ] " : " [ not specified ] " }" ) ;
Utils . Verbose ( $"maFiles path: {SteamGuardPath}" ) ;
2016-08-28 00:37:25 +02:00
2016-08-28 00:42:43 +02:00
// Perform desired action
switch ( action )
2016-08-28 00:37:25 +02:00
{
2016-08-28 00:42:43 +02:00
case "generate-code" :
2017-05-23 00:07:12 +02:00
GenerateCode ( user , passkey ) ;
2016-08-28 00:37:25 +02:00
break ;
2016-08-28 00:42:43 +02:00
case "encrypt" : // Can also be used to change passkey
2017-05-23 00:07:12 +02:00
Console . WriteLine ( Encrypt ( passkey ) ) ;
2016-08-28 00:37:25 +02:00
break ;
2016-08-28 00:42:43 +02:00
case "decrypt" :
2017-05-23 00:07:12 +02:00
Console . WriteLine ( Decrypt ( passkey ) ) ;
2016-08-28 00:42:43 +02:00
break ;
case "setup" :
2017-05-23 00:07:12 +02:00
Setup ( user , passkey ) ;
2016-08-28 00:42:43 +02:00
break ;
case "trade" :
2017-05-23 00:07:12 +02:00
Trade ( user , passkey ) ;
2016-08-28 00:42:43 +02:00
break ;
case "accept-all" :
2017-05-23 00:07:12 +02:00
AcceptAllTrades ( user , passkey ) ;
2016-08-28 00:37:25 +02:00
break ;
default :
2016-08-28 00:42:43 +02:00
Console . WriteLine ( "error: Unknown action: {0}" , action ) ;
2016-08-28 00:37:25 +02:00
return ;
}
2016-08-28 00:42:43 +02:00
}
2016-08-28 00:37:25 +02:00
2016-08-28 00:42:43 +02:00
static void ShowHelp ( )
2016-08-28 00:37:25 +02:00
{
2016-08-28 00:42:43 +02:00
var descPadding = 26 ;
var descWidth = Console . BufferWidth - descPadding ;
if ( descWidth < 20 )
descWidth = 20 ;
var flags = new Dictionary < string , string >
{
{ "-h, --help" , "Display this help message." } ,
{ "-v, --verbose" , "Display some extra information when the program is running." } ,
2016-08-28 01:02:00 +02:00
{ "-m, --mafiles-path" , "Specify which folder your maFiles are in. Ex: ~/maFiles" } ,
2017-05-23 00:13:07 +02:00
{ "-p, --passkey" , "Specify your encryption passkey." } ,
2016-08-28 00:42:43 +02:00
} ;
var actions = new Dictionary < string , string >
{
2020-05-06 15:02:41 +02:00
{ "code" , "Generate a Steam Guard code for the specified user (if any) and exit. (default)" } ,
2016-08-28 00:42:43 +02:00
{ "encrypt" , "Encrypt your maFiles or change your encryption passkey." } ,
{ "decrypt" , "Remove encryption from your maFiles." } ,
{ "add" , "Set up Steam Guard for 2 factor authentication." } ,
2017-05-14 21:52:21 +02:00
{ "trade" , "Opens an interactive prompt to handle trade confirmations." } ,
{ "accept-all" , "Accepts all trade confirmations." }
2016-08-28 00:42:43 +02:00
} ;
Console . WriteLine ( $"steamguard-cli - v{Assembly.GetExecutingAssembly().GetName().Version}" ) ;
2017-05-23 00:29:57 +02:00
Console . WriteLine ( "usage: steamguard ACTION [STEAM USERNAME] [OPTIONS]..." ) ;
2016-08-28 00:42:43 +02:00
Console . WriteLine ( ) ;
foreach ( var flag in flags )
{
// word wrap the descriptions, if needed
var desc = flag . Value ;
if ( desc . Length > descWidth )
{
var sb = new StringBuilder ( ) ;
for ( int i = 0 ; i < desc . Length ; i + = descWidth )
{
if ( i > 0 )
sb . Append ( "" . PadLeft ( ( flag . Key . StartsWith ( "--" ) ? 5 : 2 ) + descPadding ) ) ;
sb . AppendLine ( desc . Substring ( i , i + descWidth > desc . Length ? desc . Length - i : descWidth ) . Trim ( ) ) ;
}
desc = sb . ToString ( ) . TrimEnd ( '\n' ) ;
}
Console . WriteLine ( $"{(flag.Key.StartsWith(" - - ") ? " " : " " )}{flag.Key.PadRight(descPadding)}{desc}" ) ;
}
Console . WriteLine ( ) ;
Console . WriteLine ( "Actions:" ) ;
foreach ( var action in actions )
{
// word wrap the descriptions, if needed
var desc = action . Value ;
if ( desc . Length > descWidth )
{
var sb = new StringBuilder ( ) ;
for ( int i = 0 ; i < desc . Length ; i + = descWidth )
{
if ( i > 0 )
sb . Append ( "" . PadLeft ( descPadding + 2 ) ) ;
sb . AppendLine ( desc . Substring ( i , i + descWidth > desc . Length ? desc . Length - i : descWidth ) . Trim ( ) ) ;
}
desc = sb . ToString ( ) . TrimEnd ( '\n' ) ;
}
Console . WriteLine ( $" {action.Key.PadRight(descPadding)}{desc}" ) ;
}
2016-08-28 00:37:25 +02:00
}
2016-08-28 00:42:43 +02:00
2017-05-23 00:02:28 +02:00
static void GenerateCode ( string user = "" , string passkey = "" )
2016-08-28 00:37:25 +02:00
{
2018-03-27 23:48:22 +02:00
Utils . Verbose ( "Aligning time..." ) ;
2016-08-28 00:42:43 +02:00
TimeAligner . AlignTime ( ) ;
2018-03-27 23:48:22 +02:00
Utils . Verbose ( "Opening manifest..." ) ;
2016-08-28 00:42:43 +02:00
Manifest = Manifest . GetManifest ( true ) ;
2018-03-27 23:48:22 +02:00
Utils . Verbose ( "Reading accounts from manifest..." ) ;
2016-08-28 00:42:43 +02:00
if ( Manifest . Encrypted )
{
2017-05-23 00:02:28 +02:00
if ( string . IsNullOrEmpty ( passkey ) )
{
passkey = Manifest . PromptForPassKey ( ) ;
}
2016-08-28 00:42:43 +02:00
SteamGuardAccounts = Manifest . GetAllAccounts ( passkey ) ;
}
else
{
SteamGuardAccounts = Manifest . GetAllAccounts ( ) ;
}
if ( SteamGuardAccounts . Length = = 0 )
{
Console . WriteLine ( "error: No accounts read." ) ;
return ;
}
2018-03-27 23:48:22 +02:00
Utils . Verbose ( "Selecting account..." ) ;
2016-08-28 00:42:43 +02:00
string code = "" ;
for ( int i = 0 ; i < SteamGuardAccounts . Length ; i + + )
{
SteamGuardAccount account = SteamGuardAccounts [ i ] ;
if ( user ! = "" )
{
if ( account . AccountName . ToLower ( ) = = user . ToLower ( ) )
{
2018-03-28 01:16:15 +02:00
Utils . Verbose ( " Generating code for { 0 } . . . ", account.AccountName);
2016-08-28 00:42:43 +02:00
code = account . GenerateSteamGuardCode ( ) ;
break ;
}
}
else
{
2018-03-28 01:16:15 +02:00
Utils . Verbose ( "Generating code for {0}..." , account . AccountName ) ;
2016-08-28 00:42:43 +02:00
code = account . GenerateSteamGuardCode ( ) ;
break ;
}
}
if ( code ! = "" )
Console . WriteLine ( code ) ;
else
Console . WriteLine ( "error: No Steam accounts found in {0}" , SteamGuardAccounts ) ;
2016-08-28 00:37:25 +02:00
}
2017-05-23 00:02:28 +02:00
static bool Encrypt ( string passkey = "" )
2016-08-28 00:37:25 +02:00
{
2017-05-23 00:02:28 +02:00
// NOTE: in this context, `passkey` refers to the old passkey, if there was one
2018-03-27 23:48:22 +02:00
Utils . Verbose ( "Opening manifest..." ) ;
2016-08-28 00:42:43 +02:00
Manifest = Manifest . GetManifest ( true ) ;
2018-03-27 23:48:22 +02:00
Utils . Verbose ( "Reading accounts from manifest..." ) ;
2016-08-28 00:42:43 +02:00
if ( Manifest . Encrypted )
{
2017-05-23 00:02:28 +02:00
if ( string . IsNullOrEmpty ( passkey ) )
{
passkey = Manifest . PromptForPassKey ( ) ;
}
2016-08-28 00:42:43 +02:00
SteamGuardAccounts = Manifest . GetAllAccounts ( passkey ) ;
}
else
{
SteamGuardAccounts = Manifest . GetAllAccounts ( ) ;
}
string newPassKey = Manifest . PromptSetupPassKey ( ) ;
for ( int i = 0 ; i < SteamGuardAccounts . Length ; i + + )
{
var account = SteamGuardAccounts [ i ] ;
var salt = Manifest . GetRandomSalt ( ) ;
var iv = Manifest . GetInitializationVector ( ) ;
bool success = Manifest . SaveAccount ( account , true , newPassKey , salt , iv ) ;
2018-03-27 23:48:22 +02:00
Utils . Verbose ( "Encrypted {0}: {1}" , account . AccountName , success ) ;
2016-08-28 00:42:43 +02:00
if ( ! success ) return false ;
}
return true ;
2016-08-28 00:37:25 +02:00
}
2017-05-23 00:02:28 +02:00
static bool Decrypt ( string passkey = "" )
2016-08-28 00:42:43 +02:00
{
2018-03-27 23:48:22 +02:00
Utils . Verbose ( "Opening manifest..." ) ;
2016-08-28 00:42:43 +02:00
Manifest = Manifest . GetManifest ( true ) ;
2018-03-27 23:48:22 +02:00
Utils . Verbose ( "Reading accounts from manifest..." ) ;
2016-08-28 00:42:43 +02:00
if ( Manifest . Encrypted )
{
2017-05-23 00:02:28 +02:00
if ( string . IsNullOrEmpty ( passkey ) )
{
passkey = Manifest . PromptForPassKey ( ) ;
}
2016-08-28 00:42:43 +02:00
SteamGuardAccounts = Manifest . GetAllAccounts ( passkey ) ;
}
else
{
2018-03-27 23:48:22 +02:00
Utils . Verbose ( "Decryption not required." ) ;
2016-08-28 00:42:43 +02:00
return true ;
}
for ( int i = 0 ; i < SteamGuardAccounts . Length ; i + + )
{
var account = SteamGuardAccounts [ i ] ;
bool success = Manifest . SaveAccount ( account , false ) ;
2018-03-27 23:48:22 +02:00
Utils . Verbose ( "Decrypted {0}: {1}" , account . AccountName , success ) ;
2016-08-28 00:42:43 +02:00
if ( ! success ) return false ;
}
return true ;
}
2016-08-28 00:37:25 +02:00
2017-05-23 00:02:28 +02:00
static void Setup ( string username = "" , string passkey = "" )
2016-08-28 00:37:25 +02:00
{
2018-03-27 23:48:22 +02:00
Utils . Verbose ( "Opening manifest..." ) ;
2016-08-28 00:42:43 +02:00
Manifest = Manifest . GetManifest ( true ) ;
if ( string . IsNullOrWhiteSpace ( username ) )
{
Console . Write ( "Username: " ) ;
username = Console . ReadLine ( ) ;
}
Console . Write ( "Password: " ) ;
2017-05-22 23:45:49 +02:00
var password = Utils . ReadLineSecure ( ) ;
2016-08-28 00:42:43 +02:00
UserLogin login = new UserLogin ( username , password ) ;
2020-10-12 18:38:22 +02:00
LoginResult loginResult ;
do
2017-01-19 20:11:00 +01:00
{
Console . Write ( $"Logging in {username}... " ) ;
2020-10-12 18:38:22 +02:00
loginResult = login . DoLogin ( ) ;
2017-01-19 20:11:00 +01:00
Console . WriteLine ( loginResult ) ;
2020-10-12 18:38:22 +02:00
switch ( loginResult )
2017-01-19 20:11:00 +01:00
{
2020-10-12 18:38:22 +02:00
case LoginResult . NeedEmail :
Console . Write ( "Email code: " ) ;
login . EmailCode = Console . ReadLine ( ) ;
break ;
case LoginResult . Need2FA :
Console . Write ( "2FA code: " ) ;
login . TwoFactorCode = Console . ReadLine ( ) ;
break ;
case LoginResult . NeedCaptcha :
Console . WriteLine ( $"Please open: https://steamcommunity.com/public/captcha.php?gid={login.CaptchaGID}" ) ;
Console . Write ( "Captcha text: " ) ;
login . CaptchaText = Console . ReadLine ( ) ;
break ;
case LoginResult . BadCredentials :
Console . WriteLine ( "error: Bad Credentials" ) ;
return ;
case LoginResult . TooManyFailedLogins :
Console . WriteLine ( "error: Too many failed logins. Wait a bit before trying again." ) ;
return ;
case LoginResult . LoginOkay :
break ;
default :
Console . WriteLine ( $"Unknown login result: {loginResult}" ) ;
break ;
2017-01-19 20:11:00 +01:00
}
2020-10-12 18:38:22 +02:00
} while ( loginResult ! = LoginResult . LoginOkay ) ;
2016-08-28 00:37:25 +02:00
2016-08-28 00:42:43 +02:00
AuthenticatorLinker linker = new AuthenticatorLinker ( login . Session ) ;
AuthenticatorLinker . LinkResult linkResult = AuthenticatorLinker . LinkResult . GeneralFailure ;
2016-08-28 00:37:25 +02:00
2016-08-28 00:42:43 +02:00
do
2016-08-28 00:37:25 +02:00
{
2016-08-28 00:42:43 +02:00
linkResult = linker . AddAuthenticator ( ) ;
Console . WriteLine ( $"Link result: {linkResult}" ) ;
switch ( linkResult )
{
case AuthenticatorLinker . LinkResult . MustProvidePhoneNumber :
var phonenumber = "" ;
do
{
Console . WriteLine ( "Enter your mobile phone number in the following format: +{cC} phoneNumber. EG, +1 123-456-7890" ) ;
phonenumber = Console . ReadLine ( ) ;
phonenumber = FilterPhoneNumber ( phonenumber ) ;
linker . PhoneNumber = phonenumber ;
} while ( ! PhoneNumberOkay ( phonenumber ) ) ;
break ;
2020-10-12 19:12:11 +02:00
case AuthenticatorLinker . LinkResult . MustConfirmEmail :
Console . WriteLine ( "Check your email. Before continuing, click the link in the email to confirm your phone number. Press enter to continue..." ) ;
Console . ReadLine ( ) ;
break ;
2016-08-28 00:42:43 +02:00
case AuthenticatorLinker . LinkResult . MustRemovePhoneNumber :
linker . PhoneNumber = null ;
break ;
case AuthenticatorLinker . LinkResult . AwaitingFinalization :
break ;
case AuthenticatorLinker . LinkResult . GeneralFailure :
Console . WriteLine ( "error: Unable to add your phone number. Steam returned GeneralFailure" ) ;
return ;
case AuthenticatorLinker . LinkResult . AuthenticatorPresent :
2017-01-19 20:11:28 +01:00
Console . WriteLine ( "An authenticator is already present." ) ;
Console . WriteLine ( "If you have the revocation code (Rxxxxx), this program can remove it for you." ) ;
Console . Write ( "Would you like to remove the current authenticator using your revocation code? (y/n) " ) ;
var answer = Console . ReadLine ( ) ;
if ( answer ! = "y" )
continue ;
Console . Write ( "Revocation code (Rxxxxx): " ) ;
var revocationCode = Console . ReadLine ( ) ;
var account = new SteamGuardAccount ( ) ;
account . Session = login . Session ;
account . RevocationCode = revocationCode ;
if ( account . DeactivateAuthenticator ( ) )
Console . WriteLine ( "Successfully deactivated the current authenticator." ) ;
else
Console . WriteLine ( "Deactivating the current authenticator was unsuccessful." ) ;
continue ;
2016-08-28 00:42:43 +02:00
default :
Console . WriteLine ( $"error: Unexpected linker result: {linkResult}" ) ;
return ;
}
} while ( linkResult ! = AuthenticatorLinker . LinkResult . AwaitingFinalization ) ;
2016-08-28 00:37:25 +02:00
2016-08-28 00:42:43 +02:00
string passKey = null ;
if ( Manifest . Entries . Count = = 0 )
{
Console . WriteLine ( "Looks like we are setting up your first account." ) ;
2017-01-19 20:21:41 +01:00
passKey = Manifest . PromptSetupPassKey ( true ) ;
2016-08-28 00:42:43 +02:00
}
else if ( Manifest . Entries . Count > 0 & & Manifest . Encrypted )
{
2017-05-23 00:02:28 +02:00
if ( string . IsNullOrEmpty ( passkey ) )
{
passkey = Manifest . PromptForPassKey ( ) ;
}
2016-08-28 00:42:43 +02:00
}
2016-08-28 00:37:25 +02:00
2016-08-28 00:42:43 +02:00
//Save the file immediately; losing this would be bad.
if ( ! Manifest . SaveAccount ( linker . LinkedAccount , passKey ! = null , passKey ) )
{
Manifest . RemoveAccount ( linker . LinkedAccount ) ;
Console . WriteLine ( "Unable to save mobile authenticator file. The mobile authenticator has not been linked." ) ;
return ;
2016-08-28 00:37:25 +02:00
}
2016-08-28 00:42:43 +02:00
Console . WriteLine (
$"The Mobile Authenticator has not yet been linked. Before finalizing the authenticator, please write down your revocation code: {linker.LinkedAccount.RevocationCode}" ) ;
2016-08-28 00:37:25 +02:00
2016-08-28 00:42:43 +02:00
AuthenticatorLinker . FinalizeResult finalizeResponse = AuthenticatorLinker . FinalizeResult . GeneralFailure ;
do
{
Console . Write ( "Please input the SMS message sent to your phone number: " ) ;
string smsCode = Console . ReadLine ( ) ;
2016-08-28 00:37:25 +02:00
2016-08-28 00:42:43 +02:00
finalizeResponse = linker . FinalizeAddAuthenticator ( smsCode ) ;
2018-03-27 23:48:22 +02:00
Utils . Verbose ( finalizeResponse ) ;
2016-08-28 00:37:25 +02:00
2016-08-28 00:42:43 +02:00
switch ( finalizeResponse )
{
case AuthenticatorLinker . FinalizeResult . BadSMSCode :
continue ;
case AuthenticatorLinker . FinalizeResult . UnableToGenerateCorrectCodes :
Console . WriteLine (
"Unable to generate the proper codes to finalize this authenticator. The authenticator should not have been linked." ) ;
Console . WriteLine (
$"In the off-chance it was, please write down your revocation code, as this is the last chance to see it: {linker.LinkedAccount.RevocationCode}" ) ;
Manifest . RemoveAccount ( linker . LinkedAccount ) ;
return ;
case AuthenticatorLinker . FinalizeResult . GeneralFailure :
Console . WriteLine ( "Unable to finalize this authenticator. The authenticator should not have been linked." ) ;
Console . WriteLine (
$"In the off-chance it was, please write down your revocation code, as this is the last chance to see it: {linker.LinkedAccount.RevocationCode}" ) ;
Manifest . RemoveAccount ( linker . LinkedAccount ) ;
return ;
}
} while ( finalizeResponse ! = AuthenticatorLinker . FinalizeResult . Success ) ;
//Linked, finally. Re-save with FullyEnrolled property.
Manifest . SaveAccount ( linker . LinkedAccount , passKey ! = null , passKey ) ;
Console . WriteLine (
$"Mobile authenticator successfully linked. Please actually write down your revocation code: {linker.LinkedAccount.RevocationCode}" ) ;
2016-08-28 00:37:25 +02:00
}
2016-08-28 00:42:43 +02:00
public static string FilterPhoneNumber ( string phoneNumber )
= > phoneNumber . Replace ( "-" , "" ) . Replace ( "(" , "" ) . Replace ( ")" , "" ) ;
public static bool PhoneNumberOkay ( string phoneNumber )
2016-08-28 00:37:25 +02:00
{
2016-08-28 00:42:43 +02:00
if ( phoneNumber = = null | | phoneNumber . Length = = 0 ) return false ;
if ( phoneNumber [ 0 ] ! = '+' ) return false ;
return true ;
2016-08-28 00:37:25 +02:00
}
2017-05-23 00:02:28 +02:00
static void Trade ( string user = "" , string passkey = "" )
2016-08-28 00:37:25 +02:00
{
2018-03-27 23:48:22 +02:00
Utils . Verbose ( "Opening manifest..." ) ;
2016-08-28 00:42:43 +02:00
Manifest = Manifest . GetManifest ( true ) ;
2018-03-27 23:48:22 +02:00
Utils . Verbose ( "Reading accounts from manifest..." ) ;
2016-08-28 00:42:43 +02:00
if ( Manifest . Encrypted )
{
2017-05-23 00:02:28 +02:00
if ( string . IsNullOrEmpty ( passkey ) )
{
passkey = Manifest . PromptForPassKey ( ) ;
}
2016-08-28 00:42:43 +02:00
SteamGuardAccounts = Manifest . GetAllAccounts ( passkey ) ;
}
else
{
SteamGuardAccounts = Manifest . GetAllAccounts ( ) ;
}
if ( SteamGuardAccounts . Length = = 0 )
{
Console . WriteLine ( "error: No accounts read." ) ;
return ;
}
2016-08-28 00:37:25 +02:00
2016-08-28 00:42:43 +02:00
foreach ( var account in SteamGuardAccounts )
{
if ( user ! = "" )
if ( ! string . Equals ( account . AccountName , user , StringComparison . CurrentCultureIgnoreCase ) )
2018-06-14 01:37:49 +02:00
continue ;
2016-08-28 00:37:25 +02:00
2016-08-28 00:42:43 +02:00
processConfirmations ( account ) ;
}
2016-08-28 00:37:25 +02:00
}
2016-08-28 00:42:43 +02:00
enum TradeAction
2016-08-28 00:37:25 +02:00
{
2016-08-28 00:42:43 +02:00
Accept = 1 ,
Deny = 0 ,
Ignore = - 1
2016-08-28 00:37:25 +02:00
}
2016-08-28 00:42:43 +02:00
2018-03-28 01:20:21 +02:00
static bool promptRefreshSession ( SteamGuardAccount account )
{
Console . WriteLine ( "Your Steam credentials have expired. For trade and market confirmations to work properly, please login again." ) ;
string username = account . AccountName ;
Console . WriteLine ( $"Username: {username}" ) ;
Console . Write ( "Password: " ) ;
var password = Console . ReadLine ( ) ;
UserLogin login = new UserLogin ( username , password ) ;
Console . Write ( $"Logging in {username}... " ) ;
LoginResult loginResult = login . DoLogin ( ) ;
if ( loginResult = = LoginResult . Need2FA & & ! string . IsNullOrEmpty ( account . SharedSecret ) )
{
// if we need a 2fa code, and we can generate it, generate a 2fa code and log in.
Utils . Verbose ( loginResult ) ;
TimeAligner . AlignTime ( ) ;
login . TwoFactorCode = account . GenerateSteamGuardCode ( ) ;
2020-05-06 15:06:01 +02:00
Utils . Verbose ( $"Logging in {username}... " ) ;
2018-03-28 01:20:21 +02:00
loginResult = login . DoLogin ( ) ;
}
Console . WriteLine ( loginResult ) ;
if ( loginResult = = LoginResult . LoginOkay )
{
account . Session = login . Session ;
}
if ( account . RefreshSession ( ) )
{
Utils . Verbose ( "Session refreshed" ) ;
Manifest . SaveAccount ( account , Manifest . Encrypted ) ;
return true ;
}
else
{
return false ;
}
}
2016-08-28 00:42:43 +02:00
static void processConfirmations ( SteamGuardAccount account )
2016-08-28 00:37:25 +02:00
{
2018-03-27 23:48:22 +02:00
Utils . Verbose ( "Refeshing Session..." ) ;
2016-12-26 00:58:08 +01:00
if ( account . RefreshSession ( ) )
{
2018-03-27 23:48:22 +02:00
Utils . Verbose ( "Session refreshed" ) ;
2016-12-28 03:58:12 +01:00
Manifest . SaveAccount ( account , Manifest . Encrypted ) ;
2016-12-26 00:58:08 +01:00
}
else
{
2018-03-28 01:20:21 +02:00
Utils . Verbose ( "Failed to refresh session, prompting user..." ) ;
if ( ! promptRefreshSession ( account ) )
2016-12-26 01:19:51 +01:00
{
2016-12-28 03:58:12 +01:00
Console . WriteLine ( "Failed to refresh session, aborting..." ) ;
2016-12-26 01:19:51 +01:00
}
2016-12-26 00:58:08 +01:00
}
2016-08-28 00:42:43 +02:00
Console . WriteLine ( "Retrieving trade confirmations..." ) ;
2017-06-04 20:58:52 +02:00
var tradesTask = account . FetchConfirmationsAsync ( ) ;
tradesTask . Wait ( ) ;
var trades = tradesTask . Result ;
2016-08-28 00:42:43 +02:00
var tradeActions = new TradeAction [ trades . Length ] ;
for ( var i = 0 ; i < tradeActions . Length ; i + + )
{
tradeActions [ i ] = TradeAction . Ignore ;
}
if ( trades . Length = = 0 )
{
Console . WriteLine ( $"No trade confirmations for {account.AccountName}." ) ;
return ;
}
var selected = 0 ;
var colorAccept = ConsoleColor . Green ;
var colorDeny = ConsoleColor . Red ;
var colorIgnore = ConsoleColor . Gray ;
var colorSelected = ConsoleColor . Yellow ;
var confirm = false ;
do
{
Console . Clear ( ) ;
if ( selected > = trades . Length )
selected = trades . Length - 1 ;
else if ( selected < 0 )
selected = 0 ;
Console . ResetColor ( ) ;
Console . WriteLine ( $"Trade confirmations for {account.AccountName}..." ) ;
Console . WriteLine ( "No action will be made without your confirmation." ) ;
Console . WriteLine ( "[a]ccept [d]eny [i]gnore [enter] Confirm [q]uit" ) ; // accept = 1, deny = 0, ignore = -1
Console . WriteLine ( ) ;
for ( var t = 0 ; t < trades . Length ; t + + )
{
ConsoleColor itemColor ;
switch ( tradeActions [ t ] )
{
case TradeAction . Accept :
itemColor = colorAccept ;
break ;
case TradeAction . Deny :
itemColor = colorDeny ;
break ;
case TradeAction . Ignore :
itemColor = colorIgnore ;
break ;
default :
throw new ArgumentOutOfRangeException ( ) ;
}
Console . ForegroundColor = t = = selected ? colorSelected : itemColor ;
2018-04-12 01:20:01 +02:00
Console . WriteLine ( $" [{t}] [{tradeActions[t]}] {trades[t].ConfType} {trades[t].Creator} {trades[t].Description}" ) ;
2016-08-28 00:42:43 +02:00
}
var key = Console . ReadKey ( ) ;
switch ( key . Key )
{
case ConsoleKey . UpArrow :
case ConsoleKey . W :
selected - - ;
break ;
case ConsoleKey . DownArrow :
case ConsoleKey . S :
selected + + ;
break ;
case ConsoleKey . A :
tradeActions [ selected ] = TradeAction . Accept ;
break ;
case ConsoleKey . D :
tradeActions [ selected ] = TradeAction . Deny ;
break ;
case ConsoleKey . I :
tradeActions [ selected ] = TradeAction . Ignore ;
break ;
case ConsoleKey . Enter :
confirm = true ;
break ;
case ConsoleKey . Escape :
case ConsoleKey . Q :
Console . ResetColor ( ) ;
Console . WriteLine ( "Quitting..." ) ;
return ;
default :
break ;
}
} while ( ! confirm ) ;
2016-08-28 00:37:25 +02:00
Console . ResetColor ( ) ;
Console . WriteLine ( ) ;
2016-08-28 00:42:43 +02:00
Console . WriteLine ( "Processing..." ) ;
2016-08-28 00:37:25 +02:00
for ( var t = 0 ; t < trades . Length ; t + + )
{
2016-08-28 00:42:43 +02:00
bool success = false ;
2016-08-28 00:37:25 +02:00
switch ( tradeActions [ t ] )
{
case TradeAction . Accept :
2018-04-12 01:20:01 +02:00
if ( Verbose ) Console . Write ( $"Accepting {trades[t].ConfType} {trades[t].Creator} {trades[t].Description}..." ) ;
2016-08-28 00:42:43 +02:00
success = account . AcceptConfirmation ( trades [ t ] ) ;
2016-08-28 00:37:25 +02:00
break ;
case TradeAction . Deny :
2018-04-12 01:20:01 +02:00
if ( Verbose ) Console . Write ( $"Denying {trades[t].ConfType} {trades[t].Creator} {trades[t].Description}..." ) ;
2018-01-30 02:28:59 +01:00
success = account . DenyConfirmation ( trades [ t ] ) ;
2016-08-28 00:37:25 +02:00
break ;
case TradeAction . Ignore :
2018-04-12 01:20:01 +02:00
if ( Verbose ) Console . Write ( $"Ignoring {trades[t].ConfType} {trades[t].Creator} {trades[t].Description}..." ) ;
2016-08-28 00:42:43 +02:00
success = true ;
2016-08-28 00:37:25 +02:00
break ;
default :
throw new ArgumentOutOfRangeException ( ) ;
}
2018-03-27 23:48:22 +02:00
Utils . Verbose ( success ) ;
2016-08-28 00:42:43 +02:00
}
Console . WriteLine ( "Done." ) ;
}
2016-08-28 00:37:25 +02:00
2017-05-23 00:02:28 +02:00
static void AcceptAllTrades ( string user = "" , string passkey = "" )
2016-08-28 00:42:43 +02:00
{
2018-03-27 23:48:22 +02:00
Utils . Verbose ( "Opening manifest..." ) ;
2016-08-28 00:42:43 +02:00
Manifest = Manifest . GetManifest ( true ) ;
2018-03-27 23:48:22 +02:00
Utils . Verbose ( "Reading accounts from manifest..." ) ;
2016-08-28 00:42:43 +02:00
if ( Manifest . Encrypted )
{
2017-05-23 00:02:28 +02:00
if ( string . IsNullOrEmpty ( passkey ) )
{
passkey = Manifest . PromptForPassKey ( ) ;
}
2016-08-28 00:42:43 +02:00
SteamGuardAccounts = Manifest . GetAllAccounts ( passkey ) ;
2016-08-28 00:37:25 +02:00
}
2016-08-28 00:42:43 +02:00
else
2016-08-28 00:37:25 +02:00
{
2016-08-28 00:42:43 +02:00
SteamGuardAccounts = Manifest . GetAllAccounts ( ) ;
2016-08-28 00:37:25 +02:00
}
2016-08-28 00:42:43 +02:00
if ( SteamGuardAccounts . Length = = 0 )
2016-08-28 00:37:25 +02:00
{
2016-08-28 00:42:43 +02:00
Console . WriteLine ( "error: No accounts read." ) ;
return ;
2016-08-28 00:37:25 +02:00
}
2016-08-28 00:42:43 +02:00
for ( int i = 0 ; i < SteamGuardAccounts . Length ; i + + )
2016-08-28 00:37:25 +02:00
{
2016-08-28 00:42:43 +02:00
SteamGuardAccount account = SteamGuardAccounts [ i ] ;
2017-09-10 20:13:46 +02:00
if ( ( user ! = "" & & account . AccountName . ToLower ( ) = = user . ToLower ( ) ) | | user = = "" )
2016-08-28 00:37:25 +02:00
{
Console . WriteLine ( $"Accepting Confirmations on {account.AccountName}" ) ;
2018-03-27 23:48:22 +02:00
Utils . Verbose ( "Refeshing Session..." ) ;
2018-03-28 01:20:21 +02:00
if ( account . RefreshSession ( ) )
{
Utils . Verbose ( "Session refreshed" ) ;
Manifest . SaveAccount ( account , Manifest . Encrypted ) ;
}
else
{
Utils . Verbose ( "Failed to refresh session, prompting user..." ) ;
if ( ! promptRefreshSession ( account ) )
{
Console . WriteLine ( "Failed to refresh session, aborting..." ) ;
}
}
2018-03-27 23:48:22 +02:00
Utils . Verbose ( "Fetching Confirmations..." ) ;
2017-06-04 20:58:52 +02:00
var tradesTask = account . FetchConfirmationsAsync ( ) ;
tradesTask . Wait ( ) ;
Confirmation [ ] confirmations = tradesTask . Result ;
2018-03-27 23:48:22 +02:00
Utils . Verbose ( "Accepting Confirmations..." ) ;
2016-08-28 00:37:25 +02:00
account . AcceptMultipleConfirmations ( confirmations ) ;
2017-09-10 20:13:46 +02:00
if ( user ! = "" )
{
break ;
}
2016-08-28 00:37:25 +02:00
}
}
}
}
2016-08-21 17:29:27 +02:00
}