diff options
Diffstat (limited to 'Source/SPNetworkPreferencePane.m')
-rw-r--r-- | Source/SPNetworkPreferencePane.m | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/Source/SPNetworkPreferencePane.m b/Source/SPNetworkPreferencePane.m index d3e2f52f..c734f07b 100644 --- a/Source/SPNetworkPreferencePane.m +++ b/Source/SPNetworkPreferencePane.m @@ -30,8 +30,33 @@ #import "SPNetworkPreferencePane.h" +static NSString *SPSSLCipherListMarkerItem = @"--"; +static NSString *SPSSLCipherPboardTypeName = @"SSLCipherPboardType"; + +@interface SPNetworkPreferencePane (Private) +- (void)updateHiddenFiles; +- (void)loadSSLCiphers; +- (void)storeSSLCiphers; ++ (NSArray *)defaultSSLCipherList; +@end + @implementation SPNetworkPreferencePane +- (instancetype)init +{ + self = [super init]; + if (self) { + sslCiphers = [[NSMutableArray alloc] init]; + } + return self; +} + +- (void)dealloc +{ + SPClear(sslCiphers); + [super dealloc]; +} + #pragma mark - #pragma mark Preference pane protocol methods @@ -65,4 +90,241 @@ return NO; } +- (void)preferencePaneWillBeShown +{ + [self loadSSLCiphers]; + if(![[sslCipherView registeredDraggedTypes] containsObject:SPSSLCipherPboardTypeName]) + [sslCipherView registerForDraggedTypes:@[SPSSLCipherPboardTypeName]]; +} + +#pragma mark - +#pragma mark Custom SSH client methods + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + if([SPHiddenKeyFileVisibilityKey isEqualTo:keyPath]) { + [self updateHiddenFiles]; + } +} + +- (void)updateHiddenFiles +{ + [_currentFilePanel setShowsHiddenFiles:[prefs boolForKey:SPHiddenKeyFileVisibilityKey]]; +} + +- (IBAction)pickSSHClientViaFileBrowser:(id)sender +{ + _currentFilePanel = [NSOpenPanel openPanel]; + [_currentFilePanel setCanChooseFiles:YES]; + [_currentFilePanel setCanChooseDirectories:NO]; + [_currentFilePanel setAllowsMultipleSelection:NO]; + [_currentFilePanel setAccessoryView:hiddenFileView]; + [_currentFilePanel setResolvesAliases:NO]; + [self updateHiddenFiles]; + + [prefs addObserver:self + forKeyPath:SPHiddenKeyFileVisibilityKey + options:NSKeyValueObservingOptionNew + context:NULL]; + + [_currentFilePanel beginSheetModalForWindow:[_currentAlert window] completionHandler:^(NSInteger result) { + if(result == NSFileHandlingPanelOKButton) [sshClientPath setStringValue:[[_currentFilePanel URL] path]]; + + [prefs removeObserver:self forKeyPath:SPHiddenKeyFileVisibilityKey]; + + _currentFilePanel = nil; + }]; +} + +- (IBAction)pickSSHClient:(id)sender +{ + //take value from user defaults + NSString *oldPath = [prefs stringForKey:SPSSHClientPath]; + if([oldPath length]) [sshClientPath setStringValue:oldPath]; + + // set up dialog + _currentAlert = [[NSAlert alloc] init]; //needs to be ivar so we can attach the OpenPanel later + [_currentAlert setAccessoryView:sshClientPickerView]; + [_currentAlert setAlertStyle:NSWarningAlertStyle]; + [_currentAlert setMessageText:NSLocalizedString(@"Unsupported configuration!",@"Preferences : Network : Custom SSH client : warning dialog title")]; + [_currentAlert setInformativeText:NSLocalizedString(@"Sequel Pro only supports and is tested with the default OpenSSH client versions included with Mac OS X. Using different clients might cause connection issues, security risks or not work at all.\n\nPlease be aware, that we cannot provide support for such configurations.",@"Preferences : Network : Custom SSH client : warning dialog message")]; + [_currentAlert addButtonWithTitle:NSLocalizedString(@"OK",@"Preferences : Network : Custom SSH client : warning dialog : accept button")]; + [_currentAlert addButtonWithTitle:NSLocalizedString(@"Cancel",@"Preferences : Network : Custom SSH client : warning dialog : cancel button")]; + + if([_currentAlert runModal] == NSAlertFirstButtonReturn) { + //store new value to user defaults + NSString *newPath = [sshClientPath stringValue]; + if(![newPath length]) + [prefs removeObjectForKey:SPSSHClientPath]; + else + [prefs setObject:newPath forKey:SPSSHClientPath]; + } + + SPClear(_currentAlert); +} + +#pragma mark - +#pragma mark SSL cipher list methods + +- (void)loadSSLCiphers +{ + NSArray *supportedCiphers = [SPNetworkPreferencePane defaultSSLCipherList]; + [sslCiphers removeAllObjects]; + + NSString *userCipherString = [prefs stringForKey:SPSSLCipherListKey]; + if(userCipherString) { + //expand user list + NSArray *userCipherList = [userCipherString componentsSeparatedByString:@":"]; + + //compare the users list to the valid list and only copy over valid items + for (NSString *userCipher in userCipherList) { + if (![supportedCiphers containsObject:userCipher] || [sslCiphers containsObject:userCipher]) { + SPLog(@"Unknown ssl cipher in users' list: %@",userCipher); + continue; + } + [sslCiphers addObject:userCipher]; + } + + //now we do the reverse and add valid ciphers that are not yet in the users list. + //We'll just assume the ones not in the users' list are newer and therefore better and add + //them at the top + NSUInteger shift = 0; + for (NSString *validCipher in supportedCiphers) { + if(![sslCiphers containsObject:validCipher]) { + [sslCiphers insertObject:validCipher atIndex:shift++]; + } + } + } + else { + //no user prefs configured, so we'll just go with the defaults + [sslCiphers addObjectsFromArray:supportedCiphers]; + } + + //reload UI + [sslCipherView deselectAll:nil]; + [sslCipherView reloadData]; +} + +- (void)storeSSLCiphers +{ + NSString *flattedList = [sslCiphers componentsJoinedByString:@":"]; + [prefs setObject:flattedList forKey:SPSSLCipherListKey]; +} + +- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView +{ + return [sslCiphers count]; +} + +- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex +{ + NSString *value = [sslCiphers objectAtIndex:rowIndex]; + if ([value isEqualTo:SPSSLCipherListMarkerItem]) { + return NSLocalizedString(@"Disabled Cipher Suites", @"Preferences : Network : SSL Chiper suites : List seperator"); + } + return value; +} + +- (BOOL)tableView:(NSTableView *)tableView isGroupRow:(NSInteger)row +{ + return ([[sslCiphers objectAtIndex:row] isEqualTo:SPSSLCipherListMarkerItem]); +} + +- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)row +{ + return ![self tableView:tableView isGroupRow:row]; +} + +- (BOOL)tableView:(NSTableView *)aTableView acceptDrop:(id <NSDraggingInfo>)info row:(NSInteger)row dropOperation:(NSTableViewDropOperation)operation +{ + if(row < 0) return NO; //why is that even a signed int when all "indexes" are unsigned!? + + NSPasteboard *pboard = [info draggingPasteboard]; + NSArray *draggedItems = [NSKeyedUnarchiver unarchiveObjectWithData:[pboard dataForType:SPSSLCipherPboardTypeName]]; + + NSUInteger nextInsert = row; + for (NSString *item in draggedItems) { + NSUInteger oldPos = [sslCiphers indexOfObject:item]; + [sslCiphers removeObjectAtIndex:oldPos]; + + if(oldPos < (NSUInteger)row) { + // readjust position because we removed an object further up in the list, shifting all following indexes down by 1 + nextInsert--; + } + + [sslCiphers insertObject:item atIndex:nextInsert++]; + } + + NSMutableIndexSet *newSelection = [NSMutableIndexSet indexSet]; + for (NSString *item in draggedItems) { + [newSelection addIndex:[sslCiphers indexOfObject:item]]; + } + + [self storeSSLCiphers]; + [sslCipherView selectRowIndexes:newSelection byExtendingSelection:NO]; + + return YES; +} + +- (NSDragOperation)tableView:(NSTableView *)aTableView validateDrop:(id <NSDraggingInfo>)info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)operation +{ + //cannot drop something on another item in the list, only between them + return (operation == NSTableViewDropOn)? NSDragOperationNone : NSDragOperationMove; +} + +- (BOOL)tableView:(NSTableView *)aTableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pboard +{ + //the marker cannot be actively reordered + if ([rowIndexes containsIndex:[sslCiphers indexOfObject:SPSSLCipherListMarkerItem]]) + return NO; + + //put the names of the items on the pasteboard. easier to work with than indexes... + NSMutableArray *items = [NSMutableArray arrayWithCapacity:[rowIndexes count]]; + [rowIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { + [items addObject:[sslCiphers objectAtIndex:idx]]; + }]; + + NSData *arch = [NSKeyedArchiver archivedDataWithRootObject:items]; + [pboard declareTypes:@[SPSSLCipherPboardTypeName] owner:self]; + [pboard setData:arch forType:SPSSLCipherPboardTypeName]; + return YES; +} + +- (IBAction)resetCipherList:(id)sender +{ + //remove the user pref and reset the GUI + [prefs removeObjectForKey:SPSSLCipherListKey]; + [self loadSSLCiphers]; +} + ++ (NSArray *)defaultSSLCipherList +{ + //this is the default list as hardcoded in SPMySQLConnection.m + //Sadly there is no way to make MySQL give us the list of runtime-supported ciphers. + return @[@"DHE-RSA-AES256-SHA", + @"AES256-SHA", + @"DHE-RSA-AES128-SHA", + @"AES128-SHA", + @"AES256-RMD", + @"AES128-RMD", + @"DES-CBC3-RMD", + @"DHE-RSA-AES256-RMD", + @"DHE-RSA-AES128-RMD", + @"DHE-RSA-DES-CBC3-RMD", + @"RC4-SHA", + @"RC4-MD5", + @"DES-CBC3-SHA", + @"DES-CBC-SHA", + @"EDH-RSA-DES-CBC3-SHA", + @"EDH-RSA-DES-CBC-SHA", + SPSSLCipherListMarkerItem, //marker. disabled items below here + @"EDH-DSS-DES-CBC-SHA", + @"EDH-DSS-DES-CBC3-SHA", + @"DHE-DSS-AES128-SHA", + @"DHE-DSS-AES256-SHA", + @"DHE-DSS-DES-CBC3-RMD", + @"DHE-DSS-AES128-RMD", + @"DHE-DSS-AES256-RMD"]; +} + @end |