aboutsummaryrefslogtreecommitdiffstats
path: root/Source/SPConnectionHandler.m
diff options
context:
space:
mode:
authorstuconnolly <stuart02@gmail.com>2010-12-05 15:03:28 +0000
committerstuconnolly <stuart02@gmail.com>2010-12-05 15:03:28 +0000
commit4e87089af2ce50364a3e6fcfdf8a3c31e172d8aa (patch)
tree88ff9ede1a8fb612cd4fbf7a4f90b7cbc8f10f34 /Source/SPConnectionHandler.m
parent93b889667f1b938931b84e8fed07ee58bcba3c8f (diff)
downloadsequelpro-4e87089af2ce50364a3e6fcfdf8a3c31e172d8aa.tar.gz
sequelpro-4e87089af2ce50364a3e6fcfdf8a3c31e172d8aa.tar.bz2
sequelpro-4e87089af2ce50364a3e6fcfdf8a3c31e172d8aa.zip
Apply new favorites outline view patch.
Diffstat (limited to 'Source/SPConnectionHandler.m')
-rw-r--r--Source/SPConnectionHandler.m245
1 files changed, 245 insertions, 0 deletions
diff --git a/Source/SPConnectionHandler.m b/Source/SPConnectionHandler.m
new file mode 100644
index 00000000..818e528f
--- /dev/null
+++ b/Source/SPConnectionHandler.m
@@ -0,0 +1,245 @@
+//
+// $Id$
+//
+// SPConnectionHandler.m
+// sequel-pro
+//
+// Created by Stuart Connolly (stuconnolly.com) on November 15, 2010
+// Copyright (c) 2010 Stuart Connolly. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// More info at <http://code.google.com/p/sequel-pro/>
+
+#import "SPConnectionHandler.h"
+#import "SPAlertSheets.h"
+
+@implementation SPConnectionController (SPConnectionHandler)
+
+/*
+ * Initiate the SSH connection process.
+ * This should only be called as part of initiateConnection:, and will indirectly
+ * call initiateMySQLConnection if it's successful.
+ */
+- (void)initiateSSHTunnelConnection
+{
+ [progressIndicatorText setStringValue:NSLocalizedString(@"SSH connecting...", @"SSH connecting very short status message")];
+ [progressIndicatorText display];
+
+ // Trim whitespace and newlines from the SSH host field before attempting to connect
+ [self setSshHost:[[self sshHost] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
+
+ // Set up the tunnel details
+ sshTunnel = [[SPSSHTunnel alloc] initToHost:[self sshHost] port:[[self sshPort] integerValue] login:[self sshUser] tunnellingToPort:([[self port] length]?[[self port] integerValue]:3306) onHost:[self host]];
+ [sshTunnel setParentWindow:[dbDocument parentWindow]];
+
+ // Add keychain or plaintext password as appropriate - note the checks in initiateConnection.
+ if (connectionSSHKeychainItemName) {
+ [sshTunnel setPasswordKeychainName:connectionSSHKeychainItemName account:connectionSSHKeychainItemAccount];
+ } else if (sshPassword) {
+ [sshTunnel setPassword:[self sshPassword]];
+ }
+
+ // Set the public key path if appropriate
+ if (sshKeyLocationEnabled && sshKeyLocation) {
+ [sshTunnel setKeyFilePath:sshKeyLocation];
+ }
+
+ // Set the callback function on the tunnel
+ [sshTunnel setConnectionStateChangeSelector:@selector(sshTunnelCallback:) delegate:self];
+
+ // Ask the tunnel to connect. This will call the callback below on success or failure, passing
+ // itself as an argument - retain count should be one at this point.
+ [sshTunnel connect];
+}
+
+/*
+ * Cancel connection.
+ * Currently only cleans up the SSH connection (MySQL connection isn't threaded)
+ */
+- (void)cancelConnection
+{
+ if (!sshTunnel) return;
+
+ cancellingConnection = YES;
+
+ [sshTunnel disconnect];
+ [sshTunnel release];
+
+ sshTunnel = nil;
+}
+
+/*
+ * A callback function for the SSH Tunnel setup process - will be called on a connection
+ * state change, allowing connection to fail or proceed as appropriate. If successful,
+ * will call initiateMySQLConnection.
+ */
+- (void)sshTunnelCallback:(SPSSHTunnel *)theTunnel
+{
+ if (cancellingConnection) return;
+
+ NSInteger newState = [theTunnel state];
+
+ if (newState == PROXY_STATE_IDLE) {
+ [dbDocument setTitlebarStatus:NSLocalizedString(@"SSH Disconnected", @"SSH disconnected titlebar marker")];
+
+ [self failConnectionWithTitle:NSLocalizedString(@"SSH connection failed!", @"SSH connection failed title") errorMessage:[theTunnel lastError] detail:[sshTunnel debugMessages]];
+ }
+ else if (newState == PROXY_STATE_CONNECTED) {
+ [dbDocument setTitlebarStatus:NSLocalizedString(@"SSH Connected", @"SSH connected titlebar marker")];
+
+ [self initiateMySQLConnection];
+ }
+ else {
+ [dbDocument setTitlebarStatus:NSLocalizedString(@"SSH Connecting…", @"SSH connecting titlebar marker")];
+ }
+}
+
+/*
+ * Set up the MySQL connection, either through a successful tunnel or directly in the background.
+ */
+- (void)initiateMySQLConnection
+{
+ // Disable the favorites table view to prevent further connections attempts
+ [favoritesOutlineView setEnabled:NO];
+
+ if (sshTunnel) {
+ [progressIndicatorText setStringValue:NSLocalizedString(@"MySQL connecting...", @"MySQL connecting very short status message")];
+ }
+ else {
+ [progressIndicatorText setStringValue:NSLocalizedString(@"Connecting...", @"Generic connecting very short status message")];
+ }
+
+ [progressIndicatorText display];
+
+ [connectButton setTitle:NSLocalizedString(@"Cancel", @"cancel button")];
+ [connectButton setAction:@selector(cancelMySQLConnection:)];
+ [connectButton setEnabled:YES];
+ [connectButton display];
+
+ [NSThread detachNewThreadSelector:@selector(_initiateMySQLConnectionInBackground) toTarget:self withObject:nil];
+}
+
+/*
+ * Ends a connection attempt by stopping the connection animation and
+ * displaying a specified error message.
+ */
+- (void)failConnectionWithTitle:(NSString *)theTitle errorMessage:(NSString *)theErrorMessage detail:(NSString *)errorDetail
+{
+ BOOL isSSHTunnelBindError = NO;
+
+ // Clean up the interface
+ [progressIndicator stopAnimation:self];
+ [progressIndicator display];
+ [progressIndicatorText setHidden:YES];
+ [progressIndicatorText display];
+ [addToFavoritesButton setHidden:NO];
+ [addToFavoritesButton display];
+ [connectButton setEnabled:YES];
+ [dbDocument clearStatusIcon];
+
+ // Release as appropriate
+ if (sshTunnel) {
+ [sshTunnel disconnect], [sshTunnel release], sshTunnel = nil;
+
+ // If the SSH tunnel connection failed because the port it was trying to bind to was already in use take note
+ // of it so we can give the user the option of connecting via standard connection and use the existing tunnel.
+ if ([theErrorMessage rangeOfString:@"bind"].location != NSNotFound) {
+ isSSHTunnelBindError = YES;
+ }
+ }
+
+ if (errorDetail) [errorDetailText setString:errorDetail];
+
+ // Inform the delegate that the connection attempt failed
+ if (delegate && [delegate respondsToSelector:@selector(connectionControllerConnectAttemptFailed:)]) {
+ [delegate connectionControllerConnectAttemptFailed:self];
+ }
+
+ // Only display the connection error message if there is a window visible and the connection attempt
+ // wasn't cancelled even though it failed.
+ if ([[dbDocument parentWindow] isVisible] && (!mySQLConnectionCancelled)) {
+ SPBeginAlertSheet(theTitle, NSLocalizedString(@"OK", @"OK button"), (errorDetail) ? NSLocalizedString(@"Show Detail", @"Show detail button") : nil, (isSSHTunnelBindError) ? NSLocalizedString(@"Use Standard Connection", @"use standard connection button") : nil, [dbDocument parentWindow], self, @selector(connectionFailureSheetDidEnd:returnCode:contextInfo:), @"connect", theErrorMessage);
+ }
+}
+
+/**
+ * Alert sheet callback method - invoked when an error sheet is closed.
+ */
+- (void)connectionFailureSheetDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo
+{
+ // Restore the passwords from keychain for editing if appropriate
+ if (connectionKeychainItemName) {
+ [self setPassword:[keychain getPasswordForName:connectionKeychainItemName account:connectionKeychainItemAccount]];
+ }
+ if (connectionSSHKeychainItemName) {
+ [self setSshPassword:[keychain getPasswordForName:connectionSSHKeychainItemName account:connectionSSHKeychainItemAccount]];
+ }
+
+ if (returnCode == NSAlertAlternateReturn) {
+ [errorDetailText setFont:[NSFont userFontOfSize:12]];
+ [errorDetailText setAlignment:NSLeftTextAlignment];
+ [errorDetailWindow makeKeyAndOrderFront:self];
+ }
+ // Currently only SSH port bind errors offer a 3rd option in the error dialog, but if this ever changes
+ // this will definitely need to be updated.
+ else if (returnCode == NSAlertOtherReturn) {
+ // Extract the local port number that SSH attempted to bind to from the debug output
+ NSString *tunnelPort = [[[errorDetailText string] componentsMatchedByRegex:@"LOCALHOST:([0-9]+)" capture:1L] lastObject];
+
+ // Change the connection type to standard TCP/IP
+ [self setType:SPTCPIPConnection];
+
+ // Change connection details
+ [self setPort:tunnelPort];
+ [self setHost:@"127.0.0.1"];
+
+ // Change to standard TCP/IP connection view
+ [self resizeTabViewToConnectionType:SPTCPIPConnection animating:YES];
+
+ // Initiate the connection after half a second to give the connection view a chance to resize
+ [self performSelector:@selector(initiateConnection:) withObject:self afterDelay:0.5];
+ }
+}
+
+/**
+ * Add the connection to the parent document and restore the
+ * interface, allowing the application to run as normal.
+ */
+- (void)addConnectionToDocument
+{
+ // Hide the connection view and restore the main view
+ [connectionView removeFromSuperviewWithoutNeedingDisplay];
+ [databaseConnectionView setHidden:NO];
+
+ // Restore the toolbar icons
+ NSArray *toolbarItems = [[[dbDocument parentWindow] toolbar] items];
+
+ for (NSInteger i = 0; i < [toolbarItems count]; i++) [[toolbarItems objectAtIndex:i] setEnabled:YES];
+
+ // Set keychain id for saving SPF files
+ if ([self valueForKeyPath:@"selectedFavorite.id"]) {
+ [dbDocument setKeychainID:[[self valueForKeyPath:@"selectedFavorite.id"] stringValue]];
+ }
+ else {
+ [dbDocument setKeychainID:@""];
+ }
+
+ // Pass the connection to the table document, allowing it to set
+ // up the other classes and the rest of the interface.
+ [dbDocument setConnection:mySQLConnection];
+}
+
+@end