diff options
-rw-r--r-- | Interfaces/English.lproj/DBView.xib | 771 | ||||
-rw-r--r-- | Interfaces/English.lproj/Preferences.xib | 142 | ||||
-rw-r--r-- | Source/CMMCPConnection.h | 17 | ||||
-rw-r--r-- | Source/CMMCPConnection.m | 251 | ||||
-rw-r--r-- | Source/KeyChain.m | 56 | ||||
-rw-r--r-- | Source/SPPreferenceController.h | 4 | ||||
-rw-r--r-- | Source/SPPreferenceController.m | 115 | ||||
-rw-r--r-- | Source/SPSSHTunnel.h | 54 | ||||
-rw-r--r-- | Source/SPSSHTunnel.m | 359 | ||||
-rw-r--r-- | Source/SSHTunnel.h | 57 | ||||
-rw-r--r-- | Source/SSHTunnel.m | 531 | ||||
-rw-r--r-- | Source/TableDocument.h | 21 | ||||
-rw-r--r-- | Source/TableDocument.m | 438 | ||||
-rw-r--r-- | Source/TunnelPassphraseRequester.m | 96 | ||||
-rw-r--r-- | sequel-pro.xcodeproj/project.pbxproj | 148 |
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 = ( |