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