using System; using System.Drawing; using System.Diagnostics; using Bit.App.Abstractions; using Bit.App.Repositories; using Bit.App.Services; using Bit.iOS.Core.Services; using Foundation; using Microsoft.Practices.Unity; using UIKit; using XLabs.Ioc; using XLabs.Ioc.Unity; using Bit.iOS.Core; using Newtonsoft.Json; using Bit.iOS.Extension.Models; using MobileCoreServices; using Plugin.Settings.Abstractions; using Plugin.Connectivity; using Plugin.Fingerprint; using Bit.iOS.Core.Utilities; using Bit.App.Resources; using Bit.iOS.Core.Controllers; namespace Bit.iOS.Extension { public partial class LoadingViewController : ExtendedUIViewController { private Context _context = new Context(); private bool _setupHockeyApp = false; private readonly JsonSerializerSettings _jsonSettings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }; public LoadingViewController(IntPtr handle) : base(handle) { } public override void ViewDidLoad() { base.ViewDidLoad(); View.BackgroundColor = new UIColor(red: 0.94f, green: 0.94f, blue: 0.96f, alpha: 1.0f); _context.ExtContext = ExtensionContext; if(!Resolver.IsSet) { SetIoc(); } if(!_setupHockeyApp) { var appIdService = Resolver.Resolve(); var crashManagerDelegate = new HockeyAppCrashManagerDelegate( appIdService, Resolver.Resolve()); var manager = HockeyApp.iOS.BITHockeyManager.SharedHockeyManager; manager.Configure("51f96ae568ba45f699a18ad9f63046c3", crashManagerDelegate); manager.CrashManager.CrashManagerStatus = HockeyApp.iOS.BITCrashManagerStatus.AutoSend; manager.UserId = appIdService.AppId; manager.StartManager(); manager.Authenticator.AuthenticateInstallation(); _setupHockeyApp = true; } foreach(var item in ExtensionContext.InputItems) { var processed = false; foreach(var itemProvider in item.Attachments) { if(ProcessWebUrlProvider(itemProvider) || ProcessFindLoginProvider(itemProvider) || ProcessFindLoginBrowserProvider(itemProvider, Constants.UTTypeAppExtensionFillBrowserAction) || ProcessFindLoginBrowserProvider(itemProvider, Constants.UTTypeAppExtensionFillWebViewAction) || ProcessSaveLoginProvider(itemProvider) || ProcessChangePasswordProvider(itemProvider) || ProcessExtensionSetupProvider(itemProvider)) { processed = true; break; } } if(processed) { break; } } } public override void ViewDidAppear(bool animated) { base.ViewDidAppear(animated); var authService = Resolver.Resolve(); if(!authService.IsAuthenticated) { var alert = Dialogs.CreateAlert(null, "You must log into the main bitwarden app before you can use the extension.", AppResources.Ok, (a) => { CompleteRequest(null); }); PresentViewController(alert, true, null); return; } var lockService = Resolver.Resolve(); var lockType = lockService.GetLockType(false); switch(lockType) { case App.Enums.LockType.Fingerprint: PerformSegue("lockFingerprintSegue", this); break; case App.Enums.LockType.PIN: PerformSegue("lockPinSegue", this); break; case App.Enums.LockType.Password: PerformSegue("lockPasswordSegue", this); break; default: if(_context.ProviderType == Constants.UTTypeAppExtensionSaveLoginAction) { PerformSegue("newSiteSegue", this); } else if(_context.ProviderType == Constants.UTTypeAppExtensionSetup) { PerformSegue("setupSegue", this); } else { PerformSegue("siteListSegue", this); } break; } } public override void PrepareForSegue(UIStoryboardSegue segue, NSObject sender) { var navController = segue.DestinationViewController as UINavigationController; if(navController != null) { var listSiteController = navController.TopViewController as SiteListViewController; var addSiteController = navController.TopViewController as SiteAddViewController; var fingerprintViewController = navController.TopViewController as LockFingerprintViewController; var pinViewController = navController.TopViewController as LockPinViewController; var passwordViewController = navController.TopViewController as LockPasswordViewController; var setupViewController = navController.TopViewController as SetupViewController; if(listSiteController != null) { listSiteController.Context = _context; listSiteController.LoadingController = this; } else if(addSiteController != null) { addSiteController.Context = _context; addSiteController.LoadingController = this; } else if(fingerprintViewController != null) { fingerprintViewController.Context = _context; fingerprintViewController.LoadingController = this; } else if(pinViewController != null) { pinViewController.Context = _context; pinViewController.LoadingController = this; } else if(passwordViewController != null) { passwordViewController.Context = _context; passwordViewController.LoadingController = this; } else if(setupViewController != null) { setupViewController.Context = _context; setupViewController.LoadingController = this; } } } public void DismissLockAndContinue() { Debug.WriteLine("BW Log, Dismissing lock controller."); DismissViewController(false, () => { Debug.WriteLine("BW Log, Segue to site add or list."); if(_context.ProviderType == Constants.UTTypeAppExtensionSaveLoginAction) { PerformSegue("newSiteSegue", this); } else { PerformSegue("siteListSegue", this); } }); } public void CompleteUsernamePasswordRequest(string username, string password) { NSDictionary itemData = null; if(_context.ProviderType == UTType.PropertyList) { var fillScript = new FillScript(_context.Details, username, password); var scriptJson = JsonConvert.SerializeObject(fillScript, _jsonSettings); var scriptDict = new NSDictionary(Constants.AppExtensionWebViewPageFillScript, scriptJson); itemData = new NSDictionary(NSJavaScriptExtension.FinalizeArgumentKey, scriptDict); } else if(_context.ProviderType == Constants.UTTypeAppExtensionFindLoginAction) { itemData = new NSDictionary( Constants.AppExtensionUsernameKey, username, Constants.AppExtensionPasswordKey, password); } else if(_context.ProviderType == Constants.UTTypeAppExtensionFillBrowserAction || _context.ProviderType == Constants.UTTypeAppExtensionFillWebViewAction) { var fillScript = new FillScript(_context.Details, username, password); var scriptJson = JsonConvert.SerializeObject(fillScript, _jsonSettings); itemData = new NSDictionary(Constants.AppExtensionWebViewPageFillScript, scriptJson); } else if(_context.ProviderType == Constants.UTTypeAppExtensionSaveLoginAction) { itemData = new NSDictionary( Constants.AppExtensionUsernameKey, username, Constants.AppExtensionPasswordKey, password); } else if(_context.ProviderType == Constants.UTTypeAppExtensionChangePasswordAction) { itemData = new NSDictionary( Constants.AppExtensionPasswordKey, string.Empty, Constants.AppExtensionOldPasswordKey, password); } CompleteRequest(itemData); } public void CompleteRequest(NSDictionary itemData) { Debug.WriteLine("BW LOG, itemData: " + itemData); var resultsProvider = new NSItemProvider(itemData, UTType.PropertyList); var resultsItem = new NSExtensionItem { Attachments = new NSItemProvider[] { resultsProvider } }; var returningItems = new NSExtensionItem[] { resultsItem }; ExtensionContext.CompleteRequest(returningItems, null); } private void SetIoc() { var container = new UnityContainer(); container // Services .RegisterType(new ContainerControlledLifetimeManager()) .RegisterType(new ContainerControlledLifetimeManager()) .RegisterType(new ContainerControlledLifetimeManager()) .RegisterType(new ContainerControlledLifetimeManager()) .RegisterType(new ContainerControlledLifetimeManager()) .RegisterType(new ContainerControlledLifetimeManager()) .RegisterType(new ContainerControlledLifetimeManager()) .RegisterType(new ContainerControlledLifetimeManager()) .RegisterType(new ContainerControlledLifetimeManager()) .RegisterType(new ContainerControlledLifetimeManager()) .RegisterType(new ContainerControlledLifetimeManager()) .RegisterType(new ContainerControlledLifetimeManager()) .RegisterType(new ContainerControlledLifetimeManager()) // Repositories .RegisterType(new ContainerControlledLifetimeManager()) .RegisterType(new ContainerControlledLifetimeManager()) .RegisterType(new ContainerControlledLifetimeManager()) .RegisterType(new ContainerControlledLifetimeManager()) .RegisterType(new ContainerControlledLifetimeManager()) // Other .RegisterInstance(CrossConnectivity.Current, new ContainerControlledLifetimeManager()) .RegisterInstance(CrossFingerprint.Current, new ContainerControlledLifetimeManager()); ISettings settings = new Settings("group.com.8bit.bitwarden"); container.RegisterInstance(settings, new ContainerControlledLifetimeManager()); Resolver.SetResolver(new UnityResolver(container)); } private bool ProcessItemProvider(NSItemProvider itemProvider, string type, Action action) { if(!itemProvider.HasItemConformingTo(type)) { return false; } itemProvider.LoadItem(type, null, (NSObject list, NSError error) => { if(list == null) { return; } _context.ProviderType = type; var dict = list as NSDictionary; action(dict); Debug.WriteLine("BW LOG, ProviderType: " + _context.ProviderType); Debug.WriteLine("BW LOG, Url: " + _context.Url); Debug.WriteLine("BW LOG, Title: " + _context.SiteTitle); Debug.WriteLine("BW LOG, Username: " + _context.Username); Debug.WriteLine("BW LOG, Password: " + _context.Password); Debug.WriteLine("BW LOG, Old Password: " + _context.OldPassword); Debug.WriteLine("BW LOG, Notes: " + _context.Notes); Debug.WriteLine("BW LOG, Details: " + _context.Details); if(_context.PasswordOptions != null) { Debug.WriteLine("BW LOG, PasswordOptions Min Length: " + _context.PasswordOptions.MinLength); Debug.WriteLine("BW LOG, PasswordOptions Max Length: " + _context.PasswordOptions.MaxLength); Debug.WriteLine("BW LOG, PasswordOptions Require Digits: " + _context.PasswordOptions.RequireDigits); Debug.WriteLine("BW LOG, PasswordOptions Require Symbols: " + _context.PasswordOptions.RequireSymbols); Debug.WriteLine("BW LOG, PasswordOptions Forbidden Chars: " + _context.PasswordOptions.ForbiddenCharacters); } }); return true; } private bool ProcessWebUrlProvider(NSItemProvider itemProvider) { return ProcessItemProvider(itemProvider, UTType.PropertyList, (dict) => { var result = dict[NSJavaScriptExtension.PreprocessingResultsKey]; if(result == null) { return; } _context.Url = new Uri(result.ValueForKey(new NSString(Constants.AppExtensionUrlStringKey)) as NSString); var jsonStr = result.ValueForKey(new NSString(Constants.AppExtensionWebViewPageDetails)) as NSString; _context.Details = DeserializeString(jsonStr); }); } private bool ProcessFindLoginProvider(NSItemProvider itemProvider) { return ProcessItemProvider(itemProvider, Constants.UTTypeAppExtensionFindLoginAction, (dict) => { var version = dict[Constants.AppExtensionVersionNumberKey] as NSNumber; var url = dict[Constants.AppExtensionUrlStringKey] as NSString; if(url != null) { _context.Url = new Uri(url); } }); } private bool ProcessFindLoginBrowserProvider(NSItemProvider itemProvider, string action) { return ProcessItemProvider(itemProvider, action, (dict) => { var version = dict[Constants.AppExtensionVersionNumberKey] as NSNumber; var url = dict[Constants.AppExtensionUrlStringKey] as NSString; if(url != null) { _context.Url = new Uri(url); } _context.Details = DeserializeDictionary(dict[Constants.AppExtensionWebViewPageDetails] as NSDictionary); }); } private bool ProcessSaveLoginProvider(NSItemProvider itemProvider) { return ProcessItemProvider(itemProvider, Constants.UTTypeAppExtensionSaveLoginAction, (dict) => { var version = dict[Constants.AppExtensionVersionNumberKey] as NSNumber; var url = dict[Constants.AppExtensionUrlStringKey] as NSString; var title = dict[Constants.AppExtensionTitleKey] as NSString; var sectionTitle = dict[Constants.AppExtensionSectionTitleKey] as NSString; var username = dict[Constants.AppExtensionUsernameKey] as NSString; var password = dict[Constants.AppExtensionPasswordKey] as NSString; var notes = dict[Constants.AppExtensionNotesKey] as NSString; var fields = dict[Constants.AppExtensionFieldsKey] as NSDictionary; if(url != null) { _context.Url = new Uri(url); } _context.Url = new Uri(url); _context.SiteTitle = title; _context.Username = username; _context.Password = password; _context.Notes = notes; _context.PasswordOptions = DeserializeDictionary(dict[Constants.AppExtensionPasswordGeneratorOptionsKey] as NSDictionary); }); } private bool ProcessChangePasswordProvider(NSItemProvider itemProvider) { return ProcessItemProvider(itemProvider, Constants.UTTypeAppExtensionChangePasswordAction, (dict) => { var version = dict[Constants.AppExtensionVersionNumberKey] as NSNumber; var url = dict[Constants.AppExtensionUrlStringKey] as NSString; var title = dict[Constants.AppExtensionTitleKey] as NSString; var sectionTitle = dict[Constants.AppExtensionSectionTitleKey] as NSString; var username = dict[Constants.AppExtensionUsernameKey] as NSString; var password = dict[Constants.AppExtensionPasswordKey] as NSString; var oldPassword = dict[Constants.AppExtensionOldPasswordKey] as NSString; var notes = dict[Constants.AppExtensionNotesKey] as NSString; var fields = dict[Constants.AppExtensionFieldsKey] as NSDictionary; if(url != null) { _context.Url = new Uri(url); } _context.SiteTitle = title; _context.Username = username; _context.Password = password; _context.OldPassword = oldPassword; _context.Notes = notes; _context.PasswordOptions = DeserializeDictionary(dict[Constants.AppExtensionPasswordGeneratorOptionsKey] as NSDictionary); }); } private bool ProcessExtensionSetupProvider(NSItemProvider itemProvider) { if(itemProvider.HasItemConformingTo(Constants.UTTypeAppExtensionSetup)) { _context.ProviderType = Constants.UTTypeAppExtensionSetup; return true; } return false; } private T DeserializeDictionary(NSDictionary dict) { if(dict != null) { NSError jsonError; var jsonData = NSJsonSerialization.Serialize(dict, NSJsonWritingOptions.PrettyPrinted, out jsonError); if(jsonData != null) { var jsonString = new NSString(jsonData, NSStringEncoding.UTF8); return DeserializeString(jsonString); } } return default(T); } private T DeserializeString(NSString jsonString) { if(jsonString != null) { var convertedObject = JsonConvert.DeserializeObject(jsonString.ToString()); return convertedObject; } return default(T); } } }