diff --git a/MacPassHTTP.xcodeproj/project.pbxproj b/MacPassHTTP.xcodeproj/project.pbxproj index 73b40c8..4a27ad6 100644 --- a/MacPassHTTP.xcodeproj/project.pbxproj +++ b/MacPassHTTP.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 30D115AD1C9F74C900D98D00 /* NSString+Levenshtein.m in Sources */ = {isa = PBXBuildFile; fileRef = 30D115AC1C9F74C900D98D00 /* NSString+Levenshtein.m */; }; 4C10B73F1C08D2150077E477 /* MPHRequestAccessWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C10B73D1C08D2150077E477 /* MPHRequestAccessWindowController.m */; }; 4C3CAB051BFF339E009B4DF0 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 4C3CAB071BFF339E009B4DF0 /* Localizable.strings */; }; 4C487E781BF3A0A400E595DE /* MPHMacPassHTTP.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C487E771BF3A0A400E595DE /* MPHMacPassHTTP.m */; }; @@ -32,6 +33,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 30D115AB1C9F74C900D98D00 /* NSString+Levenshtein.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+Levenshtein.h"; sourceTree = ""; }; + 30D115AC1C9F74C900D98D00 /* NSString+Levenshtein.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+Levenshtein.m"; sourceTree = ""; }; 4C10B73C1C08D2150077E477 /* MPHRequestAccessWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPHRequestAccessWindowController.h; sourceTree = ""; }; 4C10B73D1C08D2150077E477 /* MPHRequestAccessWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPHRequestAccessWindowController.m; sourceTree = ""; }; 4C3CAB011BFF32AB009B4DF0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/MacPassHTTPSettings.strings; sourceTree = ""; }; @@ -106,6 +109,8 @@ 4C487E701BF39FC500E595DE /* Info.plist */, 4C967E341BF63CA900B1838C /* MPHServerDelegate.h */, 4C967E351BF63CA900B1838C /* MPHServerDelegate.m */, + 30D115AB1C9F74C900D98D00 /* NSString+Levenshtein.h */, + 30D115AC1C9F74C900D98D00 /* NSString+Levenshtein.m */, ); path = MacPassHTTP; sourceTree = ""; @@ -211,6 +216,7 @@ buildActionMask = 2147483647; files = ( 4C487E781BF3A0A400E595DE /* MPHMacPassHTTP.m in Sources */, + 30D115AD1C9F74C900D98D00 /* NSString+Levenshtein.m in Sources */, 4C967E361BF63CA900B1838C /* MPHServerDelegate.m in Sources */, 4C487E7C1BF3A47700E595DE /* MPHSettingsViewController.m in Sources */, 4C10B73F1C08D2150077E477 /* MPHRequestAccessWindowController.m in Sources */, diff --git a/MacPassHTTP/MPHServerDelegate.m b/MacPassHTTP/MPHServerDelegate.m index 568fb4c..1a936d7 100644 --- a/MacPassHTTP/MPHServerDelegate.m +++ b/MacPassHTTP/MPHServerDelegate.m @@ -12,6 +12,7 @@ #import "MPDocument.h" #import "NSString+MPPasswordCreation.h" +#import "NSString+Levenshtein.h" #import @@ -138,23 +139,41 @@ - (KPKEntry *)_createConfigurationEntry:(MPDocument *)document { #pragma mark - KPHDelegate -+ (NSArray *)recursivelyFindEntriesInGroups:(NSArray *)groups forURL:(NSString *)url { ++ (NSArray *)allEntriesInGroups:(NSArray *)groups { NSMutableArray *entries = @[].mutableCopy; for (KPKGroup *group in groups) { /* recurse through any subgroups */ - [entries addObjectsFromArray:[self recursivelyFindEntriesInGroups:group.groups forURL:url]]; + [entries addObjectsFromArray:[self allEntriesInGroups:group.groups]]; - /* check each entry in the group */ + /* add each entry in the group */ for (KPKEntry *entry in group.entries) { NSString *entryUrl = [entry.url finalValueForEntry:entry]; NSString *entryTitle = [entry.title finalValueForEntry:entry]; NSString *entryUsername = [entry.username finalValueForEntry:entry]; NSString *entryPassword = [entry.password finalValueForEntry:entry]; - if (url == nil || [entryTitle rangeOfString:url].length > 0 || [entryUrl rangeOfString:url].length > 0) { - [entries addObject:[KPHResponseEntry entryWithUrl:entryUrl name:entryTitle login:entryUsername password:entryPassword uuid:[entry.uuid UUIDString] stringFields:nil]]; + [entries addObject:[KPHResponseEntry entryWithUrl:entryUrl name:entryTitle login:entryUsername password:entryPassword uuid:[entry.uuid UUIDString] stringFields:nil]]; + } + } + return entries; +} + ++ (NSArray *)recursivelyFindEntriesInGroups:(NSArray *)groups forURL:(NSString *)url { + NSMutableArray *entries = @[].mutableCopy; + + NSUInteger minLevenshtein = NSUIntegerMax; + + /* iterate through each entry in the group to find the array of entries that have the minimum levenshtein distance */ + for (KPHResponseEntry *entry in [self allEntriesInGroups:groups]) { + NSUInteger levenshtein = [url levenshteinDistanceToString:entry.url]; + if (url == nil || ([[entry.url stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length] > 0 && levenshtein <= minLevenshtein)) { + /* if we have found a new minimum levenshtein distance, remove all previous entries */ + if (url != nil && levenshtein < minLevenshtein) { + minLevenshtein = levenshtein; + [entries removeAllObjects]; } + [entries addObject:entry]; } } return entries; @@ -246,7 +265,7 @@ - (NSArray *)allEntriesForServer:(KPHServer *)server { return @[]; } - return [MPHServerDelegate recursivelyFindEntriesInGroups:self.queryDocument.root.groups forURL:nil]; + return [MPHServerDelegate allEntriesInGroups:@[self.queryDocument.root]]; } - (NSString *)generatePasswordForServer:(KPHServer *)server { diff --git a/MacPassHTTP/NSString+Levenshtein.h b/MacPassHTTP/NSString+Levenshtein.h new file mode 100644 index 0000000..03003b5 --- /dev/null +++ b/MacPassHTTP/NSString+Levenshtein.h @@ -0,0 +1,15 @@ +// +// NSString+Levenshtein.h +// MacPassHTTP +// +// Created by Christopher Luu on 20/03/15. +// Copyright © 2015 HicknHack Software GmbH. All rights reserved. +// + +#import + +@interface NSString (Levenshtein) + +- (NSUInteger)levenshteinDistanceToString:(NSString *)string; + +@end diff --git a/MacPassHTTP/NSString+Levenshtein.m b/MacPassHTTP/NSString+Levenshtein.m new file mode 100644 index 0000000..c6e6b63 --- /dev/null +++ b/MacPassHTTP/NSString+Levenshtein.m @@ -0,0 +1,45 @@ +// +// NSString+Levenshtein.m +// MacPassHTTP +// +// Created by Christopher Luu on 20/03/15. +// Copyright © 2015 HicknHack Software GmbH. All rights reserved. +// + +#import "NSString+Levenshtein.h" +#include + +@implementation NSString (Levenshtein) + +// This implementation can be found here: https://rosettacode.org/wiki/Levenshtein_distance#Objective-C +- (NSUInteger)levenshteinDistanceToString:(NSString *)string { + NSUInteger sl = [self length]; + NSUInteger tl = [string length]; + NSUInteger *d = calloc(sizeof(*d), (sl+1) * (tl+1)); + +#define d(i, j) d[((j) * sl) + (i)] + for (NSUInteger i = 0; i <= sl; i++) { + d(i, 0) = i; + } + for (NSUInteger j = 0; j <= tl; j++) { + d(0, j) = j; + } + for (NSUInteger j = 1; j <= tl; j++) { + for (NSUInteger i = 1; i <= sl; i++) { + if ([self characterAtIndex:i-1] == [string characterAtIndex:j-1]) { + d(i, j) = d(i-1, j-1); + } else { + d(i, j) = MIN(d(i-1, j), MIN(d(i, j-1), d(i-1, j-1))) + 1; + } + } + } + + NSUInteger r = d(sl, tl); +#undef d + + free(d); + + return r; +} + +@end