curltransaction.c 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753
  1. /*=============================================================================
  2. curlTransaction
  3. =============================================================================*/
  4. #define _XOPEN_SOURCE 600 /* Make sure strdup() is in <string.h> */
  5. #include <assert.h>
  6. #include <string.h>
  7. #include <stdlib.h>
  8. #include "mallocvar.h"
  9. #include "xmlrpc-c/util.h"
  10. #include "xmlrpc-c/string_int.h"
  11. #include "xmlrpc-c/client.h"
  12. #include "xmlrpc-c/client_int.h"
  13. #include "version.h"
  14. #include <curl/curl.h>
  15. #include <curl/types.h>
  16. #include <curl/easy.h>
  17. #include "curlversion.h"
  18. #include "curltransaction.h"
  19. struct curlTransaction {
  20. /* This is all stuff that really ought to be in a Curl object, but
  21. the Curl library is a little too simple for that. So we build
  22. a layer on top of Curl, and define this "transaction," as an
  23. object subordinate to a Curl "session." A Curl session has
  24. zero or one transactions in progress. The Curl session
  25. "private data" is a pointer to the CurlTransaction object for
  26. the current transaction.
  27. */
  28. CURL * curlSessionP;
  29. /* Handle for the Curl session that hosts this transaction.
  30. Note that only one transaction at a time can use a particular
  31. Curl session, so this had better not be a session that some other
  32. transaction is using simultaneously.
  33. */
  34. curlt_finishFn * finish;
  35. curlt_progressFn * progress;
  36. void * userContextP;
  37. /* Meaningful to our client; opaque to us */
  38. CURLcode result;
  39. /* Result of the transaction (succeeded, TCP connect failed, etc.).
  40. A properly executed HTTP transaction (request & response) counts
  41. as a successful transaction. When 'result' show success,
  42. curl_easy_get_info() tells you whether the transaction succeeded
  43. at the HTTP level.
  44. */
  45. char curlError[CURL_ERROR_SIZE];
  46. /* Error message from Curl */
  47. struct curl_slist * headerList;
  48. /* The HTTP headers for the transaction */
  49. const char * serverUrl; /* malloc'ed - belongs to this object */
  50. };
  51. static void
  52. addHeader(xmlrpc_env * const envP,
  53. struct curl_slist ** const headerListP,
  54. const char * const headerText) {
  55. struct curl_slist * newHeaderList;
  56. newHeaderList = curl_slist_append(*headerListP, headerText);
  57. if (newHeaderList == NULL)
  58. xmlrpc_faultf(envP,
  59. "Could not add header '%s'. "
  60. "curl_slist_append() failed.", headerText);
  61. else
  62. *headerListP = newHeaderList;
  63. }
  64. static void
  65. addContentTypeHeader(xmlrpc_env * const envP,
  66. struct curl_slist ** const headerListP) {
  67. addHeader(envP, headerListP, "Content-Type: text/xml");
  68. }
  69. static const char *
  70. xmlrpcUserAgentPart(bool const reportIt) {
  71. const char * retval;
  72. if (reportIt) {
  73. curl_version_info_data * const curlInfoP =
  74. curl_version_info(CURLVERSION_NOW);
  75. char curlVersion[32];
  76. snprintf(curlVersion, sizeof(curlVersion), "%u.%u.%u",
  77. (curlInfoP->version_num >> 16) && 0xff,
  78. (curlInfoP->version_num >> 8) && 0xff,
  79. (curlInfoP->version_num >> 0) && 0xff
  80. );
  81. xmlrpc_asprintf(&retval,
  82. "Xmlrpc-c/%s Curl/%s",
  83. XMLRPC_C_VERSION, curlVersion);
  84. } else
  85. xmlrpc_asprintf(&retval, "%s", "");
  86. return retval;
  87. }
  88. static void
  89. addUserAgentHeader(xmlrpc_env * const envP,
  90. struct curl_slist ** const headerListP,
  91. bool const reportXmlrpc,
  92. const char * const userAgent) {
  93. /*----------------------------------------------------------------------------
  94. Add a User-Agent HTTP header to the Curl header list *headerListP,
  95. if appropriate.
  96. 'reportXmlrpc' means we want to tell the client what XML-RPC agent
  97. is being used -- Xmlrpc-c and layers below.
  98. 'userAgent' is a string describing the layers above Xmlrpc-c. We
  99. assume it is in the proper format to be included in a User-Agent
  100. header. (We should probably fix that some day -- take ownership
  101. of that format).
  102. -----------------------------------------------------------------------------*/
  103. if (reportXmlrpc || userAgent) {
  104. /* Add the header */
  105. /* Note: Curl has a CURLOPT_USERAGENT option that does some of this
  106. work. We prefer to be totally in control, though, so we build
  107. the header explicitly.
  108. */
  109. const char * const xmlrpcPart = xmlrpcUserAgentPart(reportXmlrpc);
  110. if (xmlrpc_strnomem(xmlrpcPart))
  111. xmlrpc_faultf(envP, "Couldn't allocate memory for "
  112. "User-Agent header");
  113. else {
  114. const char * const userPart = userAgent ? userAgent : "";
  115. const char * const space = userAgent && reportXmlrpc ? " " : "";
  116. const char * userAgentHeader;
  117. xmlrpc_asprintf(&userAgentHeader,
  118. "User-Agent: %s%s%s",
  119. userPart, space, xmlrpcPart);
  120. if (xmlrpc_strnomem(userAgentHeader))
  121. xmlrpc_faultf(envP, "Couldn't allocate memory for "
  122. "User-Agent header");
  123. else {
  124. addHeader(envP, headerListP, userAgentHeader);
  125. xmlrpc_strfree(userAgentHeader);
  126. }
  127. xmlrpc_strfree(xmlrpcPart);
  128. }
  129. }
  130. }
  131. static void
  132. addAuthorizationHeader(xmlrpc_env * const envP,
  133. struct curl_slist ** const headerListP,
  134. const char * const hdrValue) {
  135. const char * authorizationHeader;
  136. xmlrpc_asprintf(&authorizationHeader, "Authorization: %s", hdrValue);
  137. if (xmlrpc_strnomem(authorizationHeader))
  138. xmlrpc_faultf(envP, "Couldn't allocate memory for "
  139. "Authorization header");
  140. else {
  141. addHeader(envP, headerListP, authorizationHeader);
  142. xmlrpc_strfree(authorizationHeader);
  143. }
  144. }
  145. /*
  146. In HTTP 1.1, the client can send the header "Expect: 100-continue", which
  147. tells the server that the client isn't going to send the body until the
  148. server tells it to by sending a "continue" response (HTTP response code 100).
  149. The server is obligated to send that response.
  150. However, many servers are broken and don't send the Continue response.
  151. Early libcurl did not send the Expect: header, thus worked fine with such
  152. broken servers. But as of ca. 2007, libcurl sends the Expect:, and waits
  153. for the response, when the body is large. It gives up after 3 seconds and
  154. sends the body anyway.
  155. To accomodate the broken servers and for backward compatibility, we always
  156. force libcurl not to send the Expect and consequently not to wait for the
  157. response, using the hackish (but according to libcurl design) method of
  158. including an entry in our explicit header list that is an Expect: header
  159. with an empty argument. This causes libcurl not to send any Expect: header.
  160. This is since 1.19; we may find there are also servers and/or libcurl levels
  161. that can't work with that.
  162. We may find a case where the Expect/Continue protocol is desirable. If we
  163. do, we should add a transport option to request the function and let libcurl
  164. do its thing when the user requests it.
  165. The purpose of Expect/Continue is to save the client the trouble of
  166. generating and/or sending the body when the server is just going to reject
  167. the transaction based on the headers -- like maybe because the body is
  168. too big.
  169. */
  170. static void
  171. addExpectHeader(xmlrpc_env * const envP,
  172. struct curl_slist ** const headerListP) {
  173. addHeader(envP, headerListP, "Expect:");
  174. /* Don't send Expect header. See explanation above. */
  175. }
  176. static void
  177. createCurlHeaderList(xmlrpc_env * const envP,
  178. const char * const authHdrValue,
  179. bool const dontAdvertise,
  180. const char * const userAgent,
  181. struct curl_slist ** const headerListP) {
  182. struct curl_slist * headerList;
  183. headerList = NULL; /* initial value - empty list */
  184. addContentTypeHeader(envP, &headerList);
  185. if (!envP->fault_occurred) {
  186. addUserAgentHeader(envP, &headerList, !dontAdvertise, userAgent);
  187. if (!envP->fault_occurred) {
  188. if (authHdrValue)
  189. addAuthorizationHeader(envP, &headerList, authHdrValue);
  190. }
  191. if (!envP->fault_occurred)
  192. addExpectHeader(envP, &headerList);
  193. }
  194. if (envP->fault_occurred)
  195. curl_slist_free_all(headerList);
  196. *headerListP = headerList;
  197. }
  198. static size_t
  199. collect(void * const ptr,
  200. size_t const size,
  201. size_t const nmemb,
  202. FILE * const stream) {
  203. /*----------------------------------------------------------------------------
  204. This is a Curl output function. Curl calls this to deliver the
  205. HTTP response body to the Curl client. Curl thinks it's writing to
  206. a POSIX stream.
  207. -----------------------------------------------------------------------------*/
  208. xmlrpc_mem_block * const responseXmlP = (xmlrpc_mem_block *) stream;
  209. char * const buffer = ptr;
  210. size_t const length = nmemb * size;
  211. size_t retval;
  212. xmlrpc_env env;
  213. xmlrpc_env_init(&env);
  214. xmlrpc_mem_block_append(&env, responseXmlP, buffer, length);
  215. if (env.fault_occurred)
  216. retval = (size_t)-1;
  217. else
  218. /* Really? Shouldn't it be like fread() and return 'nmemb'? */
  219. retval = length;
  220. return retval;
  221. }
  222. static int
  223. curlProgress(void * const contextP,
  224. double const dltotal,
  225. double const dlnow,
  226. double const ultotal,
  227. double const ulnow) {
  228. /*----------------------------------------------------------------------------
  229. This is a Curl "progress function." It's something various Curl functions
  230. call every so often, including whenever something gets interrupted by the
  231. process receiving, and catching, a signal. There are two purposes of a
  232. Curl progress function: 1) lets us log the progress of a long-running
  233. transaction such as a big download, e.g. by displaying a progress bar
  234. somewhere. 2) allows us to tell the Curl function, via our return code,
  235. that calls it that we don't want to wait anymore for the operation to
  236. complete.
  237. In Curl versions before March 2007, we get called once per second and
  238. signals have no effect. In current Curl, we usually get called immediately
  239. after a signal gets caught while Curl is waiting to receive a response from
  240. the server. But Curl doesn't properly synchronize with signals, so it may
  241. miss one and then we don't get called until the next scheduled
  242. one-per-second call.
  243. All we do is pass the call through to the curlTransaction's progress
  244. function (the one that the creator of the curlTransaction registered).
  245. This function is not as important as it once was for interrupting purposes.
  246. This module used to use curl_easy_perform(), which can be interrupted only
  247. via this progress function. But because of the above-mentioned failure of
  248. Curl to properly synchronize signals (and Bryan's failure to get Curl
  249. developers to accept code to fix it), we now use the Curl "multi" facility
  250. instead and do our own pselect(). But This function still normally gets
  251. called by curl_multi_perform(), which the transport tries to call even when
  252. the user has requested interruption, because we don't trust our ability to
  253. abort a running Curl transaction. curl_multi_perform() reliably winds up a
  254. Curl transaction when this function tells it to.
  255. -----------------------------------------------------------------------------*/
  256. curlTransaction * const curlTransactionP = contextP;
  257. bool abort;
  258. /* We require anyone setting us up as the Curl progress function to
  259. supply a progress function:
  260. */
  261. assert(curlTransactionP);
  262. assert(curlTransactionP->progress);
  263. curlTransactionP->progress(curlTransactionP->userContextP,
  264. dltotal, dlnow, ultotal, ulnow,
  265. &abort);
  266. return abort;
  267. }
  268. static void
  269. setupAuth(xmlrpc_env * const envP ATTR_UNUSED,
  270. CURL * const curlSessionP,
  271. const xmlrpc_server_info * const serverInfoP,
  272. const char ** const authHdrValueP) {
  273. /*----------------------------------------------------------------------------
  274. Set the options in the Curl session 'curlSessionP' to set up the HTTP
  275. authentication described by *serverInfoP.
  276. But we have an odd special function for backward compatibility, because
  277. this code dates to a time when libcurl did not have the ability to
  278. handle authentication, but we provided such function nonetheless by
  279. building our own Authorization: header. But we did this only for
  280. HTTP basic authentication.
  281. So the special function is this: if libcurl is too old to have
  282. authorization options and *serverInfoP allows basic authentication,
  283. return as *basicAuthHdrParamP an appropriate parameter for the
  284. Authorization: Basic: HTTP header. Otherwise, return
  285. *basicAuthHdrParamP == NULL.
  286. -----------------------------------------------------------------------------*/
  287. if (serverInfoP->allowedAuth.basic) {
  288. CURLcode rc;
  289. rc = curl_easy_setopt(curlSessionP, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
  290. if (rc == CURLE_OK)
  291. *authHdrValueP = NULL;
  292. else {
  293. *authHdrValueP = strdup(serverInfoP->basicAuthHdrValue);
  294. if (*authHdrValueP == NULL)
  295. xmlrpc_faultf(envP, "Unable to allocate memory for basic "
  296. "authentication header");
  297. }
  298. } else
  299. *authHdrValueP = NULL;
  300. /* We don't worry if libcurl is too old for these other kinds of
  301. authentication; they're only defined as _allowed_
  302. authentication methods, for when client and server are capable
  303. of using it, and unlike with basic authentication, we have no
  304. historical commitment to consider an old libcurl as capable of
  305. doing these.
  306. */
  307. if (serverInfoP->userNamePw)
  308. curl_easy_setopt(curlSessionP, CURLOPT_USERPWD,
  309. serverInfoP->userNamePw);
  310. if (serverInfoP->allowedAuth.digest)
  311. curl_easy_setopt(
  312. curlSessionP, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
  313. if (serverInfoP->allowedAuth.gssnegotiate)
  314. curl_easy_setopt(
  315. curlSessionP, CURLOPT_HTTPAUTH, CURLAUTH_GSSNEGOTIATE);
  316. if (serverInfoP->allowedAuth.ntlm)
  317. curl_easy_setopt(
  318. curlSessionP, CURLOPT_HTTPAUTH, CURLAUTH_NTLM);
  319. }
  320. static void
  321. setCurlTimeout(CURL * const curlSessionP ATTR_UNUSED,
  322. unsigned int const timeout ATTR_UNUSED) {
  323. #if HAVE_CURL_NOSIGNAL
  324. unsigned int const timeoutMs = (timeout + 999)/1000;
  325. curl_easy_setopt(curlSessionP, CURLOPT_NOSIGNAL, 1);
  326. assert((long)timeoutMs == (int)timeoutMs);
  327. /* Calling requirement */
  328. curl_easy_setopt(curlSessionP, CURLOPT_TIMEOUT, (long)timeoutMs);
  329. #else
  330. abort();
  331. #endif
  332. }
  333. static void
  334. assertConstantsMatch(void) {
  335. /*----------------------------------------------------------------------------
  336. There are some constants that we define as part of the Xmlrpc-c
  337. interface that are identical to constants in the Curl interface to
  338. make curl option setting work. This function asserts such
  339. formally.
  340. -----------------------------------------------------------------------------*/
  341. assert(XMLRPC_SSLVERSION_DEFAULT == CURL_SSLVERSION_DEFAULT);
  342. assert(XMLRPC_SSLVERSION_TLSv1 == CURL_SSLVERSION_TLSv1 );
  343. assert(XMLRPC_SSLVERSION_SSLv2 == CURL_SSLVERSION_SSLv2 );
  344. assert(XMLRPC_SSLVERSION_SSLv3 == CURL_SSLVERSION_SSLv3 );
  345. assert(XMLRPC_HTTPAUTH_BASIC == CURLAUTH_BASIC );
  346. assert(XMLRPC_HTTPAUTH_DIGEST == CURLAUTH_DIGEST );
  347. assert(XMLRPC_HTTPAUTH_GSSNEGOTIATE == CURLAUTH_GSSNEGOTIATE);
  348. assert(XMLRPC_HTTPAUTH_NTLM == CURLAUTH_NTLM );
  349. assert(XMLRPC_HTTPPROXY_HTTP == CURLPROXY_HTTP );
  350. assert(XMLRPC_HTTPPROXY_SOCKS5 == CURLPROXY_SOCKS5 );
  351. }
  352. static void
  353. setupCurlSession(xmlrpc_env * const envP,
  354. curlTransaction * const curlTransactionP,
  355. xmlrpc_mem_block * const callXmlP,
  356. xmlrpc_mem_block * const responseXmlP,
  357. const xmlrpc_server_info * const serverInfoP,
  358. bool const dontAdvertise,
  359. const char * const userAgent,
  360. const struct curlSetup * const curlSetupP) {
  361. /*----------------------------------------------------------------------------
  362. Set up the Curl session for the transaction *curlTransactionP so that
  363. a subsequent curl_easy_perform() would perform said transaction.
  364. The data curl_easy_perform() would send for that transaction would
  365. be the contents of *callXmlP; the data curl_easy_perform() gets back
  366. would go into *responseXmlP.
  367. *serverInfoP tells what sort of authentication to set up. This is
  368. an embarassment, as the xmlrpc_server_info type is part of the
  369. Xmlrpc-c interface. Some day, we need to replace this with a type
  370. (probably identical) not tied to Xmlrpc-c.
  371. -----------------------------------------------------------------------------*/
  372. CURL * const curlSessionP = curlTransactionP->curlSessionP;
  373. assertConstantsMatch();
  374. /* A Curl session is serial -- it processes zero or one transaction
  375. at a time. We use the "private" attribute of the Curl session to
  376. indicate which transaction it is presently processing. This is
  377. important when the transaction finishes, because libcurl will just
  378. tell us that something finished on a particular session, not that
  379. a particular transaction finished.
  380. */
  381. /* It is out policy to do a libcurl call only where necessary, I.e. not
  382. to set what is the default anyhow. The reduction in calls may save
  383. some time, but mostly, it will save us encountering rare bugs or
  384. suffering from backward incompatibilities in future libcurl. I.e. we
  385. don't exercise any more of libcurl than we have to.
  386. */
  387. curl_easy_setopt(curlSessionP, CURLOPT_PRIVATE, curlTransactionP);
  388. curl_easy_setopt(curlSessionP, CURLOPT_POST, 1);
  389. curl_easy_setopt(curlSessionP, CURLOPT_URL, curlTransactionP->serverUrl);
  390. XMLRPC_MEMBLOCK_APPEND(char, envP, callXmlP, "\0", 1);
  391. if (!envP->fault_occurred) {
  392. curl_easy_setopt(curlSessionP, CURLOPT_POSTFIELDS,
  393. XMLRPC_MEMBLOCK_CONTENTS(char, callXmlP));
  394. curl_easy_setopt(curlSessionP, CURLOPT_WRITEFUNCTION, collect);
  395. curl_easy_setopt(curlSessionP, CURLOPT_FILE, responseXmlP);
  396. curl_easy_setopt(curlSessionP, CURLOPT_HEADER, 0);
  397. curl_easy_setopt(curlSessionP, CURLOPT_ERRORBUFFER,
  398. curlTransactionP->curlError);
  399. if (curlTransactionP->progress) {
  400. curl_easy_setopt(curlSessionP, CURLOPT_NOPROGRESS, 0);
  401. curl_easy_setopt(curlSessionP, CURLOPT_PROGRESSFUNCTION,
  402. curlProgress);
  403. curl_easy_setopt(curlSessionP, CURLOPT_PROGRESSDATA,
  404. curlTransactionP);
  405. } else
  406. curl_easy_setopt(curlSessionP, CURLOPT_NOPROGRESS, 1);
  407. curl_easy_setopt(curlSessionP, CURLOPT_SSL_VERIFYPEER,
  408. curlSetupP->sslVerifyPeer);
  409. curl_easy_setopt(curlSessionP, CURLOPT_SSL_VERIFYHOST,
  410. curlSetupP->sslVerifyHost ? 2 : 0);
  411. if (curlSetupP->networkInterface)
  412. curl_easy_setopt(curlSessionP, CURLOPT_INTERFACE,
  413. curlSetupP->networkInterface);
  414. if (curlSetupP->sslCert)
  415. curl_easy_setopt(curlSessionP, CURLOPT_SSLCERT,
  416. curlSetupP->sslCert);
  417. if (curlSetupP->sslCertType)
  418. curl_easy_setopt(curlSessionP, CURLOPT_SSLCERTTYPE,
  419. curlSetupP->sslCertType);
  420. if (curlSetupP->sslCertPasswd)
  421. curl_easy_setopt(curlSessionP, CURLOPT_SSLCERTPASSWD,
  422. curlSetupP->sslCertPasswd);
  423. if (curlSetupP->sslKey)
  424. curl_easy_setopt(curlSessionP, CURLOPT_SSLKEY,
  425. curlSetupP->sslKey);
  426. if (curlSetupP->sslKeyType)
  427. curl_easy_setopt(curlSessionP, CURLOPT_SSLKEYTYPE,
  428. curlSetupP->sslKeyType);
  429. if (curlSetupP->sslKeyPasswd)
  430. curl_easy_setopt(curlSessionP, CURLOPT_SSLKEYPASSWD,
  431. curlSetupP->sslKeyPasswd);
  432. if (curlSetupP->sslEngine)
  433. curl_easy_setopt(curlSessionP, CURLOPT_SSLENGINE,
  434. curlSetupP->sslEngine);
  435. if (curlSetupP->sslEngineDefault)
  436. /* 3rd argument seems to be required by some Curl */
  437. curl_easy_setopt(curlSessionP, CURLOPT_SSLENGINE_DEFAULT, 1l);
  438. if (curlSetupP->sslVersion != XMLRPC_SSLVERSION_DEFAULT)
  439. curl_easy_setopt(curlSessionP, CURLOPT_SSLVERSION,
  440. curlSetupP->sslVersion);
  441. if (curlSetupP->caInfo)
  442. curl_easy_setopt(curlSessionP, CURLOPT_CAINFO,
  443. curlSetupP->caInfo);
  444. if (curlSetupP->caPath)
  445. curl_easy_setopt(curlSessionP, CURLOPT_CAPATH,
  446. curlSetupP->caPath);
  447. if (curlSetupP->randomFile)
  448. curl_easy_setopt(curlSessionP, CURLOPT_RANDOM_FILE,
  449. curlSetupP->randomFile);
  450. if (curlSetupP->egdSocket)
  451. curl_easy_setopt(curlSessionP, CURLOPT_EGDSOCKET,
  452. curlSetupP->egdSocket);
  453. if (curlSetupP->sslCipherList)
  454. curl_easy_setopt(curlSessionP, CURLOPT_SSL_CIPHER_LIST,
  455. curlSetupP->sslCipherList);
  456. if (curlSetupP->proxy)
  457. curl_easy_setopt(curlSessionP, CURLOPT_PROXY, curlSetupP->proxy);
  458. if (curlSetupP->proxyAuth != CURLAUTH_BASIC)
  459. /* Note that the Xmlrpc-c default and the Curl default are
  460. different. Xmlrpc-c is none, while Curl is basic. One reason
  461. for this is that it makes our extensible parameter list scheme,
  462. wherein zero always means default, easier.
  463. */
  464. curl_easy_setopt(curlSessionP, CURLOPT_PROXYAUTH,
  465. curlSetupP->proxyAuth);
  466. if (curlSetupP->proxyPort)
  467. curl_easy_setopt(curlSessionP, CURLOPT_PROXYPORT,
  468. curlSetupP->proxyPort);
  469. if (curlSetupP->proxyUserPwd)
  470. curl_easy_setopt(curlSessionP, CURLOPT_PROXYUSERPWD,
  471. curlSetupP->proxyUserPwd);
  472. if (curlSetupP->proxyType)
  473. curl_easy_setopt(curlSessionP, CURLOPT_PROXYTYPE,
  474. curlSetupP->proxyType);
  475. if (curlSetupP->verbose)
  476. curl_easy_setopt(curlSessionP, CURLOPT_VERBOSE, 1l);
  477. if (curlSetupP->timeout)
  478. setCurlTimeout(curlSessionP, curlSetupP->timeout);
  479. {
  480. const char * authHdrValue;
  481. /* NULL means we don't have to construct an explicit
  482. Authorization: header. non-null means we have to
  483. construct one with this as its value.
  484. */
  485. setupAuth(envP, curlSessionP, serverInfoP, &authHdrValue);
  486. if (!envP->fault_occurred) {
  487. struct curl_slist * headerList;
  488. createCurlHeaderList(envP, authHdrValue,
  489. dontAdvertise, userAgent,
  490. &headerList);
  491. if (!envP->fault_occurred) {
  492. curl_easy_setopt(
  493. curlSessionP, CURLOPT_HTTPHEADER, headerList);
  494. curlTransactionP->headerList = headerList;
  495. }
  496. if (authHdrValue)
  497. xmlrpc_strfree(authHdrValue);
  498. }
  499. }
  500. }
  501. }
  502. void
  503. curlTransaction_create(xmlrpc_env * const envP,
  504. CURL * const curlSessionP,
  505. const xmlrpc_server_info * const serverP,
  506. xmlrpc_mem_block * const callXmlP,
  507. xmlrpc_mem_block * const responseXmlP,
  508. bool const dontAdvertise,
  509. const char * const userAgent,
  510. const struct curlSetup * const curlSetupStuffP,
  511. void * const userContextP,
  512. curlt_finishFn * const finish,
  513. curlt_progressFn * const progress,
  514. curlTransaction ** const curlTransactionPP) {
  515. curlTransaction * curlTransactionP;
  516. MALLOCVAR(curlTransactionP);
  517. if (curlTransactionP == NULL)
  518. xmlrpc_faultf(envP, "No memory to create Curl transaction.");
  519. else {
  520. curlTransactionP->finish = finish;
  521. curlTransactionP->curlSessionP = curlSessionP;
  522. curlTransactionP->userContextP = userContextP;
  523. curlTransactionP->progress = progress;
  524. curlTransactionP->serverUrl = strdup(serverP->serverUrl);
  525. if (curlTransactionP->serverUrl == NULL)
  526. xmlrpc_faultf(envP, "Out of memory to store server URL.");
  527. else {
  528. setupCurlSession(envP, curlTransactionP,
  529. callXmlP, responseXmlP,
  530. serverP, dontAdvertise, userAgent,
  531. curlSetupStuffP);
  532. if (envP->fault_occurred)
  533. xmlrpc_strfree(curlTransactionP->serverUrl);
  534. }
  535. if (envP->fault_occurred)
  536. free(curlTransactionP);
  537. }
  538. *curlTransactionPP = curlTransactionP;
  539. }
  540. void
  541. curlTransaction_destroy(curlTransaction * const curlTransactionP) {
  542. curl_slist_free_all(curlTransactionP->headerList);
  543. xmlrpc_strfree(curlTransactionP->serverUrl);
  544. free(curlTransactionP);
  545. }
  546. static void
  547. interpretCurlEasyError(const char ** const descriptionP,
  548. CURLcode const code) {
  549. #if HAVE_CURL_STRERROR
  550. *descriptionP = strdup(curl_easy_strerror(code));
  551. #else
  552. xmlrpc_asprintf(descriptionP, "Curl error code (CURLcode) %d", code);
  553. #endif
  554. }
  555. void
  556. curlTransaction_getError(curlTransaction * const curlTransactionP,
  557. xmlrpc_env * const envP) {
  558. if (curlTransactionP->result != CURLE_OK) {
  559. /* We've seen Curl just return a null string for an explanation
  560. (e.g. when TCP connect() fails because IP address doesn't exist).
  561. */
  562. const char * explanation;
  563. if (strlen(curlTransactionP->curlError) == 0)
  564. interpretCurlEasyError(&explanation, curlTransactionP->result);
  565. else
  566. xmlrpc_asprintf(&explanation, "%s", curlTransactionP->curlError);
  567. xmlrpc_env_set_fault_formatted(
  568. envP, XMLRPC_NETWORK_ERROR, "libcurl failed to execute the "
  569. "HTTP POST transaction, explaining: %s", explanation);
  570. xmlrpc_strfree(explanation);
  571. } else {
  572. CURLcode res;
  573. long http_result;
  574. res = curl_easy_getinfo(curlTransactionP->curlSessionP,
  575. CURLINFO_HTTP_CODE, &http_result);
  576. if (res != CURLE_OK)
  577. xmlrpc_env_set_fault_formatted(
  578. envP, XMLRPC_INTERNAL_ERROR,
  579. "Curl performed the HTTP POST request, but was "
  580. "unable to say what the HTTP result code was. "
  581. "curl_easy_getinfo(CURLINFO_HTTP_CODE) says: %s",
  582. curlTransactionP->curlError);
  583. else {
  584. if (http_result != 200)
  585. xmlrpc_env_set_fault_formatted(
  586. envP, XMLRPC_NETWORK_ERROR,
  587. "HTTP response code is %ld, not 200",
  588. http_result);
  589. }
  590. }
  591. }
  592. void
  593. curlTransaction_finish(xmlrpc_env * const envP,
  594. curlTransaction * const curlTransactionP,
  595. CURLcode const result) {
  596. curlTransactionP->result = result;
  597. if (curlTransactionP->finish)
  598. curlTransactionP->finish(envP, curlTransactionP->userContextP);
  599. }
  600. CURL *
  601. curlTransaction_curlSession(curlTransaction * const curlTransactionP) {
  602. return curlTransactionP->curlSessionP;
  603. }