1 : /******************************************************************************
2 : * $Id: cpl_http.cpp 19162 2010-03-22 16:45:08Z warmerdam $
3 : *
4 : * Project: WCS Client Driver
5 : * Purpose: Implementation of Dataset and RasterBand classes for WCS.
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 : #endif
37 :
38 : CPL_CVSID("$Id: cpl_http.cpp 19162 2010-03-22 16:45:08Z warmerdam $");
39 :
40 : // list of named persistent http sessions
41 :
42 : #ifdef HAVE_CURL
43 896 : static std::map<CPLString,CURL*> oSessionMap;
44 : static void *hSessionMapMutex = NULL;
45 : #endif
46 :
47 : /************************************************************************/
48 : /* CPLWriteFct() */
49 : /* */
50 : /* Append incoming text to our collection buffer, reallocating */
51 : /* it larger as needed. */
52 : /************************************************************************/
53 :
54 : #ifdef HAVE_CURL
55 : static size_t
56 26 : CPLWriteFct(void *buffer, size_t size, size_t nmemb, void *reqInfo)
57 :
58 : {
59 26 : CPLHTTPResult *psResult = (CPLHTTPResult *) reqInfo;
60 : int nNewSize;
61 :
62 26 : nNewSize = psResult->nDataLen + nmemb*size + 1;
63 26 : if( nNewSize > psResult->nDataAlloc )
64 : {
65 12 : psResult->nDataAlloc = (int) (nNewSize * 1.25 + 100);
66 : GByte* pabyNewData = (GByte *) VSIRealloc(psResult->pabyData,
67 12 : psResult->nDataAlloc);
68 12 : if( pabyNewData == NULL )
69 : {
70 0 : VSIFree(psResult->pabyData);
71 0 : psResult->pabyData = NULL;
72 0 : psResult->pszErrBuf = CPLStrdup(CPLString().Printf("Out of memory allocating %d bytes for HTTP data buffer.", psResult->nDataAlloc));
73 0 : psResult->nDataAlloc = psResult->nDataLen = 0;
74 :
75 0 : return 0;
76 : }
77 12 : psResult->pabyData = pabyNewData;
78 : }
79 :
80 : memcpy( psResult->pabyData + psResult->nDataLen, buffer,
81 26 : nmemb * size );
82 :
83 26 : psResult->nDataLen += nmemb * size;
84 26 : psResult->pabyData[psResult->nDataLen] = 0;
85 :
86 26 : return nmemb;
87 : }
88 :
89 : /************************************************************************/
90 : /* CPLHdrWriteFct() */
91 : /************************************************************************/
92 24 : static size_t CPLHdrWriteFct(void *buffer, size_t size, size_t nmemb, void *reqInfo)
93 : {
94 24 : CPLHTTPResult *psResult = (CPLHTTPResult *) reqInfo;
95 : // copy the buffer to a char* and initialize with zeros (zero terminate as well)
96 24 : char* pszHdr = (char*)CPLCalloc(nmemb + 1, size);
97 24 : CPLPrintString(pszHdr, (char *)buffer, nmemb * size);
98 24 : char *pszKey = NULL;
99 24 : const char *pszValue = CPLParseNameValue(pszHdr, &pszKey );
100 24 : psResult->papszHeaders = CSLSetNameValue(psResult->papszHeaders, pszKey, pszValue);
101 24 : CPLFree(pszHdr);
102 24 : CPLFree(pszKey);
103 24 : return nmemb;
104 : }
105 :
106 : #endif /* def HAVE_CURL */
107 :
108 : /************************************************************************/
109 : /* CPLHTTPFetch() */
110 : /************************************************************************/
111 :
112 : /**
113 : * \brief Fetch a document from an url and return in a string.
114 : *
115 : * @param pszURL valid URL recognized by underlying download library (libcurl)
116 : * @param papszOptions option list as a NULL-terminated array of strings. May be NULL.
117 : * The following options are handled :
118 : * <ul>
119 : * <li>TIMEOUT=val, where val is in seconds</li>
120 : * <li>HEADERS=val, where val is an extra header to use when getting a web page.
121 : * For example "Accept: application/x-ogcwkt"
122 : * <li>HTTPAUTH=[BASIC/NTLM/ANY] to specify an authentication scheme to use.
123 : * <li>USERPWD=userid:password to specify a user and password for authentication
124 : * </ul>
125 : *
126 : * @return a CPLHTTPResult* structure that must be freed by CPLHTTPDestroyResult(),
127 : * or NULL if libcurl support is diabled
128 : */
129 3 : CPLHTTPResult *CPLHTTPFetch( const char *pszURL, char **papszOptions )
130 :
131 : {
132 : #ifndef HAVE_CURL
133 : CPLError( CE_Failure, CPLE_NotSupported,
134 : "GDAL/OGR not compiled with libcurl support, remote requests not supported." );
135 : return NULL;
136 : #else
137 : /* -------------------------------------------------------------------- */
138 : /* Are we using a persistent named session? If so, search for */
139 : /* or create it. */
140 : /* */
141 : /* Currently this code does not attempt to protect against */
142 : /* multiple threads asking for the same named session. If that */
143 : /* occurs it will be in use in multiple threads at once which */
144 : /* might have bad consequences depending on what guarantees */
145 : /* libcurl gives - which I have not investigated. */
146 : /* -------------------------------------------------------------------- */
147 3 : CURL *http_handle = NULL;
148 :
149 3 : const char *pszPersistent = CSLFetchNameValue( papszOptions, "PERSISTENT" );
150 3 : if (pszPersistent)
151 : {
152 0 : CPLString osSessionName = pszPersistent;
153 0 : CPLMutexHolder oHolder( &hSessionMapMutex );
154 :
155 0 : if( oSessionMap.count( osSessionName ) == 0 )
156 : {
157 0 : oSessionMap[osSessionName] = curl_easy_init();
158 : CPLDebug( "HTTP", "Establish persistent session named '%s'.",
159 0 : osSessionName.c_str() );
160 : }
161 :
162 0 : http_handle = oSessionMap[osSessionName];
163 : }
164 : else
165 3 : http_handle = curl_easy_init();
166 :
167 : /* -------------------------------------------------------------------- */
168 : /* Setup the request. */
169 : /* -------------------------------------------------------------------- */
170 : char szCurlErrBuf[CURL_ERROR_SIZE+1];
171 : CPLHTTPResult *psResult;
172 3 : struct curl_slist *headers=NULL;
173 :
174 3 : CPLDebug( "HTTP", "Fetch(%s)", pszURL );
175 :
176 3 : psResult = (CPLHTTPResult *) CPLCalloc(1,sizeof(CPLHTTPResult));
177 :
178 3 : curl_easy_setopt(http_handle, CURLOPT_URL, pszURL );
179 :
180 : /* Support control over HTTPAUTH */
181 3 : const char *pszHttpAuth = CSLFetchNameValue( papszOptions, "HTTPAUTH" );
182 3 : if( pszHttpAuth == NULL )
183 : /* do nothing */;
184 :
185 : /* CURLOPT_HTTPAUTH is defined in curl 7.11.0 or newer */
186 : #if LIBCURL_VERSION_NUM >= 0x70B00
187 0 : else if( EQUAL(pszHttpAuth,"BASIC") )
188 0 : curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC );
189 0 : else if( EQUAL(pszHttpAuth,"NTLM") )
190 0 : curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH, CURLAUTH_NTLM );
191 0 : else if( EQUAL(pszHttpAuth,"ANY") )
192 0 : curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY );
193 : else
194 : {
195 : CPLError( CE_Warning, CPLE_AppDefined,
196 : "Unsupported HTTPAUTH value '%s', ignored.",
197 0 : pszHttpAuth );
198 : }
199 : #else
200 : else
201 : {
202 : CPLError( CE_Warning, CPLE_AppDefined,
203 : "HTTPAUTH option needs curl >= 7.11.0" );
204 : }
205 : #endif
206 :
207 : /* Support setting userid:password */
208 3 : const char *pszUserPwd = CSLFetchNameValue( papszOptions, "USERPWD" );
209 3 : if( pszUserPwd != NULL )
210 0 : curl_easy_setopt(http_handle, CURLOPT_USERPWD, pszUserPwd );
211 :
212 : // turn off SSL verification, accept all servers with ssl
213 3 : curl_easy_setopt(http_handle, CURLOPT_SSL_VERIFYPEER, FALSE);
214 :
215 : /* Enable following redirections. Requires libcurl 7.10.1 at least */
216 3 : curl_easy_setopt(http_handle, CURLOPT_FOLLOWLOCATION, 1 );
217 3 : curl_easy_setopt(http_handle, CURLOPT_MAXREDIRS, 10 );
218 :
219 : /* Set timeout.*/
220 3 : const char *pszTimeout = CSLFetchNameValue( papszOptions, "TIMEOUT" );
221 3 : if( pszTimeout != NULL )
222 : curl_easy_setopt(http_handle, CURLOPT_TIMEOUT,
223 2 : atoi(pszTimeout) );
224 :
225 : /* Set Headers.*/
226 3 : const char *pszHeaders = CSLFetchNameValue( papszOptions, "HEADERS" );
227 3 : if( pszHeaders != NULL ) {
228 2 : CPLDebug ("HTTP", "These HTTP headers were set: %s", pszHeaders);
229 2 : headers = curl_slist_append(headers, pszHeaders);
230 2 : curl_easy_setopt(http_handle, CURLOPT_HTTPHEADER, headers);
231 : }
232 :
233 : /* NOSIGNAL should be set to true for timeout to work in multithread
234 : * environments on Unix, requires libcurl 7.10 or more recent.
235 : * (this force avoiding the use of sgnal handlers)
236 : */
237 : #ifdef CURLOPT_NOSIGNAL
238 : curl_easy_setopt(http_handle, CURLOPT_NOSIGNAL, 1 );
239 : #endif
240 :
241 : // capture response headers
242 3 : curl_easy_setopt(http_handle, CURLOPT_HEADERDATA, psResult);
243 3 : curl_easy_setopt(http_handle, CURLOPT_HEADERFUNCTION, CPLHdrWriteFct);
244 :
245 3 : curl_easy_setopt(http_handle, CURLOPT_WRITEDATA, psResult );
246 3 : curl_easy_setopt(http_handle, CURLOPT_WRITEFUNCTION, CPLWriteFct );
247 :
248 3 : szCurlErrBuf[0] = '\0';
249 :
250 3 : curl_easy_setopt(http_handle, CURLOPT_ERRORBUFFER, szCurlErrBuf );
251 :
252 : /* -------------------------------------------------------------------- */
253 : /* Execute the request, waiting for results. */
254 : /* -------------------------------------------------------------------- */
255 3 : psResult->nStatus = (int) curl_easy_perform( http_handle );
256 :
257 : /* -------------------------------------------------------------------- */
258 : /* Fetch content-type if possible. */
259 : /* -------------------------------------------------------------------- */
260 : CURLcode err;
261 :
262 3 : psResult->pszContentType = NULL;
263 : err = curl_easy_getinfo( http_handle, CURLINFO_CONTENT_TYPE,
264 3 : &(psResult->pszContentType) );
265 3 : if( psResult->pszContentType != NULL )
266 3 : psResult->pszContentType = CPLStrdup(psResult->pszContentType);
267 :
268 : /* -------------------------------------------------------------------- */
269 : /* Have we encountered some sort of error? */
270 : /* -------------------------------------------------------------------- */
271 3 : if( strlen(szCurlErrBuf) > 0 )
272 : {
273 0 : psResult->pszErrBuf = CPLStrdup(szCurlErrBuf);
274 : CPLError( CE_Failure, CPLE_AppDefined,
275 0 : "%s", szCurlErrBuf );
276 : }
277 :
278 3 : if (!pszPersistent)
279 3 : curl_easy_cleanup( http_handle );
280 :
281 3 : curl_slist_free_all(headers);
282 :
283 3 : return psResult;
284 : #endif /* def HAVE_CURL */
285 : }
286 :
287 : /************************************************************************/
288 : /* CPLHTTPEnabled() */
289 : /************************************************************************/
290 :
291 : /**
292 : * \brief Return if CPLHTTP services can be usefull
293 : *
294 : * Those services depend on GDAL being build with libcurl support.
295 : *
296 : * @return TRUE if libcurl support is enabled
297 : */
298 0 : int CPLHTTPEnabled()
299 :
300 : {
301 : #ifdef HAVE_CURL
302 0 : return TRUE;
303 : #else
304 : return FALSE;
305 : #endif
306 : }
307 :
308 : /************************************************************************/
309 : /* CPLHTTPCleanup() */
310 : /************************************************************************/
311 :
312 : /**
313 : * \brief Cleanup function to call at application termination
314 : */
315 0 : void CPLHTTPCleanup()
316 :
317 : {
318 : #ifdef HAVE_CURL
319 0 : if( !hSessionMapMutex )
320 0 : return;
321 :
322 : {
323 0 : CPLMutexHolder oHolder( &hSessionMapMutex );
324 0 : std::map<CPLString,CURL*>::iterator oIt;
325 :
326 0 : for( oIt=oSessionMap.begin(); oIt != oSessionMap.end(); oIt++ )
327 0 : curl_easy_cleanup( oIt->second );
328 :
329 0 : oSessionMap.clear();
330 : }
331 :
332 : // not quite a safe sequence.
333 0 : CPLDestroyMutex( hSessionMapMutex );
334 0 : hSessionMapMutex = NULL;
335 : #endif
336 : }
337 :
338 : /************************************************************************/
339 : /* CPLHTTPDestroyResult() */
340 : /************************************************************************/
341 :
342 : /**
343 : * \brief Clean the memory associated with the return value of CPLHTTPFetch()
344 : *
345 : * @param psResult pointer to the return value of CPLHTTPFetch()
346 : */
347 3 : void CPLHTTPDestroyResult( CPLHTTPResult *psResult )
348 :
349 : {
350 3 : if( psResult )
351 : {
352 3 : CPLFree( psResult->pabyData );
353 3 : CPLFree( psResult->pszErrBuf );
354 3 : CPLFree( psResult->pszContentType );
355 3 : CSLDestroy( psResult->papszHeaders );
356 3 : CPLFree( psResult );
357 : }
358 3 : }
359 :
360 : /************************************************************************/
361 : /* CPLHTTPParseMultipartMime() */
362 : /************************************************************************/
363 :
364 : /**
365 : * \brief Parses a a MIME multipart message
366 : *
367 : * This function will iterate over each part and put it in a separate
368 : * element of the pasMimePart array of the provided psResult structure.
369 : *
370 : * @param psResult pointer to the return value of CPLHTTPFetch()
371 : * @return TRUE if the message contains MIME multipart message.
372 : */
373 0 : int CPLHTTPParseMultipartMime( CPLHTTPResult *psResult )
374 :
375 : {
376 : /* -------------------------------------------------------------------- */
377 : /* Is it already done? */
378 : /* -------------------------------------------------------------------- */
379 0 : if( psResult->nMimePartCount > 0 )
380 0 : return TRUE;
381 :
382 : /* -------------------------------------------------------------------- */
383 : /* Find the boundary setting in the content type. */
384 : /* -------------------------------------------------------------------- */
385 0 : const char *pszBound = NULL;
386 :
387 0 : if( psResult->pszContentType != NULL )
388 0 : pszBound = strstr(psResult->pszContentType,"boundary=");
389 :
390 0 : if( pszBound == NULL )
391 : {
392 : CPLError( CE_Failure, CPLE_AppDefined,
393 0 : "Unable to parse multi-part mime, no boundary setting." );
394 0 : return FALSE;
395 : }
396 :
397 0 : CPLString osBoundary;
398 : char **papszTokens =
399 : CSLTokenizeStringComplex( pszBound + 9, "\n ;",
400 0 : TRUE, FALSE );
401 :
402 0 : if( CSLCount(papszTokens) == 0 || strlen(papszTokens[0]) == 0 )
403 : {
404 : CPLError( CE_Failure, CPLE_AppDefined,
405 0 : "Unable to parse multi-part mime, boundary not parsable." );
406 0 : return FALSE;
407 : }
408 :
409 0 : osBoundary = "--";
410 0 : osBoundary += papszTokens[0];
411 0 : CSLDestroy( papszTokens );
412 :
413 : /* -------------------------------------------------------------------- */
414 : /* Find the start of the first chunk. */
415 : /* -------------------------------------------------------------------- */
416 : char *pszNext;
417 : pszNext = (char *)
418 0 : strstr((const char *) psResult->pabyData,osBoundary.c_str());
419 :
420 0 : if( pszNext == NULL )
421 : {
422 0 : CPLError( CE_Failure, CPLE_AppDefined, "No parts found." );
423 0 : return FALSE;
424 : }
425 :
426 0 : pszNext += strlen(osBoundary);
427 0 : while( *pszNext != '\n' && *pszNext != '\0' )
428 0 : pszNext++;
429 0 : if( *pszNext == '\n' )
430 0 : pszNext++;
431 :
432 : /* -------------------------------------------------------------------- */
433 : /* Loop over parts... */
434 : /* -------------------------------------------------------------------- */
435 0 : while( TRUE )
436 : {
437 0 : psResult->nMimePartCount++;
438 : psResult->pasMimePart = (CPLMimePart *)
439 : CPLRealloc(psResult->pasMimePart,
440 0 : sizeof(CPLMimePart) * psResult->nMimePartCount );
441 :
442 0 : CPLMimePart *psPart = psResult->pasMimePart+psResult->nMimePartCount-1;
443 :
444 0 : memset( psPart, 0, sizeof(CPLMimePart) );
445 :
446 : /* -------------------------------------------------------------------- */
447 : /* Collect headers. */
448 : /* -------------------------------------------------------------------- */
449 0 : while( *pszNext != '\n' && *pszNext != '\0' )
450 : {
451 0 : char *pszEOL = strstr(pszNext,"\n");
452 :
453 0 : if( pszEOL == NULL )
454 : {
455 0 : CPLAssert( FALSE );
456 0 : break;
457 : }
458 :
459 0 : *pszEOL = '\0';
460 : psPart->papszHeaders =
461 0 : CSLAddString( psPart->papszHeaders, pszNext );
462 0 : *pszEOL = '\n';
463 :
464 0 : pszNext = pszEOL + 1;
465 : }
466 :
467 0 : if( *pszNext == '\n' )
468 0 : pszNext++;
469 :
470 : /* -------------------------------------------------------------------- */
471 : /* Work out the data block size. */
472 : /* -------------------------------------------------------------------- */
473 0 : psPart->pabyData = (GByte *) pszNext;
474 :
475 : int nBytesAvail = psResult->nDataLen -
476 0 : (pszNext - (const char *) psResult->pabyData);
477 :
478 0 : while( nBytesAvail > 0
479 : && (*pszNext != '-'
480 : || strncmp(pszNext,osBoundary,strlen(osBoundary)) != 0) )
481 : {
482 0 : pszNext++;
483 0 : nBytesAvail--;
484 : }
485 :
486 0 : if( nBytesAvail == 0 )
487 : {
488 0 : CPLAssert( FALSE );
489 0 : break;
490 : }
491 :
492 0 : psPart->nDataLen = pszNext - (const char *) psPart->pabyData;
493 0 : pszNext += strlen(osBoundary);
494 :
495 0 : if( strncmp(pszNext,"--",2) == 0 )
496 : {
497 0 : break;
498 : }
499 0 : else if( *pszNext == '\n' )
500 0 : pszNext++;
501 : else
502 : {
503 0 : CPLAssert( FALSE );
504 0 : break;
505 : }
506 : }
507 :
508 0 : return TRUE;
509 896 : }
|