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