// HTTPReader.cpp: implementation of the CHTTPReader class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "aplSocketTransport.h"
#include "commands.h"

//#include <string.h>
//#include <tchar.h>

#pragma comment(lib,"wininet")

CHTTPReader::CHTTPReader() :
	m_hInternet(NULL),
	m_hConnection(NULL),
	m_hRequest(NULL),
	m_lpszServerName(_T("")),
	m_lpszDefaultHeader(_T("")),
	m_lpszDataBuffer(NULL),
	m_dwBufferSize(0),
	m_bUseSSL(false),
	m_dwLastError(0),
	m_ClientId(0)
{
	m_ProxyLogin=_T("");
	m_ProxyPassword=_T("");

	m_first = true;
	m_HTTP_SPEC_REQUEST_INDEX=0;
}


CHTTPReader::CHTTPReader(LPCTSTR lpszServerName, INTERNET_PORT port, bool bUseSSL, LPCTSTR proxy )
: m_hInternet(NULL),
  m_hConnection(NULL),
  m_hRequest(NULL),
  m_lpszServerName(_T("")),
  m_lpszDefaultHeader(_T("")),
  m_lpszDataBuffer(NULL),
  m_dwBufferSize(0),
  m_bUseSSL(bUseSSL),
  m_dwLastError(0),
  m_ClientId(0)
{
	SetDefaultHeader(
		_T("Content-Type: application/octet-stream\r\n")
		_T("Access-Control-Allow-Origin: *")
	    _T("Accept-Language:ru\r\n")
	    _T("Accept-Encoding:gzip, deflate"));

	SetServer(lpszServerName, port, bUseSSL, proxy);
}

bool CHTTPReader::SetServer(LPCTSTR lpszServerName, INTERNET_PORT port, bool bUseSSL, LPCTSTR proxy)
{
	m_lpszServerName=lpszServerName;
	if(port==0)
	{
		m_port = m_bUseSSL? INTERNET_DEFAULT_HTTPS_PORT: INTERNET_DEFAULT_HTTP_PORT;
	}
	else
	{
		m_port = port;
	}

	m_ProxyLogin=_T("");
	m_ProxyPassword=_T("");

	if(!aplIsStringEmpty(proxy)){m_ProxyName = proxy;}else{m_ProxyName = _T("");}

	m_first = true;
	m_HTTP_SPEC_REQUEST_INDEX=0;

//	m_log.SetFileName(_T("D:\\_swh\\_____logs\\aaa.log"));

	m_class_info.Format(_T("CHTTPReader ") POINTER_FORMAT _T("; create in thread ") THREAD_FORMAT ,this,GetCurrentThreadId());

	return true;
}

CHTTPReader::~CHTTPReader()
{
	delete m_lpszDataBuffer;
	SetDefaultHeader(NULL);
}

#define CheckErrorR(condition) CheckError(condition);return condition;

#define CheckError(condition) \
	if (!condition) {DWORD m_LastWsaError; CString str; m_dwLastError = ::GetLastError();\
		switch(m_dwLastError){\
		case 0:break;\
		default:GetDescriptionSystemError(m_dwLastError,str);APL_TRANSPORT_THROW_EX(ASC_ERROR_IN_WININET,str);break;}}

/*
bool CHTTPReader::CheckError(bool bTest)
{
	if (bTest == false) 
	{
		m_dwLastError = ::GetLastError();
	}
	DWORD m_LastWsaError;
	CString str;
	switch(m_dwLastError)
	{
	case 0:
		break;
	default:
		GetDescriptionSystemError(m_dwLastError,str);
		APL_TRANSPORT_ _EX(ASC_ERROR_IN_WININET,str);
		break;
	}
	return bTest;
}
*/

bool CHTTPReader::OpenInternet(LPCTSTR lpszAgent)
{
	if (m_hInternet == NULL)
	{
		LPCTSTR proxy = NULL;
		DWORD dwAccessType;
		if(m_ProxyName == _T("DIRECT"))
		{
			dwAccessType = INTERNET_OPEN_TYPE_DIRECT;
		}
		else if(m_ProxyName == _T("IE") || m_ProxyName == _T(""))
		{
			dwAccessType = INTERNET_OPEN_TYPE_PRECONFIG;
		}
		else
		{
			proxy = (LPCTSTR)m_ProxyName;
			dwAccessType = INTERNET_OPEN_TYPE_PROXY;
		}
		m_hInternet = ::InternetOpen(lpszAgent, dwAccessType, proxy, NULL, 0);
		if (m_hInternet != NULL)
		{
			DWORD timeout = 5*60*60*1000;
			if(InternetSetOption(m_hInternet,INTERNET_OPTION_RECEIVE_TIMEOUT  ,&timeout,4)==FALSE)
			{
				CheckError(m_hInternet != NULL);
			}
			
			if(InternetSetOption(m_hInternet,INTERNET_OPTION_SEND_TIMEOUT  ,&timeout,4)==FALSE)
			{
				CheckError(m_hInternet != NULL);
			}
// 			DWORD t1,t2,t3,l1,l2,l3;
// 			InternetQueryOption(m_hInternet,INTERNET_OPTION_RECEIVE_TIMEOUT  ,&t1,&l1);
// 			int a=0;
		}
	}
	CheckErrorR(m_hInternet != NULL);
}

void CHTTPReader::CloseInternet ()
{
	CloseConnection();
	
	if (m_hInternet) 
		::InternetCloseHandle(m_hInternet);
	m_hInternet = NULL;
}


bool CHTTPReader::OpenConnection (LPCTSTR lpszServerName)
{
	if (OpenInternet(HTTP_NAME_APPLICATION_BASE HTTP_NAME_APPLICATION_VER) && m_hConnection == NULL)
	{
		CString ServerName;
		if(!aplIsStringEmpty(lpszServerName))
		{
			ServerName = lpszServerName;
		}
		else if(m_lpszServerName != _T(""))
		{
			ServerName = m_lpszServerName;
		}
		else
		{
			ServerName = _T("localhost");
		}


		m_hConnection = ::InternetConnect(m_hInternet,ServerName,m_port,m_ProxyLogin,m_ProxyPassword,INTERNET_SERVICE_HTTP,0,1u);
		//AAA m_hConnection = ::InternetConnect(m_hInternet,ServerName,m_port,"deschere","ahjyn",INTERNET_SERVICE_HTTP,0,1u);
	}
	CheckErrorR(m_hConnection != NULL);
}

void CHTTPReader::CloseConnection ()
{
	CloseRequest();

	if (m_hConnection)
		::InternetCloseHandle(m_hConnection);
	m_hConnection = NULL;
}

bool CHTTPReader::SendRequest (LPCTSTR lpszVerb, LPCTSTR lpszAction, LPVOID lpszData, DWORD DataLength, LPCTSTR lpszReferer)
{
	CString curr_header;
	CString str;
	TCHAR cbuf[256];
	HWND hWnd;
	if(m_first)
	{
		str.Format( APL_T("Устанавливаем соединение с сервером %s:%i"), (LPCTSTR)m_lpszServerName, m_port);
	}
	long wait_dlg_h=0;	
	
	if (OpenConnection()) 
	{
		CloseRequest();

		DWORD dwFlags = (m_bUseSSL? INTERNET_FLAG_SECURE|INTERNET_FLAG_IGNORE_CERT_CN_INVALID: 0) ;
			//| INTERNET_FLAG_KEEP_CONNECTION ;

		LPCTSTR AcceptTypes[] = { _T("*/*"), NULL}; 

		curr_header.Format(m_lpszDefaultHeader+_T("\r\n")
			HTTP_CLIENT_ID_HEADER _T(":%i\r\n") 
			HTTP_SPEC_REQUEST_INDEX _T(":%i\r\n")
			HTTP_SPEC_REQUEST_DATA_SIZE _T(":%i\r\n")
			_T("Content-Length: %i"),
			m_ClientId, InterlockedIncrement(&m_HTTP_SPEC_REQUEST_INDEX), DataLength, DataLength);
		//LD

		if(aplIsStringEmpty(lpszAction))
		{
			lpszAction = cbuf;
			_sprintf(cbuf,_T("?SPEC_REQUEST_INDEX:%i"), InterlockedExchangeAdd(&m_HTTP_SPEC_REQUEST_INDEX, 0));
		}
		
		m_hRequest = ::HttpOpenRequest(m_hConnection, lpszVerb, lpszAction, NULL, lpszReferer, AcceptTypes, dwFlags, 1);

		if (m_hRequest != NULL) 
		{			
			do 
			{
				if(m_first)
				{
					wait_dlg_h = StartTransportWaitDlg(str);
				}				
				
				if (::HttpSendRequest(m_hRequest, curr_header, -1, lpszData, DataLength) == FALSE) 
				{					
					EndTransportWaitDlg(wait_dlg_h, false);
					CheckError(false);
					CloseRequest();
					QueryAnswerHeaders();
					return false;
				}
				
				EndTransportWaitDlg(wait_dlg_h,false);
				QueryAnswerHeaders();

//LD
// LOG_WARN(APL_T("ЗАГОЛОВКИ ОТВЕТА:\r\n===========================\r\n")+m_hdr_all+_T("\r\n===========================\r\n"));
// str.Format(APL_T("Ответ размером %i"),m_hdr_spec_answer_datasize);
// m_log.LogMessage(str,true,false,true,true);
//LD


				switch (m_dwStatus)
				{
				case HTTP_STATUS_PROXY_AUTH_REQ:    //Proxy Authentication Required
					
					if(AfxGetMainWnd()!=0)
					{
						hWnd = AfxGetMainWnd()->m_hWnd;
					}
					else
					{
						hWnd=GetTopWindow(0);
					}

					if ( InternetErrorDlg (hWnd, m_hRequest, ERROR_INTERNET_INCORRECT_PASSWORD,
							FLAGS_ERROR_UI_FILTER_FOR_ERRORS | FLAGS_ERROR_UI_FLAGS_GENERATE_DATA | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS, NULL) == ERROR_INTERNET_FORCE_RETRY) 
					{

						continue;
					}
					break;
				case HTTP_STATUS_DENIED:     //Server Authentication Required
					// Insert code to set strUsername and strPassword.
					LOG_WARN(_T("Autorisation need"));
					AfxMessageBox(_T("Autorisation need"));
// 					InternetSetOption(m_hRequest, INTERNET_OPTION_USERNAME, UName, strlen(UName)+1);
// 					InternetSetOption(m_hRequest, INTERNET_OPTION_PASSWORD, Pass, strlen(Pass)+1);
					continue;
				case HTTP_STATUS_OK:
					m_first = false;
					return true;
					break;
				case HTTP_STATUS_ACCEPTED:
					m_first = false;
					return true;
					break;
				default:
					if(m_dwStatus >= 400)
					{
						CheckError(false);
						DWORD m_LastWsaError;
						APL_TRANSPORT_THROW_EX(ASC_ERROR_IN_HTTP_REQ,curr_header);
					}
				}
			} while (true);
		}
	}

	CheckErrorR(m_hRequest != NULL);
}

bool CHTTPReader::Get (LPCTSTR lpszAction,LPCTSTR lpszReferer)
{
	return SendRequest(_T("GET"),lpszAction,NULL,0,lpszReferer);
}

bool CHTTPReader::Post (LPCTSTR lpszAction,LPVOID lpszData,DWORD DataLength,LPCTSTR lpszReferer)
{
	return SendRequest(_T("POST"),lpszAction,lpszData,DataLength,lpszReferer);
}

void CHTTPReader::CloseRequest ()
{
	if (m_hRequest)
		::InternetCloseHandle(m_hRequest);
	m_hRequest = NULL;
}

void CHTTPReader::StrDup (LPTSTR& lpszDest, LPCTSTR lpszSource)
{
	delete lpszDest;

	if (aplIsStringEmpty(lpszSource)) 
	{
		lpszDest = NULL;
	}
	else 
	{
		lpszDest = new TCHAR[_tcslen(lpszSource)+1];
		_tcscpy(lpszDest,lpszSource);
	}
}
																			    
void CHTTPReader::SetDefaultHeader (LPCTSTR lpszDefaultHeader)
{
	m_lpszDefaultHeader=lpszDefaultHeader;
}

BYTE *CHTTPReader::GetData(BYTE *lpszBuffer, DWORD dwSize, DWORD *lpdwBytesRead)
{
	DWORD dwBytesRead;
	if (lpdwBytesRead == NULL)
		lpdwBytesRead = &dwBytesRead;
	*lpdwBytesRead = 0;

	if (m_hRequest) 
	{
		bool bRead = ::InternetReadFile(m_hRequest,lpszBuffer,dwSize,lpdwBytesRead) != FALSE;
		if(*lpdwBytesRead<dwSize)
		{
			lpszBuffer[*lpdwBytesRead] = 0;
		}
		CheckError(bRead);
		return bRead && *lpdwBytesRead? lpszBuffer: NULL;
	}

	return NULL;
}

#define QUERY_HEADER_STR(name_header, plaseholder) \
	dwLengthDataSize = dwDefLengthDataSize;\
	_sprintf(buf,name_header);\
	if(::HttpQueryInfo(m_hRequest, HTTP_QUERY_CUSTOM, buf, &dwLengthDataSize, NULL) == TRUE)\
	{plaseholder = buf;}else{plaseholder=_T("");}

#define QUERY_HEADER_INT(name_header, plaseholder) \
	dwLengthDataSize = dwDefLengthDataSize;\
	_sprintf(buf,name_header);\
	if(::HttpQueryInfo(m_hRequest, HTTP_QUERY_CUSTOM, buf, &dwLengthDataSize, NULL) == TRUE)\
	{plaseholder = _atoi(buf);}else{plaseholder=0;}


bool CHTTPReader::QueryAnswerHeaders()
{

	DWORD dwDefLengthDataSize = 1024;
	DWORD dwLengthDataSize;
	TCHAR* buf = new TCHAR[dwDefLengthDataSize];

	QUERY_HEADER_STR(_T("Server"),m_hdr_server);
	QUERY_HEADER_INT(_T("PSSVersionBufferVer"),m_hdr_version_num);
	QUERY_HEADER_INT(HTTP_CLIENT_ID_HEADER,m_ClientId);
	QUERY_HEADER_INT(HTTP_SPEC_ANSWER_INDEX,m_hdr_spec_answer_index);
	QUERY_HEADER_INT(HTTP_SPEC_ANSWER_DATASIZE,m_hdr_spec_answer_datasize);
	QUERY_HEADER_INT(HTTP_SPEC_ANSWER_CRC_ORIGINAL,m_hdr_spec_answer_crc_original);
	QUERY_HEADER_INT(HTTP_SPEC_ANSWER_CRC_ENCODED,m_hdr_spec_answer_crc_encoded);
	QUERY_HEADER_INT(HTTP_SPEC_ORIGINAL_DATA_SIZE,m_hdr_spec_original_data_size);


	dwLengthDataSize=4;
	HttpQueryInfo (m_hRequest, HTTP_QUERY_STATUS_CODE |HTTP_QUERY_FLAG_NUMBER, &m_dwStatus, &dwLengthDataSize, NULL);


	dwLengthDataSize = dwDefLengthDataSize;
	_sprintf(buf,_T("PSS_ASC_ErrorCode"));
	if(::HttpQueryInfo(m_hRequest, HTTP_QUERY_CUSTOM, buf, &dwLengthDataSize, NULL) == TRUE)
	{m_asc_code = (ASC_ERROR_CODES)_atoi(buf);}else{m_asc_code=ASC_ERROR_IN_CONNECT;}

	dwLengthDataSize = dwDefLengthDataSize;
	if(::HttpQueryInfo(m_hRequest, HTTP_QUERY_STATUS_TEXT, buf, &dwLengthDataSize, NULL) == TRUE)
	{m_StatusText = buf;}else{m_StatusText = _T("??");}

	delete[] buf;

	// на время отладки!!!
	dwLengthDataSize = 1024*1024;
	buf = new TCHAR[dwLengthDataSize];

	if(::HttpQueryInfo (m_hRequest, HTTP_QUERY_RAW_HEADERS_CRLF, buf, &dwLengthDataSize, NULL)==FALSE)
	{
		m_hdr_all = _T("");
	}
	else
	{
		buf[dwLengthDataSize]=0;
		m_hdr_all = buf;
	}

	delete[] buf;

	return true;
}

DWORD CHTTPReader::GetDataSize()
{
	if (m_hRequest) 
	{
		DWORD dwDataSize = 0;
		DWORD dwLengthDataSize = sizeof(dwDataSize);

		BOOL bQuery = ::HttpQueryInfo(m_hRequest, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &dwDataSize, &dwLengthDataSize, NULL);
		if(bQuery == TRUE)
		{
			return dwDataSize;
		}
		else
		{
			CString buf;
			DWORD err = GetLastError();
			GetDescriptionSystemError(err, buf);
			DWORD e1 = ERROR_HTTP_HEADER_NOT_FOUND;
			return 0;
		}
	}

	return 0;
}

BYTE *CHTTPReader::GetData (DWORD *lpdwBytesRead)
{
	DWORD dwDataSize = GetDataSize();
	SetDataBuffer(dwDataSize);	
	return GetData(
		m_lpszDataBuffer, 
		dwDataSize? dwDataSize: m_dwBufferSize, 
		lpdwBytesRead);
}

void CHTTPReader::SetDataBuffer (DWORD dwBufferSize)
{
	if (dwBufferSize > m_dwBufferSize) {
		delete m_lpszDataBuffer;
		m_lpszDataBuffer = new BYTE[(m_dwBufferSize = dwBufferSize) + 1];
	}
}
