#include "stdafx.h"
#include "CurlClient.h"
#include "CharEncoding.h"
#include <sys/stat.h>

CCurlClient::CCurlClient(void)
{
	m_bDebug = FALSE;
	m_headers = NULL;
}

CCurlClient::~CCurlClient(void)
{
	// 释放curl的全局对象;
	ClearHeaders();
	curl_global_cleanup(); 
}

INT CCurlClient::Initialize()
{
	// 初始化全局调用模式;
	CURLcode res = ::curl_global_init( CURL_GLOBAL_ALL );
	if( CURLE_OK != res ) 
	{
		fprintf( stderr, "curl_global_init failed: %d \n", res ); 
		return -1;
	}

	return 0;
}

static int OnDebug(CURL *, curl_infotype itype, char * pData, size_t size, void *)   
{   
	if(itype == CURLINFO_TEXT)   
	{   
		//TRACE("[TEXT]%s\n", pData); 
		//LOG4C((LOG_WARN, "[TEXT]%s\n", pData));   
	}   
	else if(itype == CURLINFO_HEADER_IN)   
	{   
		//TRACE("[HEADER_IN]%s\n", pData); 
		//LOG4C((LOG_WARN, "[HEADER_IN]%s\n", pData));
	}   
	else if(itype == CURLINFO_HEADER_OUT)   
	{   
		//TRACE("[HEADER_OUT]%s\n", pData);   
		//LOG4C((LOG_WARN, "[HEADER_OUT]%s\n", pData));
	}   
	else if(itype == CURLINFO_DATA_IN)   
	{   
		//TRACE("[DATA_IN]%s\n", pData);   
		//LOG4C((LOG_WARN, "[DATA_IN]%s\n", pData));
	}   
	else if(itype == CURLINFO_DATA_OUT)   
	{   
		//TRACE("[DATA_OUT]%s\n", pData);   
		//LOG4C((LOG_WARN, "[DATA_OUT]%s\n", pData));
	}   
	return 0;   
}   

size_t CCurlClient::OnWriteData(const void *ptr, size_t size, size_t nmemb, std::string *stream)   
{   
	if( NULL == stream || NULL == ptr )
		return -1;

	stream->append((char*)ptr, size * nmemb);   
	return nmemb;   
}  

size_t CCurlClient::OnWriteFile(const void *ptr, size_t size, size_t nmemb, void *stream)   
{   
	if( NULL == stream || NULL == ptr )
		return -1;

	return fwrite(ptr, size, nmemb, (FILE*)stream);  
}

size_t CCurlClient::OnGetContentLength(void *ptr, size_t size, size_t nmemb, void *stream)
{
	int r;
	long len = 0;

	/* _snscanf() is Win32 specific */
	// r = _snscanf(ptr, size * nmemb, "Content-Length: %ld\n", &len);
	r = sscanf_s((char*)ptr, "Content-Length: %ld\n", &len);
	if (r) /* Microsoft: we don't read the specs */
		*((long *) stream) = len;

	return size * nmemb;
}

int CCurlClient::Post(const std::string & strUrl, const std::string & strPost, std::string & strResponse, long time_out /*= 3*/)
{   
	CURLcode res;   
	CURL* curl = curl_easy_init();   
	if(NULL == curl)   
	{   
		return CURLE_FAILED_INIT;   
	}   
	if(m_bDebug)   
	{// 是否开启调试日志输出;   
		curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);   
		curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, OnDebug);   
	}   
	// 设置URL地址;
	curl_easy_setopt(curl, CURLOPT_URL, strUrl.c_str());   
	// 设置POST方式;
	curl_easy_setopt(curl, CURLOPT_POST, 1);   
	// 设置POST参数;
	curl_easy_setopt(curl, CURLOPT_POSTFIELDS, strPost.c_str());  
    // 设置回调函数-读取;	
	curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL);   
	// 设置回调函数-写入;
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, OnWriteData);   
	// 设置回调函数-写入的缓存区;
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&strResponse);   
	// 设置(多线程下,只是尽量减少)无签名;
	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);   
	// 设置连接超时值(单位毫秒);
	curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30000); 
	// 设置操作超时值(单位毫秒);
	curl_easy_setopt(curl, CURLOPT_TIMEOUT, time_out);
	// 设置头;
	curl_easy_setopt(curl, CURLOPT_HTTPHEADER, m_headers);

	// 执行POST提交;
	res = curl_easy_perform(curl);   
	
	// 释放资源;
	curl_easy_cleanup(curl); 
	//curl_global_cleanup();
	ClearHeaders(); /* free the header list */

	return res;   
}  

int CCurlClient::Post(IN LPCTSTR lpUrl, IN LPCTSTR lpPost, OUT LPTSTR lpResponse, IN CONST INT& nMaxlen, long time_out /*= 3*/)
{
	if ( lpUrl == NULL || lpPost == NULL )
		return CURLE_FAILED_INIT;

	string strUrl;
	string strPost;
	string strResponse;
#ifdef UNICODE
	CharEncoding::UNICODE2ASCII((LPWCH)lpUrl, strUrl);
	CharEncoding::UNICODE2ASCII((LPWCH)lpPost, strPost);

	int res = Post(strUrl, strPost, strResponse) ;
	if ( CURLE_OK == res )
	{
		CharEncoding::ASCII2UNICODE(strResponse.c_str(), (LPWCH)lpResponse, nMaxlen);
		return CURLE_OK;
	}

	return res;
#else
	strUrl = lpUrl;
	strPost = lpPost;
	int res = Post(strUrl, strPost, strResponse, time_out) ;
	if ( CURLE_OK == res )
	{
		sprintf_s(lpResponse, nMaxlen, "%s", strResponse.c_str());
		return CURLE_OK;
	}
	return res;
#endif
}

int CCurlClient::Post(IN CString& strUrl, IN CString& strPost, OUT CString& strResponse, long time_out /*= 3*/)
{
	if ( strUrl.IsEmpty() || strPost.IsEmpty() )
		return CURLE_FAILED_INIT;

	string url;
	string post;
	string response;
#ifdef UNICODE
	CharEncoding::UNICODE2ASCII((LPWCH)strUrl.GetString(), url);
	CharEncoding::UNICODE2ASCII((LPWCH)strPost.GetString(), post);

	int res = Post(url, post, response) ;
	if ( CURLE_OK == res )
	{
		WCHAR* pResult = CharEncoding::ASCII2UNICODE(response.c_str());
		if ( pResult )
		{
			strResponse = pResult;
			delete []pResult;
			pResult = NULL;
			return CURLE_OK;
		}
	}

	return res;
#else
	url = strUrl.GetString();
	post = strPost.GetString();
	int res = Post(url, post, response, time_out) ;
	if ( CURLE_OK == res )
	{
		strResponse = response.c_str();
		return CURLE_OK;
	}
	return res;
#endif
}

int CCurlClient::Get(const std::string & strUrl, std::string & strResponse, long time_out /*= 3*/)
{   
	CURLcode res;   
	CURL* curl = curl_easy_init();   
	if(NULL == curl)   
	{   
		return CURLE_FAILED_INIT;   
	}   
	if(m_bDebug)   
	{   
		curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);   
		curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, OnDebug);   
	}   

	// 设置URL地址;
	curl_easy_setopt(curl, CURLOPT_URL, strUrl.c_str());   
	// 设置回调函数-读取;
	curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL);   
	// 设置回调函数-写入;
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, OnWriteData);   
	// 设置回调函数-写入的缓存区;
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&strResponse);   
	/**  
	* 当多个线程都使用超时处理的时候，同时主线程中有sleep或是wait等操作。  
	* 如果不设置这个选项，libcurl将会发信号打断这个wait从而导致程序退出。  
	*/  
	// 设置(多线程下,只是尽量减少)无签名;	
	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);   
	// 设置连接超时值;
	curl_easy_setopt(curl,CURLOPT_CONNECTTIMEOUT,5000);
	// 设置超时值;
	curl_easy_setopt(curl,CURLOPT_TIMEOUT, time_out);
	// 设置头;
	curl_easy_setopt(curl, CURLOPT_HTTPHEADER, m_headers);

	res = curl_easy_perform(curl);   
	curl_easy_cleanup(curl);
	//curl_global_cleanup();
	ClearHeaders(); /* free the header list */

	return res;   
}  

int CCurlClient::Get(IN LPCTSTR lpUrl, OUT LPTSTR lpResponse, IN CONST INT& nMaxlen, long time_out /*= 3*/)
{
	if ( lpUrl == NULL )
		return CURLE_FAILED_INIT;

	string strUrl;
	string strResponse;
#ifdef UNICODE
	CharEncoding::UNICODE2ASCII((LPWCH)lpUrl, strUrl);

	int res = Get(strUrl, strResponse) ;
	if ( CURLE_OK == res )
	{
		CharEncoding::ASCII2UNICODE(strResponse.c_str(), (LPWCH)lpResponse, nMaxlen);
		return CURLE_OK;
	}

	return res;
#else
	strUrl = lpUrl;
	int res = Get(strUrl, strResponse, time_out) ;
	if ( CURLE_OK == res )
	{
		sprintf_s(lpResponse, nMaxlen, "%s", strResponse.c_str());
		return CURLE_OK;
	}
	return res;
#endif
}

int CCurlClient::Get(IN CString& strUrl, OUT CString& strResponse, long time_out /*= 3*/)
{
	if ( strUrl.IsEmpty() )
		return CURLE_FAILED_INIT;

	string url;
	string post;
	string response;
#ifdef UNICODE
	CharEncoding::UNICODE2ASCII((LPWCH)strUrl.GetString(), url);

	int res = Get(url, response) ;
	if ( CURLE_OK == res )
	{
		WCHAR* pResult = CharEncoding::ASCII2UNICODE(response.c_str());
		if ( pResult )
		{
			strResponse = pResult;
			delete []pResult;
			pResult = NULL;
			return CURLE_OK;
		}
	}

	return res;
#else
	url = strUrl.GetString();
	int res = Get(url, response, time_out) ;
	if ( CURLE_OK == res )
	{
		strResponse = response.c_str();
		return CURLE_OK;
	}
	return res;
#endif
}

int CCurlClient::Posts(const std::string & strUrl, const std::string & strPost, std::string & strResponse, const char * pCaPath, long time_out /*= 3*/)
{   
	CURLcode res;   
	CURL* curl = curl_easy_init();   
	if(NULL == curl)   
	{   
		return CURLE_FAILED_INIT;   
	}   
	if(m_bDebug)   
	{   
		curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);   
		curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, OnDebug);   
	}   
	
	// 设置URL地址;
	curl_easy_setopt(curl, CURLOPT_URL, strUrl.c_str());   
	// 设置POST提交方式;
	curl_easy_setopt(curl, CURLOPT_POST, 1);   
	// 设置POST参数;
	curl_easy_setopt(curl, CURLOPT_POSTFIELDS, strPost.c_str());   
	// 设置回调函数-读取;
	curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL);   
	// 设置回调函数-写入;
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, OnWriteData);   
	// 设置回调函数-写入的缓存区;
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&strResponse);   
	// 设置;
	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);   
	if(NULL == pCaPath || pCaPath[0] == '\0')   
	{   
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);   
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);   
	}   
	else   
	{   
		//缺省情况就是PEM，所以无需设置，另外支持DER   
		//curl_easy_setopt(curl,CURLOPT_SSLCERTTYPE,"PEM");   
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, true);   
		curl_easy_setopt(curl, CURLOPT_CAINFO, pCaPath);   
	}   
	// 设置连接超时值;
	curl_easy_setopt(curl,CURLOPT_CONNECTTIMEOUT,5000);
	// 设置超时值;
	curl_easy_setopt(curl,CURLOPT_TIMEOUT, time_out);
	// 设置头;
	curl_easy_setopt(curl, CURLOPT_HTTPHEADER, m_headers);
	
	// 执行POST提交;
	res = curl_easy_perform(curl);   
	
	// 释放资源;
	curl_easy_cleanup(curl); 
	//curl_global_cleanup();
	ClearHeaders(); /* free the header list */

	return res;   
}  

int CCurlClient::Posts(IN LPCTSTR lpUrl, IN LPCTSTR lpPost, OUT LPTSTR lpResponse, IN CONST INT& nMaxlen, IN LPCTSTR lpCaPath /* = NULL */, long time_out /*= 3*/)
{
	if ( lpUrl == NULL || lpPost == NULL )
		return CURLE_FAILED_INIT;

	string strUrl;
	string strPost;
	string strCapath;
	string strResponse;
#ifdef UNICODE
	CharEncoding::UNICODE2ASCII((LPWCH)lpUrl, strUrl);
	CharEncoding::UNICODE2ASCII((LPWCH)lpPost, strPost);
	CharEncoding::UNICODE2ASCII((LPWCH)lpCaPath, strCapath);

	int res = Posts(strUrl, strPost, strResponse, strCapath.c_str()) ;
	if ( CURLE_OK == res )
	{
		CharEncoding::ASCII2UNICODE(strResponse.c_str(), (LPWCH)lpResponse, nMaxlen);
		return CURLE_OK;
	}

	return res;
#else
	strUrl = lpUrl;
	strPost = lpPost;
	strCapath = lpCaPath;
	int res = Posts(strUrl, strPost, strResponse, strCapath.c_str(), time_out ) ;
	if ( CURLE_OK == res )
	{
		sprintf_s(lpResponse, nMaxlen, "%s", strResponse.c_str());
		return CURLE_OK;
	}
	return res;
#endif
}

int CCurlClient::Posts(IN CString& strUrl, IN CString& strPost, OUT CString& strResponse, IN const CString& strCaPath /* = _T("") */, long time_out /*= 3*/)
{
	if ( strUrl.IsEmpty() || strPost.IsEmpty() )
		return CURLE_FAILED_INIT;

	string url;
	string post;
	string capth;
	string response;
#ifdef UNICODE
	CharEncoding::UNICODE2ASCII((LPWCH)strUrl.GetString(), url);
	CharEncoding::UNICODE2ASCII((LPWCH)strPost.GetString(), post);
	CharEncoding::UNICODE2ASCII((LPWCH)strCaPath.GetString(), capth);

	int res = Posts(url, post, response, capth.c_str()) ;
	if ( CURLE_OK == res )
	{
		WCHAR* pResult = CharEncoding::ASCII2UNICODE(response.c_str());
		if ( pResult )
		{
			strResponse = pResult;
			delete []pResult;
			pResult = NULL;
			return CURLE_OK;
		}
	}

	return res;
#else
	url = strUrl.GetString();
	post = strPost.GetString();
	capth = strCaPath.GetString();
	int res = Posts(url, post, response, capth.c_str(), time_out) ;
	if ( CURLE_OK == res )
	{
		strResponse = response.c_str();
		return CURLE_OK;
	}
	return res;
#endif
}

int CCurlClient::Gets(const std::string & strUrl, std::string & strResponse, const char * pCaPath, long time_out /*= 3*/)
{   
	CURLcode res;   
	CURL* curl = curl_easy_init();   
	if(NULL == curl)   
	{   
		return CURLE_FAILED_INIT;   
	}   
	if(m_bDebug)   
	{   
		curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);   
		curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, OnDebug);   
	}   
	curl_easy_setopt(curl, CURLOPT_URL, strUrl.c_str());   
	curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL);   
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, OnWriteData);   
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&strResponse);   
	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);   
	if(NULL == pCaPath || pCaPath[0] == '\0')   
	{   
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);   
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);   
	}   
	else   
	{   
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, true);   
		curl_easy_setopt(curl, CURLOPT_CAINFO, pCaPath);   
	}   
	curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5000);   
	curl_easy_setopt(curl, CURLOPT_TIMEOUT, time_out);
	// 设置头;
	curl_easy_setopt(curl, CURLOPT_HTTPHEADER, m_headers);

	res = curl_easy_perform(curl);   
	curl_easy_cleanup(curl); 
	//curl_global_cleanup();
	ClearHeaders(); /* free the header list */

	return res;   
} 

int CCurlClient::Gets(IN LPCTSTR lpUrl, OUT LPTSTR lpResponse, IN CONST INT& nMaxlen, IN LPCTSTR lpCaPath /* = NULL */, long time_out /*= 3*/)
{
	if ( lpUrl == NULL )
		return CURLE_FAILED_INIT;

	string strUrl;
	string strCapath;
	string strResponse;
#ifdef UNICODE
	CharEncoding::UNICODE2ASCII((LPWCH)lpUrl, strUrl);
	CharEncoding::UNICODE2ASCII((LPWCH)lpCaPath, strCapath);

	int res = Gets(strUrl, strResponse, strCapath.c_str()) ;
	if ( CURLE_OK == res )
	{
		CharEncoding::ASCII2UNICODE(strResponse.c_str(), (LPWCH)lpResponse, nMaxlen);
		return CURLE_OK;
	}

	return res;
#else
	strUrl = lpUrl;
	strCapath = lpCaPath;
	int res = Gets(strUrl, strResponse, strCapath.c_str(), time_out) ;
	if ( CURLE_OK == res )
	{
		sprintf_s(lpResponse, nMaxlen, "%s", strResponse.c_str());
		return CURLE_OK;
	}
	return res;
#endif
}

int CCurlClient::Gets(IN CString& strUrl, OUT CString& strResponse, IN const CString& strCaPath /* = _T("") */, long time_out /*= 3*/)
{
	if ( strUrl.IsEmpty() )
		return CURLE_FAILED_INIT;

	string url;
	string post;
	string capth;
	string response;
#ifdef UNICODE
	CharEncoding::UNICODE2ASCII((LPWCH)strUrl.GetString(), url);
	CharEncoding::UNICODE2ASCII((LPWCH)strCaPath.GetString(), capth);

	int res = Gets(url, response, capth.c_str()) ;
	if ( CURLE_OK == res )
	{
		WCHAR* pResult = CharEncoding::ASCII2UNICODE(response.c_str());
		if ( pResult )
		{
			strResponse = pResult;
			delete []pResult;
			pResult = NULL;
			return CURLE_OK;
		}
	}

	return res;
#else
	url = strUrl.GetString();
	capth = strCaPath.GetString();
	int res = Gets(url, response, capth.c_str(), time_out) ;
	if ( CURLE_OK == res )
	{
		strResponse = response.c_str();
		return CURLE_OK;
	}
	return res;
#endif
}

bool CCurlClient::Download(const std::string &url, const std::string &path, long time_out /* = 3000 */)
{
	int nStatus = 0;
	std::string data;
	int npos = url.find("https://");
	if ( npos == std::string::npos)
		nStatus = Get(url, data, time_out);
	else
		nStatus = Gets(url, data, NULL, time_out);

	if ( nStatus != CURLE_OK)
		return false;

	if ( data.size() )
	{
		FILE *pf = NULL;
		_tfopen_s(&pf, path.c_str(), "wb");
		if ( pf )
		{
			fwrite(data.c_str(), data.size(), 1, pf);
			fclose(pf);
			return true;
		}
	}

	return false;
}

// 参考:https://blog.csdn.net/lengyuezuixue/article/details/81987695
bool CCurlClient::DownloadEx(const std::string &url, const std::string &path, long time_out /* = 3000 */)
{
	FILE *pf = NULL;
	curl_off_t local_file_len = -1;
	long file_size = 0;

	CURLcode res = CURLE_GOT_NOTHING;
	int c = 0;
	struct stat file_info;
	int use_resume = 0;

	// 获取本地文件大小;
	if (stat(path.c_str(), &file_info) == 0 )
	{
		local_file_len = file_info.st_size;
		use_resume = 1;
	}

	// 采用追加方式打开文件,便于实现断点续传;
	int nErr = _tfopen_s(&pf, path.c_str(), "ab+");
	if ( pf )
	{
		CURL* curl = curl_easy_init();   
		if(NULL == curl)   
		{   
			//return CURLE_FAILED_INIT;   
			return false;
		}   
		curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
		curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, time_out);
		// 设置http头部处理函数;
		curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, OnGetContentLength);
		curl_easy_setopt(curl, CURLOPT_HEADERDATA, &file_size);
		if ( url.find("https://") != std::string::npos )
		{
			curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);   
			curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);   
		}
		// 设置文件续传的位置给curl;
		curl_easy_setopt(curl, CURLOPT_RESUME_FROM_LARGE, use_resume ? local_file_len : 0);
		curl_easy_setopt(curl, CURLOPT_WRITEDATA, pf); 
		//curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);//设置重定位URL，使用自动跳转，返回的头部中有Location(一般直接请求的url没找到)，则继续请求Location对应的数据
		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, OnWriteFile);
		curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
		curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);

		res = curl_easy_perform(curl);
		fclose(pf);
		curl_easy_cleanup(curl); 

		if ( res == CURLE_OK )
			return true;
	}

	return false;
}


void CCurlClient::SetDebug(bool bDebug)   
{   
	m_bDebug = bDebug;   
}   

void CCurlClient::SetHeaders(const std::string headers)
{
	m_headers = curl_slist_append(m_headers, headers.c_str());
}