1806 lines
50 KiB
C++
1806 lines
50 KiB
C++
/*
|
|
* File: gsoapWinHTTPX.cpp
|
|
*
|
|
* This file is a modified version of gsoapWinInet.cpp. The Initial Developer
|
|
* of the Original Code of the gsoapWinInet.cpp file is Robert A. van Engelen.
|
|
* Copyright (C) 2000-2004, Robert van Engelen, Genivia, Inc., All Rights Reserved.
|
|
* It has been modified by Intel(R) Corporation in purpose to permit
|
|
* digest authentication, to block basic authentication and to provide
|
|
* the support for client authentication mechanism.
|
|
* This file converted to use WinHTTP instead of WinInet library and renamed appropriately
|
|
*
|
|
* gSOAP redistribution notes:
|
|
* Feel free to use, improve, and share. I would appreciate
|
|
* notification of any bugs found/fixed, or improvements made. This
|
|
* code has not been extensively tested, so use at your own risk.
|
|
*/
|
|
|
|
#ifdef USE_WINSOCK2
|
|
#include <winsock2.h>
|
|
#endif
|
|
/* system */
|
|
#define _WINSOCKAPI_
|
|
#include <windows.h>
|
|
#include <crtdbg.h>
|
|
#include <winhttp.h>
|
|
#include <tchar.h>
|
|
#include <sstream>
|
|
#include <string>
|
|
using namespace std;
|
|
|
|
/* local */
|
|
#include "gsoapWinHTTP.h"
|
|
|
|
/* ensure that the winhttp library is linked */
|
|
#pragma comment( lib, "winhttp.lib" )
|
|
|
|
#define UNUSED_ARG(x) (x)
|
|
#define INVALID_BUFFER_LENGTH ((DWORD)-1)
|
|
|
|
/* plugin id */
|
|
static const char winhttp_id[] = "winhttp-5.1";
|
|
|
|
/* plugin private data */
|
|
struct winhttp_data
|
|
{
|
|
HINTERNET hInternet; /* internet session handle */
|
|
HINTERNET hConnection; /* current connection handle */
|
|
BOOL bDisconnect; /* connection is disconnected */
|
|
DWORD dwRequestFlags; /* extra request flags from user */
|
|
char * pBuffer; /* send buffer */
|
|
size_t uiBufferLenMax; /* total length of the message */
|
|
size_t uiBufferLen; /* length of data in buffer */
|
|
BOOL bIsChunkSize; /* expecting a chunk size buffer */
|
|
|
|
/* Intel(R) additions */
|
|
const char * username; /* user name for digest auth */
|
|
const char * password; /* password for digest auth */
|
|
const _TCHAR * certificateName; /* Common Name of the client certificate */
|
|
PCCERT_CONTEXT certificate; /* client certificate */
|
|
BOOL local; /* true if an app runs locally */
|
|
BOOL krb; /* true if an user uses the Kerberos Authentication scheme */
|
|
BOOL localMachineStore; /* true if the client certificate is installed
|
|
otherwise the authentication scheme is digest */
|
|
wstring headers; /* HTTP headers from http response. One line for each header*/
|
|
#ifdef SOAP_DEBUG
|
|
/* this is only used for DBGLOG output */
|
|
char * pszErrorMessage; /* winhttp/system error message */
|
|
#endif
|
|
};
|
|
|
|
/* forward declarations */
|
|
static BOOL
|
|
winhttp_init(
|
|
struct soap * soap,
|
|
struct winhttp_data * a_pData,
|
|
DWORD a_dwRequestFlags );
|
|
static int
|
|
winhttp_copy(
|
|
struct soap * soap,
|
|
struct soap_plugin * a_pDst,
|
|
struct soap_plugin * a_pSrc );
|
|
static void
|
|
winhttp_delete(
|
|
struct soap * soap,
|
|
struct soap_plugin * a_pPluginData );
|
|
static SOAP_SOCKET
|
|
winhttp_connect(
|
|
struct soap * soap,
|
|
const char * a_pszEndpoint,
|
|
const char * a_pszHost,
|
|
int a_nPort );
|
|
static int
|
|
winhttp_post_header(
|
|
struct soap * soap,
|
|
const char * a_pszKey,
|
|
const char * a_pszValue );
|
|
static int
|
|
winhttp_fsend(
|
|
struct soap * soap,
|
|
const char * a_pBuffer,
|
|
size_t a_uiBufferLen );
|
|
static size_t
|
|
winhttp_frecv(
|
|
struct soap * soap,
|
|
char * a_pBuffer,
|
|
size_t a_uiBufferLen );
|
|
static int
|
|
winhttp_disconnect(
|
|
struct soap * soap );
|
|
void CALLBACK
|
|
winhttp_callback(
|
|
HINTERNET hInternet,
|
|
DWORD_PTR dwContext,
|
|
DWORD dwInternetStatus,
|
|
LPVOID lpvStatusInformation,
|
|
DWORD dwStatusInformationLength );
|
|
static BOOL
|
|
winhttp_have_connection(
|
|
struct soap * soap,
|
|
struct winhttp_data * a_pData );
|
|
static DWORD
|
|
winhttp_set_timeout(
|
|
struct soap * soap,
|
|
struct winhttp_data * a_pData,
|
|
const char * a_pszTimeout,
|
|
DWORD a_dwOption,
|
|
int a_nTimeout );
|
|
static BOOL
|
|
winhttp_find_cert(
|
|
struct soap * soap,
|
|
HANDLE hStoreHandle,
|
|
const _TCHAR * certName,
|
|
PCCERT_CONTEXT *pCertContext );
|
|
|
|
#ifdef SOAP_DEBUG
|
|
/* this is only used for DBGLOG output */
|
|
static const char *
|
|
winhttp_error_message(
|
|
struct soap * a_pData,
|
|
DWORD a_dwErrorMsgId );
|
|
static void
|
|
winhttp_free_error_message(
|
|
struct winhttp_data * a_pData );
|
|
#else
|
|
#define winhttp_free_error_message(x)
|
|
#endif
|
|
|
|
static void
|
|
winhttp_set_headers(
|
|
struct soap* soap,
|
|
const WCHAR *headers);
|
|
|
|
static wchar_t *
|
|
convert_to_unicode(
|
|
const char * str, int strLen);
|
|
|
|
DWORD
|
|
select_scheme(
|
|
DWORD suppSchemes);
|
|
|
|
/* plugin registration */
|
|
int
|
|
winhttp_plugin(
|
|
struct soap * soap,
|
|
struct soap_plugin * a_pPluginData,
|
|
void * a_dwRequestFlags )
|
|
{
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: plugin registration\n", soap ));
|
|
|
|
a_pPluginData->id = winhttp_id;
|
|
a_pPluginData->fcopy = winhttp_copy;
|
|
a_pPluginData->fdelete = winhttp_delete;
|
|
a_pPluginData->data = (void*) malloc( sizeof(struct winhttp_data) );
|
|
if ( !a_pPluginData->data )
|
|
{
|
|
return SOAP_EOM;
|
|
}
|
|
if ( !winhttp_init( soap,
|
|
(struct winhttp_data *) a_pPluginData->data,
|
|
(DWORD) a_dwRequestFlags ) )
|
|
{
|
|
free( a_pPluginData->data );
|
|
return SOAP_EOM;
|
|
}
|
|
|
|
#ifdef SOAP_DEBUG
|
|
if ( (soap->omode & SOAP_IO) == SOAP_IO_STORE )
|
|
{
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: use of SOAP_IO_STORE is not recommended\n", soap ));
|
|
}
|
|
#endif
|
|
|
|
return SOAP_OK;
|
|
}
|
|
|
|
/* initialize private data */
|
|
int winhttp_init(
|
|
struct soap * soap,
|
|
struct winhttp_data * a_pData,
|
|
DWORD a_dwRequestFlags )
|
|
{
|
|
BOOL bResult;
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: init private data\n", soap ));
|
|
|
|
memset( a_pData, 0, sizeof(struct winhttp_data) );
|
|
a_pData->dwRequestFlags = a_dwRequestFlags;
|
|
|
|
/* start our internet session */
|
|
//Conversion Notes:
|
|
//WINHTTP_ACCESS_TYPE_DEFAULT_PROXY retrieves proxy from WINHTTP registry entry
|
|
//it is not the same as browser proxy configuration (see MSDN Help)
|
|
|
|
a_pData->hInternet = WinHttpOpen(
|
|
L"gSOAP", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
|
|
WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0 );
|
|
if ( !a_pData->hInternet )
|
|
{
|
|
soap->error = GetLastError();
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: init, error %d (%s) in InternetOpen\n",
|
|
soap, soap->error, winhttp_error_message(soap,soap->error) ));
|
|
winhttp_free_error_message( a_pData );
|
|
return FALSE;
|
|
}
|
|
|
|
//set connection context
|
|
bResult = WinHttpSetOption(a_pData->hInternet,
|
|
WINHTTP_OPTION_CONTEXT_VALUE,
|
|
(void*)soap, sizeof(void*));
|
|
if( !bResult )
|
|
{
|
|
WinHttpCloseHandle( a_pData->hInternet );
|
|
a_pData->hInternet = NULL;
|
|
soap->error = GetLastError();
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: init, error %d (%s) in WinHttpSetOption\n",
|
|
soap, soap->error, winhttp_error_message(soap,soap->error) ));
|
|
winhttp_free_error_message( a_pData );
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
/* set the timeouts, if any of these fail the error isn't fatal */
|
|
winhttp_set_timeout( soap, a_pData, "connect",
|
|
WINHTTP_OPTION_CONNECT_TIMEOUT, soap->connect_timeout );
|
|
winhttp_set_timeout( soap, a_pData, "receive",
|
|
WINHTTP_OPTION_RECEIVE_TIMEOUT, soap->recv_timeout );
|
|
winhttp_set_timeout( soap, a_pData, "send",
|
|
WINHTTP_OPTION_SEND_TIMEOUT, soap->send_timeout );
|
|
|
|
/* set up the callback function so we get notifications */
|
|
WinHttpSetStatusCallback( a_pData->hInternet, winhttp_callback, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, NULL );
|
|
|
|
/* set all of our callbacks */
|
|
soap->fopen = winhttp_connect;
|
|
soap->fposthdr = winhttp_post_header;
|
|
soap->fsend = winhttp_fsend;
|
|
soap->frecv = winhttp_frecv;
|
|
soap->fclose = winhttp_disconnect;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* copy the private data structure */
|
|
static int
|
|
winhttp_copy(
|
|
struct soap * soap,
|
|
struct soap_plugin * a_pDst,
|
|
struct soap_plugin * a_pSrc )
|
|
{
|
|
UNUSED_ARG( soap );
|
|
UNUSED_ARG( a_pDst );
|
|
UNUSED_ARG( a_pSrc );
|
|
|
|
_ASSERTE( !"winhttp doesn't support copy" );
|
|
return SOAP_FATAL_ERROR;
|
|
}
|
|
|
|
/* deallocate of our private structure */
|
|
static void
|
|
winhttp_delete(
|
|
struct soap * soap,
|
|
struct soap_plugin * a_pPluginData )
|
|
{
|
|
struct winhttp_data * pData =
|
|
(struct winhttp_data *) a_pPluginData->data;
|
|
|
|
UNUSED_ARG( soap );
|
|
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: delete private data\n", soap ));
|
|
|
|
/* force a disconnect of any existing connection */
|
|
pData->bDisconnect = TRUE;
|
|
winhttp_have_connection( soap, pData );
|
|
|
|
/* close down the internet */
|
|
if ( pData->hInternet )
|
|
{
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: closing internet handle\n", soap));
|
|
WinHttpCloseHandle( pData->hInternet );
|
|
pData->hInternet = NULL;
|
|
}
|
|
|
|
/* free our data */
|
|
winhttp_free_error_message( pData );
|
|
free( a_pPluginData->data );
|
|
|
|
/* clean up credentials */
|
|
//Conversion Notes: TBD Did not find appropriate options in WinHttp
|
|
//InternetSetOption(0,INTERNET_OPTION_DIGEST_AUTH_UNLOAD,0,0);
|
|
}
|
|
|
|
/* gsoap documentation:
|
|
Called from a client proxy to open a connection to a Web Service located
|
|
at endpoint. Input parameters host and port are micro-parsed from endpoint.
|
|
Should return a valid file descriptor, or SOAP_INVALID_SOCKET and
|
|
soap->error set to an error code. Built-in gSOAP function: tcp_connect
|
|
*/
|
|
static SOAP_SOCKET
|
|
winhttp_connect(
|
|
struct soap * soap,
|
|
const char * a_pszEndpoint,
|
|
const char * a_pszHost,
|
|
int a_nPort )
|
|
{
|
|
//Needed for ATL conversion macros
|
|
|
|
URL_COMPONENTS urlComponents;
|
|
wchar_t szUrlPath[MAX_PATH];
|
|
wchar_t szHost[MAX_PATH];
|
|
DWORD dwFlags;
|
|
HINTERNET hConnection = NULL;
|
|
HINTERNET hHttpRequest = NULL;
|
|
BOOL bResult = FALSE;
|
|
|
|
struct winhttp_data * pData =
|
|
(struct winhttp_data *) soap_lookup_plugin( soap, winhttp_id );
|
|
|
|
soap->error = SOAP_OK;
|
|
|
|
/* we parse the URL ourselves so we don't use these parameters */
|
|
UNUSED_ARG( a_pszHost );
|
|
UNUSED_ARG( a_nPort );
|
|
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: connect, endpoint = '%s'\n", soap, a_pszEndpoint ));
|
|
|
|
/* we should be initialized but not connected */
|
|
_ASSERTE( pData->hInternet );
|
|
_ASSERTE( !pData->hConnection );
|
|
_ASSERTE( soap->socket == SOAP_INVALID_SOCKET );
|
|
|
|
/* parse out the url path */
|
|
memset( &urlComponents, 0, sizeof(urlComponents) );
|
|
urlComponents.dwStructSize = sizeof(urlComponents);
|
|
urlComponents.lpszHostName = szHost;
|
|
urlComponents.dwHostNameLength = MAX_PATH;
|
|
urlComponents.lpszUrlPath = szUrlPath;
|
|
urlComponents.dwUrlPathLength = MAX_PATH;
|
|
//Sending The maximum length of a URL in the address bar = 2048 characters.
|
|
wchar_t * a_pszEndpointW = convert_to_unicode(a_pszEndpoint, 2048);
|
|
bResult = WinHttpCrackUrl( a_pszEndpointW, 0, 0, &urlComponents );
|
|
free(a_pszEndpointW);
|
|
|
|
if ( !bResult )
|
|
{
|
|
//Conversion Notes: does nothing here
|
|
//WinHttpCloseHandle( hConnection );
|
|
soap->error = GetLastError();
|
|
DBGLOG( TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: connect, error %d (%s) in WinHttpCrackUrl\n",
|
|
soap, soap->error, winhttp_error_message(soap,soap->error) ));
|
|
return SOAP_INVALID_SOCKET;
|
|
}
|
|
|
|
hConnection = WinHttpConnect( pData->hInternet,
|
|
szHost, urlComponents.nPort, 0);
|
|
|
|
if ( !hConnection )
|
|
{
|
|
soap->error = GetLastError();
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: connect, error %d (%s) in WinHttpConnect\n",
|
|
soap, soap->error, winhttp_error_message(soap,soap->error) ));
|
|
return SOAP_INVALID_SOCKET;
|
|
}
|
|
|
|
/*
|
|
Note that although we specify HTTP/1.1 for the connection here, the
|
|
actual connection may be HTTP/1.0 depending on the settings in the
|
|
control panel. See the "Internet Options", "HTTP 1.1 settings".
|
|
*/
|
|
dwFlags = pData->dwRequestFlags;
|
|
if ( soap->omode & SOAP_IO_KEEPALIVE )
|
|
{
|
|
//Converion Notes: not found such a flag
|
|
//dwFlags |= INTERNET_FLAG_KEEP_CONNECTION;
|
|
}
|
|
if ( urlComponents.nScheme == INTERNET_SCHEME_HTTPS )
|
|
{
|
|
dwFlags |= WINHTTP_FLAG_SECURE;
|
|
}
|
|
hHttpRequest = WinHttpOpenRequest(
|
|
hConnection, L"POST", szUrlPath, L"HTTP/1.1",
|
|
WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES,
|
|
dwFlags );
|
|
if ( !hHttpRequest )
|
|
{
|
|
WinHttpCloseHandle( hConnection );
|
|
soap->error = GetLastError();
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: connect, error %d (%s) in WinHttpOpenRequest\n",
|
|
soap, soap->error, winhttp_error_message(soap,soap->error) ));
|
|
return SOAP_INVALID_SOCKET;
|
|
}
|
|
if (soap->proxy_userid && soap->proxy_passwd)
|
|
{
|
|
|
|
wchar_t* proxyUsernameW = convert_to_unicode(soap->proxy_userid, 32);
|
|
wchar_t* proxyPasswordW = convert_to_unicode(soap->proxy_passwd, 32);
|
|
int proxyUsername_len = strnlen_s(soap->proxy_userid, 32);
|
|
int proxyPassword_len = strnlen_s(soap->proxy_passwd, 32);
|
|
bool returnStatus=true;
|
|
|
|
if(!WinHttpSetCredentials(hHttpRequest,WINHTTP_AUTH_TARGET_PROXY,
|
|
WINHTTP_AUTH_SCHEME_BASIC,proxyUsernameW,proxyPasswordW,NULL))
|
|
{
|
|
soap->error = GetLastError();
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: fsend, error %d (%s) in WinHttpSetCredentials\n",
|
|
soap, soap->error, winhttp_error_message(soap,soap->error) ));
|
|
returnStatus=false;
|
|
}
|
|
|
|
wmemset(proxyUsernameW,0,proxyUsername_len);
|
|
wmemset(proxyPasswordW,0,proxyPassword_len);
|
|
|
|
free(proxyUsernameW);
|
|
free(proxyPasswordW);
|
|
if (!returnStatus)
|
|
{
|
|
return SOAP_INVALID_SOCKET;
|
|
}
|
|
}
|
|
/* save the connection handle in our data structure */
|
|
pData->hConnection = hConnection;
|
|
|
|
/* return the http request handle as our file descriptor. */
|
|
_ASSERTE( sizeof(soap->socket) >= sizeof(HINTERNET) );
|
|
return (SOAP_SOCKET) hHttpRequest;
|
|
|
|
}
|
|
|
|
/* gsoap documentation:
|
|
Called by http_post and http_response (through the callbacks). Emits HTTP
|
|
key: val header entries. Should return SOAP_OK, or a gSOAP error code.
|
|
Built-in gSOAP function: http_post_header.
|
|
*/
|
|
static int
|
|
winhttp_post_header(
|
|
struct soap * soap,
|
|
const char * a_pszKey,
|
|
const char * a_pszValue )
|
|
{
|
|
HINTERNET hHttpRequest = (HINTERNET) soap->socket;
|
|
char szHeader[MAX_PATH];
|
|
int nLen;
|
|
BOOL bResult = FALSE;
|
|
struct winhttp_data * pData =
|
|
(struct winhttp_data *) soap_lookup_plugin( soap, winhttp_id );
|
|
|
|
soap->error = SOAP_OK;
|
|
|
|
/* ensure that our connection hasn't been disconnected */
|
|
if ( !winhttp_have_connection( soap, pData ) )
|
|
{
|
|
return SOAP_EOF;
|
|
}
|
|
|
|
/* if this is the initial POST header then we initialize our send buffer */
|
|
if ( a_pszKey && !a_pszValue )
|
|
{
|
|
_ASSERTE( !pData->pBuffer );
|
|
pData->uiBufferLenMax = INVALID_BUFFER_LENGTH;
|
|
pData->uiBufferLen = 0;
|
|
|
|
/* Initialize username and password and remove them from SOAP structure
|
|
to disable gsoap from adding a Basic authentication header
|
|
with the username and password automatically */
|
|
pData->username = soap->userid;
|
|
pData->password = soap->passwd;
|
|
|
|
soap->userid = NULL;
|
|
soap->passwd = NULL;
|
|
|
|
/* if we are using chunk output then we start with a chunk size */
|
|
pData->bIsChunkSize = ( (soap->omode & SOAP_IO) == SOAP_IO_CHUNK );
|
|
}
|
|
else if ( a_pszValue )
|
|
{
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: post_header, adding '%s: %s'\n",
|
|
soap, a_pszKey, a_pszValue ));
|
|
|
|
/* determine the maximum length of this message so that we can
|
|
correctly determine when we have completed the send */
|
|
if (!a_pszKey || !strcmp( a_pszKey, "Content-Length" ) )
|
|
{
|
|
_ASSERTE( pData->uiBufferLenMax == INVALID_BUFFER_LENGTH );
|
|
pData->uiBufferLenMax = strtoul( a_pszValue, NULL, 10 );
|
|
}
|
|
|
|
nLen = _snprintf( szHeader, MAX_PATH, "%s: %s\r\n", a_pszKey, a_pszValue );
|
|
if ( nLen < 0 )
|
|
{
|
|
return SOAP_EOM;
|
|
}
|
|
wchar_t * szHeaderW = convert_to_unicode(szHeader, MAX_PATH - 1);
|
|
|
|
bResult = WinHttpAddRequestHeaders( hHttpRequest,
|
|
szHeaderW,
|
|
nLen,
|
|
WINHTTP_ADDREQ_FLAG_ADD_IF_NEW );
|
|
free( szHeaderW );
|
|
#ifdef SOAP_DEBUG
|
|
/*
|
|
we don't return an error if this fails because it isn't
|
|
(or shouldn't be) critical.
|
|
*/
|
|
if ( !bResult )
|
|
{
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: post_header, error %d (%s) in WinHttpAddRequestHeaders\n",
|
|
soap, soap->error, winhttp_error_message(soap,GetLastError()) ));
|
|
}
|
|
#endif
|
|
}
|
|
return SOAP_OK;
|
|
}
|
|
|
|
/* gsoap documentation:
|
|
Called for all send operations to emit contents of s of length n.
|
|
Should return SOAP_OK, or a gSOAP error code. Built-in gSOAP
|
|
function: fsend
|
|
|
|
Notes:
|
|
I do a heap of buffering here because we need the entire message available
|
|
in a single buffer in order to iterate through the sending loop. I had
|
|
hoped that the SOAP_IO_STORE flag would have worked to do the same, however
|
|
this still breaks the messages up into blocks. Although there were a number
|
|
of ways this could've been implemented, this works and supports all of the
|
|
possible SOAP_IO flags, even though the entire message is still buffered
|
|
the same as if SOAP_IO_STORE was used.
|
|
*/
|
|
static int
|
|
winhttp_fsend(
|
|
struct soap * soap,
|
|
const char * a_pBuffer,
|
|
size_t a_uiBufferLen )
|
|
{
|
|
#if defined(__cplusplus) && !defined(WITH_LEAN)
|
|
if (soap->os)
|
|
{ soap->os->write(a_pBuffer, a_uiBufferLen);
|
|
if (soap->os->good())
|
|
return SOAP_OK;
|
|
return SOAP_EOF;
|
|
}
|
|
#endif
|
|
/* char * tmp = new char[a_uiBufferLen + 1];
|
|
tmp[a_uiBufferLen] = 0;
|
|
memcpy(tmp, a_pBuffer, a_uiBufferLen);
|
|
fprintf(stdout, "*******************\nsending\n**************\n%s\n*************\n\n", tmp);*/
|
|
HINTERNET hHttpRequest = (HINTERNET) soap->socket;
|
|
BOOL bResult;
|
|
BOOL bRetryPost;
|
|
DWORD dwStatusCode;
|
|
DWORD dwStatusCodeLen;
|
|
int nResult = SOAP_OK;
|
|
HANDLE hStoreHandle = NULL;
|
|
wchar_t *proxy_username = NULL;
|
|
wchar_t *proxy_password = NULL;
|
|
struct winhttp_data * pData =
|
|
(struct winhttp_data *) soap_lookup_plugin( soap, winhttp_id );
|
|
|
|
soap->error = SOAP_OK;
|
|
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: fsend, data len = %lu bytes\n", soap, a_uiBufferLen ));
|
|
|
|
_ASSERTE( a_uiBufferLen > 0 );
|
|
|
|
/* ensure that our connection hasn't been disconnected */
|
|
if ( !winhttp_have_connection( soap, pData ) )
|
|
{
|
|
return SOAP_EOF;
|
|
}
|
|
|
|
/* initialize on our first time through. pData->pBuffer will always be
|
|
non-null if this is not the first call. */
|
|
if ( !pData->pBuffer )
|
|
{
|
|
/*
|
|
If we are using chunked sending, then we don't know how big the
|
|
buffer will need to be. So we start with a 0 length buffer and
|
|
grow it later to ensure that it is always large enough.
|
|
|
|
uiBufferLenMax = length of the allocated memory
|
|
uiBufferLen = length of the data in the buffer
|
|
*/
|
|
if ( (soap->mode & SOAP_IO) == SOAP_IO_CHUNK )
|
|
{
|
|
/* we make the initial allocation large enough for this chunksize
|
|
buffer, plus the next chunk of actual data, and a few extra
|
|
bytes for the final "0" chunksize block. */
|
|
size_t uiChunkSize = strtoul( a_pBuffer, NULL, 16 );
|
|
pData->uiBufferLenMax = uiChunkSize + a_uiBufferLen + 16;
|
|
}
|
|
else if ( a_uiBufferLen == pData->uiBufferLenMax )
|
|
{
|
|
/*
|
|
If the currently supplied buffer from gsoap holds the entire
|
|
message then we just use their buffer and avoid any memory
|
|
allocation. This will only be true when (1) we are not using
|
|
chunked send (so uiBufferLenMax has been previously set to
|
|
the Content-Length header length), and (2) gsoap is sending
|
|
the entire message at one time.
|
|
*/
|
|
pData->pBuffer = (char *) a_pBuffer;
|
|
pData->uiBufferLen = a_uiBufferLen;
|
|
}
|
|
|
|
_ASSERTE( pData->uiBufferLenMax != INVALID_BUFFER_LENGTH );
|
|
}
|
|
|
|
/*
|
|
If we can't use the gsoap buffer, then we need to allocate our own
|
|
buffer for the entire message. This is because authentication may
|
|
require the entire message to be sent multiple times. Since this send
|
|
is only a part of the message, we need to buffer until we have the
|
|
entire message.
|
|
*/
|
|
if ( pData->pBuffer != a_pBuffer )
|
|
{
|
|
/*
|
|
We already have a buffer pointer, this means that it isn't the
|
|
first time we have been called. We have allocated a buffer and
|
|
are current filling it.
|
|
|
|
If we don't have enough room in the our buffer to add this new
|
|
data, then we need to reallocate. This case will only occur with
|
|
chunked sends.
|
|
*/
|
|
size_t uiNewBufferLen = pData->uiBufferLen + a_uiBufferLen;
|
|
if ( !pData->pBuffer || uiNewBufferLen > pData->uiBufferLenMax )
|
|
{
|
|
while ( uiNewBufferLen > pData->uiBufferLenMax )
|
|
{
|
|
pData->uiBufferLenMax = pData->uiBufferLenMax * 2;
|
|
}
|
|
pData->pBuffer = (char *) realloc( pData->pBuffer, pData->uiBufferLenMax );
|
|
if ( !pData->pBuffer )
|
|
{
|
|
return SOAP_EOM;
|
|
}
|
|
}
|
|
if (memcpy_s(pData->pBuffer + pData->uiBufferLen, pData->uiBufferLenMax - pData->uiBufferLen,
|
|
a_pBuffer, a_uiBufferLen))
|
|
{
|
|
return SOAP_ERR;
|
|
}
|
|
pData->uiBufferLen = uiNewBufferLen;
|
|
|
|
/*
|
|
if we are doing chunked transfers, and this is a chunk size block,
|
|
and it is "0", then this is the last block in the transfer and we
|
|
can set the maximum size now to continue to the actual send.
|
|
*/
|
|
if ( (soap->mode & SOAP_IO) == SOAP_IO_CHUNK
|
|
&& pData->bIsChunkSize
|
|
&& a_pBuffer[2] == '0' && !isalnum(a_pBuffer[3]) )
|
|
{
|
|
pData->uiBufferLenMax = pData->uiBufferLen;
|
|
}
|
|
}
|
|
|
|
/*
|
|
if we haven't got the entire length of the message yet, then
|
|
we return to gsoap and let it continue
|
|
*/
|
|
if ( pData->uiBufferLen < pData->uiBufferLenMax )
|
|
{
|
|
/* toggle our chunk size marker if we are chunking */
|
|
pData->bIsChunkSize =
|
|
((soap->mode & SOAP_IO) == SOAP_IO_CHUNK)
|
|
&& !pData->bIsChunkSize;
|
|
return SOAP_OK;
|
|
}
|
|
_ASSERTE( pData->uiBufferLen == pData->uiBufferLenMax );
|
|
|
|
/* we've now got the entire message, now we can enter our sending loop */
|
|
bRetryPost = TRUE;
|
|
|
|
int is_emulator = 0;
|
|
if (strstr(soap->path, ".asmx") == (soap->path+ strnlen_s(soap->path, SOAP_TAGLEN - 1) -5))
|
|
{
|
|
is_emulator = 1;
|
|
}
|
|
|
|
|
|
|
|
if(((winhttp_data*)soap->plugins->data)->krb == TRUE)
|
|
{
|
|
DWORD d = WINHTTP_ENABLE_SPN_SERVER_PORT;
|
|
if(!WinHttpSetOption(
|
|
hHttpRequest,
|
|
WINHTTP_OPTION_SPN,
|
|
(LPVOID) (&d),
|
|
sizeof(DWORD)))
|
|
{
|
|
soap->error = GetLastError();
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: fsend, error %d (%s) in WinHttpSetOption\n",
|
|
soap, soap->error, winhttp_error_message(soap,soap->error) ));
|
|
nResult = SOAP_HTTP_ERROR;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Allow only 3 attempts.
|
|
First attempt: No client certificate
|
|
Second attempt: No credentials
|
|
Third attempt: Success
|
|
*/
|
|
int attemptNum = 0;
|
|
while ( bRetryPost && attemptNum < 3 )
|
|
{
|
|
nResult = SOAP_OK;
|
|
bRetryPost = FALSE;
|
|
|
|
bResult = WinHttpSendRequest(
|
|
hHttpRequest, WINHTTP_NO_ADDITIONAL_HEADERS , 0,
|
|
pData->pBuffer, (DWORD) pData->uiBufferLen,
|
|
(DWORD) pData->uiBufferLen, (DWORD_PTR)soap);
|
|
|
|
if (bResult)
|
|
bResult = WinHttpReceiveResponse(hHttpRequest, NULL );
|
|
|
|
/*
|
|
get the headers from the response and store them in plugin data accessible by the calling
|
|
application
|
|
*/
|
|
if (bResult)
|
|
{
|
|
DWORD dwSize;
|
|
BOOL bHeadersResult = WinHttpQueryHeaders( hHttpRequest,
|
|
WINHTTP_QUERY_RAW_HEADERS_CRLF,
|
|
WINHTTP_HEADER_NAME_BY_INDEX, NULL, &dwSize,
|
|
WINHTTP_NO_HEADER_INDEX);
|
|
|
|
DWORD error = GetLastError();
|
|
|
|
// Allocate memory for the buffer.
|
|
if (error== ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
// Allocate memory for the buffer.
|
|
WCHAR *lpHeaders = new WCHAR[dwSize/sizeof(WCHAR)];
|
|
|
|
// Use HttpQueryInfo to obtain the header buffer.
|
|
bHeadersResult = WinHttpQueryHeaders( hHttpRequest,
|
|
WINHTTP_QUERY_RAW_HEADERS_CRLF,
|
|
WINHTTP_HEADER_NAME_BY_INDEX,
|
|
lpHeaders,
|
|
&dwSize,
|
|
WINHTTP_NO_HEADER_INDEX);
|
|
|
|
if (bHeadersResult)
|
|
{
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: fsend, HTTP Headers = %S\n",
|
|
soap, lpHeaders));
|
|
winhttp_set_headers(soap,lpHeaders);
|
|
}
|
|
else
|
|
{
|
|
error = GetLastError();
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: fsend, error %d (%s) in WinHttpQueryHeaders\n",
|
|
soap, error, winhttp_error_message(soap,error) ));
|
|
|
|
}
|
|
delete[] lpHeaders;
|
|
}
|
|
else
|
|
{
|
|
error = GetLastError();
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: fsend, error %d (%s) in WinHttpQueryHeaders\n",
|
|
soap, error, winhttp_error_message(soap,error) ));
|
|
}
|
|
}
|
|
|
|
/*
|
|
get the status code from the response to determine if we need
|
|
to authorize
|
|
*/
|
|
if (bResult )
|
|
{
|
|
dwStatusCodeLen = sizeof(dwStatusCode);
|
|
bResult = WinHttpQueryHeaders(
|
|
hHttpRequest,
|
|
WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
|
|
WINHTTP_HEADER_NAME_BY_INDEX,
|
|
&dwStatusCode,
|
|
&dwStatusCodeLen,
|
|
WINHTTP_NO_HEADER_INDEX );
|
|
if (bResult)
|
|
{
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: fsend, HTTP status code = %lu\n",
|
|
soap, dwStatusCode));
|
|
}
|
|
}
|
|
|
|
if ( !bResult )
|
|
{
|
|
soap->error = GetLastError();
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: fsend, error %d (%s) in WinHttpSendRequest\n",
|
|
soap, soap->error, winhttp_error_message(soap,soap->error) ));
|
|
|
|
/* Client certificate required */
|
|
if(soap->error == ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED)
|
|
{
|
|
soap->error = HTTP_STATUS_FORBIDDEN;
|
|
bRetryPost = FALSE;
|
|
|
|
/* Choose which personal store to use : Current User or Local Machine*/
|
|
DWORD flags;
|
|
if (((winhttp_data*)soap->plugins->data)->localMachineStore)
|
|
{
|
|
flags = CERT_SYSTEM_STORE_LOCAL_MACHINE;
|
|
}
|
|
else
|
|
{
|
|
flags = CERT_SYSTEM_STORE_CURRENT_USER;
|
|
}
|
|
|
|
/* Open the personal store*/
|
|
if ( !(hStoreHandle = CertOpenStore(
|
|
CERT_STORE_PROV_SYSTEM, // The store provider type
|
|
0, // The encoding type is not needed
|
|
NULL, // Use the default HCRYPTPROV
|
|
flags, // Set the store location in a registry location
|
|
L"MY" // The store name as a Unicode string
|
|
)))
|
|
{
|
|
/* Cannot open the certificates store - exit immediately */
|
|
soap->error = GetLastError();
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: fsend, error %d (%s) in CertOpenSystemStore\n",
|
|
soap, soap->error, winhttp_error_message(soap,soap->error) ));
|
|
nResult = SOAP_SSL_ERROR;
|
|
break;
|
|
}
|
|
if (pData->certificate == NULL) {
|
|
if (!winhttp_find_cert(soap,hStoreHandle,pData->certificateName,&pData->certificate))
|
|
{
|
|
/* Cannot find requested certificate - exit immediately */
|
|
nResult = SOAP_SSL_ERROR;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!WinHttpSetOption(hHttpRequest,WINHTTP_OPTION_CLIENT_CERT_CONTEXT,
|
|
(LPVOID)pData->certificate,sizeof(CERT_CONTEXT)))
|
|
{
|
|
soap->error = GetLastError();
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: fsend, error %d (%s) in WinHttpSetOption\n",
|
|
soap, soap->error, winhttp_error_message(soap,soap->error) ));
|
|
nResult = SOAP_HTTP_ERROR;
|
|
break;
|
|
}
|
|
|
|
attemptNum ++;
|
|
bRetryPost = TRUE;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
|
|
if(((winhttp_data*)soap->plugins->data)->krb == FALSE)
|
|
{
|
|
DWORD suppSchemes;
|
|
DWORD firstScheme;
|
|
DWORD authTarget;
|
|
DWORD actualScheme;
|
|
|
|
if ( !WinHttpQueryAuthSchemes( hHttpRequest,
|
|
&suppSchemes,
|
|
&firstScheme,
|
|
&authTarget))
|
|
{
|
|
soap->error = GetLastError();
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: fsend, error %d (%s) in WinHttpQueryAuthSchemes\n",
|
|
soap, soap->error, winhttp_error_message(soap,soap->error) ));
|
|
nResult = SOAP_HTTP_ERROR;
|
|
break;
|
|
}
|
|
|
|
wchar_t* usernameW = convert_to_unicode(pData->username, 32);
|
|
wchar_t* passwordW = convert_to_unicode(pData->password, 32);
|
|
|
|
int username_len = strnlen_s(pData->username, 32);
|
|
int password_len = strnlen_s(pData->password, 32);
|
|
|
|
if(is_emulator)
|
|
{
|
|
actualScheme = WINHTTP_AUTH_SCHEME_BASIC;
|
|
}
|
|
else
|
|
{
|
|
actualScheme = WINHTTP_AUTH_SCHEME_DIGEST;
|
|
}
|
|
|
|
if(!WinHttpSetCredentials(hHttpRequest,authTarget,
|
|
actualScheme,usernameW,passwordW,NULL))
|
|
{
|
|
soap->error = GetLastError();
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: fsend, error %d (%s) in WinHttpSetCredentials\n",
|
|
soap, soap->error, winhttp_error_message(soap,soap->error) ));
|
|
nResult = SOAP_HTTP_ERROR;
|
|
|
|
wmemset(usernameW,0,username_len);
|
|
wmemset(passwordW,0,password_len);
|
|
free(usernameW);
|
|
free(passwordW);
|
|
|
|
break;
|
|
}
|
|
|
|
wmemset(usernameW,0,username_len);
|
|
wmemset(passwordW,0,password_len);
|
|
|
|
free(usernameW);
|
|
free(passwordW);
|
|
|
|
bRetryPost = TRUE;
|
|
pData->bDisconnect = FALSE;
|
|
attemptNum ++;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
nResult = SOAP_HTTP_ERROR;
|
|
bRetryPost = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
if we need authentication, then request the user for the
|
|
appropriate data. Their reply is saved into the request so
|
|
that we can use it later.
|
|
*/
|
|
switch ( dwStatusCode )
|
|
{
|
|
case HTTP_STATUS_DENIED:
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: fsend, user authenication required\n",
|
|
soap ));
|
|
{
|
|
/* clean up the HTML formatted data */
|
|
LPSTR buffer;
|
|
DWORD dwBufSize = 0;
|
|
DWORD dwDataReaded = 0;
|
|
do
|
|
{
|
|
if(!WinHttpQueryDataAvailable(hHttpRequest,&dwBufSize))
|
|
{
|
|
soap->error = GetLastError();
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: fsend, error %d (%s) in WinHttpQueryDataAvailable\n",
|
|
soap, soap->error, winhttp_error_message(soap,soap->error) ));
|
|
nResult = SOAP_HTTP_ERROR;
|
|
dwBufSize = 0;
|
|
}
|
|
buffer = new char[dwBufSize + 1];
|
|
if(!buffer)
|
|
{
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: fsend, cannot allocate memory\n",
|
|
soap ));
|
|
nResult = SOAP_HTTP_ERROR;
|
|
dwBufSize = 0;
|
|
}
|
|
else
|
|
{
|
|
memset(buffer,0,dwBufSize + 1);
|
|
if(!WinHttpReadData(hHttpRequest, (LPVOID)buffer, dwBufSize,
|
|
&dwDataReaded))
|
|
{
|
|
soap->error = GetLastError();
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: fsend, error %d (%s) in WinHttpReadData\n",
|
|
soap, soap->error, winhttp_error_message(soap,soap->error) ));
|
|
nResult = SOAP_HTTP_ERROR;
|
|
dwBufSize = 0;
|
|
}
|
|
|
|
delete [] buffer;
|
|
}
|
|
}while(dwBufSize > 0);
|
|
|
|
if(nResult == SOAP_HTTP_ERROR)
|
|
{
|
|
break;
|
|
}
|
|
/*
|
|
Query HTTP header for an authentication schemes
|
|
supported by the server side
|
|
*/
|
|
DWORD suppSchemes;
|
|
DWORD firstScheme;
|
|
DWORD authTarget;
|
|
DWORD actualScheme;
|
|
|
|
if ( !WinHttpQueryAuthSchemes( hHttpRequest,
|
|
&suppSchemes,
|
|
&firstScheme,
|
|
&authTarget))
|
|
{
|
|
soap->error = GetLastError();
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: fsend, error %d (%s) in WinHttpQueryAuthSchemes\n",
|
|
soap, soap->error, winhttp_error_message(soap,soap->error) ));
|
|
nResult = SOAP_HTTP_ERROR;
|
|
break;
|
|
}
|
|
|
|
|
|
int is_emulator = 0;
|
|
if (strstr(soap->path, ".asmx") == (soap->path+ strnlen_s(soap->path, SOAP_TAGLEN - 1) -5))
|
|
{
|
|
is_emulator = 1;
|
|
}
|
|
|
|
/*
|
|
Support the basic authentication scheme only if we are working
|
|
over Intel(R) AMT Emulator.
|
|
*/
|
|
if( is_emulator == 0 && (suppSchemes & WINHTTP_AUTH_SCHEME_BASIC))
|
|
{
|
|
nResult = SOAP_HTTP_ERROR;
|
|
break;
|
|
}
|
|
|
|
/* Select required authentication scheme */
|
|
actualScheme = select_scheme(suppSchemes);
|
|
if(actualScheme == 0)
|
|
{
|
|
nResult = SOAP_HTTP_ERROR;
|
|
break;
|
|
}
|
|
|
|
if(actualScheme == WINHTTP_AUTH_SCHEME_NEGOTIATE &&
|
|
((winhttp_data*)soap->plugins->data)->krb == TRUE)
|
|
{
|
|
BOOL res;
|
|
DWORD data = WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW;
|
|
DWORD dataSize = sizeof(DWORD);
|
|
res = WinHttpSetOption(
|
|
hHttpRequest,
|
|
WINHTTP_OPTION_AUTOLOGON_POLICY,
|
|
&data,
|
|
dataSize);
|
|
}
|
|
else
|
|
{
|
|
wchar_t* usernameW = convert_to_unicode(pData->username, 32);
|
|
wchar_t* passwordW = convert_to_unicode(pData->password, 32);
|
|
|
|
int username_len = strnlen_s(pData->username, 32);
|
|
int password_len = strnlen_s(pData->password, 32);
|
|
|
|
if(is_emulator)
|
|
{
|
|
actualScheme = WINHTTP_AUTH_SCHEME_BASIC;
|
|
}
|
|
else
|
|
{
|
|
actualScheme = WINHTTP_AUTH_SCHEME_DIGEST;
|
|
}
|
|
|
|
if(!WinHttpSetCredentials(hHttpRequest,authTarget,
|
|
actualScheme,usernameW,passwordW,NULL))
|
|
{
|
|
soap->error = GetLastError();
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: fsend, error %d (%s) in WinHttpSetCredentials\n",
|
|
soap, soap->error, winhttp_error_message(soap,soap->error) ));
|
|
nResult = SOAP_HTTP_ERROR;
|
|
|
|
wmemset(usernameW,0,username_len);
|
|
wmemset(passwordW,0,password_len);
|
|
free(usernameW);
|
|
free(passwordW);
|
|
|
|
break;
|
|
}
|
|
|
|
wmemset(usernameW,0,username_len);
|
|
wmemset(passwordW,0,password_len);
|
|
|
|
free(usernameW);
|
|
free(passwordW);
|
|
|
|
}
|
|
|
|
/*
|
|
we may have been disconnected by the error. Since we
|
|
are going to try again, we will automatically be
|
|
reconnected. Therefore we want to disregard any previous
|
|
disconnection messages.
|
|
*/
|
|
pData->bDisconnect = FALSE;
|
|
/*
|
|
indicate the request failed and try again if this is the
|
|
first failing request
|
|
*/
|
|
attemptNum++;
|
|
/* set default response in case retry limit exceeded */
|
|
nResult = SOAP_HTTP_ERROR;
|
|
bRetryPost = TRUE;
|
|
continue;
|
|
}
|
|
|
|
case HTTP_STATUS_OK:
|
|
nResult = SOAP_OK;
|
|
break;
|
|
/* the client certificate was not accepted */
|
|
case HTTP_STATUS_FORBIDDEN:
|
|
nResult = SOAP_SSL_ERROR;
|
|
continue;
|
|
case 400:
|
|
nResult = SOAP_OK;
|
|
break;
|
|
case 500:
|
|
nResult = SOAP_OK;
|
|
break;
|
|
default:
|
|
nResult = SOAP_HTTP_ERROR;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* if we have an allocated buffer then we can deallocate it now */
|
|
if ( pData->pBuffer != a_pBuffer )
|
|
{
|
|
free( pData->pBuffer );
|
|
}
|
|
pData->pBuffer = 0;
|
|
pData->uiBufferLen = 0;
|
|
pData->uiBufferLenMax = INVALID_BUFFER_LENGTH;
|
|
|
|
return nResult;
|
|
}
|
|
|
|
/* gsoap documentation:
|
|
Called for all receive operations to fill buffer s of maximum length n.
|
|
Should return the number of bytes read or 0 in case of an error, e.g. EOF.
|
|
Built-in gSOAP function: frecv
|
|
*/
|
|
static size_t
|
|
winhttp_frecv(
|
|
struct soap * soap,
|
|
char * a_pBuffer,
|
|
size_t a_uiBufferLen )
|
|
{
|
|
#if defined(__cplusplus) && !defined(WITH_LEAN)
|
|
if (soap->is)
|
|
{ if (soap->is->good())
|
|
return soap->is->read(a_pBuffer, a_uiBufferLen).gcount();
|
|
return 0;
|
|
}
|
|
#endif
|
|
HINTERNET hHttpRequest = (HINTERNET) soap->socket;
|
|
DWORD dwBytesRead = 0;
|
|
size_t uiTotalBytesRead = 0;
|
|
BOOL bResult;
|
|
|
|
soap->error = SOAP_OK;
|
|
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: frecv, available buffer len = %lu\n",
|
|
soap, a_uiBufferLen ));
|
|
|
|
/*
|
|
NOTE: we do not check here that our connection hasn't been
|
|
disconnected because in HTTP/1.0 connections, it will always have been
|
|
disconnected by now. This is because the response is checked by the
|
|
winhttp_fsend function to ensure that we didn't need any special
|
|
authentication. At that time the connection would have been
|
|
disconnected. This is ok however as we can still read the response
|
|
from the request handle.
|
|
*/
|
|
|
|
do
|
|
{
|
|
/* read from the connection up to our maximum amount of data */
|
|
_ASSERTE( a_uiBufferLen <= ULONG_MAX );
|
|
bResult = WinHttpReadData(
|
|
hHttpRequest,
|
|
&a_pBuffer[uiTotalBytesRead],
|
|
(DWORD) a_uiBufferLen - (DWORD) uiTotalBytesRead,
|
|
&dwBytesRead );
|
|
if ( bResult )
|
|
{
|
|
uiTotalBytesRead += dwBytesRead;
|
|
}
|
|
else
|
|
{
|
|
soap->error = GetLastError();
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: frecv, error %d (%s) in WinHttpReadData\n",
|
|
soap, soap->error, winhttp_error_message(soap,soap->error) ));
|
|
}
|
|
}
|
|
while ( bResult && dwBytesRead && uiTotalBytesRead < a_uiBufferLen );
|
|
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: recv, received %lu bytes\n", soap, uiTotalBytesRead ));
|
|
//char * tmp = new char[uiTotalBytesRead + 1];
|
|
// tmp[uiTotalBytesRead] = 0;
|
|
// memcpy(tmp, a_pBuffer, uiTotalBytesRead);
|
|
// fprintf(stdout, "*******************\nreceieved\n**************\n%s\n*************\n\n", tmp);
|
|
return uiTotalBytesRead;
|
|
}
|
|
|
|
/* gsoap documentation:
|
|
Called by client proxy multiple times, to close a socket connection before
|
|
a new socket connection is established and at the end of communications
|
|
when the SOAP_IO_KEEPALIVE flag is not set and soap.keep_alive = 0
|
|
(indicating that the other party supports keep alive). Should return
|
|
SOAP_OK, or a gSOAP error code. Built-in gSOAP function: tcp_disconnect
|
|
*/
|
|
static int
|
|
winhttp_disconnect(
|
|
struct soap * soap )
|
|
{
|
|
struct winhttp_data * pData =
|
|
(struct winhttp_data *) soap_lookup_plugin( soap, winhttp_id );
|
|
|
|
soap->error = SOAP_OK;
|
|
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "winhttp %p: disconnect\n", soap ));
|
|
|
|
/* force a disconnect by setting the disconnect flag to TRUE */
|
|
pData->bDisconnect = TRUE;
|
|
winhttp_have_connection( soap, pData );
|
|
|
|
return SOAP_OK;
|
|
}
|
|
|
|
/* this is mostly for debug tracing */
|
|
void CALLBACK
|
|
winhttp_callback(
|
|
HINTERNET hInternet,
|
|
DWORD_PTR dwContext,
|
|
DWORD dwInternetStatus,
|
|
LPVOID lpvStatusInformation,
|
|
DWORD dwStatusInformationLength )
|
|
{
|
|
struct soap * soap = (struct soap *) dwContext;
|
|
|
|
UNUSED_ARG( hInternet );
|
|
UNUSED_ARG( lpvStatusInformation );
|
|
UNUSED_ARG( dwStatusInformationLength );
|
|
|
|
switch ( dwInternetStatus )
|
|
{
|
|
|
|
case WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION:
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION\n", soap ));
|
|
break;
|
|
case WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER:
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER\n", soap ));
|
|
break;
|
|
case WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER:
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER\n", soap ));
|
|
break;
|
|
case WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED:
|
|
DBGLOG( TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED\n", soap ));
|
|
if( soap )
|
|
{
|
|
/* the connection has been closed, so we close the handle here */
|
|
struct winhttp_data * pData =
|
|
(struct winhttp_data *) soap_lookup_plugin( soap, winhttp_id );
|
|
if ( pData->hConnection )
|
|
{
|
|
/*
|
|
we only mark this for disconnection otherwise we get
|
|
errors when reading the data from the handle. In every
|
|
function that we use the connection we will check first to
|
|
see if it has been disconnected.
|
|
*/
|
|
pData->bDisconnect = TRUE;
|
|
}
|
|
}
|
|
|
|
break;
|
|
case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE\n", soap ));
|
|
break;
|
|
case WINHTTP_CALLBACK_STATUS_HANDLE_CREATED:
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: WINHTTP_CALLBACK_STATUS_HANDLE_CREATED\n", soap ));
|
|
break;
|
|
case WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING:
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING\n", soap ));
|
|
break;
|
|
case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE:
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE\n", soap ));
|
|
break;
|
|
case WINHTTP_CALLBACK_STATUS_INTERMEDIATE_RESPONSE:
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: WINHTTP_CALLBACK_STATUS_INTERMEDIATE_RESPONSE\n", soap ));
|
|
break;
|
|
case WINHTTP_CALLBACK_STATUS_NAME_RESOLVED:
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: WINHTTP_CALLBACK_STATUS_NAME_RESOLVED\n", soap ));
|
|
break;
|
|
case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: WINHTTP_CALLBACK_STATUS_READ_COMPLETE\n", soap ));
|
|
break;
|
|
case WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE:
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE\n", soap ));
|
|
break;
|
|
case WINHTTP_CALLBACK_STATUS_REDIRECT:
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: WINHTTP_CALLBACK_STATUS_REDIRECT, new url = %s\n",
|
|
soap, (char*) lpvStatusInformation ));
|
|
break;
|
|
case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: WINHTTP_CALLBACK_STATUS_REQUEST_ERROR\n", soap ));
|
|
break;
|
|
case WINHTTP_CALLBACK_STATUS_REQUEST_SENT:
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: WINHTTP_CALLBACK_STATUS_REQUEST_SENT, bytes sent = %lu\n",
|
|
soap, *(DWORD*)lpvStatusInformation ));
|
|
break;
|
|
case WINHTTP_CALLBACK_STATUS_RESOLVING_NAME:
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: WINHTTP_CALLBACK_STATUS_RESOLVING_NAME\n", soap ));
|
|
break;
|
|
case WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED:
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, bytes received = %lu\n",
|
|
soap, *(DWORD *)lpvStatusInformation ));
|
|
break;
|
|
case WINHTTP_CALLBACK_STATUS_SECURE_FAILURE:
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: WINHTTP_CALLBACK_STATUS_SECURE_FAILURE\n", soap ));
|
|
break;
|
|
case WINHTTP_CALLBACK_STATUS_SENDING_REQUEST:
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: WINHTTP_CALLBACK_STATUS_SENDING_REQUEST\n", soap ));
|
|
break;
|
|
case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE:
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE\n", soap ));
|
|
break;
|
|
case WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE:
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE\n", soap ));
|
|
break;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
/*
|
|
check to ensure that our connection hasn't been disconnected
|
|
and disconnect remaining handles if necessary.
|
|
*/
|
|
static BOOL
|
|
winhttp_have_connection(
|
|
struct soap * soap,
|
|
struct winhttp_data * a_pData )
|
|
{
|
|
/* close the http request if we don't have a connection */
|
|
BOOL bCloseRequest = a_pData->bDisconnect || !a_pData->hConnection;
|
|
if ( bCloseRequest && soap->socket != SOAP_INVALID_SOCKET )
|
|
{
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: closing request\n", soap));
|
|
|
|
WinHttpCloseHandle( (HINTERNET) soap->socket );
|
|
soap->socket = SOAP_INVALID_SOCKET;
|
|
}
|
|
|
|
/* close the connection if we don't have a request */
|
|
if ( soap->socket == SOAP_INVALID_SOCKET && a_pData->hConnection )
|
|
{
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: closing connection\n", soap));
|
|
|
|
WinHttpCloseHandle( a_pData->hConnection );
|
|
a_pData->hConnection = NULL;
|
|
}
|
|
a_pData->bDisconnect = FALSE;
|
|
|
|
/* clean up the send details if we don't have a request */
|
|
if ( soap->socket == SOAP_INVALID_SOCKET )
|
|
{
|
|
if ( a_pData->pBuffer )
|
|
{
|
|
free( a_pData->pBuffer );
|
|
a_pData->pBuffer = 0;
|
|
}
|
|
|
|
a_pData->uiBufferLen = 0;
|
|
a_pData->uiBufferLenMax = INVALID_BUFFER_LENGTH;
|
|
}
|
|
|
|
/* we now either still have both request and connection, or neither */
|
|
return (a_pData->hConnection != NULL);
|
|
}
|
|
|
|
static DWORD
|
|
winhttp_set_timeout(
|
|
struct soap * soap,
|
|
struct winhttp_data * a_pData,
|
|
const char * a_pszTimeout,
|
|
DWORD a_dwOption,
|
|
int a_nTimeout )
|
|
{
|
|
UNUSED_ARG( soap );
|
|
UNUSED_ARG( a_pszTimeout );
|
|
|
|
if ( a_nTimeout > 0 )
|
|
{
|
|
DWORD dwTimeout = a_nTimeout * MILLISECONDS;
|
|
if ( !WinHttpSetOption( a_pData->hInternet,
|
|
a_dwOption, &dwTimeout, sizeof(DWORD) ) )
|
|
{
|
|
DWORD dwErrorCode = GetLastError();
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: failed to set %s timeout, error %d (%s)\n",
|
|
soap, a_pszTimeout, dwErrorCode,
|
|
winhttp_error_message(soap,dwErrorCode) ));
|
|
return dwErrorCode;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Intel(R) documentation:
|
|
Called by winhttp_fsend() function. Searches the certificate store
|
|
pointed by the hStoreHandle for the client certificate purposed
|
|
for an Intel(R) AMT authorization.
|
|
If such certificate was found and its Common Name (CN) match
|
|
the string pointed by the certName (or certName is NULL), pCertContext
|
|
will point to the structure that represents the certificate and this
|
|
function will return TRUE, otherwise the function will return FALSE.
|
|
*/
|
|
static BOOL
|
|
winhttp_find_cert(
|
|
struct soap * soap,
|
|
HANDLE hStoreHandle,
|
|
const _TCHAR * certName,
|
|
PCCERT_CONTEXT *pCertContext )
|
|
{
|
|
_TCHAR pszNameString[CERT_MAX_STR_LEN];
|
|
|
|
LPSTR *oids = new LPSTR[NUM_OF_OIDS];
|
|
if (!oids) {
|
|
return false;
|
|
}
|
|
oids[0] = OID_CLIENT;
|
|
if(((winhttp_data*)soap->plugins->data)->local == TRUE)
|
|
{
|
|
oids[1] = OID_LOCAL;
|
|
}
|
|
else
|
|
{
|
|
oids[1] = OID_REMOTE;
|
|
}
|
|
|
|
CERT_ENHKEY_USAGE amtOID = {NUM_OF_OIDS, oids};
|
|
bool certSuccess = false;
|
|
|
|
/*
|
|
Search for the first client certificate
|
|
purposed for an Intel(R) AMT authorization.
|
|
*/
|
|
*pCertContext = CertFindCertificateInStore(
|
|
hStoreHandle,
|
|
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|
CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG,
|
|
CERT_FIND_ENHKEY_USAGE,
|
|
&amtOID,
|
|
NULL);
|
|
|
|
/*
|
|
If certificate was found - determinate its name. Keep search
|
|
while the certificate's Common Name doesn't match the name
|
|
defined by the user
|
|
*/
|
|
while(*pCertContext != NULL)
|
|
{
|
|
if(!CertGetNameString( *pCertContext,
|
|
CERT_NAME_SIMPLE_DISPLAY_TYPE,
|
|
0,
|
|
NULL,
|
|
pszNameString,
|
|
CERT_MAX_STR_LEN-1))
|
|
{
|
|
/* obtaining certificate name failed */
|
|
soap->error = GetLastError();
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: fsend, error %d (%s) in CertGetNameString\n",
|
|
soap, soap->error, winhttp_error_message(soap,soap->error) ));
|
|
break;
|
|
}
|
|
|
|
if (certName == NULL ||
|
|
_tcscmp(pszNameString,certName) == 0)
|
|
{
|
|
certSuccess = true;
|
|
break;
|
|
}
|
|
|
|
*pCertContext =
|
|
CertFindCertificateInStore(hStoreHandle,
|
|
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|
CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG,
|
|
CERT_FIND_ENHKEY_USAGE,
|
|
&amtOID,
|
|
*pCertContext);
|
|
|
|
}//while(pCertContext != NULL && certSuccess == true)
|
|
|
|
if(*pCertContext == NULL)
|
|
{
|
|
soap->error = GetLastError();
|
|
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
|
|
"winhttp %p: fsend, error %d (%s) in CertFindCertificateInStore\n",
|
|
soap, soap->error, winhttp_error_message(soap,soap->error) ));
|
|
}
|
|
|
|
// clean up
|
|
if(oids)
|
|
{
|
|
delete [] oids;
|
|
}
|
|
|
|
return certSuccess;
|
|
}
|
|
|
|
void
|
|
winhttp_set_local(
|
|
struct soap* soap,
|
|
BOOL local)
|
|
{
|
|
((winhttp_data*)soap->plugins->data)->local = local;
|
|
}
|
|
|
|
void
|
|
winhttp_set_auth_scheme(
|
|
struct soap* soap,
|
|
BOOL krb)
|
|
{
|
|
((winhttp_data*)soap->plugins->data)->krb = krb;
|
|
}
|
|
|
|
bool
|
|
winhttp_set_proxy(struct soap* soap)
|
|
{
|
|
// winhttp proxy info
|
|
WINHTTP_PROXY_INFO info;
|
|
BOOL result;
|
|
info.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
|
|
wchar_t *proxy_target = NULL;
|
|
string uri = string(soap->proxy_host);
|
|
uri += ":";
|
|
ostringstream buffer;
|
|
buffer << soap->proxy_port;
|
|
uri += buffer.str();
|
|
//Sending The maximum length of a URI in the address bar = 2048 characters
|
|
proxy_target = convert_to_unicode(uri.c_str(), 2048);
|
|
info.lpszProxy = (LPWSTR)proxy_target;
|
|
info.lpszProxyBypass = L"";
|
|
do{
|
|
result = WinHttpSetOption(
|
|
((winhttp_data*)soap->plugins->data)->hInternet,
|
|
WINHTTP_OPTION_PROXY,
|
|
&info,
|
|
sizeof(info));
|
|
|
|
free(proxy_target);
|
|
if(!result)
|
|
break;
|
|
|
|
|
|
}while(0);
|
|
return (result == TRUE);
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
winhttp_set_local_machine_store(
|
|
struct soap* soap,
|
|
BOOL localMachineStore)
|
|
{
|
|
((winhttp_data*)soap->plugins->data)->localMachineStore = localMachineStore;
|
|
}
|
|
|
|
/*Intel(R) documentation:
|
|
Function intended for an external use. Application that uses
|
|
gSOAP with WinHTTP plug-in can perform certificate search
|
|
and pass a certificate that was found into this plug-in.
|
|
*/
|
|
void
|
|
winhttp_set_certificate(
|
|
struct soap* soap,
|
|
PCCERT_CONTEXT certificate)
|
|
{
|
|
((winhttp_data*)soap->plugins->data)->certificate = certificate;
|
|
}
|
|
|
|
/*Intel(R) documentation:
|
|
Function intended for an external use. Application that uses gSOAP
|
|
with WinHTTP plug-in sets the certificate's Common Name
|
|
to the WinHTTP plug-in structure.
|
|
*/
|
|
void
|
|
winhttp_set_certificate_name(
|
|
struct soap* soap,
|
|
const _TCHAR* certificateName)
|
|
{
|
|
((winhttp_data*)soap->plugins->data)->certificateName = certificateName;
|
|
}
|
|
|
|
static void
|
|
winhttp_set_headers(
|
|
struct soap* soap,
|
|
const WCHAR *headers)
|
|
{
|
|
((winhttp_data*)soap->plugins->data)->headers = headers;
|
|
}
|
|
|
|
const WCHAR *
|
|
winhttp_get_headers(
|
|
struct soap* soap)
|
|
{
|
|
return ((winhttp_data*)soap->plugins->data)->headers.c_str();
|
|
}
|
|
|
|
/*Intel(R) documentation:
|
|
Function converts the array of the regular one byte size characters
|
|
to the array of the wide characters.
|
|
*/
|
|
static wchar_t *
|
|
convert_to_unicode(
|
|
const char * str, int strLen)
|
|
{
|
|
if( !str )
|
|
{
|
|
return 0;
|
|
}
|
|
wchar_t *unicode_str = (wchar_t *)malloc((strnlen_s(str, strLen)+1)* sizeof( wchar_t ));
|
|
if( unicode_str )
|
|
{
|
|
size_t charsConverted = 0;
|
|
if(mbstowcs_s(&charsConverted, unicode_str, strnlen_s(str, strLen) + 1, str, strnlen_s(str, strLen) + 1) <= 0 )
|
|
{
|
|
free(unicode_str);
|
|
return 0;
|
|
}
|
|
}
|
|
return unicode_str;
|
|
}
|
|
|
|
/*Intel(R) documentation:
|
|
Function returns the most strict authentication scheme from those that
|
|
the server request. In case of error (unsupported scheme) zero is returned.
|
|
*/
|
|
DWORD
|
|
select_scheme(DWORD suppSchemes)
|
|
{
|
|
if(suppSchemes & WINHTTP_AUTH_SCHEME_NEGOTIATE)
|
|
{
|
|
return WINHTTP_AUTH_SCHEME_NEGOTIATE;
|
|
}
|
|
if(suppSchemes & WINHTTP_AUTH_SCHEME_NTLM)
|
|
{
|
|
return WINHTTP_AUTH_SCHEME_NTLM;
|
|
}
|
|
if(suppSchemes & WINHTTP_AUTH_SCHEME_PASSPORT)
|
|
{
|
|
return WINHTTP_AUTH_SCHEME_PASSPORT;
|
|
}
|
|
if(suppSchemes & WINHTTP_AUTH_SCHEME_DIGEST)
|
|
{
|
|
return WINHTTP_AUTH_SCHEME_DIGEST;
|
|
}
|
|
if(suppSchemes & WINHTTP_AUTH_SCHEME_BASIC)
|
|
{
|
|
return WINHTTP_AUTH_SCHEME_BASIC;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
#ifdef SOAP_DEBUG
|
|
static const char *
|
|
winhttp_error_message(
|
|
struct soap * soap,
|
|
DWORD a_dwErrorMsgId )
|
|
{
|
|
HINSTANCE hModule;
|
|
DWORD dwResult;
|
|
DWORD dwFormatFlags;
|
|
struct winhttp_data * pData =
|
|
(struct winhttp_data *) soap_lookup_plugin( soap, winhttp_id );
|
|
|
|
/* free any existing error message */
|
|
winhttp_free_error_message( pData );
|
|
|
|
dwFormatFlags =
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_IGNORE_INSERTS |
|
|
FORMAT_MESSAGE_FROM_SYSTEM;
|
|
|
|
/* load winhttp.dll for the error messages */
|
|
hModule = LoadLibraryExA( "winhttp.dll", NULL,
|
|
LOAD_LIBRARY_AS_DATAFILE | DONT_RESOLVE_DLL_REFERENCES );
|
|
if ( hModule )
|
|
{
|
|
dwFormatFlags |= FORMAT_MESSAGE_FROM_HMODULE;
|
|
}
|
|
|
|
/* format the messages */
|
|
dwResult = FormatMessageA(
|
|
dwFormatFlags,
|
|
hModule,
|
|
a_dwErrorMsgId,
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
|
|
(LPSTR) &pData->pszErrorMessage,
|
|
0,
|
|
NULL );
|
|
|
|
/* free the library if we loaded it */
|
|
if ( hModule )
|
|
{
|
|
FreeLibrary( hModule );
|
|
}
|
|
|
|
/* remove the CR LF from the error message */
|
|
if ( dwResult > 2 )
|
|
{
|
|
pData->pszErrorMessage[dwResult-2] = 0;
|
|
return pData->pszErrorMessage;
|
|
}
|
|
else
|
|
{
|
|
const static char szUnknown[] = "(unknown)";
|
|
return szUnknown;
|
|
}
|
|
}
|
|
|
|
static void
|
|
winhttp_free_error_message(
|
|
struct winhttp_data * a_pData )
|
|
{
|
|
if ( a_pData->pszErrorMessage )
|
|
{
|
|
LocalFree( a_pData->pszErrorMessage );
|
|
a_pData->pszErrorMessage = 0;
|
|
}
|
|
}
|
|
|
|
#endif /* SOAP_DEBUG */
|