aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrowanbeentje <rowan@beent.je>2009-05-28 01:14:26 +0000
committerrowanbeentje <rowan@beent.je>2009-05-28 01:14:26 +0000
commit1979b7c94813e8278b4b7616aeafecd5a406f7a1 (patch)
tree108669de904ec9ee85806e5f91ec17b23cda1f7e
parenta316ba498cf3300d05c8c2ba3223fa2c625d1717 (diff)
downloadsequelpro-1979b7c94813e8278b4b7616aeafecd5a406f7a1.tar.gz
sequelpro-1979b7c94813e8278b4b7616aeafecd5a406f7a1.tar.bz2
sequelpro-1979b7c94813e8278b4b7616aeafecd5a406f7a1.zip
Add support for SSH tunnels, improve password security, and tweaks:
- Implementation of a new SPSSHTunnel class, designed to closely integrate SSH tunnels within Sequel Pro. - Integration of SPSSHTunnel - new connection methods using callbacks, and CMMCPConnection integration - Keychain class upgrade to include the new SPSSHTunnel keychain password helper on the trusted access list for new passwords - Keychain passwords are now held in memory/UI for only as long as necessary, increasing password security - Updated interface to enable/add SSH tunnel functionality - Remove old SSHTunnel class - Addition of new target for the SSH Tunnel password assistant, addition as a dependency of the main target, and addition to build script to copy into resources directory - Fix a keychain password deletion crash
-rw-r--r--Interfaces/English.lproj/DBView.xib771
-rw-r--r--Interfaces/English.lproj/Preferences.xib142
-rw-r--r--Source/CMMCPConnection.h17
-rw-r--r--Source/CMMCPConnection.m251
-rw-r--r--Source/KeyChain.m56
-rw-r--r--Source/SPPreferenceController.h4
-rw-r--r--Source/SPPreferenceController.m115
-rw-r--r--Source/SPSSHTunnel.h54
-rw-r--r--Source/SPSSHTunnel.m359
-rw-r--r--Source/SSHTunnel.h57
-rw-r--r--Source/SSHTunnel.m531
-rw-r--r--Source/TableDocument.h21
-rw-r--r--Source/TableDocument.m438
-rw-r--r--Source/TunnelPassphraseRequester.m96
-rw-r--r--sequel-pro.xcodeproj/project.pbxproj148
15 files changed, 2103 insertions, 957 deletions
diff --git a/Interfaces/English.lproj/DBView.xib b/Interfaces/English.lproj/DBView.xib
index 8c6b0e98..d669d41d 100644
--- a/Interfaces/English.lproj/DBView.xib
+++ b/Interfaces/English.lproj/DBView.xib
@@ -2,13 +2,12 @@
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.03">
<data>
<int key="IBDocument.SystemTarget">1050</int>
- <string key="IBDocument.SystemVersion">9J61</string>
+ <string key="IBDocument.SystemVersion">9G55</string>
<string key="IBDocument.InterfaceBuilderVersion">677</string>
- <string key="IBDocument.AppKitVersion">949.46</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="5825"/>
</object>
<object class="NSArray" key="IBDocument.PluginDependencies">
<bool key="EncodedWithXMLCoder">YES</bool>
@@ -80,6 +79,7 @@
<int key="NSvFlags">4352</int>
<string key="NSFrameSize">{212, 393}</string>
<reference key="NSSuperview" ref="73685676"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="_NSCornerView" key="NSCornerView">
<nil key="NSNextResponder"/>
@@ -176,6 +176,7 @@
</object>
<string key="NSFrame">{{1, 1}, {212, 393}}</string>
<reference key="NSSuperview" ref="233472824"/>
+ <reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="251040077"/>
<reference key="NSDocView" ref="251040077"/>
<object class="NSColor" key="NSBGColor" id="1024678221">
@@ -191,6 +192,7 @@
<int key="NSvFlags">-2147483392</int>
<string key="NSFrame">{{175, 1}, {15, 481}}</string>
<reference key="NSSuperview" ref="233472824"/>
+ <reference key="NSWindow"/>
<reference key="NSTarget" ref="233472824"/>
<string key="NSAction">_doScroller:</string>
<double key="NSPercent">9.979253e-01</double>
@@ -200,6 +202,7 @@
<int key="NSvFlags">256</int>
<string key="NSFrame">{{-100, -100}, {141, 11}}</string>
<reference key="NSSuperview" ref="233472824"/>
+ <reference key="NSWindow"/>
<int key="NSsFlags">257</int>
<reference key="NSTarget" ref="233472824"/>
<string key="NSAction">_doScroller:</string>
@@ -208,6 +211,7 @@
</object>
<string key="NSFrameSize">{214, 395}</string>
<reference key="NSSuperview" ref="355288374"/>
+ <reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="73685676"/>
<int key="NSsFlags">530</int>
<reference key="NSVScroller" ref="693168867"/>
@@ -230,6 +234,7 @@
<int key="NSvFlags">4352</int>
<string key="NSFrameSize">{212, 123}</string>
<reference key="NSSuperview" ref="685057119"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="_NSCornerView" key="NSCornerView">
<nil key="NSNextResponder"/>
@@ -293,6 +298,7 @@
</object>
<string key="NSFrame">{{1, 1}, {212, 123}}</string>
<reference key="NSSuperview" ref="298226231"/>
+ <reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="347093764"/>
<reference key="NSDocView" ref="347093764"/>
<reference key="NSBGColor" ref="1024678221"/>
@@ -303,6 +309,7 @@
<int key="NSvFlags">-2147483392</int>
<string key="NSFrame">{{175, 1}, {15, 481}}</string>
<reference key="NSSuperview" ref="298226231"/>
+ <reference key="NSWindow"/>
<reference key="NSTarget" ref="298226231"/>
<string key="NSAction">_doScroller:</string>
<double key="NSPercent">9.979253e-01</double>
@@ -312,6 +319,7 @@
<int key="NSvFlags">256</int>
<string key="NSFrame">{{-100, -100}, {141, 11}}</string>
<reference key="NSSuperview" ref="298226231"/>
+ <reference key="NSWindow"/>
<int key="NSsFlags">257</int>
<reference key="NSTarget" ref="298226231"/>
<string key="NSAction">_doScroller:</string>
@@ -320,6 +328,7 @@
</object>
<string key="NSFrame">{{0, 404}, {214, 125}}</string>
<reference key="NSSuperview" ref="355288374"/>
+ <reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="685057119"/>
<int key="NSsFlags">530</int>
<reference key="NSVScroller" ref="245346414"/>
@@ -330,12 +339,14 @@
</object>
<string key="NSFrame">{{-1, 22}, {214, 529}}</string>
<reference key="NSSuperview" ref="372294785"/>
+ <reference key="NSWindow"/>
</object>
<object class="NSButton" id="644515521">
<reference key="NSNextResponder" ref="372294785"/>
<int key="NSvFlags">292</int>
<string key="NSFrame">{{0, -1}, {32, 25}}</string>
<reference key="NSSuperview" ref="372294785"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSButtonCell" key="NSCell" id="492393561">
<int key="NSCellFlags">-2080244224</int>
@@ -364,6 +375,7 @@
<int key="NSvFlags">292</int>
<string key="NSFrame">{{20, 0}, {46, 25}}</string>
<reference key="NSSuperview" ref="372294785"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSPopUpButtonCell" key="NSCell" id="753352469">
<int key="NSCellFlags">-2076049856</int>
@@ -372,7 +384,7 @@
<reference key="NSControlView" ref="1029554648"/>
<int key="NSButtonFlags">-2042609409</int>
<int key="NSButtonFlags2">35</int>
- <object class="NSCustomResource" key="NSNormalImage" id="1073527761">
+ <object class="NSCustomResource" key="NSNormalImage">
<string key="NSClassName">NSImage</string>
<string key="NSResourceName">button_action</string>
</object>
@@ -387,7 +399,10 @@
<string key="NSKeyEquiv"/>
<int key="NSKeyEquivModMask">1048576</int>
<int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSImage" ref="1073527761"/>
+ <object class="NSCustomResource" key="NSImage" id="971406164">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">button_action</string>
+ </object>
<string key="NSAction">_popUpItemAction:</string>
<reference key="NSTarget" ref="753352469"/>
</object>
@@ -471,6 +486,7 @@
</object>
<string key="NSFrame">{{197, 0}, {15, 23}}</string>
<reference key="NSSuperview" ref="372294785"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSImageCell" key="NSCell" id="875296521">
<int key="NSCellFlags">130560</int>
@@ -503,6 +519,7 @@
</object>
<string key="NSFrame">{{93, 0}, {104, 23}}</string>
<reference key="NSSuperview" ref="372294785"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSImageCell" key="NSCell" id="761703901">
<int key="NSCellFlags">130560</int>
@@ -523,6 +540,7 @@
<int key="NSvFlags">292</int>
<string key="NSFrame">{{61, -1}, {32, 25}}</string>
<reference key="NSSuperview" ref="372294785"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSButtonCell" key="NSCell" id="529591350">
<int key="NSCellFlags">-2080244224</int>
@@ -545,6 +563,7 @@
</object>
<string key="NSFrameSize">{212, 550}</string>
<reference key="NSSuperview" ref="937377983"/>
+ <reference key="NSWindow"/>
<string key="NSClassName">NSView</string>
</object>
<object class="NSCustomView" id="604818293">
@@ -557,6 +576,7 @@
<int key="NSvFlags">274</int>
<string key="NSFrame">{{-7, -10}, {735, 564}}</string>
<reference key="NSSuperview" ref="604818293"/>
+ <reference key="NSWindow"/>
<object class="NSMutableArray" key="NSTabViewItems">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSTabViewItem" id="831053945">
@@ -2938,7 +2958,7 @@
<reference key="NSControlView" ref="363916571"/>
<int key="NSButtonFlags">-2042609409</int>
<int key="NSButtonFlags2">35</int>
- <reference key="NSNormalImage" ref="1073527761"/>
+ <reference key="NSNormalImage" ref="971406164"/>
<string key="NSAlternateContents"/>
<string key="NSKeyEquivalent"/>
<int key="NSPeriodicDelay">400</int>
@@ -2950,7 +2970,7 @@
<string key="NSKeyEquiv"/>
<int key="NSKeyEquivModMask">1048576</int>
<int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSImage" ref="1073527761"/>
+ <reference key="NSImage" ref="971406164"/>
<string key="NSAction">_popUpItemAction:</string>
<reference key="NSTarget" ref="984501775"/>
</object>
@@ -3317,6 +3337,7 @@
<int key="NSvFlags">268</int>
<string key="NSFrame">{{81, 499}, {34, 14}}</string>
<reference key="NSSuperview" ref="730602982"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="643775960">
<int key="NSCellFlags">68288064</int>
@@ -3333,6 +3354,7 @@
<int key="NSvFlags">266</int>
<string key="NSFrame">{{305, 473}, {342, 14}}</string>
<reference key="NSSuperview" ref="730602982"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="705814774">
<int key="NSCellFlags">68288064</int>
@@ -3349,6 +3371,7 @@
<int key="NSvFlags">266</int>
<string key="NSFrame">{{308, 498}, {339, 14}}</string>
<reference key="NSSuperview" ref="730602982"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="51220700">
<int key="NSCellFlags">68288064</int>
@@ -3365,6 +3388,7 @@
<int key="NSvFlags">268</int>
<string key="NSFrame">{{58, 473}, {57, 14}}</string>
<reference key="NSSuperview" ref="730602982"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="47286035">
<int key="NSCellFlags">68288064</int>
@@ -3381,6 +3405,7 @@
<int key="NSvFlags">268</int>
<string key="NSFrame">{{59, 448}, {57, 14}}</string>
<reference key="NSSuperview" ref="730602982"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="234890062">
<int key="NSCellFlags">68288064</int>
@@ -3397,6 +3422,7 @@
<int key="NSvFlags">268</int>
<string key="NSFrame">{{117, 494}, {161, 22}}</string>
<reference key="NSSuperview" ref="730602982"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSPopUpButtonCell" key="NSCell" id="338551660">
<int key="NSCellFlags">-1539178944</int>
@@ -3429,6 +3455,7 @@
<int key="NSvFlags">268</int>
<string key="NSFrame">{{117, 469}, {161, 22}}</string>
<reference key="NSSuperview" ref="730602982"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSPopUpButtonCell" key="NSCell" id="790667540">
<int key="NSCellFlags">-1539178944</int>
@@ -3461,6 +3488,7 @@
<int key="NSvFlags">268</int>
<string key="NSFrame">{{117, 444}, {161, 22}}</string>
<reference key="NSSuperview" ref="730602982"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSPopUpButtonCell" key="NSCell" id="351231451">
<int key="NSCellFlags">-1539178944</int>
@@ -3493,6 +3521,7 @@
<int key="NSvFlags">10</int>
<string key="NSFrame">{{25, 425}, {644, 5}}</string>
<reference key="NSSuperview" ref="730602982"/>
+ <reference key="NSWindow"/>
<string key="NSOffsets">{0, 0}</string>
<object class="NSTextFieldCell" key="NSTitleCell">
<int key="NSCellFlags">67239424</int>
@@ -3515,6 +3544,7 @@
<int key="NSvFlags">10</int>
<string key="NSFrame">{{24, 314}, {644, 5}}</string>
<reference key="NSSuperview" ref="730602982"/>
+ <reference key="NSWindow"/>
<string key="NSOffsets">{0, 0}</string>
<object class="NSTextFieldCell" key="NSTitleCell">
<int key="NSCellFlags">67239424</int>
@@ -3537,6 +3567,7 @@
<int key="NSvFlags">270</int>
<string key="NSFrame">{{22, 398}, {256, 14}}</string>
<reference key="NSSuperview" ref="730602982"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="632489290">
<int key="NSCellFlags">68288064</int>
@@ -3553,6 +3584,7 @@
<int key="NSvFlags">270</int>
<string key="NSFrame">{{46, 376}, {232, 14}}</string>
<reference key="NSSuperview" ref="730602982"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="246518824">
<int key="NSCellFlags">68288064</int>
@@ -3569,6 +3601,7 @@
<int key="NSvFlags">266</int>
<string key="NSFrame">{{23, 354}, {255, 14}}</string>
<reference key="NSSuperview" ref="730602982"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="738092126">
<int key="NSCellFlags">68288064</int>
@@ -3585,6 +3618,7 @@
<int key="NSvFlags">266</int>
<string key="NSFrame">{{25, 332}, {253, 14}}</string>
<reference key="NSSuperview" ref="730602982"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="71975986">
<int key="NSCellFlags">68288064</int>
@@ -3601,6 +3635,7 @@
<int key="NSvFlags">270</int>
<string key="NSFrame">{{314, 398}, {358, 14}}</string>
<reference key="NSSuperview" ref="730602982"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="1035416348">
<int key="NSCellFlags">68288064</int>
@@ -3617,6 +3652,7 @@
<int key="NSvFlags">270</int>
<string key="NSFrame">{{290, 376}, {381, 14}}</string>
<reference key="NSSuperview" ref="730602982"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="502320319">
<int key="NSCellFlags">68288064</int>
@@ -3633,6 +3669,7 @@
<int key="NSvFlags">266</int>
<string key="NSFrame">{{309, 354}, {362, 14}}</string>
<reference key="NSSuperview" ref="730602982"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="686395978">
<int key="NSCellFlags">68288064</int>
@@ -3649,6 +3686,7 @@
<int key="NSvFlags">266</int>
<string key="NSFrame">{{289, 332}, {383, 14}}</string>
<reference key="NSSuperview" ref="730602982"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="314352793">
<int key="NSCellFlags">68288064</int>
@@ -3665,6 +3703,7 @@
<int key="NSvFlags">268</int>
<string key="NSFrame">{{50, 287}, {69, 14}}</string>
<reference key="NSSuperview" ref="730602982"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="647656972">
<int key="NSCellFlags">68288064</int>
@@ -3681,6 +3720,7 @@
<int key="NSvFlags">268</int>
<string key="NSFrame">{{34, 208}, {81, 14}}</string>
<reference key="NSSuperview" ref="730602982"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="625199500">
<int key="NSCellFlags">68288064</int>
@@ -3730,6 +3770,7 @@
</object>
<string key="NSFrameSize">{536, 14}</string>
<reference key="NSSuperview" ref="78677381"/>
+ <reference key="NSWindow"/>
<object class="NSTextContainer" key="NSTextContainer" id="708157837">
<object class="NSLayoutManager" key="NSLayoutManager">
<object class="NSTextStorage" key="NSTextStorage">
@@ -3793,6 +3834,7 @@
</object>
<string key="NSFrame">{{1, 1}, {536, 69}}</string>
<reference key="NSSuperview" ref="709531668"/>
+ <reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="69074373"/>
<reference key="NSDocView" ref="69074373"/>
<reference key="NSBGColor" ref="449903125"/>
@@ -3804,6 +3846,7 @@
<int key="NSvFlags">256</int>
<string key="NSFrame">{{537, 1}, {11, 69}}</string>
<reference key="NSSuperview" ref="709531668"/>
+ <reference key="NSWindow"/>
<int key="NSsFlags">256</int>
<reference key="NSTarget" ref="709531668"/>
<string key="NSAction">_doScroller:</string>
@@ -3814,6 +3857,7 @@
<int key="NSvFlags">256</int>
<string key="NSFrame">{{-100, -100}, {87, 18}}</string>
<reference key="NSSuperview" ref="709531668"/>
+ <reference key="NSWindow"/>
<int key="NSsFlags">1</int>
<reference key="NSTarget" ref="709531668"/>
<string key="NSAction">_doScroller:</string>
@@ -3823,6 +3867,7 @@
</object>
<string key="NSFrame">{{120, 230}, {549, 71}}</string>
<reference key="NSSuperview" ref="730602982"/>
+ <reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="78677381"/>
<int key="NSsFlags">18</int>
<reference key="NSVScroller" ref="892376800"/>
@@ -3844,6 +3889,7 @@
<int key="NSvFlags">2322</int>
<string key="NSFrameSize">{536, 14}</string>
<reference key="NSSuperview" ref="637054939"/>
+ <reference key="NSWindow"/>
<object class="NSTextContainer" key="NSTextContainer" id="194867362">
<object class="NSLayoutManager" key="NSLayoutManager">
<object class="NSTextStorage" key="NSTextStorage">
@@ -3904,6 +3950,7 @@
</object>
<string key="NSFrame">{{1, 1}, {536, 190}}</string>
<reference key="NSSuperview" ref="449863508"/>
+ <reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="421486962"/>
<reference key="NSDocView" ref="421486962"/>
<reference key="NSBGColor" ref="449903125"/>
@@ -3915,6 +3962,7 @@
<int key="NSvFlags">256</int>
<string key="NSFrame">{{537, 1}, {11, 190}}</string>
<reference key="NSSuperview" ref="449863508"/>
+ <reference key="NSWindow"/>
<int key="NSsFlags">256</int>
<reference key="NSTarget" ref="449863508"/>
<string key="NSAction">_doScroller:</string>
@@ -3925,6 +3973,7 @@
<int key="NSvFlags">256</int>
<string key="NSFrame">{{-100, -100}, {87, 18}}</string>
<reference key="NSSuperview" ref="449863508"/>
+ <reference key="NSWindow"/>
<int key="NSsFlags">1</int>
<reference key="NSTarget" ref="449863508"/>
<string key="NSAction">_doScroller:</string>
@@ -3934,6 +3983,7 @@
</object>
<string key="NSFrame">{{120, 30}, {549, 192}}</string>
<reference key="NSSuperview" ref="730602982"/>
+ <reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="637054939"/>
<int key="NSsFlags">18</int>
<reference key="NSVScroller" ref="633929130"/>
@@ -3943,6 +3993,7 @@
</object>
<string key="NSFrame">{{10, 7}, {700, 544}}</string>
<reference key="NSSuperview" ref="714795046"/>
+ <reference key="NSWindow"/>
</object>
<string key="NSLabel">Status</string>
<reference key="NSColor" ref="62854682"/>
@@ -4373,17 +4424,20 @@
</object>
<string key="NSFrame">{{221, 0}, {723, 550}}</string>
<reference key="NSSuperview" ref="937377983"/>
+ <reference key="NSWindow"/>
<string key="NSClassName">NSView</string>
</object>
</object>
<string key="NSFrameSize">{944, 550}</string>
<reference key="NSSuperview" ref="579726586"/>
+ <reference key="NSWindow"/>
<bool key="NSIsVertical">YES</bool>
<string key="NSAutosaveName">DBViewSplitter</string>
</object>
</object>
<string key="NSFrameSize">{944, 550}</string>
<reference key="NSSuperview"/>
+ <reference key="NSWindow"/>
</object>
<string key="NSScreenRect">{{0, 0}, {1440, 878}}</string>
<string key="NSMinSize">{780, 502}</string>
@@ -4393,7 +4447,7 @@
<object class="NSWindowTemplate" id="456332212">
<int key="NSWindowStyleMask">1</int>
<int key="NSWindowBacking">2</int>
- <string key="NSWindowRect">{{469, 435}, {519, 347}}</string>
+ <string key="NSWindowRect">{{469, 306}, {519, 476}}</string>
<int key="NSWTFlags">1886914560</int>
<string key="NSWindowTitle">connectSheet</string>
<string key="NSWindowClass">NSWindow</string>
@@ -4403,7 +4457,7 @@
<string key="NSWindowContentMaxSize">{3.40282e+38, 3.40282e+38}</string>
<string key="NSWindowContentMinSize">{256, 191}</string>
<object class="NSView" key="NSWindowView" id="586457094">
- <nil key="NSNextResponder"/>
+ <reference key="NSNextResponder"/>
<int key="NSvFlags">256</int>
<object class="NSMutableArray" key="NSSubviews">
<bool key="EncodedWithXMLCoder">YES</bool>
@@ -4412,6 +4466,7 @@
<int key="NSvFlags">256</int>
<string key="NSFrame">{{408, 12}, {92, 32}}</string>
<reference key="NSSuperview" ref="586457094"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSButtonCell" key="NSCell" id="492536988">
<int key="NSCellFlags">67239424</int>
@@ -4437,6 +4492,7 @@
<int key="NSvFlags">256</int>
<string key="NSFrame">{{326, 12}, {82, 32}}</string>
<reference key="NSSuperview" ref="586457094"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSButtonCell" key="NSCell" id="1016292439">
<int key="NSCellFlags">67239424</int>
@@ -4459,14 +4515,16 @@
<object class="NSPSMatrix" key="NSDrawMatrix"/>
<string key="NSFrame">{{48, 22}, {16, 16}}</string>
<reference key="NSSuperview" ref="586457094"/>
+ <reference key="NSWindow"/>
<int key="NSpiFlags">28938</int>
<double key="NSMaxValue">1.000000e+02</double>
</object>
<object class="NSTextField" id="593089150">
<reference key="NSNextResponder" ref="586457094"/>
<int key="NSvFlags">268</int>
- <string key="NSFrame">{{71, 302}, {97, 21}}</string>
+ <string key="NSFrame">{{71, 431}, {97, 21}}</string>
<reference key="NSSuperview" ref="586457094"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="196685261">
<int key="NSCellFlags">67239488</int>
@@ -4497,8 +4555,9 @@
<string>NeXT TIFF v4.0 pasteboard type</string>
</object>
</object>
- <string key="NSFrame">{{15, 287}, {48, 48}}</string>
+ <string key="NSFrame">{{15, 416}, {48, 48}}</string>
<reference key="NSSuperview" ref="586457094"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSImageCell" key="NSCell" id="706772332">
<int key="NSCellFlags">130560</int>
@@ -4519,6 +4578,7 @@
<int key="NSvFlags">292</int>
<string key="NSFrame">{{13, 16}, {25, 25}}</string>
<reference key="NSSuperview" ref="586457094"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSButtonCell" key="NSCell" id="1061790099">
<int key="NSCellFlags">67239424</int>
@@ -4537,8 +4597,9 @@
<object class="NSTextField" id="725657385">
<reference key="NSNextResponder" ref="586457094"/>
<int key="NSvFlags">-2147483356</int>
- <string key="NSFrame">{{69, 22}, {76, 14}}</string>
+ <string key="NSFrame">{{69, 22}, {116, 14}}</string>
<reference key="NSSuperview" ref="586457094"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="4826163">
<int key="NSCellFlags">67239488</int>
@@ -4563,8 +4624,9 @@
<object class="NSTextField" id="252686319">
<reference key="NSNextResponder" ref="781284031"/>
<int key="NSvFlags">256</int>
- <string key="NSFrame">{{320, 150}, {185, 18}}</string>
+ <string key="NSFrame">{{320, 279}, {185, 18}}</string>
<reference key="NSSuperview" ref="781284031"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="959000443">
<int key="NSCellFlags">-1804468671</int>
@@ -4580,8 +4642,9 @@
<object class="NSTextField" id="317936232">
<reference key="NSNextResponder" ref="781284031"/>
<int key="NSvFlags">256</int>
- <string key="NSFrame">{{287, 18}, {29, 17}}</string>
+ <string key="NSFrame">{{287, 147}, {29, 17}}</string>
<reference key="NSSuperview" ref="781284031"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="435677462">
<int key="NSCellFlags">67239424</int>
@@ -4596,8 +4659,9 @@
<object class="NSTextField" id="706801224">
<reference key="NSNextResponder" ref="781284031"/>
<int key="NSvFlags">256</int>
- <string key="NSFrame">{{284, 148}, {33, 17}}</string>
+ <string key="NSFrame">{{284, 277}, {33, 17}}</string>
<reference key="NSSuperview" ref="781284031"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="217675313">
<int key="NSCellFlags">67239424</int>
@@ -4612,8 +4676,9 @@
<object class="NSTextField" id="591248388">
<reference key="NSNextResponder" ref="781284031"/>
<int key="NSvFlags">256</int>
- <string key="NSFrame">{{320, 20}, {185, 18}}</string>
+ <string key="NSFrame">{{320, 149}, {185, 18}}</string>
<reference key="NSSuperview" ref="781284031"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="343850840">
<int key="NSCellFlags">-1804468671</int>
@@ -4630,8 +4695,9 @@
<object class="NSTextField" id="473901395">
<reference key="NSNextResponder" ref="781284031"/>
<int key="NSvFlags">256</int>
- <string key="NSFrame">{{259, 70}, {57, 17}}</string>
+ <string key="NSFrame">{{259, 199}, {57, 17}}</string>
<reference key="NSSuperview" ref="781284031"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="780018727">
<int key="NSCellFlags">67239424</int>
@@ -4646,8 +4712,9 @@
<object class="NSTextField" id="719075675">
<reference key="NSNextResponder" ref="781284031"/>
<int key="NSvFlags">256</int>
- <string key="NSFrame">{{285, 122}, {32, 17}}</string>
+ <string key="NSFrame">{{285, 251}, {32, 17}}</string>
<reference key="NSSuperview" ref="781284031"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="778458567">
<int key="NSCellFlags">67239424</int>
@@ -4662,8 +4729,9 @@
<object class="NSTextField" id="622703817">
<reference key="NSNextResponder" ref="781284031"/>
<int key="NSvFlags">256</int>
- <string key="NSFrame">{{320, 98}, {185, 18}}</string>
+ <string key="NSFrame">{{320, 227}, {185, 18}}</string>
<reference key="NSSuperview" ref="781284031"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="348978059">
<int key="NSCellFlags">-1804468671</int>
@@ -4679,8 +4747,9 @@
<object class="NSTextField" id="387080032">
<reference key="NSNextResponder" ref="781284031"/>
<int key="NSvFlags">256</int>
- <string key="NSFrame">{{320, 72}, {185, 18}}</string>
+ <string key="NSFrame">{{320, 201}, {185, 18}}</string>
<reference key="NSSuperview" ref="781284031"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="535348279">
<int key="NSCellFlags">-1804468671</int>
@@ -4697,8 +4766,9 @@
<object class="NSTextField" id="744530295">
<reference key="NSNextResponder" ref="781284031"/>
<int key="NSvFlags">256</int>
- <string key="NSFrame">{{320, 124}, {185, 18}}</string>
+ <string key="NSFrame">{{320, 253}, {185, 18}}</string>
<reference key="NSSuperview" ref="781284031"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="370939232">
<int key="NSCellFlags">-1804468671</int>
@@ -4714,8 +4784,9 @@
<object class="NSTextField" id="452595742">
<reference key="NSNextResponder" ref="781284031"/>
<int key="NSvFlags">256</int>
- <string key="NSFrame">{{320, 46}, {185, 18}}</string>
+ <string key="NSFrame">{{320, 175}, {185, 18}}</string>
<reference key="NSSuperview" ref="781284031"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="538051351">
<int key="NSCellFlags">-1804468671</int>
@@ -4732,8 +4803,9 @@
<object class="NSTextField" id="187305739">
<reference key="NSNextResponder" ref="781284031"/>
<int key="NSvFlags">256</int>
- <string key="NSFrame">{{273, 44}, {43, 17}}</string>
+ <string key="NSFrame">{{273, 173}, {43, 17}}</string>
<reference key="NSSuperview" ref="781284031"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="209896109">
<int key="NSCellFlags">67239424</int>
@@ -4748,8 +4820,9 @@
<object class="NSTextField" id="770654438">
<reference key="NSNextResponder" ref="781284031"/>
<int key="NSvFlags">256</int>
- <string key="NSFrame">{{259, 96}, {58, 17}}</string>
+ <string key="NSFrame">{{259, 225}, {58, 17}}</string>
<reference key="NSSuperview" ref="781284031"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="61722355">
<int key="NSCellFlags">67239424</int>
@@ -4764,8 +4837,9 @@
<object class="NSTextField" id="223403285">
<reference key="NSNextResponder" ref="781284031"/>
<int key="NSvFlags">268</int>
- <string key="NSFrame">{{269, 202}, {236, 17}}</string>
+ <string key="NSFrame">{{269, 331}, {236, 17}}</string>
<reference key="NSSuperview" ref="781284031"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="605875122">
<int key="NSCellFlags">67239488</int>
@@ -4784,8 +4858,9 @@
<object class="NSTextField" id="480030144">
<reference key="NSNextResponder" ref="781284031"/>
<int key="NSvFlags">256</int>
- <string key="NSFrame">{{320, 176}, {185, 18}}</string>
+ <string key="NSFrame">{{320, 305}, {185, 18}}</string>
<reference key="NSSuperview" ref="781284031"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="240173702">
<int key="NSCellFlags">-1804468671</int>
@@ -4802,8 +4877,9 @@
<object class="NSTextField" id="230979761">
<reference key="NSNextResponder" ref="781284031"/>
<int key="NSvFlags">256</int>
- <string key="NSFrame">{{237, 173}, {80, 18}}</string>
+ <string key="NSFrame">{{237, 302}, {80, 18}}</string>
<reference key="NSSuperview" ref="781284031"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="443231554">
<int key="NSCellFlags">67239424</int>
@@ -4838,14 +4914,16 @@
<object class="NSTableView" id="158332213">
<reference key="NSNextResponder" ref="131927425"/>
<int key="NSvFlags">4352</int>
- <string key="NSFrameSize">{215, 164}</string>
+ <string key="NSFrameSize">{215, 287}</string>
<reference key="NSSuperview" ref="131927425"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTableHeaderView" key="NSHeaderView" id="975843240">
<reference key="NSNextResponder" ref="333364494"/>
<int key="NSvFlags">256</int>
<string key="NSFrameSize">{215, 17}</string>
<reference key="NSSuperview" ref="333364494"/>
+ <reference key="NSWindow"/>
<reference key="NSTableView" ref="158332213"/>
</object>
<object class="_NSCornerView" key="NSCornerView" id="1013344315">
@@ -4853,6 +4931,7 @@
<int key="NSvFlags">-2147483392</int>
<string key="NSFrame">{{-26, 0}, {16, 17}}</string>
<reference key="NSSuperview" ref="112292194"/>
+ <reference key="NSWindow"/>
</object>
<object class="NSMutableArray" key="NSTableColumns">
<bool key="EncodedWithXMLCoder">YES</bool>
@@ -4898,8 +4977,9 @@
<bool key="NSAllowsTypeSelect">YES</bool>
</object>
</object>
- <string key="NSFrame">{{0, 17}, {215, 164}}</string>
+ <string key="NSFrame">{{0, 17}, {215, 287}}</string>
<reference key="NSSuperview" ref="112292194"/>
+ <reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="158332213"/>
<reference key="NSDocView" ref="158332213"/>
<reference key="NSBGColor" ref="1024678221"/>
@@ -4910,6 +4990,7 @@
<int key="NSvFlags">-2147483392</int>
<string key="NSFrame">{{199, 17}, {15, 162}}</string>
<reference key="NSSuperview" ref="112292194"/>
+ <reference key="NSWindow"/>
<reference key="NSTarget" ref="112292194"/>
<string key="NSAction">_doScroller:</string>
<double key="NSPercent">9.943503e-01</double>
@@ -4919,6 +5000,7 @@
<int key="NSvFlags">-2147483392</int>
<string key="NSFrame">{{-100, -100}, {198, 15}}</string>
<reference key="NSSuperview" ref="112292194"/>
+ <reference key="NSWindow"/>
<int key="NSsFlags">1</int>
<reference key="NSTarget" ref="112292194"/>
<string key="NSAction">_doScroller:</string>
@@ -4933,6 +5015,7 @@
</object>
<string key="NSFrameSize">{215, 17}</string>
<reference key="NSSuperview" ref="112292194"/>
+ <reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="975843240"/>
<reference key="NSDocView" ref="975843240"/>
<reference key="NSBGColor" ref="1024678221"/>
@@ -4940,8 +5023,9 @@
</object>
<reference ref="1013344315"/>
</object>
- <string key="NSFrame">{{-1, 22}, {215, 181}}</string>
+ <string key="NSFrame">{{-1, 23}, {215, 304}}</string>
<reference key="NSSuperview" ref="1043550915"/>
+ <reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="131927425"/>
<int key="NSsFlags">528</int>
<reference key="NSVScroller" ref="785468991"/>
@@ -4956,6 +5040,7 @@
<int key="NSvFlags">292</int>
<string key="NSFrame">{{-1, -1}, {32, 25}}</string>
<reference key="NSSuperview" ref="1043550915"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSButtonCell" key="NSCell" id="745497435">
<int key="NSCellFlags">-2080244224</int>
@@ -4992,6 +5077,7 @@
</object>
<string key="NSFrame">{{31, 0}, {183, 23}}</string>
<reference key="NSSuperview" ref="1043550915"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSImageCell" key="NSCell" id="769359275">
<int key="NSCellFlags">130560</int>
@@ -5005,12 +5091,14 @@
<bool key="NSEditable">YES</bool>
</object>
</object>
- <string key="NSFrame">{{1, 1}, {213, 202}}</string>
+ <string key="NSFrame">{{1, 1}, {213, 326}}</string>
<reference key="NSSuperview" ref="56931060"/>
+ <reference key="NSWindow"/>
</object>
</object>
- <string key="NSFrame">{{11, 14}, {215, 204}}</string>
+ <string key="NSFrame">{{11, 19}, {215, 328}}</string>
<reference key="NSSuperview" ref="781284031"/>
+ <reference key="NSWindow"/>
<string key="NSOffsets">{0, 0}</string>
<object class="NSTextFieldCell" key="NSTitleCell">
<int key="NSCellFlags">67239424</int>
@@ -5033,13 +5121,183 @@
<bytes key="NSRGB">MC43OTIxNTY5MyAwLjc5MjE1NjkzIDAuNzkyMTU2OTMAA</bytes>
</object>
</object>
+ <object class="NSButton" id="530719242">
+ <reference key="NSNextResponder" ref="781284031"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{318, 125}, {186, 18}}</string>
+ <reference key="NSSuperview" ref="781284031"/>
+ <reference key="NSWindow"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSButtonCell" key="NSCell" id="756602412">
+ <int key="NSCellFlags">67239424</int>
+ <int key="NSCellFlags2">131072</int>
+ <string key="NSContents">Use SSH Tunnel</string>
+ <reference key="NSSupport" ref="26"/>
+ <reference key="NSControlView" ref="530719242"/>
+ <int key="NSButtonFlags">1211912703</int>
+ <int key="NSButtonFlags2">130</int>
+ <reference key="NSNormalImage" ref="226227784"/>
+ <reference key="NSAlternateImage" ref="386686735"/>
+ <string key="NSAlternateContents"/>
+ <string key="NSKeyEquivalent"/>
+ <int key="NSPeriodicDelay">200</int>
+ <int key="NSPeriodicInterval">25</int>
+ </object>
+ </object>
+ <object class="NSTextField" id="49902530">
+ <reference key="NSNextResponder" ref="781284031"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{321, 100}, {184, 19}}</string>
+ <reference key="NSSuperview" ref="781284031"/>
+ <reference key="NSWindow"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="316194103">
+ <int key="NSCellFlags">-1267597759</int>
+ <int key="NSCellFlags2">272761856</int>
+ <string key="NSContents"/>
+ <reference key="NSSupport" ref="26"/>
+ <reference key="NSControlView" ref="49902530"/>
+ <bool key="NSDrawsBackground">YES</bool>
+ <reference key="NSBackgroundColor" ref="480189472"/>
+ <reference key="NSTextColor" ref="690893883"/>
+ </object>
+ </object>
+ <object class="NSSecureTextField" id="361394805">
+ <reference key="NSNextResponder" ref="781284031"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{320, 45}, {185, 19}}</string>
+ <reference key="NSSuperview" ref="781284031"/>
+ <reference key="NSWindow"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSSecureTextFieldCell" key="NSCell" id="416493242">
+ <int key="NSCellFlags">879885888</int>
+ <int key="NSCellFlags2">272761856</int>
+ <string key="NSContents"/>
+ <reference key="NSSupport" ref="26"/>
+ <reference key="NSControlView" ref="361394805"/>
+ <bool key="NSDrawsBackground">YES</bool>
+ <reference key="NSBackgroundColor" ref="480189472"/>
+ <reference key="NSTextColor" ref="690893883"/>
+ <object class="NSArray" key="NSAllowedInputLocales">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>NSAllRomanInputSourcesLocaleIdentifier</string>
+ </object>
+ </object>
+ </object>
+ <object class="NSTextField" id="708815656">
+ <reference key="NSNextResponder" ref="781284031"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{320, 73}, {184, 19}}</string>
+ <reference key="NSSuperview" ref="781284031"/>
+ <reference key="NSWindow"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="133587324">
+ <int key="NSCellFlags">-1267597759</int>
+ <int key="NSCellFlags2">272761856</int>
+ <string key="NSContents"/>
+ <reference key="NSSupport" ref="26"/>
+ <reference key="NSControlView" ref="708815656"/>
+ <bool key="NSDrawsBackground">YES</bool>
+ <reference key="NSBackgroundColor" ref="480189472"/>
+ <reference key="NSTextColor" ref="690893883"/>
+ </object>
+ </object>
+ <object class="NSTextField" id="830023647">
+ <reference key="NSNextResponder" ref="781284031"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{320, 19}, {184, 19}}</string>
+ <reference key="NSSuperview" ref="781284031"/>
+ <reference key="NSWindow"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="428754216">
+ <int key="NSCellFlags">-1267597759</int>
+ <int key="NSCellFlags2">272761856</int>
+ <string key="NSContents"/>
+ <reference key="NSSupport" ref="26"/>
+ <string key="NSPlaceholderString">22</string>
+ <reference key="NSControlView" ref="830023647"/>
+ <bool key="NSDrawsBackground">YES</bool>
+ <reference key="NSBackgroundColor" ref="480189472"/>
+ <reference key="NSTextColor" ref="690893883"/>
+ </object>
+ </object>
+ <object class="NSTextField" id="988378549">
+ <reference key="NSNextResponder" ref="781284031"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{228, 102}, {89, 14}}</string>
+ <reference key="NSSuperview" ref="781284031"/>
+ <reference key="NSWindow"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="364670466">
+ <int key="NSCellFlags">68288064</int>
+ <int key="NSCellFlags2">71435264</int>
+ <string key="NSContents">SSH Host:</string>
+ <reference key="NSSupport" ref="26"/>
+ <reference key="NSControlView" ref="988378549"/>
+ <reference key="NSBackgroundColor" ref="62854682"/>
+ <reference key="NSTextColor" ref="454249633"/>
+ </object>
+ </object>
+ <object class="NSTextField" id="833091345">
+ <reference key="NSNextResponder" ref="781284031"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{228, 75}, {89, 14}}</string>
+ <reference key="NSSuperview" ref="781284031"/>
+ <reference key="NSWindow"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="927737024">
+ <int key="NSCellFlags">68288064</int>
+ <int key="NSCellFlags2">71435264</int>
+ <string key="NSContents">SSH User:</string>
+ <reference key="NSSupport" ref="26"/>
+ <reference key="NSControlView" ref="833091345"/>
+ <reference key="NSBackgroundColor" ref="62854682"/>
+ <reference key="NSTextColor" ref="454249633"/>
+ </object>
+ </object>
+ <object class="NSTextField" id="372645803">
+ <reference key="NSNextResponder" ref="781284031"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{228, 48}, {89, 14}}</string>
+ <reference key="NSSuperview" ref="781284031"/>
+ <reference key="NSWindow"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="948667041">
+ <int key="NSCellFlags">68288064</int>
+ <int key="NSCellFlags2">71435264</int>
+ <string key="NSContents">SSH Password:</string>
+ <reference key="NSSupport" ref="26"/>
+ <reference key="NSControlView" ref="372645803"/>
+ <reference key="NSBackgroundColor" ref="62854682"/>
+ <reference key="NSTextColor" ref="454249633"/>
+ </object>
+ </object>
+ <object class="NSTextField" id="593551992">
+ <reference key="NSNextResponder" ref="781284031"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{228, 21}, {89, 14}}</string>
+ <reference key="NSSuperview" ref="781284031"/>
+ <reference key="NSWindow"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="270862532">
+ <int key="NSCellFlags">68288064</int>
+ <int key="NSCellFlags2">71435264</int>
+ <string key="NSContents">SSH Port:</string>
+ <reference key="NSSupport" ref="26"/>
+ <reference key="NSControlView" ref="593551992"/>
+ <reference key="NSBackgroundColor" ref="62854682"/>
+ <reference key="NSTextColor" ref="454249633"/>
+ </object>
+ </object>
</object>
- <string key="NSFrame">{{1, 1}, {523, 230}}</string>
+ <string key="NSFrame">{{1, 1}, {523, 359}}</string>
<reference key="NSSuperview" ref="248896606"/>
+ <reference key="NSWindow"/>
</object>
</object>
- <string key="NSFrame">{{-3, 44}, {525, 232}}</string>
+ <string key="NSFrame">{{-3, 44}, {525, 361}}</string>
<reference key="NSSuperview" ref="586457094"/>
+ <reference key="NSWindow"/>
<string key="NSOffsets">{0, 0}</string>
<object class="NSTextFieldCell" key="NSTitleCell">
<int key="NSCellFlags">67239424</int>
@@ -5063,6 +5321,7 @@
<int key="NSvFlags">289</int>
<string key="NSFrame">{{184, 12}, {143, 32}}</string>
<reference key="NSSuperview" ref="586457094"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSButtonCell" key="NSCell" id="729601679">
<int key="NSCellFlags">67239424</int>
@@ -5079,7 +5338,9 @@
</object>
</object>
</object>
- <string key="NSFrameSize">{519, 347}</string>
+ <string key="NSFrameSize">{519, 476}</string>
+ <reference key="NSSuperview"/>
+ <reference key="NSWindow"/>
</object>
<string key="NSScreenRect">{{0, 0}, {1440, 878}}</string>
<string key="NSMinSize">{256, 213}</string>
@@ -11955,10 +12216,6 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8</string>
<string key="NSClassName">NSView</string>
</object>
<object class="NSUserDefaultsController" id="461189245">
- <object class="NSMutableArray" key="NSDeclaredKeys">
- <bool key="EncodedWithXMLCoder">YES</bool>
- <string>CustomQueryEditorFont</string>
- </object>
<bool key="NSSharedInstance">YES</bool>
</object>
<object class="NSArrayController" id="302582105">
@@ -12096,14 +12353,6 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8</string>
<int key="connectionID">109</int>
</object>
<object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">connect:</string>
- <reference key="source" ref="427689665"/>
- <reference key="destination" ref="632312830"/>
- </object>
- <int key="connectionID">117</int>
- </object>
- <object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">dataSource</string>
<reference key="source" ref="581095761"/>
@@ -13829,14 +14078,6 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8</string>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
- <string key="label">nextKeyView</string>
- <reference key="source" ref="591248388"/>
- <reference key="destination" ref="632312830"/>
- </object>
- <int key="connectionID">4719</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBOutletConnection" key="connection">
<string key="label">databaseEncodingButton</string>
<reference key="source" ref="427689665"/>
<reference key="destination" ref="437431578"/>
@@ -15407,6 +15648,150 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8</string>
</object>
<int key="connectionID">5864</int>
</object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">delegate</string>
+ <reference key="source" ref="622703817"/>
+ <reference key="destination" ref="427689665"/>
+ </object>
+ <int key="connectionID">5865</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">nextKeyView</string>
+ <reference key="source" ref="591248388"/>
+ <reference key="destination" ref="530719242"/>
+ </object>
+ <int key="connectionID">5884</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleUseSSH:</string>
+ <reference key="source" ref="427689665"/>
+ <reference key="destination" ref="530719242"/>
+ </object>
+ <int key="connectionID">5885</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">nextKeyView</string>
+ <reference key="source" ref="530719242"/>
+ <reference key="destination" ref="49902530"/>
+ </object>
+ <int key="connectionID">5886</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">sshCheckbox</string>
+ <reference key="source" ref="427689665"/>
+ <reference key="destination" ref="530719242"/>
+ </object>
+ <int key="connectionID">5887</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">delegate</string>
+ <reference key="source" ref="49902530"/>
+ <reference key="destination" ref="427689665"/>
+ </object>
+ <int key="connectionID">5888</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">sshHostField</string>
+ <reference key="source" ref="427689665"/>
+ <reference key="destination" ref="49902530"/>
+ </object>
+ <int key="connectionID">5890</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">nextKeyView</string>
+ <reference key="source" ref="49902530"/>
+ <reference key="destination" ref="708815656"/>
+ </object>
+ <int key="connectionID">5891</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">delegate</string>
+ <reference key="source" ref="708815656"/>
+ <reference key="destination" ref="427689665"/>
+ </object>
+ <int key="connectionID">5892</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">nextKeyView</string>
+ <reference key="source" ref="708815656"/>
+ <reference key="destination" ref="361394805"/>
+ </object>
+ <int key="connectionID">5893</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">sshUserField</string>
+ <reference key="source" ref="427689665"/>
+ <reference key="destination" ref="708815656"/>
+ </object>
+ <int key="connectionID">5894</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">delegate</string>
+ <reference key="source" ref="361394805"/>
+ <reference key="destination" ref="427689665"/>
+ </object>
+ <int key="connectionID">5895</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">nextKeyView</string>
+ <reference key="source" ref="361394805"/>
+ <reference key="destination" ref="830023647"/>
+ </object>
+ <int key="connectionID">5896</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">sshPasswordField</string>
+ <reference key="source" ref="427689665"/>
+ <reference key="destination" ref="361394805"/>
+ </object>
+ <int key="connectionID">5897</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">delegate</string>
+ <reference key="source" ref="830023647"/>
+ <reference key="destination" ref="427689665"/>
+ </object>
+ <int key="connectionID">5898</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">sshPortField</string>
+ <reference key="source" ref="427689665"/>
+ <reference key="destination" ref="830023647"/>
+ </object>
+ <int key="connectionID">5899</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">nextKeyView</string>
+ <reference key="source" ref="830023647"/>
+ <reference key="destination" ref="632312830"/>
+ </object>
+ <int key="connectionID">5900</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">initiateConnection:</string>
+ <reference key="source" ref="427689665"/>
+ <reference key="destination" ref="632312830"/>
+ </object>
+ <int key="connectionID">5901</int>
+ </object>
</object>
<object class="IBMutableOrderedSet" key="objectRecords">
<object class="NSArray" key="orderedObjects">
@@ -15478,8 +15863,8 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8</string>
<reference ref="718844584"/>
<reference ref="565663839"/>
<reference ref="725657385"/>
- <reference ref="248896606"/>
<reference ref="55442463"/>
+ <reference ref="248896606"/>
</object>
<reference key="parent" ref="456332212"/>
</object>
@@ -15563,6 +15948,15 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8</string>
<reference ref="480030144"/>
<reference ref="230979761"/>
<reference ref="56931060"/>
+ <reference ref="530719242"/>
+ <reference ref="49902530"/>
+ <reference ref="361394805"/>
+ <reference ref="708815656"/>
+ <reference ref="830023647"/>
+ <reference ref="988378549"/>
+ <reference ref="833091345"/>
+ <reference ref="372645803"/>
+ <reference ref="593551992"/>
</object>
<reference key="parent" ref="586457094"/>
</object>
@@ -18909,9 +19303,9 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8</string>
<reference key="object" ref="56931060"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="112292194"/>
<reference ref="79322639"/>
<reference ref="661955989"/>
- <reference ref="112292194"/>
</object>
<reference key="parent" ref="248896606"/>
</object>
@@ -18966,34 +19360,6 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8</string>
<reference key="parent" ref="611607474"/>
</object>
<object class="IBObjectRecord">
- <int key="objectID">4511</int>
- <reference key="object" ref="79322639"/>
- <object class="NSMutableArray" key="children">
- <bool key="EncodedWithXMLCoder">YES</bool>
- <reference ref="745497435"/>
- </object>
- <reference key="parent" ref="56931060"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">4512</int>
- <reference key="object" ref="745497435"/>
- <reference key="parent" ref="79322639"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">5416</int>
- <reference key="object" ref="661955989"/>
- <object class="NSMutableArray" key="children">
- <bool key="EncodedWithXMLCoder">YES</bool>
- <reference ref="769359275"/>
- </object>
- <reference key="parent" ref="56931060"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">5417</int>
- <reference key="object" ref="769359275"/>
- <reference key="parent" ref="661955989"/>
- </object>
- <object class="IBObjectRecord">
<int key="objectID">676</int>
<reference key="object" ref="604818293"/>
<object class="NSMutableArray" key="children">
@@ -21849,6 +22215,160 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8</string>
<reference key="object" ref="625199500"/>
<reference key="parent" ref="855588490"/>
</object>
+ <object class="IBObjectRecord">
+ <int key="objectID">5866</int>
+ <reference key="object" ref="530719242"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="756602412"/>
+ </object>
+ <reference key="parent" ref="248896606"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">5867</int>
+ <reference key="object" ref="756602412"/>
+ <reference key="parent" ref="530719242"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">5868</int>
+ <reference key="object" ref="49902530"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="316194103"/>
+ </object>
+ <reference key="parent" ref="248896606"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">5869</int>
+ <reference key="object" ref="316194103"/>
+ <reference key="parent" ref="49902530"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">5870</int>
+ <reference key="object" ref="361394805"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="416493242"/>
+ </object>
+ <reference key="parent" ref="248896606"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">5871</int>
+ <reference key="object" ref="416493242"/>
+ <reference key="parent" ref="361394805"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">5872</int>
+ <reference key="object" ref="708815656"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="133587324"/>
+ </object>
+ <reference key="parent" ref="248896606"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">5873</int>
+ <reference key="object" ref="133587324"/>
+ <reference key="parent" ref="708815656"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">5874</int>
+ <reference key="object" ref="830023647"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="428754216"/>
+ </object>
+ <reference key="parent" ref="248896606"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">5875</int>
+ <reference key="object" ref="428754216"/>
+ <reference key="parent" ref="830023647"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">5876</int>
+ <reference key="object" ref="988378549"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="364670466"/>
+ </object>
+ <reference key="parent" ref="248896606"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">5877</int>
+ <reference key="object" ref="364670466"/>
+ <reference key="parent" ref="988378549"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">5878</int>
+ <reference key="object" ref="833091345"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="927737024"/>
+ </object>
+ <reference key="parent" ref="248896606"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">5879</int>
+ <reference key="object" ref="927737024"/>
+ <reference key="parent" ref="833091345"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">5880</int>
+ <reference key="object" ref="372645803"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="948667041"/>
+ </object>
+ <reference key="parent" ref="248896606"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">5881</int>
+ <reference key="object" ref="948667041"/>
+ <reference key="parent" ref="372645803"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">5882</int>
+ <reference key="object" ref="593551992"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="270862532"/>
+ </object>
+ <reference key="parent" ref="248896606"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">5883</int>
+ <reference key="object" ref="270862532"/>
+ <reference key="parent" ref="593551992"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">5416</int>
+ <reference key="object" ref="661955989"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="769359275"/>
+ </object>
+ <reference key="parent" ref="56931060"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">5417</int>
+ <reference key="object" ref="769359275"/>
+ <reference key="parent" ref="661955989"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">4511</int>
+ <reference key="object" ref="79322639"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="745497435"/>
+ </object>
+ <reference key="parent" ref="56931060"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">4512</int>
+ <reference key="object" ref="745497435"/>
+ <reference key="parent" ref="79322639"/>
+ </object>
</object>
</object>
<object class="NSMutableDictionary" key="flattenedProperties">
@@ -23177,6 +23697,24 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8</string>
<string>5827.IBPluginDependency</string>
<string>5828.IBPluginDependency</string>
<string>5829.IBPluginDependency</string>
+ <string>5866.IBPluginDependency</string>
+ <string>5867.IBPluginDependency</string>
+ <string>5868.IBPluginDependency</string>
+ <string>5869.IBPluginDependency</string>
+ <string>5870.IBPluginDependency</string>
+ <string>5871.IBPluginDependency</string>
+ <string>5872.IBPluginDependency</string>
+ <string>5873.IBPluginDependency</string>
+ <string>5874.IBPluginDependency</string>
+ <string>5875.IBPluginDependency</string>
+ <string>5876.IBPluginDependency</string>
+ <string>5877.IBPluginDependency</string>
+ <string>5878.IBPluginDependency</string>
+ <string>5879.IBPluginDependency</string>
+ <string>5880.IBPluginDependency</string>
+ <string>5881.IBPluginDependency</string>
+ <string>5882.IBPluginDependency</string>
+ <string>5883.IBPluginDependency</string>
<string>6.IBPluginDependency</string>
<string>6.ImportedFromIB2</string>
<string>604.IBEditorWindowLastContentRect</string>
@@ -24015,8 +24553,8 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string>{{116, 307}, {519, 347}}</string>
- <string>{{116, 307}, {519, 347}}</string>
+ <string>{{418, 335}, {519, 476}}</string>
+ <string>{{418, 335}, {519, 476}}</string>
<reference ref="9"/>
<string>{{136, 447}, {519, 335}}</string>
<reference ref="8"/>
@@ -25050,6 +25588,24 @@ aGUgYWN0aXZlIHNlbGVjdGlvbiAo4oyl4oyYUik</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>
+ <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>
+ <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>
<reference ref="9"/>
<string>{{62, 225}, {667, 425}}</string>
<string>{{62, 225}, {667, 425}}</string>
@@ -25303,7 +25859,7 @@ Y2hhbmdlIHRoZSBvcmRlcg</string>
</object>
</object>
<nil key="sourceID"/>
- <int key="maxID">5864</int>
+ <int key="maxID">5901</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
@@ -26016,7 +26572,6 @@ Y2hhbmdlIHRoZSBvcmRlcg</string>
<string>chooseFavorite:</string>
<string>closeDatabaseSheet:</string>
<string>closeSheet:</string>
- <string>connect:</string>
<string>connectSheetAddToFavorites:</string>
<string>connectSheetShowHelp:</string>
<string>connectToDB:</string>
@@ -26028,6 +26583,7 @@ Y2hhbmdlIHRoZSBvcmRlcg</string>
<string>flushPrivileges:</string>
<string>flushTable:</string>
<string>import:</string>
+ <string>initiateConnection:</string>
<string>optimizeTable:</string>
<string>removeDatabase:</string>
<string>repairTable:</string>
@@ -26036,6 +26592,7 @@ Y2hhbmdlIHRoZSBvcmRlcg</string>
<string>showCreateTableSyntax:</string>
<string>showMySQLHelp:</string>
<string>showVariables:</string>
+ <string>toggleUseSSH:</string>
<string>viewContent:</string>
<string>viewQuery:</string>
<string>viewRelations:</string>
@@ -26080,6 +26637,7 @@ Y2hhbmdlIHRoZSBvcmRlcg</string>
<string>id</string>
<string>id</string>
<string>id</string>
+ <string>id</string>
</object>
</object>
<object class="NSMutableDictionary" key="outlets">
@@ -26113,6 +26671,11 @@ Y2hhbmdlIHRoZSBvcmRlcg</string>
<string>sidebarGrabber</string>
<string>socketField</string>
<string>spExportControllerInstance</string>
+ <string>sshCheckbox</string>
+ <string>sshHostField</string>
+ <string>sshPasswordField</string>
+ <string>sshPortField</string>
+ <string>sshUserField</string>
<string>syntaxView</string>
<string>syntaxViewContent</string>
<string>tableContentInstance</string>
@@ -26132,7 +26695,7 @@ Y2hhbmdlIHRoZSBvcmRlcg</string>
<string>id</string>
<string>id</string>
<string>NSTableView</string>
- <string>id</string>
+ <string>NSProgressIndicator</string>
<string>NSTextField</string>
<string>id</string>
<string>NSWindow</string>
@@ -26163,6 +26726,11 @@ Y2hhbmdlIHRoZSBvcmRlcg</string>
<string>id</string>
<string>id</string>
<string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
<string>NSTabView</string>
<string>id</string>
<string>id</string>
@@ -26179,29 +26747,6 @@ Y2hhbmdlIHRoZSBvcmRlcg</string>
<object class="IBPartialClassDescription">
<string key="className">TableDocument</string>
<string key="superclassName">NSDocument</string>
- <object class="NSMutableDictionary" key="actions">
- <string key="NS.key.0">toggleUseSSH:</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>sshCheckbox</string>
- <string>sshHostField</string>
- <string>sshPasswordField</string>
- <string>sshPortField</string>
- <string>sshUserField</string>
- </object>
- <object class="NSMutableArray" key="dict.values">
- <bool key="EncodedWithXMLCoder">YES</bool>
- <string>id</string>
- <string>id</string>
- <string>id</string>
- <string>id</string>
- <string>id</string>
- </object>
- </object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBUserSource</string>
<string key="minorKey"/>
diff --git a/Interfaces/English.lproj/Preferences.xib b/Interfaces/English.lproj/Preferences.xib
index 42278fa3..a7674115 100644
--- a/Interfaces/English.lproj/Preferences.xib
+++ b/Interfaces/English.lproj/Preferences.xib
@@ -2,13 +2,13 @@
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.03">
<data>
<int key="IBDocument.SystemTarget">1050</int>
- <string key="IBDocument.SystemVersion">9J61</string>
+ <string key="IBDocument.SystemVersion">9G55</string>
<string key="IBDocument.InterfaceBuilderVersion">677</string>
- <string key="IBDocument.AppKitVersion">949.46</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="802"/>
+ <integer value="403"/>
</object>
<object class="NSArray" key="IBDocument.PluginDependencies">
<bool key="EncodedWithXMLCoder">YES</bool>
@@ -1105,7 +1105,7 @@
<string key="NSClassName">NSView</string>
</object>
<object class="NSCustomView" id="593732956">
- <nil key="NSNextResponder"/>
+ <reference key="NSNextResponder"/>
<int key="NSvFlags">268</int>
<object class="NSMutableArray" key="NSSubviews">
<bool key="EncodedWithXMLCoder">YES</bool>
@@ -1430,6 +1430,7 @@
<int key="NSCellFlags2">4326400</int>
<string key="NSContents"/>
<reference key="NSSupport" ref="26"/>
+ <string key="NSPlaceholderString">3306</string>
<reference key="NSControlView" ref="707085995"/>
<bool key="NSDrawsBackground">YES</bool>
<reference key="NSBackgroundColor" ref="1001357688"/>
@@ -1494,7 +1495,7 @@
<reference key="NSSuperview" ref="343315962"/>
<bool key="NSEnabled">YES</bool>
<object class="NSButtonCell" key="NSCell" id="1026644963">
- <int key="NSCellFlags">604110336</int>
+ <int key="NSCellFlags">67239424</int>
<int key="NSCellFlags2">131072</int>
<string key="NSContents">Use SSH tunnel</string>
<reference key="NSSupport" ref="26"/>
@@ -1570,6 +1571,7 @@
<int key="NSCellFlags2">4326400</int>
<string key="NSContents"/>
<reference key="NSSupport" ref="26"/>
+ <string key="NSPlaceholderString">22</string>
<reference key="NSControlView" ref="781513571"/>
<bool key="NSDrawsBackground">YES</bool>
<reference key="NSBackgroundColor" ref="1001357688"/>
@@ -1745,7 +1747,7 @@
<object class="NSTextFieldCell" key="NSCell" id="646676137">
<int key="NSCellFlags">68288064</int>
<int key="NSCellFlags2">71304192</int>
- <string key="NSContents">Local Port:</string>
+ <string key="NSContents">SSH Port:</string>
<reference key="NSSupport" ref="26"/>
<reference key="NSControlView" ref="266286554"/>
<reference key="NSBackgroundColor" ref="700609571"/>
@@ -1765,6 +1767,7 @@
</object>
</object>
<string key="NSFrameSize">{500, 381}</string>
+ <reference key="NSSuperview"/>
<string key="NSClassName">NSView</string>
</object>
<object class="NSCustomView" id="1041614321">
@@ -2153,6 +2156,7 @@ AQAAAAA</bytes>
</object>
<string key="NSFrame">{{154, 173}, {44, 23}}</string>
<reference key="NSSuperview" ref="1033452264"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<bool key="NSIsBordered">YES</bool>
<object class="NSColor" key="NSColor">
@@ -2172,6 +2176,7 @@ AQAAAAA</bytes>
</object>
<string key="NSFrame">{{154, 202}, {44, 23}}</string>
<reference key="NSSuperview" ref="1033452264"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<bool key="NSIsBordered">YES</bool>
<object class="NSColor" key="NSColor">
@@ -2191,6 +2196,7 @@ AQAAAAA</bytes>
</object>
<string key="NSFrame">{{154, 115}, {44, 23}}</string>
<reference key="NSSuperview" ref="1033452264"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<bool key="NSIsBordered">YES</bool>
<object class="NSColor" key="NSColor">
@@ -2210,6 +2216,7 @@ AQAAAAA</bytes>
</object>
<string key="NSFrame">{{154, 86}, {44, 23}}</string>
<reference key="NSSuperview" ref="1033452264"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<bool key="NSIsBordered">YES</bool>
<object class="NSColor" key="NSColor">
@@ -2229,6 +2236,7 @@ AQAAAAA</bytes>
</object>
<string key="NSFrame">{{154, 260}, {44, 23}}</string>
<reference key="NSSuperview" ref="1033452264"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<bool key="NSIsBordered">YES</bool>
<object class="NSColor" key="NSColor">
@@ -2248,6 +2256,7 @@ AQAAAAA</bytes>
</object>
<string key="NSFrame">{{154, 144}, {44, 23}}</string>
<reference key="NSSuperview" ref="1033452264"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<bool key="NSIsBordered">YES</bool>
<object class="NSColor" key="NSColor">
@@ -2267,6 +2276,7 @@ AQAAAAA</bytes>
</object>
<string key="NSFrame">{{154, 57}, {44, 23}}</string>
<reference key="NSSuperview" ref="1033452264"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<bool key="NSIsBordered">YES</bool>
<object class="NSColor" key="NSColor">
@@ -2286,6 +2296,7 @@ AQAAAAA</bytes>
</object>
<string key="NSFrame">{{154, 289}, {44, 23}}</string>
<reference key="NSSuperview" ref="1033452264"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<bool key="NSIsBordered">YES</bool>
<object class="NSColor" key="NSColor">
@@ -2305,6 +2316,7 @@ AQAAAAA</bytes>
</object>
<string key="NSFrame">{{154, 231}, {44, 23}}</string>
<reference key="NSSuperview" ref="1033452264"/>
+ <reference key="NSWindow"/>
<bool key="NSIsBordered">YES</bool>
<object class="NSColor" key="NSColor">
<int key="NSColorSpace">1</int>
@@ -2316,6 +2328,7 @@ AQAAAAA</bytes>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{86, 176}, {63, 17}}</string>
<reference key="NSSuperview" ref="1033452264"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="465390474">
<int key="NSCellFlags">68288064</int>
@@ -2332,6 +2345,7 @@ AQAAAAA</bytes>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{88, 147}, {61, 17}}</string>
<reference key="NSSuperview" ref="1033452264"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="629129203">
<int key="NSCellFlags">68288064</int>
@@ -2348,6 +2362,7 @@ AQAAAAA</bytes>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{79, 205}, {70, 17}}</string>
<reference key="NSSuperview" ref="1033452264"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="841770398">
<int key="NSCellFlags">68288064</int>
@@ -2364,6 +2379,7 @@ AQAAAAA</bytes>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{89, 60}, {60, 17}}</string>
<reference key="NSSuperview" ref="1033452264"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="837982163">
<int key="NSCellFlags">68288064</int>
@@ -2380,6 +2396,7 @@ AQAAAAA</bytes>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{102, 118}, {47, 17}}</string>
<reference key="NSSuperview" ref="1033452264"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="728610506">
<int key="NSCellFlags">68288064</int>
@@ -2396,6 +2413,7 @@ AQAAAAA</bytes>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{112, 292}, {37, 17}}</string>
<reference key="NSSuperview" ref="1033452264"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="693342907">
<int key="NSCellFlags">68288064</int>
@@ -2412,6 +2430,7 @@ AQAAAAA</bytes>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{46, 89}, {103, 17}}</string>
<reference key="NSSuperview" ref="1033452264"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="336899348">
<int key="NSCellFlags">68288064</int>
@@ -2428,6 +2447,7 @@ AQAAAAA</bytes>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{66, 263}, {83, 17}}</string>
<reference key="NSSuperview" ref="1033452264"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="896659768">
<int key="NSCellFlags">68288064</int>
@@ -2444,6 +2464,7 @@ AQAAAAA</bytes>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{113, 331}, {36, 17}}</string>
<reference key="NSSuperview" ref="1033452264"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="10706281">
<int key="NSCellFlags">68288064</int>
@@ -2460,6 +2481,7 @@ AQAAAAA</bytes>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{73, 12}, {131, 32}}</string>
<reference key="NSSuperview" ref="1033452264"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSButtonCell" key="NSCell" id="603963569">
<int key="NSCellFlags">-2080244224</int>
@@ -2480,6 +2502,7 @@ AQAAAAA</bytes>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{396, 321}, {90, 32}}</string>
<reference key="NSSuperview" ref="1033452264"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSButtonCell" key="NSCell" id="345790946">
<int key="NSCellFlags">67239424</int>
@@ -2500,6 +2523,7 @@ AQAAAAA</bytes>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{154, 329}, {240, 19}}</string>
<reference key="NSSuperview" ref="1033452264"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="66452133">
<int key="NSCellFlags">-2076049856</int>
@@ -2517,6 +2541,7 @@ AQAAAAA</bytes>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{107, 234}, {42, 17}}</string>
<reference key="NSSuperview" ref="1033452264"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="632150824">
<int key="NSCellFlags">605158976</int>
@@ -2533,6 +2558,7 @@ AQAAAAA</bytes>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{257, 291}, {126, 18}}</string>
<reference key="NSSuperview" ref="1033452264"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSButtonCell" key="NSCell" id="823431848">
<int key="NSCellFlags">-2080244224</int>
@@ -2555,6 +2581,7 @@ AQAAAAA</bytes>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{257, 262}, {150, 18}}</string>
<reference key="NSSuperview" ref="1033452264"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSButtonCell" key="NSCell" id="743257684">
<int key="NSCellFlags">-2080244224</int>
@@ -2577,6 +2604,7 @@ AQAAAAA</bytes>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{257, 204}, {179, 18}}</string>
<reference key="NSSuperview" ref="1033452264"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSButtonCell" key="NSCell" id="231642073">
<int key="NSCellFlags">67239424</int>
@@ -2599,6 +2627,7 @@ AQAAAAA</bytes>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{257, 233}, {185, 18}}</string>
<reference key="NSSuperview" ref="1033452264"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSButtonCell" key="NSCell" id="491730308">
<int key="NSCellFlags">-2080244224</int>
@@ -2621,6 +2650,7 @@ AQAAAAA</bytes>
<int key="NSvFlags">-2147483380</int>
<string key="NSFrame">{{257, 146}, {197, 18}}</string>
<reference key="NSSuperview" ref="1033452264"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSButtonCell" key="NSCell" id="420702000">
<int key="NSCellFlags">-1543373312</int>
@@ -2643,6 +2673,7 @@ AQAAAAA</bytes>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{357, 179}, {29, 19}}</string>
<reference key="NSSuperview" ref="1033452264"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="992523117">
<int key="NSCellFlags">-1538130368</int>
@@ -2718,6 +2749,7 @@ AQAAAAA</bytes>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{298, 181}, {54, 14}}</string>
<reference key="NSSuperview" ref="1033452264"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="499174965">
<int key="NSCellFlags">605158976</int>
@@ -2734,6 +2766,7 @@ AQAAAAA</bytes>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{392, 177}, {15, 22}}</string>
<reference key="NSSuperview" ref="1033452264"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSStepperCell" key="NSCell" id="266600395">
<int key="NSCellFlags">604896800</int>
@@ -2752,6 +2785,7 @@ AQAAAAA</bytes>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{410, 181}, {27, 14}}</string>
<reference key="NSSuperview" ref="1033452264"/>
+ <reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="516695546">
<int key="NSCellFlags">605158976</int>
@@ -2768,6 +2802,7 @@ AQAAAAA</bytes>
<int key="NSvFlags">12</int>
<string key="NSFrame">{{154, 318}, {326, 5}}</string>
<reference key="NSSuperview" ref="1033452264"/>
+ <reference key="NSWindow"/>
<string key="NSOffsets">{0, 0}</string>
<object class="NSTextFieldCell" key="NSTitleCell">
<int key="NSCellFlags">67239424</int>
@@ -2788,6 +2823,7 @@ AQAAAAA</bytes>
</object>
<string key="NSFrameSize">{500, 369}</string>
<reference key="NSSuperview"/>
+ <reference key="NSWindow"/>
<string key="NSClassName">NSView</string>
</object>
<object class="NSUserDefaultsController" id="151174232">
@@ -4288,6 +4324,86 @@ AQAAAAA</bytes>
</object>
<int key="connectionID">1114</int>
</object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">sshPasswordField</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="663701836"/>
+ </object>
+ <int key="connectionID">1115</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">sshHostField</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="905879958"/>
+ </object>
+ <int key="connectionID">1116</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">sshUserField</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="1041581151"/>
+ </object>
+ <int key="connectionID">1117</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">delegate</string>
+ <reference key="source" ref="905879958"/>
+ <reference key="destination" ref="1001"/>
+ </object>
+ <int key="connectionID">1118</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">delegate</string>
+ <reference key="source" ref="1041581151"/>
+ <reference key="destination" ref="1001"/>
+ </object>
+ <int key="connectionID">1119</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">delegate</string>
+ <reference key="source" ref="663701836"/>
+ <reference key="destination" ref="1001"/>
+ </object>
+ <int key="connectionID">1120</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">delegate</string>
+ <reference key="source" ref="781513571"/>
+ <reference key="destination" ref="1001"/>
+ </object>
+ <int key="connectionID">1121</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">sshPortField</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="781513571"/>
+ </object>
+ <int key="connectionID">1122</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBBindingConnection" key="connection">
+ <string key="label">value: selection.sshPort</string>
+ <reference key="source" ref="781513571"/>
+ <reference key="destination" ref="937123943"/>
+ <object class="NSNibBindingConnector" key="connector">
+ <reference key="NSSource" ref="781513571"/>
+ <reference key="NSDestination" ref="937123943"/>
+ <string key="NSLabel">value: selection.sshPort</string>
+ <string key="NSBinding">value</string>
+ <string key="NSKeyPath">selection.sshPort</string>
+ <int key="NSNibBindingConnectorVersion">2</int>
+ </object>
+ </object>
+ <int key="connectionID">1124</int>
+ </object>
</object>
<object class="IBMutableOrderedSet" key="objectRecords">
<object class="NSArray" key="orderedObjects">
@@ -6529,7 +6645,7 @@ AQAAAAA</bytes>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<reference ref="9"/>
- <string>{{179, 460}, {500, 381}}</string>
+ <string>{{402, 220}, {500, 381}}</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
@@ -6780,7 +6896,7 @@ AQAAAAA</bytes>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string>{{428, 626}, {500, 369}}</string>
+ <string>{{428, 487}, {500, 369}}</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<object class="NSMutableArray">
<bool key="EncodedWithXMLCoder">YES</bool>
@@ -6847,7 +6963,7 @@ AQAAAAA</bytes>
</object>
</object>
<nil key="sourceID"/>
- <int key="maxID">1114</int>
+ <int key="maxID">1124</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
@@ -6934,6 +7050,10 @@ AQAAAAA</bytes>
<string>notificationsView</string>
<string>passwordField</string>
<string>preferencesWindow</string>
+ <string>sshHostField</string>
+ <string>sshPasswordField</string>
+ <string>sshPortField</string>
+ <string>sshUserField</string>
<string>tablesView</string>
<string>userField</string>
</object>
@@ -6954,6 +7074,10 @@ AQAAAAA</bytes>
<string>NSView</string>
<string>NSSecureTextField</string>
<string>NSWindow</string>
+ <string>NSTextField</string>
+ <string>NSSecureTextField</string>
+ <string>NSTextField</string>
+ <string>NSTextField</string>
<string>NSView</string>
<string>NSTextField</string>
</object>
diff --git a/Source/CMMCPConnection.h b/Source/CMMCPConnection.h
index 8867ed82..566d628a 100644
--- a/Source/CMMCPConnection.h
+++ b/Source/CMMCPConnection.h
@@ -26,6 +26,8 @@
#import <Cocoa/Cocoa.h>
#import <MCPKit_bundled/MCPKit_bundled.h>
#import "CMMCPResult.h"
+#import "KeyChain.h"
+#import "SPSSHTunnel.h"
@interface NSObject (CMMCPConnectionDelegate)
@@ -41,13 +43,17 @@
id delegate;
BOOL nibLoaded;
+ SPSSHTunnel *connectionTunnel;
NSString *connectionLogin;
+ NSString *connectionKeychainName;
+ NSString *connectionKeychainAccount;
NSString *connectionPassword;
NSString *connectionHost;
int connectionPort;
NSString *connectionSocket;
float lastQueryExecutionTime;
int connectionTimeout;
+ int currentSSHTunnelState;
BOOL useKeepAlive;
float keepAliveInterval;
@@ -58,15 +64,18 @@
}
- (id) init;
-- (id) initToHost:(NSString *) host withLogin:(NSString *) login password:(NSString *) pass usingPort:(int) port;
-- (id) initToSocket:(NSString *) socket withLogin:(NSString *) login password:(NSString *) pass;
+- (id) initToHost:(NSString *) host withLogin:(NSString *) login usingPort:(int) port;
+- (id) initToSocket:(NSString *) socket withLogin:(NSString *) login;
- (void) initSPExtensions;
-- (BOOL) connectWithLogin:(NSString *) login password:(NSString *) pass host:(NSString *) host port:(int) port socket:(NSString *) socket;
+- (BOOL) setPassword:(NSString *)thePassword;
+- (BOOL) setPasswordKeychainName:(NSString *)theName account:(NSString *)theAccount;
+- (BOOL) setSSHTunnel:(SPSSHTunnel *)theTunnel;
+- (BOOL) connect;
- (void) disconnect;
- (BOOL) reconnect;
+- (void) setParentWindow:(NSWindow *)theWindow;
- (IBAction) closeSheet:(id)sender;
+ (NSStringEncoding) encodingForMySQLEncoding:(const char *) mysqlEncoding;
-- (void) setParentWindow:(NSWindow *)theWindow;
- (BOOL) selectDB:(NSString *) dbName;
- (CMMCPResult *) queryString:(NSString *) query;
- (CMMCPResult *) queryString:(NSString *) query usingEncoding:(NSStringEncoding) encoding;
diff --git a/Source/CMMCPConnection.m b/Source/CMMCPConnection.m
index ab9bf6e6..b7fc230f 100644
--- a/Source/CMMCPConnection.m
+++ b/Source/CMMCPConnection.m
@@ -52,7 +52,10 @@ static void forcePingTimeout(int signalNumber);
@implementation CMMCPConnection
/*
- * Override the normal init methods, extending them to also init additional details.
+ * Override the normal init methods, extending them to also init additional details,
+ * and to store details of the initialised connection to allow reconnection as method.
+ * Note this also behaves differently from the standard MCPKit connection methods -
+ * passwords are passed separately, and connections are not automatically made on init.
*/
- (id) init
{
@@ -61,16 +64,47 @@ static void forcePingTimeout(int signalNumber);
serverVersionString = nil;
return self;
}
-- (id) initToHost:(NSString *) host withLogin:(NSString *) login password:(NSString *) pass usingPort:(int) port
+- (id) initToHost:(NSString *) host withLogin:(NSString *) login usingPort:(int) port
{
[self initSPExtensions];
- self = [super initToHost:host withLogin:login password:pass usingPort:port];
+
+ self = [super init];
+ mEncoding = NSISOLatin1StringEncoding;
+ mConnection = mysql_init(mConnection);
+ mConnected = NO;
+ if (mConnection == NULL) {
+ [self autorelease];
+ return nil;
+ }
+
+ mConnectionFlags = kMCPConnectionDefaultOption;
+
+ connectionHost = [[NSString alloc] initWithString:host];
+ connectionLogin = [[NSString alloc] initWithString:login];
+ connectionPort = port;
+ connectionSocket = nil;
+
return self;
}
-- (id) initToSocket:(NSString *) socket withLogin:(NSString *) login password:(NSString *) pass
+- (id) initToSocket:(NSString *) socket withLogin:(NSString *) login
{
[self initSPExtensions];
- self = [super initToSocket:socket withLogin:login password:pass];
+ self = [super init];
+ mEncoding = NSISOLatin1StringEncoding;
+ mConnection = mysql_init(mConnection);
+ mConnected = NO;
+ if (mConnection == NULL) {
+ [self autorelease];
+ return nil;
+ }
+
+ mConnectionFlags = kMCPConnectionDefaultOption;
+
+ connectionHost = nil;
+ connectionLogin = [[NSString alloc] initWithString:login];
+ connectionSocket = [[NSString alloc] initWithString:socket];
+ connectionPort = 0;
+
return self;
}
@@ -81,12 +115,11 @@ static void forcePingTimeout(int signalNumber);
- (void) initSPExtensions
{
parentWindow = nil;
- connectionLogin = nil;
connectionPassword = nil;
- connectionHost = nil;
- connectionPort = 0;
- connectionSocket = nil;
+ connectionKeychainName = nil;
+ connectionKeychainAccount = nil;
keepAliveTimer = nil;
+ connectionTunnel = nil;
connectionTimeout = [[[NSUserDefaults standardUserDefaults] objectForKey:@"ConnectionTimeout"] intValue];
if (!connectionTimeout) connectionTimeout = 10;
useKeepAlive = [[[NSUserDefaults standardUserDefaults] objectForKey:@"UseKeepAlive"] doubleValue];
@@ -99,49 +132,131 @@ static void forcePingTimeout(int signalNumber);
}
}
+/*
+ * Sets the password to be stored locally.
+ * Providing a keychain name is much more secure.
+ */
+- (BOOL) setPassword:(NSString *)thePassword
+{
+ if (connectionPassword) [connectionPassword release], connectionPassword = nil;
+ if (connectionKeychainName) [connectionKeychainName release], connectionKeychainName = nil;
+ if (connectionKeychainAccount) [connectionKeychainAccount release], connectionKeychainAccount = nil;
+
+ connectionPassword = [[NSString alloc] initWithString:thePassword];
+
+ return YES;
+}
/*
- * Override the normal connection method, extending it to also store details of the
- * current connection to allow reconnection as necessary. This also sets the connection timeout
- * - used for pings, not for long-running commands.
+ * 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) connectWithLogin:(NSString *) login password:(NSString *) pass host:(NSString *) host port:(int) port socket:(NSString *) socket
-{
- if (connectionLogin) [connectionLogin release];
- if (login) connectionLogin = [[NSString alloc] initWithString:login];
- if (connectionPassword) [connectionPassword release];
- if (pass) connectionPassword = [[NSString alloc] initWithString:pass];
- if (connectionHost) [connectionHost release];
- if (host) connectionHost = [[NSString alloc] initWithString:host];
- connectionPort = port;
- if (connectionSocket) [connectionSocket release];
- if (socket) connectionSocket = [[NSString alloc] initWithString:socket];
+- (BOOL) setPasswordKeychainName:(NSString *)theName account:(NSString *)theAccount
+{
+ if (connectionPassword) [connectionPassword release], connectionPassword = nil;
+ if (connectionKeychainName) [connectionKeychainName release], connectionKeychainName = nil;
+ if (connectionKeychainAccount) [connectionKeychainAccount release], connectionKeychainAccount = nil;
+
+ connectionKeychainName = [[NSString alloc] initWithString:theName];
+ connectionKeychainAccount = [[NSString alloc] initWithString:theAccount];
+
+ return YES;
+}
+
+
+/*
+ * Set a SSH tunnel object to connect through. This object will be retained locally,
+ * and will be automatically connected/connection checked/reconnected/disconnected
+ * together with the main connection.
+ */
+- (BOOL) setSSHTunnel:(SPSSHTunnel *)theTunnel
+{
+ connectionTunnel = theTunnel;
+ [connectionTunnel retain];
+
+ currentSSHTunnelState = [connectionTunnel state];
+ [connectionTunnel setConnectionStateChangeSelector:@selector(sshTunnelStateChange:) delegate:self];
+
+ return YES;
+}
+
+/*
+ * Add a new connection method, intended for use with the init methods above.
+ * Uses the stored details to instantiate a connection to the specified server,
+ * including custom timeouts - used for pings, not for long-running commands.
+ */
+- (BOOL) connect
+{
+ const char *theLogin = [self cStringFromString:connectionLogin];
+ const char *theHost;
+ const char *thePass;
+ const char *theSocket;
+ void *theRet;
+
+ // Ensure that a password method has been provided
+ if (connectionKeychainName == nil && connectionPassword == nil) return NO;
+
+ // Start the keepalive timer
+ [self startKeepAliveTimerResettingState:YES];
+
+ // Disconnect if a connection is already active
+ if (mConnected) {
+ [self disconnect];
+ mConnection = mysql_init(NULL);
+ if (mConnection == NULL) return NO;
+ }
+ // Ensure the custom timeout option is set
if (mConnection != NULL) {
mysql_options(mConnection, MYSQL_OPT_CONNECT_TIMEOUT, (const void *)&connectionTimeout);
}
+
+ // Set the host as appropriate
+ if (!connectionHost || ![connectionHost length]) {
+ theHost = NULL;
+ } else {
+ theHost = [self cStringFromString:connectionHost];
+ }
+
+ // Use the default socket if none is set, or set appropriately
+ if (connectionSocket == nil || ![connectionSocket length]) {
+ theSocket = kMCPConnectionDefaultSocket;
+ } else {
+ theSocket = [self cStringFromString:connectionSocket];
+ }
+
+ // Select the password from the provided method
+ if (connectionKeychainName) {
+ KeyChain *keychain;
+ keychain = [[KeyChain alloc] init];
+ thePass = [self cStringFromString:[keychain getPasswordForName:connectionKeychainName account:connectionKeychainAccount]];
+ [keychain release];
+ } else {
+ thePass = [self cStringFromString:connectionPassword];
+ }
- [self startKeepAliveTimerResettingState:YES];
- return [super connectWithLogin:login password:pass host:host port:port socket:socket];
+ // Connect
+ theRet = mysql_real_connect(mConnection, theHost, theLogin, thePass, NULL, connectionPort, theSocket, mConnectionFlags);
+ thePass = NULL;
+ if (theRet != mConnection) {
+ return mConnected = NO;
+ }
+
+ mConnected = YES;
+ mEncoding = [MCPConnection encodingForMySQLEncoding:mysql_character_set_name(mConnection)];
+ [self timeZone]; // Getting the timezone used by the server.
+ return mConnected;
}
/*
- * Override the stored disconnection method to ensure that disconnecting clears stored details.
+ * Override the stored disconnection method to ensure that disconnecting clears stored timers.
*/
- (void) disconnect
{
[super disconnect];
-
- if (connectionLogin) [connectionLogin release];
- connectionLogin = nil;
- if (connectionPassword) [connectionPassword release];
- connectionPassword = nil;
- if (connectionHost) [connectionHost release];
- connectionHost = nil;
- connectionPort = 0;
- if (connectionSocket) [connectionSocket release];
- connectionSocket = nil;
+
+ if (connectionTunnel) [connectionTunnel disconnect];
if( serverVersionString != nil ) {
serverVersionString = nil;
@@ -233,20 +348,45 @@ static void forcePingTimeout(int signalNumber);
mConnection = NULL;
}
mConnected = NO;
-
- // Attempt to reinitialise the connection - if this fails, it will still be set to NULL.
- if (mConnection == NULL) {
- mConnection = mysql_init(NULL);
+
+ // If there is a tunnel, ensure it's disconnected and attempt to reconnect it in blocking fashion
+ if (connectionTunnel) {
+ [connectionTunnel setConnectionStateChangeSelector:nil delegate:nil];
+ if ([connectionTunnel state] != SPSSH_STATE_IDLE) [connectionTunnel disconnect];
+ [connectionTunnel connect];
+ NSDate *tunnelStartDate = [NSDate date];
+
+ // Allow the tunnel to attempt to connect in a loop
+ while (1) {
+ if ([connectionTunnel state] == SPSSH_STATE_CONNECTED) {
+ connectionPort = [connectionTunnel localPort];
+ break;
+ }
+ if ([[NSDate date] timeIntervalSinceDate:tunnelStartDate] > (connectionTimeout + 1)) {
+ [connectionTunnel disconnect];
+ break;
+ }
+ [NSThread sleepForTimeInterval:0.25];
+ }
+ currentSSHTunnelState = [connectionTunnel state];
+ [connectionTunnel setConnectionStateChangeSelector:@selector(sshTunnelStateChange:) delegate:self];
}
- if (mConnection != NULL) {
+ if (!connectionTunnel || [connectionTunnel state] == SPSSH_STATE_CONNECTED) {
- // Set a connection timeout for the new connection
- mysql_options(mConnection, MYSQL_OPT_CONNECT_TIMEOUT, (const void *)&connectionTimeout);
+ // Attempt to reinitialise the connection - if this fails, it will still be set to NULL.
+ if (mConnection == NULL) {
+ mConnection = mysql_init(NULL);
+ }
+
+ if (mConnection != NULL) {
- // Attempt to reestablish the connection - using own method so everything gets set up as standard.
- // Will store the supplied details again, which isn't a problem.
- [self connectWithLogin:connectionLogin password:connectionPassword host:connectionHost port:connectionPort socket:connectionSocket];
+ // Set a connection timeout for the new connection
+ mysql_options(mConnection, MYSQL_OPT_CONNECT_TIMEOUT, (const void *)&connectionTimeout);
+
+ // Attempt to reestablish the connection
+ [self connect];
+ }
}
// If the connection was successfully established, reselect the old database and encoding if appropriate.
@@ -289,10 +429,27 @@ static void forcePingTimeout(int signalNumber);
/*
* Set the parent window of the connection for use with dialogs.
*/
-- (void)setParentWindow:(NSWindow *)theWindow {
+- (void)setParentWindow:(NSWindow *)theWindow
+{
parentWindow = theWindow;
}
+/*
+ * Handle any state changes in the associated SSH Tunnel
+ */
+- (void)sshTunnelStateChange:(SPSSHTunnel *)theTunnel
+{
+ int newState = [theTunnel state];
+
+ // Restart the tunnel if it dies
+ if (mConnected && newState == SPSSH_STATE_IDLE && currentSSHTunnelState == SPSSH_STATE_CONNECTED) {
+ currentSSHTunnelState = newState;
+ [self reconnect];
+ }
+
+ currentSSHTunnelState = newState;
+}
+
/*
* Ends and existing modal session
diff --git a/Source/KeyChain.m b/Source/KeyChain.m
index ef3afba3..032db61e 100644
--- a/Source/KeyChain.m
+++ b/Source/KeyChain.m
@@ -35,20 +35,54 @@
- (void)addPassword:(NSString *)password forName:(NSString *)name account:(NSString *)account
{
OSStatus status;
+ SecTrustedApplicationRef sequelProRef, sequelProHelperRef;
+ SecAccessRef passwordAccessRef;
+ SecKeychainAttribute attributes[4];
+ SecKeychainAttributeList attList;
// Check if password already exists before adding
if (![self passwordExistsForName:name account:account]) {
- status = SecKeychainAddGenericPassword(
- NULL, // default keychain
- strlen([name UTF8String]), // length of service name
- [name UTF8String], // service name
- strlen([account UTF8String]), // length of account name
- [account UTF8String], // account name
- strlen([password UTF8String]), // length of password
- [password UTF8String], // pointer to password data
- NULL // the item reference
- );
+
+ // Create a trusted access list with two items - ourselves and the SSH pass app.
+ NSString *helperPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"TunnelPassphraseRequester"];
+ if ((SecTrustedApplicationCreateFromPath(NULL, &sequelProRef) == noErr) &&
+ (SecTrustedApplicationCreateFromPath([helperPath UTF8String], &sequelProHelperRef) == noErr)) {
+
+ NSArray *trustedApps = [NSArray arrayWithObjects:(id)sequelProRef, (id)sequelProHelperRef, nil];
+ status = SecAccessCreate((CFStringRef)name, (CFArrayRef)trustedApps, &passwordAccessRef);
+ if (status != noErr) {
+ NSLog(@"Error (%i) while trying to create access list for name: %@ account: %@", status, name, account);
+ passwordAccessRef = NULL;
+ }
+ }
+ // Set up the item attributes
+ attributes[0].tag = kSecGenericItemAttr;
+ attributes[0].data = "application password";
+ attributes[0].length = 20;
+ attributes[1].tag = kSecLabelItemAttr;
+ attributes[1].data = (unichar *)[name UTF8String];
+ attributes[1].length = strlen([name UTF8String]);
+ attributes[2].tag = kSecAccountItemAttr;
+ attributes[2].data = (unichar *)[account UTF8String];
+ attributes[2].length = strlen([account UTF8String]);
+ attributes[3].tag = kSecServiceItemAttr;
+ attributes[3].data = (unichar *)[name UTF8String];
+ attributes[3].length = strlen([name UTF8String]);
+ attList.count = 4;
+ attList.attr = attributes;
+
+ // Create the keychain item
+ status = SecKeychainItemCreateFromContent(
+ kSecGenericPasswordItemClass, // Generic password type
+ &attList, // The attribute list created for the keychain item
+ strlen([password UTF8String]), // Length of password
+ [password UTF8String], // Password data
+ NULL, // Default keychain
+ passwordAccessRef, // Access list for this keychain
+ NULL); // The item reference
+
+ if (passwordAccessRef) CFRelease(passwordAccessRef);
if (status != noErr) {
NSLog(@"Error (%i) while trying to add password for name: %@ account: %@", status, name, account);
}
@@ -120,7 +154,7 @@
}
}
- CFRelease(itemRef);
+ if (itemRef) CFRelease(itemRef);
}
}
diff --git a/Source/SPPreferenceController.h b/Source/SPPreferenceController.h
index 7fb849a0..bc0ff1b2 100644
--- a/Source/SPPreferenceController.h
+++ b/Source/SPPreferenceController.h
@@ -49,6 +49,10 @@
IBOutlet NSTextField *userField;
IBOutlet NSTextField *databaseField;
IBOutlet NSSecureTextField *passwordField;
+ IBOutlet NSTextField *sshHostField;
+ IBOutlet NSTextField *sshUserField;
+ IBOutlet NSSecureTextField *sshPasswordField;
+ IBOutlet NSTextField *sshPortField;
KeyChain *keychain;
IBOutlet NSTextField *editorFontName;
diff --git a/Source/SPPreferenceController.m b/Source/SPPreferenceController.m
index 829f887c..ce05117e 100644
--- a/Source/SPPreferenceController.m
+++ b/Source/SPPreferenceController.m
@@ -257,13 +257,15 @@
NSString *user = [favoritesController valueForKeyPath:@"selection.user"];
NSString *host = [favoritesController valueForKeyPath:@"selection.host"];
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];
// 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:@"%@@%@/%@", user, host, database]];
+ account:[NSString stringWithFormat:@"%@@%@", sshUser, sshHost]];
// Reset last used favorite
if ([favoritesTableView selectedRow] == [prefs integerForKey:@"LastFavoriteIndex"]) {
@@ -288,15 +290,18 @@
- (IBAction)duplicateFavorite:(id)sender
{
if ([favoritesTableView numberOfSelectedRows] == 1) {
- NSString *keychainName, *keychainAccount, *password;
+ NSString *keychainName, *keychainAccount, *password, *keychainSSHName, *keychainSSHAccount, *sshPassword;
NSMutableDictionary *favorite = [NSMutableDictionary dictionaryWithDictionary:[[favoritesController arrangedObjects] objectAtIndex:[favoritesTableView selectedRow]]];
NSNumber *favoriteid = [NSNumber numberWithInt:[[NSString stringWithFormat:@"%f", [[NSDate date] timeIntervalSince1970]] hash]];
- // Select the keychain password for duplication
+ // 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"]];
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"]];
+ sshPassword = [keychain getPasswordForName:keychainSSHName account:keychainSSHAccount];
// Update the unique ID
[favorite setObject:favoriteid forKey:@"id"];
@@ -304,12 +309,16 @@
// Alter the name for clarity
[favorite setObject:[NSString stringWithFormat:@"%@ Copy", [favorite objectForKey:@"name"]] forKey:@"name"];
- // Create a new keychain item if appropriate
+ // Create new keychain items if appropriate
if (password && [password length]) {
keychainName = [NSString stringWithFormat:@"Sequel Pro : %@ (%i)", [favorite objectForKey:@"name"], [[favorite objectForKey:@"id"] intValue]];
[keychain addPassword:password forName:keychainName account:keychainAccount];
}
- password = nil;
+ if (sshPassword && [sshPassword length]) {
+ keychainSSHName = [NSString stringWithFormat:@"Sequel Pro SSHTunnel : %@ (%i)", [favorite objectForKey:@"name"], [[favorite objectForKey:@"id"] intValue]];
+ [keychain addPassword:sshPassword forName:keychainSSHName account:keychainSSHAccount];
+ }
+ password = nil, sshPassword = nil;
[favoritesController addObject:favorite];
@@ -547,6 +556,7 @@
// If no selection is present, blank the field.
if ([[favoritesTableView selectedRowIndexes] count] == 0) {
[passwordField setStringValue:@""];
+ [sshPasswordField setStringValue:@""];
return;
}
@@ -558,6 +568,14 @@
[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"]];
+
+ [sshPasswordField setStringValue:[keychain getPasswordForName:keychainSSHName account:keychainSSHAccount]];
}
#pragma mark -
@@ -651,46 +669,67 @@
{
NSString *oldKeychainName, *newKeychainName;
NSString *oldKeychainAccount, *newKeychainAccount;
- NSString *oldPassword;
- // Only proceed for name, host, user or database changes
- if (control != nameField && control != hostField && control != userField && control != passwordField && control != databaseField)
+ // Only proceed for name, host, user or database changes, for both standard and SSH
+ if (control != nameField && control != hostField && control != userField && control != passwordField && control != databaseField
+ && control != sshHostField && control != sshUserField && control != sshPasswordField)
return YES;
- // Set 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"]];
-
- // Retrieve the old password
- oldPassword = [keychain getPasswordForName:oldKeychainName account:oldKeychainAccount];
-
- // If no details have changed, skip processing
- if ([nameField stringValue] == [favoritesController valueForKeyPath:@"selection.name"]
- && [hostField stringValue] == [favoritesController valueForKeyPath:@"selection.host"]
- && [userField stringValue] == [favoritesController valueForKeyPath:@"selection.user"]
- && [databaseField stringValue] == [favoritesController valueForKeyPath:@"selection.database"]
- && [passwordField stringValue] == oldPassword) {
- oldPassword = nil;
- return YES;
+ // If account/password details have changed, update the keychain to match
+ if ([nameField stringValue] != [favoritesController valueForKeyPath:@"selection.name"]
+ || [hostField stringValue] != [favoritesController valueForKeyPath:@"selection.host"]
+ || [userField stringValue] != [favoritesController valueForKeyPath:@"selection.user"]
+ || [databaseField stringValue] != [favoritesController valueForKeyPath:@"selection.database"]
+ || 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"]];
+
+ // 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]];
+
+ // Delete the old keychain item
+ [keychain deletePasswordForName:oldKeychainName account:oldKeychainAccount];
+
+ // Add the new keychain item if the password field has a value
+ if ([[passwordField stringValue] length])
+ [keychain addPassword:[passwordField stringValue] forName:newKeychainName account:newKeychainAccount];
}
- oldPassword = nil;
- // 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]];
- // Delete the old keychain item
- [keychain deletePasswordForName:oldKeychainName account:oldKeychainAccount];
+ // If SSH account/password details have changed, update the keychain to match
+ if ([nameField stringValue] != [favoritesController valueForKeyPath:@"selection.name"]
+ || [sshHostField stringValue] != [favoritesController valueForKeyPath:@"selection.sshHost"]
+ || [sshUserField stringValue] != [favoritesController valueForKeyPath:@"selection.sshUser"]
+ || 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"]];
+
+ // 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]];
- // Add the new keychain item if the password field has a value
- if ([[passwordField stringValue] length])
- [keychain addPassword:[passwordField stringValue] forName:newKeychainName account:newKeychainAccount];
+ // Delete the old keychain item
+ [keychain deletePasswordForName:oldKeychainName account:oldKeychainAccount];
+
+ // Add the new keychain item if the password field has a value
+ if ([[sshPasswordField stringValue] length])
+ [keychain addPassword:[sshPasswordField stringValue] forName:newKeychainName account:newKeychainAccount];
+ }
// Proceed with editing
return YES;
diff --git a/Source/SPSSHTunnel.h b/Source/SPSSHTunnel.h
new file mode 100644
index 00000000..91bd08de
--- /dev/null
+++ b/Source/SPSSHTunnel.h
@@ -0,0 +1,54 @@
+#import <Cocoa/Cocoa.h>
+
+enum spsshtunnel_states
+{
+ SPSSH_STATE_IDLE = 0,
+ SPSSH_STATE_CONNECTING = 1,
+ SPSSH_STATE_WAITING_FOR_AUTH = 2,
+ SPSSH_STATE_CONNECTED = 3
+};
+
+enum spsshtunnel_password_modes
+{
+ SPSSH_PASSWORD_USES_KEYCHAIN = 0,
+ SPSSH_PASSWORD_ASKS_UI = 1
+};
+
+
+@interface SPSSHTunnel : NSObject
+{
+ NSTask *task;
+ NSPipe *standardError;
+ id delegate;
+ SEL stateChangeSelector;
+ NSConnection *passwordConnection;
+ NSString *lastError;
+ NSString *passwordConnectionName;
+ NSString *passwordConnectionVerifyHash;
+ NSString *sshHost;
+ NSString *sshLogin;
+ NSString *remoteHost;
+ NSString *password;
+ NSString *keychainName;
+ NSString *keychainAccount;
+ BOOL passwordInKeychain;
+ int sshPort;
+ int remotePort;
+ int localPort;
+ int connectionState;
+}
+
+- (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;
+- (BOOL) setPasswordKeychainName:(NSString *)theName account:(NSString *)theAccount;
+- (int) state;
+- (NSString *) lastError;
+- (int) localPort;
+- (void) connect;
+- (void) launchTask:(id) dummy;
+- (void)disconnect;
+- (void) standardErrorHandler:(NSNotification*)aNotification;
+- (NSString *) getPasswordWithVerificationHash:(NSString *)theHash;
+
+@end
diff --git a/Source/SPSSHTunnel.m b/Source/SPSSHTunnel.m
new file mode 100644
index 00000000..619657c3
--- /dev/null
+++ b/Source/SPSSHTunnel.m
@@ -0,0 +1,359 @@
+//
+// SPSSHTunnel.m
+// sequel-pro
+//
+// Created by Rowan Beentje on April 26, 2009. Inspired by code by
+// Yann Bizuel for SSH Tunnel Manager 2.
+//
+// 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 "SPSSHTunnel.h"
+#import <netinet/in.h>
+
+
+@implementation SPSSHTunnel
+
+/*
+ * 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:(int) thePort login:(NSString *) theLogin tunnellingToPort:(int) targetPort onHost:(NSString *) targetHost
+{
+ if (!theHost || !thePort || !theLogin || !targetPort || !targetHost) return nil;
+
+ self = [super init];
+
+ // Store the connection settings as appropriate
+ sshHost = [[NSString alloc] initWithString:theHost];
+ sshLogin = [[NSString alloc] initWithString:theLogin];
+ sshPort = thePort;
+ if ([theHost isEqualToString:targetHost]) {
+ remoteHost = [[NSString alloc] initWithString:@"127.0.0.1"];
+ } else {
+ remoteHost = [[NSString alloc] initWithString:targetHost];
+ }
+ remotePort = targetPort;
+ delegate = nil;
+ stateChangeSelector = nil;
+ lastError = nil;
+
+ passwordConnection = nil;
+ password = nil;
+ keychainName = nil;
+ keychainAccount = nil;
+ passwordInKeychain = NO;
+ task = nil;
+ localPort = 0;
+ connectionState = SPSSH_STATE_IDLE;
+
+ return self;
+}
+
+/*
+ * 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
+{
+ delegate = theDelegate;
+ stateChangeSelector = theStateChangeSelector;
+
+ return true;
+}
+
+/*
+ * 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
+{
+ 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;
+ }
+
+ return YES;
+}
+
+/*
+ * 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
+{
+ if (passwordConnection) [passwordConnection release], passwordConnection = nil;
+ if (password) [password release], password = nil;
+
+ passwordInKeychain = YES;
+ keychainName = [[NSString alloc] initWithString:theName];
+ keychainAccount = [[NSString alloc] initWithString:theAccount];
+
+ return YES;
+}
+
+/*
+ * Get the state of the connection.
+ */
+- (int) state
+{
+ return connectionState;
+}
+
+/*
+ * Returns the last error string, if any.
+ */
+- (NSString *) lastError
+{
+ return [NSString stringWithString:lastError];
+}
+
+/*
+ * Initiate the SSH tunnel connection, launching the task in a background thread.
+ */
+- (void) connect
+{
+ localPort = 0;
+ if (connectionState != SPSSH_STATE_IDLE || (!passwordInKeychain && !password)) return;
+ [NSThread detachNewThreadSelector:@selector(launchTask:) toTarget: self withObject: nil ];
+}
+
+/*
+ * Launch the NSTask which wraps the SSH process, and use it to initiate the
+ * tunnel to the remote server.
+ * Sets up and tears down as appropriate for usage in a background thread.
+ */
+- (void) launchTask:(id) dummy
+{
+ if (connectionState != SPSSH_STATE_IDLE || task) return;
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ NSMutableArray *taskArguments;
+ NSMutableDictionary *taskEnvironment;
+ NSString *authenticationAppPath;
+
+ connectionState = SPSSH_STATE_CONNECTING;
+ if (delegate) [delegate performSelectorOnMainThread:stateChangeSelector withObject:self waitUntilDone:NO];
+
+ int connectionTimeout = [[[NSUserDefaults standardUserDefaults] objectForKey:@"ConnectionTimeout"] intValue];
+ if (!connectionTimeout) connectionTimeout = 10;
+ BOOL useKeepAlive = [[[NSUserDefaults standardUserDefaults] objectForKey:@"UseKeepAlive"] doubleValue];
+ double keepAliveInterval = [[[NSUserDefaults standardUserDefaults] objectForKey:@"KeepAliveInterval"] doubleValue];
+ if (!keepAliveInterval) keepAliveInterval = 0;
+
+ // If no local port has yet been chosen, choose one
+ if (!localPort) {
+ int tempSocket;
+ struct sockaddr_in tempSocketAddress;
+ int addressLength = sizeof(tempSocketAddress);
+ if((tempSocket = socket(AF_INET, SOCK_STREAM, 0)) > 0) {
+ memset(&tempSocketAddress, 0, sizeof(tempSocketAddress));
+ tempSocketAddress.sin_family = AF_INET;
+ tempSocketAddress.sin_addr.s_addr = htonl(INADDR_ANY);
+ tempSocketAddress.sin_port = 0;
+ if (bind(tempSocket, (struct sockaddr *)&tempSocketAddress, addressLength) >= 0) {
+ if (getsockname(tempSocket, (struct sockaddr *)&tempSocketAddress, (uint32_t *)&addressLength) >= 0) {
+ localPort = ntohs(tempSocketAddress.sin_port);
+ }
+ }
+ close(tempSocket);
+ }
+
+ // Abort if no local free port could be allocated
+ if (!localPort) {
+ connectionState = SPSSH_STATE_IDLE;
+ if (delegate) [delegate performSelectorOnMainThread:stateChangeSelector withObject:self waitUntilDone:NO];
+ if (lastError) [lastError release];
+ lastError = [[NSString alloc] initWithString:NSLocalizedString(@"No local port could be allocated for the SSH Tunnel.", @"SSH tunnel could not be created because no local port could be allocated")];
+ [pool release];
+ return;
+ }
+ }
+
+ // Set up the NSTask
+ task = [[NSTask alloc] init];
+ [task setLaunchPath: @"/usr/bin/ssh"];
+
+ // Set up the arguments for the task
+ taskArguments = [ NSMutableArray array ];
+ [taskArguments addObject:@"-N"]; // Tunnel only
+ [taskArguments addObject:@"-v"]; // Verbose mode for messages
+// [taskArguments addObject:@"-C"]; // TODO: compression?
+ [taskArguments addObject:@"-o ExitOnForwardFailure=yes"];
+ [taskArguments addObject:[NSString stringWithFormat:@"-o ConnectTimeout=%i", connectionTimeout]];
+ if (useKeepAlive && keepAliveInterval) {
+ [taskArguments addObject:@"-o TCPKeepAlive=no"];
+ [taskArguments addObject:[NSString stringWithFormat:@"-o ServerAliveInterval=%i", ceil(keepAliveInterval)]];
+ [taskArguments addObject:@"-o ServerAliveCountMax=1"];
+ }
+ [taskArguments addObject:[NSString stringWithFormat:@"-p %i", sshPort]];
+ [taskArguments addObject:[NSString stringWithFormat:@"%@@%@", sshLogin, sshHost]];
+ [taskArguments addObject:[NSString stringWithFormat:@"-L %i/%@/%i", localPort, remoteHost, remotePort]];
+ [task setArguments:taskArguments];
+
+ // Set up the environment for the task
+ authenticationAppPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"TunnelPassphraseRequester"];
+ taskEnvironment = [NSMutableDictionary dictionaryWithDictionary:[[NSProcessInfo processInfo] environment]];
+ [taskEnvironment removeObjectForKey: @"SSH_AGENT_PID"];
+ [taskEnvironment removeObjectForKey: @"SSH_AUTH_SOCK"];
+ [taskEnvironment setObject:authenticationAppPath forKey:@"SSH_ASKPASS"];
+ [taskEnvironment setObject:@":0" forKey:@"DISPLAY"];
+ 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"];
+ }
+ [task setEnvironment:taskEnvironment];
+
+ // Set up the standard error pipe
+ standardError = [[NSPipe alloc] init];
+ [task setStandardError:standardError];
+ [[ NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(standardErrorHandler:)
+ name:@"NSFileHandleDataAvailableNotification"
+ object:[standardError fileHandleForReading]];
+ [[standardError fileHandleForReading] waitForDataInBackgroundAndNotify];
+
+ // Launch and run the tunnel
+ [task launch];
+
+ // TODO: The below code doesn't actually appear to work. We will probably have to switch to system()/exec() for grouped children...
+ // Apply the process group to the child task to ensure it quits with the parent process.
+ // Note that if run from within Xcode, Xcode is the parent process!
+/* pid_t group = setsid();
+ if (group == -1) group = getpgrp();
+ if(setpgid([task processIdentifier], group) == -1) {
+ connectionState = SPSSH_STATE_IDLE;
+ [task terminate];
+ if (lastError) [lastError release];
+ lastError = [[NSString alloc] initWithFormat:NSLocalizedString(@"The SSH Tunnel could not safely be marked as belonging to Sequel Pro, and so has been shut down for security reasons. Please try again.\n\n(Error %i)", @"SSH tunnel could not be security marked by Sequel Pro"), errno];
+ if (delegate) [delegate performSelectorOnMainThread:stateChangeSelector withObject:self waitUntilDone:NO];
+ }*/
+
+ // Listen for output
+ [task waitUntilExit];
+ if (connectionState != SPSSH_STATE_IDLE) {
+ connectionState = SPSSH_STATE_IDLE;
+ lastError = [[NSString alloc] initWithString:NSLocalizedString(@"The SSH Tunnel has unexpectedly closed.", @"SSH tunnel unexpectedly closed")];
+ if (delegate) [delegate performSelectorOnMainThread:stateChangeSelector withObject:self waitUntilDone:NO];
+ }
+
+ // On tunnel close, clean up
+ [[NSNotificationCenter defaultCenter] removeObserver:self
+ name:@"NSFileHandleDataAvailableNotification"
+ object:[standardError fileHandleForReading]];
+ [task release], task = nil;
+ [standardError release], standardError = nil;
+
+ [pool release];
+}
+
+/*
+ * Disconnects the tunnel
+ */
+- (void)disconnect
+{
+ if (connectionState == SPSSH_STATE_IDLE) return;
+ [task terminate];
+ connectionState = SPSSH_STATE_IDLE;
+ if (delegate) [delegate performSelectorOnMainThread:stateChangeSelector withObject:self waitUntilDone:NO];
+}
+
+/*
+ * Processes messages recieved from the SSH task
+ */
+- (void)standardErrorHandler:(NSNotification*)aNotification
+{
+ NSString *notificationText;
+ NSEnumerator *enumerator;
+ NSArray *messages;
+ NSString *message;
+
+ notificationText = [[NSString alloc] initWithData:[[aNotification object] availableData] encoding:NSASCIIStringEncoding];
+
+ if ([notificationText length]) {
+ messages = [notificationText componentsSeparatedByString:@"\n"];
+ enumerator = [messages objectEnumerator];
+ while (message = [enumerator nextObject]) {
+
+ if ([message rangeOfString:@"Entering interactive session."].location != NSNotFound) {
+ connectionState = SPSSH_STATE_CONNECTED;
+ if (delegate) [delegate performSelectorOnMainThread:stateChangeSelector withObject:self waitUntilDone:NO];
+ }
+
+ if ([message rangeOfString:@"Connection established"].location != NSNotFound) {
+ connectionState = SPSSH_STATE_WAITING_FOR_AUTH;
+ if (delegate) [delegate performSelectorOnMainThread:stateChangeSelector withObject:self waitUntilDone:NO];
+ }
+
+ if ([message rangeOfString:@"closed by remote host." ].location != NSNotFound) {
+ connectionState = SPSSH_STATE_IDLE;
+ [task terminate];
+ if (lastError) [lastError release];
+ lastError = [[NSString alloc] initWithString:NSLocalizedString(@"The SSH Tunnel was closed 'by the remote host'. This may indicate a networking issue or a network timeout.", @"SSH tunnel was closed by remote host message")];
+ if (delegate) [delegate performSelectorOnMainThread:stateChangeSelector withObject:self waitUntilDone:NO];
+ }
+ if ([message rangeOfString:@"Operation timed out" ].location != NSNotFound) {
+ connectionState = SPSSH_STATE_IDLE;
+ [task terminate];
+ if (lastError) [lastError release];
+ lastError = [[NSString alloc] initWithFormat:NSLocalizedString(@"The SSH Tunnel was 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).", @"SSH tunnel failed or timed out message"), sshHost, [[[NSUserDefaults standardUserDefaults] objectForKey:@"ConnectionTimeoutValue"] intValue]];
+ if (delegate) [delegate performSelectorOnMainThread:stateChangeSelector withObject:self waitUntilDone:NO];
+ }
+ }
+ }
+
+ if (connectionState != SPSSH_STATE_IDLE) {
+ [[standardError fileHandleForReading] waitForDataInBackgroundAndNotify];
+ }
+
+ [notificationText release];
+}
+
+/*
+ * Returns the local port assigned for use by the tunnel
+ */
+- (int) localPort
+{
+ return localPort;
+}
+
+/*
+ * Method to request the password for the current connection, as used by TunnelPassphraseRequester;
+ * called with a verification hash to check against the stored hash, to provide basic security. Note
+ * that this is easily bypassed, but if bypassed the password can already easily be retrieved in the same way.
+ */
+- (NSString *)getPasswordWithVerificationHash:(NSString *)theHash
+{
+ if (passwordInKeychain) return nil;
+ if (theHash != passwordConnectionVerifyHash) return nil;
+ return password;
+}
+
+@end
diff --git a/Source/SSHTunnel.h b/Source/SSHTunnel.h
deleted file mode 100644
index ffe29624..00000000
--- a/Source/SSHTunnel.h
+++ /dev/null
@@ -1,57 +0,0 @@
-#import <Cocoa/Cocoa.h>
-
-@interface SSHTunnel : NSObject
-{
- int code;
- NSArray *tunnelsLocal;
- NSArray *tunnelsRemote;
-
- BOOL shouldStop;
- NSTask *task;
- BOOL connAuth;
- BOOL autoConnect;
- NSPipe *stdErrPipe;
- NSString *connName;
- NSString *status;
- NSString *connPort;
- BOOL connRemote;
- BOOL compression;
- BOOL v1;
- NSString * encryption;
- BOOL socks4;
- NSNumber *socks4p;
- NSString *connUser;
- NSString *connHost;
-}
--(id)initWithName:(NSString*)aName;
--(id)initWithDictionary:(NSDictionary*)aDictionary;
-+(id)tunnelWithName:(NSString*)aName;
-+(NSArray*)tunnelsFromArray:(NSArray*)anArray;
-
--(void)addLocalTunnel:(NSDictionary*)aDictionary;
-- (void)removeLocal:(int)index;
--(void)addRemoteTunnel:(NSDictionary*)aDictionary;
-- (void)removeRemote:(int)index;
-- (void)setLocalValue:(NSString*)aValue ofTunnel:(int)index forKey:(NSString*)key;
-- (void)setRemoteValue:(NSString*)aValue ofTunnel:(int)index forKey:(NSString*)key;
-
-#pragma mark -
-#pragma mark Execution related
-- (void)startTunnel;
-- (void)stopTunnel;
-- (void)toggleTunnel;
-- (void)launchTunnel:(id)foo;
-- (void)stdErr:(NSNotification*)aNotification;
-- (BOOL)isRunning;
-
-#pragma mark -
-#pragma mark Getting tunnel informations
-- (NSString*)status;
-- (NSArray*)arguments;
-- (NSDictionary*)dictionary;
-
-#pragma mark -
-#pragma mark Key/Value coding
-- (NSImage*)icon;
-
-@end
diff --git a/Source/SSHTunnel.m b/Source/SSHTunnel.m
deleted file mode 100644
index 617db8a7..00000000
--- a/Source/SSHTunnel.m
+++ /dev/null
@@ -1,531 +0,0 @@
-//
-// SSHTunnel.m
-// SSH Tunnel Manager 2
-//
-// Created by Yann Bizeul on Wed Nov 19 2003.
-// Copyright (c) 2003 __MyCompanyName__. All rights reserved.
-//
-
-#import "SSHTunnel.h"
-#include <unistd.h>
-
-// start diff lorenz textor
-/*
-#define T_START NSLocalizedString(@"T_START",@"")
-#define T_STOP NSLocalizedString(@"T_STOP",@"")
-#define S_IDLE NSLocalizedString(@"S_IDLE",@"")
-#define S_CONNECTING NSLocalizedString(@"S_CONNECTING",@"")
-#define S_CONNECTED NSLocalizedString(@"S_CONNECTED",@"")
-#define S_AUTH NSLocalizedString(@"S_AUTH",@"")
-#define S_PORT NSLocalizedString(@"S_PORT",@"")
-*/
-#define T_START @"START: %@"
-#define T_STOP @"STOP: %@"
-#define S_IDLE @"Idle"
-#define S_CONNECTING @"Connecting..."
-#define S_CONNECTED @"Connected"
-#define S_AUTH @"Authenticated"
-#define S_PORT "Port %@ forwarded"
-// end diff lorenz textor
-
-@implementation SSHTunnel
-
-#pragma mark -
-#pragma mark Initialization
--(id)init
-{
- return [ self initWithName:@"New Tunnel"];
-}
--(id)initWithName:(NSString*)aName
-{
- NSDictionary *dictionary = [ NSDictionary dictionaryWithObjectsAndKeys:
- [ NSNumber numberWithBool: NO ],@"compression",
- [ NSNumber numberWithBool: YES ],@"connAuth",
- @"", @"connHost",
- aName, @"connName",
- @"", @"connPort",
- [ NSNumber numberWithBool: NO ],@"connRemote",
- @"", @"connUser",
- @"3des", @"encryption",
- [ NSNumber numberWithBool: NO ],@"socks4",
- [ NSNumber numberWithInt: 1080 ], @"socks4p",
- [ NSArray array ], @"tunnelsLocal",
- [ NSArray array ], @"tunnelsRemote",
- [ NSNumber numberWithBool: NO ],@"v1", nil
- ];
- return [ self initWithDictionary: dictionary ];
-}
--(id)initWithDictionary:(NSDictionary*)aDictionary
-{
- NSEnumerator *e;
- NSString *key;
-
- self = [ super init ];
- e = [[ aDictionary allKeys ] objectEnumerator ];
- while (key = [ e nextObject ])
- {
- [ self setValue: [ aDictionary objectForKey: key ] forKey: key ];
- }
- code = 0;
- if ([[ self valueForKey: @"autoConnect" ] boolValue ])
- [ self startTunnel ];
- return self;
-}
-+(id)tunnelWithName:(NSString*)aName
-{
- return [[ SSHTunnel alloc ] initWithName: aName ];
-}
-+(SSHTunnel*)tunnelFromDictionary:(NSDictionary*)aDictionary
-{
- return [[ SSHTunnel alloc ] initWithDictionary: aDictionary ];
-}
-+(NSArray*)tunnelsFromArray:(NSArray*)anArray
-{
- NSMutableArray *newArray;
- SSHTunnel *currentTunnel;
- NSEnumerator *e;
- NSDictionary *currentTunnelDictionary;
-
- newArray = [ NSMutableArray array ];
- e = [ anArray objectEnumerator ];
- while (currentTunnelDictionary = [ e nextObject ])
- {
- currentTunnel = [ SSHTunnel tunnelFromDictionary: currentTunnelDictionary ];
- [ newArray addObject: currentTunnel ];
- }
- return [[ newArray copy ] autorelease ];
-}
-
-#pragma mark -
-#pragma mark Adding and removing port redir.
--(void)addLocalTunnel:(NSDictionary*)aDictionary;
-{
- NSMutableArray *tempTunnelsLocal = [ NSMutableArray arrayWithArray: tunnelsLocal ];
- [ tempTunnelsLocal addObject: aDictionary ];
- [ tunnelsLocal release ];
- tunnelsLocal = [ tempTunnelsLocal copy ];
-}
-- (void)removeLocal:(int)index
-{
- NSMutableArray *tempLocalTunnels = [ tunnelsLocal mutableCopy ];
- [ tempLocalTunnels removeObjectAtIndex: index ];
- [ tunnelsLocal release ];
- tunnelsLocal = [ tempLocalTunnels copy ];
- [ tempLocalTunnels release ];
-}
--(void)addRemoteTunnel:(NSDictionary*)aDictionary;
-{
- NSMutableArray *tempTunnelsRemote = [ NSMutableArray arrayWithArray: tunnelsRemote ];
- [ tempTunnelsRemote addObject: aDictionary ];
- [ tunnelsRemote release ];
- tunnelsRemote = [ tempTunnelsRemote copy ];
-}
-- (void)removeRemote:(int)index
-{
- NSMutableArray *tempRemoteTunnels = [ tunnelsRemote mutableCopy ];
- [ tempRemoteTunnels removeObjectAtIndex: index ];
- [ tunnelsRemote release ];
- tunnelsRemote = [ tempRemoteTunnels copy ];
- [ tempRemoteTunnels release ];
-}
-- (void)setLocalValue:(NSString*)aValue ofTunnel:(int)index forKey:(NSString*)key
-{
- NSMutableArray *tempLocalTunnel;
- NSMutableDictionary *tempCurrentTunnel;
-
- tempLocalTunnel = [tunnelsLocal mutableCopy];
- tempCurrentTunnel = [[ tempLocalTunnel objectAtIndex: index ] mutableCopy ];
-
- [ tempCurrentTunnel setObject: aValue forKey: key ];
- [ tempLocalTunnel replaceObjectAtIndex:index withObject:[tempCurrentTunnel copy ]];
- [ tempCurrentTunnel release ];
- [ tunnelsLocal release ];
- tunnelsLocal = [ tempLocalTunnel copy ];
-}
-- (void)setRemoteValue:(NSString*)aValue ofTunnel:(int)index forKey:(NSString*)key
-{
- NSMutableArray *tempRemoteTunnel;
- NSMutableDictionary *tempCurrentTunnel;
-
- tempRemoteTunnel = [tunnelsRemote mutableCopy];
- tempCurrentTunnel = [[ tempRemoteTunnel objectAtIndex: index ] mutableCopy ];
-
- [ tempCurrentTunnel setObject: aValue forKey: key ];
- [ tempRemoteTunnel replaceObjectAtIndex:index withObject:[tempCurrentTunnel copy ]];
- [ tempCurrentTunnel release ];
- [ tunnelsRemote release ];
- tunnelsRemote = [ tempRemoteTunnel copy ];
-}
-
-#pragma mark -
-#pragma mark Execution related
-- (void)startTunnel
-{
-// NSDictionary *t;
-// NSEnumerator *e;
-// BOOL asRoot = NO;
-
- if ([ self isRunning ])
- return;
-
- shouldStop = NO;
- /*
- [ arguments addObject: @"-N" ];
- [ arguments addObject: @"-v" ];
- [ arguments addObject: @"-p" ];
- if ([ connPort length ])
- [ arguments addObject: connPort];
- else
- [ arguments addObject: @"22" ];
-
- if (connRemote)
- [ arguments addObject: @"-g" ];
- if (compression)
- [ arguments addObject: @"-C" ];
- if (v1)
- [ arguments addObject: @"-1" ];
-
- [ arguments addObject: @"-c"];
- if (encryption)
- [ arguments addObject: encryption];
- else
- [ arguments addObject: @"3des"];
-
- if (socks4 && socks4p != nil)
- {
- [ arguments addObject: @"-D" ];
- [ arguments addObject: [ socks4p stringValue ]];
- }
- [ arguments addObject: [ NSString stringWithFormat: @"%@@%@",
- connUser, connHost ]
- ];
-
- NSString *hostPort;
- e = [ tunnelsLocal objectEnumerator ];
- while (t = [ e nextObject ])
- {
- [ arguments addObject: @"-L" ];
- if ([[ t objectForKey:@"hostport"] isEqualTo: @"" ])
- hostPort = [ t objectForKey:@"port" ];
- else
- hostPort = [ t objectForKey:@"hostport" ];
- [ arguments addObject: [NSString stringWithFormat:@"%@/%@/%@",
- [ t objectForKey:@"port"],
- [ t objectForKey:@"host"],
- hostPort
- ] ];
- if ([[ t objectForKey:@"port"] intValue] < 1024)
- asRoot=YES;
- }
-
- e = [ tunnelsRemote objectEnumerator ];
- while (t = [ e nextObject ])
- {
- [ arguments addObject: @"-R" ];
- if ([[ t objectForKey:@"hostport"] isEqualTo: @"" ])
- hostPort = [ t objectForKey:@"port" ];
- else
- hostPort = [ t objectForKey:@"hostport" ];
- [ arguments addObject: [NSString stringWithFormat:@"%@/%@/%@",
- [ t objectForKey:@"port"],
- [ t objectForKey:@"host"],
- hostPort
- ]];
- }
- args = [ NSMutableDictionary dictionary ];
- [ args setObject: arguments forKey:@"arguments" ];
- [ args setObject: [ NSNumber numberWithBool: connAuth ] forKey: @"handleAuth" ];
- [ args setObject: connName forKey:@"tunnelName" ];
- [ args setObject: [ NSNumber numberWithBool: asRoot ] forKey: @"asRoot" ];
-
-
- [ NSThread detachNewThreadSelector:@selector(launchTunnel:)
- toTarget: self
- withObject: args ];
- */
- [ NSThread detachNewThreadSelector:@selector(launchTunnel:)
- toTarget: self
- withObject: nil ];
-// [ arguments release ];
-
-}
-- (void)stopTunnel
-{
- if (! [ self isRunning ])
- return;
- shouldStop=YES;
- [ self setValue: nil forKey: @"status" ];
- [ task terminate ];
- code = 0;
- [[ NSNotificationCenter defaultCenter] postNotificationName:@"STMStatusChanged" object:self ];
-}
-
-- (void)toggleTunnel
-{
- if ([ self isRunning ])
- [ self stopTunnel ];
- else
- [ self startTunnel ];
-}
-
-- (void)launchTunnel:(id)foo;
-{
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
- if (task)
- [ task release ];
- task = [[ NSTask alloc ] init ];
- NSMutableDictionary *environment = [ NSMutableDictionary dictionaryWithDictionary: [[ NSProcessInfo processInfo ] environment ]];
- NSString *pathToAuthentifier = [[ NSBundle mainBundle ] pathForResource: @"askForPass" ofType: @"sh" ];
- if (socks4)
- [ task setLaunchPath: [[ NSBundle mainBundle ] pathForResource: @"ssh" ofType: @"" ]];
- else
- [ task setLaunchPath: @"/usr/bin/ssh" ];
- [ task setArguments: [ self arguments ]];
- if (connAuth)
- {
- [ environment removeObjectForKey: @"SSH_AGENT_PID" ];
- [ environment removeObjectForKey: @"SSH_AUTH_SOCK" ];
- [ environment setObject: pathToAuthentifier forKey: @"SSH_ASKPASS" ];
- [ environment setObject:@":0" forKey:@"DISPLAY" ];
- }
- [ environment setObject: connName forKey: @"TUNNEL_NAME" ];
- [ task setEnvironment: environment ];
-
- stdErrPipe = [[ NSPipe alloc ] init ];
- [ task setStandardError: stdErrPipe ];
-
- [[ NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(stdErr:)
- name: @"NSFileHandleDataAvailableNotification"
- object:[ stdErrPipe fileHandleForReading]];
-
- [[ stdErrPipe fileHandleForReading] waitForDataInBackgroundAndNotify ];
-
- NSLog(T_START,connName);
- [ self setValue: S_CONNECTING forKey: @"status" ];
- code = 1;
- [ task launch ];
- [[ NSNotificationCenter defaultCenter] postNotificationName:@"STMStatusChanged" object:self ];
- [ task waitUntilExit ];
- sleep(1);
- code = 0;
- [ self setValue: S_IDLE forKey: @"status" ];
- NSLog(T_STOP,connName);
- [[ NSNotificationCenter defaultCenter] removeObserver:self
- name: @"NSFileHandleDataAvailableNotification"
- object:[ stdErrPipe fileHandleForReading]];
- [ task release ];
- task = nil;
- [ stdErrPipe release ];
- stdErrPipe = nil;
- [[ NSNotificationCenter defaultCenter] postNotificationName:@"STMStatusChanged" object:self ];
- if (! shouldStop)
- [ self startTunnel ];
- [ pool release ];
-}
-
-- (void)stdErr:(NSNotification*)aNotification
-{
- NSData *data = [[ aNotification object ] availableData ];
- NSString *log = [[ NSString alloc ] initWithData: data encoding: NSASCIIStringEncoding ];
- BOOL wait = YES;
- if ([ log length ])
- {
- //NSLog(log);
- NSArray *lines = [ log componentsSeparatedByString:@"\n" ];
- NSEnumerator *e = [ lines objectEnumerator ];
- NSString *line;
- while (line = [ e nextObject ])
- {
- if ([ line rangeOfString:@"Entering interactive session." ].location != NSNotFound)
- {
- code = 2;
- [ self setValue: S_CONNECTED forKey: @"status"];
- }
- if ([ line rangeOfString:@"Authentication succeeded" ].location != NSNotFound)
- [ self setValue: S_AUTH forKey: @"status"];
- if ([ line rangeOfString:@"Connections to local port" ].location != NSNotFound)
- {
- NSScanner *s;
- NSString *port;
- s = [ NSScanner scannerWithString:log];
- [ s scanUpToString: @"Connections to local port " intoString: nil ];
- [ s scanString: @"Connections to local port " intoString: nil ];
- [ s scanUpToString: @"forwarded" intoString:&port];
- [ self setValue: [ NSString stringWithFormat: @"Port %@ forwarded", port ] forKey: @"status"];
- }
- if ([ line rangeOfString:@"closed by remote host." ].location != NSNotFound)
- {
- [ task terminate];
- [ self setValue: @"Connection closed" forKey: @"status"];
- wait = NO;
- }
- [[ NSNotificationCenter defaultCenter] postNotificationName:@"STMStatusChanged" object:self ];
- }
- if (wait)
- [[ stdErrPipe fileHandleForReading ] waitForDataInBackgroundAndNotify ];
- }
- [ log release] ;
-}
-
-- (BOOL)isRunning
-{
- if ([ task isRunning ])
- return YES;
- return NO;
-}
-
-#pragma mark -
-#pragma mark Getting tunnel informations
-- (NSString *)status
-{
- if (status)
- return status;
- return S_IDLE;
-}
-- (NSArray*)arguments
-{
- NSMutableArray *arguments;
- NSEnumerator *e;
- NSDictionary *t;
- BOOL asRoot;
-
- arguments = [ NSMutableArray array ];
- [ arguments addObject: @"-N" ];
- [ arguments addObject: @"-v" ];
- [ arguments addObject: @"-p" ];
- if ([ connPort length ])
- [ arguments addObject: connPort];
- else
- [ arguments addObject: @"22" ];
-
- if (connRemote)
- [ arguments addObject: @"-g" ];
- if (compression)
- [ arguments addObject: @"-C" ];
- if (v1)
- [ arguments addObject: @"-1" ];
-
- [ arguments addObject: @"-c"];
- if (encryption)
- [ arguments addObject: encryption];
- else
- [ arguments addObject: @"3des"];
-
- if (socks4 && socks4p != nil)
- {
- [ arguments addObject: @"-D" ];
- [ arguments addObject: [ socks4p stringValue ]];
- }
- [ arguments addObject: [ NSString stringWithFormat: @"%@@%@",
- connUser, connHost ]
- ];
-
- NSString *hostPort;
- e = [ tunnelsLocal objectEnumerator ];
- while (t = [ e nextObject ])
- {
- [ arguments addObject: @"-L" ];
- if ([[ t objectForKey:@"hostport"] isEqualTo: @"" ])
- hostPort = [ t objectForKey:@"port" ];
- else
- hostPort = [ t objectForKey:@"hostport" ];
- [ arguments addObject: [NSString stringWithFormat:@"%@/%@/%@",
- [ t objectForKey:@"port"],
- [ t objectForKey:@"host"],
- hostPort
- ] ];
- if ([[ t objectForKey:@"port"] intValue] < 1024)
- asRoot=YES;
- }
-
- e = [ tunnelsRemote objectEnumerator ];
- while (t = [ e nextObject ])
- {
- [ arguments addObject: @"-R" ];
- if ([[ t objectForKey:@"hostport"] isEqualTo: @"" ])
- hostPort = [ t objectForKey:@"port" ];
- else
- hostPort = [ t objectForKey:@"hostport" ];
- [ arguments addObject: [NSString stringWithFormat:@"%@/%@/%@",
- [ t objectForKey:@"port"],
- [ t objectForKey:@"host"],
- hostPort
- ]];
- }
-
- return [[ arguments copy ] autorelease ];
-}
-
-- (NSDictionary*)dictionary
-{
- return [ NSDictionary dictionaryWithObjectsAndKeys:
- [ NSNumber numberWithBool: compression ],@"compression",
- [ NSNumber numberWithBool: connAuth ],@"connAuth",
- [ NSNumber numberWithBool: autoConnect ],@"autoConnect",
- connHost, @"connHost",
- connName, @"connName",
- connPort, @"connPort",
- [ NSNumber numberWithBool: connRemote ],@"connRemote",
- connUser, @"connUser",
- encryption, @"encryption",
- [ NSNumber numberWithBool: socks4 ],@"socks4",
- socks4p, @"socks4p",
- tunnelsLocal, @"tunnelsLocal",
- tunnelsRemote, @"tunnelsRemote",
- [ NSNumber numberWithBool: v1 ],@"v1", nil
- ];
-}
-
-
-#pragma mark -
-#pragma mark Key/Value coding
-- (NSImage*)icon
-{
- switch (code)
- {
- case 0:
- return [ NSImage imageNamed: @"offState" ];
- break;
- case 1:
- return [ NSImage imageNamed: @"middleState" ];
- break;
- case 2:
- return [ NSImage imageNamed: @"onState" ];
- break;
- }
- return [ NSImage imageNamed: @"offState" ];
-}
-- (void)setValue:(id)value forUndefinedKey:(NSString *)key
-{
- NSLog(@"key %@ undefined",key);
-}
-- (id)valueForUndefinedKey:(NSString *)key
-{
- return nil;
-}
-
-#pragma mark -
-#pragma mark Misc.
--(void)dealloc
-{
- [ self stopTunnel ];
- [ tunnelsLocal release ];
- [ tunnelsRemote release ];
-
- [ task release ];
- [ stdErrPipe release ];
- [ connName release ];
- [ status release ];
- [ connPort release ];
- [ encryption release ];
- [ socks4p release ];
- [ connUser release ];
- [ connHost release ];
-
- // start diff lorenz textor
- [super dealloc];
- // end diff lorenz textor
-}
-@end
diff --git a/Source/TableDocument.h b/Source/TableDocument.h
index 738acdfb..e71e80ac 100644
--- a/Source/TableDocument.h
+++ b/Source/TableDocument.h
@@ -28,6 +28,7 @@
#import <Cocoa/Cocoa.h>
#import <MCPKit_bundled/MCPKit_bundled.h>
#import <WebKit/WebKit.h>
+#import "SPSSHTunnel.h"
@class CMMCPConnection, CMMCPResult;
@@ -65,8 +66,13 @@
IBOutlet id passwordField;
IBOutlet id portField;
IBOutlet id databaseField;
+ IBOutlet id sshCheckbox;
+ IBOutlet id sshHostField;
+ IBOutlet id sshUserField;
+ IBOutlet id sshPasswordField;
+ IBOutlet id sshPortField;
- IBOutlet id connectProgressBar;
+ IBOutlet NSProgressIndicator *connectProgressBar;
IBOutlet NSTextField *connectProgressStatusText;
IBOutlet id databaseNameField;
IBOutlet id databaseEncodingButton;
@@ -92,6 +98,11 @@
NSString *mySQLVersion;
NSUserDefaults *prefs;
+ NSString *connectionKeychainItemName;
+ NSString *connectionKeychainItemAccount;
+ NSString *connectionSSHKeychainItemName;
+ NSString *connectionSSHKeychainItemAccount;
+
NSMenu *selectEncodingMenu;
BOOL _supportsEncoding;
NSString *_encoding;
@@ -107,13 +118,17 @@
//start sheet
- (void)setShouldAutomaticallyConnect:(BOOL)shouldAutomaticallyConnect;
- (IBAction)connectToDB:(id)sender;
-- (IBAction)connect:(id)sender;
+- (IBAction)initiateConnection:(id)sender;
+- (void)initiateSSHTunnelConnection;
+- (void)sshTunnelCallback:(SPSSHTunnel *)theTunnel;
+- (void)initiateMySQLConnection:(SPSSHTunnel *)theTunnel;
+- (void)failConnectionWithErrorMessage:(NSString *)theErrorMessage;
- (IBAction)cancelConnectSheet:(id)sender;
- (IBAction)closeSheet:(id)sender;
- (IBAction)chooseFavorite:(id)sender;
+- (IBAction)toggleUseSSH:(id)sender;
- (IBAction)editFavorites:(id)sender;
- (id)selectedFavorite;
-- (NSString *)selectedFavoritePassword;
- (void)connectSheetAddToFavorites:(id)sender;
- (void)addToFavoritesName:(NSString *)name host:(NSString *)host socket:(NSString *)socket
user:(NSString *)user password:(NSString *)password
diff --git a/Source/TableDocument.m b/Source/TableDocument.m
index ff13096d..2b98c776 100644
--- a/Source/TableDocument.m
+++ b/Source/TableDocument.m
@@ -68,6 +68,11 @@ NSString *TableDocumentFavoritesControllerSelectionIndexDidChange = @"TableDocum
_encoding = [@"utf8" retain];
chooseDatabaseButton = nil;
chooseDatabaseToolbarItem = nil;
+ connectionKeychainItemName = nil;
+ connectionKeychainItemAccount = nil;
+ connectionSSHKeychainItemName = nil;
+ connectionSSHKeychainItemAccount = nil;
+ selectedDatabase = nil;
printWebView = [[WebView alloc] init];
[printWebView setFrameLoadDelegate:self];
@@ -90,7 +95,7 @@ NSString *TableDocumentFavoritesControllerSelectionIndexDidChange = @"TableDocum
// Register double click for the favorites view (double click favorite to connect)
[connectFavoritesTableView setTarget:self];
- [connectFavoritesTableView setDoubleAction:@selector(connect:)];
+ [connectFavoritesTableView setDoubleAction:@selector(initiateConnection:)];
// Find the Database -> Database Encoding menu (it's not in our nib, so we can't use interface builder)
selectEncodingMenu = [[[[[NSApp mainMenu] itemWithTag:1] submenu] itemWithTag:1] submenu];
@@ -224,7 +229,7 @@ NSString *TableDocumentFavoritesControllerSelectionIndexDidChange = @"TableDocum
//start sheet
/**
- * Set whether the connection sheet should automaticall start connecting
+ * Set whether the connection sheet should automatically start connecting
*/
- (void)setShouldAutomaticallyConnect:(BOOL)shouldAutomaticallyConnect
{
@@ -240,151 +245,264 @@ NSString *TableDocumentFavoritesControllerSelectionIndexDidChange = @"TableDocum
// load the details of the currently selected favorite into the text boxes in connect sheet
[self chooseFavorite:self];
-
+
// run the connect sheet (modal)
[NSApp beginSheet:connectSheet
modalForWindow:tableWindow
modalDelegate:self
- didEndSelector:@selector(connectSheetDidEnd:returnCode:contextInfo:)
+ didEndSelector:nil
contextInfo:nil];
// Connect automatically to the last used or default favourite
// connectSheet must open first.
if (_shouldOpenConnectionAutomatically) {
_shouldOpenConnectionAutomatically = false;
- [self connect:self];
+ [self initiateConnection:self];
}
}
+
/*
- invoked when user hits the connect-button of the connectSheet
- stops modal session with code:
- 1 when connected with success
- 2 when no connection to host
- 3 when no connection to db
- 4 when hostField and socketField are empty
+ * Starts the connection process; invoked when user hits the connect button
+ * of the connection sheet or double-clicks on a favourite of the connectSheet.
+ * Error-checks fields as required, and triggers connection of MySQL or any
+ * proxies in use.
*/
-- (IBAction)connect:(id)sender
+- (IBAction)initiateConnection:(id)sender
{
- int code;
-
+
+ // Error-check required fields before starting a connection
+ if (![[hostField stringValue] length] && ![[socketField stringValue] length]) {
+ [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")];
+ return;
+ }
+
+ // Basic details have validated - start the connection process animating
[connectProgressBar startAnimation:self];
[connectProgressStatusText setHidden:NO];
[connectProgressStatusText display];
-
- [selectedDatabase autorelease];
- selectedDatabase = nil;
-
- code = 0;
- if ( [[hostField stringValue] isEqualToString:@""] && [[socketField stringValue] isEqualToString:@""] ) {
- code = 4;
- } else {
- if ( ![[socketField stringValue] isEqualToString:@""] ) {
- //connect to socket
- mySQLConnection = [[CMMCPConnection alloc] initToSocket:[socketField stringValue]
- withLogin:[userField stringValue]
- password:[passwordField stringValue]];
- [hostField setStringValue:@"localhost"];
+
+ // If the password(s) are marked as having been originally sourced from a keychain, check whether they
+ // have been changed or not; if not, leave the mark in place and remove the password from the field
+ // for increased security.
+ if (connectionKeychainItemName) {
+ if ([[keyChainInstance getPasswordForName:connectionKeychainItemName account:connectionKeychainItemAccount] isEqualToString:[passwordField stringValue]]) {
+ [passwordField setStringValue:@""];
+ [[self undoManager] removeAllActionsWithTarget:passwordField];
} else {
- //connect to host
- mySQLConnection = [[CMMCPConnection alloc] initToHost:[hostField stringValue]
- withLogin:[userField stringValue]
- password:[passwordField stringValue]
- usingPort:[portField intValue]];
+ [connectionKeychainItemName release], connectionKeychainItemName = nil;
+ [connectionKeychainItemAccount release], connectionKeychainItemAccount = nil;
}
- [mySQLConnection setParentWindow:tableWindow];
-
- if ( ![mySQLConnection isConnected] )
- code = 2;
- if ( !code && ![[databaseField stringValue] isEqualToString:@""] ) {
- if ([mySQLConnection selectDB:[databaseField stringValue]]) {
- selectedDatabase = [[databaseField stringValue] retain];
- } else {
- code = 3;
- }
+ }
+ if (connectionSSHKeychainItemName) {
+ if ([[keyChainInstance getPasswordForName:connectionSSHKeychainItemName account:connectionSSHKeychainItemAccount] isEqualToString:[sshPasswordField stringValue]]) {
+ [sshPasswordField setStringValue:@""];
+ [[self undoManager] removeAllActionsWithTarget:sshPasswordField];
+ } else {
+ [connectionSSHKeychainItemName release], connectionSSHKeychainItemName = nil;
+ [connectionSSHKeychainItemAccount release], connectionSSHKeychainItemAccount = nil;
}
- if ( !code )
- code = 1;
}
-
- // close sheet
- [connectSheet orderOut:nil];
- [NSApp endSheet:connectSheet returnCode:code];
- [connectProgressBar stopAnimation:self];
- [connectProgressStatusText setHidden:YES];
+
+ // Initiate the SSH connection process if one has been set
+ if ([sshCheckbox state] == NSOnState) {
+ [self initiateSSHTunnelConnection];
+ return;
+ }
+
+ // ...or start the MySQL connection process directly
+ [self initiateMySQLConnection:nil];
}
--(void)connectSheetDidEnd:(NSWindow*)sheet returnCode:(int)code contextInfo:(void*)contextInfo
+/*
+ * Initiate the SSH connection process, while the connection sheet is still open.
+ * This should only be called as part of initiateConnection:, and will indirectly
+ * call initiateMySQLConnection if it's successful.
+ */
+- (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 intValue]?[sshPortField intValue]:22) login:[sshUserField stringValue] tunnellingToPort:([portField intValue]?[portField intValue]:3306) onHost:[hostField stringValue]];
+
+ // Add keychain or plaintext password as appropriate - note the checks in initiateConnection.
+ if (connectionSSHKeychainItemName) {
+ [theTunnel setPasswordKeychainName:connectionSSHKeychainItemName account:connectionSSHKeychainItemAccount];
+ } else {
+ [theTunnel setPassword:[sshPasswordField stringValue]];
+ }
+
+ // Set the callback function on the tunnel
+ [theTunnel 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];
+}
+
+/*
+ * 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
+{
+ int newState = [theTunnel state];
+
+ if (newState == SPSSH_STATE_IDLE) {
+ [self failConnectionWithErrorMessage:[theTunnel lastError]];
+ } else if (newState == SPSSH_STATE_CONNECTED) {
+ [self initiateMySQLConnection:theTunnel];
+ }
+}
+
+/*
+ * Set up the MySQL connection, either through a successful tunnel or directly.
+ */
+- (void)initiateMySQLConnection:(SPSSHTunnel *)theTunnel
{
- [sheet orderOut:self];
-
CMMCPResult *theResult;
id version;
-
- if ( code == 1) {
- //connected with success
- //register as delegate
- [mySQLConnection setDelegate:self];
- // set encoding
- NSString *encodingName = [prefs objectForKey:@"DefaultEncoding"];
- if ( [encodingName isEqualToString:@"Autodetect"] ) {
- [self setConnectionEncoding:[self databaseEncoding] reloadingViews:NO];
+
+ if (theTunnel)
+ [connectProgressStatusText setStringValue:NSLocalizedString(@"MySQL connecting...", @"MySQL connecting very short status message")];
+ else
+ [connectProgressStatusText setStringValue:NSLocalizedString(@"Connecting...", @"Generic connecting very short status message")];
+ [connectProgressStatusText display];
+
+ // Initialise to socket if appropriate.
+ // Note it is currently possible to connect to a socket with a useless SSH tunnel set
+ // up; this will be improved upon in future UI/code work.
+ if (![[socketField stringValue] isEqualToString:@""]) {
+ mySQLConnection = [[CMMCPConnection alloc] initToSocket:[socketField stringValue]
+ withLogin:[userField stringValue]];
+ [hostField setStringValue:@"localhost"];
+
+ // Otherwise, initialise to host, using tunnel if appropriate
+ } else {
+ if (theTunnel) {
+ mySQLConnection = [[CMMCPConnection alloc] initToHost:@"127.0.0.1"
+ withLogin:[userField stringValue]
+ usingPort:[theTunnel localPort]];
+ [mySQLConnection setSSHTunnel:theTunnel];
+ [theTunnel release];
} else {
- [self setConnectionEncoding:[self mysqlEncodingFromDisplayEncoding:encodingName] reloadingViews:NO];
+ mySQLConnection = [[CMMCPConnection alloc] initToHost:[hostField stringValue]
+ withLogin:[userField stringValue]
+ usingPort:[portField intValue]];
}
- //get mysql version
- theResult = [mySQLConnection queryString:@"SHOW VARIABLES LIKE 'version'"];
- version = [[theResult fetchRowAsArray] objectAtIndex:1];
- if ( [version isKindOfClass:[NSData class]] ) {
- // starting with MySQL 4.1.14 the mysql variables are returned as nsdata
- mySQLVersion = [[NSString alloc] initWithData:version encoding:[mySQLConnection encoding]];
+ }
+ [mySQLConnection setParentWindow:tableWindow];
+
+ // Set the password as appropriate
+ if (connectionKeychainItemName) {
+ [mySQLConnection setPasswordKeychainName:connectionKeychainItemName account:connectionKeychainItemAccount];
+ } else {
+ [mySQLConnection setPassword:[passwordField stringValue]];
+ }
+
+ // Connect
+ [mySQLConnection connect];
+
+ 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:@""]) {
+ if ([mySQLConnection selectDB:[databaseField stringValue]]) {
+ if (selectedDatabase) [selectedDatabase release], selectedDatabase = nil;
+ selectedDatabase = [[databaseField stringValue] retain];
} else {
- mySQLVersion = [[NSString stringWithString:version] retain];
+ [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;
}
-
- [self setDatabases:self];
-
- // For each of the main controllers assigned the current connection
- [tablesListInstance setConnection:mySQLConnection];
- [tableSourceInstance setConnection:mySQLConnection];
- [tableContentInstance setConnection:mySQLConnection];
- [tableRelationsInstance setConnection:mySQLConnection];
- [customQueryInstance setConnection:mySQLConnection];
- [tableDumpInstance setConnection:mySQLConnection];
- [spExportControllerInstance setConnection:mySQLConnection];
- [tableDataInstance setConnection:mySQLConnection];
- [extendedTableInfoInstance setConnection:mySQLConnection];
- [databaseDataInstance setConnection:mySQLConnection];
-
- // Set the cutom query editor's MySQL version
- [customQueryInstance setMySQLversion:mySQLVersion];
-
- [self setFileName:[NSString stringWithFormat:@"(MySQL %@) %@@%@ %@", mySQLVersion, [userField stringValue],
- [hostField stringValue], [databaseField stringValue]]];
- [tableWindow setTitle:[NSString stringWithFormat:@"(MySQL %@) %@/%@", mySQLVersion, [self name], [databaseField stringValue]]];
-
- // Connected Growl notification
- [[SPGrowlController sharedGrowlController] notifyWithTitle:@"Connected"
- description:[NSString stringWithFormat:NSLocalizedString(@"Connected to %@",@"description for connected growl notification"), [tableWindow title]]
- notificationName:@"Connected"];
-
- } else if (code == 2) {
- //can't connect to host
- NSBeginAlertSheet(NSLocalizedString(@"Connection failed!", @"connection failed"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil,
- @selector(sheetDidEnd:returnCode:contextInfo:), @"connect",
- [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]]);
- } else if (code == 3) {
- //can't connect to db
- NSBeginAlertSheet(NSLocalizedString(@"Connection failed!", @"connection failed"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil,
- @selector(sheetDidEnd:returnCode:contextInfo:), @"connect",
- [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]]);
- } else if (code == 4) {
- //no host is given
- NSBeginAlertSheet(NSLocalizedString(@"Insufficient connection details", @"insufficient details message"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil,
- @selector(sheetDidEnd:returnCode:contextInfo:), @"connect", NSLocalizedString(@"Insufficient details provided to establish a connection. Please provide at least a host or socket.", @"insufficient details informative message"));
}
+ // Successful connection! Close the connection sheet
+ [connectSheet orderOut:nil];
+ [NSApp endSheet:connectSheet];
+ [connectProgressBar stopAnimation:self];
+ [connectProgressStatusText setHidden:YES];
+
+ // Set up the connection.
+ // Register as a delegate
+ [mySQLConnection setDelegate:self];
+
+ // Set encoding
+ NSString *encodingName = [prefs objectForKey:@"DefaultEncoding"];
+ if ( [encodingName isEqualToString:@"Autodetect"] ) {
+ [self setConnectionEncoding:[self databaseEncoding] reloadingViews:NO];
+ } else {
+ [self setConnectionEncoding:[self mysqlEncodingFromDisplayEncoding:encodingName] reloadingViews:NO];
+ }
+
+ // Get the mysql version
+ theResult = [mySQLConnection queryString:@"SHOW VARIABLES LIKE 'version'"];
+ version = [[theResult fetchRowAsArray] objectAtIndex:1];
+ if ( [version isKindOfClass:[NSData class]] ) {
+ // starting with MySQL 4.1.14 the mysql variables are returned as nsdata
+ mySQLVersion = [[NSString alloc] initWithData:version encoding:[mySQLConnection encoding]];
+ } else {
+ mySQLVersion = [[NSString stringWithString:version] retain];
+ }
+
+ [self setDatabases:self];
+
+ // For each of the main controllers assign the current connection
+ [tablesListInstance setConnection:mySQLConnection];
+ [tableSourceInstance setConnection:mySQLConnection];
+ [tableContentInstance setConnection:mySQLConnection];
+ [tableRelationsInstance setConnection:mySQLConnection];
+ [customQueryInstance setConnection:mySQLConnection];
+ [tableDumpInstance setConnection:mySQLConnection];
+ [spExportControllerInstance setConnection:mySQLConnection];
+ [tableDataInstance setConnection:mySQLConnection];
+ [extendedTableInfoInstance setConnection:mySQLConnection];
+ [databaseDataInstance setConnection:mySQLConnection];
+
+ // Set the cutom query editor's MySQL version
+ [customQueryInstance setMySQLversion:mySQLVersion];
+
+ [self setFileName:[NSString stringWithFormat:@"(MySQL %@) %@@%@ %@", mySQLVersion, [userField stringValue],
+ [hostField stringValue], [databaseField stringValue]]];
+ [tableWindow setTitle:[NSString stringWithFormat:@"(MySQL %@) %@/%@", mySQLVersion, [self name], [databaseField stringValue]]];
+
+ // Connected Growl notification
+ [[SPGrowlController sharedGrowlController] notifyWithTitle:@"Connected"
+ description:[NSString stringWithFormat:NSLocalizedString(@"Connected to %@",@"description for connected growl notification"), [tableWindow title]]
+ notificationName:@"Connected"];
+}
+
+/*
+ * Ends a connection attempt by stopping the connect sheet animation,
+ * stopping the document-modal sheet, and displaying a specified error
+ * message. The button on the error message will open the connection
+ * sheet again with the failed details.
+ */
+- (void)failConnectionWithErrorMessage:(NSString *)theErrorMessage
+{
+ // Clean up the interface
+ [connectProgressBar stopAnimation:self];
+ [connectProgressBar display];
+ [connectProgressStatusText setHidden:YES];
+ [connectProgressStatusText display];
+
+ // Stop the modal sheet
+ [connectSheet orderOut:nil];
+ [NSApp endSheet:connectSheet];
+
+ // 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);
}
- (IBAction)cancelConnectSheet:(id)sender
@@ -411,18 +529,69 @@ NSString *TableDocumentFavoritesControllerSelectionIndexDidChange = @"TableDocum
if (![self selectedFavorite])
return;
- [nameField setStringValue:([self valueForKeyPath:@"selectedFavorite.name"] ? [self valueForKeyPath:@"selectedFavorite.name"] : @"")];
- [hostField setStringValue:([self valueForKeyPath:@"selectedFavorite.host"] ? [self valueForKeyPath:@"selectedFavorite.host"] : @"")];
- [userField setStringValue:([self valueForKeyPath:@"selectedFavorite.user"] ? [self valueForKeyPath:@"selectedFavorite.user"] : @"")];
- [passwordField setStringValue:([self selectedFavoritePassword] ? [self selectedFavoritePassword] : @"")];
- [databaseField setStringValue:([self valueForKeyPath:@"selectedFavorite.database"] ? [self valueForKeyPath:@"selectedFavorite.database"] : @"")];
- [socketField setStringValue:([self valueForKeyPath:@"selectedFavorite.socket"] ? [self valueForKeyPath:@"selectedFavorite.socket"] : @"")];
- [portField setStringValue:([self valueForKeyPath:@"selectedFavorite.port"] ? [self valueForKeyPath:@"selectedFavorite.port"] : @"")];
+ if (connectionKeychainItemName) [connectionKeychainItemName release], connectionKeychainItemName = nil;
+ if (connectionKeychainItemAccount) [connectionKeychainItemAccount release], connectionKeychainItemAccount = nil;
+ if (connectionSSHKeychainItemName) [connectionSSHKeychainItemName release], connectionSSHKeychainItemName = nil;
+ if (connectionSSHKeychainItemAccount) [connectionSSHKeychainItemAccount release], connectionSSHKeychainItemAccount = nil;
+
+ [nameField setStringValue:([self valueForKeyPath:@"selectedFavorite.name"] ? [self valueForKeyPath:@"selectedFavorite.name"] : @"")];
+ [hostField setStringValue:([self valueForKeyPath:@"selectedFavorite.host"] ? [self valueForKeyPath:@"selectedFavorite.host"] : @"")];
+ [socketField setStringValue:([self valueForKeyPath:@"selectedFavorite.socket"] ? [self valueForKeyPath:@"selectedFavorite.socket"] : @"")];
+ [userField setStringValue:([self valueForKeyPath:@"selectedFavorite.user"] ? [self valueForKeyPath:@"selectedFavorite.user"] : @"")];
+ [portField setStringValue:([self valueForKeyPath:@"selectedFavorite.port"] ? [self valueForKeyPath:@"selectedFavorite.port"] : @"")];
+ [databaseField setStringValue:([self valueForKeyPath:@"selectedFavorite.database"] ? [self valueForKeyPath:@"selectedFavorite.database"] : @"")];
+ [sshCheckbox setState:([self valueForKeyPath:@"selectedFavorite.useSSH"] ? ([[self valueForKeyPath:@"selectedFavorite.useSSH"] boolValue]?NSOnState:NSOffState) : NSOffState)];
+ [self toggleUseSSH:self];
+ [sshHostField setStringValue:([self valueForKeyPath:@"selectedFavorite.sshHost"] ? [self valueForKeyPath:@"selectedFavorite.sshHost"] : @"")];
+ [sshUserField setStringValue:([self valueForKeyPath:@"selectedFavorite.sshUser"] ? [self valueForKeyPath:@"selectedFavorite.sshUser"] : @"")];
+ [sshPortField setStringValue:([self valueForKeyPath:@"selectedFavorite.sshPort"] ? [self valueForKeyPath:@"selectedFavorite.sshPort"] : @"")];
+
+ // 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];
+ if ([keyChainInstance passwordExistsForName:connectionKeychainItemName account:connectionKeychainItemAccount]) {
+ [passwordField setStringValue:[keyChainInstance getPasswordForName:connectionKeychainItemName account:connectionKeychainItemAccount]];
+ } else {
+ [connectionKeychainItemName release], connectionKeychainItemName = nil;
+ [connectionKeychainItemAccount release], connectionKeychainItemAccount = nil;
+ [passwordField setStringValue:@""];
+ }
+
+ // 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];
+ if ([keyChainInstance passwordExistsForName:connectionSSHKeychainItemName account:connectionSSHKeychainItemAccount]) {
+ [sshPasswordField setStringValue:[keyChainInstance getPasswordForName:connectionSSHKeychainItemName account:connectionSSHKeychainItemAccount]];
+ } else {
+ [connectionSSHKeychainItemName release], connectionSSHKeychainItemName = nil;
+ [connectionSSHKeychainItemAccount release], connectionSSHKeychainItemAccount = nil;
+ [sshPasswordField setStringValue:@""];
+ }
[prefs setInteger:[favoritesController selectionIndex] forKey:@"LastFavoriteIndex"];
}
/**
+ * Updates the interface when the "Use SSH Tunnel" checkbox is ticked/unticked
+ */
+- (IBAction)toggleUseSSH:(id)sender
+{
+ BOOL sshIsEnabledValue = ([sshCheckbox state] == NSOnState);
+ [sshHostField setEnabled:sshIsEnabledValue];
+ [sshUserField setEnabled:sshIsEnabledValue];
+ [sshPasswordField setEnabled:sshIsEnabledValue];
+ [sshPortField setEnabled:sshIsEnabledValue];
+
+ if (sender == sshCheckbox) [favoritesController setSelectionIndexes:[NSIndexSet indexSet]];
+}
+
+/**
* Opens the preferences window, or brings it to the front, and switch to the favorites tab.
* If a favorite is selected in the connection sheet, it is also select in the prefs window.
*/
@@ -448,23 +617,6 @@ NSString *TableDocumentFavoritesControllerSelectionIndexDidChange = @"TableDocum
return [favoritesController selection];
}
-/**
- * fetches the password [self selectedFavorite] from the keychain, returns nil if no selection.
- */
-- (NSString *)selectedFavoritePassword
-{
- if (![self selectedFavorite])
- return nil;
-
- NSString *keychainName = [NSString stringWithFormat:@"Sequel Pro : %@ (%i)", [self valueForKeyPath:@"selectedFavorite.name"], [[self valueForKeyPath:@"selectedFavorite.id"] intValue]];
- NSString *keychainAccount = [NSString stringWithFormat:@"%@@%@/%@",
- [self valueForKeyPath:@"selectedFavorite.user"],
- [self valueForKeyPath:@"selectedFavorite.host"],
- [self valueForKeyPath:@"selectedFavorite.database"]];
-
- return [keyChainInstance getPasswordForName:keychainName account:keychainAccount];
-}
-
- (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:@""];
@@ -518,6 +670,15 @@ NSString *TableDocumentFavoritesControllerSelectionIndexDidChange = @"TableDocum
{
if ([contextInfo isEqualToString:@"connect"]) {
[sheet orderOut:self];
+
+ // Restore the passwords from keychain for editing if appropriate
+ if (connectionKeychainItemName) {
+ [passwordField setStringValue:[keyChainInstance getPasswordForName:connectionKeychainItemName account:connectionKeychainItemAccount]];
+ }
+ if (connectionSSHKeychainItemName) {
+ [sshPasswordField setStringValue:[keyChainInstance getPasswordForName:connectionSSHKeychainItemName account:connectionSSHKeychainItemAccount]];
+ }
+
[self connectToDB:nil];
return;
}
@@ -2039,7 +2200,9 @@ NSString *TableDocumentFavoritesControllerSelectionIndexDidChange = @"TableDocum
if ([aNotification object] == nameField || [aNotification object] == hostField
|| [aNotification object] == userField || [aNotification object] == passwordField
|| [aNotification object] == databaseField || [aNotification object] == socketField
- || [aNotification object] == portField) {
+ || [aNotification object] == portField || [aNotification object] == sshHostField
+ || [aNotification object] == sshUserField || [aNotification object] == sshPasswordField
+ || [aNotification object] == sshPortField) {
[favoritesController setSelectionIndexes:[NSIndexSet indexSet]];
}
else if ([aNotification object] == databaseNameField) {
@@ -2047,6 +2210,7 @@ NSString *TableDocumentFavoritesControllerSelectionIndexDidChange = @"TableDocum
}
}
+
#pragma mark SplitView delegate methods
/**
diff --git a/Source/TunnelPassphraseRequester.m b/Source/TunnelPassphraseRequester.m
new file mode 100644
index 00000000..a39c4fd1
--- /dev/null
+++ b/Source/TunnelPassphraseRequester.m
@@ -0,0 +1,96 @@
+//
+// TunnelPassphraseRequester.m
+// sequel-pro
+//
+// Created by Rowan Beentje on May 4, 2009.
+//
+// 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 <Cocoa/Cocoa.h>
+#import "KeyChain.h"
+#import "SPSSHTunnel.h"
+
+int main(int argc, const char *argv[])
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ NSDictionary *environment = [[NSProcessInfo processInfo] environment];
+
+ if (![environment objectForKey:@"SP_PASSWORD_METHOD"]) {
+ [pool release];
+ return 1;
+ }
+
+ // 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) {
+ KeyChain *keychain;
+ NSString *keychainName = [environment objectForKey:@"SP_KEYCHAIN_ITEM_NAME"];
+ NSString *keychainAccount = [environment objectForKey:@"SP_KEYCHAIN_ITEM_ACCOUNT"];
+
+ if (!keychainName || !keychainAccount) {
+ NSLog(@"SSH Tunnel: keychain authentication specified but insufficient internal details supplied");
+ [pool release];
+ return 1;
+ }
+
+ keychain = [[KeyChain alloc] init];
+ if (![keychain passwordExistsForName:keychainName account:keychainAccount]) {
+ NSLog(@"SSH Tunnel: specified keychain password not found");
+ [pool release];
+ return 1;
+ }
+
+ printf("%s\n", [[keychain getPasswordForName:keychainName account:keychainAccount] UTF8String]);
+ [pool release];
+ return 0;
+ }
+
+ // 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) {
+ NSLog(@"SSH Tunnel: internal authentication specified but insufficient details supplied");
+ [pool release];
+ return 1;
+ }
+
+ sequelProTunnel = (SPSSHTunnel *)[NSConnection rootProxyForConnectionWithRegisteredName:connectionName host:nil];
+ if (!sequelProTunnel) {
+ NSLog(@"SSH Tunnel: unable to connect to Sequel Pro for internal authentication");
+ [pool release];
+ return 1;
+ }
+
+ password = [sequelProTunnel getPasswordWithVerificationHash:verificationHash];
+ if (!password) {
+ NSLog(@"SSH Tunnel: unable to successfully request password from Sequel Pro for internal authentication");
+ [pool release];
+ return 1;
+ }
+
+ printf("%s\n", [password UTF8String]);
+ [pool release];
+ return 0;
+ }
+
+ [pool release];
+ return 1;
+} \ No newline at end of file
diff --git a/sequel-pro.xcodeproj/project.pbxproj b/sequel-pro.xcodeproj/project.pbxproj
index e9f463d4..b75008dd 100644
--- a/sequel-pro.xcodeproj/project.pbxproj
+++ b/sequel-pro.xcodeproj/project.pbxproj
@@ -36,7 +36,6 @@
17E641820EF01FA8001BC333 /* CMCopyTable.m in Sources */ = {isa = PBXBuildFile; fileRef = 17E6417D0EF01FA8001BC333 /* CMCopyTable.m */; };
17E641830EF01FA8001BC333 /* CMImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 17E6417F0EF01FA8001BC333 /* CMImageView.m */; };
17E641840EF01FA8001BC333 /* CMTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 17E641810EF01FA8001BC333 /* CMTextView.m */; };
- 17E641890EF01FB4001BC333 /* SSHTunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = 17E641860EF01FB4001BC333 /* SSHTunnel.m */; };
17E641C00EF02036001BC333 /* appicon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 17E6418C0EF02036001BC333 /* appicon.icns */; };
17E641C10EF02036001BC333 /* clearconsole.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 17E6418D0EF02036001BC333 /* clearconsole.tiff */; };
17E641D10EF02036001BC333 /* grabber-horizontal.png in Resources */ = {isa = PBXBuildFile; fileRef = 17E6419D0EF02036001BC333 /* grabber-horizontal.png */; };
@@ -89,6 +88,11 @@
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 */; };
+ 58CDB3300FCE138D00F8ACA3 /* SPSSHTunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = 58CDB32F0FCE138D00F8ACA3 /* SPSSHTunnel.m */; };
+ 58CDB33C0FCE13E200F8ACA3 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 58CDB33B0FCE13E200F8ACA3 /* Cocoa.framework */; };
+ 58CDB3400FCE13EF00F8ACA3 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B5EAC0FC0EC87FF900CC579C /* Security.framework */; };
+ 58CDB3410FCE141900F8ACA3 /* TunnelPassphraseRequester.m in Sources */ = {isa = PBXBuildFile; fileRef = 58CDB3310FCE139C00F8ACA3 /* TunnelPassphraseRequester.m */; };
+ 58CDB3420FCE142500F8ACA3 /* KeyChain.m in Sources */ = {isa = PBXBuildFile; fileRef = 17E641740EF01F80001BC333 /* KeyChain.m */; };
58FEF16D0F23D66600518E8E /* SPSQLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 58FEF16C0F23D66600518E8E /* SPSQLParser.m */; };
58FEF57E0F3B4E9700518E8E /* SPTableData.m in Sources */ = {isa = PBXBuildFile; fileRef = 58FEF57D0F3B4E9700518E8E /* SPTableData.m */; };
8D15AC340486D014006FF6A4 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A7FEA54F5311CA2CBB /* Cocoa.framework */; };
@@ -148,6 +152,13 @@
remoteGlobalIDString = 8D15AC270486D014006FF6A4;
remoteInfo = "Sequel Pro";
};
+ 58CDB34A0FCE144000F8ACA3 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 2A37F4A9FDCFA73011CA2CEA /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 58CDB3350FCE13C900F8ACA3 /* TunnelPassphraseRequester */;
+ remoteInfo = TunnelPassphraseRequester;
+ };
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -245,8 +256,6 @@
17E6417F0EF01FA8001BC333 /* CMImageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CMImageView.m; sourceTree = "<group>"; };
17E641800EF01FA8001BC333 /* CMTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMTextView.h; sourceTree = "<group>"; };
17E641810EF01FA8001BC333 /* CMTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CMTextView.m; sourceTree = "<group>"; };
- 17E641850EF01FB4001BC333 /* SSHTunnel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SSHTunnel.h; sourceTree = "<group>"; };
- 17E641860EF01FB4001BC333 /* SSHTunnel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SSHTunnel.m; sourceTree = "<group>"; };
17E6418C0EF02036001BC333 /* appicon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = appicon.icns; sourceTree = "<group>"; };
17E6418D0EF02036001BC333 /* clearconsole.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = clearconsole.tiff; sourceTree = "<group>"; };
17E6419D0EF02036001BC333 /* grabber-horizontal.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "grabber-horizontal.png"; sourceTree = "<group>"; };
@@ -315,6 +324,11 @@
58C56EF30F438E120035701E /* SPDataCellFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPDataCellFormatter.h; sourceTree = "<group>"; };
58C56EF40F438E120035701E /* SPDataCellFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPDataCellFormatter.m; sourceTree = "<group>"; };
58CB20EC0F79A75D005EA204 /* button_edit_mode_selected.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = button_edit_mode_selected.tiff; sourceTree = "<group>"; };
+ 58CDB32E0FCE138D00F8ACA3 /* SPSSHTunnel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPSSHTunnel.h; sourceTree = "<group>"; };
+ 58CDB32F0FCE138D00F8ACA3 /* SPSSHTunnel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPSSHTunnel.m; sourceTree = "<group>"; };
+ 58CDB3310FCE139C00F8ACA3 /* TunnelPassphraseRequester.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TunnelPassphraseRequester.m; sourceTree = "<group>"; };
+ 58CDB3360FCE13C900F8ACA3 /* TunnelPassphraseRequester */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; name = TunnelPassphraseRequester; path = build/Debug/TunnelPassphraseRequester; sourceTree = "<group>"; };
+ 58CDB33B0FCE13E200F8ACA3 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
58FEF16B0F23D66600518E8E /* SPSQLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPSQLParser.h; sourceTree = "<group>"; };
58FEF16C0F23D66600518E8E /* SPSQLParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPSQLParser.m; sourceTree = "<group>"; };
58FEF57C0F3B4E9700518E8E /* SPTableData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPTableData.h; sourceTree = "<group>"; };
@@ -384,6 +398,15 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 58CDB3340FCE13C900F8ACA3 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 58CDB33C0FCE13E200F8ACA3 /* Cocoa.framework in Frameworks */,
+ 58CDB3400FCE13EF00F8ACA3 /* Security.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
8D15AC330486D014006FF6A4 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -647,8 +670,9 @@
17E641720EF01F6B001BC333 /* SSHTunnel */ = {
isa = PBXGroup;
children = (
- 17E641850EF01FB4001BC333 /* SSHTunnel.h */,
- 17E641860EF01FB4001BC333 /* SSHTunnel.m */,
+ 58CDB32E0FCE138D00F8ACA3 /* SPSSHTunnel.h */,
+ 58CDB32F0FCE138D00F8ACA3 /* SPSSHTunnel.m */,
+ 58CDB3310FCE139C00F8ACA3 /* TunnelPassphraseRequester.m */,
);
name = SSHTunnel;
sourceTree = "<group>";
@@ -734,6 +758,7 @@
children = (
8D15AC370486D014006FF6A4 /* Sequel Pro.app */,
380F4ED90FC0B50500B0BFD7 /* Unit Tests.octest */,
+ 58CDB3360FCE13C900F8ACA3 /* TunnelPassphraseRequester */,
);
name = Products;
sourceTree = "<group>";
@@ -772,6 +797,7 @@
1761FD9C0EF0486A00331368 /* Scripts */,
2A37F4C3FDCFA73011CA2CEA /* Frameworks */,
19C28FB0FE9D524F11CA2CBB /* Products */,
+ 58CDB33B0FCE13E200F8ACA3 /* Cocoa.framework */,
);
name = "sequel-pro";
sourceTree = "<group>";
@@ -846,6 +872,22 @@
productReference = 380F4ED90FC0B50500B0BFD7 /* Unit Tests.octest */;
productType = "com.apple.product-type.bundle";
};
+ 58CDB3350FCE13C900F8ACA3 /* TunnelPassphraseRequester */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 58CDB33F0FCE13E300F8ACA3 /* Build configuration list for PBXNativeTarget "TunnelPassphraseRequester" */;
+ buildPhases = (
+ 58CDB3330FCE13C900F8ACA3 /* Sources */,
+ 58CDB3340FCE13C900F8ACA3 /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = TunnelPassphraseRequester;
+ productName = TunnelPassphraseRequester;
+ productReference = 58CDB3360FCE13C900F8ACA3 /* TunnelPassphraseRequester */;
+ productType = "com.apple.product-type.tool";
+ };
8D15AC270486D014006FF6A4 /* Sequel Pro */ = {
isa = PBXNativeTarget;
buildConfigurationList = C05733C708A9546B00998B17 /* Build configuration list for PBXNativeTarget "Sequel Pro" */;
@@ -859,6 +901,7 @@
buildRules = (
);
dependencies = (
+ 58CDB34B0FCE144000F8ACA3 /* PBXTargetDependency */,
);
name = "Sequel Pro";
productInstallPath = "$(HOME)/Applications";
@@ -880,6 +923,7 @@
targets = (
8D15AC270486D014006FF6A4 /* Sequel Pro */,
380F4ED80FC0B50500B0BFD7 /* Unit Tests */,
+ 58CDB3350FCE13C900F8ACA3 /* TunnelPassphraseRequester */,
);
};
/* End PBXProject section */
@@ -987,7 +1031,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "# Add build/bundle version\n\"${SRCROOT}/Scripts/build-version.pl\"\n\n# Trim application if release or dist build\nif test \"$CONFIGURATION\" == 'Release' || test \"$CONFIGURATION\" == 'Distribution'\nthen\n\t\"${SRCROOT}/Scripts/trim-application.sh\" -p \"${BUILT_PRODUCTS_DIR}/${TARGET_NAME}${WRAPPER_SUFFIX}\" -a\nfi\n\n# Perform distribution tasks on a dist build\nif [ \"$CONFIGURATION\" == 'Distribution' ]\nthen\n\t\"${SRCROOT}/Scripts/package-application.sh\" -p \"${BUILT_PRODUCTS_DIR}/${TARGET_NAME}${WRAPPER_SUFFIX}\"\nfi";
+ shellScript = "# Add build/bundle version\n\"${SRCROOT}/Scripts/build-version.pl\"\n\n# Copy the TunnelPassphraseRequester into the resources dir\ncp -f \"${BUILT_PRODUCTS_DIR}/TunnelPassphraseRequester\" \"${BUILT_PRODUCTS_DIR}/${TARGET_NAME}${WRAPPER_SUFFIX}/Contents/Resources\"\n\n# Trim application if release or dist build\nif test \"$CONFIGURATION\" == 'Release' || test \"$CONFIGURATION\" == 'Distribution'\nthen\n\t\"${SRCROOT}/Scripts/trim-application.sh\" -p \"${BUILT_PRODUCTS_DIR}/${TARGET_NAME}${WRAPPER_SUFFIX}\" -a\nfi\n\n# Perform distribution tasks on a dist build\nif [ \"$CONFIGURATION\" == 'Distribution' ]\nthen\n\t\"${SRCROOT}/Scripts/package-application.sh\" -p \"${BUILT_PRODUCTS_DIR}/${TARGET_NAME}${WRAPPER_SUFFIX}\"\nfi";
};
380F4ED70FC0B50500B0BFD7 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
@@ -1014,6 +1058,15 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 58CDB3330FCE13C900F8ACA3 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 58CDB3410FCE141900F8ACA3 /* TunnelPassphraseRequester.m in Sources */,
+ 58CDB3420FCE142500F8ACA3 /* KeyChain.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
8D15AC300486D014006FF6A4 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -1036,7 +1089,6 @@
17E641820EF01FA8001BC333 /* CMCopyTable.m in Sources */,
17E641830EF01FA8001BC333 /* CMImageView.m in Sources */,
17E641840EF01FA8001BC333 /* CMTextView.m in Sources */,
- 17E641890EF01FB4001BC333 /* SSHTunnel.m in Sources */,
58FEF16D0F23D66600518E8E /* SPSQLParser.m in Sources */,
1789343C0F30C1DD0097539A /* SPStringAdditions.m in Sources */,
58FEF57E0F3B4E9700518E8E /* SPTableData.m in Sources */,
@@ -1063,6 +1115,7 @@
1740FABB0FC4372F00CF3699 /* SPDatabaseData.m in Sources */,
17C058880FC9FC390077E9CF /* SPNarrowDownCompletion.m in Sources */,
177E7A230FCB6A2E00E9E122 /* SPExtendedTableInfo.m in Sources */,
+ 58CDB3300FCE138D00F8ACA3 /* SPSSHTunnel.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1074,6 +1127,11 @@
target = 8D15AC270486D014006FF6A4 /* Sequel Pro */;
targetProxy = 380F4EDF0FC0B51D00B0BFD7 /* PBXContainerItemProxy */;
};
+ 58CDB34B0FCE144000F8ACA3 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 58CDB3350FCE13C900F8ACA3 /* TunnelPassphraseRequester */;
+ targetProxy = 58CDB34A0FCE144000F8ACA3 /* PBXContainerItemProxy */;
+ };
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
@@ -1274,6 +1332,72 @@
};
name = Distribution;
};
+ 58CDB3380FCE13CB00F8ACA3 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h";
+ INSTALL_PATH = /usr/local/bin;
+ OTHER_LDFLAGS = (
+ "-framework",
+ Foundation,
+ "-framework",
+ AppKit,
+ );
+ PREBINDING = NO;
+ PRODUCT_NAME = TunnelPassphraseRequester;
+ };
+ name = Debug;
+ };
+ 58CDB3390FCE13CB00F8ACA3 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ GCC_ENABLE_FIX_AND_CONTINUE = NO;
+ GCC_MODEL_TUNING = G5;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h";
+ INSTALL_PATH = /usr/local/bin;
+ OTHER_LDFLAGS = (
+ "-framework",
+ Foundation,
+ "-framework",
+ AppKit,
+ );
+ PREBINDING = NO;
+ PRODUCT_NAME = TunnelPassphraseRequester;
+ ZERO_LINK = NO;
+ };
+ name = Release;
+ };
+ 58CDB33A0FCE13CB00F8ACA3 /* Distribution */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h";
+ INSTALL_PATH = /usr/local/bin;
+ OTHER_LDFLAGS = (
+ "-framework",
+ Foundation,
+ "-framework",
+ AppKit,
+ );
+ PREBINDING = NO;
+ PRODUCT_NAME = TunnelPassphraseRequester;
+ };
+ name = Distribution;
+ };
C05733C808A9546B00998B17 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -1360,6 +1484,16 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ 58CDB33F0FCE13E300F8ACA3 /* Build configuration list for PBXNativeTarget "TunnelPassphraseRequester" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 58CDB3380FCE13CB00F8ACA3 /* Debug */,
+ 58CDB3390FCE13CB00F8ACA3 /* Release */,
+ 58CDB33A0FCE13CB00F8ACA3 /* Distribution */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
C05733C708A9546B00998B17 /* Build configuration list for PBXNativeTarget "Sequel Pro" */ = {
isa = XCConfigurationList;
buildConfigurations = (