--- /dev/null
+/*=============================================================================
+ xmlrpc_wininet_transport
+===============================================================================
+ WinInet-based client transport for Xmlrpc-c. Copyright information at
+ the bottom of this file.
+
+ Changelog (changes by Steven A. Bone - sbone@pobox.com unless otherwise noted):
+ 05.01.01 - Significant refactoring of the transport layer due to internal
+ changes of the xmlrpc-c transports. Modeled after the CURL
+ based transport changes by Bryan Henderson.
+ 05.02.03 - Fixed Authorization header - thanks yamer.
+ 05.03.20 - Supports xmlrpc_xportparms, xmlrpc_wininet_xportparms added
+ *potential breaking change* - now by default we fail on invalid
+ SSL certs, use the xmlrpc_wininet_xportparms option to enable old
+ behavior.
+
+=============================================================================*/
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stddef.h>
+
+#include "xmlrpc_config.h"
+
+#include "bool.h"
+#include "mallocvar.h"
+#include "linklist.h"
+#include "casprintf.h"
+
+#include "xmlrpc-c/base.h"
+#include "xmlrpc-c/base_int.h"
+#include "xmlrpc-c/client.h"
+#include "xmlrpc-c/client_int.h"
+#include "pthreadx.h"
+
+#if defined (WIN32)
+# include <wininet.h>
+#endif /*WIN32*/
+
+#if defined (WIN32) && 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 /*WIN32 && _DEBUG*/
+
+
+static HINTERNET hSyncInternetSession = NULL;
+
+/* Declare WinInet status callback. */
+void CALLBACK statusCallback (HINTERNET hInternet,
+ unsigned long dwContext,
+ unsigned long dwInternetStatus,
+ void * lpvStatusInformation,
+ unsigned long 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) {
+
+ char *szHeaderList = NULL;
+ char *szContentType = "Content-Type: text/xml\r\n";
+
+ /* Send an authorization header if we need one. */
+ if (serverP->_http_basic_auth) {
+ /* Make the header with content type and authorization */
+ /* NOTE: A newline is required between each added header */
+ szHeaderList = malloc(strlen(szContentType) + 17 + strlen(serverP->_http_basic_auth) + 1 );
+
+ if (szHeaderList == NULL)
+ xmlrpc_env_set_fault_formatted(
+ envP, XMLRPC_INTERNAL_ERROR,
+ "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->_http_basic_auth,
+ strlen(serverP->_http_basic_auth) + 1);
+ }
+ }
+ else
+ {
+ /* Just the content type header is needed */
+ szHeaderList = malloc(strlen(szContentType) + 1);
+
+ if (szHeaderList == NULL)
+ xmlrpc_env_set_fault_formatted(
+ envP, XMLRPC_INTERNAL_ERROR, "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 winInetTransactionPP) {
+
+ winInetTransaction * winInetTransactionP;
+
+ MALLOCVAR(winInetTransactionP);
+ if (winInetTransactionP == NULL)
+ xmlrpc_env_set_fault_formatted(
+ envP, XMLRPC_INTERNAL_ERROR,
+ "No memory to create WinInet transaction.");
+ else {
+ char szExtraInfo[255];
+ char szScheme[100];
+ URL_COMPONENTS uc;
+
+ /* 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 struct */
+ 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;
+ if (InternetCrackUrl (serverP->_server_url, strlen (serverP->_server_url), ICU_ESCAPE, &uc) == FALSE)
+ {
+ xmlrpc_env_set_fault_formatted( envP, XMLRPC_INTERNAL_ERROR,
+ "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);
+ }
+ *winInetTransactionPP = 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)
+{
+ INTERNET_BUFFERS inetBuffer;
+ LPTSTR pMsg = NULL;
+ PVOID pMsgMem = NULL;
+ unsigned long dwFlags;
+ unsigned long dwErr = 0;
+ unsigned long nExpected = 0;
+ unsigned long dwLen = sizeof (unsigned long);
+ void * body = NULL;
+ BOOL bOK = FALSE;
+
+ inetBuffer.dwStructSize = sizeof (INTERNET_BUFFERS);
+ inetBuffer.Next = NULL;
+ inetBuffer.lpcszHeader = NULL;
+ inetBuffer.dwHeadersTotal = inetBuffer.dwHeadersLength = 0;
+ inetBuffer.dwOffsetHigh = inetBuffer.dwOffsetLow = 0;
+ inetBuffer.dwBufferLength = 0;
+
+ bOK = HttpQueryInfo (winInetTransactionP->hHttpRequest,
+ HTTP_QUERY_CONTENT_LENGTH|HTTP_QUERY_FLAG_NUMBER,
+ &inetBuffer.dwBufferTotal, &dwLen, NULL);
+ if (!bOK)
+ {
+ 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");
+
+ body = inetBuffer.lpvBuffer = calloc (inetBuffer.dwBufferTotal, sizeof (TCHAR));
+ dwFlags = IRF_SYNC;
+ inetBuffer.dwBufferLength = nExpected = inetBuffer.dwBufferTotal;
+ 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
+ {
+ 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 * bufptr = inetBuffer.lpvBuffer;
+ bufptr += inetBuffer.dwBufferLength;
+ inetBuffer.lpvBuffer = bufptr;
+ 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) {
+ LPTSTR pMsg = NULL;
+ LPVOID pMsgMem = NULL;
+
+ unsigned long lastErr;
+ unsigned long reqFlags = INTERNET_FLAG_NO_UI;
+ char * acceptTypes[] = {"text/xml", NULL};
+ unsigned long queryLen = sizeof (unsigned long);
+
+ 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.");
+
+ if ( HttpAddRequestHeaders (winInetTransactionP->hHttpRequest, winInetTransactionP->headerList,
+ strlen (winInetTransactionP->headerList), HTTP_ADDREQ_FLAG_ADD|HTTP_ADDREQ_FLAG_REPLACE ) ==FALSE)
+ {
+ XMLRPC_FAIL (envP, XMLRPC_INTERNAL_ERROR, "Could not set Content-Type.");
+ }
+
+#ifdef DEBUG
+ /* Provide the user with transport status information */
+ InternetSetStatusCallback (winInetTransactionP->hHttpRequest, statusCallback);
+#endif
+
+Again:
+ /* Send the requested XML remote procedure command */
+ if (HttpSendRequest (winInetTransactionP->hHttpRequest, NULL, 0,
+ winInetTransactionP->pSendData,
+ strlen(winInetTransactionP->pSendData))==FALSE)
+ {
+ 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);
+
+ }
+
+ if( HttpQueryInfo (winInetTransactionP->hHttpRequest,
+ HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
+ &winInetTransactionP->http_status, &queryLen, NULL) == FALSE)
+ {
+ 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 = 1024;
+ char errMsg [1024];
+ *errMsg = 0;
+ HttpQueryInfo (winInetTransactionP->hHttpRequest, HTTP_QUERY_STATUS_TEXT, errMsg, &msgLen, NULL);
+
+ /* Set our fault. We break this into multiple lines because it */
+ /* will generally contain line breaks to begin with. */
+ 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 != NULL)
+ {
+ LocalFree( pMsgMem );
+ }
+
+}
+
+static unsigned __stdcall
+doAsyncRpc(void * 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 0;
+}
+
+
+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_env_set_fault_formatted(
+ envP, XMLRPC_INTERNAL_ERROR,
+ "pthread_create() failed: System Resources exceeded.");
+ break;
+ case EINVAL:
+ xmlrpc_env_set_fault_formatted(
+ envP, XMLRPC_INTERNAL_ERROR,
+ "pthread_create() failed: Param Error for attr.");
+ break;
+ case ENOMEM:
+ xmlrpc_env_set_fault_formatted(
+ envP, XMLRPC_INTERNAL_ERROR,
+ "pthread_create() failed: No memory for new thread.");
+ break;
+ default:
+ xmlrpc_env_set_fault_formatted(
+ envP, XMLRPC_INTERNAL_ERROR,
+ "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_env_set_fault_formatted(
+ envP, XMLRPC_INTERNAL_ERROR,
+ "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 hInternet,
+ unsigned long dwContext,
+ unsigned long dwInternetStatus,
+ void * lpvStatusInformation,
+ unsigned long 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 struct xmlrpc_xportparms * const transportparmsP,
+ size_t const parm_size,
+ struct xmlrpc_client_transport ** const handlePP) {
+/*----------------------------------------------------------------------------
+ This does the 'create' operation for a WinInet client transport.
+-----------------------------------------------------------------------------*/
+ struct xmlrpc_client_transport * transportP;
+
+ struct xmlrpc_wininet_xportparms * const wininetXportParmsP =
+ (struct xmlrpc_wininet_xportparms *) transportparmsP;
+
+ MALLOCVAR(transportP);
+ if (transportP == NULL)
+ xmlrpc_env_set_fault_formatted(
+ envP, XMLRPC_INTERNAL_ERROR,
+ "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,
+ 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,
+};
+
+/* 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. */