/**************************************************************************************************
 * File Name	: NetSSLClientWrapper.cpp
 * Created		: 08/09/15
 * Author		: ChenWei
 * Model			: TSD
 * Description	: Network ssl connect wrapper.Disconnect not same whit the  SSD
 **************************************************************************************************/
#include "NetSSLClientWrapper.h"
#include "libsesdata.h"
#include "Student.h"
#include <sstream>
#include <ace/OS_Errno.h>
#include <netinet/tcp.h>
#include "RegMgr.h"
#include <zlib.h>
using namespace LibSESData;
using namespace std;

/**************************************************************************************************
 * Function Name	: CNetSSLClientWrapper
 * Description		:
 * Date				: 08/09/15
 * Return Code		:
 * Author			: ChenWei
 **************************************************************************************************/
CNetSSLClientWrapper::CNetSSLClientWrapper()
{
	ms_remote_ip = "";
	mi_remote_port = 8888;
	mi_out_time = 10;
	m_bIsConnect = false;
	mc_tcp_stream = NULL;
	mc_tcp_stream = new ACE_SSL_SOCK_Stream(ACE_SSL_Context::instance());

}

/**************************************************************************************************
 * Function Name	: ~CNetSSLClientWrapper
 * Description		:
 * Date				: 08/09/15
 * Return Code		:
 * Author			: ChenWei
 **************************************************************************************************/
CNetSSLClientWrapper::~CNetSSLClientWrapper()
{
	Disconnect();
	if (NULL != mc_tcp_stream)
	{
		m_streamSendLock.acquire();
		m_streamLock.acquire();
		delete mc_tcp_stream;
		mc_tcp_stream = NULL;
		m_streamLock.release();
		m_streamSendLock.release();
	}


}

/**************************************************************************************************
 * Function Name	: Connect
 * Description		:
 * Date				: 08/09/15
 * Parameter		: const std::string &r_ip, int r_port
 * Return Code		: bool
 * Author			: ChenWei
 **************************************************************************************************/
bool CNetSSLClientWrapper::Connect(const std::string &r_ip, int r_port)
{
	CSESLog::WriteLine("CNetSSLClientWrapper::Connect()>>Start");
	std::string ipLog = "CNetSSLClientWrapper::Connect():ip:";
	ipLog.append(r_ip);
	CSESLog::WriteLine(ipLog);
	if (r_ip.size() < 1)
	{
		CSESLog::WriteLine(
				"CNetSSLClientWrapper::Connect()>>r_ip.size() < 1 return");
		return false;
	}

	if (m_bIsConnect)
	{
		if (r_ip != ms_remote_ip || r_port != mi_remote_port)
		{
			Disconnect();
		}
	}
	if (NULL == mc_tcp_stream)
	{
		mc_tcp_stream = new ACE_SSL_SOCK_Stream(ACE_SSL_Context::instance());
	}
	ms_remote_ip = r_ip;
	mi_remote_port = r_port;
	m_port_to_conn.set(mi_remote_port, ms_remote_ip.c_str());
	bool bReturn = false;
	m_Timeout.set(mi_out_time);

	if (0 == mc_connector.connect(*mc_tcp_stream, m_port_to_conn, &m_Timeout))//Returns 0 if the connection succeeds
	{
		//std::string caInfo;
		//GetCAInfo(caInfo);
		m_bIsConnect = true;
		bReturn = true;
	}
	else
	{
		CSESLog::WriteLine("CNetSSLClientWrapper::Connect()>>Err");
		CSESLog::WriteLine(ACE_OS_String::strerror(errno));
		//ACE_SSL_Context::report_error();
		//mc_tcp_stream->close();now the mc_tcp_stream isn't the full Stream.
		//so don't use mc_tcp_stream->close().

		if (NULL != mc_tcp_stream)
		{
			delete mc_tcp_stream;
			mc_tcp_stream = NULL;
		}
		m_bIsConnect = false;
	}

	CSESLog::WriteLine(" CNetSSLClientWrapper::Connect()<<End");
	return bReturn;

}

/**************************************************************************************************
 * Function Name		: Disconnect
 * Description			: Disconnect
 * Date					: 08/09/15
 * Parameter			:
 * Return Code			:
 * Author				: ChenWei
 **************************************************************************************************/
bool CNetSSLClientWrapper::Disconnect()
{

	CSESLog::WriteLine("CNetSSLClientWrapper::DisConnect()>>Start");
	bool bReturn = true;
	m_streamCloseLock.acquire();
	if (true == m_bIsConnect)
	{
		if (NULL != mc_tcp_stream)
		{
			CSESLog::WriteLine(
					"CNetSSLClientWrapper::DisConnect()>>mc_tcp_stream->close()");
			//not same whit the  SSD
			//mc_connector.complete(*mc_tcp_stream);
			//SSL_shutdown() returns 1 on successful
			if (!mc_tcp_stream->close())
			{
				CSESLog::WriteLine(
						"CNetSSLClientWrapper::DisConnect()>>mc_tcp_stream->close() exception",
						CSESLog::ERROR);
				CSESLog::WriteLine(ACE_OS_String::strerror(errno));
				bReturn = false;

			}

			m_bIsConnect = false;
		}

	}
	m_streamCloseLock.release();
	// cleanups of SSL library ...
	CSESLog::WriteLine(" CNetSSLClientWrapper::DisConnect()<<End");
	return bReturn;
}

/**************************************************************************************************
 * Function Name	: Send
 * Description		: Send
 * Date				: 08/09/15
 * Parameter		: const std::string & content
 * Return Code		: Return > 0 sent bytes;Return = 0 socket close;Return < 0
 * 							socket error
 * Author			: ChenWei
 **************************************************************************************************/
int CNetSSLClientWrapper::Send(const std::string & content, int iTimeout)
{
	CSESLog::WriteLine("CNetSSLClientWrapper::Send>>Start");
	int iReturn = 0;
	char* pBuf = NULL;
	do
	{
		if (0 == content.size())
		{
			break;
		}
		int iStrLength = strlen(content.c_str()) + 1; // add terminal


		int ipkgsize = 4 + iStrLength;
		try
		{
			pBuf = new char[ipkgsize];
		} catch (...)
		{
			pBuf = NULL;
			CSESLog::WriteLine(ipkgsize, "CNetSSLClientWrapper::Send>>", 0,
					CSESLog::ERROR);
			break;
		}
		CSESLog::WriteLine(ipkgsize, "CNetSSLClientWrapper::Send>>");

		memset(pBuf, 0, ipkgsize);
		memcpy(pBuf, &ipkgsize, sizeof(ipkgsize));
		memcpy(pBuf + sizeof(ipkgsize), content.c_str(),
				strlen(content.c_str()));

		iReturn = SendN(pBuf, ipkgsize);

		if (NULL != pBuf)
		{
			delete pBuf;
			pBuf = NULL;
		}
	} while (0);
	CSESLog::WriteLine("CNetSSLClientWrapper::Send<<End");
	return iReturn;

}
/**************************************************************************************************
 * Function Name	: Send
 * Description		: Send
 * Date				: 08/09/15
 * Parameter		: const std::string & content
 * Return Code		: Return > 0 sent bytes;Return = 0 socket close;Return < 0
 * 							socket error
 * Author			: ChenWei
 **************************************************************************************************/
int CNetSSLClientWrapper::Send(unsigned char* pucMsg, int iSize, int iTimeout)
{
	CSESLog::WriteLine("CNetSSLClientWrapper::Send>>Start");

	if (0 == iSize)
	{
		return 0;
	}
	int iStrLength = iSize + 1; // add terminal
	int ret = 0;
	int ipkgsize = 4 + iStrLength;
	unsigned char* pBuf = NULL;
	do
	{
		try
		{
			pBuf = new unsigned char[ipkgsize];
		} catch (...)
		{
			pBuf = NULL;
			break;
		}

		memset(pBuf, 0, ipkgsize);
		memcpy(pBuf, &ipkgsize, sizeof(ipkgsize));
		memcpy(pBuf + sizeof(ipkgsize), pucMsg, iSize);

		CSESLog::WriteLine(ipkgsize,
				"CNetSSLClientWrapper::m_streamLock.acquire(); will mc_tcp_stream->send_n ");

		if (NULL == mc_tcp_stream)
		{
			ret = -1;
			break;
		}
		m_streamSendLock.acquire();
		ret = mc_tcp_stream->send_n(pBuf, ipkgsize);//,0,&t_value);
		m_streamSendLock.release();

		if (1 > ret)
		{
			CSESLog::WriteLine(ret, "CNetSSLClientWrapper::SendN: ");
			CSESLog::WriteLine(ACE_OS_String::strerror(errno));
		}
	} while (0);

	CSESLog::WriteLine(ret, "CNetSSLClientWrapper::m_streamLock.release();");

	if (NULL != pBuf)
	{
		delete pBuf;
		pBuf = NULL;
	}
	CSESLog::WriteLine("CNetSSLClientWrapper::Send<<End");
	return ret;
}

/**************************************************************************************************
 * Function Name	: SendN
 * Description		: SendN
 * Date				: 08/09/15
 * Parameter		: char* pRecvData, int iBufSize
 * Return Code		: Return > 0 sent bytes;Return = 0 socket close;Return < 0
 * 						  socket error
 * Author			: ChenWei
 **************************************************************************************************/
int CNetSSLClientWrapper::SendN(char* pRecvData, int iBufSize, int iTimeout)
{

	//check args.
	CSESLog::WriteLine(iBufSize, "CNetSSLClientWrapper::SendN>>Start size:");
	if (NULL == pRecvData || iBufSize <= 0)
	{
		return -1;
	}
	int ret = 0;
	int index = 0;

	ACE_Time_Value t_value(iTimeout);
	m_streamSendLock.acquire();
	CSESLog::WriteLine(iBufSize,
			"CNetSSLClientWrapper::m_streamLock.acquire(); will mc_tcp_stream->send_n ");
	do
	{
		if (NULL == mc_tcp_stream)
		{
			ret = -1;
			break;
		}
		ret = mc_tcp_stream->send_n(pRecvData + index, iBufSize - index);//,0,&t_value);
		//		int var = ACE::get_flags(mc_tcp_stream->get_handle());
		//		cout << "ACE::get_flags" << var << endl;
		if (ret > 0)
		{
			index += ret;
			stringstream strStream;
			strStream << ret;
			std::string str = "CNetSSLClientWrapper::SendN>>message's length:";
			if (strStream.good())
			{
				str.append(strStream.str());
				CSESLog::WriteLine(str);
			}
			else
			{
				//error occur
				CSESLog::WriteLine(
						"CNetSSLClientWrapper::Send>>strStream.good() Err",
						CSESLog::WARNING);
			}
		}
		if (-1 == ret)
		{
			CSESLog::WriteLine("CNetSSLClientWrapper::SendN: -1");
			CSESLog::WriteLine(ACE_OS_String::strerror(errno));
		}

		if (0 == ret)
		{
			CSESLog::WriteLine("CNetSSLClientWrapper::SendN: 0");
		}

	} while (index < iBufSize && ret > 0);
	//ACE_OS::thread_mutex_unlock(&m_streamLock);
	m_streamSendLock.release();
	CSESLog::WriteLine(iBufSize,
			"CNetSSLClientWrapper::m_streamLock.release();");
	//while (index < iBufSize);
	CSESLog::WriteLine(iBufSize, "CNetSSLClientWrapper::SendN>>ed ");
	return index;
}

/**************************************************************************************************
 * Function Name	: RecvN
 * Description		: RecvN
 * Date				: 08/09/15
 * Parameter		: char* pRecvData, int iBufSize
 * Return Code		: Return > 0 Recv bytes;Return = 0 socket close;Return < 0
 * 						  socket error
 * Author			: ChenWei
 **************************************************************************************************/
int CNetSSLClientWrapper::RecvN(char* pRecvData, int iBufSize, int iTimeout)
{
	if (NULL == pRecvData || iBufSize <= 0)
	{
		return -1;
	}
	int ret = 0;
	int index = 0;
	ACE_Time_Value t_value(iTimeout);
	//ACE_OS::thread_mutex_lock(&m_streamLock);
	//iTimeout=iBufSize/
	m_streamLock.acquire();
	do
	{

		if (0 == iTimeout)
		{
			// modify timeout time
			//iTimeout == iBufSize / 1000 + 3;
			//t_value.set(iTimeout, 0);

			ret = mc_tcp_stream->recv_n(pRecvData + index, iBufSize - index);//,0,&t_value);//,&t_value);
			stringstream strRecvLog;
			strRecvLog << iBufSize;
			strRecvLog << ret;
			std::string str = "CNetSSLClientWrapper::RecvN iBufSize ret:";
			if (strRecvLog.good())
			{
				str.append(strRecvLog.str());
				CSESLog::WriteLine(str);
			}
			else
			{
				//error occur
				CSESLog::WriteLine(
						"CNetSSLClientWrapper::RecvN>>strStream.good() Err",
						CSESLog::WARNING);
			}

		}
		else
		{
			ret = mc_tcp_stream->recv_n(pRecvData + index, iBufSize - index, 0,
					&t_value);
		}

		int var = ACE::get_flags(mc_tcp_stream->get_handle());
		CSESLog::WriteLine(var, "ACE::get_flags(mc_tcp_stream->get_handle())");
		if (ret > 0)
		{

			index += ret;
		}

		if (-1 == ret)
		{
			//ACE::sock_error
			CSESLog::WriteLine("CNetSSLClientWrapper::RecvN: -1");
			CSESLog::WriteLine(ACE_OS_String::strerror(errno));
		}

		if (0 == ret)
		{
			CSESLog::WriteLine("CNetSSLClientWrapper::RecvN: 0");
			break;
		}

	} while (index < iBufSize && ret > 0);
	//ACE_OS::thread_mutex_unlock(&m_streamLock);
	m_streamLock.release();
	if (ret < 1)
	{
		CSESLog::WriteLine("CNetSSLClientWrapper::RecvN>>error");
		index = ret;
	}

	return index;
}

/**************************************************************************************************
 * Function Name	: Recv
 * Description		: Recv
 * Date				: 08/09/15
 * Parameter		: std::string &buff
 * Return Code		: Return > 0 Recv bytes;Return = 0 socket close;Return < 0
 * 						  socket error
 * Author			: ChenWei
 **************************************************************************************************/
int CNetSSLClientWrapper::Recv(std::string &buff, int iTimeout, int iCompress)
{
	CSESLog::WriteLine("CNetSSLClientWrapper::Recv>>Start");
	ACE_Time_Value t_value(iTimeout);
	int pkgSize = 0;
	int recv1 = RecvN((char*) &pkgSize, sizeof(pkgSize), iTimeout);
	int recv2 = 0;
	int iReturn = 0;
	unsigned char* pucStr = NULL;
	char* pData = NULL;
	do
	{
		if (recv1 < 1)
		{
			iReturn = recv1;
			CSESLog::WriteLine("CNetSSLClientWrapper::Recv<< head error");
			break;
		}
		else if (pkgSize > 0)
		{

			try
			{
				pData = new char[pkgSize];
			} catch (...)
			{
				CSESLog::WriteLine(pkgSize,
						"CNetSSLClientWrapper::Recv<< pData new ERR:");
				break;
			}
			memset(pData, 0, pkgSize);

			//int recv2 = mc_tcp_stream->recv_n(pData, sizeof(pkgSize-4));
			recv2 = RecvN(pData, pkgSize - 4, iTimeout);
			if (recv2 < 1)
			{
				CSESLog::WriteLine("CNetSSLClientWrapper::Recv<<  error");
				iReturn = recv2;
			}
			else if (iCompress)
			{
				//	unsigned long iLen = 0;
				try
				{
					pucStr = new unsigned char[recv2];
				} catch (...)
				{
					pucStr = NULL;
					iReturn = 0;
					break;
				}
				if (NULL != pucStr)
				{
					memset(pucStr, 0, recv2);
					memcpy(pucStr, pData, recv2);
					iReturn = CSESDataTools::Uncompress(&pucStr, buff, recv2);
				}
				else
				{
					iReturn = 0;
				}
			}
			else
			{
				buff.assign(pData);
			}

		}
		if (iReturn > 0)
		{
			iReturn = recv1 + recv2;
		}
	} while (0);
	if (NULL != pData)
	{

		delete pData;
		pData = NULL;
	}
	CSESLog::WriteLine("CNetSSLClientWrapper::Recv<<End");
	return iReturn;
}

/**************************************************************************************************
 * Function Name	: GetErrInfo
 * Description		: GetErrInfo
 * Date				: 08/09/15
 * Parameter		: std::string& errMsg
 * Return Code		:
 * Author			: ChenWei
 **************************************************************************************************/
void CNetSSLClientWrapper::GetErrInfo(std::string& errMsg)
{
	errMsg = ACE_OS_String::strerror(errno);
}

/**************************************************************************************************
 * Function Name	: GetHandle
 * Description		: GetHandle
 * Date				: 08/09/15
 * Parameter		:
 * Return Code		:
 * Author			: ChenWei
 **************************************************************************************************/
ACE_HANDLE CNetSSLClientWrapper::GetHandle(void) const
{
	int iReturn = -1;
	if (NULL != mc_tcp_stream)
	{
		iReturn = mc_tcp_stream->get_handle();
	}
	else
	{
		CSESLog::WriteLine(
				"CNetSSLClientWrapper::GetHandle(void) return -1 Error<<End");
	}
	return iReturn;
}

void CNetSSLClientWrapper::GetCAInfo(std::string& CAInfo)
{
	//mc_tcp_stream.GetCAInfo(CAInfo);
	//	int   gethostname(char   *hostname,size_t   size)     //获得主机名
	//	  struct   hostent   *gethostbyname(const   char   *name);   //获得本机信息
	if (NULL == mc_tcp_stream)
	{
		return;
	}
	SSL* pSsl = mc_tcp_stream->ssl();
	char * str;
	X509 * server_cert;

	/* Get the cipher - opt */
	const char* pssl = SSL_get_cipher(pSsl);
	CSESLog::WriteLine(pssl);
	/* Get server's certificate (note: beware of dynamic allocation) - opt */

	server_cert = SSL_get_peer_certificate(pSsl);

	str = X509_NAME_oneline(X509_get_subject_name(server_cert), 0, 0);
	if (str != NULL)
	{

		CSESLog::WriteLine(str);
		CAInfo.append(str);
		OPENSSL_free (str);
	}

	str = X509_NAME_oneline(X509_get_issuer_name(server_cert), 0, 0);
	if (str != NULL)
	{
		CSESLog::WriteLine(str);
		CAInfo.append(str);
		OPENSSL_free (str);
	}

	/* We could do all sorts of certificate verification stuff here before
	 deallocating the certificate. */
	X509_free(server_cert);
}

/**************************************************************************************************
 * Function Name	: GetTcpStream
 * Description		: GetTcpStream
 * Date				: 08/09/15
 * Parameter		: std::string& errMsg
 * Return Code		: ACE_SSL_SOCK_STREAM*
 * Author			: ChenWei
 **************************************************************************************************/
ACE_SSL_SOCK_STREAM* CNetSSLClientWrapper::GetTcpStream() const
{
	if (NULL != mc_tcp_stream)
	{
		return mc_tcp_stream;
	}

	else
	{
		CSESLog::WriteLine(
				"CNetSSLClientWrapper::GetTcpStream() return NULL<<End");
		return NULL;
	}

}

/**************************************************************************************************
 * Function Name	: SetOption
 * Description		: SetOption
 * Date				: 08/09/15
 * Parameter		:
 * Return Code		:
 * Author			: ChenWei
 **************************************************************************************************/
int CNetSSLClientWrapper::SetOption()
{

	if (m_bIsConnect)
	{
		int keepAlive = 1;//设定KeepAlive
		int keepIdle = 5;//开始首次KeepAlive探测前的TCP空闭时间
		int keepInterval = 5;//两次KeepAlive探测间的时间间隔
		int keepCount = 3;//判定断开前的KeepAlive探测次数
		if (mc_tcp_stream->set_option(SOL_SOCKET, SO_KEEPALIVE,
				(void*) &keepAlive, sizeof(keepAlive)) == -1)
		{
			CSESLog::WriteLine(
					"CNetSSLClientWrapper setsockopt SO_KEEPALIVE error!");
			CSESLog::WriteLine(ACE_OS_String::strerror(errno));
		}
		if (mc_tcp_stream->set_option(SOL_TCP, TCP_KEEPIDLE,
				(void *) &keepIdle, sizeof(keepIdle)) == -1)
		{
			CSESLog::WriteLine(
					"CNetSSLClientWrapper setsockopt TCP_KEEPIDLE error!");
			CSESLog::WriteLine(ACE_OS_String::strerror(errno));
		}
		if (mc_tcp_stream->set_option(SOL_TCP, TCP_KEEPINTVL,
				(void*) &keepInterval, sizeof(keepInterval)) == -1)
		{
			CSESLog::WriteLine(
					"CNetSSLClientWrapper setsockopt TCP_KEEPINTVL error!");
			CSESLog::WriteLine(ACE_OS_String::strerror(errno));
		}
		if (mc_tcp_stream->set_option(SOL_TCP, TCP_KEEPCNT, (void*) &keepCount,
				sizeof(keepCount)) == -1)
		{
			CSESLog::WriteLine(
					"CNetSSLClientWrapper setsockopt TCP_KEEPCNT error!");
			CSESLog::WriteLine(ACE_OS_String::strerror(errno));
		}
	}
	return 0;
}

