1 : /******************************************************************************
2 : * $Id: cpl_http.cpp 25668 2013-02-22 18:47:08Z rouault $
3 : *
4 : * Project: libcurl based HTTP client
5 : * Purpose: libcurl based HTTP client
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2006, Frank Warmerdam
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 <map>
31 : #include "cpl_http.h"
32 : #include "cpl_multiproc.h"
33 :
34 : #ifdef HAVE_CURL
35 : # include <curl/curl.h>
36 :
37 : void CPLHTTPSetOptions(CURL *http_handle, char** papszOptions);
38 :
39 : /* CURLINFO_RESPONSE_CODE was known as CURLINFO_HTTP_CODE in libcurl 7.10.7 and earlier */
40 : #if LIBCURL_VERSION_NUM < 0x070a07
41 : #define CURLINFO_RESPONSE_CODE CURLINFO_HTTP_CODE
42 : #endif
43 :
44 : #endif
45 :
46 : CPL_CVSID("$Id: cpl_http.cpp 25668 2013-02-22 18:47:08Z rouault $");
47 :
48 : // list of named persistent http sessions
49 :
50 : #ifdef HAVE_CURL
51 758 : static std::map<CPLString,CURL*> oSessionMap;
52 : static void *hSessionMapMutex = NULL;
53 : #endif
54 :
55 : /************************************************************************/
56 : /* CPLWriteFct() */
57 : /* */
58 : /* Append incoming text to our collection buffer, reallocating */
59 : /* it larger as needed. */
60 : /************************************************************************/
61 :
62 : #ifdef HAVE_CURL
63 : static size_t
64 2949 : CPLWriteFct(void *buffer, size_t size, size_t nmemb, void *reqInfo)
65 :
66 : {
67 2949 : CPLHTTPResult *psResult = (CPLHTTPResult *) reqInfo;
68 : int nNewSize;
69 :
70 2949 : nNewSize = psResult->nDataLen + nmemb*size + 1;
71 2949 : if( nNewSize > psResult->nDataAlloc )
72 : {
73 593 : psResult->nDataAlloc = (int) (nNewSize * 1.25 + 100);
74 : GByte* pabyNewData = (GByte *) VSIRealloc(psResult->pabyData,
75 593 : psResult->nDataAlloc);
76 593 : if( pabyNewData == NULL )
77 : {
78 0 : VSIFree(psResult->pabyData);
79 0 : psResult->pabyData = NULL;
80 0 : psResult->pszErrBuf = CPLStrdup(CPLString().Printf("Out of memory allocating %d bytes for HTTP data buffer.", psResult->nDataAlloc));
81 0 : psResult->nDataAlloc = psResult->nDataLen = 0;
82 :
83 0 : return 0;
84 : }
85 593 : psResult->pabyData = pabyNewData;
86 : }
87 :
88 : memcpy( psResult->pabyData + psResult->nDataLen, buffer,
89 2949 : nmemb * size );
90 :
91 2949 : psResult->nDataLen += nmemb * size;
92 2949 : psResult->pabyData[psResult->nDataLen] = 0;
93 :
94 2949 : return nmemb;
95 : }
96 :
97 : /************************************************************************/
98 : /* CPLHdrWriteFct() */
99 : /************************************************************************/
100 1976 : static size_t CPLHdrWriteFct(void *buffer, size_t size, size_t nmemb, void *reqInfo)
101 : {
102 1976 : CPLHTTPResult *psResult = (CPLHTTPResult *) reqInfo;
103 : // copy the buffer to a char* and initialize with zeros (zero terminate as well)
104 1976 : char* pszHdr = (char*)CPLCalloc(nmemb + 1, size);
105 1976 : CPLPrintString(pszHdr, (char *)buffer, nmemb * size);
106 1976 : char *pszKey = NULL;
107 1976 : const char *pszValue = CPLParseNameValue(pszHdr, &pszKey );
108 1976 : psResult->papszHeaders = CSLSetNameValue(psResult->papszHeaders, pszKey, pszValue);
109 1976 : CPLFree(pszHdr);
110 1976 : CPLFree(pszKey);
111 1976 : return nmemb;
112 : }
113 :
114 : #endif /* def HAVE_CURL */
115 :
116 : /************************************************************************/
117 : /* CPLHTTPFetch() */
118 : /************************************************************************/
119 :
120 : /**
121 : * \brief Fetch a document from an url and return in a string.
122 : *
123 : * @param pszURL valid URL recognized by underlying download library (libcurl)
124 : * @param papszOptions option list as a NULL-terminated array of strings. May be NULL.
125 : * The following options are handled :
126 : * <ul>
127 : * <li>TIMEOUT=val, where val is in seconds</li>
128 : * <li>HEADERS=val, where val is an extra header to use when getting a web page.
129 : * For example "Accept: application/x-ogcwkt"
130 : * <li>HTTPAUTH=[BASIC/NTLM/GSSNEGOTIATE/ANY] to specify an authentication scheme to use.
131 : * <li>USERPWD=userid:password to specify a user and password for authentication
132 : * <li>POSTFIELDS=val, where val is a nul-terminated string to be passed to the server
133 : * with a POST request.
134 : * <li>PROXY=val, to make requests go through a proxy server, where val is of the
135 : * form proxy.server.com:port_number
136 : * <li>PROXYUSERPWD=val, where val is of the form username:password
137 : * <li>PROXYAUTH=[BASIC/NTLM/DIGEST/ANY] to specify an proxy authentication scheme to use.
138 : * <li>CUSTOMREQUEST=val, where val is GET, PUT, POST, DELETE, etc.. (GDAL >= 1.9.0)
139 : * </ul>
140 : *
141 : * Alternatively, if not defined in the papszOptions arguments, the PROXY,
142 : * PROXYUSERPWD and PROXYAUTH values are searched in the configuration options named
143 : * GDAL_HTTP_PROXY, GDAL_HTTP_PROXYUSERPWD and GDAL_PROXY_AUTH, as proxy configuration belongs
144 : * to networking setup and makes more sense at the configuration option level
145 : * than at the connection level.
146 : *
147 : * @return a CPLHTTPResult* structure that must be freed by
148 : * CPLHTTPDestroyResult(), or NULL if libcurl support is disabled
149 : */
150 259 : CPLHTTPResult *CPLHTTPFetch( const char *pszURL, char **papszOptions )
151 :
152 : {
153 : #ifndef HAVE_CURL
154 : (void) papszOptions;
155 : (void) pszURL;
156 :
157 : CPLError( CE_Failure, CPLE_NotSupported,
158 : "GDAL/OGR not compiled with libcurl support, remote requests not supported." );
159 : return NULL;
160 : #else
161 : /* -------------------------------------------------------------------- */
162 : /* Are we using a persistent named session? If so, search for */
163 : /* or create it. */
164 : /* */
165 : /* Currently this code does not attempt to protect against */
166 : /* multiple threads asking for the same named session. If that */
167 : /* occurs it will be in use in multiple threads at once which */
168 : /* might have bad consequences depending on what guarantees */
169 : /* libcurl gives - which I have not investigated. */
170 : /* -------------------------------------------------------------------- */
171 259 : CURL *http_handle = NULL;
172 :
173 259 : const char *pszPersistent = CSLFetchNameValue( papszOptions, "PERSISTENT" );
174 259 : const char *pszClosePersistent = CSLFetchNameValue( papszOptions, "CLOSE_PERSISTENT" );
175 259 : if (pszPersistent)
176 : {
177 102 : CPLString osSessionName = pszPersistent;
178 102 : CPLMutexHolder oHolder( &hSessionMapMutex );
179 :
180 102 : if( oSessionMap.count( osSessionName ) == 0 )
181 : {
182 24 : oSessionMap[osSessionName] = curl_easy_init();
183 : CPLDebug( "HTTP", "Establish persistent session named '%s'.",
184 24 : osSessionName.c_str() );
185 : }
186 :
187 102 : http_handle = oSessionMap[osSessionName];
188 : }
189 : /* -------------------------------------------------------------------- */
190 : /* Are we requested to close a persistent named session? */
191 : /* -------------------------------------------------------------------- */
192 157 : else if (pszClosePersistent)
193 : {
194 24 : CPLString osSessionName = pszClosePersistent;
195 24 : CPLMutexHolder oHolder( &hSessionMapMutex );
196 :
197 24 : std::map<CPLString,CURL*>::iterator oIter = oSessionMap.find( osSessionName );
198 24 : if( oIter != oSessionMap.end() )
199 : {
200 24 : curl_easy_cleanup(oIter->second);
201 24 : oSessionMap.erase(oIter);
202 : CPLDebug( "HTTP", "Ended persistent session named '%s'.",
203 24 : osSessionName.c_str() );
204 : }
205 : else
206 : {
207 : CPLDebug( "HTTP", "Could not find persistent session named '%s'.",
208 0 : osSessionName.c_str() );
209 : }
210 :
211 24 : return NULL;
212 : }
213 : else
214 133 : http_handle = curl_easy_init();
215 :
216 : /* -------------------------------------------------------------------- */
217 : /* Setup the request. */
218 : /* -------------------------------------------------------------------- */
219 : char szCurlErrBuf[CURL_ERROR_SIZE+1];
220 : CPLHTTPResult *psResult;
221 235 : struct curl_slist *headers=NULL;
222 :
223 235 : const char* pszArobase = strchr(pszURL, '@');
224 235 : const char* pszSlash = strchr(pszURL, '/');
225 235 : const char* pszColon = (pszSlash) ? strchr(pszSlash, ':') : NULL;
226 235 : if (pszArobase != NULL && pszColon != NULL && pszArobase - pszColon > 0)
227 : {
228 : /* http://user:password@www.example.com */
229 0 : char* pszSanitizedURL = CPLStrdup(pszURL);
230 0 : pszSanitizedURL[pszColon-pszURL] = 0;
231 0 : CPLDebug( "HTTP", "Fetch(%s:#password#%s)", pszSanitizedURL, pszArobase );
232 0 : CPLFree(pszSanitizedURL);
233 : }
234 : else
235 : {
236 235 : CPLDebug( "HTTP", "Fetch(%s)", pszURL );
237 : }
238 :
239 235 : psResult = (CPLHTTPResult *) CPLCalloc(1,sizeof(CPLHTTPResult));
240 :
241 235 : curl_easy_setopt(http_handle, CURLOPT_URL, pszURL );
242 :
243 235 : CPLHTTPSetOptions(http_handle, papszOptions);
244 :
245 : // turn off SSL verification, accept all servers with ssl
246 235 : curl_easy_setopt(http_handle, CURLOPT_SSL_VERIFYPEER, FALSE);
247 :
248 : /* Set Headers.*/
249 235 : const char *pszHeaders = CSLFetchNameValue( papszOptions, "HEADERS" );
250 235 : if( pszHeaders != NULL ) {
251 147 : CPLDebug ("HTTP", "These HTTP headers were set: %s", pszHeaders);
252 147 : headers = curl_slist_append(headers, pszHeaders);
253 147 : curl_easy_setopt(http_handle, CURLOPT_HTTPHEADER, headers);
254 : }
255 :
256 : // capture response headers
257 235 : curl_easy_setopt(http_handle, CURLOPT_HEADERDATA, psResult);
258 235 : curl_easy_setopt(http_handle, CURLOPT_HEADERFUNCTION, CPLHdrWriteFct);
259 :
260 235 : curl_easy_setopt(http_handle, CURLOPT_WRITEDATA, psResult );
261 235 : curl_easy_setopt(http_handle, CURLOPT_WRITEFUNCTION, CPLWriteFct );
262 :
263 235 : szCurlErrBuf[0] = '\0';
264 :
265 235 : curl_easy_setopt(http_handle, CURLOPT_ERRORBUFFER, szCurlErrBuf );
266 :
267 : static int bHasCheckVersion = FALSE;
268 : static int bSupportGZip = FALSE;
269 235 : if (!bHasCheckVersion)
270 : {
271 6 : bSupportGZip = strstr(curl_version(), "zlib/") != NULL;
272 6 : bHasCheckVersion = TRUE;
273 : }
274 235 : int bGZipRequested = FALSE;
275 235 : if (bSupportGZip && CSLTestBoolean(CPLGetConfigOption("CPL_CURL_GZIP", "YES")))
276 : {
277 235 : bGZipRequested = TRUE;
278 235 : curl_easy_setopt(http_handle, CURLOPT_ENCODING, "gzip");
279 : }
280 :
281 : /* -------------------------------------------------------------------- */
282 : /* Execute the request, waiting for results. */
283 : /* -------------------------------------------------------------------- */
284 235 : psResult->nStatus = (int) curl_easy_perform( http_handle );
285 :
286 : /* -------------------------------------------------------------------- */
287 : /* Fetch content-type if possible. */
288 : /* -------------------------------------------------------------------- */
289 : CURLcode err;
290 :
291 235 : psResult->pszContentType = NULL;
292 : err = curl_easy_getinfo( http_handle, CURLINFO_CONTENT_TYPE,
293 235 : &(psResult->pszContentType) );
294 235 : if( psResult->pszContentType != NULL )
295 227 : psResult->pszContentType = CPLStrdup(psResult->pszContentType);
296 :
297 : /* -------------------------------------------------------------------- */
298 : /* Have we encountered some sort of error? */
299 : /* -------------------------------------------------------------------- */
300 235 : if( strlen(szCurlErrBuf) > 0 )
301 : {
302 1 : int bSkipError = FALSE;
303 :
304 : /* Some servers such as http://115.113.193.14/cgi-bin/world/qgis_mapserv.fcgi?VERSION=1.1.1&SERVICE=WMS&REQUEST=GetCapabilities */
305 : /* invalidly return Content-Length as the uncompressed size, with makes curl to wait for more data */
306 : /* and time-out finally. If we got the expected data size, then we don't emit an error */
307 : /* but turn off GZip requests */
308 1 : if (bGZipRequested &&
309 : strstr(szCurlErrBuf, "transfer closed with") &&
310 : strstr(szCurlErrBuf, "bytes remaining to read"))
311 : {
312 : const char* pszContentLength =
313 0 : CSLFetchNameValue(psResult->papszHeaders, "Content-Length");
314 0 : if (pszContentLength && psResult->nDataLen != 0 &&
315 : atoi(pszContentLength) == psResult->nDataLen)
316 : {
317 0 : const char* pszCurlGZIPOption = CPLGetConfigOption("CPL_CURL_GZIP", NULL);
318 0 : if (pszCurlGZIPOption == NULL)
319 : {
320 0 : CPLSetConfigOption("CPL_CURL_GZIP", "NO");
321 : CPLDebug("HTTP", "Disabling CPL_CURL_GZIP, because %s doesn't support it properly",
322 0 : pszURL);
323 : }
324 0 : psResult->nStatus = 0;
325 0 : bSkipError = TRUE;
326 : }
327 : }
328 1 : if (!bSkipError)
329 : {
330 1 : psResult->pszErrBuf = CPLStrdup(szCurlErrBuf);
331 : CPLError( CE_Failure, CPLE_AppDefined,
332 1 : "%s", szCurlErrBuf );
333 : }
334 : }
335 : else
336 : {
337 : /* HTTP errors do not trigger curl errors. But we need to */
338 : /* propagate them to the caller though */
339 234 : long response_code = 0;
340 234 : curl_easy_getinfo(http_handle, CURLINFO_RESPONSE_CODE, &response_code);
341 234 : if (response_code >= 400 && response_code < 600)
342 : {
343 24 : psResult->pszErrBuf = CPLStrdup(CPLSPrintf("HTTP error code : %d", (int)response_code));
344 24 : CPLError( CE_Failure, CPLE_AppDefined, "%s", psResult->pszErrBuf );
345 : }
346 : }
347 :
348 235 : if (!pszPersistent)
349 133 : curl_easy_cleanup( http_handle );
350 :
351 235 : curl_slist_free_all(headers);
352 :
353 235 : return psResult;
354 : #endif /* def HAVE_CURL */
355 : }
356 :
357 : #ifdef HAVE_CURL
358 : /************************************************************************/
359 : /* CPLHTTPSetOptions() */
360 : /************************************************************************/
361 :
362 372 : void CPLHTTPSetOptions(CURL *http_handle, char** papszOptions)
363 : {
364 372 : if (CSLTestBoolean(CPLGetConfigOption("CPL_CURL_VERBOSE", "NO")))
365 0 : curl_easy_setopt(http_handle, CURLOPT_VERBOSE, 1);
366 :
367 372 : const char *pszHttpVersion = CSLFetchNameValue( papszOptions, "HTTP_VERSION");
368 372 : if( pszHttpVersion && strcmp(pszHttpVersion, "1.0") == 0 )
369 0 : curl_easy_setopt(http_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0 );
370 :
371 : /* Support control over HTTPAUTH */
372 372 : const char *pszHttpAuth = CSLFetchNameValue( papszOptions, "HTTPAUTH" );
373 372 : if( pszHttpAuth == NULL )
374 372 : pszHttpAuth = CPLGetConfigOption( "GDAL_HTTP_AUTH", NULL );
375 372 : if( pszHttpAuth == NULL )
376 : /* do nothing */;
377 :
378 : /* CURLOPT_HTTPAUTH is defined in curl 7.11.0 or newer */
379 : #if LIBCURL_VERSION_NUM >= 0x70B00
380 0 : else if( EQUAL(pszHttpAuth,"BASIC") )
381 0 : curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC );
382 0 : else if( EQUAL(pszHttpAuth,"NTLM") )
383 0 : curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH, CURLAUTH_NTLM );
384 0 : else if( EQUAL(pszHttpAuth,"ANY") )
385 0 : curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY );
386 : #ifdef CURLAUTH_GSSNEGOTIATE
387 0 : else if( EQUAL(pszHttpAuth,"NEGOTIATE") )
388 0 : curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH, CURLAUTH_GSSNEGOTIATE );
389 : #endif
390 : else
391 : {
392 : CPLError( CE_Warning, CPLE_AppDefined,
393 : "Unsupported HTTPAUTH value '%s', ignored.",
394 0 : pszHttpAuth );
395 : }
396 : #else
397 : else
398 : {
399 : CPLError( CE_Warning, CPLE_AppDefined,
400 : "HTTPAUTH option needs curl >= 7.11.0" );
401 : }
402 : #endif
403 :
404 : /* Support setting userid:password */
405 372 : const char *pszUserPwd = CSLFetchNameValue( papszOptions, "USERPWD" );
406 372 : if (pszUserPwd == NULL)
407 372 : pszUserPwd = CPLGetConfigOption("GDAL_HTTP_USERPWD", NULL);
408 372 : if( pszUserPwd != NULL )
409 0 : curl_easy_setopt(http_handle, CURLOPT_USERPWD, pszUserPwd );
410 :
411 : /* Set Proxy parameters */
412 372 : const char* pszProxy = CSLFetchNameValue( papszOptions, "PROXY" );
413 372 : if (pszProxy == NULL)
414 372 : pszProxy = CPLGetConfigOption("GDAL_HTTP_PROXY", NULL);
415 372 : if (pszProxy)
416 0 : curl_easy_setopt(http_handle,CURLOPT_PROXY,pszProxy);
417 :
418 372 : const char* pszProxyUserPwd = CSLFetchNameValue( papszOptions, "PROXYUSERPWD" );
419 372 : if (pszProxyUserPwd == NULL)
420 372 : pszProxyUserPwd = CPLGetConfigOption("GDAL_HTTP_PROXYUSERPWD", NULL);
421 372 : if (pszProxyUserPwd)
422 0 : curl_easy_setopt(http_handle,CURLOPT_PROXYUSERPWD,pszProxyUserPwd);
423 :
424 : /* Support control over PROXYAUTH */
425 372 : const char *pszProxyAuth = CSLFetchNameValue( papszOptions, "PROXYAUTH" );
426 372 : if( pszProxyAuth == NULL )
427 372 : pszProxyAuth = CPLGetConfigOption( "GDAL_PROXY_AUTH", NULL );
428 372 : if( pszProxyAuth == NULL )
429 : /* do nothing */;
430 : /* CURLOPT_PROXYAUTH is defined in curl 7.11.0 or newer */
431 : #if LIBCURL_VERSION_NUM >= 0x70B00
432 0 : else if( EQUAL(pszProxyAuth,"BASIC") )
433 0 : curl_easy_setopt(http_handle, CURLOPT_PROXYAUTH, CURLAUTH_BASIC );
434 0 : else if( EQUAL(pszProxyAuth,"NTLM") )
435 0 : curl_easy_setopt(http_handle, CURLOPT_PROXYAUTH, CURLAUTH_NTLM );
436 0 : else if( EQUAL(pszProxyAuth,"DIGEST") )
437 0 : curl_easy_setopt(http_handle, CURLOPT_PROXYAUTH, CURLAUTH_DIGEST );
438 0 : else if( EQUAL(pszProxyAuth,"ANY") )
439 0 : curl_easy_setopt(http_handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY );
440 : else
441 : {
442 : CPLError( CE_Warning, CPLE_AppDefined,
443 : "Unsupported PROXYAUTH value '%s', ignored.",
444 0 : pszProxyAuth );
445 : }
446 : #else
447 : else
448 : {
449 : CPLError( CE_Warning, CPLE_AppDefined,
450 : "PROXYAUTH option needs curl >= 7.11.0" );
451 : }
452 : #endif
453 :
454 : /* Enable following redirections. Requires libcurl 7.10.1 at least */
455 372 : curl_easy_setopt(http_handle, CURLOPT_FOLLOWLOCATION, 1 );
456 372 : curl_easy_setopt(http_handle, CURLOPT_MAXREDIRS, 10 );
457 :
458 : /* Set timeout.*/
459 372 : const char *pszTimeout = CSLFetchNameValue( papszOptions, "TIMEOUT" );
460 372 : if (pszTimeout == NULL)
461 357 : pszTimeout = CPLGetConfigOption("GDAL_HTTP_TIMEOUT", NULL);
462 372 : if( pszTimeout != NULL )
463 15 : curl_easy_setopt(http_handle, CURLOPT_TIMEOUT, atoi(pszTimeout) );
464 :
465 : /* Disable some SSL verification */
466 372 : const char *pszUnsafeSSL = CSLFetchNameValue( papszOptions, "UNSAFESSL" );
467 372 : if (pszUnsafeSSL == NULL)
468 372 : pszUnsafeSSL = CPLGetConfigOption("GDAL_HTTP_UNSAFESSL", NULL);
469 372 : if (pszUnsafeSSL != NULL && CSLTestBoolean(pszUnsafeSSL))
470 : {
471 0 : curl_easy_setopt(http_handle, CURLOPT_SSL_VERIFYPEER, 0L);
472 0 : curl_easy_setopt(http_handle, CURLOPT_SSL_VERIFYHOST, 0L);
473 : }
474 :
475 : /* Set Referer */
476 372 : const char *pszReferer = CSLFetchNameValue(papszOptions, "REFERER");
477 372 : if (pszReferer != NULL)
478 0 : curl_easy_setopt(http_handle, CURLOPT_REFERER, pszReferer);
479 :
480 : /* Set User-Agent */
481 372 : const char *pszUserAgent = CSLFetchNameValue(papszOptions, "USERAGENT");
482 372 : if (pszUserAgent == NULL)
483 365 : pszUserAgent = CPLGetConfigOption("GDAL_HTTP_USERAGENT", NULL);
484 372 : if (pszUserAgent != NULL)
485 7 : curl_easy_setopt(http_handle, CURLOPT_USERAGENT, pszUserAgent);
486 :
487 : /* NOSIGNAL should be set to true for timeout to work in multithread
488 : * environments on Unix, requires libcurl 7.10 or more recent.
489 : * (this force avoiding the use of sgnal handlers)
490 : */
491 : #ifdef CURLOPT_NOSIGNAL
492 : curl_easy_setopt(http_handle, CURLOPT_NOSIGNAL, 1 );
493 : #endif
494 :
495 : /* Set POST mode */
496 372 : const char* pszPost = CSLFetchNameValue( papszOptions, "POSTFIELDS" );
497 372 : if( pszPost != NULL )
498 : {
499 114 : curl_easy_setopt(http_handle, CURLOPT_POST, 1 );
500 114 : curl_easy_setopt(http_handle, CURLOPT_POSTFIELDS, pszPost );
501 : }
502 :
503 372 : const char* pszCustomRequest = CSLFetchNameValue( papszOptions, "CUSTOMREQUEST" );
504 372 : if( pszCustomRequest != NULL )
505 : {
506 64 : curl_easy_setopt(http_handle, CURLOPT_CUSTOMREQUEST, pszCustomRequest );
507 : }
508 372 : }
509 : #endif /* def HAVE_CURL */
510 :
511 : /************************************************************************/
512 : /* CPLHTTPEnabled() */
513 : /************************************************************************/
514 :
515 : /**
516 : * \brief Return if CPLHTTP services can be usefull
517 : *
518 : * Those services depend on GDAL being build with libcurl support.
519 : *
520 : * @return TRUE if libcurl support is enabled
521 : */
522 3 : int CPLHTTPEnabled()
523 :
524 : {
525 : #ifdef HAVE_CURL
526 3 : return TRUE;
527 : #else
528 : return FALSE;
529 : #endif
530 : }
531 :
532 : /************************************************************************/
533 : /* CPLHTTPCleanup() */
534 : /************************************************************************/
535 :
536 : /**
537 : * \brief Cleanup function to call at application termination
538 : */
539 0 : void CPLHTTPCleanup()
540 :
541 : {
542 : #ifdef HAVE_CURL
543 0 : if( !hSessionMapMutex )
544 0 : return;
545 :
546 : {
547 0 : CPLMutexHolder oHolder( &hSessionMapMutex );
548 0 : std::map<CPLString,CURL*>::iterator oIt;
549 :
550 0 : for( oIt=oSessionMap.begin(); oIt != oSessionMap.end(); oIt++ )
551 0 : curl_easy_cleanup( oIt->second );
552 :
553 0 : oSessionMap.clear();
554 : }
555 :
556 : // not quite a safe sequence.
557 0 : CPLDestroyMutex( hSessionMapMutex );
558 0 : hSessionMapMutex = NULL;
559 : #endif
560 : }
561 :
562 : /************************************************************************/
563 : /* CPLHTTPDestroyResult() */
564 : /************************************************************************/
565 :
566 : /**
567 : * \brief Clean the memory associated with the return value of CPLHTTPFetch()
568 : *
569 : * @param psResult pointer to the return value of CPLHTTPFetch()
570 : */
571 235 : void CPLHTTPDestroyResult( CPLHTTPResult *psResult )
572 :
573 : {
574 235 : if( psResult )
575 : {
576 235 : CPLFree( psResult->pabyData );
577 235 : CPLFree( psResult->pszErrBuf );
578 235 : CPLFree( psResult->pszContentType );
579 235 : CSLDestroy( psResult->papszHeaders );
580 :
581 : int i;
582 235 : for(i=0;i<psResult->nMimePartCount;i++)
583 : {
584 0 : CSLDestroy( psResult->pasMimePart[i].papszHeaders );
585 : }
586 235 : CPLFree(psResult->pasMimePart);
587 :
588 235 : CPLFree( psResult );
589 : }
590 235 : }
591 :
592 : /************************************************************************/
593 : /* CPLHTTPParseMultipartMime() */
594 : /************************************************************************/
595 :
596 : /**
597 : * \brief Parses a a MIME multipart message
598 : *
599 : * This function will iterate over each part and put it in a separate
600 : * element of the pasMimePart array of the provided psResult structure.
601 : *
602 : * @param psResult pointer to the return value of CPLHTTPFetch()
603 : * @return TRUE if the message contains MIME multipart message.
604 : */
605 0 : int CPLHTTPParseMultipartMime( CPLHTTPResult *psResult )
606 :
607 : {
608 : /* -------------------------------------------------------------------- */
609 : /* Is it already done? */
610 : /* -------------------------------------------------------------------- */
611 0 : if( psResult->nMimePartCount > 0 )
612 0 : return TRUE;
613 :
614 : /* -------------------------------------------------------------------- */
615 : /* Find the boundary setting in the content type. */
616 : /* -------------------------------------------------------------------- */
617 0 : const char *pszBound = NULL;
618 :
619 0 : if( psResult->pszContentType != NULL )
620 0 : pszBound = strstr(psResult->pszContentType,"boundary=");
621 :
622 0 : if( pszBound == NULL )
623 : {
624 : CPLError( CE_Failure, CPLE_AppDefined,
625 0 : "Unable to parse multi-part mime, no boundary setting." );
626 0 : return FALSE;
627 : }
628 :
629 0 : CPLString osBoundary;
630 : char **papszTokens =
631 : CSLTokenizeStringComplex( pszBound + 9, "\n ;",
632 0 : TRUE, FALSE );
633 :
634 0 : if( CSLCount(papszTokens) == 0 || strlen(papszTokens[0]) == 0 )
635 : {
636 : CPLError( CE_Failure, CPLE_AppDefined,
637 0 : "Unable to parse multi-part mime, boundary not parsable." );
638 0 : CSLDestroy( papszTokens );
639 0 : return FALSE;
640 : }
641 :
642 0 : osBoundary = "--";
643 0 : osBoundary += papszTokens[0];
644 0 : CSLDestroy( papszTokens );
645 :
646 : /* -------------------------------------------------------------------- */
647 : /* Find the start of the first chunk. */
648 : /* -------------------------------------------------------------------- */
649 : char *pszNext;
650 : pszNext = (char *)
651 0 : strstr((const char *) psResult->pabyData,osBoundary.c_str());
652 :
653 0 : if( pszNext == NULL )
654 : {
655 0 : CPLError( CE_Failure, CPLE_AppDefined, "No parts found." );
656 0 : return FALSE;
657 : }
658 :
659 0 : pszNext += strlen(osBoundary);
660 0 : while( *pszNext != '\n' && *pszNext != '\r' && *pszNext != '\0' )
661 0 : pszNext++;
662 0 : if( *pszNext == '\r' )
663 0 : pszNext++;
664 0 : if( *pszNext == '\n' )
665 0 : pszNext++;
666 :
667 : /* -------------------------------------------------------------------- */
668 : /* Loop over parts... */
669 : /* -------------------------------------------------------------------- */
670 0 : while( TRUE )
671 : {
672 0 : psResult->nMimePartCount++;
673 : psResult->pasMimePart = (CPLMimePart *)
674 : CPLRealloc(psResult->pasMimePart,
675 0 : sizeof(CPLMimePart) * psResult->nMimePartCount );
676 :
677 0 : CPLMimePart *psPart = psResult->pasMimePart+psResult->nMimePartCount-1;
678 :
679 0 : memset( psPart, 0, sizeof(CPLMimePart) );
680 :
681 : /* -------------------------------------------------------------------- */
682 : /* Collect headers. */
683 : /* -------------------------------------------------------------------- */
684 0 : while( *pszNext != '\n' && *pszNext != '\r' && *pszNext != '\0' )
685 : {
686 0 : char *pszEOL = strstr(pszNext,"\n");
687 :
688 0 : if( pszEOL == NULL )
689 : {
690 : CPLError(CE_Failure, CPLE_AppDefined,
691 0 : "Error while parsing multipart content (at line %d)", __LINE__);
692 0 : return FALSE;
693 : }
694 :
695 0 : *pszEOL = '\0';
696 0 : int bRestoreAntislashR = FALSE;
697 0 : if (pszEOL - pszNext > 1 && pszEOL[-1] == '\r')
698 : {
699 0 : bRestoreAntislashR = TRUE;
700 0 : pszEOL[-1] = '\0';
701 : }
702 : psPart->papszHeaders =
703 0 : CSLAddString( psPart->papszHeaders, pszNext );
704 0 : if (bRestoreAntislashR)
705 0 : pszEOL[-1] = '\r';
706 0 : *pszEOL = '\n';
707 :
708 0 : pszNext = pszEOL + 1;
709 : }
710 :
711 0 : if( *pszNext == '\r' )
712 0 : pszNext++;
713 0 : if( *pszNext == '\n' )
714 0 : pszNext++;
715 :
716 : /* -------------------------------------------------------------------- */
717 : /* Work out the data block size. */
718 : /* -------------------------------------------------------------------- */
719 0 : psPart->pabyData = (GByte *) pszNext;
720 :
721 : int nBytesAvail = psResult->nDataLen -
722 0 : (pszNext - (const char *) psResult->pabyData);
723 :
724 0 : while( nBytesAvail > 0
725 : && (*pszNext != '-'
726 : || strncmp(pszNext,osBoundary,strlen(osBoundary)) != 0) )
727 : {
728 0 : pszNext++;
729 0 : nBytesAvail--;
730 : }
731 :
732 0 : if( nBytesAvail == 0 )
733 : {
734 : CPLError(CE_Failure, CPLE_AppDefined,
735 0 : "Error while parsing multipart content (at line %d)", __LINE__);
736 0 : return FALSE;
737 : }
738 :
739 0 : psPart->nDataLen = pszNext - (const char *) psPart->pabyData;
740 0 : pszNext += strlen(osBoundary);
741 :
742 0 : if( strncmp(pszNext,"--",2) == 0 )
743 : {
744 : break;
745 : }
746 :
747 0 : if( *pszNext == '\r' )
748 0 : pszNext++;
749 0 : if( *pszNext == '\n' )
750 0 : pszNext++;
751 : else
752 : {
753 : CPLError(CE_Failure, CPLE_AppDefined,
754 0 : "Error while parsing multipart content (at line %d)", __LINE__);
755 0 : return FALSE;
756 : }
757 : }
758 :
759 0 : return TRUE;
760 2274 : }
|