diff options
-rw-r--r-- | Source/SPConnectionController.m | 16 | ||||
-rw-r--r-- | Source/SPSSHTunnel.h | 72 | ||||
-rw-r--r-- | Source/SPSSHTunnel.m | 138 |
3 files changed, 131 insertions, 95 deletions
diff --git a/Source/SPConnectionController.m b/Source/SPConnectionController.m index c521213c..41f44a1d 100644 --- a/Source/SPConnectionController.m +++ b/Source/SPConnectionController.m @@ -382,16 +382,28 @@ - (void)sshTunnelCallback:(SPSSHTunnel *)theTunnel { if (cancellingConnection) return; + NSInteger newState = [theTunnel state]; + + // If the user cancelled the password prompt dialog + if ([theTunnel passwordPromptCancelled]) { + [self _restoreConnectionInterface]; + + return; + } if (newState == PROXY_STATE_IDLE) { [tableDocument setTitlebarStatus:NSLocalizedString(@"SSH Disconnected", @"SSH disconnected titlebar marker")]; + [self failConnectionWithTitle:NSLocalizedString(@"SSH connection failed!", @"SSH connection failed title") errorMessage:[theTunnel lastError] detail:[sshTunnel debugMessages]]; [self _restoreConnectionInterface]; - } else if (newState == PROXY_STATE_CONNECTED) { + } + else if (newState == PROXY_STATE_CONNECTED) { [tableDocument setTitlebarStatus:NSLocalizedString(@"SSH Connected", @"SSH connected titlebar marker")]; + [self initiateMySQLConnection]; - } else { + } + else { [tableDocument setTitlebarStatus:NSLocalizedString(@"SSH Connecting…", @"SSH connecting titlebar marker")]; } } diff --git a/Source/SPSSHTunnel.h b/Source/SPSSHTunnel.h index 067cf42a..4afec738 100644 --- a/Source/SPSSHTunnel.h +++ b/Source/SPSSHTunnel.h @@ -27,18 +27,11 @@ @interface SPSSHTunnel : NSObject <MCPConnectionProxy> { - IBOutlet NSWindow *sshQuestionDialog; - IBOutlet NSTextField *sshQuestionText; - IBOutlet NSButton *sshPasswordKeychainCheckbox; - IBOutlet NSWindow *sshPasswordDialog; - IBOutlet NSTextField *sshPasswordText; - IBOutlet NSSecureTextField *sshPasswordField; + id delegate; NSWindow *parentWindow; NSTask *task; NSPipe *standardError; - id delegate; - SEL stateChangeSelector; NSConnection *tunnelConnection; NSString *lastError; NSString *tunnelConnectionName; @@ -53,9 +46,6 @@ NSString *identityFilePath; NSMutableArray *debugMessages; NSLock *debugMessagesLock; - BOOL useHostFallback; - BOOL requestedResponse; - BOOL passwordInKeychain; NSInteger sshPort; NSInteger remotePort; NSInteger localPort; @@ -64,29 +54,45 @@ NSLock *answerAvailableLock; NSString *currentKeyName; + + SEL stateChangeSelector; + + BOOL useHostFallback; + BOOL requestedResponse; + BOOL passwordInKeychain; + BOOL passwordPromptCancelled; + + IBOutlet NSWindow *sshQuestionDialog; + IBOutlet NSTextField *sshQuestionText; + IBOutlet NSButton *sshPasswordKeychainCheckbox; + IBOutlet NSWindow *sshPasswordDialog; + IBOutlet NSTextField *sshPasswordText; + IBOutlet NSSecureTextField *sshPasswordField; } -- (id) initToHost:(NSString *) theHost port:(NSInteger) thePort login:(NSString *) theLogin tunnellingToPort:(NSInteger) targetPort onHost:(NSString *) targetHost; -- (BOOL) setConnectionStateChangeSelector:(SEL)theStateChangeSelector delegate:(id)theDelegate; -- (void) setParentWindow:(NSWindow *)theWindow; -- (BOOL) setPasswordKeychainName:(NSString *)theName account:(NSString *)theAccount; -- (BOOL) setPassword:(NSString *)thePassword; -- (BOOL) setKeyFilePath:(NSString *)thePath; -- (NSInteger) state; -- (NSString *) lastError; -- (NSString *) debugMessages; -- (NSInteger) localPort; -- (NSInteger) localPortFallback; -- (void) connect; -- (void) launchTask:(id) dummy; -- (void) disconnect; -- (void) standardErrorHandler:(NSNotification*)aNotification; -- (NSString *) getPasswordWithVerificationHash:(NSString *)theHash; -- (BOOL) getResponseForQuestion:(NSString *)theQuestion; -- (void) workerGetResponseForQuestion:(NSString *)theQuestion; -- (NSString *) getPasswordForQuery:(NSString *)theQuery verificationHash:(NSString *)theHash; -- (void) workerGetPasswordForQuery:(NSString *)theQuery; -- (IBAction) closeSSHQuestionSheet:(id)sender; -- (IBAction) closeSSHPasswordSheet:(id)sender; +@property (readonly) BOOL passwordPromptCancelled; + +- (id)initToHost:(NSString *)theHost port:(NSInteger)thePort login:(NSString *)theLogin tunnellingToPort:(NSInteger)targetPort onHost:(NSString *)targetHost; +- (BOOL)setConnectionStateChangeSelector:(SEL)theStateChangeSelector delegate:(id)theDelegate; +- (void)setParentWindow:(NSWindow *)theWindow; +- (BOOL)setPasswordKeychainName:(NSString *)theName account:(NSString *)theAccount; +- (BOOL)setPassword:(NSString *)thePassword; +- (BOOL)setKeyFilePath:(NSString *)thePath; +- (NSInteger)state; +- (NSString *)lastError; +- (NSString *)debugMessages; +- (NSInteger)localPort; +- (NSInteger)localPortFallback; +- (void)connect; +- (void)launchTask:(id)dummy; +- (void)disconnect; +- (void)standardErrorHandler:(NSNotification*)aNotification; +- (NSString *)getPasswordWithVerificationHash:(NSString *)theHash; +- (BOOL)getResponseForQuestion:(NSString *)theQuestion; +- (void)workerGetResponseForQuestion:(NSString *)theQuestion; +- (NSString *)getPasswordForQuery:(NSString *)theQuery verificationHash:(NSString *)theHash; +- (void)workerGetPasswordForQuery:(NSString *)theQuery; +- (IBAction)closeSSHQuestionSheet:(id)sender; +- (IBAction)closeSSHPasswordSheet:(id)sender; @end diff --git a/Source/SPSSHTunnel.m b/Source/SPSSHTunnel.m index 0d4b5993..e112e624 100644 --- a/Source/SPSSHTunnel.m +++ b/Source/SPSSHTunnel.m @@ -32,57 +32,62 @@ @implementation SPSSHTunnel +@synthesize passwordPromptCancelled; + /* * Initialise with the supplied connection details. Host, login and port should all be provided. * The password can either be set later via setPassword:, which stores the password locally and is * therefore not recommended, or via setPasswordKeychainName:, which will use the keychain on-demand * and is therefore preferred. */ -- (id) initToHost:(NSString *) theHost port:(NSInteger) thePort login:(NSString *) theLogin tunnellingToPort:(NSInteger) targetPort onHost:(NSString *) targetHost +- (id)initToHost:(NSString *)theHost port:(NSInteger)thePort login:(NSString *)theLogin tunnellingToPort:(NSInteger)targetPort onHost:(NSString *)targetHost { if (!theHost || !targetPort || !targetHost) return nil; - self = [super init]; - - // Store the connection settings as appropriate - sshHost = [[NSString alloc] initWithString:theHost]; - sshLogin = [[NSString alloc] initWithString:(theLogin?theLogin:@"")]; - sshPort = thePort; - useHostFallback = [theHost isEqualToString:targetHost]; - remoteHost = [[NSString alloc] initWithString:targetHost]; - remotePort = targetPort; - delegate = nil; - stateChangeSelector = nil; - lastError = nil; - debugMessages = [[NSMutableArray alloc] init]; - debugMessagesLock = [[NSLock alloc] init]; - answerAvailableLock = [[NSLock alloc] init]; - - // Set up a connection for use by the tunnel process - tunnelConnectionName = [[NSString alloc] initWithFormat:@"SequelPro-%lu", (unsigned long)[[NSString stringWithFormat:@"%f", [[NSDate date] timeIntervalSince1970]] hash]]; - tunnelConnectionVerifyHash = [[NSString alloc] initWithFormat:@"%lu", (unsigned long)[[NSString stringWithFormat:@"%f-seeded", [[NSDate date] timeIntervalSince1970]] hash]]; - tunnelConnection = [NSConnection new]; - [tunnelConnection runInNewThread]; - [tunnelConnection removeRunLoop:[NSRunLoop currentRunLoop]]; - [tunnelConnection setRootObject:self]; - if ([tunnelConnection registerName:tunnelConnectionName] == NO) { - return nil; + if ((self = [super init])) { + + // Store the connection settings as appropriate + sshHost = [[NSString alloc] initWithString:theHost]; + sshLogin = [[NSString alloc] initWithString:(theLogin?theLogin:@"")]; + sshPort = thePort; + useHostFallback = [theHost isEqualToString:targetHost]; + remoteHost = [[NSString alloc] initWithString:targetHost]; + remotePort = targetPort; + delegate = nil; + stateChangeSelector = nil; + lastError = nil; + debugMessages = [[NSMutableArray alloc] init]; + debugMessagesLock = [[NSLock alloc] init]; + answerAvailableLock = [[NSLock alloc] init]; + + // Set up a connection for use by the tunnel process + tunnelConnectionName = [[NSString alloc] initWithFormat:@"SequelPro-%lu", (unsigned long)[[NSString stringWithFormat:@"%f", [[NSDate date] timeIntervalSince1970]] hash]]; + tunnelConnectionVerifyHash = [[NSString alloc] initWithFormat:@"%lu", (unsigned long)[[NSString stringWithFormat:@"%f-seeded", [[NSDate date] timeIntervalSince1970]] hash]]; + tunnelConnection = [NSConnection new]; + + [tunnelConnection runInNewThread]; + [tunnelConnection removeRunLoop:[NSRunLoop currentRunLoop]]; + [tunnelConnection setRootObject:self]; + + if (![tunnelConnection registerName:tunnelConnectionName]) return nil; + + parentWindow = nil; + identityFilePath = nil; + sshQuestionDialog = nil; + sshPasswordDialog = nil; + password = nil; + keychainName = nil; + keychainAccount = nil; + requestedPassphrase = nil; + task = nil; + localPort = 0; + connectionState = PROXY_STATE_IDLE; + + requestedResponse = NO; + passwordInKeychain = NO; + passwordPromptCancelled = NO; } - parentWindow = nil; - identityFilePath = nil; - sshQuestionDialog = nil; - sshPasswordDialog = nil; - password = nil; - keychainName = nil; - keychainAccount = nil; - passwordInKeychain = NO; - requestedPassphrase = nil; - requestedResponse = NO; - task = nil; - localPort = 0; - connectionState = PROXY_STATE_IDLE; - return self; } @@ -90,7 +95,7 @@ * Sets the connection callback selector; a function to be called whenever the tunnel state changes. * The callback function will be called and passed this SSH Tunnel object.. */ -- (BOOL) setConnectionStateChangeSelector:(SEL)theStateChangeSelector delegate:(id)theDelegate +- (BOOL)setConnectionStateChangeSelector:(SEL)theStateChangeSelector delegate:(id)theDelegate { delegate = theDelegate; stateChangeSelector = theStateChangeSelector; @@ -119,7 +124,7 @@ * Sets the password to be stored (and returned to the tunnel authenticator) locally. * Providing a keychain name is much more secure. */ -- (BOOL) setPassword:(NSString *)thePassword +- (BOOL)setPassword:(NSString *)thePassword { if (passwordInKeychain) return NO; password = [[NSString alloc] initWithString:thePassword]; @@ -130,7 +135,7 @@ /** * Sets the path of an identity file, or public key file, to use when connecting. */ -- (BOOL) setKeyFilePath:(NSString *)thePath +- (BOOL)setKeyFilePath:(NSString *)thePath { NSString *expandedPath = [thePath stringByExpandingTildeInPath]; if (![[NSFileManager defaultManager] fileExistsAtPath:expandedPath]) return NO; @@ -144,7 +149,7 @@ * Sets the keychain name to use to retrieve the password. This is the recommended and * secure way of supplying a password to the SSH tunnel. */ -- (BOOL) setPasswordKeychainName:(NSString *)theName account:(NSString *)theAccount +- (BOOL)setPasswordKeychainName:(NSString *)theName account:(NSString *)theAccount { if (password) [password release], password = nil; @@ -158,7 +163,7 @@ /* * Get the state of the connection. */ -- (NSInteger) state +- (NSInteger)state { // See if an auth dialog is up @@ -174,9 +179,10 @@ /* * Returns the last error string, if any. */ -- (NSString *) lastError +- (NSString *)lastError { if (!lastError) return nil; + return [NSString stringWithString:lastError]; } @@ -184,7 +190,7 @@ * Returns all the debug text for this tunnel as a string, separated * by line endings. */ -- (NSString *) debugMessages { +- (NSString *)debugMessages { [debugMessagesLock lock]; NSString *debugMessagesString = [debugMessages componentsJoinedByString:@"\n"]; [debugMessagesLock unlock]; @@ -194,7 +200,7 @@ /* * Initiate the SSH tunnel connection, launching the task in a background thread. */ -- (void) connect +- (void)connect { localPort = 0; @@ -210,7 +216,7 @@ * tunnel to the remote server. * Sets up and tears down as appropriate for usage in a background thread. */ -- (void) launchTask:(id) dummy +- (void)launchTask:(id) dummy { if (connectionState != PROXY_STATE_IDLE || task) return; NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; @@ -482,7 +488,7 @@ /* * Returns the local port assigned for use by the tunnel */ -- (NSInteger) localPort +- (NSInteger)localPort { return localPort; } @@ -490,9 +496,10 @@ /* * Returns the local port assigned for fallback use by the tunnel, if any */ -- (NSInteger) localPortFallback +- (NSInteger)localPortFallback { if (!useHostFallback) return 0; + return localPortFallback; } @@ -513,7 +520,7 @@ * a boolean. This is used by the SSH_ASKPASS environment setting to deal with situations like * host key mismatches. */ -- (BOOL) getResponseForQuestion:(NSString *)theQuestion +- (BOOL)getResponseForQuestion:(NSString *)theQuestion { // Lock the answer available lock [[answerAvailableLock onMainThread] lock]; @@ -530,12 +537,12 @@ // Unlock the lock again [answerAvailableLock unlock]; - //return the answer + // Return the answer return response; } -- (void) workerGetResponseForQuestion:(NSString *)theQuestion -{ +- (void)workerGetResponseForQuestion:(NSString *)theQuestion +{ NSSize questionTextSize; NSRect windowFrameRect; @@ -550,10 +557,11 @@ [NSApp beginSheet:sshQuestionDialog modalForWindow:parentWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; [parentWindow makeKeyAndOrderFront:self]; } + /* * Ends an existing modal session */ -- (IBAction) closeSSHQuestionSheet:(id)sender +- (IBAction)closeSSHQuestionSheet:(id)sender { requestedResponse = [sender tag]==1 ? YES : NO; [NSApp endSheet:sshQuestionDialog]; @@ -565,9 +573,11 @@ * Method to allow an SSH tunnel to request a password. This is used by the program set by the * SSH_ASKPASS environment setting to request passphrases for SSH keys. */ -- (NSString *) getPasswordForQuery:(NSString *)theQuery verificationHash:(NSString *)theHash +- (NSString *)getPasswordForQuery:(NSString *)theQuery verificationHash:(NSString *)theHash { if (![theHash isEqualToString:tunnelConnectionVerifyHash]) return nil; + + if (passwordPromptCancelled) return nil; // Lock the answer available lock [[answerAvailableLock onMainThread] lock]; @@ -591,18 +601,21 @@ // Return the answer return thePassword; } -- (void) workerGetPasswordForQuery:(NSString *)theQuery + +- (void)workerGetPasswordForQuery:(NSString *)theQuery { NSSize queryTextSize; NSRect windowFrameRect; // Work out whether a passphrase is being requested, extracting the key name NSString *keyName = [theQuery stringByMatching:@"^\\s*Enter passphrase for key \\'(.*)\\':\\s*$" capture:1L]; + if (keyName) { [sshPasswordText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Enter your password for the SSH key\n\"%@\"", @"SSH key password prompt"), keyName]]; [sshPasswordKeychainCheckbox setHidden:NO]; currentKeyName = [keyName retain]; - } else { + } + else { [sshPasswordText setStringValue:theQuery]; [sshPasswordKeychainCheckbox setHidden:YES]; currentKeyName = nil; @@ -612,6 +625,7 @@ queryTextSize = [[sshPasswordText cell] cellSizeForBounds:NSMakeRect(0, 0, [sshPasswordText bounds].size.width, 500)]; windowFrameRect = [sshPasswordDialog frame]; windowFrameRect.size.height = ((queryTextSize.height < 40)?40:queryTextSize.height) + 140 + ([sshPasswordDialog isSheet]?0:22); + [sshPasswordDialog setFrame:windowFrameRect display:NO]; [NSApp beginSheet:sshPasswordDialog modalForWindow:parentWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; [parentWindow makeKeyAndOrderFront:self]; @@ -620,9 +634,10 @@ /* * Ends an existing modal session */ -- (IBAction) closeSSHPasswordSheet:(id)sender +- (IBAction)closeSSHPasswordSheet:(id)sender { requestedResponse = [sender tag]==1 ? YES : NO; + [NSApp endSheet:sshPasswordDialog]; [sshPasswordDialog orderOut:nil]; @@ -645,10 +660,13 @@ currentKeyName = nil; } } + + if (!requestedPassphrase) passwordPromptCancelled = YES; [[answerAvailableLock onMainThread] unlock]; } +#pragma mark - - (void)dealloc { |