Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: libcurl based HTTP client
4 : * Purpose: libcurl based HTTP client
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2006, Frank Warmerdam
9 : * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "cpl_port.h"
31 : #include "cpl_http.h"
32 :
33 : #include <cstddef>
34 : #include <cstring>
35 :
36 : #include <algorithm>
37 : #include <array>
38 : #include <map>
39 : #include <mutex>
40 : #include <string>
41 : #include <vector>
42 :
43 : #include "cpl_http.h"
44 : #include "cpl_error.h"
45 : #include "cpl_multiproc.h"
46 : #include "cpl_vsil_curl_class.h"
47 :
48 : // gcc or clang complains about C-style cast in #define like
49 : // CURL_ZERO_TERMINATED
50 : #if defined(__GNUC__)
51 : #pragma GCC diagnostic push
52 : #pragma GCC diagnostic ignored "-Wold-style-cast"
53 : #endif
54 :
55 : #ifdef HAVE_CURL
56 :
57 : #include "cpl_curl_priv.h"
58 :
59 : #ifdef HAVE_OPENSSL_CRYPTO
60 : #include <openssl/err.h>
61 : #include <openssl/ssl.h>
62 : #include <openssl/engine.h>
63 : #include <openssl/x509v3.h>
64 :
65 : #if defined(_WIN32)
66 : #include <wincrypt.h>
67 : #endif
68 :
69 : #endif
70 :
71 : #ifdef HAVE_SIGACTION
72 : #include <signal.h>
73 : #endif
74 :
75 : #define unchecked_curl_easy_setopt(handle, opt, param) \
76 : CPL_IGNORE_RET_VAL(curl_easy_setopt(handle, opt, param))
77 :
78 : #endif // HAVE_CURL
79 :
80 : // list of named persistent http sessions
81 :
82 : #ifdef HAVE_CURL
83 : static std::map<CPLString, CURL *> *poSessionMap = nullptr;
84 : static std::map<CPLString, CURLM *> *poSessionMultiMap = nullptr;
85 : static CPLMutex *hSessionMapMutex = nullptr;
86 : static bool bHasCheckVersion = false;
87 : static bool bSupportGZip = false;
88 : static bool bSupportHTTP2 = false;
89 : #if defined(_WIN32) && defined(HAVE_OPENSSL_CRYPTO)
90 : static std::vector<X509 *> *poWindowsCertificateList = nullptr;
91 :
92 : #if (OPENSSL_VERSION_NUMBER < 0x10100000L)
93 : #define EVP_PKEY_get0_RSA(x) (x->pkey.rsa)
94 : #define EVP_PKEY_get0_DSA(x) (x->pkey.dsa)
95 : #define X509_get_extension_flags(x) (x->ex_flags)
96 : #define X509_get_key_usage(x) (x->ex_kusage)
97 : #define X509_get_extended_key_usage(x) (x->ex_xkusage)
98 : #endif
99 :
100 : #endif // defined(_WIN32) && defined(HAVE_OPENSSL_CRYPTO)
101 :
102 : #if defined(HAVE_OPENSSL_CRYPTO) && OPENSSL_VERSION_NUMBER < 0x10100000
103 :
104 : // Ported from https://curl.haxx.se/libcurl/c/opensslthreadlock.html
105 : static CPLMutex **pahSSLMutex = nullptr;
106 :
107 : static void CPLOpenSSLLockingFunction(int mode, int n, const char * /*file*/,
108 : int /*line*/)
109 : {
110 : if (mode & CRYPTO_LOCK)
111 : {
112 : CPLAcquireMutex(pahSSLMutex[n], 3600.0);
113 : }
114 : else
115 : {
116 : CPLReleaseMutex(pahSSLMutex[n]);
117 : }
118 : }
119 :
120 : static unsigned long CPLOpenSSLIdCallback(void)
121 : {
122 : return static_cast<unsigned long>(CPLGetPID());
123 : }
124 :
125 : static void CPLOpenSSLInit()
126 : {
127 : if (strstr(curl_version(), "OpenSSL") &&
128 : CPLTestBool(CPLGetConfigOption("CPL_OPENSSL_INIT_ENABLED", "YES")) &&
129 : CRYPTO_get_id_callback() == nullptr)
130 : {
131 : pahSSLMutex = static_cast<CPLMutex **>(
132 : CPLMalloc(CRYPTO_num_locks() * sizeof(CPLMutex *)));
133 : for (int i = 0; i < CRYPTO_num_locks(); i++)
134 : {
135 : pahSSLMutex[i] = CPLCreateMutex();
136 : CPLReleaseMutex(pahSSLMutex[i]);
137 : }
138 : CRYPTO_set_id_callback(CPLOpenSSLIdCallback);
139 : CRYPTO_set_locking_callback(CPLOpenSSLLockingFunction);
140 : }
141 : }
142 :
143 : static void CPLOpenSSLCleanup()
144 : {
145 : if (pahSSLMutex)
146 : {
147 : for (int i = 0; i < CRYPTO_num_locks(); i++)
148 : {
149 : CPLDestroyMutex(pahSSLMutex[i]);
150 : }
151 : CPLFree(pahSSLMutex);
152 : pahSSLMutex = nullptr;
153 : CRYPTO_set_id_callback(nullptr);
154 : CRYPTO_set_locking_callback(nullptr);
155 : }
156 : }
157 :
158 : #endif
159 :
160 : #if defined(_WIN32) && defined(HAVE_OPENSSL_CRYPTO)
161 :
162 : /************************************************************************/
163 : /* CPLWindowsCertificateListCleanup() */
164 : /************************************************************************/
165 :
166 : static void CPLWindowsCertificateListCleanup()
167 : {
168 : if (poWindowsCertificateList)
169 : {
170 : for (auto &&pX509 : *poWindowsCertificateList)
171 : {
172 : X509_free(pX509);
173 : }
174 : delete poWindowsCertificateList;
175 : poWindowsCertificateList = nullptr;
176 : }
177 : }
178 :
179 : /************************************************************************/
180 : /* LoadCAPICertificates() */
181 : /************************************************************************/
182 :
183 : static CPLErr LoadCAPICertificates(const char *pszName,
184 : std::vector<X509 *> *poCertificateList)
185 : {
186 : CPLAssert(pszName);
187 : CPLAssert(poCertificateList);
188 :
189 : HCERTSTORE pCertStore = CertOpenSystemStore(
190 : reinterpret_cast<HCRYPTPROV_LEGACY>(nullptr), pszName);
191 : if (pCertStore == nullptr)
192 : {
193 : CPLError(CE_Failure, CPLE_AppDefined,
194 : "CPLLoadCAPICertificates(): Unable open system "
195 : "certificate store %s.",
196 : pszName);
197 : return CE_Failure;
198 : }
199 :
200 : PCCERT_CONTEXT pCertificate =
201 : CertEnumCertificatesInStore(pCertStore, nullptr);
202 : while (pCertificate != nullptr)
203 : {
204 : X509 *pX509 = d2i_X509(
205 : nullptr,
206 : const_cast<unsigned char const **>(&pCertificate->pbCertEncoded),
207 : pCertificate->cbCertEncoded);
208 : if (pX509 == nullptr)
209 : {
210 : CPLError(CE_Warning, CPLE_AppDefined,
211 : "CPLLoadCAPICertificates(): CertEnumCertificatesInStore() "
212 : "returned a null certificate, skipping.");
213 : }
214 : else
215 : {
216 : #ifdef DEBUG_VERBOSE
217 : char szSubject[256] = {0};
218 : CPLString osSubject;
219 : X509_NAME *pName = X509_get_subject_name(pX509);
220 : if (pName)
221 : {
222 : X509_NAME_oneline(pName, szSubject, sizeof(szSubject));
223 : osSubject = szSubject;
224 : }
225 : if (!osSubject.empty())
226 : CPLDebug("HTTP", "SSL Certificate: %s", osSubject.c_str());
227 : #endif
228 : poCertificateList->push_back(pX509);
229 : }
230 : pCertificate = CertEnumCertificatesInStore(pCertStore, pCertificate);
231 : }
232 : CertCloseStore(pCertStore, 0);
233 : return CE_None;
234 : }
235 :
236 : /************************************************************************/
237 : /* CPL_ssl_ctx_callback() */
238 : /************************************************************************/
239 :
240 : // Load certificates from Windows Crypto API store.
241 : static CURLcode CPL_ssl_ctx_callback(CURL *, void *pSSL, void *)
242 : {
243 : SSL_CTX *pSSL_CTX = static_cast<SSL_CTX *>(pSSL);
244 : if (pSSL_CTX == nullptr)
245 : {
246 : CPLError(CE_Failure, CPLE_AppDefined,
247 : "CPL_ssl_ctx_callback(): OpenSSL context pointer is NULL.");
248 : return CURLE_ABORTED_BY_CALLBACK;
249 : }
250 :
251 : static std::mutex goMutex;
252 : {
253 : std::lock_guard<std::mutex> oLock(goMutex);
254 : if (poWindowsCertificateList == nullptr)
255 : {
256 : poWindowsCertificateList = new std::vector<X509 *>();
257 : if (!poWindowsCertificateList)
258 : {
259 : CPLError(CE_Failure, CPLE_AppDefined,
260 : "CPL_ssl_ctx_callback(): Unable to allocate "
261 : "structure to hold certificates.");
262 : return CURLE_FAILED_INIT;
263 : }
264 :
265 : const std::array<const char *, 3> aszStores{
266 : {"CA", "AuthRoot", "ROOT"}};
267 : for (auto &&pszStore : aszStores)
268 : {
269 : if (LoadCAPICertificates(pszStore, poWindowsCertificateList) ==
270 : CE_Failure)
271 : {
272 : CPLError(
273 : CE_Failure, CPLE_AppDefined,
274 : "CPL_ssl_ctx_callback(): Unable to load certificates "
275 : "from '%s' store.",
276 : pszStore);
277 : return CURLE_FAILED_INIT;
278 : }
279 : }
280 :
281 : CPLDebug("HTTP", "Loading %d certificates from Windows store.",
282 : static_cast<int>(poWindowsCertificateList->size()));
283 : }
284 : }
285 :
286 : X509_STORE *pX509Store = SSL_CTX_get_cert_store(pSSL_CTX);
287 : for (X509 *x509 : *poWindowsCertificateList)
288 : X509_STORE_add_cert(pX509Store, x509);
289 :
290 : return CURLE_OK;
291 : }
292 :
293 : #endif // defined(_WIN32) && defined (HAVE_OPENSSL_CRYPTO)
294 :
295 : /************************************************************************/
296 : /* CheckCurlFeatures() */
297 : /************************************************************************/
298 :
299 2241 : static void CheckCurlFeatures()
300 : {
301 4482 : CPLMutexHolder oHolder(&hSessionMapMutex);
302 2241 : if (!bHasCheckVersion)
303 : {
304 6 : const char *pszVersion = curl_version();
305 6 : CPLDebug("HTTP", "%s", pszVersion);
306 6 : bSupportGZip = strstr(pszVersion, "zlib/") != nullptr;
307 6 : bSupportHTTP2 = strstr(curl_version(), "nghttp2/") != nullptr;
308 6 : bHasCheckVersion = true;
309 :
310 6 : curl_version_info_data *data = curl_version_info(CURLVERSION_NOW);
311 6 : if (data->version_num < LIBCURL_VERSION_NUM)
312 : {
313 0 : CPLError(CE_Warning, CPLE_AppDefined,
314 : "GDAL was built against curl %d.%d.%d, but is "
315 : "running against %s. Runtime failure is likely !",
316 : LIBCURL_VERSION_MAJOR, LIBCURL_VERSION_MINOR,
317 : LIBCURL_VERSION_PATCH, data->version);
318 : }
319 6 : else if (data->version_num > LIBCURL_VERSION_NUM)
320 : {
321 0 : CPLDebug("HTTP",
322 : "GDAL was built against curl %d.%d.%d, but is "
323 : "running against %s.",
324 : LIBCURL_VERSION_MAJOR, LIBCURL_VERSION_MINOR,
325 : LIBCURL_VERSION_PATCH, data->version);
326 : }
327 :
328 : #if defined(HAVE_OPENSSL_CRYPTO) && OPENSSL_VERSION_NUMBER < 0x10100000
329 : CPLOpenSSLInit();
330 : #endif
331 : }
332 2241 : }
333 :
334 : /************************************************************************/
335 : /* CPLWriteFct() */
336 : /* */
337 : /* Append incoming text to our collection buffer, reallocating */
338 : /* it larger as needed. */
339 : /************************************************************************/
340 :
341 : class CPLHTTPResultWithLimit
342 : {
343 : public:
344 : CPLHTTPResult *psResult = nullptr;
345 : int nMaxFileSize = 0;
346 : };
347 :
348 12049 : static size_t CPLWriteFct(void *buffer, size_t size, size_t nmemb,
349 : void *reqInfo)
350 :
351 : {
352 12049 : CPLHTTPResultWithLimit *psResultWithLimit =
353 : static_cast<CPLHTTPResultWithLimit *>(reqInfo);
354 12049 : CPLHTTPResult *psResult = psResultWithLimit->psResult;
355 :
356 12049 : int nBytesToWrite = static_cast<int>(nmemb) * static_cast<int>(size);
357 12049 : int nNewSize = psResult->nDataLen + nBytesToWrite + 1;
358 12049 : if (nNewSize > psResult->nDataAlloc)
359 : {
360 1552 : psResult->nDataAlloc = static_cast<int>(nNewSize * 1.25 + 100);
361 : GByte *pabyNewData = static_cast<GByte *>(
362 1552 : VSIRealloc(psResult->pabyData, psResult->nDataAlloc));
363 1552 : if (pabyNewData == nullptr)
364 : {
365 0 : VSIFree(psResult->pabyData);
366 0 : psResult->pabyData = nullptr;
367 0 : psResult->pszErrBuf = CPLStrdup(CPLString().Printf(
368 : "Out of memory allocating %d bytes for HTTP data buffer.",
369 0 : psResult->nDataAlloc));
370 0 : psResult->nDataAlloc = psResult->nDataLen = 0;
371 :
372 0 : return 0;
373 : }
374 1552 : psResult->pabyData = pabyNewData;
375 : }
376 :
377 12049 : memcpy(psResult->pabyData + psResult->nDataLen, buffer, nBytesToWrite);
378 :
379 12049 : psResult->nDataLen += nBytesToWrite;
380 12049 : psResult->pabyData[psResult->nDataLen] = 0;
381 :
382 12049 : if (psResultWithLimit->nMaxFileSize > 0 &&
383 8 : psResult->nDataLen > psResultWithLimit->nMaxFileSize)
384 : {
385 1 : CPLError(CE_Failure, CPLE_AppDefined, "Maximum file size reached");
386 1 : return 0;
387 : }
388 :
389 12048 : return nmemb;
390 : }
391 :
392 : /************************************************************************/
393 : /* CPLHdrWriteFct() */
394 : /************************************************************************/
395 7530 : static size_t CPLHdrWriteFct(void *buffer, size_t size, size_t nmemb,
396 : void *reqInfo)
397 : {
398 7530 : CPLHTTPResult *psResult = static_cast<CPLHTTPResult *>(reqInfo);
399 : // Copy the buffer to a char* and initialize with zeros (zero
400 : // terminate as well).
401 7530 : size_t nBytes = size * nmemb;
402 7530 : char *pszHdr = static_cast<char *>(CPLCalloc(1, nBytes + 1));
403 7530 : memcpy(pszHdr, buffer, nBytes);
404 7530 : size_t nIdx = nBytes - 1;
405 : // Remove end of line characters
406 21208 : while (nIdx > 0 && (pszHdr[nIdx] == '\r' || pszHdr[nIdx] == '\n'))
407 : {
408 13678 : pszHdr[nIdx] = 0;
409 13678 : nIdx--;
410 : }
411 7530 : char *pszKey = nullptr;
412 7530 : const char *pszValue = CPLParseNameValue(pszHdr, &pszKey);
413 7530 : if (pszKey && pszValue)
414 : {
415 5311 : psResult->papszHeaders =
416 5311 : CSLAddNameValue(psResult->papszHeaders, pszKey, pszValue);
417 : }
418 7530 : CPLFree(pszHdr);
419 7530 : CPLFree(pszKey);
420 7530 : return nmemb;
421 : }
422 :
423 : /************************************************************************/
424 : /* CPLHTTPReadFunction() */
425 : /************************************************************************/
426 0 : static size_t CPLHTTPReadFunction(char *buffer, size_t size, size_t nitems,
427 : void *arg)
428 : {
429 0 : return VSIFReadL(buffer, size, nitems, static_cast<VSILFILE *>(arg));
430 : }
431 :
432 : /************************************************************************/
433 : /* CPLHTTPSeekFunction() */
434 : /************************************************************************/
435 0 : static int CPLHTTPSeekFunction(void *arg, curl_off_t offset, int origin)
436 : {
437 0 : if (VSIFSeekL(static_cast<VSILFILE *>(arg), offset, origin) == 0)
438 0 : return CURL_SEEKFUNC_OK;
439 : else
440 0 : return CURL_SEEKFUNC_FAIL;
441 : }
442 :
443 : /************************************************************************/
444 : /* CPLHTTPFreeFunction() */
445 : /************************************************************************/
446 0 : static void CPLHTTPFreeFunction(void *arg)
447 : {
448 0 : VSIFCloseL(static_cast<VSILFILE *>(arg));
449 0 : }
450 :
451 : typedef struct
452 : {
453 : GDALProgressFunc pfnProgress;
454 : void *pProgressArg;
455 : } CurlProcessData, *CurlProcessDataL;
456 :
457 0 : static int NewProcessFunction(void *p, curl_off_t dltotal, curl_off_t dlnow,
458 : curl_off_t ultotal, curl_off_t ulnow)
459 : {
460 0 : CurlProcessDataL pData = static_cast<CurlProcessDataL>(p);
461 0 : if (nullptr != pData && pData->pfnProgress)
462 : {
463 0 : if (dltotal > 0)
464 : {
465 0 : const double dfDone = double(dlnow) / dltotal;
466 0 : return pData->pfnProgress(dfDone, "Downloading ...",
467 : pData->pProgressArg) == TRUE
468 0 : ? 0
469 0 : : 1;
470 : }
471 0 : else if (ultotal > 0)
472 : {
473 0 : const double dfDone = double(ulnow) / ultotal;
474 0 : return pData->pfnProgress(dfDone, "Uploading ...",
475 : pData->pProgressArg) == TRUE
476 0 : ? 0
477 0 : : 1;
478 : }
479 : }
480 0 : return 0;
481 : }
482 :
483 : #endif /* def HAVE_CURL */
484 :
485 : /************************************************************************/
486 : /* CPLHTTPSetDefaultUserAgent() */
487 : /************************************************************************/
488 :
489 : static std::string gosDefaultUserAgent;
490 :
491 : /**
492 : * \brief Set the default user agent.
493 : *
494 : * GDAL core will by default call this method with "GDAL/x.y.z" where x.y.z
495 : * is the GDAL version number (during driver initialization). Applications may
496 : * override it.
497 : *
498 : * @param pszUserAgent String (or nullptr to cancel the default user agent)
499 : *
500 : * @since GDAL 3.7
501 : */
502 1219 : void CPLHTTPSetDefaultUserAgent(const char *pszUserAgent)
503 : {
504 1219 : gosDefaultUserAgent = pszUserAgent ? pszUserAgent : "";
505 1219 : }
506 :
507 : /************************************************************************/
508 : /* CPLHTTPGetOptionsFromEnv() */
509 : /************************************************************************/
510 :
511 : typedef struct
512 : {
513 : const char *pszEnvVar;
514 : const char *pszOptionName;
515 : } TupleEnvVarOptionName;
516 :
517 : constexpr TupleEnvVarOptionName asAssocEnvVarOptionName[] = {
518 : {"GDAL_HTTP_VERSION", "HTTP_VERSION"},
519 : {"GDAL_HTTP_CONNECTTIMEOUT", "CONNECTTIMEOUT"},
520 : {"GDAL_HTTP_TIMEOUT", "TIMEOUT"},
521 : {"GDAL_HTTP_LOW_SPEED_TIME", "LOW_SPEED_TIME"},
522 : {"GDAL_HTTP_LOW_SPEED_LIMIT", "LOW_SPEED_LIMIT"},
523 : {"GDAL_HTTP_USERPWD", "USERPWD"},
524 : {"GDAL_HTTP_PROXY", "PROXY"},
525 : {"GDAL_HTTPS_PROXY", "HTTPS_PROXY"},
526 : {"GDAL_HTTP_PROXYUSERPWD", "PROXYUSERPWD"},
527 : {"GDAL_PROXY_AUTH", "PROXYAUTH"},
528 : {"GDAL_HTTP_NETRC", "NETRC"},
529 : {"GDAL_HTTP_NETRC_FILE", "NETRC_FILE"},
530 : {"GDAL_HTTP_MAX_RETRY", "MAX_RETRY"},
531 : {"GDAL_HTTP_RETRY_DELAY", "RETRY_DELAY"},
532 : {"GDAL_CURL_CA_BUNDLE", "CAINFO"},
533 : {"CURL_CA_BUNDLE", "CAINFO"},
534 : {"SSL_CERT_FILE", "CAINFO"},
535 : {"GDAL_HTTP_HEADER_FILE", "HEADER_FILE"},
536 : {"GDAL_HTTP_CAPATH", "CAPATH"},
537 : {"GDAL_HTTP_SSL_VERIFYSTATUS", "SSL_VERIFYSTATUS"},
538 : {"GDAL_HTTP_USE_CAPI_STORE", "USE_CAPI_STORE"},
539 : {"GDAL_HTTP_HEADERS", "HEADERS"},
540 : {"GDAL_HTTP_HEADER_FILE", "HEADER_FILE"},
541 : {"GDAL_HTTP_AUTH", "HTTPAUTH"},
542 : {"GDAL_GSSAPI_DELEGATION", "GSSAPI_DELEGATION"},
543 : {"GDAL_HTTP_BEARER", "HTTP_BEARER"},
544 : {"GDAL_HTTP_COOKIE", "COOKIE"},
545 : {"GDAL_HTTP_COOKIEFILE", "COOKIEFILE"},
546 : {"GDAL_HTTP_COOKIEJAR", "COOKIEJAR"},
547 : {"GDAL_HTTP_MAX_RETRY", "MAX_RETRY"},
548 : {"GDAL_HTTP_RETRY_DELAY", "RETRY_DELAY"},
549 : {"GDAL_HTTP_TCP_KEEPALIVE", "TCP_KEEPALIVE"},
550 : {"GDAL_HTTP_TCP_KEEPIDLE", "TCP_KEEPIDLE"},
551 : {"GDAL_HTTP_TCP_KEEPINTVL", "TCP_KEEPINTVL"},
552 : };
553 :
554 1152 : char **CPLHTTPGetOptionsFromEnv(const char *pszFilename)
555 : {
556 1152 : char **papszOptions = nullptr;
557 40320 : for (size_t i = 0; i < CPL_ARRAYSIZE(asAssocEnvVarOptionName); ++i)
558 : {
559 : const char *pszVal =
560 : pszFilename
561 78336 : ? VSIGetPathSpecificOption(pszFilename,
562 39168 : asAssocEnvVarOptionName[i].pszEnvVar,
563 : nullptr)
564 0 : : CPLGetConfigOption(asAssocEnvVarOptionName[i].pszEnvVar,
565 39168 : nullptr);
566 39168 : if (pszVal != nullptr)
567 : {
568 129 : papszOptions = CSLSetNameValue(
569 129 : papszOptions, asAssocEnvVarOptionName[i].pszOptionName, pszVal);
570 : }
571 : }
572 1152 : return papszOptions;
573 : }
574 :
575 : /************************************************************************/
576 : /* CPLHTTPGetNewRetryDelay() */
577 : /************************************************************************/
578 :
579 386 : double CPLHTTPGetNewRetryDelay(int response_code, double dfOldDelay,
580 : const char *pszErrBuf, const char *pszCurlError)
581 : {
582 386 : if (response_code == 429 || response_code == 500 ||
583 309 : (response_code >= 502 && response_code <= 504) ||
584 : // S3 sends some client timeout errors as 400 Client Error
585 24 : (response_code == 400 && pszErrBuf &&
586 293 : strstr(pszErrBuf, "RequestTimeout")) ||
587 293 : (pszCurlError && (strstr(pszCurlError, "Connection timed out") ||
588 293 : strstr(pszCurlError, "Operation timed out") ||
589 293 : strstr(pszCurlError, "Connection reset by peer") ||
590 293 : strstr(pszCurlError, "Connection was reset"))))
591 : {
592 : // 'Operation tmied out': seen during some long running operation 'hang'
593 : // no error but no response from server and we are in the cURL loop
594 : // infinitely.
595 :
596 : // 'Connection was reset': was found with Azure: server resets
597 : // connection during TLS handshake (10054 error code). It seems like
598 : // the server process crashed or something forced TCP reset;
599 : // the request succeeds on retry.
600 :
601 : // Use an exponential backoff factor of 2 plus some random jitter
602 : // We don't care about cryptographic quality randomness, hence:
603 : // coverity[dont_call]
604 98 : return dfOldDelay * (2 + rand() * 0.5 / RAND_MAX);
605 : }
606 : else
607 : {
608 288 : return 0;
609 : }
610 : }
611 :
612 : #ifdef HAVE_CURL
613 :
614 : /************************************************************************/
615 : /* CPLHTTPEmitFetchDebug() */
616 : /************************************************************************/
617 :
618 1150 : static void CPLHTTPEmitFetchDebug(const char *pszURL,
619 : const char *pszExtraDebug = "")
620 : {
621 1150 : const char *pszArobase = strchr(pszURL, '@');
622 1150 : const char *pszSlash = strchr(pszURL, '/');
623 1150 : const char *pszColon = (pszSlash) ? strchr(pszSlash, ':') : nullptr;
624 1150 : if (pszArobase != nullptr && pszColon != nullptr &&
625 0 : pszArobase - pszColon > 0)
626 : {
627 : /* http://user:password@www.example.com */
628 0 : char *pszSanitizedURL = CPLStrdup(pszURL);
629 0 : pszSanitizedURL[pszColon - pszURL] = 0;
630 0 : CPLDebug("HTTP", "Fetch(%s:#password#%s%s)", pszSanitizedURL,
631 : pszArobase, pszExtraDebug);
632 0 : CPLFree(pszSanitizedURL);
633 : }
634 : else
635 : {
636 1150 : CPLDebug("HTTP", "Fetch(%s%s)", pszURL, pszExtraDebug);
637 : }
638 1150 : }
639 :
640 : #endif
641 :
642 : #ifdef HAVE_CURL
643 :
644 : /************************************************************************/
645 : /* class CPLHTTPPostFields */
646 : /************************************************************************/
647 :
648 : class CPLHTTPPostFields
649 : {
650 : public:
651 1150 : CPLHTTPPostFields() = default;
652 : CPLHTTPPostFields &operator=(const CPLHTTPPostFields &) = delete;
653 : CPLHTTPPostFields(const CPLHTTPPostFields &) = delete;
654 :
655 1150 : CPLErr Fill(CURL *http_handle, CSLConstList papszOptions)
656 : {
657 : // Fill POST form if present
658 : const char *pszFormFilePath =
659 1150 : CSLFetchNameValue(papszOptions, "FORM_FILE_PATH");
660 : const char *pszParametersCount =
661 1150 : CSLFetchNameValue(papszOptions, "FORM_ITEM_COUNT");
662 :
663 1150 : if (pszFormFilePath != nullptr || pszParametersCount != nullptr)
664 : {
665 2 : mime = curl_mime_init(http_handle);
666 2 : curl_mimepart *mimepart = curl_mime_addpart(mime);
667 2 : if (pszFormFilePath != nullptr)
668 : {
669 : const char *pszFormFileName =
670 1 : CSLFetchNameValue(papszOptions, "FORM_FILE_NAME");
671 1 : const char *pszFilename = CPLGetFilename(pszFormFilePath);
672 1 : if (pszFormFileName == nullptr)
673 : {
674 1 : pszFormFileName = pszFilename;
675 : }
676 :
677 : VSIStatBufL sStat;
678 1 : if (VSIStatL(pszFormFilePath, &sStat) == 0)
679 : {
680 0 : VSILFILE *mime_fp = VSIFOpenL(pszFormFilePath, "rb");
681 0 : if (mime_fp != nullptr)
682 : {
683 0 : curl_mime_name(mimepart, pszFormFileName);
684 0 : CPL_IGNORE_RET_VAL(
685 0 : curl_mime_filename(mimepart, pszFilename));
686 0 : curl_mime_data_cb(
687 : mimepart, sStat.st_size, CPLHTTPReadFunction,
688 : CPLHTTPSeekFunction, CPLHTTPFreeFunction, mime_fp);
689 : }
690 : else
691 : {
692 : osErrMsg = CPLSPrintf("Failed to open file %s",
693 0 : pszFormFilePath);
694 1 : return CE_Failure;
695 : }
696 :
697 0 : CPLDebug("HTTP", "Send file: %s, COPYNAME: %s",
698 : pszFormFilePath, pszFormFileName);
699 : }
700 : else
701 : {
702 : osErrMsg =
703 1 : CPLSPrintf("File '%s' not found", pszFormFilePath);
704 1 : return CE_Failure;
705 : }
706 : }
707 :
708 1 : int nParametersCount = 0;
709 1 : if (pszParametersCount != nullptr)
710 : {
711 1 : nParametersCount = atoi(pszParametersCount);
712 : }
713 :
714 2 : for (int i = 0; i < nParametersCount; ++i)
715 : {
716 2 : const char *pszKey = CSLFetchNameValue(
717 : papszOptions, CPLSPrintf("FORM_KEY_%d", i));
718 2 : const char *pszValue = CSLFetchNameValue(
719 : papszOptions, CPLSPrintf("FORM_VALUE_%d", i));
720 :
721 2 : if (nullptr == pszKey)
722 : {
723 : osErrMsg = CPLSPrintf("Key #%d is not exists. Maybe wrong "
724 : "count of form items",
725 1 : i);
726 1 : return CE_Failure;
727 : }
728 :
729 1 : if (nullptr == pszValue)
730 : {
731 : osErrMsg = CPLSPrintf("Value #%d is not exists. Maybe "
732 : "wrong count of form items",
733 0 : i);
734 0 : return CE_Failure;
735 : }
736 :
737 1 : mimepart = curl_mime_addpart(mime);
738 1 : curl_mime_name(mimepart, pszKey);
739 1 : CPL_IGNORE_RET_VAL(
740 1 : curl_mime_data(mimepart, pszValue, CURL_ZERO_TERMINATED));
741 :
742 1 : CPLDebug("HTTP", "COPYNAME: %s, COPYCONTENTS: %s", pszKey,
743 : pszValue);
744 : }
745 :
746 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_MIMEPOST, mime);
747 : }
748 1148 : return CE_None;
749 : }
750 :
751 1150 : ~CPLHTTPPostFields()
752 1150 : {
753 1150 : if (mime != nullptr)
754 : {
755 2 : curl_mime_free(mime);
756 : }
757 1150 : }
758 :
759 2 : std::string GetErrorMessage() const
760 : {
761 2 : return osErrMsg;
762 : }
763 :
764 : private:
765 : curl_mime *mime = nullptr;
766 : std::string osErrMsg{};
767 : };
768 :
769 : /************************************************************************/
770 : /* CPLHTTPFetchCleanup() */
771 : /************************************************************************/
772 :
773 1150 : static void CPLHTTPFetchCleanup(CURL *http_handle, struct curl_slist *headers,
774 : const char *pszPersistent,
775 : CSLConstList papszOptions)
776 : {
777 1150 : if (CSLFetchNameValue(papszOptions, "POSTFIELDS"))
778 157 : unchecked_curl_easy_setopt(http_handle, CURLOPT_POST, 0);
779 1150 : unchecked_curl_easy_setopt(http_handle, CURLOPT_HTTPHEADER, nullptr);
780 :
781 1150 : if (!pszPersistent)
782 662 : curl_easy_cleanup(http_handle);
783 :
784 1150 : curl_slist_free_all(headers);
785 1150 : }
786 : #endif // HAVE_CURL
787 :
788 : struct CPLHTTPFetchContext
789 : {
790 : std::vector<std::pair<CPLHTTPFetchCallbackFunc, void *>> stack{};
791 : };
792 :
793 : /************************************************************************/
794 : /* GetHTTPFetchContext() */
795 : /************************************************************************/
796 :
797 1340 : static CPLHTTPFetchContext *GetHTTPFetchContext(bool bAlloc)
798 : {
799 1340 : int bError = FALSE;
800 : CPLHTTPFetchContext *psCtx = static_cast<CPLHTTPFetchContext *>(
801 1340 : CPLGetTLSEx(CTLS_HTTPFETCHCALLBACK, &bError));
802 1340 : if (bError)
803 0 : return nullptr;
804 :
805 1340 : if (psCtx == nullptr && bAlloc)
806 : {
807 1 : const auto FreeFunc = [](void *pData)
808 1 : { delete static_cast<CPLHTTPFetchContext *>(pData); };
809 1 : psCtx = new CPLHTTPFetchContext();
810 1 : CPLSetTLSWithFreeFuncEx(CTLS_HTTPFETCHCALLBACK, psCtx, FreeFunc,
811 : &bError);
812 1 : if (bError)
813 : {
814 0 : delete psCtx;
815 0 : psCtx = nullptr;
816 : }
817 : }
818 1340 : return psCtx;
819 : }
820 :
821 : /************************************************************************/
822 : /* CPLHTTPSetFetchCallback() */
823 : /************************************************************************/
824 :
825 : static CPLHTTPFetchCallbackFunc gpsHTTPFetchCallbackFunc = nullptr;
826 : static void *gpHTTPFetchCallbackUserData = nullptr;
827 :
828 : /** Installs an alternate callback to the default implementation of
829 : * CPLHTTPFetchEx().
830 : *
831 : * This callback will be used by all threads, unless contextual callbacks are
832 : * installed with CPLHTTPPushFetchCallback().
833 : *
834 : * It is the responsibility of the caller to make sure this function is not
835 : * called concurrently, or during CPLHTTPFetchEx() execution.
836 : *
837 : * @param pFunc Callback function to be called with CPLHTTPFetchEx() is called
838 : * (or NULL to restore default handler)
839 : * @param pUserData Last argument to provide to the pFunc callback.
840 : *
841 : * @since GDAL 3.2
842 : */
843 2 : void CPLHTTPSetFetchCallback(CPLHTTPFetchCallbackFunc pFunc, void *pUserData)
844 : {
845 2 : gpsHTTPFetchCallbackFunc = pFunc;
846 2 : gpHTTPFetchCallbackUserData = pUserData;
847 2 : }
848 :
849 : /************************************************************************/
850 : /* CPLHTTPPushFetchCallback() */
851 : /************************************************************************/
852 :
853 : /** Installs an alternate callback to the default implementation of
854 : * CPLHTTPFetchEx().
855 : *
856 : * This callback will only be used in the thread where this function has been
857 : * called. It must be un-installed by CPLHTTPPopFetchCallback(), which must also
858 : * be called from the same thread.
859 : *
860 : * @param pFunc Callback function to be called with CPLHTTPFetchEx() is called.
861 : * @param pUserData Last argument to provide to the pFunc callback.
862 : * @return TRUE in case of success.
863 : *
864 : * @since GDAL 3.2
865 : */
866 1 : int CPLHTTPPushFetchCallback(CPLHTTPFetchCallbackFunc pFunc, void *pUserData)
867 : {
868 1 : auto psCtx = GetHTTPFetchContext(true);
869 1 : if (psCtx == nullptr)
870 0 : return false;
871 : psCtx->stack.emplace_back(
872 1 : std::pair<CPLHTTPFetchCallbackFunc, void *>(pFunc, pUserData));
873 1 : return true;
874 : }
875 :
876 : /************************************************************************/
877 : /* CPLHTTPPopFetchCallback() */
878 : /************************************************************************/
879 :
880 : /** Uninstalls a callback set by CPLHTTPPushFetchCallback().
881 : *
882 : * @see CPLHTTPPushFetchCallback()
883 : * @return TRUE in case of success.
884 : * @since GDAL 3.2
885 : */
886 2 : int CPLHTTPPopFetchCallback(void)
887 : {
888 2 : auto psCtx = GetHTTPFetchContext(false);
889 2 : if (psCtx == nullptr || psCtx->stack.empty())
890 : {
891 1 : CPLError(
892 : CE_Failure, CPLE_AppDefined,
893 : "CPLHTTPPushFetchCallback / CPLHTTPPopFetchCallback not balanced");
894 1 : return false;
895 : }
896 : else
897 : {
898 1 : psCtx->stack.pop_back();
899 1 : return true;
900 : }
901 : }
902 :
903 : /************************************************************************/
904 : /* CPLHTTPFetch() */
905 : /************************************************************************/
906 :
907 : // NOTE: when adding an option below, add it in asAssocEnvVarOptionName[]
908 :
909 : // clang-format off
910 : /**
911 : * \brief Fetch a document from an url and return in a string.
912 : *
913 : * @param pszURL valid URL recognized by underlying download library (libcurl)
914 : * @param papszOptions option list as a NULL-terminated array of strings. May be
915 : * NULL. The following options are handled : <ul>
916 : * <li>CONNECTTIMEOUT=val, where
917 : * val is in seconds (possibly with decimals). This is the maximum delay for the
918 : * connection to be established before being aborted (GDAL >= 2.2).</li>
919 : * <li>TIMEOUT=val, where val is in seconds. This is the maximum delay for the
920 : * whole request to complete before being aborted.</li>
921 : * <li>LOW_SPEED_TIME=val,
922 : * where val is in seconds. This is the maximum time where the transfer speed
923 : * should be below the LOW_SPEED_LIMIT (if not specified 1b/s), before the
924 : * transfer to be considered too slow and aborted. (GDAL >= 2.1)</li>
925 : * <li>LOW_SPEED_LIMIT=val, where val is in bytes/second. See LOW_SPEED_TIME.
926 : * Has only effect if LOW_SPEED_TIME is specified too. (GDAL >= 2.1)</li>
927 : * <li>HEADERS=val, where val is an extra header to use when getting a web page.
928 : * For example "Accept: application/x-ogcwkt"</li>
929 : * <li>HEADER_FILE=filename: filename of a text file with "key: value" headers.
930 : * The content of the file is not cached, and thus it is read again before
931 : * issuing each HTTP request. (GDAL >= 2.2)</li>
932 : * <li>HTTPAUTH=[BASIC/NTLM/NEGOTIATE/ANY/ANYSAFE/BEARER] to specify an
933 : * authentication scheme to use.</li>
934 : * <li>USERPWD=userid:password to specify a user and password for
935 : * authentication</li>
936 : * <li>GSSAPI_DELEGATION=[NONE/POLICY/ALWAYS] set allowed
937 : * GSS-API delegation. Relevant only with HTTPAUTH=NEGOTIATE (GDAL >= 3.3).</li>
938 : * <li>HTTP_BEARER=val set OAuth 2.0 Bearer Access Token.
939 : * Relevant only with HTTPAUTH=BEARER (GDAL >= 3.9).</li>
940 : * <li>POSTFIELDS=val, where val is a nul-terminated string to be passed to the
941 : * server with a POST request.</li>
942 : * <li>PROXY=val, to make requests go through a
943 : * proxy server, where val is of the form proxy.server.com:port_number. This
944 : * option affects both HTTP and HTTPS URLs.</li>
945 : * <li>HTTPS_PROXY=val (GDAL
946 : * >= 2.4), the same meaning as PROXY, but this option is taken into account
947 : * only for HTTPS URLs.</li>
948 : * <li>PROXYUSERPWD=val, where val is of the form
949 : * username:password</li>
950 : * <li>PROXYAUTH=[BASIC/NTLM/DIGEST/NEGOTIATE/ANY/ANYSAFE] to
951 : * specify an proxy authentication scheme to use.</li>
952 : * <li>NETRC=[YES/NO] to
953 : * enable or disable use of $HOME/.netrc (or NETRC_FILE), default YES.</li>
954 : * <li>NETRC_FILE=file name to read .netrc info from (GDAL >= 3.7).</li>
955 : * <li>CUSTOMREQUEST=val, where val is GET, PUT, POST, DELETE, etc.. (GDAL
956 : * >= 1.9.0)</li>
957 : * <li>FORM_FILE_NAME=val, where val is upload file name. If this
958 : * option and FORM_FILE_PATH present, request type will set to POST.</li>
959 : * <li>FORM_FILE_PATH=val, where val is upload file path.</li>
960 : * <li>FORM_KEY_0=val...FORM_KEY_N, where val is name of form item.</li>
961 : * <li>FORM_VALUE_0=val...FORM_VALUE_N, where val is value of the form
962 : * item.</li>
963 : * <li>FORM_ITEM_COUNT=val, where val is count of form items.</li>
964 : * <li>COOKIE=val, where val is formatted as COOKIE1=VALUE1; COOKIE2=VALUE2;
965 : * ...</li>
966 : * <li>COOKIEFILE=val, where val is file name to read cookies from
967 : * (GDAL >= 2.4)</li>
968 : * <li>COOKIEJAR=val, where val is file name to store cookies
969 : * to (GDAL >= 2.4)</li>
970 : * <li>MAX_RETRY=val, where val is the maximum number of
971 : * retry attempts if a 429, 502, 503 or 504 HTTP error occurs. Default is 0.
972 : * (GDAL >= 2.0)</li>
973 : * <li>RETRY_DELAY=val, where val is the number of seconds
974 : * between retry attempts. Default is 30. (GDAL >= 2.0)</li>
975 : * <li>MAX_FILE_SIZE=val, where val is a number of bytes (GDAL >= 2.2)</li>
976 : * <li>CAINFO=/path/to/bundle.crt. This is path to Certificate Authority (CA)
977 : * bundle file. By default, it will be looked for in a system location. If
978 : * the CAINFO option is not defined, GDAL will also look in the
979 : * CURL_CA_BUNDLE and SSL_CERT_FILE environment variables respectively
980 : * and use the first one found as the CAINFO value (GDAL >= 2.1.3). The
981 : * GDAL_CURL_CA_BUNDLE environment variable may also be used to set the
982 : * CAINFO value in GDAL >= 3.2.</li>
983 : * <li>HTTP_VERSION=1.0/1.1/2/2TLS (GDAL >= 2.3). Specify HTTP version to use.
984 : * Will default to 1.1 generally (except on some controlled environments,
985 : * like Google Compute Engine VMs, where 2TLS will be the default).
986 : * Support for HTTP/2 requires curl 7.33 or later, built against nghttp2.
987 : * "2TLS" means that HTTP/2 will be attempted for HTTPS connections only.
988 : * Whereas "2" means that HTTP/2 will be attempted for HTTP or HTTPS.</li>
989 : * <li>SSL_VERIFYSTATUS=YES/NO (GDAL >= 2.3, and curl >= 7.41): determines
990 : * whether the status of the server cert using the "Certificate Status Request"
991 : * TLS extension (aka. OCSP stapling) should be checked. If this option is
992 : * enabled but the server does not support the TLS extension, the verification
993 : * will fail. Default to NO.</li>
994 : * <li>USE_CAPI_STORE=YES/NO (GDAL >= 2.3,
995 : * Windows only): whether CA certificates from the Windows certificate store.
996 : * Defaults to NO.</li>
997 : * <li>TCP_KEEPALIVE=YES/NO (GDAL >= 3.6): whether to
998 : * enable TCP keep-alive. Defaults to NO</li> <li>TCP_KEEPIDLE=integer, in
999 : * seconds (GDAL >= 3.6): keep-alive idle time. Defaults to 60. Only taken into
1000 : * account if TCP_KEEPALIVE=YES.</li>
1001 : * <li>TCP_KEEPINTVL=integer, in seconds
1002 : * (GDAL >= 3.6): interval time between keep-alive probes. Defaults to 60. Only
1003 : * taken into account if TCP_KEEPALIVE=YES.</li>
1004 : * <li>USERAGENT=string: value of User-Agent header. Starting with GDAL 3.7,
1005 : * GDAL core sets it by default (during driver initialization) to GDAL/x.y.z
1006 : * where x.y.z is the GDAL version number. Applications may override it with the
1007 : * CPLHTTPSetDefaultUserAgent() function.</li>
1008 : * <li>SSLCERT=filename (GDAL >= 3.7): Filename of the the SSL client certificate.
1009 : * Cf https://curl.se/libcurl/c/CURLOPT_SSLCERT.html</li>
1010 : * <li>SSLCERTTYPE=string (GDAL >= 3.7): Format of the SSL certificate: "PEM"
1011 : * or "DER". Cf https://curl.se/libcurl/c/CURLOPT_SSLCERTTYPE.html</li>
1012 : * <li>SSLKEY=filename (GDAL >= 3.7): Private key file for TLS and SSL client
1013 : * certificate. Cf https://curl.se/libcurl/c/CURLOPT_SSLKEY.html</li>
1014 : * <li>KEYPASSWD=string (GDAL >= 3.7): Passphrase to private key.
1015 : * Cf https://curl.se/libcurl/c/CURLOPT_KEYPASSWD.html</li>
1016 : * </ul>
1017 : *
1018 : * Alternatively, if not defined in the papszOptions arguments, the following
1019 : * CONNECTTIMEOUT, TIMEOUT,
1020 : * LOW_SPEED_TIME, LOW_SPEED_LIMIT, USERPWD, PROXY, HTTPS_PROXY, PROXYUSERPWD,
1021 : * PROXYAUTH, NETRC, NETRC_FILE, MAX_RETRY and RETRY_DELAY, HEADER_FILE, HTTP_VERSION,
1022 : * SSL_VERIFYSTATUS, USE_CAPI_STORE, GSSAPI_DELEGATION, HTTP_BEARER, TCP_KEEPALIVE,
1023 : * TCP_KEEPIDLE, TCP_KEEPINTVL, USERAGENT, SSLCERT, SSLCERTTYPE, SSLKEY,
1024 : * KEYPASSWD values are searched in the configuration options
1025 : * respectively named GDAL_HTTP_CONNECTTIMEOUT, GDAL_HTTP_TIMEOUT,
1026 : * GDAL_HTTP_LOW_SPEED_TIME, GDAL_HTTP_LOW_SPEED_LIMIT, GDAL_HTTP_USERPWD,
1027 : * GDAL_HTTP_PROXY, GDAL_HTTPS_PROXY, GDAL_HTTP_PROXYUSERPWD, GDAL_PROXY_AUTH,
1028 : * GDAL_HTTP_NETRC, GDAL_HTTP_NETC_FILE, GDAL_HTTP_MAX_RETRY, GDAL_HTTP_RETRY_DELAY,
1029 : * GDAL_HTTP_HEADER_FILE, GDAL_HTTP_VERSION, GDAL_HTTP_SSL_VERIFYSTATUS,
1030 : * GDAL_HTTP_USE_CAPI_STORE, GDAL_GSSAPI_DELEGATION, GDAL_HTTP_BEARER,
1031 : * GDAL_HTTP_TCP_KEEPALIVE, GDAL_HTTP_TCP_KEEPIDLE, GDAL_HTTP_TCP_KEEPINTVL,
1032 : * GDAL_HTTP_USERAGENT, GDAL_HTTP_SSLCERT, GDAL_HTTP_SSLCERTTYPE,
1033 : * GDAL_HTTP_SSLKEY and GDAL_HTTP_KEYPASSWD.
1034 : *
1035 : * Starting with GDAL 3.6, the GDAL_HTTP_HEADERS configuration option can also
1036 : * be used to specify a comma separated list of key: value pairs. This is an
1037 : * alternative to the GDAL_HTTP_HEADER_FILE mechanism. If a comma or a
1038 : * double-quote character is needed in the value, then the key: value pair must
1039 : * be enclosed in double-quote characters. In that situation, backslash and
1040 : * double quote character must be backslash-escaped. e.g GDAL_HTTP_HEADERS=Foo:
1041 : * Bar,"Baz: escaped backslash \\, escaped double-quote \", end of
1042 : * value",Another: Header
1043 : *
1044 : * Starting with GDAL 3.7, the above configuration options can also be specified
1045 : * as path-specific options with VSISetPathSpecificOption().
1046 : *
1047 : * @return a CPLHTTPResult* structure that must be freed by
1048 : * CPLHTTPDestroyResult(), or NULL if libcurl support is disabled
1049 : */
1050 : // clang-format on
1051 2105 : CPLHTTPResult *CPLHTTPFetch(const char *pszURL, CSLConstList papszOptions)
1052 : {
1053 2105 : return CPLHTTPFetchEx(pszURL, papszOptions, nullptr, nullptr, nullptr,
1054 2105 : nullptr);
1055 : }
1056 :
1057 : /**
1058 : * Fetch a document from an url and return in a string.
1059 : * @param pszURL Url to fetch document from web.
1060 : * @param papszOptions Option list as a NULL-terminated array of strings.
1061 : * Available keys see in CPLHTTPFetch.
1062 : * @param pfnProgress Callback for reporting algorithm progress matching the
1063 : * GDALProgressFunc() semantics. May be NULL.
1064 : * @param pProgressArg Callback argument passed to pfnProgress.
1065 : * @param pfnWrite Write function pointer matching the CPLHTTPWriteFunc()
1066 : * semantics. May be NULL.
1067 : * @param pWriteArg Argument which will pass to a write function.
1068 : * @return A CPLHTTPResult* structure that must be freed by
1069 : * CPLHTTPDestroyResult(), or NULL if libcurl support is disabled.
1070 : */
1071 2111 : CPLHTTPResult *CPLHTTPFetchEx(const char *pszURL, CSLConstList papszOptions,
1072 : GDALProgressFunc pfnProgress, void *pProgressArg,
1073 : CPLHTTPFetchWriteFunc pfnWrite, void *pWriteArg)
1074 :
1075 : {
1076 2910 : if (STARTS_WITH(pszURL, "/vsimem/") &&
1077 : // Disabled by default for potential security issues.
1078 799 : CPLTestBool(CPLGetConfigOption("CPL_CURL_ENABLE_VSIMEM", "FALSE")))
1079 : {
1080 774 : CPLString osURL(pszURL);
1081 : const char *pszCustomRequest =
1082 774 : CSLFetchNameValue(papszOptions, "CUSTOMREQUEST");
1083 774 : if (pszCustomRequest != nullptr)
1084 : {
1085 5 : osURL += "&CUSTOMREQUEST=";
1086 5 : osURL += pszCustomRequest;
1087 : }
1088 774 : const char *pszUserPwd = CSLFetchNameValue(papszOptions, "USERPWD");
1089 774 : if (pszUserPwd != nullptr)
1090 : {
1091 1 : osURL += "&USERPWD=";
1092 1 : osURL += pszUserPwd;
1093 : }
1094 774 : const char *pszPost = CSLFetchNameValue(papszOptions, "POSTFIELDS");
1095 774 : if (pszPost != nullptr) // Hack: We append post content to filename.
1096 : {
1097 281 : osURL += "&POSTFIELDS=";
1098 281 : osURL += pszPost;
1099 : }
1100 774 : const char *pszHeaders = CSLFetchNameValue(papszOptions, "HEADERS");
1101 906 : if (pszHeaders != nullptr &&
1102 132 : CPLTestBool(
1103 : CPLGetConfigOption("CPL_CURL_VSIMEM_PRINT_HEADERS", "FALSE")))
1104 : {
1105 5 : osURL += "&HEADERS=";
1106 5 : osURL += pszHeaders;
1107 : }
1108 774 : vsi_l_offset nLength = 0;
1109 : CPLHTTPResult *psResult =
1110 774 : static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
1111 774 : GByte *pabyData = VSIGetMemFileBuffer(osURL, &nLength, FALSE);
1112 774 : if (pabyData == nullptr)
1113 : {
1114 119 : CPLDebug("HTTP", "Cannot find %s", osURL.c_str());
1115 119 : psResult->nStatus = 1;
1116 119 : psResult->pszErrBuf =
1117 119 : CPLStrdup(CPLSPrintf("HTTP error code : %d", 404));
1118 119 : CPLError(CE_Failure, CPLE_AppDefined, "%s", psResult->pszErrBuf);
1119 : }
1120 655 : else if (nLength != 0)
1121 : {
1122 613 : psResult->nDataLen = static_cast<int>(nLength);
1123 613 : psResult->pabyData = static_cast<GByte *>(
1124 613 : CPLMalloc(static_cast<size_t>(nLength) + 1));
1125 613 : memcpy(psResult->pabyData, pabyData, static_cast<size_t>(nLength));
1126 613 : psResult->pabyData[static_cast<size_t>(nLength)] = 0;
1127 : }
1128 :
1129 774 : if (psResult->pabyData != nullptr &&
1130 613 : STARTS_WITH(reinterpret_cast<char *>(psResult->pabyData),
1131 : "Content-Type: "))
1132 : {
1133 7 : const char *pszContentType =
1134 7 : reinterpret_cast<char *>(psResult->pabyData) +
1135 : strlen("Content-type: ");
1136 7 : const char *pszEOL = strchr(pszContentType, '\r');
1137 7 : if (pszEOL)
1138 7 : pszEOL = strchr(pszContentType, '\n');
1139 7 : if (pszEOL)
1140 : {
1141 7 : size_t nContentLength = pszEOL - pszContentType;
1142 7 : psResult->pszContentType =
1143 7 : static_cast<char *>(CPLMalloc(nContentLength + 1));
1144 7 : memcpy(psResult->pszContentType, pszContentType,
1145 : nContentLength);
1146 7 : psResult->pszContentType[nContentLength] = 0;
1147 : }
1148 : }
1149 :
1150 774 : return psResult;
1151 : }
1152 :
1153 : // Try to user alternate network layer if set.
1154 1337 : auto pCtx = GetHTTPFetchContext(false);
1155 1337 : if (pCtx)
1156 : {
1157 3 : for (size_t i = pCtx->stack.size(); i > 0;)
1158 : {
1159 1 : --i;
1160 1 : const auto &cbk = pCtx->stack[i];
1161 1 : auto cbkFunc = cbk.first;
1162 1 : auto pUserData = cbk.second;
1163 1 : auto res = cbkFunc(pszURL, papszOptions, pfnProgress, pProgressArg,
1164 : pfnWrite, pWriteArg, pUserData);
1165 1 : if (res)
1166 : {
1167 1 : if (CSLFetchNameValue(papszOptions, "CLOSE_PERSISTENT"))
1168 : {
1169 0 : CPLHTTPDestroyResult(res);
1170 0 : res = nullptr;
1171 : }
1172 1 : return res;
1173 : }
1174 : }
1175 : }
1176 :
1177 1336 : if (gpsHTTPFetchCallbackFunc)
1178 : {
1179 1 : auto res = gpsHTTPFetchCallbackFunc(pszURL, papszOptions, pfnProgress,
1180 : pProgressArg, pfnWrite, pWriteArg,
1181 : gpHTTPFetchCallbackUserData);
1182 1 : if (res)
1183 : {
1184 1 : if (CSLFetchNameValue(papszOptions, "CLOSE_PERSISTENT"))
1185 : {
1186 0 : CPLHTTPDestroyResult(res);
1187 0 : res = nullptr;
1188 : }
1189 1 : return res;
1190 : }
1191 : }
1192 :
1193 : #ifndef HAVE_CURL
1194 : CPLError(CE_Failure, CPLE_NotSupported,
1195 : "GDAL/OGR not compiled with libcurl support, "
1196 : "remote requests not supported.");
1197 : return nullptr;
1198 : #else
1199 :
1200 : /* -------------------------------------------------------------------- */
1201 : /* Are we using a persistent named session? If so, search for */
1202 : /* or create it. */
1203 : /* */
1204 : /* Currently this code does not attempt to protect against */
1205 : /* multiple threads asking for the same named session. If that */
1206 : /* occurs it will be in use in multiple threads at once, which */
1207 : /* will lead to potential crashes in libcurl. */
1208 : /* -------------------------------------------------------------------- */
1209 1335 : CURL *http_handle = nullptr;
1210 :
1211 1335 : const char *pszPersistent = CSLFetchNameValue(papszOptions, "PERSISTENT");
1212 : const char *pszClosePersistent =
1213 1335 : CSLFetchNameValue(papszOptions, "CLOSE_PERSISTENT");
1214 1335 : if (pszPersistent)
1215 : {
1216 976 : CPLString osSessionName = pszPersistent;
1217 488 : CPLMutexHolder oHolder(&hSessionMapMutex);
1218 :
1219 488 : if (poSessionMap == nullptr)
1220 132 : poSessionMap = new std::map<CPLString, CURL *>;
1221 488 : if (poSessionMap->count(osSessionName) == 0)
1222 : {
1223 155 : (*poSessionMap)[osSessionName] = curl_easy_init();
1224 155 : CPLDebug("HTTP", "Establish persistent session named '%s'.",
1225 : osSessionName.c_str());
1226 : }
1227 :
1228 488 : http_handle = (*poSessionMap)[osSessionName];
1229 : }
1230 : /* -------------------------------------------------------------------- */
1231 : /* Are we requested to close a persistent named session? */
1232 : /* -------------------------------------------------------------------- */
1233 847 : else if (pszClosePersistent)
1234 : {
1235 370 : CPLString osSessionName = pszClosePersistent;
1236 185 : CPLMutexHolder oHolder(&hSessionMapMutex);
1237 :
1238 185 : if (poSessionMap)
1239 : {
1240 : std::map<CPLString, CURL *>::iterator oIter =
1241 155 : poSessionMap->find(osSessionName);
1242 155 : if (oIter != poSessionMap->end())
1243 : {
1244 155 : curl_easy_cleanup(oIter->second);
1245 155 : poSessionMap->erase(oIter);
1246 155 : if (poSessionMap->empty())
1247 : {
1248 132 : delete poSessionMap;
1249 132 : poSessionMap = nullptr;
1250 : }
1251 155 : CPLDebug("HTTP", "Ended persistent session named '%s'.",
1252 : osSessionName.c_str());
1253 : }
1254 : else
1255 : {
1256 0 : CPLDebug("HTTP",
1257 : "Could not find persistent session named '%s'.",
1258 : osSessionName.c_str());
1259 : }
1260 : }
1261 :
1262 185 : return nullptr;
1263 : }
1264 : else
1265 662 : http_handle = curl_easy_init();
1266 :
1267 : /* -------------------------------------------------------------------- */
1268 : /* Setup the request. */
1269 : /* -------------------------------------------------------------------- */
1270 1150 : char szCurlErrBuf[CURL_ERROR_SIZE + 1] = {};
1271 :
1272 1150 : CPLHTTPEmitFetchDebug(pszURL);
1273 :
1274 : CPLHTTPResult *psResult =
1275 1150 : static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
1276 :
1277 : struct curl_slist *headers = reinterpret_cast<struct curl_slist *>(
1278 1150 : CPLHTTPSetOptions(http_handle, pszURL, papszOptions));
1279 1150 : if (headers != nullptr)
1280 591 : unchecked_curl_easy_setopt(http_handle, CURLOPT_HTTPHEADER, headers);
1281 :
1282 : // Are we making a head request.
1283 1150 : const char *pszNoBody = nullptr;
1284 1150 : if ((pszNoBody = CSLFetchNameValue(papszOptions, "NO_BODY")) != nullptr)
1285 : {
1286 0 : if (CPLTestBool(pszNoBody))
1287 : {
1288 0 : CPLDebug("HTTP", "HEAD Request: %s", pszURL);
1289 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_NOBODY, 1L);
1290 : }
1291 : }
1292 :
1293 : // Capture response headers.
1294 1150 : unchecked_curl_easy_setopt(http_handle, CURLOPT_HEADERDATA, psResult);
1295 1150 : unchecked_curl_easy_setopt(http_handle, CURLOPT_HEADERFUNCTION,
1296 : CPLHdrWriteFct);
1297 :
1298 1150 : CPLHTTPResultWithLimit sResultWithLimit;
1299 1150 : if (nullptr == pfnWrite)
1300 : {
1301 1149 : pfnWrite = CPLWriteFct;
1302 :
1303 1149 : sResultWithLimit.psResult = psResult;
1304 1149 : sResultWithLimit.nMaxFileSize = 0;
1305 : const char *pszMaxFileSize =
1306 1149 : CSLFetchNameValue(papszOptions, "MAX_FILE_SIZE");
1307 1149 : if (pszMaxFileSize != nullptr)
1308 : {
1309 10 : sResultWithLimit.nMaxFileSize = atoi(pszMaxFileSize);
1310 : // Only useful if size is returned by server before actual download.
1311 10 : unchecked_curl_easy_setopt(http_handle, CURLOPT_MAXFILESIZE,
1312 : sResultWithLimit.nMaxFileSize);
1313 : }
1314 1149 : pWriteArg = &sResultWithLimit;
1315 : }
1316 :
1317 1150 : unchecked_curl_easy_setopt(http_handle, CURLOPT_WRITEDATA, pWriteArg);
1318 1150 : unchecked_curl_easy_setopt(http_handle, CURLOPT_WRITEFUNCTION, pfnWrite);
1319 :
1320 1150 : CurlProcessData stProcessData = {pfnProgress, pProgressArg};
1321 1150 : if (nullptr != pfnProgress)
1322 : {
1323 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_XFERINFOFUNCTION,
1324 : NewProcessFunction);
1325 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_XFERINFODATA,
1326 : &stProcessData);
1327 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_NOPROGRESS, 0L);
1328 : }
1329 :
1330 1150 : szCurlErrBuf[0] = '\0';
1331 :
1332 1150 : unchecked_curl_easy_setopt(http_handle, CURLOPT_ERRORBUFFER, szCurlErrBuf);
1333 :
1334 1150 : bool bGZipRequested = false;
1335 1150 : if (bSupportGZip && CPLTestBool(CPLGetConfigOption("CPL_CURL_GZIP", "YES")))
1336 : {
1337 1150 : bGZipRequested = true;
1338 1150 : unchecked_curl_easy_setopt(http_handle, CURLOPT_ENCODING, "gzip");
1339 : }
1340 :
1341 2300 : CPLHTTPPostFields oPostFields;
1342 1150 : if (oPostFields.Fill(http_handle, papszOptions) != CE_None)
1343 : {
1344 2 : psResult->nStatus = 34; // CURLE_HTTP_POST_ERROR
1345 2 : psResult->pszErrBuf = CPLStrdup(oPostFields.GetErrorMessage().c_str());
1346 2 : CPLError(CE_Failure, CPLE_AppDefined, "%s", psResult->pszErrBuf);
1347 2 : CPLHTTPFetchCleanup(http_handle, headers, pszPersistent, papszOptions);
1348 2 : return psResult;
1349 : }
1350 :
1351 : /* -------------------------------------------------------------------- */
1352 : /* If 429, 502, 503 or 504 status code retry this HTTP call until max
1353 : */
1354 : /* retry has been reached */
1355 : /* -------------------------------------------------------------------- */
1356 1148 : const char *pszRetryDelay = CSLFetchNameValue(papszOptions, "RETRY_DELAY");
1357 1148 : if (pszRetryDelay == nullptr)
1358 1148 : pszRetryDelay = CPLGetConfigOption(
1359 : "GDAL_HTTP_RETRY_DELAY", CPLSPrintf("%f", CPL_HTTP_RETRY_DELAY));
1360 1148 : const char *pszMaxRetries = CSLFetchNameValue(papszOptions, "MAX_RETRY");
1361 1148 : if (pszMaxRetries == nullptr)
1362 1084 : pszMaxRetries = CPLGetConfigOption(
1363 : "GDAL_HTTP_MAX_RETRY", CPLSPrintf("%d", CPL_HTTP_MAX_RETRY));
1364 : // coverity[tainted_data]
1365 1148 : double dfRetryDelaySecs = CPLAtof(pszRetryDelay);
1366 1148 : int nMaxRetries = atoi(pszMaxRetries);
1367 1148 : int nRetryCount = 0;
1368 :
1369 : while (true)
1370 : {
1371 : /* --------------------------------------------------------------------
1372 : */
1373 : /* Execute the request, waiting for results. */
1374 : /* --------------------------------------------------------------------
1375 : */
1376 1148 : void *old_handler = CPLHTTPIgnoreSigPipe();
1377 1148 : psResult->nStatus = static_cast<int>(curl_easy_perform(http_handle));
1378 1148 : CPLHTTPRestoreSigPipeHandler(old_handler);
1379 :
1380 : /* --------------------------------------------------------------------
1381 : */
1382 : /* Fetch content-type if possible. */
1383 : /* --------------------------------------------------------------------
1384 : */
1385 1148 : psResult->pszContentType = nullptr;
1386 1148 : curl_easy_getinfo(http_handle, CURLINFO_CONTENT_TYPE,
1387 : &(psResult->pszContentType));
1388 1148 : if (psResult->pszContentType != nullptr)
1389 556 : psResult->pszContentType = CPLStrdup(psResult->pszContentType);
1390 :
1391 1148 : long response_code = 0;
1392 1148 : curl_easy_getinfo(http_handle, CURLINFO_RESPONSE_CODE, &response_code);
1393 1148 : if (response_code != 200)
1394 : {
1395 203 : const double dfNewRetryDelay = CPLHTTPGetNewRetryDelay(
1396 : static_cast<int>(response_code), dfRetryDelaySecs,
1397 203 : reinterpret_cast<const char *>(psResult->pabyData),
1398 : szCurlErrBuf);
1399 203 : if (dfNewRetryDelay > 0 && nRetryCount < nMaxRetries)
1400 : {
1401 0 : CPLError(CE_Warning, CPLE_AppDefined,
1402 : "HTTP error code: %d - %s. "
1403 : "Retrying again in %.1f secs",
1404 : static_cast<int>(response_code), pszURL,
1405 : dfRetryDelaySecs);
1406 0 : CPLSleep(dfRetryDelaySecs);
1407 0 : dfRetryDelaySecs = dfNewRetryDelay;
1408 0 : nRetryCount++;
1409 :
1410 0 : CPLFree(psResult->pszContentType);
1411 0 : psResult->pszContentType = nullptr;
1412 0 : CSLDestroy(psResult->papszHeaders);
1413 0 : psResult->papszHeaders = nullptr;
1414 0 : CPLFree(psResult->pabyData);
1415 0 : psResult->pabyData = nullptr;
1416 0 : psResult->nDataLen = 0;
1417 0 : psResult->nDataAlloc = 0;
1418 :
1419 0 : continue;
1420 : }
1421 : }
1422 :
1423 : /* --------------------------------------------------------------------
1424 : */
1425 : /* Have we encountered some sort of error? */
1426 : /* --------------------------------------------------------------------
1427 : */
1428 1148 : if (strlen(szCurlErrBuf) > 0)
1429 : {
1430 7 : bool bSkipError = false;
1431 : const char *pszContentLength =
1432 7 : CSLFetchNameValue(psResult->papszHeaders, "Content-Length");
1433 : // Some servers such as
1434 : // http://115.113.193.14/cgi-bin/world/qgis_mapserv.fcgi?VERSION=1.1.1&SERVICE=WMS&REQUEST=GetCapabilities
1435 : // invalidly return Content-Length as the uncompressed size, with
1436 : // makes curl to wait for more data and time-out finally. If we got
1437 : // the expected data size, then we don't emit an error but turn off
1438 : // GZip requests.
1439 7 : if (bGZipRequested &&
1440 7 : strstr(szCurlErrBuf, "transfer closed with") &&
1441 0 : strstr(szCurlErrBuf, "bytes remaining to read"))
1442 : {
1443 0 : if (pszContentLength && psResult->nDataLen != 0 &&
1444 0 : atoi(pszContentLength) == psResult->nDataLen)
1445 : {
1446 : const char *pszCurlGZIPOption =
1447 0 : CPLGetConfigOption("CPL_CURL_GZIP", nullptr);
1448 0 : if (pszCurlGZIPOption == nullptr)
1449 : {
1450 0 : CPLSetConfigOption("CPL_CURL_GZIP", "NO");
1451 0 : CPLDebug("HTTP",
1452 : "Disabling CPL_CURL_GZIP, "
1453 : "because %s doesn't support it properly",
1454 : pszURL);
1455 : }
1456 0 : psResult->nStatus = 0;
1457 0 : bSkipError = true;
1458 0 : }
1459 : }
1460 :
1461 : // Ignore SSL errors about non-properly terminated connection,
1462 : // often due to HTTP proxies
1463 7 : else if (pszContentLength == nullptr &&
1464 : // Cf https://github.com/curl/curl/pull/3148
1465 7 : (strstr(szCurlErrBuf,
1466 : "GnuTLS recv error (-110): The TLS connection was "
1467 7 : "non-properly terminated") != nullptr ||
1468 : // Cf https://github.com/curl/curl/issues/9024
1469 7 : strstr(szCurlErrBuf,
1470 : "SSL_read: error:0A000126:SSL "
1471 : "routines::unexpected eof while reading") !=
1472 : nullptr))
1473 : {
1474 0 : psResult->nStatus = 0;
1475 0 : bSkipError = true;
1476 : }
1477 7 : else if (CPLTestBool(
1478 : CPLGetConfigOption("CPL_CURL_IGNORE_ERROR", "NO")))
1479 : {
1480 0 : psResult->nStatus = 0;
1481 0 : bSkipError = true;
1482 : }
1483 :
1484 7 : if (!bSkipError)
1485 : {
1486 7 : psResult->pszErrBuf = CPLStrdup(szCurlErrBuf);
1487 7 : if (psResult->nDataLen > 0)
1488 : {
1489 1 : CPLError(CE_Failure, CPLE_AppDefined,
1490 : "%s. You may set the CPL_CURL_IGNORE_ERROR "
1491 : "configuration option to YES to try to ignore it.",
1492 : szCurlErrBuf);
1493 : }
1494 : else
1495 : {
1496 6 : CPLError(CE_Failure, CPLE_AppDefined, "%s", szCurlErrBuf);
1497 : }
1498 : }
1499 : }
1500 : else
1501 : {
1502 1141 : if (response_code >= 400 && response_code < 600)
1503 : {
1504 193 : psResult->pszErrBuf = CPLStrdup(CPLSPrintf(
1505 : "HTTP error code : %d", static_cast<int>(response_code)));
1506 193 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
1507 : psResult->pszErrBuf);
1508 : }
1509 : }
1510 1148 : break;
1511 0 : }
1512 :
1513 1148 : CPLHTTPFetchCleanup(http_handle, headers, pszPersistent, papszOptions);
1514 :
1515 1148 : return psResult;
1516 : #endif /* def HAVE_CURL */
1517 : }
1518 :
1519 : #ifdef HAVE_CURL
1520 : /************************************************************************/
1521 : /* CPLMultiPerformWait() */
1522 : /************************************************************************/
1523 :
1524 1853 : bool CPLMultiPerformWait(void *hCurlMultiHandleIn, int & /*repeats*/)
1525 : {
1526 1853 : CURLM *hCurlMultiHandle = static_cast<CURLM *>(hCurlMultiHandleIn);
1527 :
1528 : // Wait for events on the sockets
1529 :
1530 : // Using curl_multi_poll() is preferred to avoid hitting the 1024 file
1531 : // descriptor limit
1532 :
1533 1853 : int numfds = 0;
1534 1853 : if (curl_multi_poll(hCurlMultiHandle, nullptr, 0, 1000, &numfds) !=
1535 : CURLM_OK)
1536 : {
1537 0 : CPLError(CE_Failure, CPLE_AppDefined, "curl_multi_poll() failed");
1538 0 : return false;
1539 : }
1540 1853 : return true;
1541 : }
1542 :
1543 : class CPLHTTPErrorBuffer
1544 : {
1545 : public:
1546 : char szBuffer[CURL_ERROR_SIZE + 1];
1547 :
1548 0 : CPLHTTPErrorBuffer()
1549 0 : {
1550 0 : szBuffer[0] = '\0';
1551 0 : }
1552 : };
1553 :
1554 : #endif // HAVE_CURL
1555 :
1556 : /************************************************************************/
1557 : /* CPLHTTPMultiFetch() */
1558 : /************************************************************************/
1559 :
1560 : /**
1561 : * \brief Fetch several documents at once
1562 : *
1563 : * @param papszURL array of valid URLs recognized by underlying download library
1564 : * (libcurl)
1565 : * @param nURLCount number of URLs of papszURL
1566 : * @param nMaxSimultaneous maximum number of downloads to issue simultaneously.
1567 : * Any negative or zer value means unlimited.
1568 : * @param papszOptions option list as a NULL-terminated array of strings. May be
1569 : * NULL. Refer to CPLHTTPFetch() for valid options.
1570 : * @return an array of CPLHTTPResult* structures that must be freed by
1571 : * CPLHTTPDestroyMultiResult() or NULL if libcurl support is disabled
1572 : *
1573 : * @since GDAL 2.3
1574 : */
1575 0 : CPLHTTPResult **CPLHTTPMultiFetch(const char *const *papszURL, int nURLCount,
1576 : int nMaxSimultaneous,
1577 : CSLConstList papszOptions)
1578 : {
1579 : #ifndef HAVE_CURL
1580 : (void)papszURL;
1581 : (void)nURLCount;
1582 : (void)nMaxSimultaneous;
1583 : (void)papszOptions;
1584 :
1585 : CPLError(CE_Failure, CPLE_NotSupported,
1586 : "GDAL/OGR not compiled with libcurl support, "
1587 : "remote requests not supported.");
1588 : return nullptr;
1589 : #else /* def HAVE_CURL */
1590 :
1591 : /* -------------------------------------------------------------------- */
1592 : /* Are we using a persistent named session? If so, search for */
1593 : /* or create it. */
1594 : /* */
1595 : /* Currently this code does not attempt to protect against */
1596 : /* multiple threads asking for the same named session. If that */
1597 : /* occurs it will be in use in multiple threads at once, which */
1598 : /* will lead to potential crashes in libcurl. */
1599 : /* -------------------------------------------------------------------- */
1600 0 : CURLM *hCurlMultiHandle = nullptr;
1601 :
1602 0 : const char *pszPersistent = CSLFetchNameValue(papszOptions, "PERSISTENT");
1603 : const char *pszClosePersistent =
1604 0 : CSLFetchNameValue(papszOptions, "CLOSE_PERSISTENT");
1605 0 : if (pszPersistent)
1606 : {
1607 0 : CPLString osSessionName = pszPersistent;
1608 0 : CPLMutexHolder oHolder(&hSessionMapMutex);
1609 :
1610 0 : if (poSessionMultiMap == nullptr)
1611 0 : poSessionMultiMap = new std::map<CPLString, CURLM *>;
1612 0 : if (poSessionMultiMap->count(osSessionName) == 0)
1613 : {
1614 0 : (*poSessionMultiMap)[osSessionName] = curl_multi_init();
1615 0 : CPLDebug("HTTP", "Establish persistent session named '%s'.",
1616 : osSessionName.c_str());
1617 : }
1618 :
1619 0 : hCurlMultiHandle = (*poSessionMultiMap)[osSessionName];
1620 : }
1621 : /* -------------------------------------------------------------------- */
1622 : /* Are we requested to close a persistent named session? */
1623 : /* -------------------------------------------------------------------- */
1624 0 : else if (pszClosePersistent)
1625 : {
1626 0 : CPLString osSessionName = pszClosePersistent;
1627 0 : CPLMutexHolder oHolder(&hSessionMapMutex);
1628 :
1629 0 : if (poSessionMultiMap)
1630 : {
1631 0 : auto oIter = poSessionMultiMap->find(osSessionName);
1632 0 : if (oIter != poSessionMultiMap->end())
1633 : {
1634 0 : VSICURLMultiCleanup(oIter->second);
1635 0 : poSessionMultiMap->erase(oIter);
1636 0 : if (poSessionMultiMap->empty())
1637 : {
1638 0 : delete poSessionMultiMap;
1639 0 : poSessionMultiMap = nullptr;
1640 : }
1641 0 : CPLDebug("HTTP", "Ended persistent session named '%s'.",
1642 : osSessionName.c_str());
1643 : }
1644 : else
1645 : {
1646 0 : CPLDebug("HTTP",
1647 : "Could not find persistent session named '%s'.",
1648 : osSessionName.c_str());
1649 : }
1650 : }
1651 :
1652 0 : return nullptr;
1653 : }
1654 : else
1655 : {
1656 0 : hCurlMultiHandle = curl_multi_init();
1657 : }
1658 :
1659 : CPLHTTPResult **papsResults = static_cast<CPLHTTPResult **>(
1660 0 : CPLCalloc(nURLCount, sizeof(CPLHTTPResult *)));
1661 0 : std::vector<CURL *> asHandles;
1662 0 : std::vector<CPLHTTPResultWithLimit> asResults;
1663 0 : asResults.resize(nURLCount);
1664 0 : std::vector<struct curl_slist *> aHeaders;
1665 0 : aHeaders.resize(nURLCount);
1666 0 : std::vector<CPLHTTPErrorBuffer> asErrorBuffers;
1667 0 : asErrorBuffers.resize(nURLCount);
1668 :
1669 0 : for (int i = 0; i < nURLCount; i++)
1670 : {
1671 0 : papsResults[i] =
1672 0 : static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
1673 :
1674 0 : const char *pszURL = papszURL[i];
1675 0 : CURL *http_handle = curl_easy_init();
1676 :
1677 0 : aHeaders[i] = reinterpret_cast<struct curl_slist *>(
1678 0 : CPLHTTPSetOptions(http_handle, pszURL, papszOptions));
1679 :
1680 : // Set Headers.
1681 0 : const char *pszHeaders = CSLFetchNameValue(papszOptions, "HEADERS");
1682 0 : if (pszHeaders != nullptr)
1683 : {
1684 : char **papszTokensHeaders =
1685 0 : CSLTokenizeString2(pszHeaders, "\r\n", 0);
1686 0 : for (int j = 0; papszTokensHeaders[j] != nullptr; ++j)
1687 0 : aHeaders[i] =
1688 0 : curl_slist_append(aHeaders[i], papszTokensHeaders[j]);
1689 0 : CSLDestroy(papszTokensHeaders);
1690 : }
1691 :
1692 0 : if (aHeaders[i] != nullptr)
1693 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_HTTPHEADER,
1694 : aHeaders[i]);
1695 :
1696 : // Capture response headers.
1697 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_HEADERDATA,
1698 : papsResults[i]);
1699 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_HEADERFUNCTION,
1700 : CPLHdrWriteFct);
1701 :
1702 0 : asResults[i].psResult = papsResults[i];
1703 : const char *pszMaxFileSize =
1704 0 : CSLFetchNameValue(papszOptions, "MAX_FILE_SIZE");
1705 0 : if (pszMaxFileSize != nullptr)
1706 : {
1707 0 : asResults[i].nMaxFileSize = atoi(pszMaxFileSize);
1708 : // Only useful if size is returned by server before actual download.
1709 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_MAXFILESIZE,
1710 : asResults[i].nMaxFileSize);
1711 : }
1712 :
1713 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_WRITEDATA,
1714 : &asResults[i]);
1715 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_WRITEFUNCTION,
1716 : CPLWriteFct);
1717 :
1718 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_ERRORBUFFER,
1719 : asErrorBuffers[i].szBuffer);
1720 :
1721 0 : if (bSupportGZip &&
1722 0 : CPLTestBool(CPLGetConfigOption("CPL_CURL_GZIP", "YES")))
1723 : {
1724 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_ENCODING, "gzip");
1725 : }
1726 :
1727 0 : asHandles.push_back(http_handle);
1728 : }
1729 :
1730 0 : int iCurRequest = 0;
1731 0 : for (;
1732 0 : iCurRequest <
1733 0 : std::min(nURLCount, nMaxSimultaneous > 0 ? nMaxSimultaneous : INT_MAX);
1734 : iCurRequest++)
1735 : {
1736 0 : CPLHTTPEmitFetchDebug(papszURL[iCurRequest],
1737 : CPLSPrintf(" %d/%d", iCurRequest + 1, nURLCount));
1738 0 : curl_multi_add_handle(hCurlMultiHandle, asHandles[iCurRequest]);
1739 : }
1740 :
1741 0 : int repeats = 0;
1742 0 : void *old_handler = CPLHTTPIgnoreSigPipe();
1743 : while (true)
1744 : {
1745 0 : int still_running = 0;
1746 0 : while (curl_multi_perform(hCurlMultiHandle, &still_running) ==
1747 : CURLM_CALL_MULTI_PERFORM)
1748 : {
1749 : // loop
1750 : }
1751 0 : if (!still_running && iCurRequest == nURLCount)
1752 : {
1753 0 : break;
1754 : }
1755 :
1756 0 : bool bRequestsAdded = false;
1757 : CURLMsg *msg;
1758 0 : do
1759 : {
1760 0 : int msgq = 0;
1761 0 : msg = curl_multi_info_read(hCurlMultiHandle, &msgq);
1762 0 : if (msg && (msg->msg == CURLMSG_DONE))
1763 : {
1764 0 : if (iCurRequest < nURLCount)
1765 : {
1766 0 : CPLHTTPEmitFetchDebug(
1767 0 : papszURL[iCurRequest],
1768 : CPLSPrintf(" %d/%d", iCurRequest + 1, nURLCount));
1769 0 : curl_multi_add_handle(hCurlMultiHandle,
1770 0 : asHandles[iCurRequest]);
1771 0 : iCurRequest++;
1772 0 : bRequestsAdded = true;
1773 : }
1774 : }
1775 0 : } while (msg);
1776 :
1777 0 : if (!bRequestsAdded)
1778 0 : CPLMultiPerformWait(hCurlMultiHandle, repeats);
1779 0 : }
1780 0 : CPLHTTPRestoreSigPipeHandler(old_handler);
1781 :
1782 0 : for (int i = 0; i < nURLCount; i++)
1783 : {
1784 0 : if (asErrorBuffers[i].szBuffer[0] != '\0')
1785 0 : papsResults[i]->pszErrBuf = CPLStrdup(asErrorBuffers[i].szBuffer);
1786 : else
1787 : {
1788 0 : long response_code = 0;
1789 0 : curl_easy_getinfo(asHandles[i], CURLINFO_RESPONSE_CODE,
1790 : &response_code);
1791 :
1792 0 : if (response_code >= 400 && response_code < 600)
1793 : {
1794 0 : papsResults[i]->pszErrBuf = CPLStrdup(CPLSPrintf(
1795 : "HTTP error code : %d", static_cast<int>(response_code)));
1796 : }
1797 : }
1798 :
1799 0 : curl_easy_getinfo(asHandles[i], CURLINFO_CONTENT_TYPE,
1800 : &(papsResults[i]->pszContentType));
1801 0 : if (papsResults[i]->pszContentType != nullptr)
1802 0 : papsResults[i]->pszContentType =
1803 0 : CPLStrdup(papsResults[i]->pszContentType);
1804 :
1805 0 : curl_multi_remove_handle(hCurlMultiHandle, asHandles[i]);
1806 0 : curl_easy_cleanup(asHandles[i]);
1807 : }
1808 :
1809 0 : if (!pszPersistent)
1810 0 : VSICURLMultiCleanup(hCurlMultiHandle);
1811 :
1812 0 : for (size_t i = 0; i < aHeaders.size(); i++)
1813 0 : curl_slist_free_all(aHeaders[i]);
1814 :
1815 0 : return papsResults;
1816 : #endif /* def HAVE_CURL */
1817 : }
1818 :
1819 : /************************************************************************/
1820 : /* CPLHTTPDestroyMultiResult() */
1821 : /************************************************************************/
1822 : /**
1823 : * \brief Clean the memory associated with the return value of
1824 : * CPLHTTPMultiFetch()
1825 : *
1826 : * @param papsResults pointer to the return value of CPLHTTPMultiFetch()
1827 : * @param nCount value of the nURLCount parameter passed to CPLHTTPMultiFetch()
1828 : * @since GDAL 2.3
1829 : */
1830 0 : void CPLHTTPDestroyMultiResult(CPLHTTPResult **papsResults, int nCount)
1831 : {
1832 0 : if (papsResults)
1833 : {
1834 0 : for (int i = 0; i < nCount; i++)
1835 : {
1836 0 : CPLHTTPDestroyResult(papsResults[i]);
1837 : }
1838 0 : CPLFree(papsResults);
1839 : }
1840 0 : }
1841 :
1842 : #ifdef HAVE_CURL
1843 :
1844 : #ifdef _WIN32
1845 :
1846 : #include <windows.h>
1847 :
1848 : /************************************************************************/
1849 : /* CPLFindWin32CurlCaBundleCrt() */
1850 : /************************************************************************/
1851 :
1852 : static const char *CPLFindWin32CurlCaBundleCrt()
1853 : {
1854 : DWORD nResLen;
1855 : const DWORD nBufSize = MAX_PATH + 1;
1856 : char *pszFilePart = nullptr;
1857 :
1858 : char *pszPath = static_cast<char *>(CPLCalloc(1, nBufSize));
1859 :
1860 : nResLen = SearchPathA(nullptr, "curl-ca-bundle.crt", nullptr, nBufSize,
1861 : pszPath, &pszFilePart);
1862 : if (nResLen > 0)
1863 : {
1864 : const char *pszRet = CPLSPrintf("%s", pszPath);
1865 : CPLFree(pszPath);
1866 : return pszRet;
1867 : }
1868 : CPLFree(pszPath);
1869 : return nullptr;
1870 : }
1871 :
1872 : #endif // WIN32
1873 :
1874 : /************************************************************************/
1875 : /* CPLHTTPCurlDebugFunction() */
1876 : /************************************************************************/
1877 :
1878 13 : static int CPLHTTPCurlDebugFunction(CURL *handle, curl_infotype type,
1879 : char *data, size_t size, void *userp)
1880 : {
1881 : (void)handle;
1882 : (void)userp;
1883 :
1884 13 : const char *pszDebugKey = nullptr;
1885 13 : if (type == CURLINFO_TEXT)
1886 : {
1887 7 : pszDebugKey = "CURL_INFO_TEXT";
1888 : }
1889 6 : else if (type == CURLINFO_HEADER_OUT)
1890 : {
1891 1 : pszDebugKey = "CURL_INFO_HEADER_OUT";
1892 : }
1893 5 : else if (type == CURLINFO_HEADER_IN)
1894 : {
1895 5 : pszDebugKey = "CURL_INFO_HEADER_IN";
1896 : }
1897 0 : else if (type == CURLINFO_DATA_IN &&
1898 0 : CPLTestBool(CPLGetConfigOption("CPL_CURL_VERBOSE_DATA_IN", "NO")))
1899 : {
1900 0 : pszDebugKey = "CURL_INFO_DATA_IN";
1901 : }
1902 :
1903 13 : if (pszDebugKey)
1904 : {
1905 26 : std::string osMsg(data, size);
1906 13 : if (!osMsg.empty() && osMsg.back() == '\n')
1907 13 : osMsg.resize(osMsg.size() - 1);
1908 13 : CPLDebug(pszDebugKey, "%s", osMsg.c_str());
1909 : }
1910 13 : return 0;
1911 : }
1912 :
1913 : /************************************************************************/
1914 : /* CPLHTTPSetOptions() */
1915 : /************************************************************************/
1916 :
1917 : // Note: papszOptions must be kept alive until curl_easy/multi_perform()
1918 : // has completed, and we must be careful not to set short lived strings
1919 : // with unchecked_curl_easy_setopt(), as long as we need to support curl < 7.17
1920 : // see https://curl.haxx.se/libcurl/c/unchecked_curl_easy_setopt.html
1921 : // caution: if we remove that assumption, we'll needto use
1922 : // CURLOPT_COPYPOSTFIELDS
1923 :
1924 2241 : void *CPLHTTPSetOptions(void *pcurl, const char *pszURL,
1925 : const char *const *papszOptions)
1926 : {
1927 2241 : CheckCurlFeatures();
1928 :
1929 2241 : CURL *http_handle = reinterpret_cast<CURL *>(pcurl);
1930 :
1931 2241 : unchecked_curl_easy_setopt(http_handle, CURLOPT_URL, pszURL);
1932 :
1933 2241 : if (CPLTestBool(CPLGetConfigOption("CPL_CURL_VERBOSE", "NO")))
1934 : {
1935 1 : unchecked_curl_easy_setopt(http_handle, CURLOPT_VERBOSE, 1);
1936 :
1937 1 : if (CPLGetConfigOption("CPL_DEBUG", nullptr) != nullptr)
1938 : {
1939 1 : unchecked_curl_easy_setopt(http_handle, CURLOPT_DEBUGFUNCTION,
1940 : CPLHTTPCurlDebugFunction);
1941 : }
1942 : }
1943 :
1944 : const char *pszHttpVersion =
1945 2241 : CSLFetchNameValue(papszOptions, "HTTP_VERSION");
1946 2241 : if (pszHttpVersion == nullptr)
1947 2241 : pszHttpVersion = CPLGetConfigOption("GDAL_HTTP_VERSION", nullptr);
1948 2241 : if (pszHttpVersion && strcmp(pszHttpVersion, "1.0") == 0)
1949 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_HTTP_VERSION,
1950 : CURL_HTTP_VERSION_1_0);
1951 2241 : else if (pszHttpVersion && strcmp(pszHttpVersion, "1.1") == 0)
1952 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_HTTP_VERSION,
1953 : CURL_HTTP_VERSION_1_1);
1954 2241 : else if (pszHttpVersion && (strcmp(pszHttpVersion, "2") == 0 ||
1955 0 : strcmp(pszHttpVersion, "2.0") == 0))
1956 : {
1957 0 : if (bSupportHTTP2)
1958 : {
1959 : // Try HTTP/2 both for HTTP and HTTPS. With fallback to HTTP/1.1
1960 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_HTTP_VERSION,
1961 : CURL_HTTP_VERSION_2_0);
1962 : }
1963 : }
1964 2241 : else if (pszHttpVersion == nullptr || strcmp(pszHttpVersion, "2TLS") == 0)
1965 : {
1966 2241 : if (bSupportHTTP2)
1967 : {
1968 : // Only enable this mode if explicitly required, or if the
1969 : // machine is a GCE instance. On other networks, requesting a
1970 : // file in HTTP/2 is found to be significantly slower than HTTP/1.1
1971 : // for unknown reasons.
1972 2241 : if (pszHttpVersion != nullptr || CPLIsMachineForSureGCEInstance())
1973 : {
1974 : static bool bDebugEmitted = false;
1975 0 : if (!bDebugEmitted)
1976 : {
1977 0 : CPLDebug("HTTP", "Using HTTP/2 for HTTPS when possible");
1978 0 : bDebugEmitted = true;
1979 : }
1980 :
1981 : // CURL_HTTP_VERSION_2TLS means for HTTPS connection, try to
1982 : // negotiate HTTP/2 with the server (and fallback to HTTP/1.1
1983 : // otherwise), and for HTTP connection do HTTP/1
1984 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_HTTP_VERSION,
1985 : CURL_HTTP_VERSION_2TLS);
1986 : }
1987 2241 : }
1988 : }
1989 : else
1990 : {
1991 0 : CPLError(CE_Warning, CPLE_NotSupported, "HTTP_VERSION=%s not supported",
1992 : pszHttpVersion);
1993 : }
1994 :
1995 : // Default value is 1 since curl 7.50.2. But worth applying it on
1996 : // previous versions as well.
1997 : const char *pszTCPNoDelay =
1998 2241 : CSLFetchNameValueDef(papszOptions, "TCP_NODELAY", "1");
1999 2241 : unchecked_curl_easy_setopt(http_handle, CURLOPT_TCP_NODELAY,
2000 : atoi(pszTCPNoDelay));
2001 :
2002 : /* Support control over HTTPAUTH */
2003 2241 : const char *pszHttpAuth = CSLFetchNameValue(papszOptions, "HTTPAUTH");
2004 2241 : if (pszHttpAuth == nullptr)
2005 2239 : pszHttpAuth = CPLGetConfigOption("GDAL_HTTP_AUTH", nullptr);
2006 2241 : if (pszHttpAuth == nullptr)
2007 : {
2008 : /* do nothing */;
2009 : }
2010 3 : else if (EQUAL(pszHttpAuth, "BASIC"))
2011 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH,
2012 : CURLAUTH_BASIC);
2013 3 : else if (EQUAL(pszHttpAuth, "NTLM"))
2014 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH,
2015 : CURLAUTH_NTLM);
2016 3 : else if (EQUAL(pszHttpAuth, "ANY"))
2017 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
2018 3 : else if (EQUAL(pszHttpAuth, "ANYSAFE"))
2019 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH,
2020 : CURLAUTH_ANYSAFE);
2021 3 : else if (EQUAL(pszHttpAuth, "BEARER"))
2022 : {
2023 3 : const char *pszBearer = CSLFetchNameValue(papszOptions, "HTTP_BEARER");
2024 3 : if (pszBearer == nullptr)
2025 1 : pszBearer = CPLGetConfigOption("GDAL_HTTP_BEARER", nullptr);
2026 3 : if (pszBearer != nullptr)
2027 3 : unchecked_curl_easy_setopt(http_handle, CURLOPT_XOAUTH2_BEARER,
2028 : pszBearer);
2029 3 : unchecked_curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH,
2030 : CURLAUTH_BEARER);
2031 : }
2032 0 : else if (EQUAL(pszHttpAuth, "NEGOTIATE"))
2033 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH,
2034 : CURLAUTH_NEGOTIATE);
2035 : else
2036 : {
2037 0 : CPLError(CE_Warning, CPLE_AppDefined,
2038 : "Unsupported HTTPAUTH value '%s', ignored.", pszHttpAuth);
2039 : }
2040 :
2041 : const char *pszGssDelegation =
2042 2241 : CSLFetchNameValue(papszOptions, "GSSAPI_DELEGATION");
2043 2241 : if (pszGssDelegation == nullptr)
2044 : pszGssDelegation =
2045 2241 : CPLGetConfigOption("GDAL_GSSAPI_DELEGATION", nullptr);
2046 2241 : if (pszGssDelegation == nullptr)
2047 : {
2048 : }
2049 0 : else if (EQUAL(pszGssDelegation, "NONE"))
2050 : {
2051 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_GSSAPI_DELEGATION,
2052 : CURLGSSAPI_DELEGATION_NONE);
2053 : }
2054 0 : else if (EQUAL(pszGssDelegation, "POLICY"))
2055 : {
2056 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_GSSAPI_DELEGATION,
2057 : CURLGSSAPI_DELEGATION_POLICY_FLAG);
2058 : }
2059 0 : else if (EQUAL(pszGssDelegation, "ALWAYS"))
2060 : {
2061 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_GSSAPI_DELEGATION,
2062 : CURLGSSAPI_DELEGATION_FLAG);
2063 : }
2064 : else
2065 : {
2066 0 : CPLError(CE_Warning, CPLE_AppDefined,
2067 : "Unsupported GSSAPI_DELEGATION value '%s', ignored.",
2068 : pszGssDelegation);
2069 : }
2070 :
2071 : // Support use of .netrc - default enabled.
2072 2241 : const char *pszHttpNetrc = CSLFetchNameValue(papszOptions, "NETRC");
2073 2241 : if (pszHttpNetrc == nullptr)
2074 2241 : pszHttpNetrc = CPLGetConfigOption("GDAL_HTTP_NETRC", "YES");
2075 2241 : if (pszHttpNetrc == nullptr || CPLTestBool(pszHttpNetrc))
2076 2241 : unchecked_curl_easy_setopt(http_handle, CURLOPT_NETRC, 1L);
2077 :
2078 : // Custom .netrc file location
2079 : const char *pszHttpNetrcFile =
2080 2241 : CSLFetchNameValue(papszOptions, "NETRC_FILE");
2081 2241 : if (pszHttpNetrcFile == nullptr)
2082 2241 : pszHttpNetrcFile = CPLGetConfigOption("GDAL_HTTP_NETRC_FILE", nullptr);
2083 2241 : if (pszHttpNetrcFile)
2084 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_NETRC_FILE,
2085 : pszHttpNetrcFile);
2086 :
2087 : // Support setting userid:password.
2088 2241 : const char *pszUserPwd = CSLFetchNameValue(papszOptions, "USERPWD");
2089 2241 : if (pszUserPwd == nullptr)
2090 2203 : pszUserPwd = CPLGetConfigOption("GDAL_HTTP_USERPWD", nullptr);
2091 2241 : if (pszUserPwd != nullptr)
2092 38 : unchecked_curl_easy_setopt(http_handle, CURLOPT_USERPWD, pszUserPwd);
2093 :
2094 : // Set Proxy parameters.
2095 2241 : const char *pszProxy = CSLFetchNameValue(papszOptions, "PROXY");
2096 2241 : if (pszProxy == nullptr)
2097 2241 : pszProxy = CPLGetConfigOption("GDAL_HTTP_PROXY", nullptr);
2098 2241 : if (pszProxy)
2099 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_PROXY, pszProxy);
2100 :
2101 2241 : const char *pszHttpsProxy = CSLFetchNameValue(papszOptions, "HTTPS_PROXY");
2102 2241 : if (pszHttpsProxy == nullptr)
2103 2241 : pszHttpsProxy = CPLGetConfigOption("GDAL_HTTPS_PROXY", nullptr);
2104 2241 : if (pszHttpsProxy && (STARTS_WITH(pszURL, "https")))
2105 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_PROXY, pszHttpsProxy);
2106 :
2107 : const char *pszProxyUserPwd =
2108 2241 : CSLFetchNameValue(papszOptions, "PROXYUSERPWD");
2109 2241 : if (pszProxyUserPwd == nullptr)
2110 2241 : pszProxyUserPwd = CPLGetConfigOption("GDAL_HTTP_PROXYUSERPWD", nullptr);
2111 2241 : if (pszProxyUserPwd)
2112 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_PROXYUSERPWD,
2113 : pszProxyUserPwd);
2114 :
2115 : // Support control over PROXYAUTH.
2116 2241 : const char *pszProxyAuth = CSLFetchNameValue(papszOptions, "PROXYAUTH");
2117 2241 : if (pszProxyAuth == nullptr)
2118 2241 : pszProxyAuth = CPLGetConfigOption("GDAL_PROXY_AUTH", nullptr);
2119 2241 : if (pszProxyAuth == nullptr)
2120 : {
2121 : // Do nothing.
2122 : }
2123 0 : else if (EQUAL(pszProxyAuth, "BASIC"))
2124 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_PROXYAUTH,
2125 : CURLAUTH_BASIC);
2126 0 : else if (EQUAL(pszProxyAuth, "NTLM"))
2127 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_PROXYAUTH,
2128 : CURLAUTH_NTLM);
2129 0 : else if (EQUAL(pszProxyAuth, "DIGEST"))
2130 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_PROXYAUTH,
2131 : CURLAUTH_DIGEST);
2132 0 : else if (EQUAL(pszProxyAuth, "ANY"))
2133 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_PROXYAUTH,
2134 : CURLAUTH_ANY);
2135 0 : else if (EQUAL(pszProxyAuth, "ANYSAFE"))
2136 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_PROXYAUTH,
2137 : CURLAUTH_ANYSAFE);
2138 0 : else if (EQUAL(pszProxyAuth, "NEGOTIATE"))
2139 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_PROXYAUTH,
2140 : CURLAUTH_NEGOTIATE);
2141 : else
2142 : {
2143 0 : CPLError(CE_Warning, CPLE_AppDefined,
2144 : "Unsupported PROXYAUTH value '%s', ignored.", pszProxyAuth);
2145 : }
2146 :
2147 2241 : unchecked_curl_easy_setopt(http_handle, CURLOPT_SUPPRESS_CONNECT_HEADERS,
2148 : 1L);
2149 :
2150 2241 : unchecked_curl_easy_setopt(http_handle, CURLOPT_FOLLOWLOCATION, 1);
2151 2241 : unchecked_curl_easy_setopt(http_handle, CURLOPT_MAXREDIRS, 10);
2152 2241 : unchecked_curl_easy_setopt(http_handle, CURLOPT_POSTREDIR,
2153 : CURL_REDIR_POST_ALL);
2154 :
2155 : // Set connect timeout.
2156 : const char *pszConnectTimeout =
2157 2241 : CSLFetchNameValue(papszOptions, "CONNECTTIMEOUT");
2158 2241 : if (pszConnectTimeout == nullptr)
2159 : pszConnectTimeout =
2160 2241 : CPLGetConfigOption("GDAL_HTTP_CONNECTTIMEOUT", nullptr);
2161 2241 : if (pszConnectTimeout != nullptr)
2162 : {
2163 : // coverity[tainted_data]
2164 1 : unchecked_curl_easy_setopt(
2165 : http_handle, CURLOPT_CONNECTTIMEOUT_MS,
2166 : static_cast<int>(1000 * CPLAtof(pszConnectTimeout)));
2167 : }
2168 :
2169 : // Set timeout.
2170 2241 : const char *pszTimeout = CSLFetchNameValue(papszOptions, "TIMEOUT");
2171 2241 : if (pszTimeout == nullptr)
2172 1952 : pszTimeout = CPLGetConfigOption("GDAL_HTTP_TIMEOUT", nullptr);
2173 2241 : if (pszTimeout != nullptr)
2174 : {
2175 : // coverity[tainted_data]
2176 301 : unchecked_curl_easy_setopt(
2177 : http_handle, CURLOPT_TIMEOUT_MS,
2178 : static_cast<int>(1000 * CPLAtof(pszTimeout)));
2179 : }
2180 :
2181 : // Set low speed time and limit.
2182 : const char *pszLowSpeedTime =
2183 2241 : CSLFetchNameValue(papszOptions, "LOW_SPEED_TIME");
2184 2241 : if (pszLowSpeedTime == nullptr)
2185 : pszLowSpeedTime =
2186 2241 : CPLGetConfigOption("GDAL_HTTP_LOW_SPEED_TIME", nullptr);
2187 2241 : if (pszLowSpeedTime != nullptr)
2188 : {
2189 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_LOW_SPEED_TIME,
2190 : atoi(pszLowSpeedTime));
2191 :
2192 : const char *pszLowSpeedLimit =
2193 0 : CSLFetchNameValue(papszOptions, "LOW_SPEED_LIMIT");
2194 0 : if (pszLowSpeedLimit == nullptr)
2195 : pszLowSpeedLimit =
2196 0 : CPLGetConfigOption("GDAL_HTTP_LOW_SPEED_LIMIT", "1");
2197 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_LOW_SPEED_LIMIT,
2198 : atoi(pszLowSpeedLimit));
2199 : }
2200 :
2201 : /* Disable some SSL verification */
2202 2241 : const char *pszUnsafeSSL = CSLFetchNameValue(papszOptions, "UNSAFESSL");
2203 2241 : if (pszUnsafeSSL == nullptr)
2204 2221 : pszUnsafeSSL = CPLGetConfigOption("GDAL_HTTP_UNSAFESSL", nullptr);
2205 2241 : if (pszUnsafeSSL != nullptr && CPLTestBool(pszUnsafeSSL))
2206 : {
2207 20 : unchecked_curl_easy_setopt(http_handle, CURLOPT_SSL_VERIFYPEER, 0L);
2208 20 : unchecked_curl_easy_setopt(http_handle, CURLOPT_SSL_VERIFYHOST, 0L);
2209 : }
2210 :
2211 : const char *pszUseCAPIStore =
2212 2241 : CSLFetchNameValue(papszOptions, "USE_CAPI_STORE");
2213 2241 : if (pszUseCAPIStore == nullptr)
2214 2241 : pszUseCAPIStore = CPLGetConfigOption("GDAL_HTTP_USE_CAPI_STORE", "NO");
2215 2241 : if (CPLTestBool(pszUseCAPIStore))
2216 : {
2217 : #if defined(_WIN32) && defined(HAVE_OPENSSL_CRYPTO)
2218 : // Use certificates from Windows certificate store; requires
2219 : // crypt32.lib, OpenSSL crypto and ssl libraries.
2220 : unchecked_curl_easy_setopt(http_handle, CURLOPT_SSL_CTX_FUNCTION,
2221 : *CPL_ssl_ctx_callback);
2222 : #else // defined(_WIN32) && defined(HAVE_OPENSSL_CRYPTO)
2223 2 : CPLError(CE_Warning, CPLE_NotSupported,
2224 : "GDAL_HTTP_USE_CAPI_STORE requested, but libcurl too old, "
2225 : "non-Windows platform or OpenSSL missing.");
2226 : #endif // defined(_WIN32) && defined(HAVE_OPENSSL_CRYPTO)
2227 : }
2228 :
2229 : // Enable OCSP stapling if requested.
2230 : const char *pszSSLVerifyStatus =
2231 2241 : CSLFetchNameValue(papszOptions, "SSL_VERIFYSTATUS");
2232 2241 : if (pszSSLVerifyStatus == nullptr)
2233 : pszSSLVerifyStatus =
2234 2241 : CPLGetConfigOption("GDAL_HTTP_SSL_VERIFYSTATUS", "NO");
2235 2241 : if (CPLTestBool(pszSSLVerifyStatus))
2236 : {
2237 1 : unchecked_curl_easy_setopt(http_handle, CURLOPT_SSL_VERIFYSTATUS, 1L);
2238 : }
2239 :
2240 : // Custom path to SSL certificates.
2241 2241 : const char *pszCAInfo = CSLFetchNameValue(papszOptions, "CAINFO");
2242 2241 : if (pszCAInfo == nullptr)
2243 : // Name of GDAL environment variable for the CA Bundle path
2244 2241 : pszCAInfo = CPLGetConfigOption("GDAL_CURL_CA_BUNDLE", nullptr);
2245 2241 : if (pszCAInfo == nullptr)
2246 : // Name of environment variable used by the curl binary
2247 2241 : pszCAInfo = CPLGetConfigOption("CURL_CA_BUNDLE", nullptr);
2248 2241 : if (pszCAInfo == nullptr)
2249 : // Name of environment variable used by the curl binary (tested
2250 : // after CURL_CA_BUNDLE
2251 2241 : pszCAInfo = CPLGetConfigOption("SSL_CERT_FILE", nullptr);
2252 : #ifdef _WIN32
2253 : if (pszCAInfo == nullptr)
2254 : {
2255 : pszCAInfo = CPLFindWin32CurlCaBundleCrt();
2256 : }
2257 : #endif
2258 2241 : if (pszCAInfo != nullptr)
2259 : {
2260 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_CAINFO, pszCAInfo);
2261 : }
2262 :
2263 2241 : const char *pszCAPath = CSLFetchNameValue(papszOptions, "CAPATH");
2264 2241 : if (pszCAPath != nullptr)
2265 : {
2266 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_CAPATH, pszCAPath);
2267 : }
2268 :
2269 : // Support for SSL client certificates
2270 :
2271 : // Filename of the the client certificate
2272 2241 : const char *pszSSLCert = CSLFetchNameValue(papszOptions, "SSLCERT");
2273 2241 : if (!pszSSLCert)
2274 2241 : pszSSLCert = CPLGetConfigOption("GDAL_HTTP_SSLCERT", nullptr);
2275 2241 : if (pszSSLCert)
2276 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_SSLCERT, pszSSLCert);
2277 :
2278 : // private key file for TLS and SSL client cert
2279 2241 : const char *pszSSLKey = CSLFetchNameValue(papszOptions, "SSLKEY");
2280 2241 : if (!pszSSLKey)
2281 2241 : pszSSLKey = CPLGetConfigOption("GDAL_HTTP_SSLKEY", nullptr);
2282 2241 : if (pszSSLKey)
2283 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_SSLKEY, pszSSLKey);
2284 :
2285 : // type of client SSL certificate ("PEM", "DER", ...)
2286 2241 : const char *pszSSLCertType = CSLFetchNameValue(papszOptions, "SSLCERTTYPE");
2287 2241 : if (!pszSSLCertType)
2288 2241 : pszSSLCertType = CPLGetConfigOption("GDAL_HTTP_SSLCERTTYPE", nullptr);
2289 2241 : if (pszSSLCertType)
2290 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_SSLCERTTYPE,
2291 : pszSSLCertType);
2292 :
2293 : // passphrase to private key
2294 2241 : const char *pszKeyPasswd = CSLFetchNameValue(papszOptions, "KEYPASSWD");
2295 2241 : if (!pszKeyPasswd)
2296 2241 : pszKeyPasswd = CPLGetConfigOption("GDAL_HTTP_KEYPASSWD", nullptr);
2297 2241 : if (pszKeyPasswd)
2298 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_KEYPASSWD,
2299 : pszKeyPasswd);
2300 :
2301 : /* Set Referer */
2302 2241 : const char *pszReferer = CSLFetchNameValue(papszOptions, "REFERER");
2303 2241 : if (pszReferer != nullptr)
2304 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_REFERER, pszReferer);
2305 :
2306 : /* Set User-Agent */
2307 2241 : const char *pszUserAgent = CSLFetchNameValue(papszOptions, "USERAGENT");
2308 2241 : if (pszUserAgent == nullptr)
2309 2197 : pszUserAgent = CPLGetConfigOption("GDAL_HTTP_USERAGENT",
2310 : gosDefaultUserAgent.c_str());
2311 2241 : if (pszUserAgent != nullptr && !EQUAL(pszUserAgent, ""))
2312 : {
2313 2241 : unchecked_curl_easy_setopt(http_handle, CURLOPT_USERAGENT,
2314 : pszUserAgent);
2315 : }
2316 :
2317 : /* NOSIGNAL should be set to true for timeout to work in multithread
2318 : * environments on Unix, requires libcurl 7.10 or more recent.
2319 : * (this force avoiding the use of signal handlers)
2320 : */
2321 2241 : unchecked_curl_easy_setopt(http_handle, CURLOPT_NOSIGNAL, 1);
2322 :
2323 : const char *pszFormFilePath =
2324 2241 : CSLFetchNameValue(papszOptions, "FORM_FILE_PATH");
2325 : const char *pszParametersCount =
2326 2241 : CSLFetchNameValue(papszOptions, "FORM_ITEM_COUNT");
2327 2241 : if (pszFormFilePath == nullptr && pszParametersCount == nullptr)
2328 : {
2329 : /* Set POST mode */
2330 2239 : const char *pszPost = CSLFetchNameValue(papszOptions, "POSTFIELDS");
2331 2239 : if (pszPost != nullptr)
2332 : {
2333 157 : CPLDebug("HTTP", "These POSTFIELDS were sent:%.4000s", pszPost);
2334 157 : unchecked_curl_easy_setopt(http_handle, CURLOPT_POST, 1);
2335 157 : unchecked_curl_easy_setopt(http_handle, CURLOPT_POSTFIELDS,
2336 : pszPost);
2337 : }
2338 : }
2339 :
2340 : const char *pszCustomRequest =
2341 2241 : CSLFetchNameValue(papszOptions, "CUSTOMREQUEST");
2342 2241 : if (pszCustomRequest != nullptr)
2343 : {
2344 137 : unchecked_curl_easy_setopt(http_handle, CURLOPT_CUSTOMREQUEST,
2345 : pszCustomRequest);
2346 : }
2347 :
2348 2241 : const char *pszCookie = CSLFetchNameValue(papszOptions, "COOKIE");
2349 2241 : if (pszCookie == nullptr)
2350 2241 : pszCookie = CPLGetConfigOption("GDAL_HTTP_COOKIE", nullptr);
2351 2241 : if (pszCookie != nullptr)
2352 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_COOKIE, pszCookie);
2353 :
2354 2241 : const char *pszCookieFile = CSLFetchNameValue(papszOptions, "COOKIEFILE");
2355 2241 : if (pszCookieFile == nullptr)
2356 2241 : pszCookieFile = CPLGetConfigOption("GDAL_HTTP_COOKIEFILE", nullptr);
2357 2241 : if (pszCookieFile != nullptr)
2358 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_COOKIEFILE,
2359 : pszCookieFile);
2360 :
2361 2241 : const char *pszCookieJar = CSLFetchNameValue(papszOptions, "COOKIEJAR");
2362 2241 : if (pszCookieJar == nullptr)
2363 2241 : pszCookieJar = CPLGetConfigOption("GDAL_HTTP_COOKIEJAR", nullptr);
2364 2241 : if (pszCookieJar != nullptr)
2365 0 : unchecked_curl_easy_setopt(http_handle, CURLOPT_COOKIEJAR,
2366 : pszCookieJar);
2367 :
2368 : // TCP keep-alive
2369 : const char *pszTCPKeepAlive =
2370 2241 : CSLFetchNameValue(papszOptions, "TCP_KEEPALIVE");
2371 2241 : if (pszTCPKeepAlive == nullptr)
2372 2241 : pszTCPKeepAlive = CPLGetConfigOption("GDAL_HTTP_TCP_KEEPALIVE", "YES");
2373 2241 : if (pszTCPKeepAlive != nullptr && CPLTestBool(pszTCPKeepAlive))
2374 : {
2375 : // Set keep-alive interval.
2376 2241 : int nKeepAliveInterval = 60;
2377 : const char *pszKeepAliveInterval =
2378 2241 : CSLFetchNameValue(papszOptions, "TCP_KEEPINTVL");
2379 2241 : if (pszKeepAliveInterval == nullptr)
2380 : pszKeepAliveInterval =
2381 2241 : CPLGetConfigOption("GDAL_HTTP_TCP_KEEPINTVL", nullptr);
2382 2241 : if (pszKeepAliveInterval != nullptr)
2383 1 : nKeepAliveInterval = atoi(pszKeepAliveInterval);
2384 :
2385 : // Set keep-alive idle wait time.
2386 2241 : int nKeepAliveIdle = 60;
2387 : const char *pszKeepAliveIdle =
2388 2241 : CSLFetchNameValue(papszOptions, "TCP_KEEPIDLE");
2389 2241 : if (pszKeepAliveIdle == nullptr)
2390 : pszKeepAliveIdle =
2391 2241 : CPLGetConfigOption("GDAL_HTTP_TCP_KEEPIDLE", nullptr);
2392 2241 : if (pszKeepAliveIdle != nullptr)
2393 1 : nKeepAliveIdle = atoi(pszKeepAliveIdle);
2394 :
2395 2241 : unchecked_curl_easy_setopt(http_handle, CURLOPT_TCP_KEEPALIVE, 1L);
2396 2241 : unchecked_curl_easy_setopt(http_handle, CURLOPT_TCP_KEEPINTVL,
2397 : nKeepAliveInterval);
2398 2241 : unchecked_curl_easy_setopt(http_handle, CURLOPT_TCP_KEEPIDLE,
2399 : nKeepAliveIdle);
2400 : }
2401 :
2402 2241 : struct curl_slist *headers = nullptr;
2403 2241 : const char *pszHeaderFile = CSLFetchNameValue(papszOptions, "HEADER_FILE");
2404 2241 : if (pszHeaderFile == nullptr)
2405 2239 : pszHeaderFile = CPLGetConfigOption("GDAL_HTTP_HEADER_FILE", nullptr);
2406 2241 : if (pszHeaderFile != nullptr)
2407 : {
2408 2 : VSILFILE *fp = nullptr;
2409 : // Do not allow /vsicurl/ access from /vsicurl because of
2410 : // GetCurlHandleFor() e.g. "/vsicurl/,HEADER_FILE=/vsicurl/,url= " would
2411 : // cause use of memory after free
2412 2 : if (!STARTS_WITH(pszHeaderFile, "/vsi") ||
2413 2 : STARTS_WITH(pszHeaderFile, "/vsimem/"))
2414 : {
2415 2 : fp = VSIFOpenL(pszHeaderFile, "rb");
2416 : }
2417 2 : if (fp == nullptr)
2418 : {
2419 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot read %s", pszHeaderFile);
2420 : }
2421 : else
2422 : {
2423 2 : const char *pszLine = nullptr;
2424 4 : while ((pszLine = CPLReadLineL(fp)) != nullptr)
2425 : {
2426 2 : headers = curl_slist_append(headers, pszLine);
2427 : }
2428 2 : VSIFCloseL(fp);
2429 : }
2430 : }
2431 :
2432 2241 : const char *pszHeaders = CSLFetchNameValue(papszOptions, "HEADERS");
2433 2241 : if (pszHeaders == nullptr)
2434 1648 : pszHeaders = CPLGetConfigOption("GDAL_HTTP_HEADERS", nullptr);
2435 2241 : if (pszHeaders)
2436 : {
2437 593 : bool bHeadersDone = false;
2438 : // Compatibility hack for "HEADERS=Accept: text/plain, application/json"
2439 593 : if (strstr(pszHeaders, "\r\n") == nullptr)
2440 : {
2441 538 : const char *pszComma = strchr(pszHeaders, ',');
2442 538 : if (pszComma != nullptr && strchr(pszComma, ':') == nullptr)
2443 : {
2444 234 : headers = curl_slist_append(headers, pszHeaders);
2445 234 : bHeadersDone = true;
2446 : }
2447 : }
2448 593 : if (!bHeadersDone)
2449 : {
2450 : // We accept both raw headers with \r\n as a separator, or as
2451 : // a comma separated list of foo: bar values.
2452 : const CPLStringList aosTokens(
2453 359 : strstr(pszHeaders, "\r\n")
2454 55 : ? CSLTokenizeString2(pszHeaders, "\r\n", 0)
2455 773 : : CSLTokenizeString2(pszHeaders, ",", CSLT_HONOURSTRINGS));
2456 777 : for (int i = 0; i < aosTokens.size(); ++i)
2457 : {
2458 418 : headers = curl_slist_append(headers, aosTokens[i]);
2459 : }
2460 : }
2461 : }
2462 :
2463 2241 : return headers;
2464 : }
2465 :
2466 : /************************************************************************/
2467 : /* CPLHTTPIgnoreSigPipe() */
2468 : /************************************************************************/
2469 :
2470 : /* If using OpenSSL with Curl, openssl can cause SIGPIPE to be triggered */
2471 : /* As we set CURLOPT_NOSIGNAL = 1, we must manually handle this situation */
2472 :
2473 2400 : void *CPLHTTPIgnoreSigPipe()
2474 : {
2475 : #if defined(SIGPIPE) && defined(HAVE_SIGACTION)
2476 : struct sigaction old_pipe_act;
2477 : struct sigaction action;
2478 : /* Get previous handler */
2479 2400 : memset(&old_pipe_act, 0, sizeof(struct sigaction));
2480 2400 : sigaction(SIGPIPE, nullptr, &old_pipe_act);
2481 :
2482 : /* Install new handler */
2483 2400 : action = old_pipe_act;
2484 2400 : action.sa_handler = SIG_IGN;
2485 2400 : sigaction(SIGPIPE, &action, nullptr);
2486 :
2487 2400 : void *ret = CPLMalloc(sizeof(old_pipe_act));
2488 2400 : memcpy(ret, &old_pipe_act, sizeof(old_pipe_act));
2489 2400 : return ret;
2490 : #else
2491 : return nullptr;
2492 : #endif
2493 : }
2494 :
2495 : /************************************************************************/
2496 : /* CPLHTTPRestoreSigPipeHandler() */
2497 : /************************************************************************/
2498 :
2499 2400 : void CPLHTTPRestoreSigPipeHandler(void *old_handler)
2500 : {
2501 : #if defined(SIGPIPE) && defined(HAVE_SIGACTION)
2502 2400 : sigaction(SIGPIPE, static_cast<struct sigaction *>(old_handler), nullptr);
2503 2400 : CPLFree(old_handler);
2504 : #else
2505 : (void)old_handler;
2506 : #endif
2507 2400 : }
2508 :
2509 : #endif // def HAVE_CURL
2510 :
2511 : /************************************************************************/
2512 : /* CPLHTTPEnabled() */
2513 : /************************************************************************/
2514 :
2515 : /**
2516 : * \brief Return if CPLHTTP services can be useful
2517 : *
2518 : * Those services depend on GDAL being build with libcurl support.
2519 : *
2520 : * @return TRUE if libcurl support is enabled
2521 : */
2522 8 : int CPLHTTPEnabled()
2523 :
2524 : {
2525 : #ifdef HAVE_CURL
2526 8 : return TRUE;
2527 : #else
2528 : return FALSE;
2529 : #endif
2530 : }
2531 :
2532 : /************************************************************************/
2533 : /* CPLHTTPCleanup() */
2534 : /************************************************************************/
2535 :
2536 : /**
2537 : * \brief Cleanup function to call at application termination
2538 : */
2539 852 : void CPLHTTPCleanup()
2540 :
2541 : {
2542 : #ifdef HAVE_CURL
2543 852 : if (!hSessionMapMutex)
2544 851 : return;
2545 :
2546 : {
2547 2 : CPLMutexHolder oHolder(&hSessionMapMutex);
2548 1 : if (poSessionMap)
2549 : {
2550 0 : for (auto &kv : *poSessionMap)
2551 : {
2552 0 : curl_easy_cleanup(kv.second);
2553 : }
2554 0 : delete poSessionMap;
2555 0 : poSessionMap = nullptr;
2556 : }
2557 1 : if (poSessionMultiMap)
2558 : {
2559 0 : for (auto &kv : *poSessionMultiMap)
2560 : {
2561 0 : VSICURLMultiCleanup(kv.second);
2562 : }
2563 0 : delete poSessionMultiMap;
2564 0 : poSessionMultiMap = nullptr;
2565 : }
2566 : }
2567 :
2568 : // Not quite a safe sequence.
2569 1 : CPLDestroyMutex(hSessionMapMutex);
2570 1 : hSessionMapMutex = nullptr;
2571 :
2572 : #if defined(_WIN32) && defined(HAVE_OPENSSL_CRYPTO)
2573 : // This cleanup must be absolutely done before CPLOpenSSLCleanup()
2574 : // for some unknown reason, but otherwise X509_free() in
2575 : // CPLWindowsCertificateListCleanup() will crash.
2576 : CPLWindowsCertificateListCleanup();
2577 : #endif
2578 :
2579 : #if defined(HAVE_OPENSSL_CRYPTO) && OPENSSL_VERSION_NUMBER < 0x10100000
2580 : CPLOpenSSLCleanup();
2581 : #endif
2582 :
2583 : #endif
2584 : }
2585 :
2586 : /************************************************************************/
2587 : /* CPLHTTPDestroyResult() */
2588 : /************************************************************************/
2589 :
2590 : /**
2591 : * \brief Clean the memory associated with the return value of CPLHTTPFetch()
2592 : *
2593 : * @param psResult pointer to the return value of CPLHTTPFetch()
2594 : */
2595 2153 : void CPLHTTPDestroyResult(CPLHTTPResult *psResult)
2596 :
2597 : {
2598 2153 : if (psResult)
2599 : {
2600 1968 : CPLFree(psResult->pabyData);
2601 1968 : CPLFree(psResult->pszErrBuf);
2602 1968 : CPLFree(psResult->pszContentType);
2603 1968 : CSLDestroy(psResult->papszHeaders);
2604 :
2605 2025 : for (int i = 0; i < psResult->nMimePartCount; i++)
2606 : {
2607 57 : CSLDestroy(psResult->pasMimePart[i].papszHeaders);
2608 : }
2609 1968 : CPLFree(psResult->pasMimePart);
2610 :
2611 1968 : CPLFree(psResult);
2612 : }
2613 2153 : }
2614 :
2615 : /************************************************************************/
2616 : /* CPLHTTPParseMultipartMime() */
2617 : /************************************************************************/
2618 :
2619 : /**
2620 : * \brief Parses a MIME multipart message.
2621 : *
2622 : * This function will iterate over each part and put it in a separate
2623 : * element of the pasMimePart array of the provided psResult structure.
2624 : *
2625 : * @param psResult pointer to the return value of CPLHTTPFetch()
2626 : * @return TRUE if the message contains MIME multipart message.
2627 : */
2628 45 : int CPLHTTPParseMultipartMime(CPLHTTPResult *psResult)
2629 :
2630 : {
2631 : /* -------------------------------------------------------------------- */
2632 : /* Is it already done? */
2633 : /* -------------------------------------------------------------------- */
2634 45 : if (psResult->nMimePartCount > 0)
2635 5 : return TRUE;
2636 :
2637 : /* -------------------------------------------------------------------- */
2638 : /* Find the boundary setting in the content type. */
2639 : /* -------------------------------------------------------------------- */
2640 40 : const char *pszBound = nullptr;
2641 :
2642 40 : if (psResult->pszContentType != nullptr)
2643 38 : pszBound = strstr(psResult->pszContentType, "boundary=");
2644 :
2645 40 : if (pszBound == nullptr)
2646 : {
2647 2 : CPLError(CE_Failure, CPLE_AppDefined,
2648 : "Unable to parse multi-part mime, no boundary setting.");
2649 2 : return FALSE;
2650 : }
2651 :
2652 76 : CPLString osBoundary;
2653 : char **papszTokens =
2654 38 : CSLTokenizeStringComplex(pszBound + 9, "\n ;", TRUE, FALSE);
2655 :
2656 38 : if (CSLCount(papszTokens) == 0 || strlen(papszTokens[0]) == 0)
2657 : {
2658 1 : CPLError(CE_Failure, CPLE_AppDefined,
2659 : "Unable to parse multi-part mime, boundary not parsable.");
2660 1 : CSLDestroy(papszTokens);
2661 1 : return FALSE;
2662 : }
2663 :
2664 37 : osBoundary = "--";
2665 37 : osBoundary += papszTokens[0];
2666 37 : CSLDestroy(papszTokens);
2667 :
2668 : /* -------------------------------------------------------------------- */
2669 : /* Find the start of the first chunk. */
2670 : /* -------------------------------------------------------------------- */
2671 37 : char *pszNext = psResult->pabyData
2672 37 : ? strstr(reinterpret_cast<char *>(psResult->pabyData),
2673 : osBoundary.c_str())
2674 37 : : nullptr;
2675 :
2676 37 : if (pszNext == nullptr)
2677 : {
2678 1 : CPLError(CE_Failure, CPLE_AppDefined, "No parts found.");
2679 1 : return FALSE;
2680 : }
2681 :
2682 36 : pszNext += osBoundary.size();
2683 146 : while (*pszNext != '\n' && *pszNext != '\r' && *pszNext != '\0')
2684 110 : pszNext++;
2685 36 : if (*pszNext == '\r')
2686 30 : pszNext++;
2687 36 : if (*pszNext == '\n')
2688 36 : pszNext++;
2689 :
2690 : /* -------------------------------------------------------------------- */
2691 : /* Loop over parts... */
2692 : /* -------------------------------------------------------------------- */
2693 : while (true)
2694 : {
2695 57 : psResult->nMimePartCount++;
2696 57 : psResult->pasMimePart = static_cast<CPLMimePart *>(
2697 114 : CPLRealloc(psResult->pasMimePart,
2698 57 : sizeof(CPLMimePart) * psResult->nMimePartCount));
2699 :
2700 57 : CPLMimePart *psPart =
2701 57 : psResult->pasMimePart + psResult->nMimePartCount - 1;
2702 :
2703 57 : memset(psPart, 0, sizeof(CPLMimePart));
2704 :
2705 : /* --------------------------------------------------------------------
2706 : */
2707 : /* Collect headers. */
2708 : /* --------------------------------------------------------------------
2709 : */
2710 145 : while (*pszNext != '\n' && *pszNext != '\r' && *pszNext != '\0')
2711 : {
2712 90 : if (!STARTS_WITH(pszNext, "Content-"))
2713 : {
2714 1 : break;
2715 : }
2716 89 : char *pszEOL = strstr(pszNext, "\n");
2717 :
2718 89 : if (pszEOL == nullptr)
2719 : {
2720 1 : CPLError(CE_Failure, CPLE_AppDefined,
2721 : "Error while parsing multipart content (at line %d)",
2722 : __LINE__);
2723 1 : return FALSE;
2724 : }
2725 :
2726 88 : *pszEOL = '\0';
2727 88 : bool bRestoreAntislashR = false;
2728 88 : if (pszEOL - pszNext > 1 && pszEOL[-1] == '\r')
2729 : {
2730 80 : bRestoreAntislashR = true;
2731 80 : pszEOL[-1] = '\0';
2732 : }
2733 88 : char *pszKey = nullptr;
2734 88 : const char *pszValue = CPLParseNameValue(pszNext, &pszKey);
2735 88 : if (pszKey && pszValue)
2736 : {
2737 88 : psPart->papszHeaders =
2738 88 : CSLSetNameValue(psPart->papszHeaders, pszKey, pszValue);
2739 : }
2740 88 : CPLFree(pszKey);
2741 88 : if (bRestoreAntislashR)
2742 80 : pszEOL[-1] = '\r';
2743 88 : *pszEOL = '\n';
2744 :
2745 88 : pszNext = pszEOL + 1;
2746 : }
2747 :
2748 56 : if (*pszNext == '\r')
2749 54 : pszNext++;
2750 56 : if (*pszNext == '\n')
2751 54 : pszNext++;
2752 :
2753 : /* --------------------------------------------------------------------
2754 : */
2755 : /* Work out the data block size. */
2756 : /* --------------------------------------------------------------------
2757 : */
2758 56 : psPart->pabyData = reinterpret_cast<GByte *>(pszNext);
2759 :
2760 56 : int nBytesAvail = psResult->nDataLen -
2761 56 : static_cast<int>(pszNext - reinterpret_cast<char *>(
2762 56 : psResult->pabyData));
2763 :
2764 6579040 : while (nBytesAvail > 0 &&
2765 3289520 : (*pszNext != '-' ||
2766 109 : strncmp(pszNext, osBoundary, osBoundary.size()) != 0))
2767 : {
2768 3289470 : pszNext++;
2769 3289470 : nBytesAvail--;
2770 : }
2771 :
2772 56 : if (nBytesAvail == 0)
2773 : {
2774 2 : CPLError(CE_Failure, CPLE_AppDefined,
2775 : "Error while parsing multipart content (at line %d)",
2776 : __LINE__);
2777 2 : return FALSE;
2778 : }
2779 :
2780 54 : psPart->nDataLen = static_cast<int>(
2781 54 : pszNext - reinterpret_cast<char *>(psPart->pabyData));
2782 : // Normally the part should end with "\r\n--boundary_marker"
2783 54 : if (psPart->nDataLen >= 2 && pszNext[-2] == '\r' && pszNext[-1] == '\n')
2784 : {
2785 46 : psPart->nDataLen -= 2;
2786 : }
2787 :
2788 54 : pszNext += osBoundary.size();
2789 :
2790 54 : if (STARTS_WITH(pszNext, "--"))
2791 : {
2792 31 : break;
2793 : }
2794 :
2795 23 : if (*pszNext == '\r')
2796 19 : pszNext++;
2797 23 : if (*pszNext == '\n')
2798 21 : pszNext++;
2799 : else
2800 : {
2801 2 : CPLError(CE_Failure, CPLE_AppDefined,
2802 : "Error while parsing multipart content (at line %d)",
2803 : __LINE__);
2804 2 : return FALSE;
2805 : }
2806 21 : }
2807 :
2808 31 : return TRUE;
2809 : }
2810 :
2811 : #if defined(__GNUC__)
2812 : #pragma GCC diagnostic pop
2813 : #endif
|