//#ifdef USE_SSL
//#include "netinc.h"
//#include "util.h"
//#include "connection.h"
#include "wac_network_ssl_connection.h"

SSL_CTX *sslContext = 0;

#ifdef _DEBUG

extern "C" void apps_ssl_info_callback( const SSL * s, int where, int ret )
{
	/*
	**  DEBUG INFO HERE
	*/
}
#endif

/*
**  Well, you should probably change this based on like...
**  well, you're level of trust heh
**  For now, this basically trusts all certs :)
**
*/
#if 0
extern "C" int verify_callback( int ok, X509_STORE_CTX * ctx )
{
	/* For certificate verification */
	int verify_depth = 0;
	int verify_error = X509_V_OK;

	char buf[ 1024 ] = { 0 };
	X509 *err_cert = X509_STORE_CTX_get_current_cert( ctx );
	int err = X509_STORE_CTX_get_error( ctx );
	int depth = X509_STORE_CTX_get_error_depth( ctx );
	X509_NAME_oneline( X509_get_subject_name( err_cert ), buf, sizeof( buf ) );

	if ( !ok )
	{
		if ( verify_depth >= depth )
		{
			ok = 1;
			verify_error = X509_V_OK;
		}
		else
		{
			ok = 0;
			verify_error = X509_V_ERR_CERT_CHAIN_TOO_LONG;
		}
	}
	switch ( ctx->error )
	{
		case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
			X509_NAME_oneline( X509_get_issuer_name( ctx->current_cert ), buf, sizeof( buf ) );
			break;

		case X509_V_ERR_CERT_NOT_YET_VALID:
		case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
			break;

		case X509_V_ERR_CERT_HAS_EXPIRED:
		case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
			break;
	}
	return ok;
}
#endif

JNL_SSL_Connection::JNL_SSL_Connection() : forceConnect( false ), m_ssl( 0 ), m_bsslinit( false ), m_bcontextowned( true )
{
	m_bsslinit = true;
}


JNL_SSL_Connection::JNL_SSL_Connection( SSL *pssl, api_dns *dns, size_t sendbufsize, size_t recvbufsize ) : WAC_Network_Connection( dns, sendbufsize, recvbufsize ), forceConnect( false )
{
	m_ssl = pssl;
	m_bsslinit = false;

	if ( m_ssl )
	{
		m_bcontextowned = false;
	}
	else
	{
		m_bcontextowned = true;
	}

	if ( m_bcontextowned == false )
	{
		return;
	}

	m_bsslinit = true;


	/* See the SSL states in our own callback */
#ifdef _DEBUG 
	//	SSL_CTX_set_info_callback(m_app_ctx, apps_ssl_info_callback);
#endif

	/* Set the certificate verification callback */
	//SSL_CTX_set_verify(sslContext, SSL_VERIFY_PEER, verify_callback);

	/* Not sure what this does */
	//SSL_CTX_set_session_cache_mode(sslContext, SSL_SESS_CACHE_CLIENT);

	return;
}

int JNL_SSL_Connection::socket_connect()
{
	int retval;

	if ( m_bcontextowned == false )
	{
		/*
		** WTF?
		*/
		return -1;
	}
	if ( m_ssl != NULL )
	{
		return -1;
	}

	retval = WAC_Network_Connection::socket_connect();

	if ( retval != 0 )
	{
		if ( ERRNO != EINPROGRESS )
		{
			return retval; // benski> if the underlying socket hasn't connected yet, then we can't start the SSL connection
			/*
			**  Joshua Teitelbaum 3/2/2006
			**  Fatal error here
			*/
		}
	}

	// moved from InitSSL() as no need to create this unless
	// we're actually going to use it which helps slow loads
	if ( !sslContext )
	{
		sslContext = SSL_CTX_new( SSLv23_client_method() );
		if ( sslContext )
		{
			SSL_CTX_set_verify( sslContext, SSL_VERIFY_NONE, NULL );
		}
		else
		{
			return -1;
		}
	}

	m_ssl = SSL_new( sslContext );

	if ( m_ssl == NULL )
	{
		return -1;
	}

	/* Tell that we are in connect mode */
	SSL_set_connect_state( m_ssl );

	/* Set socket descriptor with the socket we already have open */
	if ( SSL_set_fd( m_ssl, m_socket ) != 0 )
		return -1;

	return retval;
}

void JNL_SSL_Connection::socket_shutdown()
{
	if ( m_ssl )
		SSL_shutdown( m_ssl );

	WAC_Network_Connection::socket_shutdown();

	if ( m_ssl )
	{
		SSL_free( m_ssl );
		m_ssl = NULL;
	}

	return;
}

void JNL_SSL_Connection::run( size_t max_send_bytes, size_t max_recv_bytes, size_t *bytes_sent, size_t *bytes_rcvd )
{
	if ( !m_bsslinit )
	{
		int rval = SSL_accept( m_ssl );
		if ( rval == -1 )
		{
			int e = SSL_get_error( m_ssl, rval );

			if ( !( ( e == SSL_ERROR_WANT_READ ) || ( e == SSL_ERROR_WANT_WRITE ) ) )
			{
				m_state = STATE_ERROR;
			}

			return;
		}
		else
		{
			m_bsslinit = true;
		}
	}

	/**
	 ** benski - march 2, 2006
	 **if the underlying socket didn't connected yet, we need to try the SSL connection again
	 */
	if ( forceConnect )
	{
		if ( init_ssl_connection() == false )
		{
			return;
		}
	}

	WAC_Network_Connection::run( max_send_bytes, max_recv_bytes, bytes_sent, bytes_rcvd );
}

/*
**  init_ssl_connection:
**  Returns true, meaning can continue
**  Else, cannot continue with underlying run
**  side effects:
**  sets forceConnect
*/
bool JNL_SSL_Connection::init_ssl_connection()
{
	if ( m_ssl == NULL )
	{
		/*
		**  WTF?
		**  cascade up.
		*/
		return true;
	}

	int retval = SSL_connect( m_ssl );

	if ( retval < 0 )
	{
		int err = SSL_get_error( m_ssl, retval );

		switch ( err )
		{
			case SSL_ERROR_WANT_READ:
			case SSL_ERROR_WANT_WRITE:
			case SSL_ERROR_WANT_CONNECT:
				forceConnect = true;
				break;
				// fall through
			default: // TODO: benski> MOST other errors are OK, (especially "want read" and "want write", but we need to think through all the scenarios here
				forceConnect = false;
		}
	}
	else if ( retval )
	{
		/*
		**  success
		*/
		forceConnect = false;
	}

	/*
	**  If retval == 0
	**  socket is closed, or serious error occurred.
	**  cascade up.
	*/

	return forceConnect == false;
}

void JNL_SSL_Connection::on_socket_connected( void )
{
	init_ssl_connection();
}

void JNL_SSL_Connection::connect( SOCKET s, sockaddr *addr, socklen_t length )
{
	/*
	**  Joshua Teitelbaum 2/01/2006
	**  No need to close
	**  This is the reason for divergence as well as setting
	**  the connect state
	*/

	m_socket = s;
	address = (sockaddr *)malloc( length );
	memcpy( address, addr, length );

	m_remote_port = 0;
	if ( m_socket != -1 )
	{
		SET_SOCK_BLOCK( m_socket, 0 );
		m_state = STATE_CONNECTED;
		SSL_set_fd( m_ssl, m_socket );
	}
	else
	{
		m_errorstr = _strdup( "invalid socket passed to connect" );
		m_state = STATE_ERROR;
	}
}

ssize_t JNL_SSL_Connection::socket_recv( char *buf, size_t len, int options )
{
	return SSL_read( m_ssl, buf, (int)len );
}

ssize_t JNL_SSL_Connection::socket_send( const char *buf, size_t len, int options )
{
	return SSL_write( m_ssl, buf, (int)len );
}

JNL_SSL_Connection::~JNL_SSL_Connection()
{
	if ( m_ssl )
	{
		SSL_free( m_ssl );
		m_ssl = NULL;
	}
}
//#endif