diff --git a/ApplicationController.m b/ApplicationController.m index 703da02..ad7149c 100644 --- a/ApplicationController.m +++ b/ApplicationController.m @@ -68,8 +68,8 @@ if ([PBGitDefaults useAskPasswd]) { // Make sure Git's SSH password requests get forwarded to our little UI tool: - setenv( "SSH_ASKPASS", [[[NSBundle mainBundle] pathForResource: @"gitx_askpasswd" ofType: @""] UTF8String], 1 ); - setenv( "DISPLAY", "localhost:0", 1 ); + setenv( "SSH_ASKPASS", [[[NSBundle mainBundle] pathForResource: @"gitx_askpasswd" ofType: @""] UTF8String], 1 ); + setenv( "DISPLAY", "localhost:0", 1 ); } [self registerServices]; diff --git a/gitx_askpasswd_main.m b/gitx_askpasswd_main.m index 07a7dbb..75a0a34 100644 --- a/gitx_askpasswd_main.m +++ b/gitx_askpasswd_main.m @@ -9,166 +9,243 @@ #include #import +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -#define OKBUTTONWIDTH 100.0 -#define OKBUTTONHEIGHT 24.0 -#define CANCELBUTTONWIDTH 100.0 -#define CANCELBUTTONHEIGHT 24.0 -#define PASSHEIGHT 22.0 -#define PASSLABELHEIGHT 16.0 -#define WINDOWAUTOSAVENAME @"GitXAskPasswordWindowFrame" +#define OKBUTTONWIDTH 100.0 +#define OKBUTTONHEIGHT 24.0 +#define CANCELBUTTONWIDTH 100.0 +#define CANCELBUTTONHEIGHT 24.0 +#define PASSHEIGHT 22.0 +#define PASSLABELHEIGHT 16.0 +#define WINDOWAUTOSAVENAME @"GitXAskPasswordWindowFrame" -@interface GAPAppDelegate : NSObject +@interface GAPAppDelegate : NSObject /**/ { - NSPanel* mPasswordPanel; - NSSecureTextField* mPasswordField; } --(NSPanel*) passwordPanel; - --(IBAction) doOKButton: (id)sender; --(IBAction) doCancelButton: (id)sender; - @end +NSString* url; +OSStatus StorePasswordKeychain (const char *url, UInt32 urlLength, void* password,UInt32 passwordLength); -@implementation GAPAppDelegate --(NSPanel*) passwordPanel -{ - if( !mPasswordPanel ) - { - NSRect box = NSMakeRect( 100, 100, 400, 134 ); - mPasswordPanel = [[NSPanel alloc] initWithContentRect: box - styleMask: NSTitledWindowMask - backing: NSBackingStoreBuffered defer: NO]; - [mPasswordPanel setHidesOnDeactivate: NO]; - [mPasswordPanel setLevel: NSFloatingWindowLevel]; - [mPasswordPanel setTitle: @"GitX SSH Remote Login"]; - if (![mPasswordPanel setFrameUsingName: WINDOWAUTOSAVENAME]) { - [mPasswordPanel center]; - [mPasswordPanel setFrameAutosaveName: WINDOWAUTOSAVENAME]; +@implementation GAPAppDelegate + +-(void)yesNo:(NSString *)prompt url:(NSString *)url{ + NSAlert *alert = [[NSAlert alloc] init]; + [alert addButtonWithTitle:@"YES"]; + [alert addButtonWithTitle:@"NO"]; + [alert setMessageText:[NSString stringWithFormat:@"%@?",url]]; + [alert setInformativeText:prompt]; + [alert setAlertStyle:NSWarningAlertStyle]; + NSInteger result = [alert runModal]; + + Boolean yes=NO; + if ( result == NSAlertFirstButtonReturn ) { + yes=YES; + } + [alert release]; + printf("%s",yes?"yes":"no"); +} + + +- (void)pasword:(NSString *)prompt url:(NSString *)url{ + + NSRect box = NSMakeRect(0, 0, 200, 24); + + NSSecureTextField * passView = [[NSSecureTextField alloc] initWithFrame: box]; + [passView setSelectable: YES]; + [passView setEditable: YES]; + [passView setBordered: YES]; + [passView setBezeled: YES]; + [passView setBezelStyle: NSTextFieldSquareBezel]; + [passView selectText: self]; + + + NSAlert *alert = [[NSAlert alloc] init]; + [alert addButtonWithTitle:@"Ok"]; + [alert addButtonWithTitle:@"cancel"]; + [alert setMessageText:[NSString stringWithFormat:@"%@?",url]]; + [alert setInformativeText:prompt]; + [alert setAlertStyle:NSWarningAlertStyle]; + [alert setAccessoryView:passView]; + [alert setShowsSuppressionButton:YES]; + [[alert suppressionButton] setTitle:@"Save on keychain"]; + NSInteger result = [alert runModal]; + if ( result == NSAlertFirstButtonReturn ) { + NSString *pas=[passView stringValue]; + printf( "%s", [pas UTF8String] ); + if ([[alert suppressionButton] state] == NSOnState) { + StorePasswordKeychain ([url cStringUsingEncoding:NSASCIIStringEncoding], + [url lengthOfBytesUsingEncoding:NSASCIIStringEncoding], + (void *)[pas cStringUsingEncoding:NSASCIIStringEncoding], + [pas lengthOfBytesUsingEncoding:NSASCIIStringEncoding]); } - - box.origin = NSZeroPoint; // Only need local coords from now on. - - // OK: - NSRect okBox = box; - okBox.origin.x = NSMaxX( box ) -OKBUTTONWIDTH -20; - okBox.size.width = OKBUTTONWIDTH; - okBox.origin.y += 20; - okBox.size.height = OKBUTTONHEIGHT; - NSButton *okButton = [[NSButton alloc] initWithFrame: okBox]; - [okButton setTarget: self]; - [okButton setAction: @selector(doOKButton:)]; - [okButton setTitle: @"OK"]; // +++ Localize. - [okButton setKeyEquivalent: @"\r"]; - [okButton setBordered: YES]; - [okButton setBezelStyle: NSRoundedBezelStyle]; - [[mPasswordPanel contentView] addSubview: okButton]; - - // Cancel: - NSRect cancelBox = box; - cancelBox.origin.x = NSMinX( okBox ) -CANCELBUTTONWIDTH -6; - cancelBox.size.width = CANCELBUTTONWIDTH; - cancelBox.origin.y += 20; - cancelBox.size.height = CANCELBUTTONHEIGHT; - NSButton *cancleButton = [[NSButton alloc] initWithFrame: cancelBox]; - [cancleButton setTarget: self]; - [cancleButton setAction: @selector(doCancelButton:)]; - [cancleButton setTitle: @"Cancel"]; // +++ Localize. - [cancleButton setBordered: YES]; - [cancleButton setBezelStyle: NSRoundedBezelStyle]; - [[mPasswordPanel contentView] addSubview: cancleButton]; - - // Password field: - NSRect passBox = box; - passBox.origin.y = NSMaxY(okBox) + 24; - passBox.size.height = PASSHEIGHT; - passBox.origin.x += 104; - passBox.size.width -= 104 + 20; - mPasswordField = [[NSSecureTextField alloc] initWithFrame: passBox]; - [mPasswordField setSelectable: YES]; - [mPasswordField setEditable: YES]; - [mPasswordField setBordered: YES]; - [mPasswordField setBezeled: YES]; - [mPasswordField setBezelStyle: NSTextFieldSquareBezel]; - [mPasswordField selectText: self]; - [[mPasswordPanel contentView] addSubview: mPasswordField]; - - // Password label: - NSRect passLabelBox = box; - passLabelBox.origin.y = NSMaxY(passBox) + 8; - passLabelBox.size.height = PASSLABELHEIGHT; - passLabelBox.origin.x += 100; - passLabelBox.size.width -= 100 + 20; - NSTextField *passwordLabel = [[NSTextField alloc] initWithFrame: passLabelBox]; - [passwordLabel setSelectable: YES]; - [passwordLabel setEditable: NO]; - [passwordLabel setBordered: NO]; - [passwordLabel setBezeled: NO]; - [passwordLabel setDrawsBackground: NO]; - [passwordLabel setStringValue: @"Please enter your password:"]; // +++ Localize. - [[mPasswordPanel contentView] addSubview: passwordLabel]; - - // GitX icon: - NSRect gitxIconBox = box; - gitxIconBox.origin.y = NSMaxY(box) - 78; - gitxIconBox.size.height = 64; - gitxIconBox.origin.x += 20; - gitxIconBox.size.width = 64; - NSImageView *gitxIconView = [[NSImageView alloc] initWithFrame: gitxIconBox]; - [gitxIconView setEditable: NO]; - NSString *gitxIconPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent: @"gitx.icns"]; - NSImage *gitxIcon = [[NSImage alloc] initWithContentsOfFile: gitxIconPath]; - [gitxIconView setImage: gitxIcon]; - [[mPasswordPanel contentView] addSubview: gitxIconView]; } - return mPasswordPanel; -} - - --(IBAction) doOKButton: (id)sender -{ - printf( "%s\n", [[mPasswordField stringValue] UTF8String] ); - [[NSApplication sharedApplication] stopModalWithCode: 0]; -} - - -// TODO: Need to find out how to get SSH to cancel. -// When the user cancels the window it is opened again for however -// many times the remote server allows failed attempts. --(IBAction) doCancelButton: (id)sender -{ - [[NSApplication sharedApplication] stopModalWithCode: 1]; + [alert release]; } @end +void getproclline(pid_t pid, char *command_name); - -int main( int argc, const char** argv ) +void getproclline(pid_t pid, char *command_name) { - // close stderr to stop cocoa log messages from being picked up by GitX - close(STDERR_FILENO); + int mib[3], argmax, nargs, c = 0; + size_t size; + char *procargs, *sp, *np, *cp; + + mib[0] = CTL_KERN; + mib[1] = KERN_ARGMAX; + + size = sizeof(argmax); + if (sysctl(mib, 2, &argmax, &size, NULL, 0) == -1) { + return; + } + + /* Allocate space for the arguments. */ + procargs = (char *)malloc(argmax); + if (procargs == NULL) { + return; + } + + mib[0] = CTL_KERN; + mib[1] = KERN_PROCARGS2; + mib[2] = pid; + + size = (size_t)argmax; + if (sysctl(mib, 3, procargs, &size, NULL, 0) == -1) { + return; + } + + memcpy(&nargs, procargs, sizeof(nargs)); + cp = procargs + sizeof(nargs); + + /* Skip the saved exec_path. */ + for (; cp < &procargs[size]; cp++) { + if (*cp == '\0') { + /* End of exec_path reached. */ + break; + } + } + if (cp == &procargs[size]) { + return; + } + + /* Skip trailing '\0' characters. */ + for (; cp < &procargs[size]; cp++) { + if (*cp != '\0') { + /* Beginning of first argument reached. */ + break; + } + } + if (cp == &procargs[size]) { + return; + } + /* Save where the argv[0] string starts. */ + sp = cp; + + for (np = NULL; c < nargs && cp < &procargs[size]; cp++) { + if (*cp == '\0') { + c++; + if (np != NULL) { + *np = ' '; + } + np = cp; + } + } + sprintf(command_name, "%s",sp); +} + +OSStatus StorePasswordKeychain (const char *url, UInt32 urlLength, void* password,UInt32 passwordLength) +{ + OSStatus status; + status = SecKeychainAddGenericPassword ( + NULL, // default keychain + 4, // length of service name + "GitX", // service name + urlLength, // length of account name + url, // account name + passwordLength, // length of password + password, // pointer to password data + NULL // the item reference + ); + return (status); +} + +OSStatus GetPasswordKeychain (const char *url, UInt32 urlLength ,void *passwordData,UInt32 *passwordLength, + SecKeychainItemRef *itemRef) +{ + OSStatus status ; + status = SecKeychainFindGenericPassword ( + NULL, // default keychain + 4, // length of service name + "GitX", // service name + urlLength, // length of account name + url, // account name + passwordLength, // length of password + passwordData, // pointer to password data + itemRef // the item reference + ); + return (status); +} + + +int main( int argc, const char* argv[] ) +{ + // close stderr to stop cocoa log messages from being picked up by GitX + close(STDERR_FILENO); - NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + ProcessSerialNumber myPSN = { 0, kCurrentProcess }; + TransformProcessType( &myPSN, kProcessTransformToForegroundApplication ); + + NSApplication *app = [NSApplication sharedApplication]; + GAPAppDelegate *appDel = [[GAPAppDelegate alloc] init]; + [app setDelegate: appDel]; - ProcessSerialNumber myPSN = { 0, kCurrentProcess }; - TransformProcessType( &myPSN, kProcessTransformToForegroundApplication ); + + char args[4024]; + getproclline(getppid(),args); + NSString *cmd=[NSString stringWithFormat:@"%@",[NSString stringWithUTF8String:args]]; - NSApplication *app = [NSApplication sharedApplication]; - GAPAppDelegate *appDel = [[GAPAppDelegate alloc] init]; - [app setDelegate: appDel]; - NSWindow *passPanel = [appDel passwordPanel]; + NSLog(@"cmd: '%@'",cmd); - [app activateIgnoringOtherApps: YES]; - [passPanel makeKeyAndOrderFront: nil]; - NSInteger code = [app runModalForWindow: passPanel]; + if([cmd hasPrefix:@"git-remote"]){ + NSArray *args=[cmd componentsSeparatedByString:@" "]; + NSString *url=[args objectAtIndex:[args count]-1]; + + void *passwordData = nil; + SecKeychainItemRef itemRef = nil; + UInt32 passwordLength = 0; + + OSStatus status = GetPasswordKeychain ([url cStringUsingEncoding:NSASCIIStringEncoding],[url lengthOfBytesUsingEncoding:NSASCIIStringEncoding],&passwordData,&passwordLength,&itemRef); + if (status == noErr) { + SecKeychainItemFreeContent (NULL,passwordData); + NSString *pas=[[NSString stringWithCString:passwordData encoding:NSASCIIStringEncoding] substringToIndex:passwordLength]; + printf( "%s", [pas UTF8String] ); + return 0; + } + + NSString *prompt=[NSString stringWithFormat:@"%@",[NSString stringWithCString:argv[1] encoding:NSASCIIStringEncoding]]; + [appDel pasword:prompt url:url]; + }else{ // yes/no? + NSString *prompt=[NSString stringWithFormat:@"%@",[NSString stringWithCString:argv[1] encoding:NSASCIIStringEncoding]]; + NSArray *args=[cmd componentsSeparatedByString:@" "]; + NSString *url=[args objectAtIndex:1]; + [appDel yesNo:prompt url:url]; + } - [defaults synchronize]; - - return code; -} \ No newline at end of file + return 0; +}