779 lines
No EOL
21 KiB
C++
779 lines
No EOL
21 KiB
C++
#include "SCENE.H"
|
|
#ifdef ONLINE_MODE
|
|
//#pragma optimize("",off)
|
|
//#include "stdafx.h"
|
|
#define CURL_STATICLIB 1
|
|
#include <curl/curl.h>
|
|
#include <windows.h>
|
|
#include <winhttp.h> // requires recent Windows SDK, tested with v6.1
|
|
#include <process.h>
|
|
#include "http_client_curl.h"
|
|
|
|
#pragma comment(lib, "winhttp.lib")
|
|
|
|
#define _Curl (CURL *)_CurlStruct
|
|
|
|
#ifdef USE_OPENSSL
|
|
|
|
#include "openssl/crypto.h"
|
|
#include <windows.h>
|
|
|
|
|
|
struct CRYPTO_dynlock_value
|
|
{ MUTEX_TYPE mutex;
|
|
};
|
|
|
|
static MUTEX_TYPE *mutex_buf;
|
|
|
|
static struct CRYPTO_dynlock_value *dyn_create_function(const char *file, int line)
|
|
{ struct CRYPTO_dynlock_value *value;
|
|
value = (struct CRYPTO_dynlock_value*)malloc(sizeof(struct CRYPTO_dynlock_value));
|
|
if (value)
|
|
MUTEX_SETUP(value->mutex);
|
|
return value;
|
|
}
|
|
|
|
static void dyn_lock_function(int mode, struct CRYPTO_dynlock_value *l, const char *file, int line)
|
|
{ if (mode & CRYPTO_LOCK)
|
|
MUTEX_LOCK(l->mutex);
|
|
else
|
|
MUTEX_UNLOCK(l->mutex);
|
|
}
|
|
|
|
static void dyn_destroy_function(struct CRYPTO_dynlock_value *l, const char *file, int line)
|
|
{ MUTEX_CLEANUP(l->mutex);
|
|
free(l);
|
|
}
|
|
|
|
void locking_function(int mode, int n, const char *file, int line)
|
|
{ if (mode & CRYPTO_LOCK)
|
|
MUTEX_LOCK(mutex_buf[n]);
|
|
else
|
|
MUTEX_UNLOCK(mutex_buf[n]);
|
|
}
|
|
|
|
unsigned long id_function()
|
|
{ return (unsigned long)THREAD_ID;
|
|
}
|
|
|
|
bool CRYPTO_thread_setup()
|
|
{
|
|
mutex_buf = (MUTEX_TYPE*)malloc(CRYPTO_num_locks() * sizeof(MUTEX_TYPE));
|
|
if (!mutex_buf)
|
|
return false;
|
|
for (int i = 0; i < CRYPTO_num_locks(); i++)
|
|
MUTEX_SETUP(mutex_buf[i]);
|
|
CRYPTO_set_id_callback(id_function);
|
|
CRYPTO_set_locking_callback(locking_function);
|
|
CRYPTO_set_dynlock_create_callback(dyn_create_function);
|
|
CRYPTO_set_dynlock_lock_callback(dyn_lock_function);
|
|
CRYPTO_set_dynlock_destroy_callback(dyn_destroy_function);
|
|
return true;
|
|
}
|
|
|
|
void CRYPTO_thread_cleanup()
|
|
{
|
|
int i;
|
|
if (!mutex_buf)
|
|
return;
|
|
CRYPTO_set_id_callback(NULL);
|
|
CRYPTO_set_locking_callback(NULL);
|
|
CRYPTO_set_dynlock_create_callback(NULL);
|
|
CRYPTO_set_dynlock_lock_callback(NULL);
|
|
CRYPTO_set_dynlock_destroy_callback(NULL);
|
|
for (i = 0; i < CRYPTO_num_locks(); i++)
|
|
MUTEX_CLEANUP(mutex_buf[i]);
|
|
free(mutex_buf);
|
|
mutex_buf = NULL;
|
|
}
|
|
|
|
#endif //USE_OPENSSL
|
|
|
|
// From CamCam utilities
|
|
void SaveDBGInfo(char *text);
|
|
|
|
#define _CRT_SECURE_NO_WARNINGS
|
|
|
|
#define VARGS_TO_STR(_str, _format, _size) \
|
|
char _strbuf[_size]; \
|
|
va_list _args; \
|
|
va_start(_args, _format); \
|
|
int _res = _vsnprintf(_strbuf, _size-1, _format, _args); \
|
|
if ((_res == -1) || (_res == _size-1)) \
|
|
{ \
|
|
_strbuf[_size-1] = '\0'; \
|
|
} \
|
|
va_end(_args); \
|
|
_str = _strbuf
|
|
|
|
void logStr(const char *format, ...)
|
|
{
|
|
char *str;
|
|
const int maxLineSize = 512;
|
|
VARGS_TO_STR(str, format, maxLineSize);
|
|
if (strlen(str)<maxLineSize-1)
|
|
strcat(str, "\n");
|
|
else
|
|
str[maxLineSize-2] = '\n'; // end-2:'\n', end-1:'\0'
|
|
SaveDBGInfo(str);
|
|
}
|
|
|
|
std::string printfToStr(const char *format, ...)
|
|
{
|
|
std::string str;
|
|
VARGS_TO_STR(str, format, 1024);
|
|
return str;
|
|
}
|
|
|
|
UEString::UEString(const char *src, bool encode, CHttpClient *client)
|
|
{
|
|
if (encode)
|
|
_Str = curl_easy_escape((CURL*)(client->getCurlStruct()), src, 0);
|
|
else
|
|
_Str = curl_easy_unescape((CURL*)(client->getCurlStruct()), src, 0, NULL);
|
|
}
|
|
|
|
UEString::~UEString()
|
|
{
|
|
curl_free((void*)_Str);
|
|
}
|
|
|
|
|
|
#undef _CRT_SECURE_NO_WARNINGS
|
|
|
|
// CURL callbacks
|
|
size_t onDataReceivedCallback(void *buffer, size_t size, size_t nmemb, void *pHttpClient)
|
|
{
|
|
CHttpClient * httpClient = static_cast<CHttpClient*>(pHttpClient);
|
|
httpClient->onDataReceived((char*)buffer, size*nmemb);
|
|
|
|
//ifPrepareLuaFunction(GameMessageReceive)
|
|
//{
|
|
// callPreparedLuaCallback(GameMessageReceive, 0);
|
|
//}
|
|
return size*nmemb;
|
|
}
|
|
size_t onHeaderReceivedCallback( void *buffer, size_t size, size_t nmemb, void *pHttpClient)
|
|
{
|
|
CHttpClient * httpClient = static_cast<CHttpClient*>(pHttpClient);
|
|
httpClient->onHeaderReceived((char*)buffer, size*nmemb);
|
|
return size*nmemb;
|
|
}
|
|
|
|
int onProgressCallback(void *pHttpClient, double dltotal, double dlnow, double ultotal, double ulnow)
|
|
{
|
|
CHttpClient * httpClient = static_cast<CHttpClient*>(pHttpClient);
|
|
return httpClient->onProgress(dltotal, dlnow, ultotal, ulnow);
|
|
}
|
|
|
|
int CHttpClient::_InstanceCount = 0;
|
|
MUTEX_TYPE CHttpClient::_MutexInstanceCount = GET_MUTEX_SETUP;
|
|
|
|
// ***************************************************************************
|
|
bool CHttpClient::init()
|
|
{
|
|
if (_Initialized)
|
|
return true;
|
|
|
|
ctassert(MY_CURL_ERROR_SIZE == CURL_ERROR_SIZE);
|
|
ctassert(ErrorInvalidFile == CURLE_READ_ERROR);
|
|
|
|
// Global init
|
|
MUTEX_LOCK(_MutexInstanceCount);
|
|
if (_InstanceCount==0)
|
|
{
|
|
#ifdef USE_OPENSSL
|
|
if (!CRYPTO_thread_setup())
|
|
logStr("WRN: Could not setup crypto locking functions");
|
|
#endif
|
|
curl_global_init(CURL_GLOBAL_ALL);
|
|
}
|
|
++_InstanceCount;
|
|
MUTEX_UNLOCK(_MutexInstanceCount);
|
|
|
|
curl_version_info_data *vdata = curl_version_info(CURLVERSION_NOW);
|
|
//logStr("Libcurl v%s", vdata->version);
|
|
if ((vdata->features & CURL_VERSION_SSL) == 0)
|
|
logStr("WRN: SSL not supported");
|
|
|
|
_Curl = curl_easy_init();
|
|
if(_Curl == NULL)
|
|
return false;
|
|
|
|
_CookiesList.clear();
|
|
|
|
_Initialized = true;
|
|
return true;
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CHttpClient::setProxy(const char *ip_port)
|
|
{
|
|
curl_easy_setopt(_Curl, CURLOPT_PROXY, ip_port);
|
|
}
|
|
|
|
// ***************************************************************************
|
|
void CHttpClient::setUserAgent(const char *userAgent)
|
|
{
|
|
_UserAgentStr.clear();
|
|
_UserAgentStr.appendStrCat(userAgent, MaxUserAgentChars);
|
|
}
|
|
|
|
// ***************************************************************************
|
|
bool CHttpClient::setCredentials(const char *user, const char *password)
|
|
{
|
|
_AuthStr.clear();
|
|
if ((user == NULL) || (user[0] == '\0'))
|
|
return true;
|
|
|
|
_AuthStr.appendStrCat(user, MaxUserChars);
|
|
_AuthStr.appendStrCat(":", 1);
|
|
_AuthStr.appendStrCat(password, MaxPasswordChars);
|
|
return true;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
bool CHttpClient::setCredentials(const char *userPassword)
|
|
{
|
|
_AuthStr.clear();
|
|
if ((userPassword == NULL) || (userPassword[0] == '\0'))
|
|
return true;
|
|
|
|
_AuthStr.appendStrCat(userPassword, MaxUserChars+1+MaxPasswordChars);
|
|
return true;
|
|
}
|
|
// ***************************************************************************
|
|
bool CHttpClient::setCookie(const char *cookieName, const char *cookieValue)
|
|
{
|
|
//_CookieStr.clear();
|
|
if ((cookieName == NULL) || (cookieName[0] == '\0'))
|
|
return true;
|
|
|
|
if(_CookiesList.find(std::string(cookieName)) != _CookiesList.end())
|
|
{
|
|
_CookiesList.erase(_CookiesList.find(std::string(cookieName)));
|
|
}
|
|
|
|
_CookiesList.insert( std::make_pair (std::string(cookieName), std::string(cookieValue)));
|
|
|
|
|
|
//_CookieStr.appendStrCat(cookieName, MaxCookieNameChars);
|
|
//_CookieStr.appendStrCat("=", 1);
|
|
//_CookieStr.appendStrCat(cookieValue, MaxCookieValueChars);
|
|
return true;
|
|
}
|
|
// ***************************************************************************
|
|
void CHttpClient::resetCookie()
|
|
{
|
|
_CookieStr.clear();
|
|
_CookiesList.clear();
|
|
}
|
|
|
|
//const char *CAFilename = "./ssl_ca_cert.pem";
|
|
|
|
// ***************************************************************************
|
|
bool CHttpClient::setVerifyServer(bool verify, const char *certicatesFilename)
|
|
{
|
|
curl_easy_setopt(_Curl, CURLOPT_SSL_VERIFYHOST, verify ? 2 : 0); // set to compare the actual server hostname with the hostname specified in the certificate coming from the server
|
|
curl_easy_setopt(_Curl, CURLOPT_SSL_VERIFYPEER, verify ? 1 : 0); // set to check that the certificate coming from the server is valid (needs local certificate to check against)
|
|
//curl_easy_setopt(_Curl, CURLOPT_SSL_CTX_FUNCTION, *sslctx_function); // would allow to provide the CA in memory instead of using CURLOPT_CAINFO, but needs to include and link OpenSSL
|
|
curl_easy_setopt(_Curl, CURLOPT_CAINFO, certicatesFilename); // only needed if the default OpenSSL CA certificates are not sufficient to validate the server certificate
|
|
//curl_easy_setopt(_Curl, CURLOPT_CAPATH, NULL);
|
|
//curl_easy_setopt(_Curl, CURLOPT_SSLCERTTYPE, "PEM");
|
|
return true;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
bool CHttpClient::sendRequest(const char *pageURL, const char *paramStr, bool isPostRequest, bool wantHeader, bool verbose)
|
|
{
|
|
_Headers.clear();
|
|
_ReceiveBuffer.clear();
|
|
//HttpPage().swap(_ReceiveBuffer);
|
|
|
|
if (verbose)
|
|
curl_easy_setopt(_Curl, CURLOPT_VERBOSE, 1);
|
|
|
|
if (!_UserAgentStr.empty())
|
|
{
|
|
curl_easy_setopt(_Curl, CURLOPT_USERAGENT, _UserAgentStr.c_str());
|
|
}
|
|
|
|
// Authentication
|
|
if (!_AuthStr.empty())
|
|
{
|
|
curl_easy_setopt(_Curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
|
|
curl_easy_setopt(_Curl, CURLOPT_USERPWD, _AuthStr.c_str());
|
|
curl_easy_setopt(_Curl, CURLOPT_UNRESTRICTED_AUTH, 1);
|
|
|
|
}
|
|
|
|
if ((paramStr == NULL) || (paramStr[0] == '\0'))
|
|
{
|
|
// Just set URL
|
|
curl_easy_setopt(_Curl, CURLOPT_URL, pageURL);
|
|
curl_easy_setopt(_Curl, CURLOPT_POST, false);
|
|
}
|
|
else
|
|
{
|
|
if (isPostRequest)
|
|
{
|
|
// Set URL and post params
|
|
curl_easy_setopt(_Curl, CURLOPT_URL, pageURL);
|
|
curl_easy_setopt(_Curl, CURLOPT_POST, true);
|
|
curl_easy_setopt(_Curl, CURLOPT_POSTFIELDS, paramStr);
|
|
}
|
|
else
|
|
{
|
|
// Append params to URL
|
|
SimpleString<MaxURLChars> urlWithParams;
|
|
urlWithParams.appendStrCat(pageURL, MaxPageURLChars);
|
|
urlWithParams.appendStrCat("?", 1);
|
|
urlWithParams.appendStrCat(paramStr, MaxURLParamChars);
|
|
curl_easy_setopt(_Curl, CURLOPT_HTTPGET, true);
|
|
curl_easy_setopt(_Curl, CURLOPT_URL, urlWithParams.c_str());
|
|
}
|
|
}
|
|
|
|
// Cookie
|
|
if (!_CookiesList.empty())
|
|
{
|
|
// curl_easy_setopt(_Curl, CURLOPT_COOKIEFILE, "C:\c.txt");//_CookieStr.c_str());
|
|
|
|
// Make Cookies
|
|
std::string listC("");
|
|
for (CookiesList::iterator it = _CookiesList.begin(); it != _CookiesList.end(); ++ it)
|
|
{
|
|
listC = listC + it->first + std::string("=") + it->second + std::string(";");
|
|
}
|
|
//logStr("Cookies CURL=%s", listC.c_str());
|
|
curl_easy_setopt(_Curl, CURLOPT_COOKIE, listC.c_str());
|
|
}
|
|
|
|
if (wantHeader)
|
|
{
|
|
// Include the header in the response
|
|
curl_easy_setopt(_Curl, CURLOPT_HEADER, 1);
|
|
}
|
|
|
|
// Register the receive callback
|
|
curl_easy_setopt(_Curl, CURLOPT_WRITEFUNCTION, onDataReceivedCallback);
|
|
curl_easy_setopt(_Curl, CURLOPT_WRITEDATA, this);
|
|
curl_easy_setopt(_Curl, CURLOPT_HEADERFUNCTION, onHeaderReceivedCallback);
|
|
curl_easy_setopt(_Curl, CURLOPT_WRITEHEADER, this);
|
|
|
|
// Register the progress callback (this make the requests interruptable)
|
|
curl_easy_setopt(_Curl, CURLOPT_NOPROGRESS, FALSE);
|
|
curl_easy_setopt(_Curl, CURLOPT_PROGRESSFUNCTION, onProgressCallback);
|
|
curl_easy_setopt(_Curl, CURLOPT_PROGRESSDATA, this);
|
|
|
|
curl_easy_setopt(_Curl, CURLOPT_ERRORBUFFER, _CurlErrorString.Buf);
|
|
|
|
// Redirection options
|
|
if(_WantRedirection)
|
|
{
|
|
curl_easy_setopt(_Curl, CURLOPT_FOLLOWLOCATION, 1);
|
|
curl_easy_setopt(_Curl, CURLOPT_MAXREDIRS, _MaxRedirection);
|
|
}
|
|
else
|
|
{
|
|
curl_easy_setopt(_Curl, CURLOPT_FOLLOWLOCATION, 0);
|
|
}
|
|
|
|
curl_easy_setopt(_Curl, CURLOPT_POST301, 1); // aka CURLOPT_POSTREDIR in newer versions of Curl
|
|
curl_easy_setopt(_Curl, CURLOPT_UNRESTRICTED_AUTH, 1);
|
|
curl_easy_setopt(_Curl, CURLOPT_AUTOREFERER, 1);
|
|
|
|
// Send
|
|
_RequestCode = curl_easy_perform(_Curl);
|
|
if (_RequestCode != 0)
|
|
{
|
|
if (verbose)
|
|
logStr("WRN: %s", _CurlErrorString.c_str());
|
|
return false;
|
|
}
|
|
|
|
// Get result
|
|
curl_easy_getinfo(_Curl, CURLINFO_RESPONSE_CODE, &_RequestCode);
|
|
if (verbose)
|
|
logStr("Response Code: %u", _RequestCode);
|
|
|
|
return true;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
bool CHttpClient::addMultipartParam(const char *field, const MultipartParam& value)
|
|
{
|
|
switch (value.Source)
|
|
{
|
|
case CHttpClient::ParamCString:
|
|
return (curl_formadd((curl_httppost**)&_FirstParamStruct, (curl_httppost**)&_LastParamStruct,
|
|
CURLFORM_COPYNAME, field,
|
|
CURLFORM_COPYCONTENTS, value.Str,
|
|
CURLFORM_END) == 0);
|
|
//break;
|
|
case CHttpClient::ParamLoadFile:
|
|
return (curl_formadd((curl_httppost**)&_FirstParamStruct, (curl_httppost**)&_LastParamStruct,
|
|
CURLFORM_COPYNAME, field,
|
|
CURLFORM_FILE, value.Str,
|
|
CURLFORM_END) == 0);
|
|
//break;
|
|
default:;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
bool CHttpClient::sendMultipartRequest(const char *pageURL, bool wantHeader, bool verbose)
|
|
{
|
|
_Headers.clear();
|
|
_ReceiveBuffer.clear();
|
|
|
|
if (verbose)
|
|
curl_easy_setopt(_Curl, CURLOPT_VERBOSE, 1);
|
|
|
|
if (!_UserAgentStr.empty())
|
|
{
|
|
curl_easy_setopt(_Curl, CURLOPT_USERAGENT, _UserAgentStr.c_str());
|
|
}
|
|
|
|
// Authentication
|
|
if (!_AuthStr.empty())
|
|
{
|
|
curl_easy_setopt(_Curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
|
|
curl_easy_setopt(_Curl, CURLOPT_USERPWD, _AuthStr.c_str());
|
|
}
|
|
|
|
// URL and params
|
|
curl_easy_setopt(_Curl, CURLOPT_URL, pageURL);
|
|
curl_easy_setopt(_Curl, CURLOPT_HTTPPOST, (curl_httppost *)_FirstParamStruct);
|
|
|
|
// Cookie
|
|
if (!_CookieStr.empty())
|
|
curl_easy_setopt(_Curl, CURLOPT_COOKIE, _CookieStr.c_str());
|
|
|
|
if (wantHeader)
|
|
{
|
|
// Include the header in the response
|
|
curl_easy_setopt(_Curl, CURLOPT_HEADER, 1);
|
|
}
|
|
|
|
// Register the receive callback
|
|
curl_easy_setopt(_Curl, CURLOPT_WRITEFUNCTION, onDataReceivedCallback);
|
|
curl_easy_setopt(_Curl, CURLOPT_WRITEDATA, this);
|
|
curl_easy_setopt(_Curl, CURLOPT_HEADERFUNCTION, onHeaderReceivedCallback);
|
|
curl_easy_setopt(_Curl, CURLOPT_WRITEHEADER, this);
|
|
|
|
// Register the progress callback (this make the requests interruptable)
|
|
curl_easy_setopt(_Curl, CURLOPT_NOPROGRESS, FALSE);
|
|
curl_easy_setopt(_Curl, CURLOPT_PROGRESSFUNCTION, onProgressCallback);
|
|
curl_easy_setopt(_Curl, CURLOPT_PROGRESSDATA, this);
|
|
|
|
ctassert(MY_CURL_ERROR_SIZE == CURL_ERROR_SIZE);
|
|
curl_easy_setopt(_Curl, CURLOPT_ERRORBUFFER, _CurlErrorString.Buf);
|
|
|
|
// Send
|
|
_RequestCode = curl_easy_perform(_Curl);
|
|
curl_formfree((curl_httppost *)_FirstParamStruct);
|
|
_FirstParamStruct = NULL;
|
|
_LastParamStruct = NULL;
|
|
if (_RequestCode != 0)
|
|
{
|
|
if (verbose)
|
|
logStr("WRN: %s", _CurlErrorString.c_str());
|
|
return false;
|
|
}
|
|
|
|
// Get result
|
|
curl_easy_getinfo(_Curl, CURLINFO_RESPONSE_CODE, &_RequestCode);
|
|
if (verbose)
|
|
logStr("Response Code: %u", _RequestCode);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// ***************************************************************************
|
|
void CHttpClient::onDataReceived(char * buffer, size_t size)
|
|
{
|
|
#ifdef DYNALLOC_HTTPCLIENT
|
|
_ReceiveBuffer.insert(_ReceiveBuffer.end(), buffer, buffer+size);
|
|
#else
|
|
_ReceiveBuffer.appendBinary(buffer, size);
|
|
#endif
|
|
}
|
|
|
|
// ***************************************************************************
|
|
void CHttpClient::onHeaderReceived(char * buffer, size_t size)
|
|
{
|
|
// Parse and store into map
|
|
std::string s(buffer, buffer+size);
|
|
std::string::size_type p = s.find(":");
|
|
if (p != std::string::npos)
|
|
{
|
|
// Ex: "Content-Type" -> "text/html"
|
|
std::string::size_type q = p+1;
|
|
while (q<s.size() && s[q]==' ')
|
|
++q; // skip blank chars after ':'
|
|
_Headers[s.substr(0, p)] = s.substr(q, s.rfind("\r\n")-q);
|
|
}
|
|
else
|
|
{
|
|
// Ex: "HTTP/1.1 200 OK" -> ""
|
|
_Headers[s.substr(0, s.rfind("\r\n"))] = std::string();
|
|
}
|
|
}
|
|
|
|
// ***************************************************************************
|
|
int CHttpClient::onProgress(double dltotal, double dlnow, double ultotal, double ulnow)
|
|
{
|
|
#ifdef SIMULATE_SLOW_UPLOAD
|
|
if (ultotal != 0)
|
|
Sleep(1000);
|
|
#endif
|
|
_DLTotal = dltotal;
|
|
_DLNow = dlnow;
|
|
_ULTotal = ultotal;
|
|
_ULNow = ulnow;
|
|
return (isAborting() ? 1 : 0);
|
|
|
|
}
|
|
|
|
// ***************************************************************************
|
|
#ifndef USER_MODE
|
|
unsigned int CHttpClient::getMemoryUsage() const
|
|
{
|
|
MUTEX_LOCK(_MutexInstanceCount);
|
|
unsigned int size = 0;
|
|
size += sizeof(*this);
|
|
size += sizeof(char) * (MaxUserAgentChars + MaxAuthChars + MaxCookieChars + MY_CURL_ERROR_SIZE + 1);
|
|
|
|
// sizeof _Headers
|
|
for(HeaderMap::const_iterator it = _Headers.begin(); it != _Headers.end(); ++ it)
|
|
{
|
|
size += sizeof(std::string::value_type) * (it->first.capacity() + it->second.capacity());
|
|
}
|
|
|
|
size += _ReceiveBuffer.capacity() * sizeof(std::string::value_type);
|
|
MUTEX_UNLOCK(_MutexInstanceCount);
|
|
return size;
|
|
}
|
|
#endif
|
|
// ***************************************************************************
|
|
const HttpPage& CHttpClient::getResponsePage(bool verbose) const
|
|
{
|
|
if (verbose)
|
|
logStr("Accessing %u bytes", _ReceiveBuffer.size());
|
|
return _ReceiveBuffer;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
HttpPage& CHttpClient::getAndEditResponsePage(bool verbose)
|
|
{
|
|
if (verbose)
|
|
logStr("Accessing/editing %u bytes", _ReceiveBuffer.size());
|
|
return _ReceiveBuffer;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
void BackgroundTaskRun(void *instance)
|
|
{
|
|
(static_cast<CBackgroundTask*>(instance))->run();
|
|
}
|
|
|
|
// ***************************************************************************
|
|
bool CBackgroundTask::start()
|
|
{
|
|
_IsRunning = true;
|
|
_beginthread(BackgroundTaskRun, 0, this);
|
|
return true;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
void CBackgroundTask::waitForExit()
|
|
{
|
|
while (_IsRunning)
|
|
{
|
|
Sleep(10);
|
|
}
|
|
}
|
|
|
|
// ***************************************************************************
|
|
void CHttpClient::release()
|
|
{
|
|
curl_easy_cleanup(_Curl);
|
|
|
|
_Initialized = false;
|
|
|
|
// Global cleanup
|
|
MUTEX_LOCK(_MutexInstanceCount);
|
|
--_InstanceCount;
|
|
if (_InstanceCount == 0)
|
|
{
|
|
curl_global_cleanup();
|
|
#ifdef USE_OPENSSL
|
|
CRYPTO_thread_cleanup();
|
|
#endif
|
|
}
|
|
MUTEX_UNLOCK(_MutexInstanceCount);
|
|
}
|
|
|
|
// ***************************************************************************
|
|
CHttpClient::~CHttpClient()
|
|
{
|
|
if (_Initialized)
|
|
release();
|
|
}
|
|
|
|
// ***************************************************************************
|
|
bool CHttpClient::nextTryAutoConfigureProxy(const char *url, void* hSession)
|
|
{
|
|
// Windows-specific code (needs XP SP1 or newer)
|
|
|
|
// If previous calls could not deduce the proxy, abandon
|
|
if (_CurrentPacm == PacmFailed)
|
|
{
|
|
logStr(__FUNCTION__"-error: could not determine proxy");
|
|
if (hSession)
|
|
WinHttpCloseHandle(hSession);
|
|
return false;
|
|
}
|
|
|
|
// A WinHttp session is needed for certain calls
|
|
if (!hSession)
|
|
{
|
|
hSession = WinHttpOpen(NULL, WINHTTP_ACCESS_TYPE_NO_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
|
|
if (!hSession)
|
|
return false;
|
|
}
|
|
|
|
BOOL res = false;
|
|
switch (_CurrentPacm)
|
|
{
|
|
case PacmDirectConnection:
|
|
{
|
|
const char *curlProxy = getProxy();
|
|
if ((curlProxy == NULL) || (*curlProxy != NULL)) // try only if a proxy is set, because we get here only when a connection failed
|
|
{
|
|
logStr(__FUNCTION__"-info: trying direct connection...");
|
|
setProxy("");
|
|
res = true;
|
|
}
|
|
}
|
|
break;
|
|
case PacmHostname:
|
|
case PacmWPADURL:
|
|
// WPAD protocol not configured => try to get settings from IE as a fall-back
|
|
logStr(__FUNCTION__"-info: reading IE proxy config...");
|
|
WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ieCfg;
|
|
res = WinHttpGetIEProxyConfigForCurrentUser(&ieCfg);
|
|
if (res)
|
|
{
|
|
res = false;
|
|
if ((_CurrentPacm == PacmHostname) && ieCfg.lpszProxy)
|
|
{
|
|
logStr(__FUNCTION__"-info: using proxy hostname...");
|
|
char proxyAddr [2048];
|
|
wcstombs(proxyAddr, ieCfg.lpszProxy, 2047);
|
|
setProxy(proxyAddr);
|
|
res = true;
|
|
// ideally, should take ieCfg.lpszProxyBypass into account
|
|
}
|
|
else if ((_CurrentPacm == PacmWPADURL) && ieCfg.lpszAutoConfigUrl)
|
|
{
|
|
logStr(__FUNCTION__"-info: trying WPAD proxy auto-config from URL...");
|
|
res = setProxyFromWPAD((void*)hSession, url, ieCfg.lpszAutoConfigUrl);
|
|
}
|
|
}
|
|
if (ieCfg.lpszProxy)
|
|
GlobalFree(ieCfg.lpszProxy);
|
|
if (ieCfg.lpszAutoConfigUrl)
|
|
GlobalFree(ieCfg.lpszAutoConfigUrl);
|
|
if (ieCfg.lpszProxyBypass)
|
|
GlobalFree(ieCfg.lpszProxyBypass);
|
|
break;
|
|
|
|
case PacmDNS:
|
|
// Try DNS-based auto-detection (using 'wpad' host), not DHCP-based (said to have issues)
|
|
logStr(__FUNCTION__"-info: trying WPAD proxy auto-detection...");
|
|
res = setProxyFromWPAD((void*)hSession, url, L"http://wpad/wpad.dat");
|
|
break;
|
|
|
|
case PacmFullAuto:
|
|
// Try full auto-detection (including DHCP) as a last resort
|
|
logStr(__FUNCTION__"-info: trying full proxy auto-detection...");
|
|
res = setProxyFromWPAD((void*)hSession, url, NULL);
|
|
break;
|
|
|
|
default:
|
|
_CurrentPacm = PacmBeforeFailed;
|
|
}
|
|
|
|
++_CurrentPacm; // set-up for next (potential) call
|
|
if (res)
|
|
{
|
|
// Return success to try connecting
|
|
logStr(__FUNCTION__"-info: proxy set");
|
|
WinHttpCloseHandle(hSession);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// Try next proxy auto-config method
|
|
return nextTryAutoConfigureProxy(url, hSession);
|
|
}
|
|
}
|
|
|
|
// ***************************************************************************
|
|
bool CHttpClient::setProxyFromWPAD(void *hSession, const char *url, const wchar_t *wpadwURL)
|
|
{
|
|
// Windows-specific code, adapted from Google Chromium (needs XP SP1 or newer)
|
|
WINHTTP_AUTOPROXY_OPTIONS options = {0};
|
|
options.fAutoLogonIfChallenged = TRUE;
|
|
if (wpadwURL)
|
|
{
|
|
options.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
|
|
options.lpszAutoConfigUrl = wpadwURL;
|
|
}
|
|
else
|
|
{
|
|
options.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT,
|
|
options.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A;
|
|
}
|
|
WINHTTP_PROXY_INFO info = {0};
|
|
wchar_t wurl[2048];
|
|
mbstowcs(wurl, url, 2047);
|
|
BOOL res = WinHttpGetProxyForUrl(hSession, wurl, &options, &info);
|
|
if (res)
|
|
{
|
|
// Set proxy
|
|
res = true;
|
|
switch (info.dwAccessType)
|
|
{
|
|
case WINHTTP_ACCESS_TYPE_NO_PROXY:
|
|
setProxy("");
|
|
break;
|
|
case WINHTTP_ACCESS_TYPE_NAMED_PROXY:
|
|
char proxyAddr [2048];
|
|
wcstombs(proxyAddr, info.lpszProxy, 2047);
|
|
setProxy(proxyAddr);
|
|
break;
|
|
default:
|
|
res = false;
|
|
}
|
|
}
|
|
// Clean-up
|
|
if (info.lpszProxy)
|
|
GlobalFree(info.lpszProxy);
|
|
if (info.lpszProxyBypass)
|
|
GlobalFree(info.lpszProxyBypass);
|
|
return (bool)res;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
const char *CHttpClient::getProxy() const
|
|
{
|
|
return curl_easy_getopt_proxy(_Curl);
|
|
}
|
|
|
|
#endif // ONLINE_MODE
|