From 92e7b9a652b0d1806d732079574aea7270b8a2c0 Mon Sep 17 00:00:00 2001 From: rowanbeentje Date: Mon, 13 Sep 2010 22:26:54 +0000 Subject: - Implement support for MySQL over SSL for both TCP/IP and Socket connection modes. - Upgrade the MySQL binaries to version 5.1.50 (was 5.1.46) - Enable SSL support in the MySQL libraries (this leads to a large increase in library size, unfortunately) - Enable more optimisations in the MySQL libraries (especially --enable-assembler for faster in-library string processing and --with-mysqld-ldflags=-all-static) This completes support for Issue #27. --- Source/SPConnectionController.h | 28 ++++- Source/SPConnectionController.m | 224 +++++++++++++++++++++++++++++++++------- Source/SPDatabaseDocument.m | 35 ++++++- Source/SPPreferenceController.h | 12 ++- Source/SPPreferenceController.m | 118 ++++++++++++++++----- 5 files changed, 351 insertions(+), 66 deletions(-) (limited to 'Source') diff --git a/Source/SPConnectionController.h b/Source/SPConnectionController.h index 3aac3054..73622f40 100644 --- a/Source/SPConnectionController.h +++ b/Source/SPConnectionController.h @@ -77,6 +77,13 @@ NSString *database; NSString *socket; NSString *port; + int useSSL; + int sslKeyFileLocationEnabled; + NSString *sslKeyFileLocation; + int sslCertificateFileLocationEnabled; + NSString *sslCertificateFileLocation; + int sslCACertFileLocationEnabled; + NSString *sslCACertFileLocation; NSString *sshHost; NSString *sshUser; NSString *sshPassword; @@ -103,9 +110,14 @@ IBOutlet NSView *connectionResizeContainer; IBOutlet NSView *standardConnectionFormContainer; + IBOutlet NSView *standardConnectionSSLDetailsContainer; IBOutlet NSView *socketConnectionFormContainer; + IBOutlet NSView *socketConnectionSSLDetailsContainer; IBOutlet NSView *sshConnectionFormContainer; IBOutlet NSView *sshKeyLocationHelp; + IBOutlet NSView *sslKeyFileLocationHelp; + IBOutlet NSView *sslCertificateLocationHelp; + IBOutlet NSView *sslCACertLocationHelp; IBOutlet NSTextField *standardSQLHostField; IBOutlet NSTextField *sshSQLHostField; @@ -114,6 +126,12 @@ IBOutlet NSSecureTextField *sshPasswordField; IBOutlet NSSecureTextField *sshSSHPasswordField; IBOutlet NSButton *sshSSHKeyButton; + IBOutlet NSButton *standardSSLKeyFileButton; + IBOutlet NSButton *standardSSLCertificateButton; + IBOutlet NSButton *standardSSLCACertButton; + IBOutlet NSButton *socketSSLKeyFileButton; + IBOutlet NSButton *socketSSLCertificateButton; + IBOutlet NSButton *socketSSLCACertButton; IBOutlet NSButton *addToFavoritesButton; IBOutlet NSButton *connectButton; @@ -137,6 +155,13 @@ @property (readwrite, retain) NSString *database; @property (readwrite, retain) NSString *socket; @property (readwrite, retain) NSString *port; +@property (readwrite, assign) int useSSL; +@property (readwrite, assign) int sslKeyFileLocationEnabled; +@property (readwrite, retain) NSString *sslKeyFileLocation; +@property (readwrite, assign) int sslCertificateFileLocationEnabled; +@property (readwrite, retain) NSString *sslCertificateFileLocation; +@property (readwrite, assign) int sslCACertFileLocationEnabled; +@property (readwrite, retain) NSString *sslCACertFileLocation; @property (readwrite, retain) NSString *sshHost; @property (readwrite, retain) NSString *sshUser; @property (readwrite, retain) NSString *sshPassword; @@ -165,9 +190,10 @@ - (void)addConnectionToDocument; // Interface interaction -- (IBAction)chooseSSHKey:(id)sender; +- (IBAction)chooseKeyLocation:(id)sender; - (IBAction)editFavorites:(id)sender; - (IBAction)showHelp:(id)sender; +- (IBAction)updateSSLInterface:(id)sender; - (void)resizeTabViewToConnectionType:(NSUInteger)theType animating:(BOOL)animate; - (IBAction)sortFavorites:(id)sender; - (IBAction)reverseSortFavorites:(id)sender; diff --git a/Source/SPConnectionController.m b/Source/SPConnectionController.m index 258a0b0f..aefca1fd 100644 --- a/Source/SPConnectionController.m +++ b/Source/SPConnectionController.m @@ -51,6 +51,13 @@ @synthesize database; @synthesize socket; @synthesize port; +@synthesize useSSL; +@synthesize sslKeyFileLocationEnabled; +@synthesize sslKeyFileLocation; +@synthesize sslCertificateFileLocationEnabled; +@synthesize sslCertificateFileLocation; +@synthesize sslCACertFileLocationEnabled; +@synthesize sslCACertFileLocation; @synthesize sshHost; @synthesize sshUser; @synthesize sshPassword; @@ -130,8 +137,8 @@ if (tableRow < [favorites count]) { previousType = [[[favorites objectAtIndex:tableRow] objectForKey:@"type"] integerValue]; - [self resizeTabViewToConnectionType:[[[favorites objectAtIndex:tableRow] objectForKey:@"type"] integerValue] animating:NO]; [favoritesTable selectRowIndexes:[NSIndexSet indexSetWithIndex:tableRow] byExtendingSelection:NO]; + [self resizeTabViewToConnectionType:[[[favorites objectAtIndex:tableRow] objectForKey:@"type"] integerValue] animating:NO]; [favoritesTable scrollRowToVisible:[favoritesTable selectedRow]]; } else { previousType = SPTCPIPConnection; @@ -196,7 +203,35 @@ // Ensure that a socket connection is not inadvertently used if (![self checkHost]) return; - + + // If SSL keys have been supplied, verify they exist + if (([self type] == SPTCPIPConnection || [self type] == SPSocketConnection) && [self useSSL]) { + if (sslKeyFileLocationEnabled && sslKeyFileLocation + && ![[NSFileManager defaultManager] fileExistsAtPath:[sslKeyFileLocation stringByExpandingTildeInPath]]) + { + [self setSslKeyFileLocationEnabled:NSOffState]; + [self setSslKeyFileLocation:nil]; + SPBeginAlertSheet(NSLocalizedString(@"SSL Key File not found", @"SSL key file check error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocument parentWindow], self, nil, nil, NSLocalizedString(@"A SSL key file location was specified, but no file was found in the specified location. Please re-select the key file and try again.", @"SSL key file not found message")); + return; + } + if (sslCertificateFileLocationEnabled && sslCertificateFileLocation + && ![[NSFileManager defaultManager] fileExistsAtPath:[sslCertificateFileLocation stringByExpandingTildeInPath]]) + { + [self setSslCertificateFileLocationEnabled:NSOffState]; + [self setSslCertificateFileLocation:nil]; + SPBeginAlertSheet(NSLocalizedString(@"SSL Certificate File not found", @"SSL certificate file check error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocument parentWindow], self, nil, nil, NSLocalizedString(@"A SSL certificate location was specified, but no file was found in the specified location. Please re-select the certificate and try again.", @"SSL certificate file not found message")); + return; + } + if (sslCACertFileLocationEnabled && sslCACertFileLocation + && ![[NSFileManager defaultManager] fileExistsAtPath:[sslCACertFileLocation stringByExpandingTildeInPath]]) + { + [self setSslCACertFileLocationEnabled:NSOffState]; + [self setSslCACertFileLocation:nil]; + SPBeginAlertSheet(NSLocalizedString(@"SSL Certificate Authority File not found", @"SSL certificate authority file check error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocument parentWindow], self, nil, nil, NSLocalizedString(@"A SSL Certificate Authority certificate location was specified, but no file was found in the specified location. Please re-select the Certificate Authority certificate and try again.", @"SSL CA certificate file not found message")); + return; + } + } + // Basic details have validated - start the connection process animating isConnecting = YES; cancellingConnection = NO; @@ -479,47 +514,114 @@ #pragma mark Interface interaction /** - * Opens the SSH key selection window, ready to select a SSH key. + * Opens the SSH/SSL key selection window, ready to select a key file. */ -- (IBAction)chooseSSHKey:(id)sender +- (IBAction)chooseKeyLocation:(id)sender { [favoritesTable deselectAll:self]; NSString *directoryPath = nil; NSString *filePath = nil; + NSArray *permittedFileTypes = nil; + NSOpenPanel *openPanel = [NSOpenPanel openPanel]; - // If the custom key location is currently disabled - after the button - // action - leave it disabled and return without showing the sheet. - if (!sshKeyLocationEnabled) { - return; - } + // Switch details by sender. + // First, SSH keys: + if (sender == sshSSHKeyButton) { + + // If the custom key location is currently disabled - after the button + // action - leave it disabled and return without showing the sheet. + if (!sshKeyLocationEnabled) { + return; + } + + // Otherwise open a panel at the last or default location + if (sshKeyLocation && [sshKeyLocation length]) { + filePath = [sshKeyLocation lastPathComponent]; + directoryPath = [sshKeyLocation stringByDeletingLastPathComponent]; + } + + permittedFileTypes = [NSArray arrayWithObjects:@"pem", @"", nil]; + [openPanel setAccessoryView:sshKeyLocationHelp]; - // Otherwise open a panel at the last or default location - if (sshKeyLocation && [sshKeyLocation length]) { - filePath = [sshKeyLocation lastPathComponent]; - directoryPath = [sshKeyLocation stringByDeletingLastPathComponent]; + // SSL key file location: + } else if (sender == standardSSLKeyFileButton || sender == socketSSLKeyFileButton) { + if ([sender state] == NSOffState) { + [self setSslKeyFileLocation:nil]; + return; + } + permittedFileTypes = [NSArray arrayWithObjects:@"pem", @"key", @"", nil]; + [openPanel setAccessoryView:sslKeyFileLocationHelp]; + + // SSL certificate file location: + } else if (sender == standardSSLCertificateButton || sender == socketSSLCertificateButton) { + if ([sender state] == NSOffState) { + [self setSslCertificateFileLocation:nil]; + return; + } + permittedFileTypes = [NSArray arrayWithObjects:@"pem", @"cert", @"", nil]; + [openPanel setAccessoryView:sslCertificateLocationHelp]; + + // SSL CA certificate file location: + } else if (sender == standardSSLCACertButton || sender == socketSSLCACertButton) { + if ([sender state] == NSOffState) { + [self setSslCACertFileLocation:nil]; + return; + } + permittedFileTypes = [NSArray arrayWithObjects:@"pem", @"cert", @"", nil]; + [openPanel setAccessoryView:sslCACertLocationHelp]; } - NSOpenPanel *openPanel = [NSOpenPanel openPanel]; - [openPanel setAccessoryView:sshKeyLocationHelp]; [openPanel beginSheetForDirectory:directoryPath file:filePath - types:[NSArray arrayWithObjects:@"pem", @"", nil] + types:permittedFileTypes modalForWindow:[tableDocument parentWindow] modalDelegate:self - didEndSelector:@selector(chooseSSHKeySheetDidEnd:returnCode:contextInfo:) - contextInfo:nil]; + didEndSelector:@selector(chooseKeyLocationSheetDidEnd:returnCode:contextInfo:) + contextInfo:sender]; } /** - * Called after closing the SSH key selection sheet. + * Called after closing the SSH/SSL key selection sheet. */ -- (void)chooseSSHKeySheetDidEnd:(NSOpenPanel *)openPanel returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo +- (void)chooseKeyLocationSheetDidEnd:(NSOpenPanel *)openPanel returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo { - if (returnCode == NSCancelButton) { - [self setSshKeyLocationEnabled:NSOffState]; - return; + NSString *abbreviatedFileName = [[openPanel filename] stringByAbbreviatingWithTildeInPath]; + + // SSH key file selection + if (contextInfo == sshSSHKeyButton) { + if (returnCode == NSCancelButton) { + [self setSshKeyLocationEnabled:NSOffState]; + return; + } + [self setSshKeyLocation:abbreviatedFileName]; + + // SSL key file selection + } else if (contextInfo == standardSSLKeyFileButton || contextInfo == socketSSLKeyFileButton) { + if (returnCode == NSCancelButton) { + [self setSslKeyFileLocationEnabled:NSOffState]; + [self setSslKeyFileLocation:nil]; + return; + } + [self setSslKeyFileLocation:abbreviatedFileName]; + + // SSL certificate file selection + } else if (contextInfo == standardSSLCertificateButton || contextInfo == socketSSLCertificateButton) { + if (returnCode == NSCancelButton) { + [self setSslCertificateFileLocationEnabled:NSOffState]; + [self setSslCertificateFileLocation:nil]; + return; + } + [self setSslCertificateFileLocation:abbreviatedFileName]; + + // SSL CA certificate file selection + } else if (contextInfo == standardSSLCACertButton || contextInfo == socketSSLCACertButton) { + if (returnCode == NSCancelButton) { + [self setSslCACertFileLocationEnabled:NSOffState]; + [self setSslCACertFileLocation:nil]; + return; + } + [self setSslCACertFileLocation:abbreviatedFileName]; } - [self setSshKeyLocation:[[openPanel filename] stringByAbbreviatingWithTildeInPath]]; } /** @@ -544,6 +646,14 @@ [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:SPLOCALIZEDURL_CONNECTIONHELP]]; } +/** + * Resize parts of the interface to reflect SSL status. + */ +- (IBAction)updateSSLInterface:(id)sender +{ + [self resizeTabViewToConnectionType:[self type] animating:YES]; +} + #pragma mark - #pragma mark Connection details interaction and display @@ -638,9 +748,11 @@ switch (theType) { case SPTCPIPConnection: targetResizeRect = [standardConnectionFormContainer frame]; + if ([self useSSL]) additionalFormHeight += [standardConnectionSSLDetailsContainer frame].size.height; break; case SPSocketConnection: targetResizeRect = [socketConnectionFormContainer frame]; + if ([self useSSL]) additionalFormHeight += [socketConnectionSSLDetailsContainer frame].size.height; break; case SPSSHTunnelConnection: targetResizeRect = [sshConnectionFormContainer frame]; @@ -761,18 +873,29 @@ if (connectionSSHKeychainItemAccount) [connectionSSHKeychainItemAccount release], connectionSSHKeychainItemAccount = nil; // Update key-value properties from the selected favourite, using empty strings where not found - [self setType:([self valueForKeyPath:@"selectedFavorite.type"] ? [[self valueForKeyPath:@"selectedFavorite.type"] integerValue] : SPTCPIPConnection)]; - [self setName:([self valueForKeyPath:@"selectedFavorite.name"] ? [self valueForKeyPath:@"selectedFavorite.name"] : @"")]; - [self setHost:([self valueForKeyPath:@"selectedFavorite.host"] ? [self valueForKeyPath:@"selectedFavorite.host"] : @"")]; - [self setSocket:([self valueForKeyPath:@"selectedFavorite.socket"] ? [self valueForKeyPath:@"selectedFavorite.socket"] : @"")]; - [self setUser:([self valueForKeyPath:@"selectedFavorite.user"] ? [self valueForKeyPath:@"selectedFavorite.user"] : @"")]; - [self setPort:([self valueForKeyPath:@"selectedFavorite.port"] ? [self valueForKeyPath:@"selectedFavorite.port"] : @"")]; - [self setDatabase:([self valueForKeyPath:@"selectedFavorite.database"] ? [self valueForKeyPath:@"selectedFavorite.database"] : @"")]; - [self setSshHost:([self valueForKeyPath:@"selectedFavorite.sshHost"] ? [self valueForKeyPath:@"selectedFavorite.sshHost"] : @"")]; - [self setSshUser:([self valueForKeyPath:@"selectedFavorite.sshUser"] ? [self valueForKeyPath:@"selectedFavorite.sshUser"] : @"")]; - [self setSshKeyLocationEnabled:([self valueForKeyPath:@"selectedFavorite.sshKeyLocationEnabled"] ? [[self valueForKeyPath:@"selectedFavorite.sshKeyLocationEnabled"] intValue] : NSOffState)]; - [self setSshKeyLocation:([self valueForKeyPath:@"selectedFavorite.sshKeyLocation"] ? [self valueForKeyPath:@"selectedFavorite.sshKeyLocation"] : @"")]; - [self setSshPort:([self valueForKeyPath:@"selectedFavorite.sshPort"] ? [self valueForKeyPath:@"selectedFavorite.sshPort"] : @"")]; + NSDictionary *fav = [self selectedFavorite]; + [self setType:([fav objectForKey:@"type"] ? [[fav objectForKey:@"type"] integerValue] : SPTCPIPConnection)]; + [self setName:([fav objectForKey:@"name"] ? [fav objectForKey:@"name"] : @"")]; + [self setHost:([fav objectForKey:@"host"] ? [fav objectForKey:@"host"] : @"")]; + [self setSocket:([fav objectForKey:@"socket"] ? [fav objectForKey:@"socket"] : @"")]; + [self setUser:([fav objectForKey:@"user"] ? [fav objectForKey:@"user"] : @"")]; + [self setPort:([fav objectForKey:@"port"] ? [fav objectForKey:@"port"] : @"")]; + [self setUseSSL:([fav objectForKey:@"useSSL"] ? [[fav objectForKey:@"useSSL"] intValue] : NSOffState)]; + [self setSslKeyFileLocationEnabled:([fav objectForKey:@"sslKeyFileLocationEnabled"] ? [[fav objectForKey:@"sslKeyFileLocationEnabled"] intValue] : NSOffState)]; + [self setSslKeyFileLocation:([fav objectForKey:@"sslKeyFileLocation"] ? [fav objectForKey:@"sslKeyFileLocation"] : @"")]; + [self setSslCertificateFileLocationEnabled:([fav objectForKey:@"sslCertificateFileLocationEnabled"] ? [[fav objectForKey:@"sslCertificateFileLocationEnabled"] intValue] : NSOffState)]; + [self setSslCertificateFileLocation:([fav objectForKey:@"sslCertificateFileLocation"] ? [fav objectForKey:@"sslCertificateFileLocation"] : @"")]; + [self setSslCACertFileLocationEnabled:([fav objectForKey:@"sslCACertFileLocationEnabled"] ? [[fav objectForKey:@"sslCACertFileLocationEnabled"] intValue] : NSOffState)]; + [self setSslCACertFileLocation:([fav objectForKey:@"sslCACertFileLocation"] ? [fav objectForKey:@"sslCACertFileLocation"] : @"")]; + [self setDatabase:([fav objectForKey:@"database"] ? [fav objectForKey:@"database"] : @"")]; + [self setSshHost:([fav objectForKey:@"sshHost"] ? [fav objectForKey:@"sshHost"] : @"")]; + [self setSshUser:([fav objectForKey:@"sshUser"] ? [fav objectForKey:@"sshUser"] : @"")]; + [self setSshKeyLocationEnabled:([fav objectForKey:@"sshKeyLocationEnabled"] ? [[fav objectForKey:@"sshKeyLocationEnabled"] intValue] : NSOffState)]; + [self setSshKeyLocation:([fav objectForKey:@"sshKeyLocation"] ? [fav objectForKey:@"sshKeyLocation"] : @"")]; + [self setSshPort:([fav objectForKey:@"sshPort"] ? [fav objectForKey:@"sshPort"] : @"")]; + + // Trigger an interface update + [self resizeTabViewToConnectionType:[self type] animating:YES]; // Check whether the password exists in the keychain, and if so add it; also record the // keychain details so we can pass around only those details if the password doesn't change @@ -863,6 +986,13 @@ if ([self socket]) [newFavorite setObject:[self socket] forKey:@"socket"]; if ([self user]) [newFavorite setObject:[self user] forKey:@"user"]; if ([self port]) [newFavorite setObject:[self port] forKey:@"port"]; + if ([self useSSL]) [newFavorite setObject:[NSNumber numberWithInt:[self useSSL]] forKey:@"useSSL"]; + [newFavorite setObject:[NSNumber numberWithInt:[self sslKeyFileLocationEnabled]] forKey:@"sslKeyFileLocationEnabled"]; + if ([self sslKeyFileLocation]) [newFavorite setObject:[self sslKeyFileLocation] forKey:@"sslKeyFileLocation"]; + [newFavorite setObject:[NSNumber numberWithInt:[self sslCertificateFileLocationEnabled]] forKey:@"sslCertificateFileLocationEnabled"]; + if ([self sslCertificateFileLocation]) [newFavorite setObject:[self sslCertificateFileLocation] forKey:@"sslCertificateFileLocation"]; + [newFavorite setObject:[NSNumber numberWithInt:[self sslCACertFileLocationEnabled]] forKey:@"sslCACertFileLocationEnabled"]; + if ([self sslCACertFileLocation]) [newFavorite setObject:[self sslCACertFileLocation] forKey:@"sslCACertFileLocation"]; if ([self database]) [newFavorite setObject:[self database] forKey:@"database"]; if ([self sshHost]) [newFavorite setObject:[self sshHost] forKey:@"sshHost"]; if ([self sshUser]) [newFavorite setObject:[self sshUser] forKey:@"sshUser"]; @@ -1248,7 +1378,16 @@ [progressIndicator stopAnimation:self]; [progressIndicatorText setHidden:YES]; [addToFavoritesButton setHidden:NO]; - + + // If SSL was enabled, check it was established correctly + if (useSSL && ([self type] == SPTCPIPConnection || [self type] == SPSocketConnection)) { + if (![mySQLConnection isConnectedViaSSL]) { + SPBeginAlertSheet(NSLocalizedString(@"SSL connection not established", @"SSL requested but not used title"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocument parentWindow], nil, nil, nil, NSLocalizedString(@"You requested that the connection should be established using SSL, but MySQL made the connection without SSL.\n\nThis may be because the server does not support SSL connections, or has SSL disabled; or insufficient details were supplied to establish an SSL connection.\n\nThis connection is not encrypted.", @"SSL connection requested but not established error detail")); + } else { + [tableDocument setStatusIconToImageWithName:@"titlebarlock"]; + } + } + // Re-enable favorites table view [favoritesTable setEnabled:YES]; [favoritesTable display]; @@ -1289,10 +1428,19 @@ if (!connectionKeychainItemName && [self password]) { [mySQLConnection setPassword:[self password]]; } + + // Enable SSL if set + if ([self useSSL]) { + [mySQLConnection setSSL:YES + usingKeyFilePath:[self sslKeyFileLocationEnabled] ? [self sslKeyFileLocation] : nil + certificatePath:[self sslCertificateFileLocationEnabled] ? [self sslCertificateFileLocation] : nil + certificateAuthorityCertificatePath:[self sslCACertFileLocationEnabled] ? [self sslCACertFileLocation] : nil]; + } + // Connection delegate must be set before actual connection attempt is made [mySQLConnection setDelegate:tableDocument]; - + // Set whether or not we should enable delegate logging according to the prefs [mySQLConnection setDelegateQueryLogging:[prefs boolForKey:SPConsoleEnableLogging]]; diff --git a/Source/SPDatabaseDocument.m b/Source/SPDatabaseDocument.m index 648a7691..1a0ae6d6 100644 --- a/Source/SPDatabaseDocument.m +++ b/Source/SPDatabaseDocument.m @@ -267,10 +267,17 @@ [connectionController setHost:@""]; [connectionController setPort:@""]; [connectionController setSocket:@""]; + [connectionController setUseSSL:NSOffState]; + [connectionController setSslKeyFileLocationEnabled:NSOffState]; + [connectionController setSslKeyFileLocation:nil]; + [connectionController setSslCertificateFileLocationEnabled:NSOffState]; + [connectionController setSslCertificateFileLocation:nil]; + [connectionController setSslCACertFileLocationEnabled:NSOffState]; + [connectionController setSslCACertFileLocation:nil]; [connectionController setSshHost:@""]; [connectionController setSshUser:@""]; [connectionController setSshKeyLocationEnabled:NSOffState]; - [connectionController setSshKeyLocation:@""]; + [connectionController setSshKeyLocation:nil]; [connectionController setSshPort:@""]; [connectionController setDatabase:@""]; [connectionController setPassword:nil]; @@ -468,6 +475,22 @@ [connectionController setHost:[connection objectForKey:@"host"]]; if([connection objectForKey:@"port"]) [connectionController setPort:[NSString stringWithFormat:@"%ld", (long)[[connection objectForKey:@"port"] integerValue]]]; + + if([connection objectForKey:@"useSSL"]) + [connectionController setUseSSL:[[connection objectForKey:@"useSSL"] intValue]]; + if([connection objectForKey:@"sslKeyFileLocationEnabled"]) + [connectionController setSslKeyFileLocationEnabled:[[connection objectForKey:@"sslKeyFileLocationEnabled"] intValue]]; + if([connection objectForKey:@"sslKeyFileLocation"]) + [connectionController setSslKeyFileLocation:[connection objectForKey:@"sslKeyFileLocation"]]; + if([connection objectForKey:@"sslCertificateFileLocationEnabled"]) + [connectionController setSslCertificateFileLocationEnabled:[[connection objectForKey:@"sslCertificateFileLocationEnabled"] intValue]]; + if([connection objectForKey:@"sslCertificateFileLocation"]) + [connectionController setSslCertificateFileLocation:[connection objectForKey:@"sslCertificateFileLocation"]]; + if([connection objectForKey:@"sslCACertFileLocationEnabled"]) + [connectionController setSslCACertFileLocationEnabled:[[connection objectForKey:@"sslCACertFileLocationEnabled"] intValue]]; + if([connection objectForKey:@"sslCACertFileLocation"]) + [connectionController setSslCACertFileLocation:[connection objectForKey:@"sslCACertFileLocation"]]; + if([connection objectForKey:@"kcid"] && [(NSString *)[connection objectForKey:@"kcid"] length]) [self setKeychainID:[connection objectForKey:@"kcid"]]; @@ -530,6 +553,8 @@ [spfDocData setObject:[NSNumber numberWithBool:NO] forKey:@"auto_connect"]; + [connectionController updateSSLInterface:self]; + if([spf objectForKey:@"auto_connect"] && [[spf valueForKey:@"auto_connect"] boolValue]) { [spfDocData setObject:[NSNumber numberWithBool:YES] forKey:@"auto_connect"]; [connectionController initiateConnection:self]; @@ -3292,6 +3317,14 @@ [connection setObject:[self host] forKey:@"host"]; [connection setObject:[self user] forKey:@"user"]; + [connection setObject:[NSNumber numberWithInt:[connectionController useSSL]] forKey:@"useSSL"]; + [connection setObject:[NSNumber numberWithInt:[connectionController sslKeyFileLocationEnabled]] forKey:@"sslKeyFileLocationEnabled"]; + [connection setObject:[connectionController sslKeyFileLocation] forKey:@"sslKeyFileLocation"]; + [connection setObject:[NSNumber numberWithInt:[connectionController sslCertificateFileLocationEnabled]] forKey:@"sslCertificateFileLocationEnabled"]; + [connection setObject:[connectionController sslCertificateFileLocation] forKey:@"sslCertificateFileLocation"]; + [connection setObject:[NSNumber numberWithInt:[connectionController sslCACertFileLocationEnabled]] forKey:@"sslCACertFileLocationEnabled"]; + [connection setObject:[connectionController sslCACertFileLocation] forKey:@"sslCACertFileLocation"]; + switch([connectionController type]) { case SPTCPIPConnection: aString = @"SPTCPIPConnection"; diff --git a/Source/SPPreferenceController.h b/Source/SPPreferenceController.h index 7d8e0375..9f85ecc1 100644 --- a/Source/SPPreferenceController.h +++ b/Source/SPPreferenceController.h @@ -57,8 +57,18 @@ IBOutlet NSTextField *favoriteUserTextFieldSocket; IBOutlet NSTextField *favoriteUserTextFieldSSH; IBOutlet NSTextField *favoriteHostTextFieldSSH; + IBOutlet NSButton *sshSSHKeyButton; + IBOutlet NSButton *standardSSLKeyFileButton; + IBOutlet NSButton *standardSSLCertificateButton; + IBOutlet NSButton *standardSSLCACertButton; + IBOutlet NSButton *socketSSLKeyFileButton; + IBOutlet NSButton *socketSSLCertificateButton; + IBOutlet NSButton *socketSSLCACertButton; IBOutlet NSMenuItem *favoritesSortByMenuItem; IBOutlet NSView *sshKeyLocationHelp; + IBOutlet NSView *sslKeyFileLocationHelp; + IBOutlet NSView *sslCertificateLocationHelp; + IBOutlet NSView *sslCACertLocationHelp; IBOutlet NSWindow *enterNameWindow; IBOutlet NSTextField *enterNameLabel; @@ -133,7 +143,7 @@ - (IBAction)closePanelSheet:(id)sender; - (IBAction)duplicateTheme:(id)sender; - (IBAction)removeTheme:(id)sender; -- (IBAction)chooseSSHKey:(id)sender; +- (IBAction)chooseKeyLocation:(id)sender; // Toolbar item IBAction methods - (IBAction)displayGeneralPreferences:(id)sender; diff --git a/Source/SPPreferenceController.m b/Source/SPPreferenceController.m index c1f3877a..d05868b9 100644 --- a/Source/SPPreferenceController.m +++ b/Source/SPPreferenceController.m @@ -409,8 +409,8 @@ NSNumber *favoriteid = [NSNumber numberWithInteger:[[NSString stringWithFormat:@"%f", [[NSDate date] timeIntervalSince1970]] hash]]; // Create default favorite - NSMutableDictionary *favorite = [NSMutableDictionary dictionaryWithObjects:[NSArray arrayWithObjects:@"New Favorite", [NSNumber numberWithInteger:0], @"", @"", @"", @"", @"", @"", @"", [NSNumber numberWithInt:NSOffState], @"", @"", favoriteid, nil] - forKeys:[NSArray arrayWithObjects:@"name", @"type", @"host", @"socket", @"user", @"port", @"database", @"sshHost", @"sshUser", @"sshKeyLocationEnabled", @"sshKeyLocation", @"sshPort", @"id", nil]]; + NSMutableDictionary *favorite = [NSMutableDictionary dictionaryWithObjects:[NSArray arrayWithObjects:@"New Favorite", [NSNumber numberWithInteger:0], @"", @"", @"", @"", [NSNumber numberWithInt:NSOffState], [NSNumber numberWithInt:NSOffState], [NSNumber numberWithInt:NSOffState], [NSNumber numberWithInt:NSOffState], @"", @"", @"", [NSNumber numberWithInt:NSOffState], @"", @"", favoriteid, nil] + forKeys:[NSArray arrayWithObjects:@"name", @"type", @"host", @"socket", @"user", @"port", @"useSSL", @"sslKeyFileLocationEnabled", @"sslCertificateFileLocationEnabled", @"sslCACertFileLocationEnabled", @"database", @"sshHost", @"sshUser", @"sshKeyLocationEnabled", @"sshKeyLocation", @"sshPort", @"id", nil]]; [favoritesController addObject:favorite]; [favoritesController setSelectedObjects:[NSArray arrayWithObject:favorite]]; @@ -686,47 +686,115 @@ } /** - * Opens the SSH key selection window, ready to select a SSH key. + * Opens the SSH/SSL key selection window, ready to select a key file. */ -- (IBAction)chooseSSHKey:(id)sender + - (IBAction)chooseKeyLocation:(id)sender { NSString *directoryPath = nil; NSString *filePath = nil; + NSArray *permittedFileTypes = nil; + NSOpenPanel *openPanel = [NSOpenPanel openPanel]; - // If the custom key location is currently disabled - after the button - // action - leave it disabled and return without showing the sheet. - if (![[favoritesController valueForKeyPath:@"selection.sshKeyLocationEnabled"] intValue]) { - return; - } + // Switch details by sender. + // First, SSH keys: + if (sender == sshSSHKeyButton) { - // Otherwise open a panel at the last or default location - if ([favoritesController valueForKeyPath:@"selection.sshKeyLocation"] && [[favoritesController valueForKeyPath:@"selection.sshKeyLocation"] length]) { - filePath = [[favoritesController valueForKeyPath:@"selection.sshKeyLocation"] lastPathComponent]; - directoryPath = [[favoritesController valueForKeyPath:@"selection.sshKeyLocation"] stringByDeletingLastPathComponent]; - } + // If the custom key location is currently disabled - after the button + // action - leave it disabled and return without showing the sheet. + if (![favoritesController valueForKeyPath:@"selection.sshKeyLocationEnabled"]) { + return; + } + // Otherwise open a panel at the last or default location + NSString *sshKeyLocation = [favoritesController valueForKeyPath:@"selection.sshKeyLocation"]; + if (sshKeyLocation && [sshKeyLocation length]) { + filePath = [sshKeyLocation lastPathComponent]; + directoryPath = [sshKeyLocation stringByDeletingLastPathComponent]; + } + + permittedFileTypes = [NSArray arrayWithObjects:@"pem", @"", nil]; + [openPanel setAccessoryView:sshKeyLocationHelp]; + + // SSL key file location: + } else if (sender == standardSSLKeyFileButton || sender == socketSSLKeyFileButton) { + if ([sender state] == NSOffState) { + [favoritesController setValue:nil forKeyPath:@"selection.sslKeyFileLocation"]; + return; + } + permittedFileTypes = [NSArray arrayWithObjects:@"pem", @"key", @"", nil]; + [openPanel setAccessoryView:sslKeyFileLocationHelp]; + + // SSL certificate file location: + } else if (sender == standardSSLCertificateButton || sender == socketSSLCertificateButton) { + if ([sender state] == NSOffState) { + [favoritesController setValue:nil forKeyPath:@"selection.sslCertificateFileLocation"]; + return; + } + permittedFileTypes = [NSArray arrayWithObjects:@"pem", @"cert", @"", nil]; + [openPanel setAccessoryView:sslCertificateLocationHelp]; + + // SSL CA certificate file location: + } else if (sender == standardSSLCACertButton || sender == socketSSLCACertButton) { + if ([sender state] == NSOffState) { + [favoritesController setValue:nil forKeyPath:@"selection.sslCACertFileLocation"]; + return; + } + permittedFileTypes = [NSArray arrayWithObjects:@"pem", @"cert", @"", nil]; + [openPanel setAccessoryView:sslCACertLocationHelp]; + } - NSOpenPanel *openPanel = [NSOpenPanel openPanel]; - [openPanel setAccessoryView:sshKeyLocationHelp]; [openPanel beginSheetForDirectory:directoryPath file:filePath - types:[NSArray arrayWithObjects:@"pem", @"", nil] + types:permittedFileTypes modalForWindow:preferencesWindow modalDelegate:self - didEndSelector:@selector(chooseSSHKeySheetDidEnd:returnCode:contextInfo:) - contextInfo:nil]; + didEndSelector:@selector(chooseKeyLocationSheetDidEnd:returnCode:contextInfo:) + contextInfo:sender]; } /** - * Called after closing the SSH key selection sheet. + * Called after closing the SSH/SSL key selection sheet. */ -- (void)chooseSSHKeySheetDidEnd:(NSOpenPanel *)openPanel returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo +- (void)chooseKeyLocationSheetDidEnd:(NSOpenPanel *)openPanel returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo { - if (returnCode == NSCancelButton) { - [favoritesController setValue:[NSNumber numberWithInt:NSOffState] forKeyPath:@"selection.sshKeyLocationEnabled"]; - return; + NSString *abbreviatedFileName = [[openPanel filename] stringByAbbreviatingWithTildeInPath]; + + // SSH key file selection + if (contextInfo == sshSSHKeyButton) { + if (returnCode == NSCancelButton) { + [favoritesController setValue:[NSNumber numberWithInt:NSOffState] forKeyPath:@"selection.sshKeyLocationEnabled"]; + return; + } + [favoritesController setValue:abbreviatedFileName forKeyPath:@"selection.sshKeyLocation"]; + [self setSshKeyLocation:abbreviatedFileName]; + + // SSL key file selection + } else if (contextInfo == standardSSLKeyFileButton || contextInfo == socketSSLKeyFileButton) { + if (returnCode == NSCancelButton) { + [favoritesController setValue:[NSNumber numberWithInt:NSOffState] forKeyPath:@"selection.sslKeyFileLocationEnabled"]; + [favoritesController setValue:nil forKeyPath:@"selection.sslKeyFileLocation"]; + return; + } + [favoritesController setValue:abbreviatedFileName forKeyPath:@"selection.sslKeyFileLocation"]; + + // SSL certificate file selection + } else if (contextInfo == standardSSLCertificateButton || contextInfo == socketSSLCertificateButton) { + if (returnCode == NSCancelButton) { + [favoritesController setValue:[NSNumber numberWithInt:NSOffState] forKeyPath:@"selection.sslCertificateFileLocationEnabled"]; + [favoritesController setValue:nil forKeyPath:@"selection.sslCertificateFileLocation"]; + return; + } + [favoritesController setValue:abbreviatedFileName forKeyPath:@"selection.sslCertificateFileLocation"]; + + // SSL CA certificate file selection + } else if (contextInfo == standardSSLCACertButton || contextInfo == socketSSLCACertButton) { + if (returnCode == NSCancelButton) { + [favoritesController setValue:[NSNumber numberWithInt:NSOffState] forKeyPath:@"selection.sslCACertFileLocationEnabled"]; + [favoritesController setValue:nil forKeyPath:@"selection.sslCACertFileLocation"]; + return; + } + [favoritesController setValue:abbreviatedFileName forKeyPath:@"selection.sslCACertFileLocation"]; } - [favoritesController setValue:[[openPanel filename] stringByAbbreviatingWithTildeInPath] forKeyPath:@"selection.sshKeyLocation"]; } #pragma mark - -- cgit v1.2.3