2019-10-12 18:03:45 +00:00
using System ;
2020-04-02 16:41:50 +00:00
using System.Reactive ;
using System.Reactive.Linq ;
2020-04-04 14:33:58 +00:00
using System.Security ;
2019-12-06 05:29:17 +00:00
using System.Threading.Tasks ;
2019-10-12 18:03:45 +00:00
using CG.Web.MegaApiClient ;
2020-04-29 15:39:05 +00:00
using Newtonsoft.Json ;
2020-04-02 16:41:50 +00:00
using ReactiveUI ;
2019-10-12 18:03:45 +00:00
using Wabbajack.Common ;
2020-04-06 20:48:54 +00:00
using Wabbajack.Common.Serialization.Json ;
2019-10-12 18:03:45 +00:00
2019-10-16 03:10:34 +00:00
namespace Wabbajack.Lib.Downloaders
2019-10-12 18:03:45 +00:00
{
2020-04-04 13:54:09 +00:00
public class MegaDownloader : IUrlDownloader , INeedsLoginCredentials
2019-10-12 18:03:45 +00:00
{
2020-04-02 16:41:50 +00:00
public MegaApiClient MegaApiClient ;
private const string DataName = "mega-cred" ;
2020-04-29 15:39:05 +00:00
[JsonName("MEGAAuthInfos")]
//https://github.com/gpailler/MegaApiClient/blob/master/MegaApiClient/MegaApiClient.cs#L1242
internal class MEGAAuthInfos
{
[JsonProperty]
public string Email { get ; private set ; } = string . Empty ;
[JsonProperty]
public string Hash { get ; private set ; } = string . Empty ;
[JsonProperty]
public byte [ ] ? PasswordAesKey { get ; private set ; }
[JsonProperty]
public string MFAKey { get ; private set ; } = string . Empty ;
public static MEGAAuthInfos ToMEGAAuthInfos ( MegaApiClient . AuthInfos infos )
{
return new MEGAAuthInfos
{
Email = infos . Email ,
Hash = infos . Hash ,
2020-05-05 14:17:55 +00:00
PasswordAesKey = infos . PasswordAesKey ,
MFAKey = infos . MFAKey
2020-04-29 15:39:05 +00:00
} ;
}
public MegaApiClient . AuthInfos ToAuthInfos ( )
{
2020-05-05 14:17:55 +00:00
return new MegaApiClient . AuthInfos ( Email , Hash , PasswordAesKey , MFAKey ) ;
2020-04-29 15:39:05 +00:00
}
}
2020-06-26 17:54:44 +00:00
public async Task < LoginReturnMessage > LoginWithCredentials ( string username , SecureString password , string? mfa )
2020-04-04 13:54:09 +00:00
{
MegaApiClient . AuthInfos authInfos ;
2020-05-30 12:23:23 +00:00
try
{
MegaApiClient . Logout ( ) ;
}
2020-06-26 17:54:44 +00:00
catch ( NotSupportedException )
2020-05-30 12:23:23 +00:00
{
// Not logged in, so ignore
}
2020-04-04 13:54:09 +00:00
try
{
2020-05-05 14:17:55 +00:00
authInfos = MegaApiClient . GenerateAuthInfos ( username , password . ToNormalString ( ) , mfa ) ;
2020-04-04 13:54:09 +00:00
}
catch ( ApiException e )
{
return e . ApiResultCode switch
{
ApiResultCode . BadArguments = > new LoginReturnMessage ( $"Email or password was wrong! {e.Message}" ,
2020-05-05 14:17:55 +00:00
LoginReturnCode . BadCredentials ) ,
2020-04-04 13:54:09 +00:00
ApiResultCode . InternalError = > new LoginReturnMessage (
2020-05-05 14:17:55 +00:00
$"Internal error occured! Please report this to the Wabbajack Team! {e.Message}" , LoginReturnCode . InternalError ) ,
_ = > new LoginReturnMessage ( $"Error generating authentication information! {e.Message}" , LoginReturnCode . InternalError )
2020-04-04 13:54:09 +00:00
} ;
}
try
{
MegaApiClient . Login ( authInfos ) ;
}
catch ( ApiException e )
{
return e . ApiResultCode switch
{
2020-04-10 10:47:29 +00:00
ApiResultCode . TwoFactorAuthenticationError = > new LoginReturnMessage (
2020-05-05 14:17:55 +00:00
$"Two-Factor Authentication is enabled. Input your TFA key above and try again! {e.Message}" , LoginReturnCode . NeedsMFA ) ,
2020-04-04 13:54:09 +00:00
ApiResultCode . InternalError = > new LoginReturnMessage (
2020-05-05 14:17:55 +00:00
$"Internal error occured! Please report this to the Wabbajack Team! {e.Message}" , LoginReturnCode . InternalError ) ,
ApiResultCode . RequestIncomplete = > new LoginReturnMessage (
$"Bad credentials! {e.Message}" , LoginReturnCode . BadCredentials ) ,
ApiResultCode . ResourceNotExists = > new LoginReturnMessage (
$"Bad credentials! {e.Message}" , LoginReturnCode . BadCredentials ) ,
_ = > new LoginReturnMessage ( $"Error during login: {e.Message}" , LoginReturnCode . InternalError )
2020-04-04 13:54:09 +00:00
} ;
}
2020-04-29 15:39:05 +00:00
if ( MegaApiClient . IsLoggedIn )
{
var infos = MEGAAuthInfos . ToMEGAAuthInfos ( authInfos ) ;
2020-06-26 17:54:44 +00:00
await infos . ToEcryptedJson ( DataName ) ;
2020-04-29 15:39:05 +00:00
}
2020-04-04 13:54:09 +00:00
return new LoginReturnMessage ( "Logged in successfully, you can now close this window." ,
2020-05-05 14:17:55 +00:00
! MegaApiClient . IsLoggedIn | | ! Utils . HaveEncryptedJson ( DataName ) ? LoginReturnCode . Success : LoginReturnCode . InternalError ) ;
2020-04-04 13:54:09 +00:00
}
2020-04-02 16:41:50 +00:00
public MegaDownloader ( )
{
MegaApiClient = new MegaApiClient ( ) ;
TriggerLogin = ReactiveCommand . Create ( ( ) = > { } ,
IsLoggedIn . Select ( b = > ! b ) . ObserveOnGuiThread ( ) ) ;
2020-06-26 17:08:30 +00:00
ClearLogin = ReactiveCommand . CreateFromTask ( ( ) = > Utils . CatchAndLog ( async ( ) = > await Utils . DeleteEncryptedJson ( DataName ) ) ,
2020-04-02 16:41:50 +00:00
IsLoggedIn . ObserveOnGuiThread ( ) ) ;
}
2020-04-09 20:20:34 +00:00
public async Task < AbstractDownloadState ? > GetDownloaderState ( dynamic archiveINI , bool quickMode )
2019-10-12 18:03:45 +00:00
{
2019-11-21 15:51:57 +00:00
var url = archiveINI ? . General ? . directURL ;
2019-10-16 11:44:45 +00:00
return GetDownloaderState ( url ) ;
}
2020-04-09 20:20:34 +00:00
public AbstractDownloadState ? GetDownloaderState ( string url )
2019-10-16 11:44:45 +00:00
{
2020-04-27 18:49:22 +00:00
if ( url ! = null & & ( url . StartsWith ( Consts . MegaPrefix ) | | url . StartsWith ( Consts . MegaFilePrefix ) ) )
2020-04-09 20:20:34 +00:00
return new State ( url ) ;
2019-10-12 18:03:45 +00:00
return null ;
}
2019-12-07 02:45:13 +00:00
public async Task Prepare ( )
2019-10-12 22:15:20 +00:00
{
}
2020-04-06 20:48:54 +00:00
[JsonName("MegaDownloader")]
2019-10-12 21:37:16 +00:00
public class State : HTTPDownloader . State
2019-10-12 18:03:45 +00:00
{
2020-04-09 20:20:34 +00:00
public State ( string url )
: base ( url )
{
}
2020-04-04 22:55:17 +00:00
private static MegaApiClient MegaApiClient = > DownloadDispatcher . GetInstance < MegaDownloader > ( ) . MegaApiClient ;
2020-04-02 16:41:50 +00:00
2020-05-05 14:17:55 +00:00
private static readonly AsyncLock _loginLock = new AsyncLock ( ) ;
2020-04-29 12:26:44 +00:00
private static async Task MegaLogin ( )
2019-10-12 18:03:45 +00:00
{
2020-04-29 12:26:44 +00:00
using var _ = await _loginLock . WaitAsync ( ) ;
2020-04-04 14:43:42 +00:00
if ( MegaApiClient . IsLoggedIn )
return ;
if ( ! Utils . HaveEncryptedJson ( DataName ) )
2020-04-02 16:41:50 +00:00
{
Utils . Status ( "Logging into MEGA (as anonymous)" ) ;
2020-04-29 12:26:44 +00:00
await MegaApiClient . LoginAnonymousAsync ( ) ;
2020-04-04 14:43:42 +00:00
}
else
2020-04-04 13:54:09 +00:00
{
Utils . Status ( "Logging into MEGA with saved credentials." ) ;
2020-05-25 14:31:56 +00:00
var infos = await Utils . FromEncryptedJson < MEGAAuthInfos > ( DataName ) ;
2020-04-29 15:39:05 +00:00
var authInfo = infos . ToAuthInfos ( ) ;
2020-04-29 12:26:44 +00:00
await MegaApiClient . LoginAsync ( authInfo ) ;
2020-04-02 16:41:50 +00:00
}
2020-04-04 14:43:42 +00:00
}
2020-03-25 22:30:43 +00:00
public override async Task < bool > Download ( Archive a , AbsolutePath destination )
2019-10-12 18:03:45 +00:00
{
2020-04-29 12:26:44 +00:00
await MegaLogin ( ) ;
2020-04-02 16:41:50 +00:00
2019-11-21 15:51:57 +00:00
var fileLink = new Uri ( Url ) ;
2019-10-12 18:03:45 +00:00
Utils . Status ( $"Downloading MEGA file: {a.Name}" ) ;
2020-04-04 22:15:55 +00:00
await MegaApiClient . DownloadFileAsync ( fileLink , ( string ) destination , new Progress < double > ( p = > Utils . Status ( $"Downloading MEGA File: {a.Name}" , Percent . FactoryPutInRange ( p ) ) ) ) ;
2020-01-18 19:57:26 +00:00
return true ;
2019-10-12 18:03:45 +00:00
}
2020-01-13 22:55:55 +00:00
public override async Task < bool > Verify ( Archive a )
2019-10-12 18:03:45 +00:00
{
2020-04-29 12:26:44 +00:00
await MegaLogin ( ) ;
2020-04-02 16:41:50 +00:00
2019-11-21 15:51:57 +00:00
var fileLink = new Uri ( Url ) ;
2019-10-12 18:03:45 +00:00
try
{
2020-04-26 09:31:00 +00:00
var node = await MegaApiClient . GetNodeFromLinkAsync ( fileLink ) ;
2020-04-27 18:49:22 +00:00
return node ! = null ;
2019-10-12 18:03:45 +00:00
}
catch ( Exception )
{
return false ;
}
}
2020-07-13 03:50:12 +00:00
public override async Task < ( Archive ? Archive , TempFile NewFile ) > FindUpgrade ( Archive a )
{
return default ;
}
2019-10-12 18:03:45 +00:00
}
2020-04-02 16:41:50 +00:00
public ReactiveCommand < Unit , Unit > TriggerLogin { get ; }
public ReactiveCommand < Unit , Unit > ClearLogin { get ; }
public IObservable < bool > IsLoggedIn = > Utils . HaveEncryptedJsonObservable ( DataName ) ;
public string SiteName = > "MEGA" ;
public IObservable < string > MetaInfo = > Observable . Return ( "" ) ;
public Uri SiteURL = > new Uri ( "https://mega.nz/" ) ;
public Uri IconUri = > new Uri ( "https://mega.nz/favicon.ico" ) ;
2019-10-12 18:03:45 +00:00
}
}