diff --git a/Wabbajack.Lib/LibCefHelpers/Init.cs b/Wabbajack.Lib/LibCefHelpers/Init.cs index 146bd66b..27cc7885 100644 --- a/Wabbajack.Lib/LibCefHelpers/Init.cs +++ b/Wabbajack.Lib/LibCefHelpers/Init.cs @@ -3,9 +3,11 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; +using System.Threading; using System.Threading.Tasks; using Alphaleonis.Win32.Filesystem; using Wabbajack.Common; +using Xilium.CefGlue; namespace Wabbajack.Lib.LibCefHelpers { @@ -29,5 +31,51 @@ namespace Wabbajack.Lib.LibCefHelpers FileExtractor.ExtractAll(wq, "cefglue.7z", "."); } + public static async Task GetCookies(string domainEnding) + { + var manager = CefCookieManager.GetGlobal(null); + var visitor = new CookieVisitor(); + if (!manager.VisitAllCookies(visitor)) + return new Cookie[0]; + var cc = await visitor.Task; + + return (await visitor.Task).Where(c => c.Domain.EndsWith(domainEnding)).ToArray(); + } + + private class CookieVisitor : CefCookieVisitor + { + TaskCompletionSource> _source = new TaskCompletionSource>(); + public Task> Task => _source.Task; + + public List Cookies { get; } = new List(); + protected override bool Visit(CefCookie cookie, int count, int total, out bool delete) + { + Cookies.Add(new Cookie + { + Name = cookie.Name, + Value = cookie.Value, + Domain = cookie.Domain, + Path = cookie.Path + }); + if (count == total) + _source.SetResult(Cookies); + delete = false; + return true; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + _source.SetResult(Cookies); + } + } + + public class Cookie + { + public string Name { get; set; } + public string Value { get; set; } + public string Domain { get; set; } + public string Path { get; set; } + } } } diff --git a/Wabbajack.Lib/MO2Installer.cs b/Wabbajack.Lib/MO2Installer.cs index 7d978754..a38c7e37 100644 --- a/Wabbajack.Lib/MO2Installer.cs +++ b/Wabbajack.Lib/MO2Installer.cs @@ -40,6 +40,10 @@ namespace Wabbajack.Lib ConfigureProcessor(18, RecommendQueueSize()); var game = GameRegistry.Games[ModList.GameType]; + // TODO: TESTING + var api = new NexusApiClient(); + api.GetUserStatus(); + if (GameFolder == null) GameFolder = game.GameLocation; diff --git a/Wabbajack.Lib/NexusApi/NexusApi.cs b/Wabbajack.Lib/NexusApi/NexusApi.cs index 96b554a2..d0b979f1 100644 --- a/Wabbajack.Lib/NexusApi/NexusApi.cs +++ b/Wabbajack.Lib/NexusApi/NexusApi.cs @@ -12,10 +12,16 @@ using System.Security.Authentication; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; +using System.Windows.Documents; using Syroot.Windows.IO; using Wabbajack.Common; using Wabbajack.Lib.Downloaders; +using Wabbajack.Lib.LibCefHelpers; using WebSocketSharp; +using Xilium.CefGlue; +using Xilium.CefGlue.Common; +using Xilium.CefGlue.Common.Handlers; +using Xilium.CefGlue.WPF; using static Wabbajack.Lib.NexusApi.NexusApiUtils; namespace Wabbajack.Lib.NexusApi @@ -66,17 +72,11 @@ namespace Wabbajack.Lib.NexusApi File.Delete(API_KEY_CACHE_FILE); } - var cacheFolder = Path.Combine(new KnownFolder(KnownFolderType.LocalAppData).Path, "Wabbajack"); - if (!Directory.Exists(cacheFolder)) - { - Directory.CreateDirectory(cacheFolder); - } - try { return Utils.FromEncryptedJson("nexusapikey"); } - catch (CryptographicException) + catch (Exception) { } @@ -92,8 +92,36 @@ namespace Wabbajack.Lib.NexusApi } } - public async Task SetupNexusLogin(Action browserNavigate) + class RefererHandler : RequestHandler { + private string _referer; + + public RefererHandler(string referer) + { + _referer = referer; + } + protected override bool OnBeforeBrowse(CefBrowser browser, CefFrame frame, CefRequest request, bool userGesture, bool isRedirect) + { + base.OnBeforeBrowse(browser, frame, request, userGesture, isRedirect); + if (request.ReferrerURL == null) + request.SetReferrer(_referer, CefReferrerPolicy.Default); + return false; + } + } + + public static async Task SetupNexusLogin(BaseCefBrowser browser, Action updateStatus) + { + updateStatus("Please Log Into the Nexus"); + browser.Address = "https://users.nexusmods.com/auth/continue?client_id=nexus&redirect_uri=https://www.nexusmods.com/oauth/callback&response_type=code&referrer=//www.nexusmods.com"; + while (true) + { + var cookies = (await Helpers.GetCookies("nexusmods.com")); + if (cookies.FirstOrDefault(c => c.Name == "member_id") != null) + break; + await Task.Delay(500); + } + + // open a web socket to receive the api key var guid = Guid.NewGuid(); var _websocket = new WebSocket("wss://sso.nexusmods.com") @@ -104,14 +132,16 @@ namespace Wabbajack.Lib.NexusApi } }; + updateStatus("Please Authorize Wabbajack to Download Mods"); var api_key = new TaskCompletionSource(); _websocket.OnMessage += (sender, msg) => { api_key.SetResult(msg.Data); }; _websocket.Connect(); _websocket.Send("{\"id\": \"" + guid + "\", \"appid\": \"" + Consts.AppName + "\"}"); + await Task.Delay(1000); // open a web browser to get user permission - browserNavigate(new Uri($"https://www.nexusmods.com/sso?id={guid}&application=" + Consts.AppName)); + browser.Address = $"https://www.nexusmods.com/sso?id={guid}&application={Consts.AppName}"; return await api_key.Task; } diff --git a/Wabbajack/Util/AutoScrollBehavior.cs b/Wabbajack/Util/AutoScrollBehavior.cs index 462f666f..fd564732 100644 --- a/Wabbajack/Util/AutoScrollBehavior.cs +++ b/Wabbajack/Util/AutoScrollBehavior.cs @@ -102,10 +102,14 @@ namespace Wabbajack { if (e.Action == NotifyCollectionChangedAction.Add) { - _listBox.ScrollIntoView(e.NewItems[0]); - _listBox.SelectedItem = e.NewItems[0]; + try + { + _listBox.ScrollIntoView(e.NewItems[0]); + _listBox.SelectedItem = e.NewItems[0]; + } + catch (ArgumentOutOfRangeException) { } } } } } -} \ No newline at end of file +} diff --git a/Wabbajack/View Models/MainWindowVM.cs b/Wabbajack/View Models/MainWindowVM.cs index e28fd7b8..bfebbbc3 100644 --- a/Wabbajack/View Models/MainWindowVM.cs +++ b/Wabbajack/View Models/MainWindowVM.cs @@ -6,10 +6,13 @@ using System; using System.Linq; using System.Reactive.Disposables; using System.Reactive.Linq; +using System.Threading.Tasks; using System.Windows; +using System.Windows.Threading; using Wabbajack.Common; using Wabbajack.Common.StatusFeed; using Wabbajack.Lib; +using Wabbajack.Lib.NexusApi; using Wabbajack.Lib.StatusMessages; namespace Wabbajack @@ -34,10 +37,12 @@ namespace Wabbajack public readonly Lazy Gallery; public readonly ModeSelectionVM ModeSelectionVM; public readonly WebBrowserVM WebBrowserVM; + public Dispatcher ViewDispatcher { get; set; } public MainWindowVM(MainWindow mainWindow, MainSettings settings) { MainWindow = mainWindow; + ViewDispatcher = MainWindow.Dispatcher; Settings = settings; Installer = new Lazy(() => new InstallerVM(this)); Compiler = new Lazy(() => new CompilerVM(this)); @@ -61,6 +66,10 @@ namespace Wabbajack .OfType() .Subscribe(msg => ConfirmUpdate(msg)); + Utils.LogMessages + .OfType() + .Subscribe(HandleRequestNexusAuthorization); + if (IsStartingFromModlist(out var path)) { Installer.Value.ModListLocation.TargetPath = path; @@ -69,11 +78,42 @@ namespace Wabbajack else { // Start on mode selection - //ActivePane = ModeSelectionVM; - ActivePane = WebBrowserVM; + ActivePane = ModeSelectionVM; } } + private void HandleRequestNexusAuthorization(RequestNexusAuthorization msg) + { + ViewDispatcher.InvokeAsync(async () => + { + var oldPane = ActivePane; + var vm = new WebBrowserVM(); + ActivePane = vm; + try + { + vm.BackCommand = ReactiveCommand.Create(() => + { + ActivePane = oldPane; + msg.Cancel(); + }); + } + catch (Exception e) + { } + + try + { + var key = await NexusApiClient.SetupNexusLogin(vm.Browser, m => vm.Instructions = m); + msg.Resume(key); + } + catch (Exception ex) + { + msg.Cancel(); + } + ActivePane = oldPane; + + }); + } + private void ConfirmUpdate(ConfirmUpdateOfExistingInstall msg) { var result = MessageBox.Show(msg.ExtendedDescription, msg.ShortDescription, MessageBoxButton.OKCancel); diff --git a/Wabbajack/View Models/WebBrowserVM.cs b/Wabbajack/View Models/WebBrowserVM.cs index da617ee1..5fcf7a65 100644 --- a/Wabbajack/View Models/WebBrowserVM.cs +++ b/Wabbajack/View Models/WebBrowserVM.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using ReactiveUI; using ReactiveUI.Fody.Helpers; using Wabbajack.Lib; using Xilium.CefGlue.Common; @@ -12,16 +13,17 @@ namespace Wabbajack { public class WebBrowserVM : ViewModel { - [Reactive] - public string Address { get; set; } - [Reactive] public string Instructions { get; set; } - public BaseCefBrowser Browser { get; internal set; } + public WpfCefBrowser Browser { get; } - public WebBrowserVM() + [Reactive] + public IReactiveCommand BackCommand { get; set; } + public WebBrowserVM(string url = "http://www.wabbajack.org") { + Browser = new WpfCefBrowser(); + Browser.Address = url; Instructions = "Wabbajack Web Browser"; } } diff --git a/Wabbajack/Views/WebBrowserView.xaml b/Wabbajack/Views/WebBrowserView.xaml index a5ea432a..324b3cce 100644 --- a/Wabbajack/Views/WebBrowserView.xaml +++ b/Wabbajack/Views/WebBrowserView.xaml @@ -65,6 +65,7 @@ ToolTip="Back to main menu"> - + + diff --git a/Wabbajack/Views/WebBrowserView.xaml.cs b/Wabbajack/Views/WebBrowserView.xaml.cs index a2ae5d2d..b43d7839 100644 --- a/Wabbajack/Views/WebBrowserView.xaml.cs +++ b/Wabbajack/Views/WebBrowserView.xaml.cs @@ -23,7 +23,6 @@ namespace Wabbajack public WebBrowserView() { InitializeComponent(); - ((WebBrowserVM)DataContext).Browser = Browser; } } }