/*
 * This code implements communication based on Miranda netlib library
 *
 * (c) majvan 2002-2004
 */
#include "../stdafx.h"
 //--------------------------------------------------------------------------------------------------
BOOL SSLLoaded = FALSE;
HNETLIBUSER hNetlibUser = nullptr;
void __stdcall	SSL_DebugLog(const char *fmt, ...)
{
	char str[4096];
	va_list	vararg;
	va_start(vararg, fmt);
	int tBytes = mir_vsnprintf(str, _countof(str), fmt, vararg);
	if (tBytes == 0)
		return;
	if (tBytes > 0)
		str[tBytes] = 0;
	else
		str[sizeof(str) - 1] = 0;
	Netlib_Log(hNetlibUser, str);
	va_end(vararg);
}
HANDLE RegisterNLClient(char *name)
{
	#ifdef DEBUG_COMM
	DebugLog(CommFile, "");
	#endif
	NETLIBUSER nlu = {};
	nlu.flags = NUF_OUTGOING | NUF_HTTPCONNS;
	nlu.szDescriptiveName.a = name;
	nlu.szSettingsModule = (char *)name;
	hNetlibUser = Netlib_RegisterUser(&nlu);
	#ifdef DEBUG_COMM
	if (NULL == hNetlibUser)
		DebugLog(CommFile, "\n");
	else
		DebugLog(CommFile, "\n");
	#endif
	return hNetlibUser;
}
//Move connection to SSL
void CNLClient::SSLify() throw(DWORD)
{
	#ifdef DEBUG_COMM
	SSL_DebugLog("Staring SSL...");
	#endif
	int socket = Netlib_GetSocket(hConnection);
	if (socket != INVALID_SOCKET) {
		#ifdef DEBUG_COMM
		SSL_DebugLog("Staring netlib core SSL");
		#endif
		if (Netlib_StartSsl(hConnection, nullptr)) {
			#ifdef DEBUG_COMM
			SSL_DebugLog("Netlib core SSL started");
			#endif
			isTLSed = true;
			SSLLoaded = TRUE;
			return;
		}
	}
	//ssl could not be created
	throw NetworkError = (uint32_t)ESSL_CREATESSL;
}
//Connects to the server through the sock
//if not success, exception is throwed
void CNLClient::Connect(const char *servername, const int port) throw(DWORD)
{
	NetworkError = SystemError = 0;
	isTLSed = false;
	#ifdef DEBUG_COMM
	DebugLog(CommFile, "\n");
	#endif
	try {
		if (nullptr == (hConnection = Netlib_OpenConnection(hNetlibUser, servername, port))) {
			SystemError = WSAGetLastError();
			throw NetworkError = (uint32_t)ENL_CONNECT;
		}
		#ifdef DEBUG_COMM
		DebugLog(CommFile, "\n");
		#endif
		return;
	}
	catch (...) {
		#ifdef DEBUG_COMM
		DebugLog(CommFile, "\n");
		#endif
		throw;
	}
}
//Performs a simple query
// query- command to send
int CNLClient::LocalNetlib_Send(HNETLIBCONN hConn, const char *buf, int len, int flags)
{
	return Netlib_Send(hConn, buf, len, flags);
}
void CNLClient::Send(const char *query) throw(DWORD)
{
	unsigned int Sent;
	if (nullptr == query)
		return;
	if (hConnection == nullptr)
		return;
	#ifdef DEBUG_COMM
	DebugLog(CommFile, "%s", query);
	#endif
	try {
		if ((SOCKET_ERROR == (Sent = LocalNetlib_Send(hConnection, query, (int)mir_strlen(query), MSG_DUMPASTEXT))) || Sent != (unsigned int)mir_strlen(query)) {
			SystemError = WSAGetLastError();
			throw NetworkError = (uint32_t)ENL_SEND;
		}
		#ifdef DEBUG_COMM
		DebugLog(CommFile, "\n");
		#endif
	}
	catch (...) {
		#ifdef DEBUG_COMM
		DebugLog(CommFile, "\n");
		#endif
		throw;
	}
}
//Reads data from socket
// buf- buffer where to store max. buflen of received characters
//      if buf is NULL, creates buffer of buflen size
//      buf is NULL by default
//You need free() returned buffer, which can be allocated in this function
//if not success, exception is throwed
int CNLClient::LocalNetlib_Recv(HNETLIBCONN hConn, char *buf, int len, int flags)
{
	int iReturn = Netlib_Recv(hConn, buf, len, flags);
	if (isTLSed) {
		#ifdef DEBUG_COMM
		SSL_DebugLog("SSL recv: %s", buf);
		#endif
	}
	return iReturn;
}
char *CNLClient::Recv(char *buf, int buflen) throw(DWORD)
{
	#ifdef DEBUG_COMM
	DebugLog(CommFile, "");
	#endif
	try {
		if (buf == nullptr)
			buf = (char *)malloc(sizeof(char) * (buflen + 1));
		if (buf == nullptr)
			throw NetworkError = (uint32_t)ENL_RECVALLOC;
		if (!isTLSed) {
			NETLIBSELECT nls = {};
			nls.dwTimeout = 60000;
			nls.hReadConns[0] = hConnection;
			switch (Netlib_Select(&nls)) {
			case SOCKET_ERROR:
				free(buf);
				SystemError = WSAGetLastError();
				throw NetworkError = (uint32_t)ENL_RECV;
			case 0: // time out!
				free(buf);
				throw NetworkError = (uint32_t)ENL_TIMEOUT;
			}
		}
		memset(buf, 0, buflen);
		if (SOCKET_ERROR == (Rcv = LocalNetlib_Recv(hConnection, buf, buflen, MSG_DUMPASTEXT))) {
			free(buf);
			SystemError = WSAGetLastError();
			throw NetworkError = (uint32_t)ENL_RECV;
		}
		if (!Rcv) {
			free(buf);
			SystemError = WSAGetLastError();
			throw NetworkError = (uint32_t)ENL_RECV;
		}
		#ifdef DEBUG_COMM
		*(buf + Rcv) = 0;				//end the buffer to write it to file
		DebugLog(CommFile, "%s", buf);
		DebugLog(CommFile, "\n");
		#endif
		return(buf);
	}
	catch (...) {
		#ifdef DEBUG_COMM
		DebugLog(CommFile, "\n");
		#endif
		throw;
	}
}
//Closes netlib connection
void CNLClient::Disconnect()
{
	Netlib_CloseHandle(hConnection);
	hConnection = nullptr;
}
//Uninitializes netlib library
void UnregisterNLClient()
{
	#ifdef DEBUG_COMM
	DebugLog(CommFile, "");
	#endif
	Netlib_CloseHandle(hNetlibUser);
	hNetlibUser = nullptr;
	#ifdef DEBUG_COMM
	DebugLog(CommFile, "\n");
	#endif
}