mirror of
https://github.com/bitwarden/android.git
synced 2024-12-24 09:58:27 +03:00
Split extension up into smaller parts. Process in Loading controller. Response in action controller.
This commit is contained in:
parent
bff7c79ebe
commit
9755d4c79b
12 changed files with 512 additions and 490 deletions
31
src/iOS.Core/Constants.cs
Normal file
31
src/iOS.Core/Constants.cs
Normal file
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
|
@ -46,6 +46,7 @@
|
||||||
<Folder Include="Resources\" />
|
<Folder Include="Resources\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="Constants.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Services\SqlService.cs" />
|
<Compile Include="Services\SqlService.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using Bit.iOS.Core;
|
||||||
using System.Diagnostics;
|
using Bit.iOS.Extension.Models;
|
||||||
using System.Linq;
|
|
||||||
using Foundation;
|
using Foundation;
|
||||||
using MobileCoreServices;
|
using MobileCoreServices;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
@ -11,404 +10,69 @@ namespace Bit.iOS.Extension
|
||||||
{
|
{
|
||||||
public partial class ActionViewController : UIViewController
|
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 ActionViewController(IntPtr handle) : base(handle)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public NSExtensionContext Context { get; set; }
|
public Context 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 override void ViewDidLoad()
|
public override void ViewDidLoad()
|
||||||
{
|
{
|
||||||
base.ViewDidLoad();
|
base.ViewDidLoad();
|
||||||
View.BackgroundColor = UIColor.FromPatternImage(new UIImage("boxed-bg.png"));
|
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)
|
partial void CancelClicked(UIBarButtonItem sender)
|
||||||
{
|
{
|
||||||
Context.CompleteRequest(null, null);
|
CompleteRequest(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void DoneClicked(NSObject sender)
|
partial void DoneClicked(NSObject sender)
|
||||||
{
|
{
|
||||||
NSDictionary itemData = null;
|
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 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);
|
itemData = new NSDictionary(NSJavaScriptExtension.FinalizeArgumentKey, scriptDict);
|
||||||
}
|
}
|
||||||
if(ProviderType == UTTypeAppExtensionFindLoginAction)
|
if(Context.ProviderType == Constants.UTTypeAppExtensionFindLoginAction)
|
||||||
{
|
{
|
||||||
itemData = new NSDictionary(
|
itemData = new NSDictionary(
|
||||||
AppExtensionUsernameKey, "me@example.com",
|
Constants.AppExtensionUsernameKey, "me@example.com",
|
||||||
AppExtensionPasswordKey, "mypassword");
|
Constants.AppExtensionPasswordKey, "mypassword");
|
||||||
}
|
}
|
||||||
else if(ProviderType == UTTypeAppExtensionFillBrowserAction
|
else if(Context.ProviderType == Constants.UTTypeAppExtensionFillBrowserAction
|
||||||
|| ProviderType == UTTypeAppExtensionFillWebViewAction)
|
|| 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 });
|
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(
|
itemData = new NSDictionary(
|
||||||
AppExtensionUsernameKey, "me@example.com",
|
Constants.AppExtensionUsernameKey, "me@example.com",
|
||||||
AppExtensionPasswordKey, "mypassword");
|
Constants.AppExtensionPasswordKey, "mypassword");
|
||||||
}
|
}
|
||||||
else if(ProviderType == UTTypeAppExtensionChangePasswordAction)
|
else if(Context.ProviderType == Constants.UTTypeAppExtensionChangePasswordAction)
|
||||||
{
|
{
|
||||||
itemData = new NSDictionary(
|
itemData = new NSDictionary(
|
||||||
AppExtensionPasswordKey, "mynewpassword",
|
Constants.AppExtensionPasswordKey, "mynewpassword",
|
||||||
AppExtensionOldPasswordKey, "myoldpassword");
|
Constants.AppExtensionOldPasswordKey, "myoldpassword");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CompleteRequest(itemData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CompleteRequest(NSDictionary itemData)
|
||||||
|
{
|
||||||
var resultsProvider = new NSItemProvider(itemData, UTType.PropertyList);
|
var resultsProvider = new NSItemProvider(itemData, UTType.PropertyList);
|
||||||
var resultsItem = new NSExtensionItem { Attachments = new NSItemProvider[] { resultsProvider } };
|
var resultsItem = new NSExtensionItem { Attachments = new NSItemProvider[] { resultsProvider } };
|
||||||
var returningItems = new NSExtensionItem[] { resultsItem };
|
var returningItems = new NSExtensionItem[] { resultsItem };
|
||||||
|
|
||||||
Context.CompleteRequest(returningItems, null);
|
Context.ExtContext.CompleteRequest(returningItems, null);
|
||||||
}
|
|
||||||
|
|
||||||
private bool ProcessItemProvider(NSItemProvider itemProvider, string type, Action<NSDictionary> 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<PageDetails>(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<PageDetails>(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<PasswordGenerationOptions>(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<PasswordGenerationOptions>(dict[AppExtensionPasswordGeneratorOptionsKey] as NSDictionary);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private T DeserializeDictionary<T>(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<T>(jsonString);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return default(T);
|
|
||||||
}
|
|
||||||
|
|
||||||
private T DeserializeString<T>(NSString jsonString)
|
|
||||||
{
|
|
||||||
if(jsonString != null)
|
|
||||||
{
|
|
||||||
var convertedObject = JsonConvert.DeserializeObject<T>(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<string, Form> Forms { get; set; }
|
|
||||||
public List<Field> 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<List<string>>();
|
|
||||||
|
|
||||||
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<string> { "click_on_opid", username.OpId });
|
|
||||||
Script.Add(new List<string> { "fill_by_opid", username.OpId, "me@example.com" });
|
|
||||||
}
|
|
||||||
|
|
||||||
Script.Add(new List<string> { "click_on_opid", password.OpId });
|
|
||||||
Script.Add(new List<string> { "fill_by_opid", password.OpId, "mypassword" });
|
|
||||||
|
|
||||||
if(loginForm.HtmlAction != null)
|
|
||||||
{
|
|
||||||
AutoSubmit = new Submit { FocusOpId = password.OpId };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[JsonProperty(PropertyName = "script")]
|
|
||||||
public List<List<string>> 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; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
282
src/iOS.Extension/LoadingViewController.cs
Normal file
282
src/iOS.Extension/LoadingViewController.cs
Normal file
|
@ -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<IDatabaseService, DatabaseService>(new ContainerControlledLifetimeManager())
|
||||||
|
.RegisterType<ISqlService, SqlService>(new ContainerControlledLifetimeManager())
|
||||||
|
//.RegisterType<ISecureStorageService, KeyChainStorageService>(new ContainerControlledLifetimeManager())
|
||||||
|
.RegisterType<ICryptoService, CryptoService>(new ContainerControlledLifetimeManager())
|
||||||
|
.RegisterType<IAuthService, AuthService>(new ContainerControlledLifetimeManager())
|
||||||
|
.RegisterType<IFolderService, FolderService>(new ContainerControlledLifetimeManager())
|
||||||
|
.RegisterType<ISiteService, SiteService>(new ContainerControlledLifetimeManager())
|
||||||
|
.RegisterType<ISyncService, SyncService>(new ContainerControlledLifetimeManager())
|
||||||
|
//.RegisterType<IClipboardService, ClipboardService>(new ContainerControlledLifetimeManager())
|
||||||
|
// Repositories
|
||||||
|
.RegisterType<IFolderRepository, FolderRepository>(new ContainerControlledLifetimeManager())
|
||||||
|
.RegisterType<IFolderApiRepository, FolderApiRepository>(new ContainerControlledLifetimeManager())
|
||||||
|
.RegisterType<ISiteRepository, SiteRepository>(new ContainerControlledLifetimeManager())
|
||||||
|
.RegisterType<ISiteApiRepository, SiteApiRepository>(new ContainerControlledLifetimeManager())
|
||||||
|
.RegisterType<IAuthApiRepository, AuthApiRepository>(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<NSDictionary> 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<PageDetails>(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<PageDetails>(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<PasswordGenerationOptions>(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<PasswordGenerationOptions>(dict[Constants.AppExtensionPasswordGeneratorOptionsKey] as NSDictionary);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private T DeserializeDictionary<T>(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<T>(jsonString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return default(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
private T DeserializeString<T>(NSString jsonString)
|
||||||
|
{
|
||||||
|
if(jsonString != null)
|
||||||
|
{
|
||||||
|
var convertedObject = JsonConvert.DeserializeObject<T>(jsonString.ToString());
|
||||||
|
return convertedObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
return default(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,8 +11,8 @@ using UIKit;
|
||||||
|
|
||||||
namespace Bit.iOS.Extension
|
namespace Bit.iOS.Extension
|
||||||
{
|
{
|
||||||
[Register ("SplashViewController")]
|
[Register ("LoadingViewController")]
|
||||||
partial class SplashViewController
|
partial class LoadingViewController
|
||||||
{
|
{
|
||||||
void ReleaseDesignerOutlets ()
|
void ReleaseDesignerOutlets ()
|
||||||
{
|
{
|
|
@ -18,26 +18,28 @@
|
||||||
<color key="backgroundColor" colorSpace="calibratedWhite" white="1" alpha="1"/>
|
<color key="backgroundColor" colorSpace="calibratedWhite" white="1" alpha="1"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="1717" translatesAutoresizingMaskIntoConstraints="NO">
|
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="1717" translatesAutoresizingMaskIntoConstraints="NO">
|
||||||
<rect key="frame" x="20" y="72" width="560" height="483"/>
|
<rect key="frame" x="0.0" y="72" width="600" height="506"/>
|
||||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
<prototypes>
|
<prototypes>
|
||||||
<tableViewCell contentMode="scaleToFill" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" id="1722" rowHeight="44">
|
<tableViewCell contentMode="scaleToFill" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" id="1722" rowHeight="44">
|
||||||
<rect key="frame" x="0.0" y="22" width="560" height="44"/>
|
<rect key="frame" x="0.0" y="22" width="600" height="44"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="1723" tableViewCell="1722">
|
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="1723" tableViewCell="1722">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="560" height="43.5"/>
|
<rect key="frame" x="0.0" y="0.0" width="600" height="43.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||||
</view>
|
</view>
|
||||||
</tableViewCell>
|
</tableViewCell>
|
||||||
</prototypes>
|
</prototypes>
|
||||||
|
<constraints>
|
||||||
|
<constraint id="1745" firstAttribute="width" constant="600"/>
|
||||||
|
<constraint id="1746" firstAttribute="height" constant="506"/>
|
||||||
|
</constraints>
|
||||||
</tableView>
|
</tableView>
|
||||||
</subviews>
|
</subviews>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint id="1724" firstItem="1717" firstAttribute="top" secondItem="qkL-Od-lgU" secondAttribute="bottom" constant="8" symbolic="YES"/>
|
<constraint id="1743" firstItem="zMn-AG-sqS" firstAttribute="trailing" secondItem="1717" secondAttribute="trailing"/>
|
||||||
<constraint id="1725" firstItem="n38-gi-rB5" firstAttribute="top" secondItem="1717" secondAttribute="bottom" constant="8" symbolic="YES"/>
|
<constraint id="1744" firstItem="zMn-AG-sqS" firstAttribute="bottom" secondItem="1717" secondAttribute="bottom" constant="22"/>
|
||||||
<constraint id="1726" firstItem="1717" firstAttribute="leading" secondItem="zMn-AG-sqS" secondAttribute="leading" constant="20" symbolic="YES"/>
|
|
||||||
<constraint id="1727" firstItem="zMn-AG-sqS" firstAttribute="trailing" secondItem="1717" secondAttribute="trailing" constant="20" symbolic="YES"/>
|
|
||||||
</constraints>
|
</constraints>
|
||||||
</view>
|
</view>
|
||||||
<navigationItem key="navigationItem" id="yHH-Ee-etX" title="Sites">
|
<navigationItem key="navigationItem" id="yHH-Ee-etX" title="Sites">
|
||||||
|
@ -60,7 +62,7 @@
|
||||||
<!--Splash View Controller-->
|
<!--Splash View Controller-->
|
||||||
<scene sceneID="42">
|
<scene sceneID="42">
|
||||||
<objects>
|
<objects>
|
||||||
<viewController id="43" customClass="SplashViewController" sceneMemberID="viewController">
|
<viewController id="43" customClass="LoadingViewController" sceneMemberID="viewController">
|
||||||
<layoutGuides>
|
<layoutGuides>
|
||||||
<viewControllerLayoutGuide type="top" id="40"/>
|
<viewControllerLayoutGuide type="top" id="40"/>
|
||||||
<viewControllerLayoutGuide type="bottom" id="41"/>
|
<viewControllerLayoutGuide type="bottom" id="41"/>
|
||||||
|
|
19
src/iOS.Extension/Models/Context.cs
Normal file
19
src/iOS.Extension/Models/Context.cs
Normal file
|
@ -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; }
|
||||||
|
}
|
||||||
|
}
|
71
src/iOS.Extension/Models/FillScript.cs
Normal file
71
src/iOS.Extension/Models/FillScript.cs
Normal file
|
@ -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<List<string>>();
|
||||||
|
|
||||||
|
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<string> { "click_on_opid", username.OpId });
|
||||||
|
Script.Add(new List<string> { "fill_by_opid", username.OpId, "me@example.com" });
|
||||||
|
}
|
||||||
|
|
||||||
|
Script.Add(new List<string> { "click_on_opid", password.OpId });
|
||||||
|
Script.Add(new List<string> { "fill_by_opid", password.OpId, "mypassword" });
|
||||||
|
|
||||||
|
if(loginForm.HtmlAction != null)
|
||||||
|
{
|
||||||
|
AutoSubmit = new Submit { FocusOpId = password.OpId };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "script")]
|
||||||
|
public List<List<string>> 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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
46
src/iOS.Extension/Models/PageDetails.cs
Normal file
46
src/iOS.Extension/Models/PageDetails.cs
Normal file
|
@ -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<string, Form> Forms { get; set; }
|
||||||
|
public List<Field> 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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
13
src/iOS.Extension/Models/PasswordGenerationOptions.cs
Normal file
13
src/iOS.Extension/Models/PasswordGenerationOptions.cs
Normal file
|
@ -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; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<IDatabaseService, DatabaseService>(new ContainerControlledLifetimeManager())
|
|
||||||
.RegisterType<ISqlService, SqlService>(new ContainerControlledLifetimeManager())
|
|
||||||
//.RegisterType<ISecureStorageService, KeyChainStorageService>(new ContainerControlledLifetimeManager())
|
|
||||||
.RegisterType<ICryptoService, CryptoService>(new ContainerControlledLifetimeManager())
|
|
||||||
.RegisterType<IAuthService, AuthService>(new ContainerControlledLifetimeManager())
|
|
||||||
.RegisterType<IFolderService, FolderService>(new ContainerControlledLifetimeManager())
|
|
||||||
.RegisterType<ISiteService, SiteService>(new ContainerControlledLifetimeManager())
|
|
||||||
.RegisterType<ISyncService, SyncService>(new ContainerControlledLifetimeManager())
|
|
||||||
//.RegisterType<IClipboardService, ClipboardService>(new ContainerControlledLifetimeManager())
|
|
||||||
// Repositories
|
|
||||||
.RegisterType<IFolderRepository, FolderRepository>(new ContainerControlledLifetimeManager())
|
|
||||||
.RegisterType<IFolderApiRepository, FolderApiRepository>(new ContainerControlledLifetimeManager())
|
|
||||||
.RegisterType<ISiteRepository, SiteRepository>(new ContainerControlledLifetimeManager())
|
|
||||||
.RegisterType<ISiteApiRepository, SiteApiRepository>(new ContainerControlledLifetimeManager())
|
|
||||||
.RegisterType<IAuthApiRepository, AuthApiRepository>(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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -95,9 +95,13 @@
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Main.cs" />
|
<Compile Include="Main.cs" />
|
||||||
<Compile Include="AppDelegate.cs" />
|
<Compile Include="AppDelegate.cs" />
|
||||||
<Compile Include="SplashViewController.cs" />
|
<Compile Include="Models\Context.cs" />
|
||||||
<Compile Include="SplashViewController.designer.cs">
|
<Compile Include="Models\FillScript.cs" />
|
||||||
<DependentUpon>SplashViewController.cs</DependentUpon>
|
<Compile Include="Models\PageDetails.cs" />
|
||||||
|
<Compile Include="Models\PasswordGenerationOptions.cs" />
|
||||||
|
<Compile Include="LoadingViewController.cs" />
|
||||||
|
<Compile Include="LoadingViewController.designer.cs">
|
||||||
|
<DependentUpon>LoadingViewController.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<None Include="Info.plist" />
|
<None Include="Info.plist" />
|
||||||
<None Include="Entitlements.plist" />
|
<None Include="Entitlements.plist" />
|
||||||
|
|
Loading…
Reference in a new issue