diff --git a/src/App/Platforms/Android/Handlers/CustomTabbedPageHandler.cs b/src/App/Platforms/Android/Handlers/CustomTabbedPageHandler.cs index 8d863e003..1a734eeb6 100644 --- a/src/App/Platforms/Android/Handlers/CustomTabbedPageHandler.cs +++ b/src/App/Platforms/Android/Handlers/CustomTabbedPageHandler.cs @@ -1,5 +1,6 @@ using AndroidX.AppCompat.View.Menu; using Bit.Core.Abstractions; +using Bit.Core.Services; using Bit.Core.Utilities; using Google.Android.Material.BottomNavigation; using Microsoft.Maui.Handlers; @@ -90,7 +91,17 @@ namespace Bit.App.Handlers if(e.Item is MenuItemImpl item) { System.Diagnostics.Debug.WriteLine($"Tab '{item.Title}' was reselected so we'll PopToRoot."); - MainThread.BeginInvokeOnMainThread(async () => await _tabbedPage.CurrentPage.Navigation.PopToRootAsync()); + MainThread.BeginInvokeOnMainThread(async () => + { + try + { + await _tabbedPage.CurrentPage.Navigation.PopToRootAsync(); + } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); + } + }); } } diff --git a/src/App/Platforms/iOS/AppDelegate.cs b/src/App/Platforms/iOS/AppDelegate.cs index af4d6667b..b9f648805 100644 --- a/src/App/Platforms/iOS/AppDelegate.cs +++ b/src/App/Platforms/iOS/AppDelegate.cs @@ -15,6 +15,7 @@ using CoreNFC; using Foundation; using Microsoft.Maui.Platform; using UIKit; +using UserNotifications; using WatchConnectivity; namespace Bit.iOS @@ -41,73 +42,78 @@ namespace Bit.iOS private IStateService _stateService; private IEventService _eventService; - private LazyResolve _deepLinkContext = new LazyResolve(); + private readonly LazyResolve _deepLinkContext = new LazyResolve(); public override bool FinishedLaunching(UIApplication app, NSDictionary options) { - InitApp(); - - _deviceActionService = ServiceContainer.Resolve("deviceActionService"); - _messagingService = ServiceContainer.Resolve("messagingService"); - _broadcasterService = ServiceContainer.Resolve("broadcasterService"); - _storageService = ServiceContainer.Resolve("storageService"); - _stateService = ServiceContainer.Resolve("stateService"); - _eventService = ServiceContainer.Resolve("eventService"); - - ConnectToWatchIfNeededAsync().FireAndForget(); - - _broadcasterService.Subscribe(nameof(AppDelegate), async (message) => + try { - try + InitApp(); + + _deviceActionService = ServiceContainer.Resolve("deviceActionService"); + _messagingService = ServiceContainer.Resolve("messagingService"); + _broadcasterService = ServiceContainer.Resolve("broadcasterService"); + _storageService = ServiceContainer.Resolve("storageService"); + _stateService = ServiceContainer.Resolve("stateService"); + _eventService = ServiceContainer.Resolve("eventService"); + + ConnectToWatchIfNeededAsync().FireAndForget(); + + _broadcasterService.Subscribe(nameof(AppDelegate), async (message) => { - if (message.Command == "startEventTimer") + try { - StartEventTimer(); - } - else if (message.Command == "stopEventTimer") - { - var task = StopEventTimerAsync(); - } - else if (message.Command is ThemeManager.UPDATED_THEME_MESSAGE_KEY) - { - MainThread.BeginInvokeOnMainThread(() => + if (message.Command == "startEventTimer") { - iOSCoreHelpers.AppearanceAdjustments(); - }); - } - else if (message.Command == "listenYubiKeyOTP") - { - iOSCoreHelpers.ListenYubiKey((bool)message.Data, _deviceActionService, _nfcSession, _nfcDelegate); - } - else if (message.Command == "unlocked") - { - var needsAutofillReplacement = await _storageService.GetAsync( - Core.Constants.AutofillNeedsIdentityReplacementKey); - if (needsAutofillReplacement.GetValueOrDefault()) - { - await ASHelpers.ReplaceAllIdentities(); + StartEventTimer(); } - } - else if (message.Command == "showAppExtension") - { - MainThread.BeginInvokeOnMainThread(() => ShowAppExtension((ExtensionPageViewModel)message.Data)); - } - else if (message.Command == "syncCompleted") - { - if (message.Data is Dictionary data && data.ContainsKey("successfully")) + else if (message.Command == "stopEventTimer") { - var success = data["successfully"] as bool?; - if (success.GetValueOrDefault() && _deviceActionService.SystemMajorVersion() >= 12) + var task = StopEventTimerAsync(); + } + else if (message.Command is ThemeManager.UPDATED_THEME_MESSAGE_KEY) + { + await MainThread.InvokeOnMainThreadAsync(() => + { + iOSCoreHelpers.AppearanceAdjustments(); + }); + } + else if (message.Command == "listenYubiKeyOTP" && message.Data is bool listen) + { + iOSCoreHelpers.ListenYubiKey(listen, _deviceActionService, _nfcSession, _nfcDelegate); + } + else if (message.Command == "unlocked") + { + var needsAutofillReplacement = await _storageService.GetAsync( + Core.Constants.AutofillNeedsIdentityReplacementKey); + if (needsAutofillReplacement.GetValueOrDefault()) { await ASHelpers.ReplaceAllIdentities(); } } - } - else if (message.Command == "addedCipher" || message.Command == "editedCipher" || - message.Command == "restoredCipher") - { - if (_deviceActionService.SystemMajorVersion() >= 12) + else if (message.Command == "showAppExtension") { + await MainThread.InvokeOnMainThreadAsync(() => ShowAppExtension((ExtensionPageViewModel)message.Data)); + } + else if (message.Command == "syncCompleted") + { + if (message.Data is Dictionary data && data.TryGetValue("successfully", out var value)) + { + var success = value as bool?; + if (success.GetValueOrDefault() && _deviceActionService.SystemMajorVersion() >= 12) + { + await ASHelpers.ReplaceAllIdentities(); + } + } + } + else if (message.Command == "addedCipher" || message.Command == "editedCipher" || + message.Command == "restoredCipher") + { + if (!UIDevice.CurrentDevice.CheckSystemVersion(12, 0)) + { + return; + } + if (await ASHelpers.IdentitiesCanIncremental()) { var cipherId = message.Data as string; @@ -125,11 +131,13 @@ namespace Bit.iOS } await ASHelpers.ReplaceAllIdentities(); } - } - else if (message.Command == "deletedCipher" || message.Command == "softDeletedCipher") - { - if (_deviceActionService.SystemMajorVersion() >= 12) + else if (message.Command == "deletedCipher" || message.Command == "softDeletedCipher") { + if (!UIDevice.CurrentDevice.CheckSystemVersion(12, 0)) + { + return; + } + if (await ASHelpers.IdentitiesCanIncremental()) { var identity = ASHelpers.ToCredentialIdentity( @@ -144,101 +152,145 @@ namespace Bit.iOS } await ASHelpers.ReplaceAllIdentities(); } - } - else if (message.Command == "logout") - { - if (_deviceActionService.SystemMajorVersion() >= 12) + else if (message.Command == "logout" && UIDevice.CurrentDevice.CheckSystemVersion(12, 0)) { await ASCredentialIdentityStore.SharedStore?.RemoveAllCredentialIdentitiesAsync(); } - } - else if ((message.Command == "softDeletedCipher" || message.Command == "restoredCipher") - && _deviceActionService.SystemMajorVersion() >= 12) - { - await ASHelpers.ReplaceAllIdentities(); - } - else if (message.Command == AppHelpers.VAULT_TIMEOUT_ACTION_CHANGED_MESSAGE_COMMAND) - { - var timeoutAction = await _stateService.GetVaultTimeoutActionAsync(); - if (timeoutAction == VaultTimeoutAction.Logout) - { - await ASCredentialIdentityStore.SharedStore?.RemoveAllCredentialIdentitiesAsync(); - } - else + else if ((message.Command == "softDeletedCipher" || message.Command == "restoredCipher") + && UIDevice.CurrentDevice.CheckSystemVersion(12, 0)) { await ASHelpers.ReplaceAllIdentities(); } + else if (message.Command == AppHelpers.VAULT_TIMEOUT_ACTION_CHANGED_MESSAGE_COMMAND) + { + var timeoutAction = await _stateService.GetVaultTimeoutActionAsync(); + if (timeoutAction == VaultTimeoutAction.Logout) + { + if (UIDevice.CurrentDevice.CheckSystemVersion(12, 0)) + { + await ASCredentialIdentityStore.SharedStore?.RemoveAllCredentialIdentitiesAsync(); + } + } + else + { + await ASHelpers.ReplaceAllIdentities(); + } + } } - } - catch (Exception ex) - { - LoggerHelper.LogEvenIfCantBeResolved(ex); - } - }); + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); + } + }); - var finishedLaunching = base.FinishedLaunching(app, options); + var finishedLaunching = base.FinishedLaunching(app, options); - ThemeManager.SetTheme(Microsoft.Maui.Controls.Application.Current.Resources); - iOSCoreHelpers.AppearanceAdjustments(); + ThemeManager.SetTheme(Microsoft.Maui.Controls.Application.Current.Resources); + iOSCoreHelpers.AppearanceAdjustments(); - return finishedLaunching; + return finishedLaunching; + } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); + throw; + } } public override void OnResignActivation(UIApplication uiApplication) { - if (UIApplication.SharedApplication.KeyWindow != null) + try { - var view = new UIView(UIApplication.SharedApplication.KeyWindow.Frame) + if (UIApplication.SharedApplication.KeyWindow != null) { - Tag = SPLASH_VIEW_TAG - }; - var backgroundView = new UIView(UIApplication.SharedApplication.KeyWindow.Frame) - { - BackgroundColor = ThemeManager.GetResourceColor("SplashBackgroundColor").ToPlatform() - }; - var logo = new UIImage(!ThemeManager.UsingLightTheme ? "logo_white.png" : "logo.png"); - var frame = new CGRect(0, 0, 280, 100); //Setting image width to avoid it being larger and getting cropped on smaller devices. This harcoded size should be good even for very small devices. - var imageView = new UIImageView(frame) - { - Image = logo, - Center = new CGPoint(view.Center.X, view.Center.Y - 30), - ContentMode = UIViewContentMode.ScaleAspectFit - }; - view.AddSubview(backgroundView); - view.AddSubview(imageView); - UIApplication.SharedApplication.KeyWindow.AddSubview(view); - UIApplication.SharedApplication.KeyWindow.BringSubviewToFront(view); - UIApplication.SharedApplication.KeyWindow.EndEditing(true); + var view = new UIView(UIApplication.SharedApplication.KeyWindow.Frame) + { + Tag = SPLASH_VIEW_TAG + }; + var backgroundView = new UIView(UIApplication.SharedApplication.KeyWindow.Frame) + { + BackgroundColor = ThemeManager.GetResourceColor("SplashBackgroundColor").ToPlatform() + }; + var logo = new UIImage(!ThemeManager.UsingLightTheme ? "logo_white.png" : "logo.png"); + var frame = new CGRect(0, 0, 280, 100); //Setting image width to avoid it being larger and getting cropped on smaller devices. This harcoded size should be good even for very small devices. + var imageView = new UIImageView(frame) + { + Image = logo, + Center = new CGPoint(view.Center.X, view.Center.Y - 30), + ContentMode = UIViewContentMode.ScaleAspectFit + }; + view.AddSubview(backgroundView); + view.AddSubview(imageView); + UIApplication.SharedApplication.KeyWindow.AddSubview(view); + UIApplication.SharedApplication.KeyWindow.BringSubviewToFront(view); + UIApplication.SharedApplication.KeyWindow.EndEditing(true); + } + base.OnResignActivation(uiApplication); + } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); + throw; } - base.OnResignActivation(uiApplication); } public override void DidEnterBackground(UIApplication uiApplication) { - if (_stateService != null && _deviceActionService != null) + try { - _stateService.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime()); - } + if (_stateService != null && _deviceActionService != null) + { + _stateService.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime()); + } - _messagingService?.Send("slept"); - base.DidEnterBackground(uiApplication); + _messagingService?.Send("slept"); + base.DidEnterBackground(uiApplication); + } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); + throw; + } } - public override void OnActivated(UIApplication uiApplication) + public override async void OnActivated(UIApplication uiApplication) { - base.OnActivated(uiApplication); - UIApplication.SharedApplication.ApplicationIconBadgeNumber = 0; - UIApplication.SharedApplication.KeyWindow? - .ViewWithTag(SPLASH_VIEW_TAG)? - .RemoveFromSuperview(); + try + { + base.OnActivated(uiApplication); - ThemeManager.UpdateThemeOnPagesAsync(); + if (UIDevice.CurrentDevice.CheckSystemVersion(17, 0)) + { + await UNUserNotificationCenter.Current.SetBadgeCountAsync(0); + } + else + { + UIApplication.SharedApplication.ApplicationIconBadgeNumber = 0; + } + + UIApplication.SharedApplication.KeyWindow? + .ViewWithTag(SPLASH_VIEW_TAG)? + .RemoveFromSuperview(); + + ThemeManager.UpdateThemeOnPagesAsync(); + } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); + } } public override void WillEnterForeground(UIApplication uiApplication) { - _messagingService?.Send(AppHelpers.RESUMED_MESSAGE_COMMAND); - base.WillEnterForeground(uiApplication); + try + { + _messagingService?.Send(AppHelpers.RESUMED_MESSAGE_COMMAND); + base.WillEnterForeground(uiApplication); + } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); + } } [Export("application:openURL:sourceApplication:annotation:")] @@ -249,15 +301,30 @@ namespace Bit.iOS public override bool OpenUrl(UIApplication app, NSUrl url, NSDictionary options) { - return _deepLinkContext.Value.OnNewUri(url) || base.OpenUrl(app, url, options); + try + { + return _deepLinkContext.Value.OnNewUri(url) || base.OpenUrl(app, url, options); + } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); + return false; + } } public override bool ContinueUserActivity(UIApplication application, NSUserActivity userActivity, UIApplicationRestorationHandler completionHandler) { - if (Microsoft.Maui.ApplicationModel.Platform.ContinueUserActivity(application, userActivity, completionHandler)) + try { - return true; + if (Microsoft.Maui.ApplicationModel.Platform.ContinueUserActivity(application, userActivity, completionHandler)) + { + return true; + } + } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); } return base.ContinueUserActivity(application, userActivity, completionHandler); } @@ -265,33 +332,68 @@ namespace Bit.iOS [Export("application:didFailToRegisterForRemoteNotificationsWithError:")] public void FailedToRegisterForRemoteNotifications(UIApplication application, NSError error) { - _pushHandler?.OnErrorReceived(error); + try + { + _pushHandler?.OnErrorReceived(error); + } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); + } } [Export("application:didRegisterForRemoteNotificationsWithDeviceToken:")] public void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken) { - _pushHandler?.OnRegisteredSuccess(deviceToken); + try + { + _pushHandler?.OnRegisteredSuccess(deviceToken); + } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); + } } [Export("application:didRegisterUserNotificationSettings:")] public void DidRegisterUserNotificationSettings(UIApplication application, UIUserNotificationSettings notificationSettings) { - application.RegisterForRemoteNotifications(); + try + { + application.RegisterForRemoteNotifications(); + } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); + } } [Export("application:didReceiveRemoteNotification:fetchCompletionHandler:")] public void DidReceiveRemoteNotification(UIApplication application, NSDictionary userInfo, Action completionHandler) { - _pushHandler?.OnMessageReceived(userInfo); + try + { + _pushHandler?.OnMessageReceived(userInfo); + } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); + } } [Export("application:didReceiveRemoteNotification:")] public void ReceivedRemoteNotification(UIApplication application, NSDictionary userInfo) { - _pushHandler?.OnMessageReceived(userInfo); + try + { + _pushHandler?.OnMessageReceived(userInfo); + } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); + } } public void InitApp() @@ -304,17 +406,6 @@ namespace Bit.iOS // Migration services ServiceContainer.Register("nativeLogService", new ConsoleLogService()); - // Note: This might cause a race condition. Investigate more. - //Task.Run(() => - //{ - // FFImageLoading.Forms.Platform.CachedImageRenderer.Init(); - // FFImageLoading.ImageService.Instance.Initialize(new FFImageLoading.Config.Configuration - // { - // FadeAnimationEnabled = false, - // FadeAnimationForCachedImages = false - // }); - //}); - iOSCoreHelpers.RegisterLocalServices(); RegisterPush(); var deviceActionService = ServiceContainer.Resolve("deviceActionService"); @@ -328,7 +419,7 @@ namespace Bit.iOS _nfcDelegate = new Core.NFCReaderDelegate((success, message) => _messagingService.Send("gotYubiKeyOTP", message)); - iOSCoreHelpers.Bootstrap(async () => await ApplyManagedSettingsAsync()); + iOSCoreHelpers.Bootstrap(ApplyManagedSettingsAsync); } private void RegisterPush() @@ -373,31 +464,45 @@ namespace Bit.iOS _eventTimer = null; MainThread.BeginInvokeOnMainThread(() => { - _eventTimer = NSTimer.CreateScheduledTimer(60, true, timer => + try { - var task = Task.Run(() => _eventService.UploadEventsAsync()); - }); + _eventTimer = NSTimer.CreateScheduledTimer(60, true, timer => + { + _eventService?.UploadEventsAsync().FireAndForget(); + }); + } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); + } }); } private async Task StopEventTimerAsync() { - _eventTimer?.Invalidate(); - _eventTimer?.Dispose(); - _eventTimer = null; - if (_eventBackgroundTaskId > 0) + try { + _eventTimer?.Invalidate(); + _eventTimer?.Dispose(); + _eventTimer = null; + if (_eventBackgroundTaskId > 0) + { + UIApplication.SharedApplication.EndBackgroundTask(_eventBackgroundTaskId); + _eventBackgroundTaskId = 0; + } + _eventBackgroundTaskId = UIApplication.SharedApplication.BeginBackgroundTask(() => + { + UIApplication.SharedApplication.EndBackgroundTask(_eventBackgroundTaskId); + _eventBackgroundTaskId = 0; + }); + await _eventService.UploadEventsAsync(); UIApplication.SharedApplication.EndBackgroundTask(_eventBackgroundTaskId); _eventBackgroundTaskId = 0; } - _eventBackgroundTaskId = UIApplication.SharedApplication.BeginBackgroundTask(() => + catch (Exception ex) { - UIApplication.SharedApplication.EndBackgroundTask(_eventBackgroundTaskId); - _eventBackgroundTaskId = 0; - }); - await _eventService.UploadEventsAsync(); - UIApplication.SharedApplication.EndBackgroundTask(_eventBackgroundTaskId); - _eventBackgroundTaskId = 0; + LoggerHelper.LogEvenIfCantBeResolved(ex); + } } private async Task ApplyManagedSettingsAsync() diff --git a/src/Core/App.xaml.cs b/src/Core/App.xaml.cs index 7deed659d..4f2fa1b3a 100644 --- a/src/Core/App.xaml.cs +++ b/src/Core/App.xaml.cs @@ -81,7 +81,7 @@ namespace Bit.App { get { - return Application.Current.Windows.OfType().FirstOrDefault(w => w.IsActive); + return Application.Current?.Windows.OfType().FirstOrDefault(w => w.IsActive); } } @@ -145,11 +145,14 @@ namespace Bit.App { get { - return Application.Current.MainPage; + return Application.Current?.MainPage; } set { - Application.Current.MainPage = value; + if (Application.Current != null) + { + Application.Current.MainPage = value; + } } } #endif diff --git a/src/Core/Pages/Accounts/HomePage.xaml.cs b/src/Core/Pages/Accounts/HomePage.xaml.cs index 11c5b36ba..9a418ac4d 100644 --- a/src/Core/Pages/Accounts/HomePage.xaml.cs +++ b/src/Core/Pages/Accounts/HomePage.xaml.cs @@ -153,7 +153,7 @@ namespace Bit.App.Pages private async Task StartEnvironmentAsync() { - await _accountListOverlay.HideAsync(); + await _accountListOverlay.HideAsync(); var page = new EnvironmentPage(); await Navigation.PushModalAsync(new NavigationPage(page)); } diff --git a/src/Core/Pages/Accounts/LockPage.xaml.cs b/src/Core/Pages/Accounts/LockPage.xaml.cs index 6861f7bc2..bd3be1858 100644 --- a/src/Core/Pages/Accounts/LockPage.xaml.cs +++ b/src/Core/Pages/Accounts/LockPage.xaml.cs @@ -81,7 +81,7 @@ namespace Bit.App.Pages { if (message.Command == Constants.ClearSensitiveFields) { - MainThread.BeginInvokeOnMainThread(_vm.ResetPinPasswordFields); + MainThread.BeginInvokeOnMainThread(() => _vm?.ResetPinPasswordFields()); } }); if (_appeared) diff --git a/src/Core/Pages/Accounts/LockPageViewModel.cs b/src/Core/Pages/Accounts/LockPageViewModel.cs index 2464524cf..b3bd61015 100644 --- a/src/Core/Pages/Accounts/LockPageViewModel.cs +++ b/src/Core/Pages/Accounts/LockPageViewModel.cs @@ -245,9 +245,9 @@ namespace Bit.App.Pages public async Task SubmitAsync() { - ShowPassword = false; try { + ShowPassword = false; var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile)); if (PinEnabled) { @@ -257,12 +257,15 @@ namespace Bit.App.Pages { await UnlockWithMasterPasswordAsync(kdfConfig); } - } catch (LegacyUserException) { await HandleLegacyUserAsync(); } + catch (Exception ex) + { + HandleException(ex); + } } private async Task UnlockWithPinAsync(KdfConfig kdfConfig) diff --git a/src/Core/Pages/Accounts/LoginPage.xaml.cs b/src/Core/Pages/Accounts/LoginPage.xaml.cs index 84f62879f..7139e57b1 100644 --- a/src/Core/Pages/Accounts/LoginPage.xaml.cs +++ b/src/Core/Pages/Accounts/LoginPage.xaml.cs @@ -3,6 +3,7 @@ using Bit.App.Utilities; using Bit.Core; using Bit.Core.Abstractions; using Bit.Core.Enums; +using Bit.Core.Services; using Bit.Core.Utilities; namespace Bit.App.Pages @@ -74,7 +75,7 @@ namespace Bit.App.Pages { if (message.Command == Constants.ClearSensitiveFields) { - MainThread.BeginInvokeOnMainThread(_vm.ResetPasswordField); + MainThread.BeginInvokeOnMainThread(() => _vm?.ResetPasswordField()); } }); _mainContent.Content = _mainLayout; @@ -188,12 +189,20 @@ namespace Bit.App.Pages private async Task LogInSuccessAsync() { - if (AppHelpers.SetAlternateMainPage(_appOptions)) + try { - return; + if (AppHelpers.SetAlternateMainPage(_appOptions)) + { + return; + } + var previousPage = await AppHelpers.ClearPreviousPage(); + App.MainPage = new TabsPage(_appOptions, previousPage); + } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); + throw; } - var previousPage = await AppHelpers.ClearPreviousPage(); - App.MainPage = new TabsPage(_appOptions, previousPage); } private async Task UpdateTempPasswordAsync() diff --git a/src/Core/Pages/Accounts/LoginPasswordlessRequestPage.xaml.cs b/src/Core/Pages/Accounts/LoginPasswordlessRequestPage.xaml.cs index ad5e3aba3..a88f27a03 100644 --- a/src/Core/Pages/Accounts/LoginPasswordlessRequestPage.xaml.cs +++ b/src/Core/Pages/Accounts/LoginPasswordlessRequestPage.xaml.cs @@ -1,6 +1,7 @@ using Bit.App.Models; using Bit.App.Utilities; using Bit.Core.Enums; +using Bit.Core.Services; namespace Bit.App.Pages { @@ -48,12 +49,20 @@ namespace Bit.App.Pages private async Task LogInSuccessAsync() { - if (AppHelpers.SetAlternateMainPage(_appOptions)) + try { - return; + if (AppHelpers.SetAlternateMainPage(_appOptions)) + { + return; + } + var previousPage = await AppHelpers.ClearPreviousPage(); + App.MainPage = new TabsPage(_appOptions, previousPage); + } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); + throw; } - var previousPage = await AppHelpers.ClearPreviousPage(); - App.MainPage = new TabsPage(_appOptions, previousPage); } private async Task UpdateTempPasswordAsync() diff --git a/src/Core/Pages/Accounts/LoginSsoPage.xaml.cs b/src/Core/Pages/Accounts/LoginSsoPage.xaml.cs index 3760f961e..1c1402a8b 100644 --- a/src/Core/Pages/Accounts/LoginSsoPage.xaml.cs +++ b/src/Core/Pages/Accounts/LoginSsoPage.xaml.cs @@ -1,6 +1,7 @@ using Bit.App.Models; using Bit.App.Utilities; using Bit.Core.Abstractions; +using Bit.Core.Services; using Bit.Core.Utilities; namespace Bit.App.Pages @@ -89,16 +90,30 @@ namespace Bit.App.Pages private async Task StartTwoFactorAsync() { - RestoreAppOptionsFromCopy(); - var page = new TwoFactorPage(true, _appOptions, _vm.OrgIdentifier); - await Navigation.PushModalAsync(new NavigationPage(page)); + try + { + RestoreAppOptionsFromCopy(); + var page = new TwoFactorPage(true, _appOptions, _vm.OrgIdentifier); + await Navigation.PushModalAsync(new NavigationPage(page)); + } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); + } } private async Task StartSetPasswordAsync() { - RestoreAppOptionsFromCopy(); - var page = new SetPasswordPage(_appOptions, _vm.OrgIdentifier); - await Navigation.PushModalAsync(new NavigationPage(page)); + try + { + RestoreAppOptionsFromCopy(); + var page = new SetPasswordPage(_appOptions, _vm.OrgIdentifier); + await Navigation.PushModalAsync(new NavigationPage(page)); + } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); + } } private async Task UpdateTempPasswordAsync() @@ -115,16 +130,23 @@ namespace Bit.App.Pages private async Task SsoAuthSuccessAsync() { - RestoreAppOptionsFromCopy(); - await AppHelpers.ClearPreviousPage(); + try + { + RestoreAppOptionsFromCopy(); + await AppHelpers.ClearPreviousPage(); - if (await _vaultTimeoutService.IsLockedAsync()) - { - App.MainPage = new NavigationPage(new LockPage(_appOptions)); + if (await _vaultTimeoutService.IsLockedAsync()) + { + App.MainPage = new NavigationPage(new LockPage(_appOptions)); + } + else + { + App.MainPage = new TabsPage(_appOptions, null); + } } - else + catch (Exception ex) { - App.MainPage = new TabsPage(_appOptions, null); + LoggerHelper.LogEvenIfCantBeResolved(ex); } } } diff --git a/src/Core/Pages/Accounts/SetPasswordPage.xaml.cs b/src/Core/Pages/Accounts/SetPasswordPage.xaml.cs index 5504b9d43..19013dba4 100644 --- a/src/Core/Pages/Accounts/SetPasswordPage.xaml.cs +++ b/src/Core/Pages/Accounts/SetPasswordPage.xaml.cs @@ -1,5 +1,6 @@ using Bit.App.Models; using Bit.App.Utilities; +using Bit.Core.Services; namespace Bit.App.Pages { @@ -64,12 +65,19 @@ namespace Bit.App.Pages private async Task SetPasswordSuccessAsync() { - if (AppHelpers.SetAlternateMainPage(_appOptions)) + try { - return; + if (AppHelpers.SetAlternateMainPage(_appOptions)) + { + return; + } + var previousPage = await AppHelpers.ClearPreviousPage(); + App.MainPage = new TabsPage(_appOptions, previousPage); + } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); } - var previousPage = await AppHelpers.ClearPreviousPage(); - App.MainPage = new TabsPage(_appOptions, previousPage); } } } diff --git a/src/Core/Pages/Accounts/TwoFactorPage.xaml.cs b/src/Core/Pages/Accounts/TwoFactorPage.xaml.cs index 1b218dc3d..9322936ce 100644 --- a/src/Core/Pages/Accounts/TwoFactorPage.xaml.cs +++ b/src/Core/Pages/Accounts/TwoFactorPage.xaml.cs @@ -2,6 +2,7 @@ using Bit.App.Models; using Bit.App.Utilities; using Bit.Core.Abstractions; +using Bit.Core.Services; using Bit.Core.Utilities; namespace Bit.App.Pages @@ -63,11 +64,11 @@ namespace Bit.App.Pages if (_vm.YubikeyMethod && !string.IsNullOrWhiteSpace(token) && token.Length == 44 && !token.Contains(" ")) { - MainThread.BeginInvokeOnMainThread(async () => + MainThread.BeginInvokeOnMainThread(() => { _vm.Token = token; - await _vm.SubmitAsync(); }); + _vm.SubmitCommand.Execute(null); } } else if (message.Command == "resumeYubiKey") @@ -124,12 +125,9 @@ namespace Bit.App.Pages return base.OnBackButtonPressed(); } - private async void Continue_Clicked(object sender, EventArgs e) + private void Continue_Clicked(object sender, EventArgs e) { - if (DoOnce()) - { - await _vm.SubmitAsync(); - } + _vm.SubmitCommand.Execute(null); } private async void Methods_Clicked(object sender, EventArgs e) @@ -158,17 +156,24 @@ namespace Bit.App.Pages private async void TryAgain_Clicked(object sender, EventArgs e) { - if (DoOnce()) + try { - if (_vm.Fido2Method) + if (DoOnce()) { - await _vm.Fido2AuthenticateAsync(); - } - else if (_vm.YubikeyMethod) - { - _messagingService.Send("listenYubiKeyOTP", true); + if (_vm.Fido2Method) + { + await _vm.Fido2AuthenticateAsync(); + } + else if (_vm.YubikeyMethod) + { + _messagingService.Send("listenYubiKeyOTP", true); + } } } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); + } } private async Task StartSetPasswordAsync() diff --git a/src/Core/Pages/Accounts/TwoFactorPageViewModel.cs b/src/Core/Pages/Accounts/TwoFactorPageViewModel.cs index 0952c5be4..319d55160 100644 --- a/src/Core/Pages/Accounts/TwoFactorPageViewModel.cs +++ b/src/Core/Pages/Accounts/TwoFactorPageViewModel.cs @@ -1,25 +1,16 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Threading.Tasks; +using System.Net; using System.Windows.Input; using Bit.App.Abstractions; -using Bit.Core.Resources.Localization; using Bit.App.Utilities; using Bit.Core.Abstractions; using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.Models.Domain; using Bit.Core.Models.Request; -using Bit.Core.Services; +using Bit.Core.Resources.Localization; using Bit.Core.Utilities; using Newtonsoft.Json; -using Microsoft.Maui.Authentication; -using Microsoft.Maui.Controls; -using Microsoft.Maui; - namespace Bit.App.Pages { public class TwoFactorPageViewModel : CaptchaProtectedViewModel @@ -62,7 +53,7 @@ namespace Bit.App.Pages _deviceTrustCryptoService = ServiceContainer.Resolve(); PageTitle = AppResources.TwoStepLogin; - SubmitCommand = new Command(async () => await SubmitAsync()); + SubmitCommand = CreateDefaultAsyncRelayCommand(() => MainThread.InvokeOnMainThreadAsync(async () => await SubmitAsync()), allowsMultipleExecutions: false); MoreCommand = CreateDefaultAsyncRelayCommand(MoreAsync, onException: _logger.Exception, allowsMultipleExecutions: false); } @@ -91,8 +82,7 @@ namespace Bit.App.Pages public bool TotpMethod => AuthenticatorMethod || EmailMethod; - public bool ShowTryAgain => (YubikeyMethod && // TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes -Device.RuntimePlatform == Device.iOS) || Fido2Method; + public bool ShowTryAgain => (YubikeyMethod && DeviceInfo.Platform == DevicePlatform.iOS) || Fido2Method; public bool ShowContinue { @@ -106,9 +96,11 @@ Device.RuntimePlatform == Device.iOS) || Fido2Method; set => SetProperty(ref _enableContinue, value); } - public string YubikeyInstruction => // TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes -Device.RuntimePlatform == Device.iOS ? AppResources.YubiKeyInstructionIos : - AppResources.YubiKeyInstruction; +#if IOS + public string YubikeyInstruction => AppResources.YubiKeyInstructionIos; +#else + public string YubikeyInstruction => AppResources.YubiKeyInstruction; +#endif public TwoFactorProviderType? SelectedProviderType { @@ -124,7 +116,7 @@ Device.RuntimePlatform == Device.iOS ? AppResources.YubiKeyInstructionIos : nameof(ShowTryAgain), }); } - public Command SubmitCommand { get; } + public ICommand SubmitCommand { get; } public ICommand MoreCommand { get; } public Action TwoFactorAuthSuccessAction { get; set; } public Action LockAction { get; set; } @@ -186,7 +178,7 @@ Device.RuntimePlatform == Device.iOS ? AppResources.YubiKeyInstructionIos : page.DuoWebView.RegisterAction(sig => { Token = sig; - Device.BeginInvokeOnMainThread(async () => await SubmitAsync()); + SubmitCommand.Execute(null); }); break; case TwoFactorProviderType.Email: @@ -213,68 +205,76 @@ Device.RuntimePlatform == Device.iOS ? AppResources.YubiKeyInstructionIos : public async Task Fido2AuthenticateAsync(Dictionary providerData = null) { - await _deviceActionService.ShowLoadingAsync(AppResources.Validating); - - if (providerData == null) - { - providerData = _authService.TwoFactorProvidersData[TwoFactorProviderType.Fido2WebAuthn]; - } - - var callbackUri = "bitwarden://webauthn-callback"; - var data = AppHelpers.EncodeDataParameter(new - { - callbackUri = callbackUri, - data = JsonConvert.SerializeObject(providerData), - headerText = AppResources.Fido2Title, - btnText = AppResources.Fido2AuthenticateWebAuthn, - btnReturnText = AppResources.Fido2ReturnToApp, - }); - - var url = _webVaultUrl + "/webauthn-mobile-connector.html?" + "data=" + data + - "&parent=" + Uri.EscapeDataString(callbackUri) + "&v=2"; - - WebAuthenticatorResult authResult = null; try { - var options = new WebAuthenticatorOptions - { - Url = new Uri(url), - CallbackUrl = new Uri(callbackUri), - PrefersEphemeralWebBrowserSession = true, - }; - authResult = await WebAuthenticator.AuthenticateAsync(options); - } - catch (TaskCanceledException) - { - // user canceled - await _deviceActionService.HideLoadingAsync(); - return; - } + await _deviceActionService.ShowLoadingAsync(AppResources.Validating); - string response = null; - if (authResult != null && authResult.Properties.TryGetValue("data", out var resultData)) - { - response = Uri.UnescapeDataString(resultData); - } - if (!string.IsNullOrWhiteSpace(response)) - { - Token = response; - await SubmitAsync(false); - } - else - { - await _deviceActionService.HideLoadingAsync(); - if (authResult != null && authResult.Properties.TryGetValue("error", out var resultError)) + if (providerData == null) { - var message = AppResources.Fido2CheckBrowser + "\n\n" + resultError; - await _platformUtilsService.ShowDialogAsync(message, AppResources.AnErrorHasOccurred, - AppResources.Ok); + providerData = _authService.TwoFactorProvidersData[TwoFactorProviderType.Fido2WebAuthn]; + } + + var callbackUri = "bitwarden://webauthn-callback"; + var data = AppHelpers.EncodeDataParameter(new + { + callbackUri = callbackUri, + data = JsonConvert.SerializeObject(providerData), + headerText = AppResources.Fido2Title, + btnText = AppResources.Fido2AuthenticateWebAuthn, + btnReturnText = AppResources.Fido2ReturnToApp, + }); + + var url = _webVaultUrl + "/webauthn-mobile-connector.html?" + "data=" + data + + "&parent=" + Uri.EscapeDataString(callbackUri) + "&v=2"; + + WebAuthenticatorResult authResult = null; + try + { + var options = new WebAuthenticatorOptions + { + Url = new Uri(url), + CallbackUrl = new Uri(callbackUri), + PrefersEphemeralWebBrowserSession = true, + }; + authResult = await WebAuthenticator.AuthenticateAsync(options); + } + catch (TaskCanceledException) + { + // user canceled + await _deviceActionService.HideLoadingAsync(); + return; + } + + string response = null; + if (authResult != null && authResult.Properties.TryGetValue("data", out var resultData)) + { + response = Uri.UnescapeDataString(resultData); + } + if (!string.IsNullOrWhiteSpace(response)) + { + Token = response; + await SubmitAsync(false); } else { - await _platformUtilsService.ShowDialogAsync(AppResources.Fido2CheckBrowser, - AppResources.AnErrorHasOccurred, AppResources.Ok); + await _deviceActionService.HideLoadingAsync(); + if (authResult != null && authResult.Properties.TryGetValue("error", out var resultError)) + { + var message = AppResources.Fido2CheckBrowser + "\n\n" + resultError; + await _platformUtilsService.ShowDialogAsync(message, AppResources.AnErrorHasOccurred, + AppResources.Ok); + } + else + { + await _platformUtilsService.ShowDialogAsync(AppResources.Fido2CheckBrowser, + AppResources.AnErrorHasOccurred, AppResources.Ok); + } } + + } + catch (Exception ex) + { + HandleException(ex); } } diff --git a/src/Core/Pages/BaseContentPage.cs b/src/Core/Pages/BaseContentPage.cs index d04c8a852..8aa3b453c 100644 --- a/src/Core/Pages/BaseContentPage.cs +++ b/src/Core/Pages/BaseContentPage.cs @@ -170,8 +170,15 @@ namespace Bit.App.Pages { Task.Run(async () => { - await Task.Delay(ShowModalAnimationDelay); - MainThread.BeginInvokeOnMainThread(() => input.Focus()); + try + { + await Task.Delay(ShowModalAnimationDelay); + MainThread.BeginInvokeOnMainThread(() => input.Focus()); + } + catch (Exception ex) + { + _logger.Value.Exception(ex); + } }); } diff --git a/src/iOS.Core/Handlers/CustomTabbedHandler.cs b/src/iOS.Core/Handlers/CustomTabbedHandler.cs index aa3a6f4ad..694720534 100644 --- a/src/iOS.Core/Handlers/CustomTabbedHandler.cs +++ b/src/iOS.Core/Handlers/CustomTabbedHandler.cs @@ -1,5 +1,4 @@ -using Bit.App.Abstractions; -using Bit.App.Pages; +using Bit.App.Pages; using Bit.App.Utilities; using Bit.Core.Abstractions; using Bit.Core.Utilities; @@ -13,7 +12,7 @@ namespace Bit.iOS.Core.Handlers public partial class CustomTabbedHandler : TabbedRenderer { private IBroadcasterService _broadcasterService; - private UITabBarItem _previousSelectedItem; + private UITabBarItem? _previousSelectedItem; public CustomTabbedHandler() { @@ -73,8 +72,7 @@ namespace Bit.iOS.Core.Handlers private void UpdateTabBarAppearance() { // https://developer.apple.com/forums/thread/682420 - var deviceActionService = ServiceContainer.Resolve("deviceActionService"); - if (deviceActionService.SystemMajorVersion() >= 15) + if (UIDevice.CurrentDevice.CheckSystemVersion(15,0)) { var appearance = new UITabBarAppearance(); appearance.ConfigureWithOpaqueBackground(); diff --git a/src/iOS.Core/Services/LocalizeService.cs b/src/iOS.Core/Services/LocalizeService.cs index 88f8dde94..287e44e2a 100644 --- a/src/iOS.Core/Services/LocalizeService.cs +++ b/src/iOS.Core/Services/LocalizeService.cs @@ -1,5 +1,4 @@ -using System; -using System.Diagnostics; +using System.Diagnostics; using System.Globalization; using Bit.App.Abstractions; using Bit.App.Models; @@ -20,7 +19,7 @@ namespace Bit.iOS.Core.Services } // This gets called a lot - try/catch can be expensive so consider caching or something - CultureInfo ci = null; + CultureInfo? ci; try { ci = new CultureInfo(netLanguage); @@ -108,7 +107,7 @@ namespace Bit.iOS.Core.Services { df.Locale = NSLocale.CurrentLocale; df.DateStyle = NSDateFormatterStyle.Short; - return df.StringFor((NSDate)date); + return df.StringFor((NSDate?)date); } } @@ -118,7 +117,7 @@ namespace Bit.iOS.Core.Services { df.Locale = NSLocale.CurrentLocale; df.TimeStyle = NSDateFormatterStyle.Short; - return df.StringFor((NSDate)time); + return df.StringFor((NSDate?)time); } } } diff --git a/src/iOS.Core/Utilities/DictionaryExtensions.cs b/src/iOS.Core/Utilities/DictionaryExtensions.cs index de3387237..b88e7411d 100644 --- a/src/iOS.Core/Utilities/DictionaryExtensions.cs +++ b/src/iOS.Core/Utilities/DictionaryExtensions.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Bit.Core.Models.Domain; -using Foundation; +using Foundation; using Newtonsoft.Json; namespace Bit.iOS.Core.Utilities @@ -15,6 +11,7 @@ namespace Bit.iOS.Core.Utilities } public static NSDictionary ToNSDictionary(this Dictionary dict, Func keyConverter, Func valueConverter) + where KFrom : notnull where KTo : NSObject where VTo : NSObject { @@ -23,19 +20,20 @@ namespace Bit.iOS.Core.Utilities return NSDictionary.FromObjectsAndKeys(NSValues, NSKeys, NSKeys.Count()); } - public static Dictionary ToDictionary(this NSDictionary nsDict) + public static Dictionary ToDictionary(this NSDictionary nsDict) { - return nsDict.ToDictionary(v => v?.ToString() as object); + return nsDict.ToDictionary(v => v?.ToString()); } - public static Dictionary ToDictionary(this NSDictionary nsDict, Func valueTransformer) + public static Dictionary ToDictionary(this NSDictionary nsDict, Func valueTransformer) { return nsDict.ToDictionary(k => k.ToString(), v => valueTransformer(v)); } - public static Dictionary ToDictionary(this NSDictionary nsDict, Func keyConverter, Func valueConverter) + public static Dictionary ToDictionary(this NSDictionary nsDict, Func keyConverter, Func valueConverter) where KFrom : NSObject where VFrom : NSObject + where KTo : notnull { var keys = nsDict.Keys.Select(k => keyConverter(k)).ToArray(); var values = nsDict.Values.Select(v => valueConverter(v)).ToArray(); diff --git a/src/iOS.Core/Utilities/WCSessionManager.cs b/src/iOS.Core/Utilities/WCSessionManager.cs index e3e0375cd..d4d116b04 100644 --- a/src/iOS.Core/Utilities/WCSessionManager.cs +++ b/src/iOS.Core/Utilities/WCSessionManager.cs @@ -1,14 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading.Tasks; -using Bit.Core.Models.Domain; +using System.Diagnostics; using Bit.Core.Services; using Bit.iOS.Core.Utilities; using Foundation; using Newtonsoft.Json; -using ObjCRuntime; namespace WatchConnectivity { @@ -17,35 +11,45 @@ namespace WatchConnectivity // Setup is converted from https://www.natashatherobot.com/watchconnectivity-say-hello-to-wcsession/ // with some extra bits private static readonly WCSessionManager sharedManager = new WCSessionManager(); - private static WCSession session = WCSession.IsSupported ? WCSession.DefaultSession : null; + private static WCSession? session = WCSession.IsSupported ? WCSession.DefaultSession : null; - public event WCSessionReceiveDataHandler OnApplicationContextUpdated; - public event WCSessionReceiveDataHandler OnMessagedReceived; - public delegate void WCSessionReceiveDataHandler(WCSession session, Dictionary data); + public event WCSessionReceiveDataHandler? OnApplicationContextUpdated; + public event WCSessionReceiveDataHandler? OnMessagedReceived; + public delegate void WCSessionReceiveDataHandler(WCSession session, Dictionary data); - WCSessionUserInfoTransfer _transf; + WCSessionUserInfoTransfer? _transf; - private WCSession validSession + private WCSession? validSession { get { + if (session is null) + { + return null; + } + Debug.WriteLine($"Paired status:{(session.Paired ? '✓' : '✗')}\n"); Debug.WriteLine($"Watch App Installed status:{(session.WatchAppInstalled ? '✓' : '✗')}\n"); return (session.Paired && session.WatchAppInstalled) ? session : null; } } - private WCSession validReachableSession + private WCSession? validReachableSession { get { + if (session is null) + { + return null; + } + return session.Reachable ? validSession : null; } } public bool IsValidSession => validSession != null; - public bool IsSessionReachable => session.Reachable; + public bool IsSessionReachable => session?.Reachable ?? false; public bool IsSessionActivated => validSession?.ActivationState == WCSessionActivationState.Activated; @@ -71,7 +75,7 @@ namespace WatchConnectivity public override void SessionReachabilityDidChange(WCSession session) { - Debug.WriteLine($"Watch connectivity Reachable:{(session.Reachable ? '✓' : '✗')}"); + Debug.WriteLine($"Watch connectivity Reachable:{(session?.Reachable == true ? '✓' : '✗')}"); } public void SendBackgroundHighPriorityMessage(NSDictionary applicationContext) @@ -102,7 +106,7 @@ namespace WatchConnectivity public void SendBackgroundFifoHighPriorityMessage(Dictionary message) { - if(validSession is null || validSession.ActivationState != WCSessionActivationState.Activated) + if (session is null || validSession is null || validSession.ActivationState != WCSessionActivationState.Activated) { return; } @@ -112,6 +116,10 @@ namespace WatchConnectivity Debug.WriteLine("Started transferring user info"); _transf = session.TransferUserInfo(message.ToNSDictionary()); + if (_transf is null) + { + return; + } Task.Run(async () => { @@ -136,7 +144,7 @@ namespace WatchConnectivity if (OnApplicationContextUpdated != null) { var keys = applicationContext.Keys.Select(k => k.ToString()).ToArray(); - var values = applicationContext.Values.Select(v => JsonConvert.DeserializeObject(v.ToString())).ToArray(); + var values = applicationContext.Values.Select(v => v != null ? JsonConvert.DeserializeObject(v.ToString()) : null).ToArray(); var dictionary = keys.Zip(values, (k, v) => new { Key = k, Value = v }) .ToDictionary(x => x.Key, x => x.Value); diff --git a/src/iOS.Core/Utilities/iOSCoreHelpers.cs b/src/iOS.Core/Utilities/iOSCoreHelpers.cs index dd2efa399..a9efe2f03 100644 --- a/src/iOS.Core/Utilities/iOSCoreHelpers.cs +++ b/src/iOS.Core/Utilities/iOSCoreHelpers.cs @@ -116,9 +116,13 @@ namespace Bit.iOS.Core.Utilities ServiceContainer.Register("nativeLogService", new ConsoleLogService()); } - ILogger logger = null; - if (ServiceContainer.Resolve("logger", true) == null) + ILogger? logger = null; + if (ServiceContainer.TryResolve(out var resolvedLogger)) { + logger = resolvedLogger; + } + else + { #if DEBUG logger = DebugLogger.Instance; #else @@ -129,6 +133,12 @@ namespace Bit.iOS.Core.Utilities var preferencesStorage = new PreferencesStorageService(AppGroupId); var appGroupContainer = new NSFileManager().GetContainerUrl(AppGroupId); + if (appGroupContainer?.Path is null) + { + var nreAppGroupContainer = new NullReferenceException("appGroupContainer or its Path is null when registering local services"); + logger!.Exception(nreAppGroupContainer); + throw nreAppGroupContainer; + } var liteDbStorage = new LiteDbStorageService( Path.Combine(appGroupContainer.Path, "Library", "bitwarden.db")); var localizeService = new LocalizeService(); @@ -187,14 +197,14 @@ namespace Bit.iOS.Core.Utilities ServiceContainer.Resolve())); } - public static void Bootstrap(Func postBootstrapFunc = null) + public static void Bootstrap(Func? postBootstrapFunc = null) { var locale = ServiceContainer.Resolve().GetLocale(); (ServiceContainer.Resolve("i18nService") as MobileI18nService) - .Init(locale != null ? new System.Globalization.CultureInfo(locale) : null); + ?.Init(locale != null ? new System.Globalization.CultureInfo(locale) : null); ServiceContainer.Resolve("authService").Init(); (ServiceContainer. - Resolve("platformUtilsService") as MobilePlatformUtilsService).Init(); + Resolve("platformUtilsService") as MobilePlatformUtilsService)?.Init(); var accountsManager = new AccountsManager( ServiceContainer.Resolve("broadcasterService"), @@ -231,20 +241,31 @@ namespace Bit.iOS.Core.Utilities if (message.Command == "showDialog") { var details = message.Data as DialogDetails; + if (details is null) + { + return; + } var confirmText = string.IsNullOrWhiteSpace(details.ConfirmText) ? AppResources.Ok : details.ConfirmText; NSRunLoop.Main.BeginInvokeOnMainThread(async () => { - var result = await deviceActionService.DisplayAlertAsync(details.Title, details.Text, - details.CancelText, confirmText); - var confirmed = result == details.ConfirmText; - messagingService.Send("showDialogResolve", new Tuple(details.DialogId, confirmed)); + try + { + var result = await deviceActionService.DisplayAlertAsync(details.Title, details.Text, + details.CancelText, confirmText); + var confirmed = result == details.ConfirmText; + messagingService.Send("showDialogResolve", new Tuple(details.DialogId, confirmed)); + } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); + } }); } - else if (message.Command == "listenYubiKeyOTP") + else if (message.Command == "listenYubiKeyOTP" && message.Data is bool listen) { - ListenYubiKey((bool)message.Data, deviceActionService, nfcSession, nfcDelegate); + ListenYubiKey(listen, deviceActionService, nfcSession, nfcDelegate); } }); } @@ -268,29 +289,36 @@ namespace Bit.iOS.Core.Utilities } } - private static async Task BootstrapAsync(Func postBootstrapFunc = null) + private static async Task BootstrapAsync(Func? postBootstrapFunc = null) { - await ServiceContainer.Resolve("environmentService").SetUrlsFromStorageAsync(); - - InitializeAppSetup(); - // TODO: Update when https://github.com/bitwarden/mobile/pull/1662 gets merged - var deleteAccountActionFlowExecutioner = new DeleteAccountActionFlowExecutioner( - ServiceContainer.Resolve("apiService"), - ServiceContainer.Resolve("messagingService"), - ServiceContainer.Resolve("platformUtilsService"), - ServiceContainer.Resolve("deviceActionService"), - ServiceContainer.Resolve("logger")); - ServiceContainer.Register("deleteAccountActionFlowExecutioner", deleteAccountActionFlowExecutioner); - - var verificationActionsFlowHelper = new VerificationActionsFlowHelper( - ServiceContainer.Resolve("passwordRepromptService"), - ServiceContainer.Resolve("cryptoService"), - ServiceContainer.Resolve()); - ServiceContainer.Register("verificationActionsFlowHelper", verificationActionsFlowHelper); - - if (postBootstrapFunc != null) + try { - await postBootstrapFunc.Invoke(); + await ServiceContainer.Resolve("environmentService").SetUrlsFromStorageAsync(); + + InitializeAppSetup(); + // TODO: Update when https://github.com/bitwarden/mobile/pull/1662 gets merged + var deleteAccountActionFlowExecutioner = new DeleteAccountActionFlowExecutioner( + ServiceContainer.Resolve("apiService"), + ServiceContainer.Resolve("messagingService"), + ServiceContainer.Resolve("platformUtilsService"), + ServiceContainer.Resolve("deviceActionService"), + ServiceContainer.Resolve("logger")); + ServiceContainer.Register("deleteAccountActionFlowExecutioner", deleteAccountActionFlowExecutioner); + + var verificationActionsFlowHelper = new VerificationActionsFlowHelper( + ServiceContainer.Resolve("passwordRepromptService"), + ServiceContainer.Resolve("cryptoService"), + ServiceContainer.Resolve()); + ServiceContainer.Register("verificationActionsFlowHelper", verificationActionsFlowHelper); + + if (postBootstrapFunc != null) + { + await postBootstrapFunc.Invoke(); + } + } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); } } diff --git a/src/iOS.Core/Views/ExtensionSearchDelegate.cs b/src/iOS.Core/Views/ExtensionSearchDelegate.cs index 3fa4ff64a..b3753d511 100644 --- a/src/iOS.Core/Views/ExtensionSearchDelegate.cs +++ b/src/iOS.Core/Views/ExtensionSearchDelegate.cs @@ -1,6 +1,4 @@ -using System; -using System.Threading; -using System.Threading.Tasks; +using Bit.Core.Services; using Foundation; using UIKit; @@ -9,7 +7,7 @@ namespace Bit.iOS.Core.Views public class ExtensionSearchDelegate : UISearchBarDelegate { private readonly UITableView _tableView; - private CancellationTokenSource _filterResultsCancellationTokenSource; + private CancellationTokenSource? _filterResultsCancellationTokenSource; public ExtensionSearchDelegate(UITableView tableView) { @@ -23,25 +21,34 @@ namespace Bit.iOS.Core.Views { NSRunLoop.Main.BeginInvokeOnMainThread(async () => { - if (!string.IsNullOrWhiteSpace(searchText)) - { - await Task.Delay(300); - if (searchText != searchBar.Text) - { - return; - } - else - { - _filterResultsCancellationTokenSource?.Cancel(); - } - } try { - ((ExtensionTableSource)_tableView.Source).FilterResults(searchText, cts.Token); - _tableView.ReloadData(); + if (!string.IsNullOrWhiteSpace(searchText)) + { + await Task.Delay(300); + if (searchText != searchBar.Text) + { + return; + } + else + { + _filterResultsCancellationTokenSource?.Cancel(); + } + } + try + { + ((ExtensionTableSource)_tableView.Source).FilterResults(searchText, cts.Token); + _tableView.ReloadData(); + } + catch (OperationCanceledException) { } + _filterResultsCancellationTokenSource = cts; + } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); + _filterResultsCancellationTokenSource?.Cancel(); + cts?.Cancel(); } - catch (OperationCanceledException) { } - _filterResultsCancellationTokenSource = cts; }); }, cts.Token); } diff --git a/src/iOS.Extension/LoadingViewController.cs b/src/iOS.Extension/LoadingViewController.cs index 4daeb7670..85f77329a 100644 --- a/src/iOS.Extension/LoadingViewController.cs +++ b/src/iOS.Extension/LoadingViewController.cs @@ -479,16 +479,23 @@ namespace Bit.iOS.Extension { NSRunLoop.Main.BeginInvokeOnMainThread(async () => { - if (await IsAuthed()) + try { - var stateService = ServiceContainer.Resolve("stateService"); - await AppHelpers.LogOutAsync(await stateService.GetActiveUserIdAsync()); - var deviceActionService = ServiceContainer.Resolve("deviceActionService"); - if (deviceActionService.SystemMajorVersion() >= 12) + if (await IsAuthed()) { - await ASCredentialIdentityStore.SharedStore?.RemoveAllCredentialIdentitiesAsync(); + var stateService = ServiceContainer.Resolve("stateService"); + await AppHelpers.LogOutAsync(await stateService.GetActiveUserIdAsync()); + if (UIDevice.CurrentDevice.CheckSystemVersion(12, 0)) + { + await ASCredentialIdentityStore.SharedStore?.RemoveAllCredentialIdentitiesAsync(); + } } } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); + throw; + } }); } diff --git a/src/iOS.ShareExtension/LoadingViewController.cs b/src/iOS.ShareExtension/LoadingViewController.cs index c77582ca1..fc3ae0e2d 100644 --- a/src/iOS.ShareExtension/LoadingViewController.cs +++ b/src/iOS.ShareExtension/LoadingViewController.cs @@ -274,14 +274,22 @@ namespace Bit.iOS.ShareExtension { NSRunLoop.Main.BeginInvokeOnMainThread(async () => { - if (await IsAuthed()) + try { - await AppHelpers.LogOutAsync(await _stateService.Value.GetActiveUserIdAsync()); - if (UIDevice.CurrentDevice.CheckSystemVersion(12, 0)) + if (await IsAuthed()) { - await ASCredentialIdentityStore.SharedStore?.RemoveAllCredentialIdentitiesAsync(); + await AppHelpers.LogOutAsync(await _stateService.Value.GetActiveUserIdAsync()); + if (UIDevice.CurrentDevice.CheckSystemVersion(12, 0)) + { + await ASCredentialIdentityStore.SharedStore?.RemoveAllCredentialIdentitiesAsync(); + } } } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); + throw; + } }); }