mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
Merge Master
This commit is contained in:
commit
895a555802
@ -11,6 +11,7 @@ using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
@ -1136,5 +1137,19 @@ namespace Wabbajack.Common
|
||||
await using var d = File.Create(dest);
|
||||
await s.CopyToAsync(d);
|
||||
}
|
||||
|
||||
public static string ToNormalString(this SecureString value)
|
||||
{
|
||||
var valuePtr = IntPtr.Zero;
|
||||
try
|
||||
{
|
||||
valuePtr = Marshal.SecureStringToGlobalAllocUnicode(value);
|
||||
return Marshal.PtrToStringUni(valuePtr) ?? "";
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.ZeroFreeGlobalAllocUnicode(valuePtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reactive;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using System.Security;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Wabbajack.Lib.Downloaders
|
||||
@ -20,4 +15,21 @@ namespace Wabbajack.Lib.Downloaders
|
||||
Uri SiteURL { get; }
|
||||
Uri IconUri { get; }
|
||||
}
|
||||
|
||||
public struct LoginReturnMessage
|
||||
{
|
||||
public string Message;
|
||||
public bool Failure;
|
||||
|
||||
public LoginReturnMessage(string message, bool failure)
|
||||
{
|
||||
Message = message;
|
||||
Failure = failure;
|
||||
}
|
||||
}
|
||||
|
||||
public interface INeedsLoginCredentials : INeedsLogin
|
||||
{
|
||||
LoginReturnMessage LoginWithCredentials(string username, SecureString password);
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,77 @@
|
||||
using System;
|
||||
using System.Reactive;
|
||||
using System.Reactive.Linq;
|
||||
using System.Security;
|
||||
using System.Threading.Tasks;
|
||||
using CG.Web.MegaApiClient;
|
||||
using ReactiveUI;
|
||||
using Wabbajack.Common;
|
||||
|
||||
namespace Wabbajack.Lib.Downloaders
|
||||
{
|
||||
public class MegaDownloader : IDownloader, IUrlDownloader
|
||||
public class MegaDownloader : IUrlDownloader, INeedsLoginCredentials
|
||||
{
|
||||
public MegaApiClient MegaApiClient;
|
||||
private const string DataName = "mega-cred";
|
||||
|
||||
public LoginReturnMessage LoginWithCredentials(string username, SecureString password)
|
||||
{
|
||||
MegaApiClient.AuthInfos authInfos;
|
||||
|
||||
try
|
||||
{
|
||||
authInfos = MegaApiClient.GenerateAuthInfos(username, password.ToNormalString());
|
||||
username = null;
|
||||
password = null;
|
||||
}
|
||||
catch (ApiException e)
|
||||
{
|
||||
return e.ApiResultCode switch
|
||||
{
|
||||
ApiResultCode.BadArguments => new LoginReturnMessage($"Email or password was wrong! {e.Message}",
|
||||
true),
|
||||
ApiResultCode.InternalError => new LoginReturnMessage(
|
||||
$"Internal error occured! Please report this to the Wabbajack Team! {e.Message}", true),
|
||||
_ => new LoginReturnMessage($"Error generating authentication information! {e.Message}", true)
|
||||
};
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
MegaApiClient.Login(authInfos);
|
||||
}
|
||||
catch (ApiException e)
|
||||
{
|
||||
if ((int)e.ApiResultCode == -26)
|
||||
{
|
||||
return new LoginReturnMessage("Two-Factor Authentication needs to be disabled before login!", true);
|
||||
}
|
||||
return e.ApiResultCode switch
|
||||
{
|
||||
ApiResultCode.InternalError => new LoginReturnMessage(
|
||||
$"Internal error occured! Please report this to the Wabbajack Team! {e.Message}", true),
|
||||
_ => new LoginReturnMessage($"Error during login: {e.Message}", true)
|
||||
};
|
||||
}
|
||||
|
||||
if(MegaApiClient.IsLoggedIn)
|
||||
authInfos.ToEcryptedJson(DataName);
|
||||
|
||||
return new LoginReturnMessage("Logged in successfully, you can now close this window.",
|
||||
!MegaApiClient.IsLoggedIn || !Utils.HaveEncryptedJson(DataName));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
var url = archiveINI?.General?.directURL;
|
||||
@ -16,7 +81,7 @@ namespace Wabbajack.Lib.Downloaders
|
||||
public AbstractDownloadState GetDownloaderState(string url)
|
||||
{
|
||||
if (url != null && url.StartsWith(Consts.MegaPrefix))
|
||||
return new State { Url = url };
|
||||
return new State { Url = url, MegaApiClient = MegaApiClient};
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -26,27 +91,44 @@ namespace Wabbajack.Lib.Downloaders
|
||||
|
||||
public class State : HTTPDownloader.State
|
||||
{
|
||||
public MegaApiClient MegaApiClient;
|
||||
|
||||
private void MegaLogin()
|
||||
{
|
||||
if (MegaApiClient.IsLoggedIn)
|
||||
return;
|
||||
|
||||
if (!Utils.HaveEncryptedJson(DataName))
|
||||
{
|
||||
Utils.Status("Logging into MEGA (as anonymous)");
|
||||
MegaApiClient.LoginAnonymous();
|
||||
}
|
||||
else
|
||||
{
|
||||
Utils.Status("Logging into MEGA with saved credentials.");
|
||||
var authInfo = Utils.FromEncryptedJson<MegaApiClient.AuthInfos>(DataName);
|
||||
MegaApiClient.Login(authInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task<bool> Download(Archive a, AbsolutePath destination)
|
||||
{
|
||||
var client = new MegaApiClient();
|
||||
Utils.Status("Logging into MEGA (as anonymous)");
|
||||
client.LoginAnonymous();
|
||||
MegaLogin();
|
||||
|
||||
var fileLink = new Uri(Url);
|
||||
var node = client.GetNodeFromLink(fileLink);
|
||||
Utils.Status($"Downloading MEGA file: {a.Name}");
|
||||
client.DownloadFile(fileLink, (string)destination);
|
||||
await MegaApiClient.DownloadFileAsync(fileLink, (string)destination, new Progress<double>(p => Utils.Status($"Downloading MEGA File: {a.Name}", Percent.FactoryPutInRange(p))));
|
||||
return true;
|
||||
}
|
||||
|
||||
public override async Task<bool> Verify(Archive a)
|
||||
{
|
||||
var client = new MegaApiClient();
|
||||
Utils.Status("Logging into MEGA (as anonymous)");
|
||||
client.LoginAnonymous();
|
||||
MegaLogin();
|
||||
|
||||
var fileLink = new Uri(Url);
|
||||
try
|
||||
{
|
||||
var node = client.GetNodeFromLink(fileLink);
|
||||
var node = MegaApiClient.GetNodeFromLink(fileLink);
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
@ -55,5 +137,13 @@ namespace Wabbajack.Lib.Downloaders
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
40
Wabbajack/View Models/Settings/CredentialsLoginVM.cs
Normal file
40
Wabbajack/View Models/Settings/CredentialsLoginVM.cs
Normal file
@ -0,0 +1,40 @@
|
||||
using System.Reactive;
|
||||
using System.Reactive.Linq;
|
||||
using System.Security;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using Wabbajack.Lib;
|
||||
using Wabbajack.Lib.Downloaders;
|
||||
|
||||
namespace Wabbajack
|
||||
{
|
||||
public class CredentialsLoginVM : ViewModel
|
||||
{
|
||||
[Reactive]
|
||||
public string Username { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public LoginReturnMessage ReturnMessage { get; set; }
|
||||
|
||||
private readonly ObservableAsPropertyHelper<bool> _loginEnabled;
|
||||
public bool LoginEnabled => _loginEnabled.Value;
|
||||
|
||||
private readonly INeedsLoginCredentials _downloader;
|
||||
|
||||
public CredentialsLoginVM(INeedsLoginCredentials downloader)
|
||||
{
|
||||
_downloader = downloader;
|
||||
|
||||
_loginEnabled = this.WhenAny(x => x.Username)
|
||||
.Select(username => !string.IsNullOrWhiteSpace(username))
|
||||
.ToGuiProperty(this,
|
||||
nameof(LoginEnabled));
|
||||
}
|
||||
|
||||
public void Login(SecureString password)
|
||||
{
|
||||
ReturnMessage = _downloader.LoginWithCredentials(Username, password);
|
||||
password.Clear();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive;
|
||||
using System.Reactive.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
@ -27,16 +28,39 @@ namespace Wabbajack
|
||||
|
||||
public class LoginTargetVM : ViewModel
|
||||
{
|
||||
private readonly ObservableAsPropertyHelper<string> _MetaInfo;
|
||||
public string MetaInfo => _MetaInfo.Value;
|
||||
private readonly ObservableAsPropertyHelper<string> _metaInfo;
|
||||
public string MetaInfo => _metaInfo.Value;
|
||||
|
||||
public INeedsLogin Login { get; }
|
||||
public INeedsLoginCredentials LoginWithCredentials { get; }
|
||||
public bool UsesCredentials { get; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit> TriggerCredentialsLogin;
|
||||
|
||||
public LoginTargetVM(INeedsLogin login)
|
||||
{
|
||||
Login = login;
|
||||
_MetaInfo = (login.MetaInfo ?? Observable.Return(""))
|
||||
|
||||
if (login is INeedsLoginCredentials loginWithCredentials)
|
||||
{
|
||||
UsesCredentials = true;
|
||||
LoginWithCredentials = loginWithCredentials;
|
||||
}
|
||||
|
||||
_metaInfo = (login.MetaInfo ?? Observable.Return(""))
|
||||
.ToGuiProperty(this, nameof(MetaInfo));
|
||||
|
||||
if (!UsesCredentials)
|
||||
return;
|
||||
|
||||
TriggerCredentialsLogin = ReactiveCommand.Create(() =>
|
||||
{
|
||||
if (!(login is INeedsLoginCredentials))
|
||||
return;
|
||||
|
||||
var loginWindow = new LoginWindowView(LoginWithCredentials);
|
||||
loginWindow.Show();
|
||||
}, LoginWithCredentials.IsLoggedIn.Select(b => !b).ObserveOnGuiThread());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
34
Wabbajack/Views/Settings/CredentialsLoginView.xaml
Normal file
34
Wabbajack/Views/Settings/CredentialsLoginView.xaml
Normal file
@ -0,0 +1,34 @@
|
||||
<rxui:ReactiveUserControl
|
||||
x:Class="Wabbajack.CredentialsLoginView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Wabbajack"
|
||||
xmlns:rxui="http://reactiveui.net"
|
||||
x:TypeArguments="local:CredentialsLoginVM"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Grid Margin="16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="60"/>
|
||||
<RowDefinition Height="60"/>
|
||||
<RowDefinition Height="40"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="120"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBox FontSize="20" x:Name="Username" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"/>
|
||||
<PasswordBox Margin="0 16 0 -8" FontSize="20" x:Name="Password" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"/>
|
||||
|
||||
<Button Margin="8 8 0 0" x:Name="LoginButton" Grid.Row="2" Grid.Column="2" Click="LoginButton_OnClick">
|
||||
<TextBlock FontSize="14">Login</TextBlock>
|
||||
</Button>
|
||||
|
||||
<TextBox x:Name="Message" FontSize="20" Margin="0 16 0 0" Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="3"/>
|
||||
</Grid>
|
||||
</rxui:ReactiveUserControl>
|
37
Wabbajack/Views/Settings/CredentialsLoginView.xaml.cs
Normal file
37
Wabbajack/Views/Settings/CredentialsLoginView.xaml.cs
Normal file
@ -0,0 +1,37 @@
|
||||
using System.Reactive.Disposables;
|
||||
using System.Windows;
|
||||
using ReactiveUI;
|
||||
using Wabbajack.Lib.Downloaders;
|
||||
|
||||
namespace Wabbajack
|
||||
{
|
||||
public partial class CredentialsLoginView
|
||||
{
|
||||
public INeedsLoginCredentials Downloader { get; set; }
|
||||
|
||||
public CredentialsLoginView(INeedsLoginCredentials downloader)
|
||||
{
|
||||
Downloader = downloader;
|
||||
|
||||
if (ViewModel == null)
|
||||
ViewModel = new CredentialsLoginVM(downloader);
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
this.WhenActivated(disposable =>
|
||||
{
|
||||
this.Bind(ViewModel, x => x.Username, x => x.Username.Text)
|
||||
.DisposeWith(disposable);
|
||||
this.Bind(ViewModel, x => x.LoginEnabled, x => x.LoginButton.IsEnabled)
|
||||
.DisposeWith(disposable);
|
||||
this.OneWayBind(ViewModel, x => x.ReturnMessage.Message, x => x.Message.Text)
|
||||
.DisposeWith(disposable);
|
||||
});
|
||||
}
|
||||
|
||||
private void LoginButton_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.Login(Password.SecurePassword);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,40 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Wabbajack
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for LoginItemView.xaml
|
||||
/// </summary>
|
||||
public partial class LoginItemView : ReactiveUserControl<LoginTargetVM>
|
||||
public partial class LoginItemView
|
||||
{
|
||||
public LoginItemView()
|
||||
{
|
||||
InitializeComponent();
|
||||
this.WhenActivated(disposable =>
|
||||
{
|
||||
this.OneWayBindStrict(this.ViewModel, x => x.Login.SiteName, x => x.SiteNameText.Text)
|
||||
this.OneWayBindStrict(ViewModel, x => x.Login.SiteName, x => x.SiteNameText.Text)
|
||||
.DisposeWith(disposable);
|
||||
this.OneWayBindStrict(this.ViewModel, x => x.Login.TriggerLogin, x => x.LoginButton.Command)
|
||||
|
||||
if (!ViewModel.UsesCredentials)
|
||||
{
|
||||
this.OneWayBindStrict(ViewModel, x => x.Login.TriggerLogin, x => x.LoginButton.Command)
|
||||
.DisposeWith(disposable);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.OneWayBindStrict(ViewModel, x => x.TriggerCredentialsLogin, x => x.LoginButton.Command)
|
||||
.DisposeWith(disposable);
|
||||
this.OneWayBindStrict(this.ViewModel, x => x.Login.ClearLogin, x => x.LogoutButton.Command)
|
||||
}
|
||||
|
||||
this.OneWayBindStrict(ViewModel, x => x.Login.ClearLogin, x => x.LogoutButton.Command)
|
||||
.DisposeWith(disposable);
|
||||
this.OneWayBindStrict(this.ViewModel, x => x.MetaInfo, x => x.MetaText.Text)
|
||||
this.OneWayBindStrict(ViewModel, x => x.MetaInfo, x => x.MetaText.Text)
|
||||
.DisposeWith(disposable);
|
||||
|
||||
// Modify label state
|
||||
@ -42,13 +36,14 @@ namespace Wabbajack
|
||||
.Switch()
|
||||
.Subscribe(x =>
|
||||
{
|
||||
this.LoginButton.Content = x ? "Login" : "Logged In";
|
||||
LoginButton.Content = x ? "Login" : "Logged In";
|
||||
});
|
||||
|
||||
this.WhenAny(x => x.ViewModel.Login.ClearLogin.CanExecute)
|
||||
.Switch()
|
||||
.Subscribe(x =>
|
||||
{
|
||||
this.LogoutButton.Content = x ? "Logout" : "Logged Out";
|
||||
LogoutButton.Content = x ? "Logout" : "Logged Out";
|
||||
});
|
||||
});
|
||||
}
|
||||
|
16
Wabbajack/Views/Settings/LoginWindowView.xaml
Normal file
16
Wabbajack/Views/Settings/LoginWindowView.xaml
Normal file
@ -0,0 +1,16 @@
|
||||
<mah:MetroWindow x:Class="Wabbajack.LoginWindowView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:Wabbajack"
|
||||
mc:Ignorable="d"
|
||||
WindowTitleBrush="{StaticResource MahApps.Brushes.Accent}"
|
||||
Style="{StaticResource {x:Type Window}}"
|
||||
d:DataContext="{d:DesignInstance local:LoginWindowView }"
|
||||
Title="Login" Height="450" Width="800">
|
||||
<Grid x:Name="Grid">
|
||||
|
||||
</Grid>
|
||||
</mah:MetroWindow>
|
20
Wabbajack/Views/Settings/LoginWindowView.xaml.cs
Normal file
20
Wabbajack/Views/Settings/LoginWindowView.xaml.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using Wabbajack.Lib.Downloaders;
|
||||
|
||||
namespace Wabbajack
|
||||
{
|
||||
public partial class LoginWindowView
|
||||
{
|
||||
public INeedsLoginCredentials Downloader { get; set; }
|
||||
|
||||
public LoginWindowView(INeedsLoginCredentials downloader)
|
||||
{
|
||||
Downloader = downloader;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
var loginView = new CredentialsLoginView(downloader);
|
||||
|
||||
Grid.Children.Add(loginView);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user