99
1010#include < ApplicationServices/ApplicationServices.h>
1111#import < AppKit/AppKit.h>
12-
13- #define OKBUTTONWIDTH 100.0
14- #define OKBUTTONHEIGHT 24.0
15- #define CANCELBUTTONWIDTH 100.0
16- #define CANCELBUTTONHEIGHT 24.0
17- #define PASSHEIGHT 22.0
18- #define PASSLABELHEIGHT 16.0
19- #define WINDOWAUTOSAVENAME @" GitXAskPasswordWindowFrame"
20-
21-
22- @interface GAPAppDelegate : NSObject
12+ #include < errno.h>
13+ #include < string.h>
14+ #include < sys/sysctl.h>
15+ #include < Security/Security.h>
16+ #include < CoreServices/CoreServices.h>
17+ #include < Security/SecKeychain.h>
18+ #include < Security/SecKeychainItem.h>
19+ #include < Security/SecAccess.h>
20+ #include < Security/SecTrustedApplication.h>
21+ #include < Security/SecACL.h>
22+ #include < CoreFoundation/CoreFoundation.h>
23+
24+ #define OKBUTTONWIDTH 100.0
25+ #define OKBUTTONHEIGHT 24.0
26+ #define CANCELBUTTONWIDTH 100.0
27+ #define CANCELBUTTONHEIGHT 24.0
28+ #define PASSHEIGHT 22.0
29+ #define PASSLABELHEIGHT 16.0
30+ #define WINDOWAUTOSAVENAME @" GitXAskPasswordWindowFrame"
31+
32+
33+ @interface GAPAppDelegate : NSObject /* <NSApplicationDelegate>*/
2334{
24- NSPanel * mPasswordPanel;
25- NSSecureTextField * mPasswordField;
2635}
2736
28- -( NSPanel *) passwordPanel ;
37+ @end
2938
30- -( IBAction ) doOKButton : ( id ) sender ;
31- -( IBAction ) doCancelButton : ( id ) sender ;
39+ NSString * url ;
40+ OSStatus StorePasswordKeychain ( const char *url, UInt32 urlLength, void * password,UInt32 passwordLength) ;
3241
33- @end
3442
43+ @implementation GAPAppDelegate
3544
36- @implementation GAPAppDelegate
45+ -(void )yesNo : (NSString *)prompt url : (NSString *)url {
46+ NSAlert *alert = [[NSAlert alloc ] init ];
47+ [alert addButtonWithTitle: @" YES" ];
48+ [alert addButtonWithTitle: @" NO" ];
49+ [alert setMessageText: [NSString stringWithFormat: @" %@ ?" ,url]];
50+ [alert setInformativeText: prompt];
51+ [alert setAlertStyle: NSWarningAlertStyle ];
52+ NSInteger result = [alert runModal ];
53+
54+ Boolean yes=NO ;
55+ if ( result == NSAlertFirstButtonReturn ) {
56+ yes=YES ;
57+ }
58+ [alert release ];
59+ printf (" %s " ,yes?" yes" :" no" );
60+ }
3761
38- -(NSPanel *) passwordPanel
39- {
40- if ( !mPasswordPanel )
41- {
42- NSRect box = NSMakeRect ( 100 , 100 , 400 , 134 );
43- mPasswordPanel = [[NSPanel alloc ] initWithContentRect: box
44- styleMask: NSTitledWindowMask
45- backing: NSBackingStoreBuffered defer: NO ];
46- [mPasswordPanel setHidesOnDeactivate: NO ];
47- [mPasswordPanel setLevel: NSFloatingWindowLevel ];
48- [mPasswordPanel setTitle: @" GitX SSH Remote Login" ];
49- if (![mPasswordPanel setFrameUsingName: WINDOWAUTOSAVENAME]) {
50- [mPasswordPanel center ];
51- [mPasswordPanel setFrameAutosaveName: WINDOWAUTOSAVENAME];
62+
63+ - (void )pasword : (NSString *)prompt url : (NSString *)url {
64+
65+ NSRect box = NSMakeRect (0 , 0 , 200 , 24 );
66+
67+ NSSecureTextField * passView = [[NSSecureTextField alloc ] initWithFrame: box];
68+ [passView setSelectable: YES ];
69+ [passView setEditable: YES ];
70+ [passView setBordered: YES ];
71+ [passView setBezeled: YES ];
72+ [passView setBezelStyle: NSTextFieldSquareBezel];
73+ [passView selectText: self ];
74+
75+
76+ NSAlert *alert = [[NSAlert alloc ] init ];
77+ [alert addButtonWithTitle: @" Ok" ];
78+ [alert addButtonWithTitle: @" cancel" ];
79+ [alert setMessageText: [NSString stringWithFormat: @" %@ ?" ,url]];
80+ [alert setInformativeText: prompt];
81+ [alert setAlertStyle: NSWarningAlertStyle ];
82+ [alert setAccessoryView: passView];
83+ [alert setShowsSuppressionButton: YES ];
84+ [[alert suppressionButton ] setTitle: @" Save on keychain" ];
85+ NSInteger result = [alert runModal ];
86+ if ( result == NSAlertFirstButtonReturn ) {
87+ NSString *pas=[passView stringValue ];
88+ printf ( " %s " , [pas UTF8String ] );
89+ if ([[alert suppressionButton ] state ] == NSOnState ) {
90+ StorePasswordKeychain ([url cStringUsingEncoding: NSASCIIStringEncoding],
91+ [url lengthOfBytesUsingEncoding: NSASCIIStringEncoding],
92+ (void *)[pas cStringUsingEncoding: NSASCIIStringEncoding],
93+ [pas lengthOfBytesUsingEncoding: NSASCIIStringEncoding]);
5294 }
53-
54- box.origin = NSZeroPoint ; // Only need local coords from now on.
55-
56- // OK:
57- NSRect okBox = box;
58- okBox.origin .x = NSMaxX ( box ) -OKBUTTONWIDTH -20 ;
59- okBox.size .width = OKBUTTONWIDTH;
60- okBox.origin .y += 20 ;
61- okBox.size .height = OKBUTTONHEIGHT;
62- NSButton *okButton = [[NSButton alloc ] initWithFrame: okBox];
63- [okButton setTarget: self ];
64- [okButton setAction: @selector (doOKButton: )];
65- [okButton setTitle: @" OK" ]; // +++ Localize.
66- [okButton setKeyEquivalent: @" \r " ];
67- [okButton setBordered: YES ];
68- [okButton setBezelStyle: NSRoundedBezelStyle ];
69- [[mPasswordPanel contentView ] addSubview: okButton];
70-
71- // Cancel:
72- NSRect cancelBox = box;
73- cancelBox.origin .x = NSMinX ( okBox ) -CANCELBUTTONWIDTH -6 ;
74- cancelBox.size .width = CANCELBUTTONWIDTH;
75- cancelBox.origin .y += 20 ;
76- cancelBox.size .height = CANCELBUTTONHEIGHT;
77- NSButton *cancleButton = [[NSButton alloc ] initWithFrame: cancelBox];
78- [cancleButton setTarget: self ];
79- [cancleButton setAction: @selector (doCancelButton: )];
80- [cancleButton setTitle: @" Cancel" ]; // +++ Localize.
81- [cancleButton setBordered: YES ];
82- [cancleButton setBezelStyle: NSRoundedBezelStyle ];
83- [[mPasswordPanel contentView ] addSubview: cancleButton];
84-
85- // Password field:
86- NSRect passBox = box;
87- passBox.origin .y = NSMaxY (okBox) + 24 ;
88- passBox.size .height = PASSHEIGHT;
89- passBox.origin .x += 104 ;
90- passBox.size .width -= 104 + 20 ;
91- mPasswordField = [[NSSecureTextField alloc ] initWithFrame: passBox];
92- [mPasswordField setSelectable: YES ];
93- [mPasswordField setEditable: YES ];
94- [mPasswordField setBordered: YES ];
95- [mPasswordField setBezeled: YES ];
96- [mPasswordField setBezelStyle: NSTextFieldSquareBezel];
97- [mPasswordField selectText: self ];
98- [[mPasswordPanel contentView ] addSubview: mPasswordField];
99-
100- // Password label:
101- NSRect passLabelBox = box;
102- passLabelBox.origin .y = NSMaxY (passBox) + 8 ;
103- passLabelBox.size .height = PASSLABELHEIGHT;
104- passLabelBox.origin .x += 100 ;
105- passLabelBox.size .width -= 100 + 20 ;
106- NSTextField *passwordLabel = [[NSTextField alloc ] initWithFrame: passLabelBox];
107- [passwordLabel setSelectable: YES ];
108- [passwordLabel setEditable: NO ];
109- [passwordLabel setBordered: NO ];
110- [passwordLabel setBezeled: NO ];
111- [passwordLabel setDrawsBackground: NO ];
112- [passwordLabel setStringValue: @" Please enter your password:" ]; // +++ Localize.
113- [[mPasswordPanel contentView ] addSubview: passwordLabel];
114-
115- // GitX icon:
116- NSRect gitxIconBox = box;
117- gitxIconBox.origin .y = NSMaxY (box) - 78 ;
118- gitxIconBox.size .height = 64 ;
119- gitxIconBox.origin .x += 20 ;
120- gitxIconBox.size .width = 64 ;
121- NSImageView *gitxIconView = [[NSImageView alloc ] initWithFrame: gitxIconBox];
122- [gitxIconView setEditable: NO ];
123- NSString *gitxIconPath = [[[NSBundle mainBundle ] bundlePath ] stringByAppendingPathComponent: @" gitx.icns" ];
124- NSImage *gitxIcon = [[NSImage alloc ] initWithContentsOfFile: gitxIconPath];
125- [gitxIconView setImage: gitxIcon];
126- [[mPasswordPanel contentView ] addSubview: gitxIconView];
12795 }
12896
129- return mPasswordPanel ;
97+ [alert release ] ;
13098}
13199
100+ @end
101+
102+ void getproclline (pid_t pid, char *command_name);
132103
133- -( IBAction ) doOKButton : ( id ) sender
104+ void getproclline ( pid_t pid, char *command_name)
134105{
135- printf ( " %s \n " , [[mPasswordField stringValue ] UTF8String ] );
136- [[NSApplication sharedApplication ] stopModalWithCode: 0 ];
106+ int mib[3 ], argmax, nargs, c = 0 ;
107+ size_t size;
108+ char *procargs, *sp, *np, *cp;
109+
110+ mib[0 ] = CTL_KERN;
111+ mib[1 ] = KERN_ARGMAX;
112+
113+ size = sizeof (argmax);
114+ if (sysctl (mib, 2 , &argmax, &size, NULL , 0 ) == -1 ) {
115+ return ;
116+ }
117+
118+ /* Allocate space for the arguments. */
119+ procargs = (char *)malloc (argmax);
120+ if (procargs == NULL ) {
121+ return ;
122+ }
123+
124+ mib[0 ] = CTL_KERN;
125+ mib[1 ] = KERN_PROCARGS2;
126+ mib[2 ] = pid;
127+
128+ size = (size_t )argmax;
129+ if (sysctl (mib, 3 , procargs, &size, NULL , 0 ) == -1 ) {
130+ return ;
131+ }
132+
133+ memcpy (&nargs, procargs, sizeof (nargs));
134+ cp = procargs + sizeof (nargs);
135+
136+ /* Skip the saved exec_path. */
137+ for (; cp < &procargs[size]; cp++) {
138+ if (*cp == ' \0 ' ) {
139+ /* End of exec_path reached. */
140+ break ;
141+ }
142+ }
143+ if (cp == &procargs[size]) {
144+ return ;
145+ }
146+
147+ /* Skip trailing '\0' characters. */
148+ for (; cp < &procargs[size]; cp++) {
149+ if (*cp != ' \0 ' ) {
150+ /* Beginning of first argument reached. */
151+ break ;
152+ }
153+ }
154+ if (cp == &procargs[size]) {
155+ return ;
156+ }
157+ /* Save where the argv[0] string starts. */
158+ sp = cp;
159+
160+ for (np = NULL ; c < nargs && cp < &procargs[size]; cp++) {
161+ if (*cp == ' \0 ' ) {
162+ c++;
163+ if (np != NULL ) {
164+ *np = ' ' ;
165+ }
166+ np = cp;
167+ }
168+ }
169+ sprintf (command_name, " %s " ,sp);
137170}
138171
139-
140- // TODO: Need to find out how to get SSH to cancel.
141- // When the user cancels the window it is opened again for however
142- // many times the remote server allows failed attempts.
143- -(IBAction ) doCancelButton : (id )sender
172+ OSStatus StorePasswordKeychain (const char *url, UInt32 urlLength, void * password,UInt32 passwordLength)
144173{
145- [[NSApplication sharedApplication ] stopModalWithCode: 1 ];
174+ OSStatus status;
175+ status = SecKeychainAddGenericPassword (
176+ NULL , // default keychain
177+ 4 , // length of service name
178+ " GitX" , // service name
179+ urlLength, // length of account name
180+ url, // account name
181+ passwordLength, // length of password
182+ password, // pointer to password data
183+ NULL // the item reference
184+ );
185+ return (status);
146186}
147187
148- @end
149-
188+ OSStatus GetPasswordKeychain (const char *url, UInt32 urlLength ,void *passwordData,UInt32 *passwordLength,
189+ SecKeychainItemRef *itemRef)
190+ {
191+ OSStatus status ;
192+ status = SecKeychainFindGenericPassword (
193+ NULL , // default keychain
194+ 4 , // length of service name
195+ " GitX" , // service name
196+ urlLength, // length of account name
197+ url, // account name
198+ passwordLength, // length of password
199+ passwordData, // pointer to password data
200+ itemRef // the item reference
201+ );
202+ return (status);
203+ }
150204
151205
152- int main ( int argc, const char ** argv )
206+ int main ( int argc, const char * argv[] )
153207{
154- // close stderr to stop cocoa log messages from being picked up by GitX
155- close (STDERR_FILENO);
156-
157- NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults ];
208+ // close stderr to stop cocoa log messages from being picked up by GitX
209+ close (STDERR_FILENO);
158210
159- ProcessSerialNumber myPSN = { 0 , kCurrentProcess };
160- TransformProcessType ( &myPSN, kProcessTransformToForegroundApplication );
211+ ProcessSerialNumber myPSN = { 0 , kCurrentProcess };
212+ TransformProcessType ( &myPSN, kProcessTransformToForegroundApplication );
213+
214+ NSApplication *app = [NSApplication sharedApplication ];
215+ GAPAppDelegate *appDel = [[GAPAppDelegate alloc ] init ];
216+ [app setDelegate: appDel];
161217
162- NSApplication *app = [ NSApplication sharedApplication ];
163- GAPAppDelegate *appDel = [[GAPAppDelegate alloc ] init ];
164- [app setDelegate: appDel] ;
165- NSWindow *passPanel = [appDel passwordPanel ];
218+
219+ char args[ 4024 ];
220+ getproclline ( getppid (),args) ;
221+ NSString *cmd=[ NSString stringWithFormat: @" %@ " ,[ NSString stringWithUTF8String: args] ];
166222
167- [app activateIgnoringOtherApps: YES ];
168- [passPanel makeKeyAndOrderFront: nil ];
169- NSInteger code = [app runModalForWindow: passPanel];
223+ NSLog (@" cmd: '%@ '" ,cmd);
170224
171- [defaults synchronize ];
225+ if ([cmd hasPrefix: @" git-remote" ]){
226+ NSArray *args=[cmd componentsSeparatedByString: @" " ];
227+ NSString *url=[args objectAtIndex: [args count ]-1 ];
228+
229+ void *passwordData = nil ;
230+ SecKeychainItemRef itemRef = nil ;
231+ UInt32 passwordLength = 0 ;
232+
233+ OSStatus status = GetPasswordKeychain ([url cStringUsingEncoding: NSASCIIStringEncoding],[url lengthOfBytesUsingEncoding: NSASCIIStringEncoding],&passwordData,&passwordLength,&itemRef);
234+ if (status == noErr) {
235+ SecKeychainItemFreeContent (NULL ,passwordData);
236+ NSString *pas=[[NSString stringWithCString: passwordData encoding: NSASCIIStringEncoding] substringToIndex: passwordLength];
237+ printf ( " %s " , [pas UTF8String ] );
238+ return 0 ;
239+ }
240+
241+ NSString *prompt=[NSString stringWithFormat: @" %@ " ,[NSString stringWithCString: argv[1 ] encoding: NSASCIIStringEncoding]];
242+ [appDel pasword: prompt url: url];
243+ }else { // yes/no?
244+ NSString *prompt=[NSString stringWithFormat: @" %@ " ,[NSString stringWithCString: argv[1 ] encoding: NSASCIIStringEncoding]];
245+ NSArray *args=[cmd componentsSeparatedByString: @" " ];
246+ NSString *url=[args objectAtIndex: 1 ];
247+ [appDel yesNo: prompt url: url];
248+ }
172249
173- return code ;
174- }
250+ return 0 ;
251+ }
0 commit comments