wabbajack/Wabbajack.Lib/Downloaders/MEGADownloader.cs

198 lines
7.5 KiB
C#
Raw Normal View History

2019-10-12 18:03:45 +00:00
using System;
using System.Reactive;
using System.Reactive.Linq;
using System.Security;
using System.Threading.Tasks;
2019-10-12 18:03:45 +00:00
using CG.Web.MegaApiClient;
using Newtonsoft.Json;
using ReactiveUI;
2019-10-12 18:03:45 +00:00
using Wabbajack.Common;
using Wabbajack.Common.Serialization.Json;
2019-10-12 18:03:45 +00:00
namespace Wabbajack.Lib.Downloaders
2019-10-12 18:03:45 +00:00
{
public class MegaDownloader : IUrlDownloader, INeedsLoginCredentials
2019-10-12 18:03:45 +00:00
{
public MegaApiClient MegaApiClient;
private const string DataName = "mega-cred";
[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,
PasswordAesKey = infos.PasswordAesKey,
MFAKey = infos.MFAKey
};
}
public MegaApiClient.AuthInfos ToAuthInfos()
{
return new MegaApiClient.AuthInfos(Email, Hash, PasswordAesKey, MFAKey);
}
}
public LoginReturnMessage LoginWithCredentials(string username, SecureString password, string? mfa)
{
MegaApiClient.AuthInfos authInfos;
try
{
authInfos = MegaApiClient.GenerateAuthInfos(username, password.ToNormalString(), mfa);
}
catch (ApiException e)
{
return e.ApiResultCode switch
{
ApiResultCode.BadArguments => new LoginReturnMessage($"Email or password was wrong! {e.Message}",
LoginReturnCode.BadCredentials),
ApiResultCode.InternalError => new LoginReturnMessage(
$"Internal error occured! Please report this to the Wabbajack Team! {e.Message}", LoginReturnCode.InternalError),
_ => new LoginReturnMessage($"Error generating authentication information! {e.Message}", LoginReturnCode.InternalError)
};
}
try
{
MegaApiClient.Login(authInfos);
}
catch (ApiException e)
{
return e.ApiResultCode switch
{
ApiResultCode.TwoFactorAuthenticationError => new LoginReturnMessage(
$"Two-Factor Authentication is enabled. Input your TFA key above and try again! {e.Message}", LoginReturnCode.NeedsMFA),
ApiResultCode.InternalError => new LoginReturnMessage(
$"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)
};
}
if (MegaApiClient.IsLoggedIn)
{
var infos = MEGAAuthInfos.ToMEGAAuthInfos(authInfos);
infos.ToEcryptedJson(DataName);
}
return new LoginReturnMessage("Logged in successfully, you can now close this window.",
!MegaApiClient.IsLoggedIn || !Utils.HaveEncryptedJson(DataName) ? LoginReturnCode.Success : LoginReturnCode.InternalError);
}
public MegaDownloader()
{
MegaApiClient = new MegaApiClient();
TriggerLogin = ReactiveCommand.Create(() => { },
IsLoggedIn.Select(b => !b).ObserveOnGuiThread());
ClearLogin = ReactiveCommand.Create(() => Utils.CatchAndLog(() => Utils.DeleteEncryptedJson(DataName)),
IsLoggedIn.ObserveOnGuiThread());
}
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;
return GetDownloaderState(url);
}
public AbstractDownloadState? GetDownloaderState(string url)
{
if (url != null && (url.StartsWith(Consts.MegaPrefix) || url.StartsWith(Consts.MegaFilePrefix)))
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()
{
}
[JsonName("MegaDownloader")]
public class State : HTTPDownloader.State
2019-10-12 18:03:45 +00:00
{
public State(string url)
: base(url)
{
}
2020-04-04 22:55:17 +00:00
private static MegaApiClient MegaApiClient => DownloadDispatcher.GetInstance<MegaDownloader>().MegaApiClient;
private static readonly AsyncLock _loginLock = new AsyncLock();
private static async Task MegaLogin()
2019-10-12 18:03:45 +00:00
{
using var _ = await _loginLock.WaitAsync();
if (MegaApiClient.IsLoggedIn)
return;
if (!Utils.HaveEncryptedJson(DataName))
{
Utils.Status("Logging into MEGA (as anonymous)");
await MegaApiClient.LoginAnonymousAsync();
}
else
{
Utils.Status("Logging into MEGA with saved credentials.");
var infos = Utils.FromEncryptedJson<MEGAAuthInfos>(DataName);
var authInfo = infos.ToAuthInfos();
await MegaApiClient.LoginAsync(authInfo);
}
}
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
{
await MegaLogin();
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))));
return true;
2019-10-12 18:03:45 +00:00
}
public override async Task<bool> Verify(Archive a)
2019-10-12 18:03:45 +00:00
{
await MegaLogin();
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);
return node != null;
2019-10-12 18:03:45 +00:00
}
catch (Exception)
{
return false;
}
}
}
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
}
}