xmlrpc_wininet_transport.c 33 KB

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