#import "MVKeyChain.h"

#define MVStringByteLength(a) (( [a UTF8String] ? strlen( [a UTF8String] ) : 0 ))

static MVKeyChain *sharedInstance = nil;

@implementation MVKeyChain
+ (MVKeyChain *) defaultKeyChain {
	extern MVKeyChain *sharedInstance;
	return ( sharedInstance ? sharedInstance : ( sharedInstance = [[self alloc] init] ) );
}

- (void) dealloc {
	extern MVKeyChain *sharedInstance;
	if( sharedInstance == self ) sharedInstance = nil;
	[super dealloc];
}

- (void) setGenericPassword:(NSString *) password forService:(NSString *) service account:(NSString *) account {
	NSParameterAssert( service );
	NSParameterAssert( account );

	if( ! [password length] ) {
		[self removeGenericPasswordForService:service account:account];
	} else if( ! [[self genericPasswordForService:service account:account] isEqualToString:password] ) {
		[self removeGenericPasswordForService:service account:account];
		SecKeychainAddGenericPassword( NULL, MVStringByteLength( service ), [service UTF8String], MVStringByteLength( account ), [account UTF8String], MVStringByteLength( password ), (void *) [password UTF8String], NULL );
	}
}

- (NSString *) genericPasswordForService:(NSString *) service account:(NSString *) account {
	OSStatus ret = 0;
	unsigned long len = 0;
	void *p = NULL;
	NSString *string = nil;

	ret = SecKeychainFindGenericPassword( NULL, MVStringByteLength( service ), [service UTF8String], MVStringByteLength( account ), [account UTF8String], &len, &p, NULL );
	if( ret == noErr ) string = [NSString stringWithCString:(const char *) p length:len];
	SecKeychainItemFreeContent( NULL, p );

	return string;
}

- (void) removeGenericPasswordForService:(NSString *) service account:(NSString *) account {
	OSStatus ret = 0;
	SecKeychainItemRef itemref = NULL;

	NSParameterAssert( service );
	NSParameterAssert( account );

	ret = SecKeychainFindGenericPassword( NULL, MVStringByteLength( service ), [service UTF8String], MVStringByteLength( account ), [account UTF8String], NULL, NULL, &itemref );
	if( ret == noErr ) SecKeychainItemDelete( itemref );
}

- (void) setInternetPassword:(NSString *) password forServer:(NSString *) server securityDomain:(NSString *) domain account:(NSString *) account path:(NSString *) path port:(unsigned short) port protocol:(MVKeyChainProtocol) protocol authenticationType:(MVKeyChainAuthenticationType) authType {
	NSParameterAssert( server || account );

	if( ! [password length] ) {
		[self removeInternetPasswordForServer:server securityDomain:domain account:account path:nil port:port protocol:protocol authenticationType:authType];
	} else if( ! [[self internetPasswordForServer:server securityDomain:domain account:account path:nil port:port protocol:protocol authenticationType:authType] isEqualToString:password] ) {
		[self removeInternetPasswordForServer:server securityDomain:domain account:account path:nil port:port protocol:protocol authenticationType:authType];
		SecKeychainAddInternetPassword( NULL, MVStringByteLength( server ), [server UTF8String], MVStringByteLength( domain ), [domain UTF8String], MVStringByteLength( account ), [account UTF8String], MVStringByteLength( path ), [path UTF8String], port, protocol, authType, MVStringByteLength( password ), (void *) [password UTF8String], NULL );
	}
}

- (NSString *) internetPasswordForServer:(NSString *) server securityDomain:(NSString *) domain account:(NSString *) account path:(NSString *) path port:(unsigned short) port protocol:(MVKeyChainProtocol) protocol authenticationType:(MVKeyChainAuthenticationType) authType {
	OSStatus ret = 0;
	unsigned long len = 0;
	void *p = NULL;
	NSString *string = nil;

	ret = SecKeychainFindInternetPassword( NULL, MVStringByteLength( server ), [server UTF8String], MVStringByteLength( domain ), [domain UTF8String], MVStringByteLength( account ), [account UTF8String], MVStringByteLength( path ), [path UTF8String], port, protocol, authType, &len, &p, NULL );
	if( ret == noErr ) string = [NSString stringWithCString:(const char *) p length:len];
	SecKeychainItemFreeContent( NULL, p );

	return string;
}

- (void) removeInternetPasswordForServer:(NSString *) server securityDomain:(NSString *) domain account:(NSString *) account path:(NSString *) path port:(unsigned short) port protocol:(MVKeyChainProtocol) protocol authenticationType:(MVKeyChainAuthenticationType) authType {
	OSStatus ret = 0;
	SecKeychainItemRef itemref = NULL;

	NSParameterAssert( server || account );

	ret = SecKeychainFindInternetPassword( NULL, MVStringByteLength( server ), [server UTF8String], MVStringByteLength( domain ), [domain UTF8String], MVStringByteLength( account ), [account UTF8String], MVStringByteLength( path ), [path UTF8String], port, protocol, authType, NULL, NULL, &itemref );
	if( ret == noErr ) SecKeychainItemDelete( itemref );
}
@end