diff --git a/src/iOS.Core/Constants.cs b/src/iOS.Core/Constants.cs
new file mode 100644
index 000000000..d44f85062
--- /dev/null
+++ b/src/iOS.Core/Constants.cs
@@ -0,0 +1,31 @@
+namespace Bit.iOS.Core
+{
+ public static class Constants
+ {
+ public const string AppExtensionVersionNumberKey = "version_number";
+ public const string AppExtensionUrlStringKey = "url_string";
+ public const string AppExtensionUsernameKey = "username";
+ public const string AppExtensionPasswordKey = "password";
+ public const string AppExtensionTotpKey = "totp";
+ public const string AppExtensionTitleKey = "login_title";
+ public const string AppExtensionNotesKey = "notes";
+ public const string AppExtensionSectionTitleKey = "section_title";
+ public const string AppExtensionFieldsKey = "fields";
+ public const string AppExtensionReturnedFieldsKey = "returned_fields";
+ public const string AppExtensionOldPasswordKey = "old_password";
+ public const string AppExtensionPasswordGeneratorOptionsKey = "password_generator_options";
+ public const string AppExtensionGeneratedPasswordMinLengthKey = "password_min_length";
+ public const string AppExtensionGeneratedPasswordMaxLengthKey = "password_max_length";
+ public const string AppExtensionGeneratedPasswordRequireDigitsKey = "password_require_digits";
+ public const string AppExtensionGeneratedPasswordRequireSymbolsKey = "password_require_symbols";
+ public const string AppExtensionGeneratedPasswordForbiddenCharactersKey = "password_forbidden_characters";
+ public const string AppExtensionWebViewPageFillScript = "fillScript";
+ public const string AppExtensionWebViewPageDetails = "pageDetails";
+
+ public const string UTTypeAppExtensionFindLoginAction = "org.appextension.find-login-action";
+ public const string UTTypeAppExtensionSaveLoginAction = "org.appextension.save-login-action";
+ public const string UTTypeAppExtensionChangePasswordAction = "org.appextension.change-password-action";
+ public const string UTTypeAppExtensionFillWebViewAction = "org.appextension.fill-webview-action";
+ public const string UTTypeAppExtensionFillBrowserAction = "org.appextension.fill-browser-action";
+ }
+}
diff --git a/src/iOS.Core/iOS.Core.csproj b/src/iOS.Core/iOS.Core.csproj
index df18dbea6..cd1a82d61 100644
--- a/src/iOS.Core/iOS.Core.csproj
+++ b/src/iOS.Core/iOS.Core.csproj
@@ -46,6 +46,7 @@
+
diff --git a/src/iOS.Extension/ActionViewController.cs b/src/iOS.Extension/ActionViewController.cs
index 8ca145636..4f0a46727 100644
--- a/src/iOS.Extension/ActionViewController.cs
+++ b/src/iOS.Extension/ActionViewController.cs
@@ -1,7 +1,6 @@
using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
+using Bit.iOS.Core;
+using Bit.iOS.Extension.Models;
using Foundation;
using MobileCoreServices;
using Newtonsoft.Json;
@@ -11,404 +10,69 @@ namespace Bit.iOS.Extension
{
public partial class ActionViewController : UIViewController
{
- private const string AppExtensionVersionNumberKey = "version_number";
-
- private const string AppExtensionUrlStringKey = "url_string";
- private const string AppExtensionUsernameKey = "username";
- private const string AppExtensionPasswordKey = "password";
- private const string AppExtensionTotpKey = "totp";
- private const string AppExtensionTitleKey = "login_title";
- private const string AppExtensionNotesKey = "notes";
- private const string AppExtensionSectionTitleKey = "section_title";
- private const string AppExtensionFieldsKey = "fields";
- private const string AppExtensionReturnedFieldsKey = "returned_fields";
- private const string AppExtensionOldPasswordKey = "old_password";
- private const string AppExtensionPasswordGeneratorOptionsKey = "password_generator_options";
-
- private const string AppExtensionGeneratedPasswordMinLengthKey = "password_min_length";
- private const string AppExtensionGeneratedPasswordMaxLengthKey = "password_max_length";
- private const string AppExtensionGeneratedPasswordRequireDigitsKey = "password_require_digits";
- private const string AppExtensionGeneratedPasswordRequireSymbolsKey = "password_require_symbols";
- private const string AppExtensionGeneratedPasswordForbiddenCharactersKey = "password_forbidden_characters";
-
- private const string AppExtensionWebViewPageFillScript = "fillScript";
- private const string AppExtensionWebViewPageDetails = "pageDetails";
-
- private const string UTTypeAppExtensionFindLoginAction = "org.appextension.find-login-action";
- private const string UTTypeAppExtensionSaveLoginAction = "org.appextension.save-login-action";
- private const string UTTypeAppExtensionChangePasswordAction = "org.appextension.change-password-action";
- private const string UTTypeAppExtensionFillWebViewAction = "org.appextension.fill-webview-action";
- private const string UTTypeAppExtensionFillBrowserAction = "org.appextension.fill-browser-action";
-
public ActionViewController(IntPtr handle) : base(handle)
{
}
- public NSExtensionContext Context { get; set; }
- public string ProviderType { get; set; }
- public Uri Url { get; set; }
- public string SiteTitle { get; set; }
- public string Username { get; set; }
- public string Password { get; set; }
- public string OldPassword { get; set; }
- public string Notes { get; set; }
- public PasswordGenerationOptions PasswordOptions { get; set; }
- public PageDetails Details { get; set; }
+ public Context Context { get; set; }
public override void ViewDidLoad()
{
base.ViewDidLoad();
View.BackgroundColor = UIColor.FromPatternImage(new UIImage("boxed-bg.png"));
-
- foreach(var item in Context.InputItems)
- {
- var processed = false;
- foreach(var itemProvider in item.Attachments)
- {
- if(ProcessWebUrlProvider(itemProvider)
- || ProcessFindLoginProvider(itemProvider)
- || ProcessFindLoginBrowserProvider(itemProvider, UTTypeAppExtensionFillBrowserAction)
- || ProcessFindLoginBrowserProvider(itemProvider, UTTypeAppExtensionFillWebViewAction)
- || ProcessSaveLoginProvider(itemProvider)
- || ProcessChangePasswordProvider(itemProvider))
- {
- processed = true;
- break;
- }
- }
-
- if(processed)
- {
- break;
- }
- }
}
- partial void CancelClicked (UIBarButtonItem sender)
- {
- Context.CompleteRequest(null, null);
- }
+ partial void CancelClicked(UIBarButtonItem sender)
+ {
+ CompleteRequest(null);
+ }
partial void DoneClicked(NSObject sender)
{
NSDictionary itemData = null;
- if(ProviderType == UTType.PropertyList)
+ if(Context.ProviderType == UTType.PropertyList)
{
- var fillScript = new FillScript(Details);
+ var fillScript = new FillScript(Context.Details);
var scriptJson = JsonConvert.SerializeObject(fillScript, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
- var scriptDict = new NSDictionary(AppExtensionWebViewPageFillScript, scriptJson);
+ var scriptDict = new NSDictionary(Constants.AppExtensionWebViewPageFillScript, scriptJson);
itemData = new NSDictionary(NSJavaScriptExtension.FinalizeArgumentKey, scriptDict);
}
- if(ProviderType == UTTypeAppExtensionFindLoginAction)
+ if(Context.ProviderType == Constants.UTTypeAppExtensionFindLoginAction)
{
itemData = new NSDictionary(
- AppExtensionUsernameKey, "me@example.com",
- AppExtensionPasswordKey, "mypassword");
+ Constants.AppExtensionUsernameKey, "me@example.com",
+ Constants.AppExtensionPasswordKey, "mypassword");
}
- else if(ProviderType == UTTypeAppExtensionFillBrowserAction
- || ProviderType == UTTypeAppExtensionFillWebViewAction)
+ else if(Context.ProviderType == Constants.UTTypeAppExtensionFillBrowserAction
+ || Context.ProviderType == Constants.UTTypeAppExtensionFillWebViewAction)
{
- var fillScript = new FillScript(Details);
+ var fillScript = new FillScript(Context.Details);
var scriptJson = JsonConvert.SerializeObject(fillScript, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
- itemData = new NSDictionary(AppExtensionWebViewPageFillScript, scriptJson);
+ itemData = new NSDictionary(Constants.AppExtensionWebViewPageFillScript, scriptJson);
}
- else if(ProviderType == UTTypeAppExtensionSaveLoginAction)
+ else if(Context.ProviderType == Constants.UTTypeAppExtensionSaveLoginAction)
{
itemData = new NSDictionary(
- AppExtensionUsernameKey, "me@example.com",
- AppExtensionPasswordKey, "mypassword");
+ Constants.AppExtensionUsernameKey, "me@example.com",
+ Constants.AppExtensionPasswordKey, "mypassword");
}
- else if(ProviderType == UTTypeAppExtensionChangePasswordAction)
+ else if(Context.ProviderType == Constants.UTTypeAppExtensionChangePasswordAction)
{
itemData = new NSDictionary(
- AppExtensionPasswordKey, "mynewpassword",
- AppExtensionOldPasswordKey, "myoldpassword");
+ Constants.AppExtensionPasswordKey, "mynewpassword",
+ Constants.AppExtensionOldPasswordKey, "myoldpassword");
}
+ CompleteRequest(itemData);
+ }
+
+ private void CompleteRequest(NSDictionary itemData)
+ {
var resultsProvider = new NSItemProvider(itemData, UTType.PropertyList);
var resultsItem = new NSExtensionItem { Attachments = new NSItemProvider[] { resultsProvider } };
var returningItems = new NSExtensionItem[] { resultsItem };
- Context.CompleteRequest(returningItems, null);
- }
-
- 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;
- }
-
- ProviderType = type;
- var dict = list as NSDictionary;
- action(dict);
-
- Debug.WriteLine("BW LOG, ProviderType: " + ProviderType);
- Debug.WriteLine("BW LOG, Url: " + Url);
- Debug.WriteLine("BW LOG, Title: " + SiteTitle);
- Debug.WriteLine("BW LOG, Username: " + Username);
- Debug.WriteLine("BW LOG, Password: " + Password);
- Debug.WriteLine("BW LOG, Old Password: " + OldPassword);
- Debug.WriteLine("BW LOG, Notes: " + Notes);
- Debug.WriteLine("BW LOG, Details: " + Details);
-
- if(PasswordOptions != null)
- {
- Debug.WriteLine("BW LOG, PasswordOptions Min Length: " + PasswordOptions.MinLength);
- Debug.WriteLine("BW LOG, PasswordOptions Max Length: " + PasswordOptions.MaxLength);
- Debug.WriteLine("BW LOG, PasswordOptions Require Digits: " + PasswordOptions.RequireDigits);
- Debug.WriteLine("BW LOG, PasswordOptions Require Symbols: " + PasswordOptions.RequireSymbols);
- Debug.WriteLine("BW LOG, PasswordOptions Forbidden Chars: " + PasswordOptions.ForbiddenCharacters);
- }
- });
-
- return true;
- }
-
- private bool ProcessWebUrlProvider(NSItemProvider itemProvider)
- {
- return ProcessItemProvider(itemProvider, UTType.PropertyList, (dict) =>
- {
- var result = dict[NSJavaScriptExtension.PreprocessingResultsKey];
- if(result == null)
- {
- return;
- }
-
- Url = new Uri(result.ValueForKey(new NSString(AppExtensionUrlStringKey)) as NSString);
- var jsonStr = result.ValueForKey(new NSString(AppExtensionWebViewPageDetails)) as NSString;
- Details = DeserializeString(jsonStr);
- });
- }
-
- private bool ProcessFindLoginProvider(NSItemProvider itemProvider)
- {
- return ProcessItemProvider(itemProvider, UTTypeAppExtensionFindLoginAction, (dict) =>
- {
- var version = dict[AppExtensionVersionNumberKey] as NSNumber;
- var url = dict[AppExtensionUrlStringKey] as NSString;
-
- if(url != null)
- {
- Url = new Uri(url);
- }
- });
- }
-
- private bool ProcessFindLoginBrowserProvider(NSItemProvider itemProvider, string action)
- {
- return ProcessItemProvider(itemProvider, action, (dict) =>
- {
- var version = dict[AppExtensionVersionNumberKey] as NSNumber;
- var url = dict[AppExtensionUrlStringKey] as NSString;
- if(url != null)
- {
- Url = new Uri(url);
- }
-
- Details = DeserializeDictionary(dict[AppExtensionWebViewPageDetails] as NSDictionary);
- });
- }
-
- private bool ProcessSaveLoginProvider(NSItemProvider itemProvider)
- {
- return ProcessItemProvider(itemProvider, UTTypeAppExtensionSaveLoginAction, (dict) =>
- {
- var version = dict[AppExtensionVersionNumberKey] as NSNumber;
- var url = dict[AppExtensionUrlStringKey] as NSString;
- var title = dict[AppExtensionTitleKey] as NSString;
- var sectionTitle = dict[AppExtensionSectionTitleKey] as NSString;
- var username = dict[AppExtensionUsernameKey] as NSString;
- var password = dict[AppExtensionPasswordKey] as NSString;
- var notes = dict[AppExtensionNotesKey] as NSString;
- var fields = dict[AppExtensionFieldsKey] as NSDictionary;
-
- if(url != null)
- {
- Url = new Uri(url);
- }
-
- Url = new Uri(url);
- SiteTitle = title;
- Username = username;
- Password = password;
- Notes = notes;
- PasswordOptions = DeserializeDictionary(dict[AppExtensionPasswordGeneratorOptionsKey] as NSDictionary);
- });
- }
-
- private bool ProcessChangePasswordProvider(NSItemProvider itemProvider)
- {
- return ProcessItemProvider(itemProvider, UTTypeAppExtensionChangePasswordAction, (dict) =>
- {
- var version = dict[AppExtensionVersionNumberKey] as NSNumber;
- var url = dict[AppExtensionUrlStringKey] as NSString;
- var title = dict[AppExtensionTitleKey] as NSString;
- var sectionTitle = dict[AppExtensionSectionTitleKey] as NSString;
- var username = dict[AppExtensionUsernameKey] as NSString;
- var password = dict[AppExtensionPasswordKey] as NSString;
- var oldPassword = dict[AppExtensionOldPasswordKey] as NSString;
- var notes = dict[AppExtensionNotesKey] as NSString;
- var fields = dict[AppExtensionFieldsKey] as NSDictionary;
-
- if(url != null)
- {
- Url = new Uri(url);
- }
-
- SiteTitle = title;
- Username = username;
- Password = password;
- OldPassword = oldPassword;
- Notes = notes;
- PasswordOptions = DeserializeDictionary(dict[AppExtensionPasswordGeneratorOptionsKey] as NSDictionary);
- });
- }
-
- 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);
- }
-
- public class PasswordGenerationOptions
- {
- public int MinLength { get; set; }
- public int MaxLength { get; set; }
- public bool RequireDigits { get; set; }
- public bool RequireSymbols { get; set; }
- public string ForbiddenCharacters { get; set; }
- }
-
- public class PageDetails
- {
- public string DocumentUUID { get; set; }
- public string Title { get; set; }
- public string Url { get; set; }
- public string DocumentUrl { get; set; }
- public string TabUrl { get; set; }
- public Dictionary Forms { get; set; }
- public List Fields { get; set; }
- public long CollectedTimestamp { get; set; }
-
- public class Form
- {
- public string OpId { get; set; }
- public string HtmlName { get; set; }
- public string HtmlId { get; set; }
- public string HtmlAction { get; set; }
- public string HtmlMethod { get; set; }
- }
-
- public class Field
- {
- public string OpId { get; set; }
- public int ElementNumber { get; set; }
- public bool Visible { get; set; }
- public bool Viewable { get; set; }
- public string HtmlId { get; set; }
- public string HtmlName { get; set; }
- public string HtmlClass { get; set; }
- public string LabelRight { get; set; }
- public string LabelLeft { get; set; }
- public string Type { get; set; }
- public string Value { get; set; }
- public bool Disabled { get; set; }
- public bool Readonly { get; set; }
- public string OnePasswordFieldType { get; set; }
- public string Form { get; set; }
- }
- }
-
- public class FillScript
- {
- public FillScript(PageDetails pageDetails)
- {
- if(pageDetails == null)
- {
- return;
- }
-
- DocumentUUID = pageDetails.DocumentUUID;
-
- var loginForm = pageDetails.Forms.FirstOrDefault(form => pageDetails.Fields.Any(f => f.Form == form.Key && f.Type == "password")).Value;
- if(loginForm == null)
- {
- return;
- }
-
- Script = new List>();
-
- var password = pageDetails.Fields.FirstOrDefault(f =>
- f.Form == loginForm.OpId
- && f.Type == "password");
-
- var username = pageDetails.Fields.LastOrDefault(f =>
- f.Form == loginForm.OpId
- && (f.Type == "text" || f.Type == "email")
- && f.ElementNumber < password.ElementNumber);
-
- if(username != null)
- {
- Script.Add(new List { "click_on_opid", username.OpId });
- Script.Add(new List { "fill_by_opid", username.OpId, "me@example.com" });
- }
-
- Script.Add(new List { "click_on_opid", password.OpId });
- Script.Add(new List { "fill_by_opid", password.OpId, "mypassword" });
-
- if(loginForm.HtmlAction != null)
- {
- AutoSubmit = new Submit { FocusOpId = password.OpId };
- }
- }
-
- [JsonProperty(PropertyName = "script")]
- public List> Script { get; set; }
- [JsonProperty(PropertyName = "autosubmit")]
- public Submit AutoSubmit { get; set; }
- [JsonProperty(PropertyName = "documentUUID")]
- public object DocumentUUID { get; set; }
- [JsonProperty(PropertyName = "properties")]
- public object Properties { get; set; } = new object();
- [JsonProperty(PropertyName = "options")]
- public object Options { get; set; } = new object();
- [JsonProperty(PropertyName = "metadata")]
- public object MetaData { get; set; } = new object();
-
- public class Submit
- {
- [JsonProperty(PropertyName = "focusOpid")]
- public string FocusOpId { get; set; }
- }
+ Context.ExtContext.CompleteRequest(returningItems, null);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/iOS.Extension/LoadingViewController.cs b/src/iOS.Extension/LoadingViewController.cs
new file mode 100644
index 000000000..3950b0042
--- /dev/null
+++ b/src/iOS.Extension/LoadingViewController.cs
@@ -0,0 +1,282 @@
+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;
+
+namespace Bit.iOS.Extension
+{
+ public partial class LoadingViewController : UIViewController
+ {
+ private Context _context = new Context();
+
+ public LoadingViewController(IntPtr handle) : base(handle)
+ {
+ }
+
+ public override void ViewDidLoad()
+ {
+ base.ViewDidLoad();
+ View.BackgroundColor = UIColor.FromPatternImage(new UIImage("boxed-bg.png"));
+ NavigationController.SetNavigationBarHidden(true, false);
+ _context.ExtContext = ExtensionContext;
+ }
+
+ public override void ViewDidAppear(bool animated)
+ {
+ base.ViewDidAppear(animated);
+
+ if(!Resolver.IsSet)
+ {
+ SetIoc();
+ }
+
+ 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))
+ {
+ processed = true;
+ break;
+ }
+ }
+
+ if(processed)
+ {
+ break;
+ }
+ }
+
+ PerformSegue("seque", this);
+ }
+
+ public override void PrepareForSegue(UIStoryboardSegue segue, NSObject sender)
+ {
+ var navController = segue.DestinationViewController as UINavigationController;
+ if(navController != null)
+ {
+ var actionController = navController.TopViewController as ActionViewController;
+ if(actionController != null)
+ {
+ actionController.Context = _context;
+ }
+ }
+ }
+
+ 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())
+ // Repositories
+ .RegisterType(new ContainerControlledLifetimeManager())
+ .RegisterType(new ContainerControlledLifetimeManager())
+ .RegisterType(new ContainerControlledLifetimeManager())
+ .RegisterType(new ContainerControlledLifetimeManager())
+ .RegisterType(new ContainerControlledLifetimeManager());
+ // Other
+ //.RegisterInstance(CrossSettings.Current, new ContainerControlledLifetimeManager())
+ //.RegisterInstance(CrossConnectivity.Current, new ContainerControlledLifetimeManager())
+ //.RegisterInstance(UserDialogs.Instance, new ContainerControlledLifetimeManager())
+ //.RegisterInstance(CrossFingerprint.Current, 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 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);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/iOS.Extension/SplashViewController.designer.cs b/src/iOS.Extension/LoadingViewController.designer.cs
similarity index 82%
rename from src/iOS.Extension/SplashViewController.designer.cs
rename to src/iOS.Extension/LoadingViewController.designer.cs
index eb595279f..a528ba3b7 100644
--- a/src/iOS.Extension/SplashViewController.designer.cs
+++ b/src/iOS.Extension/LoadingViewController.designer.cs
@@ -11,8 +11,8 @@ using UIKit;
namespace Bit.iOS.Extension
{
- [Register ("SplashViewController")]
- partial class SplashViewController
+ [Register ("LoadingViewController")]
+ partial class LoadingViewController
{
void ReleaseDesignerOutlets ()
{
diff --git a/src/iOS.Extension/MainInterface.storyboard b/src/iOS.Extension/MainInterface.storyboard
index bb288336f..a67b63df7 100644
--- a/src/iOS.Extension/MainInterface.storyboard
+++ b/src/iOS.Extension/MainInterface.storyboard
@@ -18,26 +18,28 @@
-
+
-
+
-
+
+
+
+
+
-
-
-
-
+
+
@@ -60,7 +62,7 @@
-
+
diff --git a/src/iOS.Extension/Models/Context.cs b/src/iOS.Extension/Models/Context.cs
new file mode 100644
index 000000000..721258a9d
--- /dev/null
+++ b/src/iOS.Extension/Models/Context.cs
@@ -0,0 +1,19 @@
+using System;
+using Foundation;
+
+namespace Bit.iOS.Extension.Models
+{
+ public class Context
+ {
+ public NSExtensionContext ExtContext { get; set; }
+ public string ProviderType { get; set; }
+ public Uri Url { get; set; }
+ public string SiteTitle { get; set; }
+ public string Username { get; set; }
+ public string Password { get; set; }
+ public string OldPassword { get; set; }
+ public string Notes { get; set; }
+ public PasswordGenerationOptions PasswordOptions { get; set; }
+ public PageDetails Details { get; set; }
+ }
+}
diff --git a/src/iOS.Extension/Models/FillScript.cs b/src/iOS.Extension/Models/FillScript.cs
new file mode 100644
index 000000000..32d02faab
--- /dev/null
+++ b/src/iOS.Extension/Models/FillScript.cs
@@ -0,0 +1,71 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Newtonsoft.Json;
+
+namespace Bit.iOS.Extension.Models
+{
+ public class FillScript
+ {
+ public FillScript(PageDetails pageDetails)
+ {
+ if(pageDetails == null)
+ {
+ return;
+ }
+
+ DocumentUUID = pageDetails.DocumentUUID;
+
+ var loginForm = pageDetails.Forms.FirstOrDefault(form => pageDetails.Fields.Any(f => f.Form == form.Key && f.Type == "password")).Value;
+ if(loginForm == null)
+ {
+ return;
+ }
+
+ Script = new List>();
+
+ var password = pageDetails.Fields.FirstOrDefault(f =>
+ f.Form == loginForm.OpId
+ && f.Type == "password");
+
+ var username = pageDetails.Fields.LastOrDefault(f =>
+ f.Form == loginForm.OpId
+ && (f.Type == "text" || f.Type == "email")
+ && f.ElementNumber < password.ElementNumber);
+
+ if(username != null)
+ {
+ Script.Add(new List { "click_on_opid", username.OpId });
+ Script.Add(new List { "fill_by_opid", username.OpId, "me@example.com" });
+ }
+
+ Script.Add(new List { "click_on_opid", password.OpId });
+ Script.Add(new List { "fill_by_opid", password.OpId, "mypassword" });
+
+ if(loginForm.HtmlAction != null)
+ {
+ AutoSubmit = new Submit { FocusOpId = password.OpId };
+ }
+ }
+
+ [JsonProperty(PropertyName = "script")]
+ public List> Script { get; set; }
+ [JsonProperty(PropertyName = "autosubmit")]
+ public Submit AutoSubmit { get; set; }
+ [JsonProperty(PropertyName = "documentUUID")]
+ public object DocumentUUID { get; set; }
+ [JsonProperty(PropertyName = "properties")]
+ public object Properties { get; set; } = new object();
+ [JsonProperty(PropertyName = "options")]
+ public object Options { get; set; } = new object();
+ [JsonProperty(PropertyName = "metadata")]
+ public object MetaData { get; set; } = new object();
+
+ public class Submit
+ {
+ [JsonProperty(PropertyName = "focusOpid")]
+ public string FocusOpId { get; set; }
+ }
+ }
+
+}
diff --git a/src/iOS.Extension/Models/PageDetails.cs b/src/iOS.Extension/Models/PageDetails.cs
new file mode 100644
index 000000000..f2fc44e4f
--- /dev/null
+++ b/src/iOS.Extension/Models/PageDetails.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+
+namespace Bit.iOS.Extension.Models
+{
+ public class PageDetails
+ {
+ public string DocumentUUID { get; set; }
+ public string Title { get; set; }
+ public string Url { get; set; }
+ public string DocumentUrl { get; set; }
+ public string TabUrl { get; set; }
+ public Dictionary Forms { get; set; }
+ public List Fields { get; set; }
+ public long CollectedTimestamp { get; set; }
+
+ public class Form
+ {
+ public string OpId { get; set; }
+ public string HtmlName { get; set; }
+ public string HtmlId { get; set; }
+ public string HtmlAction { get; set; }
+ public string HtmlMethod { get; set; }
+ }
+
+ public class Field
+ {
+ public string OpId { get; set; }
+ public int ElementNumber { get; set; }
+ public bool Visible { get; set; }
+ public bool Viewable { get; set; }
+ public string HtmlId { get; set; }
+ public string HtmlName { get; set; }
+ public string HtmlClass { get; set; }
+ public string LabelRight { get; set; }
+ public string LabelLeft { get; set; }
+ public string Type { get; set; }
+ public string Value { get; set; }
+ public bool Disabled { get; set; }
+ public bool Readonly { get; set; }
+ public string OnePasswordFieldType { get; set; }
+ public string Form { get; set; }
+ }
+ }
+
+}
diff --git a/src/iOS.Extension/Models/PasswordGenerationOptions.cs b/src/iOS.Extension/Models/PasswordGenerationOptions.cs
new file mode 100644
index 000000000..0f897a78b
--- /dev/null
+++ b/src/iOS.Extension/Models/PasswordGenerationOptions.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace Bit.iOS.Extension.Models
+{
+ public class PasswordGenerationOptions
+ {
+ public int MinLength { get; set; }
+ public int MaxLength { get; set; }
+ public bool RequireDigits { get; set; }
+ public bool RequireSymbols { get; set; }
+ public string ForbiddenCharacters { get; set; }
+ }
+}
diff --git a/src/iOS.Extension/SplashViewController.cs b/src/iOS.Extension/SplashViewController.cs
deleted file mode 100644
index d7072f41e..000000000
--- a/src/iOS.Extension/SplashViewController.cs
+++ /dev/null
@@ -1,111 +0,0 @@
-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;
-
-namespace Bit.iOS.Extension
-{
- public partial class SplashViewController : UIViewController
- {
- public SplashViewController(IntPtr handle) : base(handle)
- {
- }
-
- public override void DidReceiveMemoryWarning()
- {
- // Releases the view if it doesn't have a superview.
- base.DidReceiveMemoryWarning();
-
- // Release any cached data, images, etc that aren't in use.
- }
-
- #region View lifecycle
-
- public override void ViewDidLoad()
- {
- base.ViewDidLoad();
- View.BackgroundColor = UIColor.FromPatternImage(new UIImage("boxed-bg.png"));
- NavigationController.SetNavigationBarHidden(true, false);
- }
-
- public override void ViewWillAppear(bool animated)
- {
- base.ViewWillAppear(animated);
- }
-
- public override void ViewDidAppear(bool animated)
- {
- base.ViewDidAppear(animated);
-
- if(!Resolver.IsSet)
- {
- SetIoc();
- }
-
- PerformSegue("seque", this);
- }
-
- public override void PrepareForSegue (UIStoryboardSegue segue, NSObject sender)
- {
- var navController = segue.DestinationViewController as UINavigationController;
- if(navController != null)
- {
- var actionController = navController.TopViewController as ActionViewController;
- if(actionController != null)
- {
- actionController.Context = ExtensionContext;
- }
- }
- }
-
- 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())
- // Repositories
- .RegisterType(new ContainerControlledLifetimeManager())
- .RegisterType(new ContainerControlledLifetimeManager())
- .RegisterType(new ContainerControlledLifetimeManager())
- .RegisterType(new ContainerControlledLifetimeManager())
- .RegisterType(new ContainerControlledLifetimeManager());
- // Other
- //.RegisterInstance(CrossSettings.Current, new ContainerControlledLifetimeManager())
- //.RegisterInstance(CrossConnectivity.Current, new ContainerControlledLifetimeManager())
- //.RegisterInstance(UserDialogs.Instance, new ContainerControlledLifetimeManager())
- //.RegisterInstance(CrossFingerprint.Current, new ContainerControlledLifetimeManager());
-
- Resolver.SetResolver(new UnityResolver(container));
- }
-
- public override void ViewWillDisappear(bool animated)
- {
- base.ViewWillDisappear(animated);
- }
-
- public override void ViewDidDisappear(bool animated)
- {
- base.ViewDidDisappear(animated);
- }
-
- #endregion
- }
-}
\ No newline at end of file
diff --git a/src/iOS.Extension/iOS.Extension.csproj b/src/iOS.Extension/iOS.Extension.csproj
index a6d36de2b..eb16d6d16 100644
--- a/src/iOS.Extension/iOS.Extension.csproj
+++ b/src/iOS.Extension/iOS.Extension.csproj
@@ -95,9 +95,13 @@
-
-
- SplashViewController.cs
+
+
+
+
+
+
+ LoadingViewController.cs