978 lines
33 KiB
C
978 lines
33 KiB
C
/*=============================================================================
|
|
xmlrpc_wininet_transport
|
|
===============================================================================
|
|
WinInet-based client transport for Xmlrpc-c. Copyright information at
|
|
the bottom of this file.
|
|
|
|
=============================================================================*/
|
|
|
|
#include "xmlrpc_config.h"
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <stddef.h>
|
|
#include <windows.h>
|
|
#include <wininet.h>
|
|
|
|
#include "bool.h"
|
|
#include "mallocvar.h"
|
|
#include "linklist.h"
|
|
#include "casprintf.h"
|
|
#include "pthreadx.h"
|
|
|
|
#include "xmlrpc-c/base.h"
|
|
#include "xmlrpc-c/base_int.h"
|
|
#include "xmlrpc-c/client.h"
|
|
#include "xmlrpc-c/client_int.h"
|
|
#include "xmlrpc-c/transport.h"
|
|
|
|
#if defined(_DEBUG)
|
|
# include <crtdbg.h>
|
|
# define new DEBUG_NEW
|
|
# define malloc(size) _malloc_dbg( size, _NORMAL_BLOCK, __FILE__, __LINE__)
|
|
# undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
|
|
static HINTERNET hSyncInternetSession = NULL;
|
|
|
|
/* Declare WinInet status callback. */
|
|
void CALLBACK
|
|
statusCallback(HINTERNET const hInternet,
|
|
unsigned long const dwContext,
|
|
unsigned long const dwInternetStatus,
|
|
void * const lpvStatusInformation,
|
|
unsigned long const dwStatusInformationLength);
|
|
|
|
|
|
struct xmlrpc_client_transport {
|
|
pthread_mutex_t listLock;
|
|
struct list_head rpcList;
|
|
/* List of all RPCs that exist for this transport. An RPC exists
|
|
from the time the user requests it until the time the user
|
|
acknowledges it is done.
|
|
*/
|
|
int allowInvalidSSLCerts;
|
|
/* Flag to specify if we ignore invalid SSL Certificates. If this
|
|
is set to zero, calling a XMLRPC server with an invalid SSL
|
|
certificate will fail. This is the default behavior of the other
|
|
transports, but invalid certificates were allowed in pre 1.2
|
|
wininet xmlrpc-c transports.
|
|
*/
|
|
};
|
|
|
|
typedef struct {
|
|
unsigned long http_status;
|
|
HINTERNET hHttpRequest;
|
|
HINTERNET hURL;
|
|
INTERNET_PORT nPort;
|
|
char szHostName[255];
|
|
char szUrlPath[255];
|
|
BOOL bUseSSL;
|
|
char * headerList;
|
|
BYTE * pSendData;
|
|
xmlrpc_mem_block * pResponseData;
|
|
} winInetTransaction;
|
|
|
|
typedef struct {
|
|
struct list_head link; /* link in transport's list of RPCs */
|
|
winInetTransaction * winInetTransactionP;
|
|
/* The object which does the HTTP transaction, with no knowledge
|
|
of XML-RPC or Xmlrpc-c.
|
|
*/
|
|
xmlrpc_mem_block * responseXmlP;
|
|
xmlrpc_bool threadExists;
|
|
pthread_t thread;
|
|
xmlrpc_transport_asynch_complete complete;
|
|
/* Routine to call to complete the RPC after it is complete HTTP-wise.
|
|
NULL if none.
|
|
*/
|
|
struct xmlrpc_call_info * callInfoP;
|
|
/* User's identifier for this RPC */
|
|
struct xmlrpc_client_transport * clientTransportP;
|
|
} rpc;
|
|
|
|
|
|
|
|
static void
|
|
createWinInetHeaderList(xmlrpc_env * const envP,
|
|
const xmlrpc_server_info * const serverP,
|
|
char ** const headerListP) {
|
|
|
|
const char * const szContentType = "Content-Type: text/xml\r\n";
|
|
|
|
char * szHeaderList;
|
|
|
|
/* Send an authorization header if we need one. */
|
|
if (serverP->allowedAuth.basic) {
|
|
/* Make the header with content type and authorization */
|
|
/* NOTE: A newline is required between each added header */
|
|
szHeaderList = malloc(strlen(szContentType) + 17 +
|
|
strlen(serverP->basicAuthHdrValue) + 1);
|
|
|
|
if (szHeaderList == NULL)
|
|
xmlrpc_faultf(envP,
|
|
"Couldn't allocate memory for authorization header");
|
|
else {
|
|
memcpy(szHeaderList, szContentType, strlen(szContentType));
|
|
memcpy(szHeaderList + strlen(szContentType),"\r\nAuthorization: ",
|
|
17);
|
|
memcpy(szHeaderList + strlen(szContentType) + 17,
|
|
serverP->basicAuthHdrValue,
|
|
strlen(serverP->basicAuthHdrValue) + 1);
|
|
}
|
|
} else {
|
|
/* Just the content type header is needed */
|
|
szHeaderList = malloc(strlen(szContentType) + 1);
|
|
|
|
if (szHeaderList == NULL)
|
|
xmlrpc_faultf(envP,
|
|
"Couldn't allocate memory for standard header");
|
|
else
|
|
memcpy(szHeaderList, szContentType, strlen(szContentType) + 1);
|
|
}
|
|
*headerListP = szHeaderList;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
createWinInetTransaction(xmlrpc_env * const envP,
|
|
const xmlrpc_server_info * const serverP,
|
|
xmlrpc_mem_block * const callXmlP,
|
|
xmlrpc_mem_block * const responseXmlP,
|
|
winInetTransaction ** const winInetTranPP) {
|
|
|
|
winInetTransaction * winInetTransactionP;
|
|
|
|
MALLOCVAR(winInetTransactionP);
|
|
if (winInetTransactionP == NULL)
|
|
xmlrpc_faultf(envP, "No memory to create WinInet transaction.");
|
|
else {
|
|
char szExtraInfo[255];
|
|
char szScheme[100];
|
|
URL_COMPONENTS uc;
|
|
BOOL succeeded;
|
|
|
|
/* Init to defaults */
|
|
winInetTransactionP->http_status = 0;
|
|
winInetTransactionP->hHttpRequest = NULL;
|
|
winInetTransactionP->hURL = NULL;
|
|
winInetTransactionP->headerList = NULL;
|
|
winInetTransactionP->pSendData = NULL;
|
|
winInetTransactionP->pResponseData = responseXmlP;
|
|
|
|
/* Parse the URL and store results into the winInetTransaction */
|
|
|
|
memset(&uc, 0, sizeof(uc));
|
|
uc.dwStructSize = sizeof (uc);
|
|
uc.lpszScheme = szScheme;
|
|
uc.dwSchemeLength = 100;
|
|
uc.lpszHostName = winInetTransactionP->szHostName;
|
|
uc.dwHostNameLength = 255;
|
|
uc.lpszUrlPath = winInetTransactionP->szUrlPath;
|
|
uc.dwUrlPathLength = 255;
|
|
uc.lpszExtraInfo = szExtraInfo;
|
|
uc.dwExtraInfoLength = 255;
|
|
succeeded = InternetCrackUrl(serverP->serverUrl,
|
|
strlen(serverP->serverUrl),
|
|
ICU_ESCAPE, &uc);
|
|
if (!succeeded)
|
|
xmlrpc_faultf(envP, "Unable to parse the server URL.");
|
|
else {
|
|
winInetTransactionP->nPort =
|
|
uc.nPort ? uc.nPort : INTERNET_DEFAULT_HTTP_PORT;
|
|
if (_strnicmp(uc.lpszScheme, "https", 5) == 0)
|
|
winInetTransactionP->bUseSSL=TRUE;
|
|
else
|
|
winInetTransactionP->bUseSSL=FALSE;
|
|
createWinInetHeaderList(envP, serverP,
|
|
&winInetTransactionP->headerList);
|
|
|
|
XMLRPC_MEMBLOCK_APPEND(char, envP, callXmlP, "\0", 1);
|
|
if (!envP->fault_occurred) {
|
|
winInetTransactionP->pSendData =
|
|
XMLRPC_MEMBLOCK_CONTENTS(char, callXmlP);
|
|
}
|
|
}
|
|
|
|
if (envP->fault_occurred)
|
|
free(winInetTransactionP);
|
|
}
|
|
*winInetTranPP = winInetTransactionP;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
destroyWinInetTransaction(winInetTransaction * const winInetTransactionP) {
|
|
|
|
XMLRPC_ASSERT_PTR_OK(winInetTransactionP);
|
|
|
|
if (winInetTransactionP->hHttpRequest)
|
|
InternetCloseHandle(winInetTransactionP->hHttpRequest);
|
|
|
|
if (winInetTransactionP->hURL)
|
|
InternetCloseHandle(winInetTransactionP->hURL);
|
|
|
|
if (winInetTransactionP->headerList)
|
|
free(winInetTransactionP->headerList);
|
|
|
|
free(winInetTransactionP);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
get_wininet_response(xmlrpc_env * const envP,
|
|
winInetTransaction * const winInetTransactionP) {
|
|
|
|
unsigned long dwLen;
|
|
INTERNET_BUFFERS inetBuffer;
|
|
unsigned long dwFlags;
|
|
unsigned long dwErr;
|
|
unsigned long nExpected;
|
|
void * body;
|
|
BOOL bOK;
|
|
PVOID pMsgMem;
|
|
|
|
pMsgMem = NULL; /* initial value */
|
|
dwErr = 0; /* initial value */
|
|
body = NULL; /* initial value */
|
|
dwLen = sizeof(unsigned long); /* initial value */
|
|
|
|
inetBuffer.dwStructSize = sizeof (INTERNET_BUFFERS);
|
|
inetBuffer.Next = NULL;
|
|
inetBuffer.lpcszHeader = NULL;
|
|
inetBuffer.dwHeadersTotal = 0;
|
|
inetBuffer.dwHeadersLength = 0;
|
|
inetBuffer.dwOffsetHigh = 0;
|
|
inetBuffer.dwOffsetLow = 0;
|
|
inetBuffer.dwBufferLength = 0;
|
|
|
|
/* Note that while Content-Length is optional in HTTP 1.1, it is
|
|
required by XML-RPC. Following fails if server didn't send it.
|
|
*/
|
|
|
|
bOK = HttpQueryInfo(winInetTransactionP->hHttpRequest,
|
|
HTTP_QUERY_CONTENT_LENGTH|HTTP_QUERY_FLAG_NUMBER,
|
|
&inetBuffer.dwBufferTotal, &dwLen, NULL);
|
|
if (!bOK) {
|
|
LPTSTR pMsg;
|
|
dwErr = GetLastError ();
|
|
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_FROM_SYSTEM,
|
|
NULL,
|
|
dwErr,
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
(LPTSTR) &pMsgMem,
|
|
1024,NULL);
|
|
|
|
pMsg = pMsgMem ? (LPTSTR)pMsgMem : "Sync HttpQueryInfo failed.";
|
|
XMLRPC_FAIL(envP, XMLRPC_NETWORK_ERROR, pMsg);
|
|
}
|
|
|
|
if (inetBuffer.dwBufferTotal == 0)
|
|
XMLRPC_FAIL(envP, XMLRPC_NETWORK_ERROR, "WinInet returned no data");
|
|
|
|
inetBuffer.lpvBuffer = calloc(inetBuffer.dwBufferTotal, sizeof(TCHAR));
|
|
body = inetBuffer.lpvBuffer;
|
|
dwFlags = IRF_SYNC;
|
|
nExpected = inetBuffer.dwBufferTotal;
|
|
inetBuffer.dwBufferLength = nExpected;
|
|
InternetQueryDataAvailable(winInetTransactionP->hHttpRequest,
|
|
&inetBuffer.dwBufferLength, 0, 0);
|
|
|
|
/* Read Response from InternetFile */
|
|
do {
|
|
if (inetBuffer.dwBufferLength != 0)
|
|
bOK = InternetReadFileEx(winInetTransactionP->hHttpRequest,
|
|
&inetBuffer, dwFlags, 1);
|
|
|
|
if (!bOK)
|
|
dwErr = GetLastError();
|
|
|
|
if (dwErr) {
|
|
if (dwErr == WSAEWOULDBLOCK || dwErr == ERROR_IO_PENDING) {
|
|
/* Non-block socket operation wait 10 msecs */
|
|
SleepEx(10, TRUE);
|
|
/* Reset dwErr to zero for next pass */
|
|
dwErr = 0;
|
|
} else {
|
|
LPTSTR pMsg;
|
|
FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_FROM_SYSTEM,
|
|
NULL,
|
|
dwErr,
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
(LPTSTR) &pMsgMem,
|
|
1024,NULL);
|
|
pMsg = pMsgMem ?
|
|
(LPTSTR)pMsgMem : "ASync InternetReadFileEx failed.";
|
|
XMLRPC_FAIL(envP, XMLRPC_NETWORK_ERROR, pMsg);
|
|
}
|
|
}
|
|
|
|
if (inetBuffer.dwBufferLength) {
|
|
TCHAR * const oldBufptr = inetBuffer.lpvBuffer;
|
|
|
|
inetBuffer.lpvBuffer = oldBufptr + inetBuffer.dwBufferLength;
|
|
nExpected -= inetBuffer.dwBufferLength;
|
|
/* Adjust inetBuffer.dwBufferLength when it is greater than the */
|
|
/* expected end of file */
|
|
if (inetBuffer.dwBufferLength > nExpected)
|
|
inetBuffer.dwBufferLength = nExpected;
|
|
|
|
} else
|
|
inetBuffer.dwBufferLength = nExpected;
|
|
dwErr = 0;
|
|
} while (nExpected != 0);
|
|
|
|
/* Add to the response buffer. */
|
|
xmlrpc_mem_block_append(envP, winInetTransactionP->pResponseData, body,
|
|
inetBuffer.dwBufferTotal);
|
|
XMLRPC_FAIL_IF_FAULT(envP);
|
|
|
|
cleanup:
|
|
/* Since the XMLRPC_FAIL calls goto cleanup, we must handle */
|
|
/* the free'ing of the memory here. */
|
|
if (pMsgMem != NULL)
|
|
LocalFree(pMsgMem);
|
|
|
|
if (body)
|
|
free(body);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
performWinInetTransaction(
|
|
xmlrpc_env * const envP,
|
|
winInetTransaction * const winInetTransactionP,
|
|
struct xmlrpc_client_transport * const clientTransportP) {
|
|
|
|
const char * const acceptTypes[] = {"text/xml", NULL};
|
|
|
|
unsigned long queryLen;
|
|
LPVOID pMsgMem;
|
|
BOOL succeeded;
|
|
|
|
unsigned long lastErr;
|
|
unsigned long reqFlags;
|
|
|
|
pMsgMem = NULL; /* initial value */
|
|
|
|
reqFlags = INTERNET_FLAG_NO_UI; /* initial value */
|
|
|
|
winInetTransactionP->hURL =
|
|
InternetConnect(hSyncInternetSession,
|
|
winInetTransactionP->szHostName,
|
|
winInetTransactionP->nPort,
|
|
NULL, NULL, INTERNET_SERVICE_HTTP, 0, 1);
|
|
|
|
/* Start our request running. */
|
|
if (winInetTransactionP->bUseSSL == TRUE)
|
|
reqFlags |=
|
|
INTERNET_FLAG_SECURE | INTERNET_FLAG_IGNORE_CERT_CN_INVALID;
|
|
|
|
winInetTransactionP->hHttpRequest =
|
|
HttpOpenRequest(winInetTransactionP->hURL, "POST",
|
|
winInetTransactionP->szUrlPath, "HTTP/1.1", NULL,
|
|
(const char **)&acceptTypes,
|
|
reqFlags, 1);
|
|
|
|
XMLRPC_FAIL_IF_NULL(winInetTransactionP->hHttpRequest, envP,
|
|
XMLRPC_INTERNAL_ERROR,
|
|
"Unable to open the requested URL.");
|
|
|
|
succeeded =
|
|
HttpAddRequestHeaders(winInetTransactionP->hHttpRequest,
|
|
winInetTransactionP->headerList,
|
|
strlen (winInetTransactionP->headerList),
|
|
HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE);
|
|
|
|
if (!succeeded)
|
|
XMLRPC_FAIL(envP, XMLRPC_INTERNAL_ERROR,
|
|
"Could not set Content-Type.");
|
|
|
|
{
|
|
/* By default, a request times out after 30 seconds. We don't want
|
|
it to timeout at all, since we don't know what the user is doing.
|
|
*/
|
|
DWORD dwTimeOut = 0x7FFFFFFF; /* Approximation of infinity */
|
|
InternetSetOption(winInetTransactionP->hHttpRequest,
|
|
INTERNET_OPTION_RECEIVE_TIMEOUT,
|
|
&dwTimeOut, sizeof(dwTimeOut));
|
|
}
|
|
Again:
|
|
/* Send the requested XML remote procedure command */
|
|
succeeded = HttpSendRequest(winInetTransactionP->hHttpRequest, NULL, 0,
|
|
winInetTransactionP->pSendData,
|
|
strlen(winInetTransactionP->pSendData));
|
|
if (!succeeded) {
|
|
LPTSTR pMsg;
|
|
|
|
lastErr = GetLastError();
|
|
|
|
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
NULL,
|
|
lastErr,
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
(LPTSTR) &pMsgMem,
|
|
0, NULL);
|
|
|
|
if (pMsgMem == NULL) {
|
|
switch (lastErr) {
|
|
case ERROR_INTERNET_CANNOT_CONNECT:
|
|
pMsg = "Sync HttpSendRequest failed: Connection refused.";
|
|
break;
|
|
case ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED:
|
|
pMsg = "Sync HttpSendRequest failed: "
|
|
"Client authorization certificate needed.";
|
|
break;
|
|
|
|
/* The following conditions are recommendations that microsoft */
|
|
/* provides in their knowledge base. */
|
|
|
|
/* HOWTO: Handle Invalid Certificate Authority Error with
|
|
WinInet (Q182888)
|
|
*/
|
|
case ERROR_INTERNET_INVALID_CA:
|
|
if (clientTransportP->allowInvalidSSLCerts){
|
|
OutputDebugString(
|
|
"Sync HttpSendRequest failed: "
|
|
"The function is unfamiliar with the certificate "
|
|
"authority that generated the server's certificate. ");
|
|
reqFlags = SECURITY_FLAG_IGNORE_UNKNOWN_CA;
|
|
|
|
InternetSetOption(winInetTransactionP->hHttpRequest,
|
|
INTERNET_OPTION_SECURITY_FLAGS,
|
|
&reqFlags, sizeof(reqFlags));
|
|
|
|
goto Again;
|
|
} else
|
|
pMsg = "Invalid or unknown/untrusted "
|
|
"SSL Certificate Authority.";
|
|
break;
|
|
|
|
/* HOWTO: Make SSL Requests Using WinInet (Q168151) */
|
|
case ERROR_INTERNET_SEC_CERT_CN_INVALID:
|
|
if (clientTransportP->allowInvalidSSLCerts) {
|
|
OutputDebugString(
|
|
"Sync HttpSendRequest failed: "
|
|
"The SSL certificate common name (host name field) "
|
|
"is incorrect\r\n "
|
|
"for example, if you entered www.server.com "
|
|
"and the common name "
|
|
"on the certificate says www.different.com. ");
|
|
|
|
reqFlags = INTERNET_FLAG_IGNORE_CERT_CN_INVALID;
|
|
|
|
InternetSetOption(winInetTransactionP->hHttpRequest,
|
|
INTERNET_OPTION_SECURITY_FLAGS,
|
|
&reqFlags, sizeof(reqFlags));
|
|
|
|
goto Again;
|
|
} else
|
|
pMsg = "The SSL certificate common name "
|
|
"(host name field) is incorrect.";
|
|
break;
|
|
|
|
case ERROR_INTERNET_SEC_CERT_DATE_INVALID:
|
|
if (clientTransportP->allowInvalidSSLCerts) {
|
|
OutputDebugString(
|
|
"Sync HttpSendRequest failed: "
|
|
"The SSL certificate date that was received "
|
|
"from the server is "
|
|
"bad. The certificate is expired. ");
|
|
|
|
reqFlags = INTERNET_FLAG_IGNORE_CERT_DATE_INVALID;
|
|
|
|
InternetSetOption(winInetTransactionP->hHttpRequest,
|
|
INTERNET_OPTION_SECURITY_FLAGS,
|
|
&reqFlags, sizeof(reqFlags));
|
|
|
|
goto Again;
|
|
} else
|
|
pMsg = "The SSL certificate date that was received "
|
|
"from the server is invalid.";
|
|
break;
|
|
|
|
default:
|
|
pMsg = (LPTSTR)pMsgMem = LocalAlloc(LPTR, MAX_PATH);
|
|
sprintf(pMsg, "Sync HttpSendRequest failed: "
|
|
"GetLastError (%d)", lastErr);
|
|
break;
|
|
|
|
}
|
|
} else
|
|
pMsg = (LPTSTR)pMsgMem;
|
|
|
|
XMLRPC_FAIL(envP, XMLRPC_NETWORK_ERROR, pMsg);
|
|
}
|
|
|
|
queryLen = sizeof(unsigned long); /* initial value */
|
|
|
|
succeeded = HttpQueryInfo(winInetTransactionP->hHttpRequest,
|
|
HTTP_QUERY_FLAG_NUMBER | HTTP_QUERY_STATUS_CODE,
|
|
&winInetTransactionP->http_status,
|
|
&queryLen, NULL);
|
|
if (!succeeded) {
|
|
LPTSTR pMsg;
|
|
|
|
lastErr = GetLastError();
|
|
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_FROM_SYSTEM,
|
|
NULL,
|
|
lastErr,
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
(LPTSTR) &pMsgMem,
|
|
1024, NULL);
|
|
|
|
pMsg = pMsgMem ? (LPTSTR)pMsgMem : "Sync HttpQueryInfo failed.";
|
|
XMLRPC_FAIL(envP, XMLRPC_NETWORK_ERROR, pMsg);
|
|
}
|
|
|
|
/* Make sure we got a "200 OK" message from the remote server. */
|
|
if (winInetTransactionP->http_status != 200) {
|
|
unsigned long msgLen;
|
|
char errMsg[1024];
|
|
errMsg[0] = '\0';
|
|
msgLen = 1024; /* initial value */
|
|
|
|
HttpQueryInfo(winInetTransactionP->hHttpRequest,
|
|
HTTP_QUERY_STATUS_TEXT, errMsg, &msgLen, NULL);
|
|
|
|
xmlrpc_env_set_fault_formatted(
|
|
envP, XMLRPC_NETWORK_ERROR,
|
|
"HTTP error #%d occurred\n %s",
|
|
winInetTransactionP->http_status, errMsg);
|
|
goto cleanup;
|
|
}
|
|
/* Read the response. */
|
|
get_wininet_response(envP, winInetTransactionP);
|
|
XMLRPC_FAIL_IF_FAULT(envP);
|
|
|
|
cleanup:
|
|
/* Since the XMLRPC_FAIL calls goto cleanup, we must handle */
|
|
/* the free'ing of the memory here. */
|
|
if (pMsgMem)
|
|
LocalFree(pMsgMem);
|
|
}
|
|
|
|
|
|
|
|
static void *
|
|
doAsyncRpc(void * const arg) {
|
|
|
|
rpc * const rpcP = arg;
|
|
|
|
xmlrpc_env env;
|
|
xmlrpc_env_init(&env);
|
|
|
|
performWinInetTransaction(&env, rpcP->winInetTransactionP,
|
|
rpcP->clientTransportP );
|
|
|
|
rpcP->complete(rpcP->callInfoP, rpcP->responseXmlP, env);
|
|
|
|
xmlrpc_env_clean(&env);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
createRpcThread(xmlrpc_env * const envP,
|
|
rpc * const rpcP,
|
|
pthread_t * const threadP) {
|
|
|
|
int rc;
|
|
|
|
rc = pthread_create(threadP, NULL, doAsyncRpc, rpcP);
|
|
switch (rc) {
|
|
case 0:
|
|
break;
|
|
case EAGAIN:
|
|
xmlrpc_faultf(envP, "pthread_create() failed: "
|
|
"System Resources exceeded.");
|
|
break;
|
|
case EINVAL:
|
|
xmlrpc_faultf(envP, "pthread_create() failed: "
|
|
"Param Error for attr.");
|
|
break;
|
|
case ENOMEM:
|
|
xmlrpc_faultf(envP, "pthread_create() failed: "
|
|
"No memory for new thread.");
|
|
break;
|
|
default:
|
|
xmlrpc_faultf(envP, "pthread_create() failed: "
|
|
"Unrecognized error code %d.", rc);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
rpcCreate(xmlrpc_env * const envP,
|
|
struct xmlrpc_client_transport * const clientTransportP,
|
|
const xmlrpc_server_info * const serverP,
|
|
xmlrpc_mem_block * const callXmlP,
|
|
xmlrpc_mem_block * const responseXmlP,
|
|
xmlrpc_transport_asynch_complete complete,
|
|
struct xmlrpc_call_info * const callInfoP,
|
|
rpc ** const rpcPP) {
|
|
|
|
rpc * rpcP;
|
|
|
|
MALLOCVAR(rpcP);
|
|
if (rpcP == NULL)
|
|
xmlrpc_faultf(envP, "Couldn't allocate memory for rpc object");
|
|
else {
|
|
rpcP->callInfoP = callInfoP;
|
|
rpcP->complete = complete;
|
|
rpcP->responseXmlP = responseXmlP;
|
|
rpcP->threadExists = FALSE;
|
|
|
|
createWinInetTransaction(envP, serverP, callXmlP, responseXmlP,
|
|
&rpcP->winInetTransactionP);
|
|
if (!envP->fault_occurred) {
|
|
if (complete) {
|
|
createRpcThread(envP, rpcP, &rpcP->thread);
|
|
if (!envP->fault_occurred)
|
|
rpcP->threadExists = TRUE;
|
|
}
|
|
if (!envP->fault_occurred) {
|
|
list_init_header(&rpcP->link, rpcP);
|
|
pthread_mutex_lock(&clientTransportP->listLock);
|
|
list_add_head(&clientTransportP->rpcList, &rpcP->link);
|
|
pthread_mutex_unlock(&clientTransportP->listLock);
|
|
}
|
|
if (envP->fault_occurred)
|
|
destroyWinInetTransaction(rpcP->winInetTransactionP);
|
|
}
|
|
if (envP->fault_occurred)
|
|
free(rpcP);
|
|
}
|
|
*rpcPP = rpcP;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
rpcDestroy(rpc * const rpcP) {
|
|
|
|
XMLRPC_ASSERT_PTR_OK(rpcP);
|
|
XMLRPC_ASSERT(!rpcP->threadExists);
|
|
|
|
destroyWinInetTransaction(rpcP->winInetTransactionP);
|
|
|
|
list_remove(&rpcP->link);
|
|
|
|
free(rpcP);
|
|
}
|
|
|
|
|
|
|
|
static void *
|
|
finishRpc(struct list_head * const headerP,
|
|
void * const context ATTR_UNUSED) {
|
|
|
|
rpc * const rpcP = headerP->itemP;
|
|
|
|
if (rpcP->threadExists) {
|
|
void * status;
|
|
int result;
|
|
|
|
result = pthread_join(rpcP->thread, &status);
|
|
|
|
rpcP->threadExists = FALSE;
|
|
}
|
|
|
|
XMLRPC_MEMBLOCK_FREE(char, rpcP->responseXmlP);
|
|
|
|
rpcDestroy(rpcP);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* Used for debugging purposes to track the status of
|
|
your request.
|
|
*/
|
|
void CALLBACK
|
|
statusCallback (HINTERNET const hInternet,
|
|
unsigned long const dwContext,
|
|
unsigned long const dwInternetStatus,
|
|
void * const lpvStatusInformation,
|
|
unsigned long const dwStatusInformationLength) {
|
|
|
|
switch (dwInternetStatus) {
|
|
case INTERNET_STATUS_RESOLVING_NAME:
|
|
OutputDebugString("INTERNET_STATUS_RESOLVING_NAME\r\n");
|
|
break;
|
|
|
|
case INTERNET_STATUS_NAME_RESOLVED:
|
|
OutputDebugString("INTERNET_STATUS_NAME_RESOLVED\r\n");
|
|
break;
|
|
|
|
case INTERNET_STATUS_HANDLE_CREATED:
|
|
OutputDebugString("INTERNET_STATUS_HANDLE_CREATED\r\n");
|
|
break;
|
|
|
|
case INTERNET_STATUS_CONNECTING_TO_SERVER:
|
|
OutputDebugString("INTERNET_STATUS_CONNECTING_TO_SERVER\r\n");
|
|
break;
|
|
|
|
case INTERNET_STATUS_REQUEST_SENT:
|
|
OutputDebugString("INTERNET_STATUS_REQUEST_SENT\r\n");
|
|
break;
|
|
|
|
case INTERNET_STATUS_SENDING_REQUEST:
|
|
OutputDebugString("INTERNET_STATUS_SENDING_REQUEST\r\n");
|
|
break;
|
|
|
|
case INTERNET_STATUS_CONNECTED_TO_SERVER:
|
|
OutputDebugString("INTERNET_STATUS_CONNECTED_TO_SERVER\r\n");
|
|
break;
|
|
|
|
case INTERNET_STATUS_RECEIVING_RESPONSE:
|
|
OutputDebugString("INTERNET_STATUS_RECEIVING_RESPONSE\r\n");
|
|
break;
|
|
|
|
case INTERNET_STATUS_RESPONSE_RECEIVED:
|
|
OutputDebugString("INTERNET_STATUS_RESPONSE_RECEIVED\r\n");
|
|
break;
|
|
|
|
case INTERNET_STATUS_CLOSING_CONNECTION:
|
|
OutputDebugString("INTERNET_STATUS_CLOSING_CONNECTION\r\n");
|
|
break;
|
|
|
|
case INTERNET_STATUS_CONNECTION_CLOSED:
|
|
OutputDebugString("INTERNET_STATUS_CONNECTION_CLOSED\r\n");
|
|
break;
|
|
|
|
case INTERNET_STATUS_HANDLE_CLOSING:
|
|
OutputDebugString("INTERNET_STATUS_HANDLE_CLOSING\r\n");
|
|
break;
|
|
|
|
case INTERNET_STATUS_CTL_RESPONSE_RECEIVED:
|
|
OutputDebugString("INTERNET_STATUS_CTL_RESPONSE_RECEIVED\r\n");
|
|
break;
|
|
|
|
case INTERNET_STATUS_REDIRECT:
|
|
OutputDebugString("INTERNET_STATUS_REDIRECT\r\n");
|
|
break;
|
|
|
|
case INTERNET_STATUS_REQUEST_COMPLETE:
|
|
/* This indicates the data is ready. */
|
|
OutputDebugString("INTERNET_STATUS_REQUEST_COMPLETE\r\n");
|
|
break;
|
|
|
|
default:
|
|
OutputDebugString("statusCallback, default case!\r\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
create(xmlrpc_env * const envP,
|
|
int const flags ATTR_UNUSED,
|
|
const char * const appname ATTR_UNUSED,
|
|
const char * const appversion ATTR_UNUSED,
|
|
const void * const transportparmsP,
|
|
size_t const parm_size,
|
|
struct xmlrpc_client_transport ** const handlePP) {
|
|
/*----------------------------------------------------------------------------
|
|
This does the 'create' operation for a WinInet client transport.
|
|
-----------------------------------------------------------------------------*/
|
|
const struct xmlrpc_wininet_xportparms * const wininetXportParmsP =
|
|
transportparmsP;
|
|
|
|
struct xmlrpc_client_transport * transportP;
|
|
|
|
MALLOCVAR(transportP);
|
|
if (transportP == NULL)
|
|
xmlrpc_faultf(envP, "Unable to allocate transport descriptor.");
|
|
else {
|
|
pthread_mutex_init(&transportP->listLock, NULL);
|
|
|
|
list_make_empty(&transportP->rpcList);
|
|
|
|
if (hSyncInternetSession == NULL)
|
|
hSyncInternetSession =
|
|
InternetOpen("xmlrpc-c wininet transport",
|
|
INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
|
|
|
|
if (!wininetXportParmsP ||
|
|
parm_size < XMLRPC_WXPSIZE(allowInvalidSSLCerts))
|
|
transportP->allowInvalidSSLCerts = 0;
|
|
else
|
|
transportP->allowInvalidSSLCerts =
|
|
wininetXportParmsP->allowInvalidSSLCerts;
|
|
|
|
*handlePP = transportP;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
destroy(struct xmlrpc_client_transport * const clientTransportP) {
|
|
/*----------------------------------------------------------------------------
|
|
This does the 'destroy' operation for a WinInet client transport.
|
|
-----------------------------------------------------------------------------*/
|
|
XMLRPC_ASSERT(clientTransportP != NULL);
|
|
|
|
XMLRPC_ASSERT(list_is_empty(&clientTransportP->rpcList));
|
|
|
|
if (hSyncInternetSession)
|
|
InternetCloseHandle(hSyncInternetSession);
|
|
hSyncInternetSession = NULL;
|
|
|
|
pthread_mutex_destroy(&clientTransportP->listLock);
|
|
|
|
free(clientTransportP);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
sendRequest(xmlrpc_env * const envP,
|
|
struct xmlrpc_client_transport * const clientTransportP,
|
|
const xmlrpc_server_info * const serverP,
|
|
xmlrpc_mem_block * const callXmlP,
|
|
xmlrpc_transport_asynch_complete complete,
|
|
xmlrpc_transport_progress progress,
|
|
struct xmlrpc_call_info * const callInfoP) {
|
|
/*----------------------------------------------------------------------------
|
|
Initiate an XML-RPC rpc asynchronously. Don't wait for it to go to
|
|
the server.
|
|
|
|
Unless we return failure, we arrange to have complete() called when
|
|
the rpc completes.
|
|
|
|
This does the 'send_request' operation for a WinInet client transport.
|
|
-----------------------------------------------------------------------------*/
|
|
rpc * rpcP;
|
|
xmlrpc_mem_block * responseXmlP;
|
|
|
|
responseXmlP = XMLRPC_MEMBLOCK_NEW(char, envP, 0);
|
|
if (!envP->fault_occurred) {
|
|
rpcCreate(envP, clientTransportP, serverP, callXmlP, responseXmlP,
|
|
complete, callInfoP,
|
|
&rpcP);
|
|
|
|
if (envP->fault_occurred)
|
|
XMLRPC_MEMBLOCK_FREE(char, responseXmlP);
|
|
}
|
|
/* The user's eventual finish_asynch call will destroy this RPC
|
|
and response buffer
|
|
*/
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
finishAsynch(struct xmlrpc_client_transport * const clientTransportP,
|
|
xmlrpc_timeoutType const timeoutType ATTR_UNUSED,
|
|
xmlrpc_timeout const timeout ATTR_UNUSED) {
|
|
/*----------------------------------------------------------------------------
|
|
Wait for the threads of all outstanding RPCs to exit and destroy those
|
|
RPCs.
|
|
|
|
This does the 'finish_asynch' operation for a WinInet client transport.
|
|
-----------------------------------------------------------------------------*/
|
|
/* We ignore any timeout request. Some day, we should figure out how
|
|
to set an alarm and interrupt running threads.
|
|
*/
|
|
|
|
pthread_mutex_lock(&clientTransportP->listLock);
|
|
|
|
list_foreach(&clientTransportP->rpcList, finishRpc, NULL);
|
|
|
|
pthread_mutex_unlock(&clientTransportP->listLock);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
call(xmlrpc_env * const envP,
|
|
struct xmlrpc_client_transport * const clientTransportP,
|
|
const xmlrpc_server_info * const serverP,
|
|
xmlrpc_mem_block * const callXmlP,
|
|
xmlrpc_mem_block ** const responsePP) {
|
|
|
|
xmlrpc_mem_block * responseXmlP;
|
|
rpc * rpcP;
|
|
|
|
XMLRPC_ASSERT_ENV_OK(envP);
|
|
XMLRPC_ASSERT_PTR_OK(serverP);
|
|
XMLRPC_ASSERT_PTR_OK(callXmlP);
|
|
XMLRPC_ASSERT_PTR_OK(responsePP);
|
|
|
|
responseXmlP = XMLRPC_MEMBLOCK_NEW(char, envP, 0);
|
|
if (!envP->fault_occurred) {
|
|
rpcCreate(envP, clientTransportP, serverP, callXmlP, responseXmlP,
|
|
NULL, NULL, &rpcP);
|
|
if (!envP->fault_occurred) {
|
|
performWinInetTransaction(envP, rpcP->winInetTransactionP,
|
|
clientTransportP);
|
|
|
|
*responsePP = responseXmlP;
|
|
|
|
rpcDestroy(rpcP);
|
|
}
|
|
if (envP->fault_occurred)
|
|
XMLRPC_MEMBLOCK_FREE(char, responseXmlP);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
struct xmlrpc_client_transport_ops xmlrpc_wininet_transport_ops = {
|
|
NULL,
|
|
NULL,
|
|
&create,
|
|
&destroy,
|
|
&sendRequest,
|
|
&call,
|
|
&finishAsynch,
|
|
NULL,
|
|
};
|
|
|
|
|
|
|
|
/* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
|
|
**
|
|
** Redistribution and use in source and binary forms, with or without
|
|
** modification, are permitted provided that the following conditions
|
|
** are met:
|
|
** 1. Redistributions of source code must retain the above copyright
|
|
** notice, this list of conditions and the following disclaimer.
|
|
** 2. Redistributions in binary form must reproduce the above copyright
|
|
** notice, this list of conditions and the following disclaimer in the
|
|
** documentation and/or other materials provided with the distribution.
|
|
** 3. The name of the author may not be used to endorse or promote products
|
|
** derived from this software without specific prior written permission.
|
|
**
|
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
** SUCH DAMAGE. */
|