added domain name parsing. moved iphone debug profile to only arm64 for faster building.

This commit is contained in:
Kyle Spearrin 2016-06-26 00:16:10 -04:00
parent e38dbff152
commit 954e2294c0
8 changed files with 12010 additions and 16 deletions

View file

@ -87,6 +87,7 @@
<Compile Include="Models\Data\FolderData.cs" />
<Compile Include="Abstractions\IDataObject.cs" />
<Compile Include="Models\Data\SiteData.cs" />
<Compile Include="Models\DomainName.cs" />
<Compile Include="Models\Folder.cs" />
<Compile Include="Models\Page\SettingsFolderPageModel.cs" />
<Compile Include="Models\Page\PinPageModel.cs" />
@ -150,6 +151,7 @@
<None Include="packages.config">
<SubType>Designer</SubType>
</None>
<EmbeddedResource Include="Resources\public_suffix_list.dat" />
</ItemGroup>
<ItemGroup>
<Reference Include="Acr.UserDialogs, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">

View file

@ -0,0 +1,327 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
namespace Bit.App.Models
{
// ref: https://github.com/danesparza/domainname-parser
public class DomainName
{
private string _subDomain = string.Empty;
private string _domain = string.Empty;
private string _tld = string.Empty;
private TLDRule _tldRule = null;
public string SubDomain => _subDomain;
public string Domain => _domain;
public string SLD => _domain;
public string TLD => _tld;
public TLDRule Rule => _tldRule;
public DomainName(string TLD, string SLD, string SubDomain, TLDRule TLDRule)
{
_tld = TLD;
_domain = SLD;
_subDomain = SubDomain;
_tldRule = TLDRule;
}
public static bool TryParse(string domainString, out DomainName result)
{
bool retval = false;
// Our temporary domain parts:
var tld = string.Empty;
var sld = string.Empty;
var subdomain = string.Empty;
TLDRule _tldrule = null;
result = null;
try
{
// Try parsing the domain name ... this might throw formatting exceptions
ParseDomainName(domainString, out tld, out sld, out subdomain, out _tldrule);
// Construct a new DomainName object and return it
result = new DomainName(tld, sld, subdomain, _tldrule);
// Return 'true'
retval = true;
}
catch
{
// Looks like something bad happened -- return 'false'
retval = false;
}
return retval;
}
private static void ParseDomainName(string domainString, out string TLD, out string SLD, out string SubDomain, out TLDRule MatchingRule)
{
// Make sure domain is all lowercase
domainString = domainString.ToLower();
TLD = string.Empty;
SLD = string.Empty;
SubDomain = string.Empty;
MatchingRule = null;
// If the fqdn is empty, we have a problem already
if(domainString.Trim() == string.Empty)
{
throw new ArgumentException("The domain cannot be blank");
}
// Next, find the matching rule:
MatchingRule = FindMatchingTLDRule(domainString);
// At this point, no rules match, we have a problem
if(MatchingRule == null)
{
throw new FormatException("The domain does not have a recognized TLD");
}
// Based on the tld rule found, get the domain (and possibly the subdomain)
var tempSudomainAndDomain = string.Empty;
var tldIndex = 0;
// First, determine what type of rule we have, and set the TLD accordingly
switch(MatchingRule.Type)
{
case TLDRule.RuleType.Normal:
tldIndex = domainString.LastIndexOf("." + MatchingRule.Name);
tempSudomainAndDomain = domainString.Substring(0, tldIndex);
TLD = domainString.Substring(tldIndex + 1);
break;
case TLDRule.RuleType.Wildcard:
// This finds the last portion of the TLD...
tldIndex = domainString.LastIndexOf("." + MatchingRule.Name);
tempSudomainAndDomain = domainString.Substring(0, tldIndex);
// But we need to find the wildcard portion of it:
tldIndex = tempSudomainAndDomain.LastIndexOf(".");
tempSudomainAndDomain = domainString.Substring(0, tldIndex);
TLD = domainString.Substring(tldIndex + 1);
break;
case TLDRule.RuleType.Exception:
tldIndex = domainString.LastIndexOf(".");
tempSudomainAndDomain = domainString.Substring(0, tldIndex);
TLD = domainString.Substring(tldIndex + 1);
break;
}
// See if we have a subdomain:
List<string> lstRemainingParts = new List<string>(tempSudomainAndDomain.Split('.'));
// If we have 0 parts left, there is just a tld and no domain or subdomain
// If we have 1 part, it's the domain, and there is no subdomain
// If we have 2+ parts, the last part is the domain, the other parts (combined) are the subdomain
if(lstRemainingParts.Count > 0)
{
// Set the domain:
SLD = lstRemainingParts[lstRemainingParts.Count - 1];
// Set the subdomain, if there is one to set:
if(lstRemainingParts.Count > 1)
{
// We strip off the trailing period, too
SubDomain = tempSudomainAndDomain.Substring(0, tempSudomainAndDomain.Length - SLD.Length - 1);
}
}
}
private static TLDRule FindMatchingTLDRule(string domainString)
{
// Split our domain into parts (based on the '.')
// ...Put these parts in a list
// ...Make sure these parts are in reverse order (we'll be checking rules from the right-most pat of the domain)
var lstDomainParts = domainString.Split('.').ToList();
lstDomainParts.Reverse();
// Begin building our partial domain to check rules with:
var checkAgainst = string.Empty;
// Our 'matches' collection:
var ruleMatches = new List<TLDRule>();
foreach(string domainPart in lstDomainParts)
{
// Add on our next domain part:
checkAgainst = string.Format("{0}.{1}", domainPart, checkAgainst);
// If we end in a period, strip it off:
if(checkAgainst.EndsWith("."))
{
checkAgainst = checkAgainst.Substring(0, checkAgainst.Length - 1);
}
var rules = Enum.GetValues(typeof(TLDRule.RuleType)).Cast<TLDRule.RuleType>();
foreach(var rule in rules)
{
// Try to match rule:
TLDRule result;
if(TLDRulesCache.Instance.TLDRuleLists[rule].TryGetValue(checkAgainst, out result))
{
ruleMatches.Add(result);
}
Debug.WriteLine(string.Format("Domain part {0} matched {1} {2} rules", checkAgainst, result == null ? 0 : 1, rule));
}
}
// Sort our matches list (longest rule wins, according to :
var results = from match in ruleMatches
orderby match.Name.Length descending
select match;
// Take the top result (our primary match):
TLDRule primaryMatch = results.Take(1).SingleOrDefault();
if(primaryMatch != null)
{
Debug.WriteLine(string.Format("Looks like our match is: {0}, which is a(n) {1} rule.", primaryMatch.Name, primaryMatch.Type));
}
else
{
Debug.WriteLine(string.Format("No rules matched domain: {0}", domainString));
}
return primaryMatch;
}
public class TLDRule : IComparable<TLDRule>
{
public string Name { get; private set; }
public RuleType Type { get; private set; }
public TLDRule(string RuleInfo)
{
// Parse the rule and set properties accordingly:
if(RuleInfo.StartsWith("*"))
{
Type = RuleType.Wildcard;
Name = RuleInfo.Substring(2);
}
else if(RuleInfo.StartsWith("!"))
{
Type = RuleType.Exception;
Name = RuleInfo.Substring(1);
}
else
{
Type = RuleType.Normal;
Name = RuleInfo;
}
}
public int CompareTo(TLDRule other)
{
if(other == null)
{
return -1;
}
return Name.CompareTo(other.Name);
}
public enum RuleType
{
Normal,
Wildcard,
Exception
}
}
public class TLDRulesCache
{
private static volatile TLDRulesCache _uniqueInstance;
private static object _syncObj = new object();
private static object _syncList = new object();
private IDictionary<TLDRule.RuleType, IDictionary<string, TLDRule>> _lstTLDRules;
private TLDRulesCache()
{
// Initialize our internal list:
_lstTLDRules = GetTLDRules();
}
public static TLDRulesCache Instance
{
get
{
if(_uniqueInstance == null)
{
lock(_syncObj)
{
if(_uniqueInstance == null)
{
_uniqueInstance = new TLDRulesCache();
}
}
}
return (_uniqueInstance);
}
}
public IDictionary<TLDRule.RuleType, IDictionary<string, TLDRule>> TLDRuleLists
{
get
{
return _lstTLDRules;
}
set
{
_lstTLDRules = value;
}
}
public static void Reset()
{
lock(_syncObj)
{
_uniqueInstance = null;
}
}
private IDictionary<TLDRule.RuleType, IDictionary<string, TLDRule>> GetTLDRules()
{
var results = new Dictionary<TLDRule.RuleType, IDictionary<string, TLDRule>>();
var rules = Enum.GetValues(typeof(TLDRule.RuleType)).Cast<TLDRule.RuleType>();
foreach(var rule in rules)
{
results[rule] = new Dictionary<string, TLDRule>(StringComparer.CurrentCultureIgnoreCase);
}
var ruleStrings = ReadRulesData();
// Strip out any lines that are:
// a.) A comment
// b.) Blank
foreach(var ruleString in ruleStrings.Where(ruleString => !ruleString.StartsWith("//") && ruleString.Trim().Length != 0))
{
var result = new TLDRule(ruleString);
results[result.Type][result.Name] = result;
}
// Return our results:
Debug.WriteLine(string.Format("Loaded {0} rules into cache.", results.Values.Sum(r => r.Values.Count)));
return results;
}
private IEnumerable<string> ReadRulesData()
{
var assembly = typeof(TLDRulesCache).GetTypeInfo().Assembly;
var stream = assembly.GetManifestResourceStream("Bit.App.Resources.public_suffix_list.dat");
string line;
using(var reader = new StreamReader(stream))
{
while((line = reader.ReadLine()) != null)
{
yield return line;
}
}
}
}
}
}

View file

@ -13,6 +13,7 @@ namespace Bit.App.Models.Page
private string _uri;
private string _notes;
private bool _revealPassword;
private string _uriHost;
public VaultViewSitePageModel() { }
@ -68,19 +69,34 @@ namespace Bit.App.Models.Page
{
get
{
if(ShowUri)
if(!ShowUri)
{
try
{
return new Uri(Uri).Host;
}
catch
{
return Uri;
}
return null;
}
return null;
if(_uriHost != null)
{
return _uriHost;
}
try
{
DomainName domain;
if(DomainName.TryParse(Uri, out domain))
{
_uriHost = domain.Domain;
}
else
{
_uriHost = new Uri(Uri).Host;
}
return _uriHost;
}
catch
{
return Uri;
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Bit.App.Abstractions;
using Bit.App.Models;
using Bit.iOS.Core;
using Bit.iOS.Extension.Models;
using Foundation;
@ -32,10 +33,16 @@ namespace Bit.iOS.Extension
{
base.ViewDidLoad();
var siteService = Resolver.Resolve<ISiteService>();
var sites = await siteService.GetAllAsync();
var siteModels = sites.Select(s => new SiteViewModel(s));
var filteredSiteModels = siteModels.Where(s => s.HostName == Context.Url?.Host);
IEnumerable<SiteViewModel> filteredSiteModels = new List< SiteViewModel>();
DomainName domain;
if(Context.Url?.Host != null && DomainName.TryParse(Context.Url?.Host, out domain))
{
var siteService = Resolver.Resolve<ISiteService>();
var sites = await siteService.GetAllAsync();
var siteModels = sites.Select(s => new SiteViewModel(s));
filteredSiteModels = siteModels.Where(s => s.Domain.Domain == domain.Domain);
}
tableView.Source = new TableSource(filteredSiteModels, this);
AutomaticallyAdjustsScrollViewInsets = false;
}

View file

@ -7,6 +7,9 @@ namespace Bit.iOS.Extension.Models
{
public class SiteViewModel
{
private DomainName _domain = null;
private bool _domainParsed = false;
public SiteViewModel(Site site)
{
Id = site.Id;
@ -40,5 +43,26 @@ namespace Bit.iOS.Extension.Models
};
}
}
public DomainName Domain
{
get
{
if(_domainParsed)
{
return _domain;
}
_domainParsed = true;
DomainName domain;
if(DomainName.TryParse(Uri, out domain))
{
_domain = domain;
}
return _domain;
}
}
}
}

View file

@ -47,7 +47,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
<MtouchArch>ARMv7, ARM64</MtouchArch>
<MtouchArch>ARM64</MtouchArch>
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
<CodesignKey>iPhone Developer</CodesignKey>
<MtouchDebug>True</MtouchDebug>

View file

@ -54,7 +54,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
<MtouchArch>ARMv7, ARM64</MtouchArch>
<MtouchArch>ARM64</MtouchArch>
<CodesignKey>iPhone Developer</CodesignKey>
<MtouchDebug>true</MtouchDebug>
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>