
#import <foundation/foundation.h>

#import <stdio.h>
#import <ansi/string.h>

#import <ssl/err.h>

#import "SSLSocket.h"
#import "CTXHandler.h"

extern void OmniLog(NSString *format, ...);

@implementation SSLSocket

#ifdef DEBUG
void _PrintSSLError()
{
	unsigned long	l;
	char			buf[200];

	while ((l = ERR_get_error()))
		OmniLog(@"%s", ERR_error_string(l, buf));
}

int _verify_callback(int ok, X509 *xs, X509 *xi, int depth, int error)
{
	char	*name;

	name = X509_NAME_oneline(X509_get_subject_name(xs));
	if (name == NULL)
	{
		_PrintSSLError();
		return(0);
	}
	
	OmniLog(@"depth = %d %s", depth, name);
	free(name);
	
	if (error == VERIFY_ERR_UNABLE_TO_GET_ISSUER)
	{
		name = X509_NAME_oneline(X509_get_issuer_name(xs));
		if (name == NULL)
		{
			OmniLog(@"verify error");
			_PrintSSLError();
			return(0);
		}
		
		OmniLog(@"issuer = %s", name);
		free(name);

		return(ok);
	}
	
	if (!ok)
	{
		OmniLog(@"verify error: num = %d:%s", error, X509_cert_verify_error_string(error));
	}

	OmniLog(@"verify return: %d", ok);
	
	return(ok);
}
#endif

+ (SSLSocket *)sslSocket;
{
    return (SSLSocket *)[SSLSocket socket];
}

- (void)dealloc;
{
	if (_sslHandle != NULL)
	{
    	SSL_free(_sslHandle);
		_sslHandle = NULL;
	}

	if (_ctxHandler != nil)
	{
		[_ctxHandler release];
		_ctxHandler = nil;
	}
		
    [super dealloc];
}

- (int)readBytes:(unsigned int)byteCount intoBuffer:(char *)aBuffer;
{
	int	bytesRead;

	bytesRead = SSL_read(_sslHandle, aBuffer, byteCount);
	if (bytesRead == -1)
		[NSException raise:@"SocketReadFailed"
		format:@"Unable to read from socket: %s", strerror(cthread_errno())];

	return bytesRead;
}

- (int)writeBytes:(unsigned int)byteCount fromBuffer:(const char *)aBuffer;
{
	int	bytesWritten;

	bytesWritten = SSL_write(_sslHandle, (char *)aBuffer, byteCount);
	if (bytesWritten == -1)
		[NSException raise:@"SocketWriteFailed"
		format:@"Unable to write to socket: %s", strerror(cthread_errno())];

	return bytesWritten;
}

- (void)connectToAddress:(OmniHostAddress *)hostAddress port:(int)port;
{
	[super connectToAddress:hostAddress port:port];
	
	// Get the CTX-Structure and do some initialization
	// for the SSLeay-lib. The CTX-Structure is shared by
	// all SSLSocket-Instances.
	_ctxHandler = [[CTXHandler sharedCTXHandler] retain];
	if (_ctxHandler == nil) 
	{
		[NSException raise:@"CTXFailure"
		format:@"SSLSocket: Unable to get CTX-Structure"];
	}
	
	// Get the SSL-Handle which is different 
	// for each Instance of SSLSocket
	_sslHandle = SSL_new([_ctxHandler ctx]);
	
	SSL_set_fd(_sslHandle, socketFD);
#ifdef DEBUG
	SSL_set_verify(_sslHandle, SSL_VERIFY_NONE, _verify_callback);
#else
	SSL_set_verify(_sslHandle, SSL_VERIFY_NONE, NULL);
#endif

	if (!SSL_connect(_sslHandle)) 
	{
		[NSException raise:@"ConnectFailed"
		format:@"SSLSocket: Unable to ssl_connect to remote host (errno = %d): %s %s.",
		cthread_errno(), 
		ERR_func_error_string(ERR_peek_error()), ERR_reason_error_string(ERR_peek_error())];
	}
}

@end
