initial load of upstream version 1.06.32
[xmlrpc-c] / lib / wininet_transport / xmlrpc_wininet_transport.c
1 /*=============================================================================
2                            xmlrpc_wininet_transport
3 ===============================================================================
4    WinInet-based client transport for Xmlrpc-c.  Copyright information at
5    the bottom of this file.
6    
7    Changelog (changes by Steven A. Bone - sbone@pobox.com unless otherwise noted):
8    05.01.01 - Significant refactoring of the transport layer due to internal
9               changes of the xmlrpc-c transports.  Modeled after the CURL
10               based transport changes by Bryan Henderson.              
11    05.02.03 - Fixed Authorization header - thanks yamer.
12    05.03.20 - Supports xmlrpc_xportparms, xmlrpc_wininet_xportparms added
13               *potential breaking change* - now by default we fail on invalid
14                           SSL certs, use the xmlrpc_wininet_xportparms option to enable old
15                           behavior.
16    
17 =============================================================================*/
18
19 #include <string.h>
20 #include <stdlib.h>
21 #include <errno.h>
22 #include <stddef.h>
23
24 #include "xmlrpc_config.h"
25
26 #include "bool.h"
27 #include "mallocvar.h"
28 #include "linklist.h"
29 #include "casprintf.h"
30
31 #include "xmlrpc-c/base.h"
32 #include "xmlrpc-c/base_int.h"
33 #include "xmlrpc-c/client.h"
34 #include "xmlrpc-c/client_int.h"
35 #include "pthreadx.h"
36
37 #if defined (WIN32)
38 #       include <wininet.h>
39 #endif /*WIN32*/
40
41 #if defined (WIN32) && defined(_DEBUG)
42 #               include <crtdbg.h>
43 #               define new DEBUG_NEW
44 #               define malloc(size) _malloc_dbg( size, _NORMAL_BLOCK, __FILE__, __LINE__)
45 #               undef THIS_FILE
46                 static char THIS_FILE[] = __FILE__;
47 #endif /*WIN32 && _DEBUG*/
48
49
50 static HINTERNET hSyncInternetSession = NULL;
51
52 /* Declare WinInet status callback. */
53 void CALLBACK statusCallback (HINTERNET hInternet,
54                           unsigned long dwContext,
55                           unsigned long dwInternetStatus,
56                           void * lpvStatusInformation,
57                           unsigned long dwStatusInformationLength);
58
59
60 struct xmlrpc_client_transport {
61     pthread_mutex_t listLock;
62     struct list_head rpcList;
63         /* List of all RPCs that exist for this transport.  An RPC exists
64            from the time the user requests it until the time the user 
65            acknowledges it is done.
66         */
67     int allowInvalidSSLCerts;
68         /* Flag to specify if we ignore invalid SSL Certificates.  If this
69                    is set to zero, calling a XMLRPC server with an invalid SSL
70                    certificate will fail.  This is the default behavior of the other
71                    transports, but invalid certificates were allowed in pre 1.2 
72                    wininet xmlrpc-c transports.
73         */
74 };
75
76 typedef struct {
77     unsigned long http_status;
78         HINTERNET hHttpRequest;
79         HINTERNET hURL;
80         INTERNET_PORT nPort;
81         char szHostName[255];   
82         char szUrlPath[255];
83         BOOL bUseSSL;
84         char *headerList;
85         BYTE *pSendData;
86         xmlrpc_mem_block *pResponseData;
87 } winInetTransaction;
88
89 typedef struct {
90     struct list_head link;  /* link in transport's list of RPCs */
91     winInetTransaction * winInetTransactionP;
92         /* The object which does the HTTP transaction, with no knowledge
93            of XML-RPC or Xmlrpc-c.
94         */
95     xmlrpc_mem_block * responseXmlP;
96     xmlrpc_bool threadExists;
97     pthread_t thread;
98     xmlrpc_transport_asynch_complete complete;
99         /* Routine to call to complete the RPC after it is complete HTTP-wise.
100            NULL if none.
101         */
102     struct xmlrpc_call_info * callInfoP;
103         /* User's identifier for this RPC */
104         struct xmlrpc_client_transport * clientTransportP;
105 } rpc;
106
107 static void
108 createWinInetHeaderList( xmlrpc_env *         const envP,
109                                                  const xmlrpc_server_info * const serverP,
110                                                  char ** const headerListP) {
111
112     char *szHeaderList = NULL;
113         char *szContentType = "Content-Type: text/xml\r\n";
114
115     /* Send an authorization header if we need one. */
116     if (serverP->_http_basic_auth) {
117         /* Make the header with content type and authorization   */
118         /* NOTE: A newline is required between each added header */
119                 szHeaderList = malloc(strlen(szContentType) + 17 + strlen(serverP->_http_basic_auth) + 1 );
120         
121         if (szHeaderList == NULL)
122             xmlrpc_env_set_fault_formatted(
123                 envP, XMLRPC_INTERNAL_ERROR,
124                 "Couldn't allocate memory for authorization header");
125         else {
126             memcpy(szHeaderList,szContentType, strlen(szContentType));
127                         memcpy(szHeaderList + strlen(szContentType),"\r\nAuthorization: ", 17);
128                         memcpy(szHeaderList + strlen(szContentType) + 17, serverP->_http_basic_auth,
129                    strlen(serverP->_http_basic_auth) + 1);
130         }
131     }
132         else
133         {
134                 /* Just the content type header is needed */
135                 szHeaderList = malloc(strlen(szContentType) + 1);
136         
137         if (szHeaderList == NULL)
138             xmlrpc_env_set_fault_formatted(
139                 envP, XMLRPC_INTERNAL_ERROR, "Couldn't allocate memory for standard header");
140         else 
141             memcpy(szHeaderList,szContentType, strlen(szContentType)+1);
142         }
143
144     *headerListP = szHeaderList;
145 }
146
147 static void
148 createWinInetTransaction(xmlrpc_env *         const envP,
149                       const xmlrpc_server_info * const serverP,
150                       xmlrpc_mem_block *   const callXmlP,
151                       xmlrpc_mem_block *   const responseXmlP,
152                       winInetTransaction **   const winInetTransactionPP) {
153
154     winInetTransaction * winInetTransactionP;
155
156     MALLOCVAR(winInetTransactionP);
157     if (winInetTransactionP == NULL)
158         xmlrpc_env_set_fault_formatted(
159             envP, XMLRPC_INTERNAL_ERROR,
160             "No memory to create WinInet transaction.");
161     else {
162                 char szExtraInfo[255];
163                 char szScheme[100];
164                 URL_COMPONENTS uc;
165
166                 /* Init to defaults */
167                 winInetTransactionP->http_status = 0;
168                 winInetTransactionP->hHttpRequest = NULL;
169                 winInetTransactionP->hURL = NULL;
170                 winInetTransactionP->headerList=NULL;
171                 winInetTransactionP->pSendData=NULL;
172                 winInetTransactionP->pResponseData=responseXmlP;
173
174                 /* Parse the URL and store results into the  winInetTransaction struct */
175                 memset (&uc, 0, sizeof (uc));
176                 uc.dwStructSize = sizeof (uc);
177                 uc.lpszScheme = szScheme;
178                 uc.dwSchemeLength = 100;
179                 uc.lpszHostName = winInetTransactionP->szHostName;
180                 uc.dwHostNameLength = 255;
181                 uc.lpszUrlPath = winInetTransactionP->szUrlPath;
182                 uc.dwUrlPathLength = 255;
183                 uc.lpszExtraInfo = szExtraInfo;
184                 uc.dwExtraInfoLength = 255;
185                 if (InternetCrackUrl (serverP->_server_url, strlen (serverP->_server_url), ICU_ESCAPE, &uc) == FALSE)
186                 {
187                         xmlrpc_env_set_fault_formatted( envP, XMLRPC_INTERNAL_ERROR,
188                                                                                         "Unable to parse the server URL.");
189                 }
190                 else
191                 {
192                         winInetTransactionP->nPort = (uc.nPort) ? uc.nPort : INTERNET_DEFAULT_HTTP_PORT;
193                         if (_strnicmp (uc.lpszScheme, "https", 5) == 0)
194                                 winInetTransactionP->bUseSSL=TRUE;
195                         else
196                                 winInetTransactionP->bUseSSL=FALSE;
197                         createWinInetHeaderList(envP, serverP, &winInetTransactionP->headerList);
198
199                         XMLRPC_MEMBLOCK_APPEND(char, envP, callXmlP, "\0", 1);
200                         if (!envP->fault_occurred) {
201                                 winInetTransactionP->pSendData = XMLRPC_MEMBLOCK_CONTENTS(char, callXmlP);
202                         }
203                 }
204                         
205                 
206                 if (envP->fault_occurred)
207             free(winInetTransactionP);
208     }
209     *winInetTransactionPP = winInetTransactionP;
210 }
211
212
213
214 static void
215 destroyWinInetTransaction(winInetTransaction * const winInetTransactionP) {
216
217         XMLRPC_ASSERT_PTR_OK(winInetTransactionP);
218
219         if (winInetTransactionP->hHttpRequest)
220                 InternetCloseHandle (winInetTransactionP->hHttpRequest);
221
222         if (winInetTransactionP->hURL)
223                 InternetCloseHandle (winInetTransactionP->hURL);
224
225         if (winInetTransactionP->headerList)
226                 free(winInetTransactionP->headerList);
227
228         free(winInetTransactionP);
229 }
230
231 static void get_wininet_response (      xmlrpc_env *      const envP,
232                                                                         winInetTransaction * const winInetTransactionP)
233 {
234         INTERNET_BUFFERS inetBuffer;
235         LPTSTR pMsg = NULL;
236         PVOID pMsgMem = NULL;
237         unsigned long dwFlags;
238         unsigned long dwErr = 0; 
239         unsigned long nExpected = 0;
240         unsigned long dwLen = sizeof (unsigned long);
241         void * body = NULL;
242         BOOL bOK = FALSE;
243
244         inetBuffer.dwStructSize = sizeof (INTERNET_BUFFERS);
245         inetBuffer.Next = NULL;
246         inetBuffer.lpcszHeader = NULL;
247         inetBuffer.dwHeadersTotal = inetBuffer.dwHeadersLength = 0;
248         inetBuffer.dwOffsetHigh = inetBuffer.dwOffsetLow = 0;
249         inetBuffer.dwBufferLength = 0;
250
251         bOK = HttpQueryInfo (winInetTransactionP->hHttpRequest, 
252                 HTTP_QUERY_CONTENT_LENGTH|HTTP_QUERY_FLAG_NUMBER, 
253                 &inetBuffer.dwBufferTotal, &dwLen, NULL);
254         if (!bOK)
255         {
256                 dwErr = GetLastError ();
257                 FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | 
258                         FORMAT_MESSAGE_FROM_SYSTEM, 
259                         NULL,
260                         dwErr,
261                         MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
262                         (LPTSTR) &pMsgMem,
263                         1024,NULL);
264
265                 pMsg = (pMsgMem) ? (LPTSTR)(pMsgMem) : "Sync HttpQueryInfo failed.";
266                 XMLRPC_FAIL (envP, XMLRPC_NETWORK_ERROR, pMsg);
267         }
268
269     if (inetBuffer.dwBufferTotal == 0)
270                 XMLRPC_FAIL (envP, XMLRPC_NETWORK_ERROR, "WinInet returned no data");
271
272         body = inetBuffer.lpvBuffer = calloc (inetBuffer.dwBufferTotal, sizeof (TCHAR));
273         dwFlags = IRF_SYNC;
274         inetBuffer.dwBufferLength = nExpected = inetBuffer.dwBufferTotal;
275         InternetQueryDataAvailable (winInetTransactionP->hHttpRequest, &inetBuffer.dwBufferLength, 0, 0);
276
277         /* Read Response from InternetFile */
278         do
279         {
280                 if (inetBuffer.dwBufferLength != 0)
281                         bOK = InternetReadFileEx (winInetTransactionP->hHttpRequest, &inetBuffer, dwFlags, 1);
282
283                 if (!bOK)
284                         dwErr = GetLastError ();
285
286                 if (dwErr)
287                 {
288                         if (dwErr == WSAEWOULDBLOCK || dwErr == ERROR_IO_PENDING) 
289                         {
290                                 /* Non-block socket operation wait 10 msecs */
291                                 SleepEx (10, TRUE);
292                                 /* Reset dwErr to zero for next pass */
293                                 dwErr = 0;
294                         }
295                         else
296                         {
297                                 FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | 
298                                         FORMAT_MESSAGE_FROM_SYSTEM, 
299                                         NULL,
300                                         dwErr,
301                                         MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
302                                         (LPTSTR) &pMsgMem,
303                                         1024,NULL);
304                                 pMsg = (pMsgMem) ? (LPTSTR)(pMsgMem) : "ASync InternetReadFileEx failed.";
305                                 XMLRPC_FAIL (envP, XMLRPC_NETWORK_ERROR, pMsg);
306                         }
307                 }
308                 
309                 if (inetBuffer.dwBufferLength)
310                 {
311                         TCHAR * bufptr = inetBuffer.lpvBuffer;
312                         bufptr += inetBuffer.dwBufferLength;
313                         inetBuffer.lpvBuffer = bufptr;
314                         nExpected -= inetBuffer.dwBufferLength;
315                         /* Adjust inetBuffer.dwBufferLength when it is greater than the */
316                         /* expected end of file */
317                         if (inetBuffer.dwBufferLength > nExpected)
318                                 inetBuffer.dwBufferLength = nExpected; 
319
320                 }
321                 else
322                         inetBuffer.dwBufferLength = nExpected;
323                 dwErr = 0;
324         } while  (nExpected != 0);
325
326
327     /* Add to the response buffer. */ 
328         xmlrpc_mem_block_append(envP, winInetTransactionP->pResponseData, body, inetBuffer.dwBufferTotal);
329      XMLRPC_FAIL_IF_FAULT (envP);
330
331  cleanup:
332         /* Since the XMLRPC_FAIL calls goto cleanup, we must handle */
333         /* the free'ing of the memory here. */
334         if (pMsgMem != NULL)
335         {
336                 LocalFree( pMsgMem );
337         }
338
339         if (body)
340                 free (body);
341 }
342
343
344 static void
345 performWinInetTransaction(xmlrpc_env *      const envP,
346                        winInetTransaction * const winInetTransactionP,
347                                            struct xmlrpc_client_transport * const clientTransportP) {
348         LPTSTR pMsg = NULL;
349         LPVOID pMsgMem = NULL;
350
351         unsigned long lastErr;
352         unsigned long reqFlags = INTERNET_FLAG_NO_UI;
353         char * acceptTypes[] = {"text/xml", NULL};
354         unsigned long queryLen = sizeof (unsigned long);
355         
356         winInetTransactionP->hURL = InternetConnect (hSyncInternetSession, 
357                                         winInetTransactionP->szHostName, winInetTransactionP->nPort, 
358                                         NULL, NULL, INTERNET_SERVICE_HTTP, 0, 1);
359
360         /* Start our request running. */
361         if (winInetTransactionP->bUseSSL == TRUE)
362                 reqFlags |= INTERNET_FLAG_SECURE |INTERNET_FLAG_IGNORE_CERT_CN_INVALID;
363
364         winInetTransactionP->hHttpRequest = HttpOpenRequest (winInetTransactionP->hURL, "POST",
365                 winInetTransactionP->szUrlPath, "HTTP/1.1", NULL, (const char **)&acceptTypes,
366                 reqFlags, 1);
367
368         XMLRPC_FAIL_IF_NULL(winInetTransactionP->hHttpRequest,envP, XMLRPC_INTERNAL_ERROR,
369                                                 "Unable to open the requested URL.");
370
371         if ( HttpAddRequestHeaders (winInetTransactionP->hHttpRequest, winInetTransactionP->headerList, 
372                 strlen (winInetTransactionP->headerList), HTTP_ADDREQ_FLAG_ADD|HTTP_ADDREQ_FLAG_REPLACE ) ==FALSE)
373         {
374                 XMLRPC_FAIL (envP, XMLRPC_INTERNAL_ERROR, "Could not set Content-Type.");
375         }
376
377 #ifdef DEBUG
378         /* Provide the user with transport status information */
379         InternetSetStatusCallback (winInetTransactionP->hHttpRequest, statusCallback);
380 #endif
381
382 Again:
383         /* Send the requested XML remote procedure command */ 
384         if (HttpSendRequest (winInetTransactionP->hHttpRequest, NULL, 0, 
385                 winInetTransactionP->pSendData, 
386                 strlen(winInetTransactionP->pSendData))==FALSE)
387         {
388                 lastErr = GetLastError ();
389
390                 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
391                         FORMAT_MESSAGE_ALLOCATE_BUFFER |
392                         FORMAT_MESSAGE_IGNORE_INSERTS, 
393                         NULL,
394                         lastErr,
395                         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
396                         (LPTSTR) &pMsgMem,
397                         0, NULL);
398
399
400                 if (pMsgMem == NULL)
401                 {
402                         switch (lastErr)
403                         {
404                         case ERROR_INTERNET_CANNOT_CONNECT:
405                                 pMsg = "Sync HttpSendRequest failed: Connection refused.";
406                                 break;
407                         case ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED:
408                                 pMsg = "Sync HttpSendRequest failed: Client authorization certificate needed.";
409                                 break;
410
411                         /* The following conditions are recommendations that microsoft */
412                         /* provides in their knowledge base. */
413
414                         /* HOWTO: Handle Invalid Certificate Authority Error with WinInet (Q182888) */
415                         case ERROR_INTERNET_INVALID_CA:
416                                 if (clientTransportP->allowInvalidSSLCerts){
417                                         OutputDebugString ("Sync HttpSendRequest failed: "
418                                                 "The function is unfamiliar with the certificate "
419                                                 "authority that generated the server's certificate. ");
420                                         reqFlags = SECURITY_FLAG_IGNORE_UNKNOWN_CA;
421
422                                         InternetSetOption (winInetTransactionP->hHttpRequest, INTERNET_OPTION_SECURITY_FLAGS,
423                                                                                 &reqFlags, sizeof (reqFlags));
424
425                                         goto Again;
426                                 }
427                                 else{
428                                         pMsg = "Invalid or unknown/untrusted SSL Certificate Authority.";
429                                 }
430                                 break;
431
432                         /* HOWTO: Make SSL Requests Using WinInet (Q168151) */
433                         case ERROR_INTERNET_SEC_CERT_CN_INVALID:
434                                 if (clientTransportP->allowInvalidSSLCerts){
435                                         OutputDebugString ("Sync HttpSendRequest failed: "
436                                                 "The SSL certificate common name (host name field) is incorrect\r\n "
437                                                 "for example, if you entered www.server.com and the common name "
438                                                 "on the certificate says www.different.com. ");
439                                         
440                                         reqFlags = INTERNET_FLAG_IGNORE_CERT_CN_INVALID;
441
442                                         InternetSetOption (winInetTransactionP->hHttpRequest, INTERNET_OPTION_SECURITY_FLAGS,
443                                                                                 &reqFlags, sizeof (reqFlags));
444
445                                         goto Again;
446                                 }
447                                 else{
448                                         pMsg = "The SSL certificate common name (host name field) is incorrect.";
449                                 }
450                                 break;
451
452                         case ERROR_INTERNET_SEC_CERT_DATE_INVALID:
453                                 if (clientTransportP->allowInvalidSSLCerts){
454                                         OutputDebugString ("Sync HttpSendRequest failed: "
455                                                 "The SSL certificate date that was received from the server is "
456                                                 "bad. The certificate is expired. ");
457
458                                         reqFlags = INTERNET_FLAG_IGNORE_CERT_DATE_INVALID;
459
460                                         InternetSetOption (winInetTransactionP->hHttpRequest, INTERNET_OPTION_SECURITY_FLAGS,
461                                                                                 &reqFlags, sizeof (reqFlags));
462
463                                         goto Again;
464                                 }
465                                 else{
466                                         pMsg = "The SSL certificate date that was received from the server is invalid.";
467                                 }
468                                 break;
469
470                         default:
471                                 pMsg = (LPTSTR)pMsgMem = LocalAlloc (LPTR, MAX_PATH);
472                                 sprintf (pMsg, "Sync HttpSendRequest failed: GetLastError (%d)", lastErr);
473                                 break;
474
475                         }
476                 }
477                 else
478                 {
479                         pMsg = (LPTSTR)(pMsgMem); 
480
481                 }
482                 XMLRPC_FAIL (envP, XMLRPC_NETWORK_ERROR, pMsg);
483
484         }
485
486         if( HttpQueryInfo (winInetTransactionP->hHttpRequest, 
487                 HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
488                 &winInetTransactionP->http_status, &queryLen, NULL) == FALSE)
489         {
490                 lastErr = GetLastError ();
491                 FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | 
492                         FORMAT_MESSAGE_FROM_SYSTEM, 
493                         NULL,
494                         lastErr,
495                         MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
496                         (LPTSTR) &pMsgMem,
497                         1024,NULL);
498
499                 pMsg = (pMsgMem) ? (LPTSTR)(pMsgMem) : "Sync HttpQueryInfo failed.";
500                 XMLRPC_FAIL (envP, XMLRPC_NETWORK_ERROR, pMsg);
501
502         }
503
504     /* Make sure we got a "200 OK" message from the remote server. */
505         if(winInetTransactionP->http_status!=200)
506         {
507                 unsigned long msgLen = 1024;
508                 char errMsg [1024];
509                 *errMsg = 0;
510                 HttpQueryInfo (winInetTransactionP->hHttpRequest, HTTP_QUERY_STATUS_TEXT, errMsg, &msgLen, NULL);
511
512                 /* Set our fault. We break this into multiple lines because it */
513                 /* will generally contain line breaks to begin with. */
514                 xmlrpc_env_set_fault_formatted (envP, XMLRPC_NETWORK_ERROR,
515                                            "HTTP error #%d occurred\n %s", winInetTransactionP->http_status, errMsg);
516                 goto cleanup;
517                 
518          }
519     /* Read the response. */    
520     get_wininet_response (envP, winInetTransactionP);
521     XMLRPC_FAIL_IF_FAULT (envP);
522
523  cleanup:
524         /* Since the XMLRPC_FAIL calls goto cleanup, we must handle */
525         /* the free'ing of the memory here. */
526         if (pMsgMem != NULL)
527         {
528                 LocalFree( pMsgMem );
529         }
530
531 }
532
533 static unsigned __stdcall 
534 doAsyncRpc(void * arg) {
535     rpc * const rpcP = arg;
536     xmlrpc_env env;
537     xmlrpc_env_init(&env);
538     performWinInetTransaction(&env, rpcP->winInetTransactionP, rpcP->clientTransportP );
539     rpcP->complete(rpcP->callInfoP, rpcP->responseXmlP, env);
540     xmlrpc_env_clean(&env);
541     return 0;
542 }
543
544
545 static void
546 createRpcThread(xmlrpc_env *              const envP,
547                 rpc *                     const rpcP,
548                 pthread_t *               const threadP) {
549
550     int rc;
551
552     rc = pthread_create(threadP, NULL, doAsyncRpc, rpcP);
553     switch (rc) {
554     case 0: 
555         break;
556     case EAGAIN:
557         xmlrpc_env_set_fault_formatted(
558             envP, XMLRPC_INTERNAL_ERROR, 
559             "pthread_create() failed:  System Resources exceeded.");
560         break;
561     case EINVAL:
562         xmlrpc_env_set_fault_formatted(
563             envP, XMLRPC_INTERNAL_ERROR, 
564             "pthread_create() failed:  Param Error for attr.");
565         break;
566     case ENOMEM:
567         xmlrpc_env_set_fault_formatted(
568             envP, XMLRPC_INTERNAL_ERROR, 
569             "pthread_create() failed:  No memory for new thread.");
570         break;
571     default:
572         xmlrpc_env_set_fault_formatted(
573             envP, XMLRPC_INTERNAL_ERROR, 
574             "pthread_create() failed: Unrecognized error code %d.", rc);
575         break;
576     }
577 }
578
579 static void
580 rpcCreate(xmlrpc_env *             const envP,
581           struct xmlrpc_client_transport * const clientTransportP,
582           const xmlrpc_server_info * const serverP,
583           xmlrpc_mem_block *       const callXmlP,
584           xmlrpc_mem_block *       const responseXmlP,
585           xmlrpc_transport_asynch_complete      complete, 
586           struct xmlrpc_call_info *       const callInfoP,
587           rpc **                   const rpcPP) {
588
589     rpc * rpcP;
590
591     MALLOCVAR(rpcP);
592     if (rpcP == NULL)
593         xmlrpc_env_set_fault_formatted(
594             envP, XMLRPC_INTERNAL_ERROR,
595             "Couldn't allocate memory for rpc object");
596     else {
597         rpcP->callInfoP = callInfoP;
598         rpcP->complete  = complete;
599         rpcP->responseXmlP = responseXmlP;
600         rpcP->threadExists = FALSE;
601
602         createWinInetTransaction(envP, serverP,
603                               callXmlP, responseXmlP, 
604                               &rpcP->winInetTransactionP);
605         if (!envP->fault_occurred) {
606             if (complete) {
607                 createRpcThread(envP, rpcP, &rpcP->thread);
608                 if (!envP->fault_occurred)
609                     rpcP->threadExists = TRUE;
610             }
611             if (!envP->fault_occurred) {
612                 list_init_header(&rpcP->link, rpcP);
613                 pthread_mutex_lock(&clientTransportP->listLock);
614                 list_add_head(&clientTransportP->rpcList, &rpcP->link);
615                 pthread_mutex_unlock(&clientTransportP->listLock);
616             }
617             if (envP->fault_occurred)
618                     destroyWinInetTransaction(rpcP->winInetTransactionP);
619         }
620         if (envP->fault_occurred)
621             free(rpcP);
622     }
623     *rpcPP = rpcP;
624 }
625
626 static void 
627 rpcDestroy(rpc * const rpcP) {
628
629     XMLRPC_ASSERT_PTR_OK(rpcP);
630     XMLRPC_ASSERT(!rpcP->threadExists);
631
632     destroyWinInetTransaction(rpcP->winInetTransactionP);
633
634     list_remove(&rpcP->link);
635
636     free(rpcP);
637 }
638
639 static void * 
640 finishRpc(struct list_head * const headerP, 
641           void *             const context ATTR_UNUSED) {
642     
643     rpc * const rpcP = headerP->itemP;
644
645     if (rpcP->threadExists) {
646         void *status;
647         int result;
648
649         result = pthread_join(rpcP->thread, &status);
650         
651         rpcP->threadExists = FALSE;
652     }
653
654     XMLRPC_MEMBLOCK_FREE(char, rpcP->responseXmlP);
655
656     rpcDestroy(rpcP);
657
658     return NULL;
659 }
660
661
662 /* Used for debugging purposes to track the status of
663 ** your request. */
664 void CALLBACK statusCallback (HINTERNET hInternet,
665                          unsigned long dwContext,
666                          unsigned long dwInternetStatus,
667                          void * lpvStatusInformation,
668                          unsigned long dwStatusInformationLength)
669 {
670         switch (dwInternetStatus)
671         {
672         case INTERNET_STATUS_RESOLVING_NAME:
673                 OutputDebugString("INTERNET_STATUS_RESOLVING_NAME\r\n");
674                 break;
675
676         case INTERNET_STATUS_NAME_RESOLVED:
677                 OutputDebugString("INTERNET_STATUS_NAME_RESOLVED\r\n");
678                 break;
679
680         case INTERNET_STATUS_HANDLE_CREATED:
681                 OutputDebugString("INTERNET_STATUS_HANDLE_CREATED\r\n");
682                 break;
683
684         case INTERNET_STATUS_CONNECTING_TO_SERVER:
685                 OutputDebugString("INTERNET_STATUS_CONNECTING_TO_SERVER\r\n");
686                 break;
687
688         case INTERNET_STATUS_REQUEST_SENT:
689                 OutputDebugString("INTERNET_STATUS_REQUEST_SENT\r\n");
690                 break;
691
692         case INTERNET_STATUS_SENDING_REQUEST:
693                 OutputDebugString("INTERNET_STATUS_SENDING_REQUEST\r\n");
694                 break;
695
696         case INTERNET_STATUS_CONNECTED_TO_SERVER:
697                 OutputDebugString("INTERNET_STATUS_CONNECTED_TO_SERVER\r\n");
698                 break;
699
700         case INTERNET_STATUS_RECEIVING_RESPONSE:
701                 OutputDebugString("INTERNET_STATUS_RECEIVING_RESPONSE\r\n");
702                 break;
703
704         case INTERNET_STATUS_RESPONSE_RECEIVED:
705                 OutputDebugString("INTERNET_STATUS_RESPONSE_RECEIVED\r\n");
706                 break;
707
708         case INTERNET_STATUS_CLOSING_CONNECTION:
709                 OutputDebugString("INTERNET_STATUS_CLOSING_CONNECTION\r\n");
710                 break;
711
712         case INTERNET_STATUS_CONNECTION_CLOSED:
713                 OutputDebugString("INTERNET_STATUS_CONNECTION_CLOSED\r\n");
714                 break;
715
716         case INTERNET_STATUS_HANDLE_CLOSING:
717                 OutputDebugString("INTERNET_STATUS_HANDLE_CLOSING\r\n");
718                 break;
719
720         case INTERNET_STATUS_CTL_RESPONSE_RECEIVED:
721                 OutputDebugString("INTERNET_STATUS_CTL_RESPONSE_RECEIVED\r\n");
722                 break;
723
724         case INTERNET_STATUS_REDIRECT:
725                 OutputDebugString("INTERNET_STATUS_REDIRECT\r\n");
726                 break;
727
728         case INTERNET_STATUS_REQUEST_COMPLETE:
729                 /* This indicates the data is ready. */
730                 OutputDebugString("INTERNET_STATUS_REQUEST_COMPLETE\r\n");
731                 break;
732
733         default:
734                 OutputDebugString("statusCallback, default case!\r\n");
735                 break;
736      }
737 }
738
739 static void 
740 create(xmlrpc_env *              const envP,
741        int                       const flags ATTR_UNUSED,
742        const char *              const appname ATTR_UNUSED,
743        const char *              const appversion ATTR_UNUSED,
744            const struct xmlrpc_xportparms * const transportparmsP,
745        size_t                    const parm_size,
746        struct xmlrpc_client_transport ** const handlePP) {
747 /*----------------------------------------------------------------------------
748    This does the 'create' operation for a WinInet client transport.
749 -----------------------------------------------------------------------------*/
750     struct xmlrpc_client_transport * transportP;
751
752         struct xmlrpc_wininet_xportparms * const wininetXportParmsP = 
753                 (struct xmlrpc_wininet_xportparms *) transportparmsP;
754
755     MALLOCVAR(transportP);
756     if (transportP == NULL)
757         xmlrpc_env_set_fault_formatted(
758             envP, XMLRPC_INTERNAL_ERROR, 
759             "Unable to allocate transport descriptor.");
760     else {
761         pthread_mutex_init(&transportP->listLock, NULL);
762         
763         list_make_empty(&transportP->rpcList);
764
765                 if (hSyncInternetSession == NULL)
766                         hSyncInternetSession = InternetOpen ("xmlrpc-c wininet transport",
767                                 INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
768
769                 if (!wininetXportParmsP || parm_size < XMLRPC_WXPSIZE(allowInvalidSSLCerts))
770                     transportP->allowInvalidSSLCerts = 0;
771                 else
772             transportP->allowInvalidSSLCerts = wininetXportParmsP->allowInvalidSSLCerts;
773
774         *handlePP = transportP;
775     }
776 }
777
778
779 static void 
780 destroy(struct xmlrpc_client_transport * const clientTransportP) {
781 /*----------------------------------------------------------------------------
782    This does the 'destroy' operation for a WinInet client transport.
783 -----------------------------------------------------------------------------*/
784         XMLRPC_ASSERT(clientTransportP != NULL);
785
786     XMLRPC_ASSERT(list_is_empty(&clientTransportP->rpcList));
787
788         if (hSyncInternetSession)
789                 InternetCloseHandle(hSyncInternetSession);
790         hSyncInternetSession = NULL;
791
792         pthread_mutex_destroy(&clientTransportP->listLock);
793
794         free(clientTransportP);
795 }
796
797
798 static void 
799 sendRequest(xmlrpc_env *             const envP, 
800             struct xmlrpc_client_transport * const clientTransportP,
801             const xmlrpc_server_info * const serverP,
802             xmlrpc_mem_block *       const callXmlP,
803             xmlrpc_transport_asynch_complete      complete,
804             struct xmlrpc_call_info *       const callInfoP) {
805 /*----------------------------------------------------------------------------
806    Initiate an XML-RPC rpc asynchronously.  Don't wait for it to go to
807    the server.
808
809    Unless we return failure, we arrange to have complete() called when
810    the rpc completes.
811
812    This does the 'send_request' operation for a WinInet client transport.
813 -----------------------------------------------------------------------------*/
814     rpc * rpcP;
815     xmlrpc_mem_block * responseXmlP;
816
817     responseXmlP = XMLRPC_MEMBLOCK_NEW(char, envP, 0);
818     if (!envP->fault_occurred) {
819         rpcCreate(envP, clientTransportP, serverP, callXmlP, responseXmlP,
820                   complete, callInfoP,
821                   &rpcP);
822
823         if (envP->fault_occurred)
824             XMLRPC_MEMBLOCK_FREE(char, responseXmlP);
825     }
826     /* The user's eventual finish_asynch call will destroy this RPC
827        and response buffer
828     */
829 }
830
831 static void 
832 finishAsynch(struct xmlrpc_client_transport * const clientTransportP,
833              xmlrpc_timeoutType         const timeoutType ATTR_UNUSED,
834              xmlrpc_timeout                const timeout ATTR_UNUSED) {
835 /*----------------------------------------------------------------------------
836    Wait for the threads of all outstanding RPCs to exit and destroy those
837    RPCs.
838
839    This does the 'finish_asynch' operation for a WinInet client transport.
840 -----------------------------------------------------------------------------*/
841     /* We ignore any timeout request.  Some day, we should figure out how
842        to set an alarm and interrupt running threads.
843     */
844
845     pthread_mutex_lock(&clientTransportP->listLock);
846
847     list_foreach(&clientTransportP->rpcList, finishRpc, NULL);
848
849     pthread_mutex_unlock(&clientTransportP->listLock);
850 }
851
852
853 static void
854 call(xmlrpc_env *             const envP,
855      struct xmlrpc_client_transport * const clientTransportP,
856      const xmlrpc_server_info * const serverP,
857      xmlrpc_mem_block *       const callXmlP,
858      xmlrpc_mem_block **      const responsePP) {
859
860          
861     xmlrpc_mem_block * responseXmlP;
862     rpc * rpcP;
863
864     XMLRPC_ASSERT_ENV_OK(envP);
865     XMLRPC_ASSERT_PTR_OK(serverP);
866     XMLRPC_ASSERT_PTR_OK(callXmlP);
867     XMLRPC_ASSERT_PTR_OK(responsePP);
868
869     responseXmlP = XMLRPC_MEMBLOCK_NEW(char, envP, 0);
870     if (!envP->fault_occurred) {
871         rpcCreate(envP, clientTransportP, serverP, callXmlP, responseXmlP,
872                   NULL, NULL, &rpcP);
873         if (!envP->fault_occurred) {
874             performWinInetTransaction(envP, rpcP->winInetTransactionP, clientTransportP);
875             
876             *responsePP = responseXmlP;
877             
878             rpcDestroy(rpcP);
879         }
880         if (envP->fault_occurred)
881             XMLRPC_MEMBLOCK_FREE(char, responseXmlP);
882     }
883 }
884
885
886 struct xmlrpc_client_transport_ops xmlrpc_wininet_transport_ops = {
887     NULL,
888     NULL,
889     &create,
890     &destroy,
891     &sendRequest,
892     &call,
893     &finishAsynch,
894 };
895
896 /* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
897 **
898 ** Redistribution and use in source and binary forms, with or without
899 ** modification, are permitted provided that the following conditions
900 ** are met:
901 ** 1. Redistributions of source code must retain the above copyright
902 **    notice, this list of conditions and the following disclaimer.
903 ** 2. Redistributions in binary form must reproduce the above copyright
904 **    notice, this list of conditions and the following disclaimer in the
905 **    documentation and/or other materials provided with the distribution.
906 ** 3. The name of the author may not be used to endorse or promote products
907 **    derived from this software without specific prior written permission. 
908 **  
909 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
910 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
911 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
912 ** ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
913 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
914 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
915 ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
916 ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
917 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
918 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
919 ** SUCH DAMAGE. */