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