aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrowanbeentje <rowan@beent.je>2009-06-04 01:44:16 +0000
committerrowanbeentje <rowan@beent.je>2009-06-04 01:44:16 +0000
commit31123c9ff149908120a5bca94e76f5023c0b7e1e (patch)
treef76933e4c19d63cd17f160919731fbb5c9162b75
parentfc748400d92a0b7874a19eba3fa573cdf1415ee5 (diff)
downloadsequelpro-31123c9ff149908120a5bca94e76f5023c0b7e1e.tar.gz
sequelpro-31123c9ff149908120a5bca94e76f5023c0b7e1e.tar.bz2
sequelpro-31123c9ff149908120a5bca94e76f5023c0b7e1e.zip
Further SSH tunnel improvements, password handling improvements, and minor bugfixes:
- SSH tunnels can now correctly show dialogs for ssh queries, eg host key mismatches - SSH tunnels are now correctly closed by the document for connection failures - Keychain password item name and account generation has been moved to within the keychain class, to centralise generation for consistency - Keychain item names and accounts now correctly deal with nil values, allowing more keychain items to be read - "Add to favorites" button and menu item now correctly store passwords and SSH tunnel settings - Duplicating favorites in preferences now selects the newly created favorite instead of deselecting everything - Fixes an occasional crasher sometimes encountered in keychain usage
-rw-r--r--Interfaces/SSHQuestionDialog.xib497
-rw-r--r--Source/CMMCPConnection.m2
-rw-r--r--Source/KeyChain.h4
-rw-r--r--Source/KeyChain.m62
-rw-r--r--Source/SPPreferenceController.m63
-rw-r--r--Source/SPSSHTunnel.h17
-rw-r--r--Source/SPSSHTunnel.m87
-rw-r--r--Source/TableDocument.h3
-rw-r--r--Source/TableDocument.m118
-rw-r--r--Source/TunnelPassphraseRequester.m27
-rw-r--r--sequel-pro.xcodeproj/project.pbxproj4
11 files changed, 774 insertions, 110 deletions
diff --git a/Interfaces/SSHQuestionDialog.xib b/Interfaces/SSHQuestionDialog.xib
new file mode 100644
index 00000000..bbb00fe3
--- /dev/null
+++ b/Interfaces/SSHQuestionDialog.xib
@@ -0,0 +1,497 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.03">
+ <data>
+ <int key="IBDocument.SystemTarget">1050</int>
+ <string key="IBDocument.SystemVersion">9G55</string>
+ <string key="IBDocument.InterfaceBuilderVersion">677</string>
+ <string key="IBDocument.AppKitVersion">949.43</string>
+ <string key="IBDocument.HIToolboxVersion">353.00</string>
+ <object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <integer value="367"/>
+ </object>
+ <object class="NSArray" key="IBDocument.PluginDependencies">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilderKit</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ <object class="NSMutableDictionary" key="IBDocument.Metadata">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.RootObjects" id="1048">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSCustomObject" id="1021">
+ <string key="NSClassName">NSObject</string>
+ </object>
+ <object class="NSCustomObject" id="1014">
+ <string key="NSClassName">FirstResponder</string>
+ </object>
+ <object class="NSCustomObject" id="1050">
+ <string key="NSClassName">NSApplication</string>
+ </object>
+ <object class="NSWindowTemplate" id="513744381">
+ <int key="NSWindowStyleMask">1</int>
+ <int key="NSWindowBacking">2</int>
+ <string key="NSWindowRect">{{196, 301}, {536, 209}}</string>
+ <int key="NSWTFlags">603979776</int>
+ <string key="NSWindowTitle">SSH Tunnel Query</string>
+ <string key="NSWindowClass">NSWindow</string>
+ <nil key="NSViewClass"/>
+ <string key="NSWindowContentMaxSize">{3.40282e+38, 3.40282e+38}</string>
+ <object class="NSView" key="NSWindowView" id="414427165">
+ <reference key="NSNextResponder"/>
+ <int key="NSvFlags">256</int>
+ <object class="NSMutableArray" key="NSSubviews">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSTextField" id="958459073">
+ <reference key="NSNextResponder" ref="414427165"/>
+ <int key="NSvFlags">274</int>
+ <string key="NSFrame">{{126, 46}, {393, 143}}</string>
+ <reference key="NSSuperview" ref="414427165"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="296218965">
+ <int key="NSCellFlags">67239424</int>
+ <int key="NSCellFlags2">272891904</int>
+ <string key="NSContents"/>
+ <object class="NSFont" key="NSSupport">
+ <string key="NSName">LucidaGrande</string>
+ <double key="NSSize">1.300000e+01</double>
+ <int key="NSfFlags">16</int>
+ </object>
+ <reference key="NSControlView" ref="958459073"/>
+ <object class="NSColor" key="NSBackgroundColor">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">controlColor</string>
+ <object class="NSColor" key="NSColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MC42NjY2NjY2OQA</bytes>
+ </object>
+ </object>
+ <object class="NSColor" key="NSTextColor">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">controlTextColor</string>
+ <object class="NSColor" key="NSColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MAA</bytes>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSButton" id="819605912">
+ <reference key="NSNextResponder" ref="414427165"/>
+ <int key="NSvFlags">289</int>
+ <string key="NSFrame">{{426, 12}, {96, 32}}</string>
+ <reference key="NSSuperview" ref="414427165"/>
+ <int key="NSTag">1</int>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSButtonCell" key="NSCell" id="915366670">
+ <int key="NSCellFlags">67239424</int>
+ <int key="NSCellFlags2">134217728</int>
+ <string key="NSContents">Yes</string>
+ <object class="NSFont" key="NSSupport" id="313221240">
+ <string key="NSName">LucidaGrande</string>
+ <double key="NSSize">1.300000e+01</double>
+ <int key="NSfFlags">1044</int>
+ </object>
+ <reference key="NSControlView" ref="819605912"/>
+ <int key="NSButtonFlags">-2038284033</int>
+ <int key="NSButtonFlags2">129</int>
+ <string key="NSAlternateContents"/>
+ <string type="base64-UTF8" key="NSKeyEquivalent">DQ</string>
+ <int key="NSPeriodicDelay">200</int>
+ <int key="NSPeriodicInterval">25</int>
+ </object>
+ </object>
+ <object class="NSImageView" id="346757277">
+ <reference key="NSNextResponder" ref="414427165"/>
+ <int key="NSvFlags">256</int>
+ <object class="NSMutableSet" key="NSDragTypes">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="set.sortedObjects">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>Apple PDF pasteboard type</string>
+ <string>Apple PICT pasteboard type</string>
+ <string>Apple PNG pasteboard type</string>
+ <string>NSFilenamesPboardType</string>
+ <string>NeXT Encapsulated PostScript v1.2 pasteboard type</string>
+ <string>NeXT TIFF v4.0 pasteboard type</string>
+ </object>
+ </object>
+ <string key="NSFrame">{{20, 115}, {75, 74}}</string>
+ <reference key="NSSuperview" ref="414427165"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSImageCell" key="NSCell" id="490596046">
+ <int key="NSCellFlags">130560</int>
+ <int key="NSCellFlags2">33554432</int>
+ <object class="NSCustomResource" key="NSContents">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">appicon</string>
+ </object>
+ <int key="NSAlign">0</int>
+ <int key="NSScale">0</int>
+ <int key="NSStyle">0</int>
+ <bool key="NSAnimates">YES</bool>
+ </object>
+ <bool key="NSEditable">YES</bool>
+ </object>
+ <object class="NSButton" id="472545742">
+ <reference key="NSNextResponder" ref="414427165"/>
+ <int key="NSvFlags">289</int>
+ <string key="NSFrame">{{330, 12}, {96, 32}}</string>
+ <reference key="NSSuperview" ref="414427165"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSButtonCell" key="NSCell" id="382904691">
+ <int key="NSCellFlags">67239424</int>
+ <int key="NSCellFlags2">134217728</int>
+ <string key="NSContents">No</string>
+ <reference key="NSSupport" ref="313221240"/>
+ <reference key="NSControlView" ref="472545742"/>
+ <int key="NSButtonFlags">-2038284033</int>
+ <int key="NSButtonFlags2">129</int>
+ <string key="NSAlternateContents"/>
+ <string key="NSKeyEquivalent"/>
+ <int key="NSPeriodicDelay">200</int>
+ <int key="NSPeriodicInterval">25</int>
+ </object>
+ </object>
+ <object class="NSImageView" id="205963577">
+ <reference key="NSNextResponder" ref="414427165"/>
+ <int key="NSvFlags">256</int>
+ <object class="NSMutableSet" key="NSDragTypes">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="set.sortedObjects">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>Apple PDF pasteboard type</string>
+ <string>Apple PICT pasteboard type</string>
+ <string>Apple PNG pasteboard type</string>
+ <string>NSFilenamesPboardType</string>
+ <string>NeXT Encapsulated PostScript v1.2 pasteboard type</string>
+ <string>NeXT TIFF v4.0 pasteboard type</string>
+ </object>
+ </object>
+ <string key="NSFrame">{{68, 113}, {32, 32}}</string>
+ <reference key="NSSuperview" ref="414427165"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSImageCell" key="NSCell" id="406303847">
+ <int key="NSCellFlags">130560</int>
+ <int key="NSCellFlags2">33554432</int>
+ <object class="NSCustomResource" key="NSContents">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">toolbar-preferences-network</string>
+ </object>
+ <int key="NSAlign">0</int>
+ <int key="NSScale">0</int>
+ <int key="NSStyle">0</int>
+ <bool key="NSAnimates">YES</bool>
+ </object>
+ <bool key="NSEditable">YES</bool>
+ </object>
+ </object>
+ <string key="NSFrameSize">{536, 209}</string>
+ <reference key="NSSuperview"/>
+ </object>
+ <string key="NSScreenRect">{{0, 0}, {1920, 1178}}</string>
+ <string key="NSMaxSize">{3.40282e+38, 3.40282e+38}</string>
+ </object>
+ </object>
+ <object class="IBObjectContainer" key="IBDocument.Objects">
+ <object class="NSMutableArray" key="connectionRecords">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">sshQuestionText</string>
+ <reference key="source" ref="1021"/>
+ <reference key="destination" ref="958459073"/>
+ </object>
+ <int key="connectionID">464</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">sshQuestionDialog</string>
+ <reference key="source" ref="1021"/>
+ <reference key="destination" ref="513744381"/>
+ </object>
+ <int key="connectionID">465</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">closeSheet:</string>
+ <reference key="source" ref="1021"/>
+ <reference key="destination" ref="819605912"/>
+ </object>
+ <int key="connectionID">466</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">closeSheet:</string>
+ <reference key="source" ref="1021"/>
+ <reference key="destination" ref="472545742"/>
+ </object>
+ <int key="connectionID">467</int>
+ </object>
+ </object>
+ <object class="IBMutableOrderedSet" key="objectRecords">
+ <object class="NSArray" key="orderedObjects">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBObjectRecord">
+ <int key="objectID">0</int>
+ <object class="NSArray" key="object" id="1049">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <reference key="children" ref="1048"/>
+ <nil key="parent"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-2</int>
+ <reference key="object" ref="1021"/>
+ <reference key="parent" ref="1049"/>
+ <string type="base64-UTF8" key="objectName">RmlsZSdzIE93bmVyA</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-1</int>
+ <reference key="object" ref="1014"/>
+ <reference key="parent" ref="1049"/>
+ <string key="objectName">First Responder</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-3</int>
+ <reference key="object" ref="1050"/>
+ <reference key="parent" ref="1049"/>
+ <string key="objectName">Application</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">367</int>
+ <reference key="object" ref="513744381"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="414427165"/>
+ </object>
+ <reference key="parent" ref="1049"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">368</int>
+ <reference key="object" ref="414427165"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="205963577"/>
+ <reference ref="958459073"/>
+ <reference ref="819605912"/>
+ <reference ref="472545742"/>
+ <reference ref="346757277"/>
+ </object>
+ <reference key="parent" ref="513744381"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">450</int>
+ <reference key="object" ref="958459073"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="296218965"/>
+ </object>
+ <reference key="parent" ref="414427165"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">451</int>
+ <reference key="object" ref="296218965"/>
+ <reference key="parent" ref="958459073"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">456</int>
+ <reference key="object" ref="205963577"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="406303847"/>
+ </object>
+ <reference key="parent" ref="414427165"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">457</int>
+ <reference key="object" ref="406303847"/>
+ <reference key="parent" ref="205963577"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">458</int>
+ <reference key="object" ref="819605912"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="915366670"/>
+ </object>
+ <reference key="parent" ref="414427165"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">459</int>
+ <reference key="object" ref="915366670"/>
+ <reference key="parent" ref="819605912"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">460</int>
+ <reference key="object" ref="472545742"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="382904691"/>
+ </object>
+ <reference key="parent" ref="414427165"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">461</int>
+ <reference key="object" ref="382904691"/>
+ <reference key="parent" ref="472545742"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">462</int>
+ <reference key="object" ref="346757277"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="490596046"/>
+ </object>
+ <reference key="parent" ref="414427165"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">463</int>
+ <reference key="object" ref="490596046"/>
+ <reference key="parent" ref="346757277"/>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="flattenedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>-1.IBPluginDependency</string>
+ <string>-2.IBPluginDependency</string>
+ <string>-3.IBPluginDependency</string>
+ <string>367.IBEditorWindowLastContentRect</string>
+ <string>367.IBWindowTemplateEditedContentRect</string>
+ <string>367.NSWindowTemplate.visibleAtLaunch</string>
+ <string>367.editorWindowContentRectSynchronizationRect</string>
+ <string>368.IBPluginDependency</string>
+ <string>450.IBPluginDependency</string>
+ <string>451.IBPluginDependency</string>
+ <string>458.IBPluginDependency</string>
+ <string>459.IBPluginDependency</string>
+ <string>460.IBPluginDependency</string>
+ <string>461.IBPluginDependency</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilderKit</string>
+ <string>com.apple.InterfaceBuilderKit</string>
+ <string>{{187, 404}, {536, 209}}</string>
+ <string>{{187, 404}, {536, 209}}</string>
+ <boolean value="NO"/>
+ <string>{{11, 666}, {480, 270}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="unlocalizedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="activeLocalization"/>
+ <object class="NSMutableDictionary" key="localizations">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="sourceID"/>
+ <int key="maxID">467</int>
+ </object>
+ <object class="IBClassDescriber" key="IBDocument.Classes">
+ <object class="NSMutableArray" key="referencedPartialClassDescriptions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSApplication</string>
+ <object class="NSMutableDictionary" key="outlets">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>sshQuestionDialog</string>
+ <string>sshQuestionText</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>NSWindow</string>
+ <string>NSTextField</string>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBUserSource</string>
+ <string key="minorKey"/>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">Source/CMImageView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">Source/CMMCPConnection.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="NSMutableDictionary" key="actions">
+ <string key="NS.key.0">closeSheet:</string>
+ <string key="NS.object.0">id</string>
+ </object>
+ <object class="NSMutableDictionary" key="outlets">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>sshQuestionDialog</string>
+ <string>sshQuestionText</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>NSWindow</string>
+ <string>NSTextField</string>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBUserSource</string>
+ <string key="minorKey"/>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSWindow</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">Source/SPWindowAdditions.h</string>
+ </object>
+ </object>
+ </object>
+ </object>
+ <int key="IBDocument.localizationMode">0</int>
+ <string key="IBDocument.LastKnownRelativeProjectPath">../sequel-pro.xcodeproj</string>
+ <int key="IBDocument.defaultPropertyAccessControl">3</int>
+ </data>
+</archive>
diff --git a/Source/CMMCPConnection.m b/Source/CMMCPConnection.m
index aa2dd430..4ee9263e 100644
--- a/Source/CMMCPConnection.m
+++ b/Source/CMMCPConnection.m
@@ -239,6 +239,7 @@ static void forcePingTimeout(int signalNumber);
theRet = mysql_real_connect(mConnection, theHost, theLogin, thePass, NULL, connectionPort, theSocket, mConnectionFlags);
thePass = NULL;
if (theRet != mConnection) {
+ if (connectionTunnel) [connectionTunnel disconnect];
return mConnected = NO;
}
@@ -576,6 +577,7 @@ static void forcePingTimeout(int signalNumber);
[self startKeepAliveTimerResettingState:YES];
return YES;
}
+ if (connectionTunnel) [connectionTunnel disconnect];
return NO;
}
diff --git a/Source/KeyChain.h b/Source/KeyChain.h
index 086ee6a7..966a8c04 100644
--- a/Source/KeyChain.h
+++ b/Source/KeyChain.h
@@ -32,5 +32,9 @@
- (NSString *)getPasswordForName:(NSString *)name account:(NSString *)account;
- (void)deletePasswordForName:(NSString *)name account:(NSString *)account;
- (BOOL)passwordExistsForName:(NSString *)name account:(NSString *)account;
+- (NSString *)nameForFavoriteName:(NSString *)theName id:(NSString *)theID;
+- (NSString *)accountForUser:(NSString *)theUser host:(NSString *)theHost database:(NSString *)theDatabase;
+- (NSString *)nameForSSHForFavoriteName:(NSString *)theName id:(NSString *)theID;
+- (NSString *)accountForSSHUser:(NSString *)theSSHUser sshHost:(NSString *)theSSHHost;
@end
diff --git a/Source/KeyChain.m b/Source/KeyChain.m
index 032db61e..da3e761c 100644
--- a/Source/KeyChain.m
+++ b/Source/KeyChain.m
@@ -164,9 +164,8 @@
- (BOOL)passwordExistsForName:(NSString *)name account:(NSString *)account
{
SecKeychainItemRef item;
- SecKeychainSearchRef search;
+ SecKeychainSearchRef search = NULL;
int numberOfItemsFound = 0;
-
SecKeychainAttributeList list;
SecKeychainAttribute attributes[2];
@@ -188,9 +187,66 @@
}
}
- CFRelease(search);
+ if (search) CFRelease(search);
return (numberOfItemsFound > 0);
}
+/**
+ * Retrieve the keychain item name for a supplied name and id.
+ */
+- (NSString *)nameForFavoriteName:(NSString *)theName id:(NSString *)theID
+{
+ NSString *keychainItemName;
+
+ keychainItemName = [NSString stringWithFormat:@"Sequel Pro : %@ (%i)",
+ theName,
+ [theID intValue]];
+
+ return keychainItemName;
+}
+
+/**
+ * Retrieve the keychain item account for a supplied user, host, and database - which can be nil.
+ */
+- (NSString *)accountForUser:(NSString *)theUser host:(NSString *)theHost database:(NSString *)theDatabase
+{
+ NSString *keychainItemAccount;
+
+ keychainItemAccount = [NSString stringWithFormat:@"%@@%@/%@",
+ theUser?theUser:@"",
+ theHost?theHost:@"",
+ theDatabase?theDatabase:@""];
+
+ return keychainItemAccount;
+}
+
+/**
+ * Retrieve the keychain SSH item name for a supplied name and id.
+ */
+- (NSString *)nameForSSHForFavoriteName:(NSString *)theName id:(NSString *)theID
+{
+ NSString *sshKeychainItemName;
+
+ sshKeychainItemName = [NSString stringWithFormat:@"Sequel Pro SSHTunnel : %@ (%i)",
+ theName,
+ [theID intValue]];
+
+ return sshKeychainItemName;
+}
+
+/**
+ * Retrieve the keychain SSH item account for a supplied SSH user and host - which can be nil.
+ */
+- (NSString *)accountForSSHUser:(NSString *)theSSHUser sshHost:(NSString *)theSSHHost
+{
+ NSString *sshKeychainItemAccount;
+
+ sshKeychainItemAccount = [NSString stringWithFormat:@"%@@%@",
+ theSSHUser?theSSHUser:@"",
+ theSSHHost?theSSHHost:@""];
+
+ return sshKeychainItemAccount;
+}
+
@end
diff --git a/Source/SPPreferenceController.m b/Source/SPPreferenceController.m
index ce05117e..b6395c3c 100644
--- a/Source/SPPreferenceController.m
+++ b/Source/SPPreferenceController.m
@@ -259,13 +259,13 @@
NSString *database = [favoritesController valueForKeyPath:@"selection.database"];
NSString *sshUser = [favoritesController valueForKeyPath:@"selection.sshUser"];
NSString *sshHost = [favoritesController valueForKeyPath:@"selection.sshHost"];
- int favoriteid = [[favoritesController valueForKeyPath:@"selection.id"] intValue];
+ NSString *favoriteid = [favoritesController valueForKeyPath:@"selection.id"];
// Remove passwords from the Keychain
- [keychain deletePasswordForName:[NSString stringWithFormat:@"Sequel Pro : %@ (%i)", name, favoriteid]
- account:[NSString stringWithFormat:@"%@@%@/%@", user, host, database]];
- [keychain deletePasswordForName:[NSString stringWithFormat:@"Sequel Pro SSHTunnel : %@ (%i)", name, favoriteid]
- account:[NSString stringWithFormat:@"%@@%@", sshUser, sshHost]];
+ [keychain deletePasswordForName:[keychain nameForFavoriteName:name id:favoriteid]
+ account:[keychain accountForUser:user host:host database:database]];
+ [keychain deletePasswordForName:[keychain nameForSSHForFavoriteName:name id:favoriteid]
+ account:[keychain accountForSSHUser:sshUser sshHost:sshHost]];
// Reset last used favorite
if ([favoritesTableView selectedRow] == [prefs integerForKey:@"LastFavoriteIndex"]) {
@@ -295,12 +295,11 @@
NSNumber *favoriteid = [NSNumber numberWithInt:[[NSString stringWithFormat:@"%f", [[NSDate date] timeIntervalSince1970]] hash]];
// Select the keychain passwords for duplication
- keychainName = [NSString stringWithFormat:@"Sequel Pro : %@ (%i)", [favorite objectForKey:@"name"], [[favorite objectForKey:@"id"] intValue]];
- keychainAccount = [NSString stringWithFormat:@"%@@%@/%@",
- [favorite objectForKey:@"user"], [favorite objectForKey:@"host"], [favorite objectForKey:@"database"]];
+ keychainName = [keychain nameForFavoriteName:[favorite objectForKey:@"name"] id:[favorite objectForKey:@"id"]];
+ keychainAccount = [keychain accountForUser:[favorite objectForKey:@"user"] host:[favorite objectForKey:@"host"] database:[favorite objectForKey:@"database"]];
password = [keychain getPasswordForName:keychainName account:keychainAccount];
- keychainSSHName = [NSString stringWithFormat:@"Sequel Pro SSHTunnel : %@ (%i)", [favorite objectForKey:@"name"], [[favorite objectForKey:@"id"] intValue]];
- keychainSSHAccount = [NSString stringWithFormat:@"%@@%@", [favorite objectForKey:@"sshUser"], [favorite objectForKey:@"sshHost"]];
+ keychainSSHName = [keychain nameForSSHForFavoriteName:[favorite objectForKey:@"name"] id:[favorite objectForKey:@"id"]];
+ keychainSSHAccount = [keychain accountForSSHUser:[favorite objectForKey:@"sshUser"] sshHost:[favorite objectForKey:@"sshHost"]];
sshPassword = [keychain getPasswordForName:keychainSSHName account:keychainSSHAccount];
// Update the unique ID
@@ -311,16 +310,17 @@
// Create new keychain items if appropriate
if (password && [password length]) {
- keychainName = [NSString stringWithFormat:@"Sequel Pro : %@ (%i)", [favorite objectForKey:@"name"], [[favorite objectForKey:@"id"] intValue]];
+ keychainName = [keychain nameForFavoriteName:[favorite objectForKey:@"name"] id:[favorite objectForKey:@"id"]];
[keychain addPassword:password forName:keychainName account:keychainAccount];
}
if (sshPassword && [sshPassword length]) {
- keychainSSHName = [NSString stringWithFormat:@"Sequel Pro SSHTunnel : %@ (%i)", [favorite objectForKey:@"name"], [[favorite objectForKey:@"id"] intValue]];
+ keychainSSHName = [keychain nameForSSHForFavoriteName:[favorite objectForKey:@"name"] id:[favorite objectForKey:@"id"]];
[keychain addPassword:sshPassword forName:keychainSSHName account:keychainSSHAccount];
}
password = nil, sshPassword = nil;
[favoritesController addObject:favorite];
+ [favoritesController setSelectionIndex:[[favoritesController arrangedObjects] count]-1];
[favoritesTableView reloadData];
[self updateDefaultFavoritePopup];
@@ -561,19 +561,14 @@
}
// Otherwise retrieve and set the password.
- NSString *keychainName = [NSString stringWithFormat:@"Sequel Pro : %@ (%i)", [favoritesController valueForKeyPath:@"selection.name"], [[favoritesController valueForKeyPath:@"selection.id"] intValue]];
- NSString *keychainAccount = [NSString stringWithFormat:@"%@@%@/%@",
- [favoritesController valueForKeyPath:@"selection.user"],
- [favoritesController valueForKeyPath:@"selection.host"],
- [favoritesController valueForKeyPath:@"selection.database"]];
+ NSString *keychainName = [keychain nameForFavoriteName:[favoritesController valueForKeyPath:@"selection.name"] id:[favoritesController valueForKeyPath:@"selection.id"]];
+ NSString *keychainAccount = [keychain accountForUser:[favoritesController valueForKeyPath:@"selection.user"] host:[favoritesController valueForKeyPath:@"selection.host"] database:[favoritesController valueForKeyPath:@"selection.database"]];
[passwordField setStringValue:[keychain getPasswordForName:keychainName account:keychainAccount]];
// Retrieve the SSH keychain password if appropriate.
- NSString *keychainSSHName = [NSString stringWithFormat:@"Sequel Pro SSHTunnel : %@ (%i)", [favoritesController valueForKeyPath:@"selection.name"], [[favoritesController valueForKeyPath:@"selection.id"] intValue]];
- NSString *keychainSSHAccount = [NSString stringWithFormat:@"%@@%@",
- [favoritesController valueForKeyPath:@"selection.sshUser"],
- [favoritesController valueForKeyPath:@"selection.sshHost"]];
+ NSString *keychainSSHName = [keychain nameForSSHForFavoriteName:[favoritesController valueForKeyPath:@"selection.name"] id:[favoritesController valueForKeyPath:@"selection.id"]];
+ NSString *keychainSSHAccount = [keychain accountForSSHUser:[favoritesController valueForKeyPath:@"selection.sshUser"] sshHost:[favoritesController valueForKeyPath:@"selection.sshHost"]];
[sshPasswordField setStringValue:[keychain getPasswordForName:keychainSSHName account:keychainSSHAccount]];
}
@@ -683,18 +678,12 @@
|| control == passwordField) {
// Get the current keychain name and account strings
- oldKeychainName = [NSString stringWithFormat:@"Sequel Pro : %@ (%i)", [favoritesController valueForKeyPath:@"selection.name"], [[favoritesController valueForKeyPath:@"selection.id"] intValue]];
- oldKeychainAccount = [NSString stringWithFormat:@"%@@%@/%@",
- [favoritesController valueForKeyPath:@"selection.user"],
- [favoritesController valueForKeyPath:@"selection.host"],
- [favoritesController valueForKeyPath:@"selection.database"]];
+ oldKeychainName = [keychain nameForFavoriteName:[favoritesController valueForKeyPath:@"selection.name"] id:[favoritesController valueForKeyPath:@"selection.id"]];
+ oldKeychainAccount = [keychain accountForUser:[favoritesController valueForKeyPath:@"selection.user"] host:[favoritesController valueForKeyPath:@"selection.host"] database:[favoritesController valueForKeyPath:@"selection.database"]];
// Set up the new keychain name and account strings
- newKeychainName = [NSString stringWithFormat:@"Sequel Pro : %@ (%i)", [nameField stringValue], [[favoritesController valueForKeyPath:@"selection.id"] intValue]];
- newKeychainAccount = [NSString stringWithFormat:@"%@@%@/%@",
- [userField stringValue],
- [hostField stringValue],
- [databaseField stringValue]];
+ newKeychainName = [keychain nameForFavoriteName:[nameField stringValue] id:[favoritesController valueForKeyPath:@"selection.id"]];
+ newKeychainAccount = [keychain accountForUser:[userField stringValue] host:[hostField stringValue] database:[databaseField stringValue]];
// Delete the old keychain item
[keychain deletePasswordForName:oldKeychainName account:oldKeychainAccount];
@@ -712,16 +701,12 @@
|| control == sshPasswordField) {
// Get the current keychain name and account strings
- oldKeychainName = [NSString stringWithFormat:@"Sequel Pro SSHTunnel : %@ (%i)", [favoritesController valueForKeyPath:@"selection.name"], [[favoritesController valueForKeyPath:@"selection.id"] intValue]];
- oldKeychainAccount = [NSString stringWithFormat:@"%@@%@",
- [favoritesController valueForKeyPath:@"selection.sshUser"],
- [favoritesController valueForKeyPath:@"selection.sshHost"]];
+ oldKeychainName = [keychain nameForSSHForFavoriteName:[favoritesController valueForKeyPath:@"selection.name"] id:[favoritesController valueForKeyPath:@"selection.id"]];
+ oldKeychainAccount = [keychain accountForSSHUser:[favoritesController valueForKeyPath:@"selection.sshUser"] sshHost:[favoritesController valueForKeyPath:@"selection.sshHost"]];
// Set up the new keychain name and account strings
- newKeychainName = [NSString stringWithFormat:@"Sequel Pro SSHTunnel : %@ (%i)", [nameField stringValue], [[favoritesController valueForKeyPath:@"selection.id"] intValue]];
- newKeychainAccount = [NSString stringWithFormat:@"%@@%@",
- [sshUserField stringValue],
- [sshHostField stringValue]];
+ newKeychainName = [keychain nameForFavoriteName:[nameField stringValue] id:[favoritesController valueForKeyPath:@"selection.id"]];
+ newKeychainAccount = [keychain accountForSSHUser:[sshUserField stringValue] sshHost:[sshHostField stringValue]];
// Delete the old keychain item
[keychain deletePasswordForName:oldKeychainName account:oldKeychainAccount];
diff --git a/Source/SPSSHTunnel.h b/Source/SPSSHTunnel.h
index 91bd08de..612ef595 100644
--- a/Source/SPSSHTunnel.h
+++ b/Source/SPSSHTunnel.h
@@ -17,14 +17,18 @@ enum spsshtunnel_password_modes
@interface SPSSHTunnel : NSObject
{
+ IBOutlet NSWindow *sshQuestionDialog;
+ IBOutlet NSTextField *sshQuestionText;
+
+ NSWindow *parentWindow;
NSTask *task;
NSPipe *standardError;
id delegate;
SEL stateChangeSelector;
- NSConnection *passwordConnection;
+ NSConnection *tunnelConnection;
NSString *lastError;
- NSString *passwordConnectionName;
- NSString *passwordConnectionVerifyHash;
+ NSString *tunnelConnectionName;
+ NSString *tunnelConnectionVerifyHash;
NSString *sshHost;
NSString *sshLogin;
NSString *remoteHost;
@@ -40,15 +44,18 @@ enum spsshtunnel_password_modes
- (id) initToHost:(NSString *) theHost port:(int) thePort login:(NSString *) theLogin tunnellingToPort:(int) targetPort onHost:(NSString *) targetHost;
- (BOOL) setConnectionStateChangeSelector:(SEL)theStateChangeSelector delegate:(id)theDelegate;
-- (BOOL) setPassword:(NSString *)thePassword;
+- (void) setParentWindow:(NSWindow *)theWindow;
- (BOOL) setPasswordKeychainName:(NSString *)theName account:(NSString *)theAccount;
+- (BOOL) setPassword:(NSString *)thePassword;
- (int) state;
- (NSString *) lastError;
- (int) localPort;
- (void) connect;
- (void) launchTask:(id) dummy;
-- (void)disconnect;
+- (void) disconnect;
- (void) standardErrorHandler:(NSNotification*)aNotification;
- (NSString *) getPasswordWithVerificationHash:(NSString *)theHash;
+- (BOOL) getResponseForQuestion:(NSString *)theQuestion;
+- (IBAction) closeSheet:(id)sender;
@end
diff --git a/Source/SPSSHTunnel.m b/Source/SPSSHTunnel.m
index 433ccad4..c4c01783 100644
--- a/Source/SPSSHTunnel.m
+++ b/Source/SPSSHTunnel.m
@@ -53,7 +53,17 @@
stateChangeSelector = nil;
lastError = nil;
- passwordConnection = nil;
+ // Set up a connection for use by the tunnel process
+ tunnelConnectionName = [NSString stringWithFormat:@"SequelPro-%f", [[NSString stringWithFormat:@"%f", [[NSDate date] timeIntervalSince1970]] hash]];
+ tunnelConnection = [[NSConnection defaultConnection] retain];
+ [tunnelConnection runInNewThread];
+ [tunnelConnection removeRunLoop:[NSRunLoop currentRunLoop]];
+ [tunnelConnection setRootObject:self];
+ if ([tunnelConnection registerName:tunnelConnectionName] == NO) {
+ return nil;
+ }
+
+ parentWindow = nil;
password = nil;
keychainName = nil;
keychainAccount = nil;
@@ -78,6 +88,18 @@
}
/*
+ * Set the parent window of the connection for use with dialogs.
+ */
+- (void)setParentWindow:(NSWindow *)theWindow
+{
+ parentWindow = theWindow;
+ if (![NSBundle loadNibNamed:@"SSHQuestionDialog" owner:self]) {
+ NSLog(@"SSH query dialog could not be loaded; SSH tunnels will not function correctly.");
+ parentWindow = nil;
+ }
+}
+
+/*
* Sets the password to be stored (and returned to the tunnel authenticator) locally.
* Providing a keychain name is much more secure.
*/
@@ -85,16 +107,7 @@
{
if (passwordInKeychain) return NO;
password = [[NSString alloc] initWithString:thePassword];
- passwordConnection = [[NSConnection defaultConnection] retain];
- [passwordConnection runInNewThread];
- [passwordConnection removeRunLoop:[NSRunLoop currentRunLoop]];
- [passwordConnection setRootObject:self];
- passwordConnectionName = [NSString stringWithFormat:@"SequelPro-%f", [[NSString stringWithFormat:@"%f", [[NSDate date] timeIntervalSince1970]] hash]];
- passwordConnectionVerifyHash = [NSString stringWithFormat:@"%f", [[NSString stringWithFormat:@"%f%i", [[NSDate date] timeIntervalSince1970]] hash]];
- if ([passwordConnection registerName:passwordConnectionName] == NO) {
- [password release], password = nil;
- return NO;
- }
+ tunnelConnectionVerifyHash = [NSString stringWithFormat:@"%f", [[NSString stringWithFormat:@"%f%i", [[NSDate date] timeIntervalSince1970]] hash]];
return YES;
}
@@ -105,7 +118,6 @@
*/
- (BOOL) setPasswordKeychainName:(NSString *)theName account:(NSString *)theAccount
{
- if (passwordConnection) [passwordConnection release], passwordConnection = nil;
if (password) [password release], password = nil;
passwordInKeychain = YES;
@@ -157,6 +169,16 @@
connectionState = SPSSH_STATE_CONNECTING;
if (delegate) [delegate performSelectorOnMainThread:stateChangeSelector withObject:self waitUntilDone:NO];
+ // Enforce a parent window being present for dialogs
+ if (!parentWindow) {
+ connectionState = SPSSH_STATE_IDLE;
+ if (delegate) [delegate performSelectorOnMainThread:stateChangeSelector withObject:self waitUntilDone:NO];
+ if (lastError) [lastError release];
+ lastError = [[NSString alloc] initWithString:@"SSH Tunnel started without a parent window. A parent window must be present."];
+ [pool release];
+ return;
+ }
+
int connectionTimeout = [[[NSUserDefaults standardUserDefaults] objectForKey:@"ConnectionTimeout"] intValue];
if (!connectionTimeout) connectionTimeout = 10;
BOOL useKeepAlive = [[[NSUserDefaults standardUserDefaults] objectForKey:@"UseKeepAlive"] doubleValue];
@@ -221,14 +243,14 @@
[taskEnvironment removeObjectForKey: @"SSH_AUTH_SOCK"];
[taskEnvironment setObject:authenticationAppPath forKey:@"SSH_ASKPASS"];
[taskEnvironment setObject:@":0" forKey:@"DISPLAY"];
+ [taskEnvironment setObject:tunnelConnectionName forKey:@"SP_CONNECTION_NAME"];
if (passwordInKeychain) {
[taskEnvironment setObject:[[NSNumber numberWithInt:SPSSH_PASSWORD_USES_KEYCHAIN] stringValue] forKey:@"SP_PASSWORD_METHOD"];
[taskEnvironment setObject:keychainName forKey:@"SP_KEYCHAIN_ITEM_NAME"];
[taskEnvironment setObject:keychainAccount forKey:@"SP_KEYCHAIN_ITEM_ACCOUNT"];
} else {
[taskEnvironment setObject:[[NSNumber numberWithInt:SPSSH_PASSWORD_ASKS_UI] stringValue] forKey:@"SP_PASSWORD_METHOD"];
- [taskEnvironment setObject:passwordConnectionName forKey:@"SP_CONNECTION_NAME"];
- [taskEnvironment setObject:passwordConnectionVerifyHash forKey:@"SP_CONNECTION_VERIFY_HASH"];
+ [taskEnvironment setObject:tunnelConnectionVerifyHash forKey:@"SP_CONNECTION_VERIFY_HASH"];
}
[task setEnvironment:taskEnvironment];
@@ -360,8 +382,43 @@
- (NSString *)getPasswordWithVerificationHash:(NSString *)theHash
{
if (passwordInKeychain) return nil;
- if (![theHash isEqualToString:passwordConnectionVerifyHash]) return nil;
+ if (![theHash isEqualToString:tunnelConnectionVerifyHash]) return nil;
return password;
}
+/*
+ * Method to allow an SSH tunnel to request the response to a question, returning the response as
+ * a boolean. This is used by the SSH_ASKPASS environment setting to deal with situations like
+ * host key mismatches.
+ */
+- (BOOL) getResponseForQuestion:(NSString *)theQuestion
+{
+
+ // Ask how to proceed
+ [sshQuestionText setStringValue:theQuestion];
+ [NSApp beginSheet:sshQuestionDialog modalForWindow:parentWindow modalDelegate:self didEndSelector:nil contextInfo:nil];
+ int sshQueryResponseCode = [NSApp runModalForWindow:sshQuestionDialog];
+ [NSApp endSheet:sshQuestionDialog];
+ [sshQuestionDialog orderOut:nil];
+
+ switch (sshQueryResponseCode) {
+
+ // Yes
+ case 1:
+ return YES;
+
+ // No
+ default:
+ return NO;
+ }
+}
+
+/*
+ * Ends an existing modal session
+ */
+- (IBAction) closeSheet:(id)sender
+{
+ [NSApp stopModalWithCode:[sender tag]];
+}
+
@end
diff --git a/Source/TableDocument.h b/Source/TableDocument.h
index e71e80ac..c996a0bd 100644
--- a/Source/TableDocument.h
+++ b/Source/TableDocument.h
@@ -92,6 +92,7 @@
IBOutlet NSWindow *createTableSyntaxWindow;
CMMCPConnection *mySQLConnection;
+ SPSSHTunnel *sshTunnel;
NSArray *variables;
NSString *selectedDatabase;
@@ -121,7 +122,7 @@
- (IBAction)initiateConnection:(id)sender;
- (void)initiateSSHTunnelConnection;
- (void)sshTunnelCallback:(SPSSHTunnel *)theTunnel;
-- (void)initiateMySQLConnection:(SPSSHTunnel *)theTunnel;
+- (void)initiateMySQLConnection;
- (void)failConnectionWithErrorMessage:(NSString *)theErrorMessage;
- (IBAction)cancelConnectSheet:(id)sender;
- (IBAction)closeSheet:(id)sender;
diff --git a/Source/TableDocument.m b/Source/TableDocument.m
index 5ed89519..d0b04d5e 100644
--- a/Source/TableDocument.m
+++ b/Source/TableDocument.m
@@ -73,6 +73,7 @@ NSString *TableDocumentFavoritesControllerSelectionIndexDidChange = @"TableDocum
connectionSSHKeychainItemName = nil;
connectionSSHKeychainItemAccount = nil;
selectedDatabase = nil;
+ sshTunnel = nil;
printWebView = [[WebView alloc] init];
[printWebView setFrameLoadDelegate:self];
@@ -277,8 +278,8 @@ NSString *TableDocumentFavoritesControllerSelectionIndexDidChange = @"TableDocum
[self failConnectionWithErrorMessage:NSLocalizedString(@"Insufficient details provided to establish a connection. Please provide at least a host or socket.", @"insufficient details informative message")];
return;
}
- if ([sshCheckbox state] == NSOnState && (![[sshHostField stringValue] length] || ![[sshUserField stringValue] length])) {
- [self failConnectionWithErrorMessage:NSLocalizedString(@"Please enter the hostname and username for the SSH Tunnel, or disable the SSH Tunnel.", @"message of panel when ssh details are incomplete")];
+ if ([sshCheckbox state] == NSOnState && ![[sshHostField stringValue] length]) {
+ [self failConnectionWithErrorMessage:NSLocalizedString(@"Please enter the hostname for the SSH Tunnel, or disable the SSH Tunnel.", @"message of panel when ssh details are incomplete")];
return;
}
@@ -292,7 +293,7 @@ NSString *TableDocumentFavoritesControllerSelectionIndexDidChange = @"TableDocum
// for increased security.
if (connectionKeychainItemName) {
if ([[keyChainInstance getPasswordForName:connectionKeychainItemName account:connectionKeychainItemAccount] isEqualToString:[passwordField stringValue]]) {
- [passwordField setStringValue:@""];
+ [passwordField setStringValue:[[NSString string] stringByPaddingToLength:[[passwordField stringValue] length] withString:@"sp" startingAtIndex:0]];
[[self undoManager] removeAllActionsWithTarget:passwordField];
} else {
[connectionKeychainItemName release], connectionKeychainItemName = nil;
@@ -301,7 +302,7 @@ NSString *TableDocumentFavoritesControllerSelectionIndexDidChange = @"TableDocum
}
if (connectionSSHKeychainItemName) {
if ([[keyChainInstance getPasswordForName:connectionSSHKeychainItemName account:connectionSSHKeychainItemAccount] isEqualToString:[sshPasswordField stringValue]]) {
- [sshPasswordField setStringValue:@""];
+ [sshPasswordField setStringValue:[[NSString string] stringByPaddingToLength:[[sshPasswordField stringValue] length] withString:@"sp" startingAtIndex:0]];
[[self undoManager] removeAllActionsWithTarget:sshPasswordField];
} else {
[connectionSSHKeychainItemName release], connectionSSHKeychainItemName = nil;
@@ -316,7 +317,7 @@ NSString *TableDocumentFavoritesControllerSelectionIndexDidChange = @"TableDocum
}
// ...or start the MySQL connection process directly
- [self initiateMySQLConnection:nil];
+ [self initiateMySQLConnection];
}
/*
@@ -326,26 +327,26 @@ NSString *TableDocumentFavoritesControllerSelectionIndexDidChange = @"TableDocum
*/
- (void)initiateSSHTunnelConnection
{
- SPSSHTunnel *theTunnel;
[connectProgressStatusText setStringValue:NSLocalizedString(@"SSH connecting...", @"SSH connecting very short status message")];
[connectProgressStatusText display];
// Set up the tunnel details
- theTunnel = [[SPSSHTunnel alloc] initToHost:[sshHostField stringValue] port:([[sshPortField stringValue] length]?[sshPortField intValue]:22) login:[sshUserField stringValue] tunnellingToPort:([[portField stringValue] length]?[portField intValue]:3306) onHost:[hostField stringValue]];
+ sshTunnel = [[SPSSHTunnel alloc] initToHost:[sshHostField stringValue] port:([[sshPortField stringValue] length]?[sshPortField intValue]:22) login:[sshUserField stringValue] tunnellingToPort:([[portField stringValue] length]?[portField intValue]:3306) onHost:[hostField stringValue]];
+ [sshTunnel setParentWindow:tableWindow];
// Add keychain or plaintext password as appropriate - note the checks in initiateConnection.
if (connectionSSHKeychainItemName) {
- [theTunnel setPasswordKeychainName:connectionSSHKeychainItemName account:connectionSSHKeychainItemAccount];
+ [sshTunnel setPasswordKeychainName:connectionSSHKeychainItemName account:connectionSSHKeychainItemAccount];
} else {
- [theTunnel setPassword:[sshPasswordField stringValue]];
+ [sshTunnel setPassword:[sshPasswordField stringValue]];
}
// Set the callback function on the tunnel
- [theTunnel setConnectionStateChangeSelector:@selector(sshTunnelCallback:) delegate:self];
+ [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.
- [theTunnel connect];
+ [sshTunnel connect];
}
/*
@@ -360,19 +361,19 @@ NSString *TableDocumentFavoritesControllerSelectionIndexDidChange = @"TableDocum
if (newState == SPSSH_STATE_IDLE) {
[self failConnectionWithErrorMessage:[theTunnel lastError]];
} else if (newState == SPSSH_STATE_CONNECTED) {
- [self initiateMySQLConnection:theTunnel];
+ [self initiateMySQLConnection];
}
}
/*
* Set up the MySQL connection, either through a successful tunnel or directly.
*/
-- (void)initiateMySQLConnection:(SPSSHTunnel *)theTunnel
+- (void)initiateMySQLConnection
{
CMMCPResult *theResult;
id version;
- if (theTunnel)
+ if (sshTunnel)
[connectProgressStatusText setStringValue:NSLocalizedString(@"MySQL connecting...", @"MySQL connecting very short status message")];
else
[connectProgressStatusText setStringValue:NSLocalizedString(@"Connecting...", @"Generic connecting very short status message")];
@@ -388,12 +389,12 @@ NSString *TableDocumentFavoritesControllerSelectionIndexDidChange = @"TableDocum
// Otherwise, initialise to host, using tunnel if appropriate
} else {
- if (theTunnel) {
+ if (sshTunnel) {
mySQLConnection = [[CMMCPConnection alloc] initToHost:@"127.0.0.1"
withLogin:[userField stringValue]
- usingPort:[theTunnel localPort]];
- [mySQLConnection setSSHTunnel:theTunnel];
- [theTunnel release];
+ usingPort:[sshTunnel localPort]];
+ [mySQLConnection setSSHTunnel:sshTunnel];
+ [sshTunnel release], sshTunnel = nil;
} else {
mySQLConnection = [[CMMCPConnection alloc] initToHost:[hostField stringValue]
withLogin:[userField stringValue]
@@ -414,7 +415,6 @@ NSString *TableDocumentFavoritesControllerSelectionIndexDidChange = @"TableDocum
if (![mySQLConnection isConnected]) {
[self failConnectionWithErrorMessage:[NSString stringWithFormat:NSLocalizedString(@"Unable to connect to host %@, or the request timed out.\n\nBe sure that the address is correct and that you have the necessary privileges, or try increasing the connection timeout (currently %i seconds).\n\nMySQL said: %@", @"message of panel when connection to host failed"), [hostField stringValue], [[prefs objectForKey:@"ConnectionTimeoutValue"] intValue], [mySQLConnection getLastErrorMessage]]];
- if (theTunnel) [theTunnel disconnect];
return;
}
if (![[databaseField stringValue] isEqualToString:@""]) {
@@ -423,7 +423,6 @@ NSString *TableDocumentFavoritesControllerSelectionIndexDidChange = @"TableDocum
selectedDatabase = [[databaseField stringValue] retain];
} else {
[self failConnectionWithErrorMessage:[NSString stringWithFormat:NSLocalizedString(@"Connected to host, but unable to connect to database %@.\n\nBe sure that the database exists and that you have the necessary privileges.\n\nMySQL said: %@", @"message of panel when connection to db failed"), [databaseField stringValue], [mySQLConnection getLastErrorMessage]]];
- if (theTunnel) [theTunnel disconnect];
return;
}
}
@@ -500,6 +499,9 @@ NSString *TableDocumentFavoritesControllerSelectionIndexDidChange = @"TableDocum
// Stop the modal sheet
[connectSheet orderOut:nil];
[NSApp endSheet:connectSheet];
+
+ // Release as appropriate
+ if (sshTunnel) [sshTunnel disconnect], [sshTunnel release], sshTunnel = nil;
// Display the connection error message
NSBeginAlertSheet(NSLocalizedString(@"Connection failed!", @"connection failed title"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, @selector(sheetDidEnd:returnCode:contextInfo:), @"connect", theErrorMessage);
@@ -548,11 +550,8 @@ NSString *TableDocumentFavoritesControllerSelectionIndexDidChange = @"TableDocum
// 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
- connectionKeychainItemName = [[NSString stringWithFormat:@"Sequel Pro : %@ (%i)", [self valueForKeyPath:@"selectedFavorite.name"], [[self valueForKeyPath:@"selectedFavorite.id"] intValue]] retain];
- connectionKeychainItemAccount = [[NSString stringWithFormat:@"%@@%@/%@",
- [self valueForKeyPath:@"selectedFavorite.user"],
- [self valueForKeyPath:@"selectedFavorite.host"],
- [self valueForKeyPath:@"selectedFavorite.database"]] retain];
+ connectionKeychainItemName = [[keyChainInstance nameForFavoriteName:[self valueForKeyPath:@"selectedFavorite.name"] id:[self valueForKeyPath:@"selectedFavorite.id"]] retain];
+ connectionKeychainItemAccount = [[keyChainInstance accountForUser:[self valueForKeyPath:@"selectedFavorite.user"] host:[self valueForKeyPath:@"selectedFavorite.host"] database:[self valueForKeyPath:@"selectedFavorite.database"]] retain];
if ([keyChainInstance passwordExistsForName:connectionKeychainItemName account:connectionKeychainItemAccount]) {
[passwordField setStringValue:[keyChainInstance getPasswordForName:connectionKeychainItemName account:connectionKeychainItemAccount]];
} else {
@@ -562,10 +561,8 @@ NSString *TableDocumentFavoritesControllerSelectionIndexDidChange = @"TableDocum
}
// And the same for the SSH password
- connectionSSHKeychainItemName = [[NSString stringWithFormat:@"Sequel Pro SSHTunnel : %@ (%i)", [self valueForKeyPath:@"selectedFavorite.name"], [[self valueForKeyPath:@"selectedFavorite.id"] intValue]] retain];
- connectionSSHKeychainItemAccount = [[NSString stringWithFormat:@"%@@%@",
- [self valueForKeyPath:@"selectedFavorite.sshUser"],
- [self valueForKeyPath:@"selectedFavorite.sshHost"]] retain];
+ connectionSSHKeychainItemName = [[NSString alloc] initWithString:[keyChainInstance nameForSSHForFavoriteName:[self valueForKeyPath:@"selectedFavorite.name"] id:[self valueForKeyPath:@"selectedFavorite.id"]]];
+ connectionSSHKeychainItemAccount = [[NSString alloc] initWithString:[keyChainInstance accountForSSHUser:[self valueForKeyPath:@"selectedFavorite.sshUser"] sshHost:[self valueForKeyPath:@"selectedFavorite.sshHost"]]];
if ([keyChainInstance passwordExistsForName:connectionSSHKeychainItemName account:connectionSSHKeychainItemAccount]) {
[sshPasswordField setStringValue:[keyChainInstance getPasswordForName:connectionSSHKeychainItemName account:connectionSSHKeychainItemAccount]];
} else {
@@ -619,7 +616,7 @@ NSString *TableDocumentFavoritesControllerSelectionIndexDidChange = @"TableDocum
- (void)connectSheetAddToFavorites:(id)sender
{
- [self addToFavoritesName:[nameField stringValue] host:[hostField stringValue] socket:[socketField stringValue] user:[userField stringValue] password:[passwordField stringValue] port:[portField stringValue] database:[databaseField stringValue] useSSH:false sshHost:@"" sshUser:@"" sshPassword:@"" sshPort:@""];
+ [self addToFavoritesName:[nameField stringValue] host:[hostField stringValue] socket:[socketField stringValue] user:[userField stringValue] password:[passwordField stringValue] port:[portField stringValue] database:[databaseField stringValue] useSSH:([sshCheckbox state] == NSOnState) sshHost:[sshHostField stringValue] sshUser:[sshUserField stringValue] sshPassword:[sshPasswordField stringValue] sshPort:[sshPortField stringValue]];
}
/**
@@ -628,31 +625,50 @@ NSString *TableDocumentFavoritesControllerSelectionIndexDidChange = @"TableDocum
- (void)addToFavoritesName:(NSString *)name host:(NSString *)host socket:(NSString *)socket
user:(NSString *)user password:(NSString *)password
port:(NSString *)port database:(NSString *)database
- useSSH:(BOOL)useSSH // no-longer in use
- sshHost:(NSString *)sshHost // no-longer in use
- sshUser:(NSString *)sshUser // no-longer in use
- sshPassword:(NSString *)sshPassword // no-longer in use
- sshPort:(NSString *)sshPort // no-longer in use
+ useSSH:(BOOL)useSSH
+ sshHost:(NSString *)sshHost sshUser:(NSString *)sshUser
+ sshPassword:(NSString *)sshPassword sshPort:(NSString *)sshPort
{
NSString *favoriteName = [name length]?name:[NSString stringWithFormat:@"%@@%@", user, host];
NSNumber *favoriteid = [NSNumber numberWithInt:[[NSString stringWithFormat:@"%f", [[NSDate date] timeIntervalSince1970]] hash]];
if (![name length] && ![database isEqualToString:@""])
favoriteName = [NSString stringWithFormat:@"%@ %@", database, favoriteName];
- // test if host and socket are not nil
+ // Ensure that host and socket are not nil
if ([host isEqualToString:@""] && [socket isEqualToString:@""]) {
NSRunAlertPanel(NSLocalizedString(@"Insufficient connection details", @"insufficient details message"), NSLocalizedString(@"Insufficient details provided to establish a connection. Please provide at least a host or socket.", @"insufficient details informative message"), NSLocalizedString(@"OK", @"OK button"), nil, nil);
-
return;
}
- // write favorites and password
- NSMutableDictionary *newFavorite = [NSMutableDictionary dictionaryWithObjects:[NSArray arrayWithObjects:favoriteName, host, socket, user, port, database, favoriteid, nil]
- forKeys:[NSArray arrayWithObjects:@"name", @"host", @"socket", @"user", @"port", @"database", @"id", nil]];
+ // If SSH is enabled, ensure that the SSH host is not nil
+ if ([sshCheckbox state] == NSOnState && ![[sshHostField stringValue] length]) {
+ NSRunAlertPanel(NSLocalizedString(@"Insufficient connection details", @"insufficient details message"), NSLocalizedString(@"Please enter the hostname for the SSH Tunnel, or disable the SSH Tunnel.", @"message of panel when ssh details are incomplete"), NSLocalizedString(@"OK", @"OK button"), nil, nil);
+ return;
+ }
+
+ // Write favorites and password(s)
+ NSDictionary *newFavorite = [NSDictionary dictionaryWithObjectsAndKeys:
+ favoriteName, @"name",
+ host, @"host",
+ socket, @"socket",
+ user, @"user",
+ port, @"port",
+ database, @"database",
+ [NSNumber numberWithBool:useSSH], @"useSSH",
+ sshHost, @"sshHost",
+ sshUser, @"sshUser",
+ sshPort, @"sshPort",
+ favoriteid, @"id",
+ nil];
if (![password isEqualToString:@""]) {
[keyChainInstance addPassword:password
- forName:[NSString stringWithFormat:@"Sequel Pro : %@ (%i)", favoriteName, [favoriteid intValue]]
- account:[NSString stringWithFormat:@"%@@%@/%@", user, host, database]];
+ forName:[keyChainInstance nameForFavoriteName:favoriteName id:[NSString stringWithFormat:@"%i", [favoriteid intValue]]]
+ account:[keyChainInstance accountForUser:user host:host database:database]];
+ }
+ if (![sshPassword isEqualToString:@""]) {
+ [keyChainInstance addPassword:password
+ forName:[keyChainInstance nameForSSHForFavoriteName:favoriteName id:[NSString stringWithFormat:@"%i", [favoriteid intValue]]]
+ account:[keyChainInstance accountForSSHUser:sshUser sshHost:sshHost]];
}
[favoritesController addObject:newFavorite];
@@ -1568,7 +1584,7 @@ NSString *TableDocumentFavoritesControllerSelectionIndexDidChange = @"TableDocum
- (void)closeConnection
{
[mySQLConnection disconnect];
-
+
// Disconnected Growl notification
[[SPGrowlController sharedGrowlController] notifyWithTitle:@"Disconnected"
description:[NSString stringWithFormat:NSLocalizedString(@"Disconnected from %@",@"description for disconnected growl notification"), [tableWindow title]]
@@ -1858,8 +1874,19 @@ NSString *TableDocumentFavoritesControllerSelectionIndexDidChange = @"TableDocum
return;
}
- // Add current connection to favorites using the same method as used on the connection sheet to provide consistency.
- [self connectSheetAddToFavorites:self];
+ // Add current connection to favorites
+ NSString *password, *sshPassword;
+ if (connectionKeychainItemName) {
+ password = [keyChainInstance getPasswordForName:connectionKeychainItemName account:connectionKeychainItemAccount];
+ } else {
+ password = [passwordField stringValue];
+ }
+ if (connectionSSHKeychainItemName) {
+ sshPassword = [keyChainInstance getPasswordForName:connectionSSHKeychainItemName account:connectionSSHKeychainItemAccount];
+ } else {
+ sshPassword = [sshPasswordField stringValue];
+ }
+ [self addToFavoritesName:[nameField stringValue] host:[hostField stringValue] socket:[socketField stringValue] user:[userField stringValue] password:password port:[portField stringValue] database:[databaseField stringValue] useSSH:([sshCheckbox state] == NSOnState) sshHost:[sshHostField stringValue] sshUser:[sshUserField stringValue] sshPassword:sshPassword sshPort:[sshPortField stringValue]];
}
/**
@@ -2151,6 +2178,7 @@ NSString *TableDocumentFavoritesControllerSelectionIndexDidChange = @"TableDocum
- (void)windowWillClose:(NSNotification *)aNotification
{
if ([mySQLConnection isConnected]) [self closeConnection];
+ if (sshTunnel) [sshTunnel disconnect], [sshTunnel release], sshTunnel = nil;
if ([[[SPQueryConsole sharedQueryConsole] window] isVisible]) [self toggleConsole:self];
[[customQueryInstance helpWebViewWindow] release];
[createTableSyntaxWindow release];
diff --git a/Source/TunnelPassphraseRequester.m b/Source/TunnelPassphraseRequester.m
index a39c4fd1..4391e3e7 100644
--- a/Source/TunnelPassphraseRequester.m
+++ b/Source/TunnelPassphraseRequester.m
@@ -28,12 +28,37 @@ int main(int argc, const char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSDictionary *environment = [[NSProcessInfo processInfo] environment];
+ NSString *argument = nil;
+ SPSSHTunnel *sequelProTunnel;
+ NSString *connectionName = [environment objectForKey:@"SP_CONNECTION_NAME"];
if (![environment objectForKey:@"SP_PASSWORD_METHOD"]) {
[pool release];
return 1;
}
+ if (argc > 1) {
+ argument = [[NSString alloc] initWithCString:argv[1] encoding:NSUTF8StringEncoding];
+ }
+
+ // Check if we're being asked a question and respond if so
+ if (argument && [argument rangeOfString:@" (yes/no)?"].location != NSNotFound) {
+ sequelProTunnel = (SPSSHTunnel *)[NSConnection rootProxyForConnectionWithRegisteredName:connectionName host:nil];
+ if (!sequelProTunnel) {
+ NSLog(@"SSH Tunnel: unable to connect to Sequel Pro to show SSH question");
+ [pool release];
+ return 1;
+ }
+ BOOL response = [sequelProTunnel getResponseForQuestion:argument];
+ if (response) {
+ printf("yes\n");
+ } else {
+ printf("no\n");
+ }
+ [pool release];
+ return 0;
+ }
+
// If the password method is set to use the keychain, use the supplied keychain name to
// request the password
if ([[environment objectForKey:@"SP_PASSWORD_METHOD"] intValue] == SPSSH_PASSWORD_USES_KEYCHAIN) {
@@ -61,9 +86,7 @@ int main(int argc, const char *argv[])
// If the password method is set to request the password from the tunnel instance, do so.
if ([[environment objectForKey:@"SP_PASSWORD_METHOD"] intValue] == SPSSH_PASSWORD_ASKS_UI) {
- SPSSHTunnel *sequelProTunnel;
NSString *password;
- NSString *connectionName = [environment objectForKey:@"SP_CONNECTION_NAME"];
NSString *verificationHash = [environment objectForKey:@"SP_CONNECTION_VERIFY_HASH"];
if (!connectionName || !verificationHash) {
diff --git a/sequel-pro.xcodeproj/project.pbxproj b/sequel-pro.xcodeproj/project.pbxproj
index dfb7735e..a6ae8294 100644
--- a/sequel-pro.xcodeproj/project.pbxproj
+++ b/sequel-pro.xcodeproj/project.pbxproj
@@ -85,6 +85,7 @@
58186D210F4CB38900851FE9 /* ConnectionErrorDialog.xib in Resources */ = {isa = PBXBuildFile; fileRef = 58186D1F0F4CB38900851FE9 /* ConnectionErrorDialog.xib */; };
5841423F0F97E11000A34B47 /* NoodleLineNumberView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5841423E0F97E11000A34B47 /* NoodleLineNumberView.m */; };
584F5F8F0F50ACD800036517 /* table-view-small.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584F5F8E0F50ACD800036517 /* table-view-small.tiff */; };
+ 586F432B0FD74CFC00B428D7 /* SSHQuestionDialog.xib in Resources */ = {isa = PBXBuildFile; fileRef = 586F432A0FD74CFC00B428D7 /* SSHQuestionDialog.xib */; };
5885940F0F7AEE6000ED0E67 /* sparkle-public-key.pem in Resources */ = {isa = PBXBuildFile; fileRef = 5885940E0F7AEE6000ED0E67 /* sparkle-public-key.pem */; };
58C56EF50F438E120035701E /* SPDataCellFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 58C56EF40F438E120035701E /* SPDataCellFormatter.m */; };
58CB20ED0F79A75D005EA204 /* button_edit_mode_selected.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 58CB20EC0F79A75D005EA204 /* button_edit_mode_selected.tiff */; };
@@ -318,6 +319,7 @@
5841423D0F97E11000A34B47 /* NoodleLineNumberView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NoodleLineNumberView.h; sourceTree = "<group>"; };
5841423E0F97E11000A34B47 /* NoodleLineNumberView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NoodleLineNumberView.m; sourceTree = "<group>"; };
584F5F8E0F50ACD800036517 /* table-view-small.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "table-view-small.tiff"; sourceTree = "<group>"; };
+ 586F432A0FD74CFC00B428D7 /* SSHQuestionDialog.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SSHQuestionDialog.xib; sourceTree = "<group>"; };
588593F30F7AEC9500ED0E67 /* package-application.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "package-application.sh"; sourceTree = "<group>"; };
5885940E0F7AEE6000ED0E67 /* sparkle-public-key.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "sparkle-public-key.pem"; sourceTree = "<group>"; };
58C56EF30F438E120035701E /* SPDataCellFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPDataCellFormatter.h; sourceTree = "<group>"; };
@@ -746,6 +748,7 @@
58186D1F0F4CB38900851FE9 /* ConnectionErrorDialog.xib */,
B57747D00F7A891A003B34F9 /* Preferences.xib */,
B5E92F170F75B2D100012500 /* ExportDialog.xib */,
+ 586F432A0FD74CFC00B428D7 /* SSHQuestionDialog.xib */,
);
path = Interfaces;
sourceTree = "<group>";
@@ -1009,6 +1012,7 @@
177E792E0FCB54EC00E9E122 /* database-small.png in Resources */,
177E792F0FCB54EC00E9E122 /* dummy-small.png in Resources */,
177E79300FCB54EC00E9E122 /* table-small-square.tiff in Resources */,
+ 586F432B0FD74CFC00B428D7 /* SSHQuestionDialog.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};