/*
** JNetLib
** Copyright (C) 2000-2007 Nullsoft, Inc.
** Author: Justin Frankel
** File: util.cpp - JNL implementation of basic network utilities
** License: see jnetlib.h
*/

#include "netinc.h"
#include "util.h"
#include "foundation/error.h"
#ifdef USE_SSL
#include "sslconnection.h"
#ifdef _WIN32
#include <wincrypt.h>
#endif
#include <openssl/rand.h>

#ifdef _WIN32
static HCRYPTPROV GetKeySet()
{
	HCRYPTPROV   hCryptProv;
	LPCWSTR UserName = L"WinampKeyContainer";  // name of the key container

	if (CryptAcquireContext(
	      &hCryptProv,               // handle to the CSP
	      UserName,                  // container name
	      NULL,                      // use the default provider
	      PROV_RSA_FULL,             // provider type
	      0))                        // flag values
	{
		return hCryptProv;
	}
	else if (CryptAcquireContext(
	           &hCryptProv,
	           UserName,
	           NULL,
	           PROV_RSA_FULL,
	           CRYPT_NEWKEYSET))
	{
		return hCryptProv;
	}
	else
		return 0;
}
#endif

static void InitSSL()
{
	SSL_load_error_strings();
	SSL_library_init();
#ifdef _WIN32
	HCRYPTPROV hCryptProv = GetKeySet();
	if (hCryptProv)
	{
		BYTE pbData[8*sizeof(unsigned long)] = {0};
		if (CryptGenRandom(hCryptProv, 8*sizeof(unsigned long), pbData))
		{
			RAND_seed(pbData, 16);
		}
		CryptReleaseContext(hCryptProv,0);
	}
#endif
//	sslContext = SSL_CTX_new(SSLv23_client_method());
//	SSL_CTX_set_verify(sslContext, SSL_VERIFY_NONE, NULL);

//	SSL_CTX_set_session_cache_mode(sslContext, SSL_SESS_CACHE_OFF);
}
static int open_ssl_initted = 0;
#endif

static int was_initted = 0;

int JNL::open_socketlib()
{
#ifdef _WIN32
	if (!was_initted)
	{
		WSADATA wsaData = {0};
		if (WSAStartup(MAKEWORD(1, 1), &wsaData))
		{
			return NErr_Error;
		}
	}
#endif
#ifdef USE_SSL
	if (!open_ssl_initted)
	{
		InitSSL();
		open_ssl_initted=1;
	}
#endif
	return NErr_Success;
}

void JNL::close_socketlib()
{
#ifdef _WIN32
	if (was_initted)
	{
		WSACleanup();
	}
#ifdef USE_SSL
	// TODO need to do some reference counting to free this correctly
	//SSL_CTX_free(sslContext);
#endif
#endif
}

static char *jnl_strndup(const char *str, size_t n)
{
	char *o = (char *)calloc(n+1, sizeof(char));
	if (!o)
	{
		return 0;
	}
	strncpy(o, str, n);
	o[n]=0;
	return o;
}

int JNL::parse_url(const char *url, char **prot, char **host, unsigned short *port, char **req, char **lp)
{
	free(*prot); *prot=0;
	free(*host); *host = 0;
	free(*req); *req = 0;
	free(*lp); *lp = 0;
	*port = 0;

	const char *p;
	const char *protocol = strstr(url, "://");
	if (protocol)
	{
		*prot = jnl_strndup(url, protocol-url);
		p = protocol + 3;	
	}
	else
	{
		p = url;
	}

	while (p && *p && *p == '/') p++; // skip extra /

	size_t end = strcspn(p, "@/");

	// check for username
	if (p[end] == '@')
	{
		*lp = jnl_strndup(p, end);
		p = p+end+1;
		end = strcspn(p, "[:/");
	}

	if (p[0] == '[') // IPv6 style address
	{
		p++;
		const char *ipv6_end = strchr(p, ']');
		if (!ipv6_end)
			return NErr_Malformed;

		*host = jnl_strndup(p, ipv6_end-p);
		p = ipv6_end+1;
	}
	else
	{
		end = strcspn(p, ":/");
		*host = jnl_strndup(p, end);
		p += end;
	}

	// is there a port number?
	if (p[0] == ':')
	{
		char *new_end;
		*port = (unsigned short)strtoul(p+1, &new_end, 10);
		p = new_end;
	}

	if (p[0])	
	{
		*req = _strdup(p);
	}
	
	return NErr_Success;
}