initial load of upstream version 1.06.32
[xmlrpc-c] / lib / wininet_transport / xmlrpc_wininet_transport.c
diff --git a/lib/wininet_transport/xmlrpc_wininet_transport.c b/lib/wininet_transport/xmlrpc_wininet_transport.c
new file mode 100644 (file)
index 0000000..c583309
--- /dev/null
@@ -0,0 +1,919 @@
+/*=============================================================================
+                           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. */