Skip to content

Update url matching to use levenshtein distance #23

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions MacPassHTTP.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 */; };
Expand All @@ -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 = "<group>"; };
30D115AC1C9F74C900D98D00 /* NSString+Levenshtein.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+Levenshtein.m"; sourceTree = "<group>"; };
4C10B73C1C08D2150077E477 /* MPHRequestAccessWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPHRequestAccessWindowController.h; sourceTree = "<group>"; };
4C10B73D1C08D2150077E477 /* MPHRequestAccessWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPHRequestAccessWindowController.m; sourceTree = "<group>"; };
4C3CAB011BFF32AB009B4DF0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/MacPassHTTPSettings.strings; sourceTree = "<group>"; };
Expand Down Expand Up @@ -106,6 +109,8 @@
4C487E701BF39FC500E595DE /* Info.plist */,
4C967E341BF63CA900B1838C /* MPHServerDelegate.h */,
4C967E351BF63CA900B1838C /* MPHServerDelegate.m */,
30D115AB1C9F74C900D98D00 /* NSString+Levenshtein.h */,
30D115AC1C9F74C900D98D00 /* NSString+Levenshtein.m */,
);
path = MacPassHTTP;
sourceTree = "<group>";
Expand Down Expand Up @@ -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 */,
Expand Down
31 changes: 25 additions & 6 deletions MacPassHTTP/MPHServerDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#import "MPDocument.h"
#import "NSString+MPPasswordCreation.h"
#import "NSString+Levenshtein.h"

#import <KeePassKit/KeePassKit.h>

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down
15 changes: 15 additions & 0 deletions MacPassHTTP/NSString+Levenshtein.h
Original file line number Diff line number Diff line change
@@ -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 <Cocoa/Cocoa.h>

@interface NSString (Levenshtein)

- (NSUInteger)levenshteinDistanceToString:(NSString *)string;

@end
45 changes: 45 additions & 0 deletions MacPassHTTP/NSString+Levenshtein.m
Original file line number Diff line number Diff line change
@@ -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 <stdlib.h>

@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