1 : /******************************************************************************
2 : * $Id: cpl_http.cpp 17927 2009-10-30 18:37:12Z rouault $
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 "cpl_conv.h"
31 : #include "cpl_http.h"
32 :
33 : #ifdef HAVE_CURL
34 : # include <curl/curl.h>
35 : #endif
36 :
37 : CPL_CVSID("$Id: cpl_http.cpp 17927 2009-10-30 18:37:12Z rouault $");
38 :
39 : /************************************************************************/
40 : /* CPLWriteFct() */
41 : /* */
42 : /* Append incoming text to our collection buffer, reallocating */
43 : /* it larger as needed. */
44 : /************************************************************************/
45 :
46 : #ifdef HAVE_CURL
47 : static size_t
48 13 : CPLWriteFct(void *buffer, size_t size, size_t nmemb, void *reqInfo)
49 :
50 : {
51 13 : CPLHTTPResult *psResult = (CPLHTTPResult *) reqInfo;
52 : int nNewSize;
53 :
54 13 : nNewSize = psResult->nDataLen + nmemb*size + 1;
55 13 : if( nNewSize > psResult->nDataAlloc )
56 : {
57 9 : psResult->nDataAlloc = (int) (nNewSize * 1.25 + 100);
58 : GByte* pabyNewData = (GByte *) VSIRealloc(psResult->pabyData,
59 9 : psResult->nDataAlloc);
60 9 : if( pabyNewData == NULL )
61 : {
62 0 : VSIFree(psResult->pabyData);
63 0 : psResult->pabyData = NULL;
64 0 : psResult->pszErrBuf = CPLStrdup(CPLString().Printf("Out of memory allocating %d bytes for HTTP data buffer.", psResult->nDataAlloc));
65 0 : psResult->nDataAlloc = psResult->nDataLen = 0;
66 :
67 0 : return 0;
68 : }
69 9 : psResult->pabyData = pabyNewData;
70 : }
71 :
72 : memcpy( psResult->pabyData + psResult->nDataLen, buffer,
73 13 : nmemb * size );
74 :
75 13 : psResult->nDataLen += nmemb * size;
76 13 : psResult->pabyData[psResult->nDataLen] = 0;
77 :
78 13 : return nmemb;
79 : }
80 : #endif /* def HAVE_CURL */
81 :
82 : /************************************************************************/
83 : /* CPLHTTPFetch() */
84 : /************************************************************************/
85 :
86 : /**
87 : * \brief Fetch a document from an url and return in a string.
88 : *
89 : * @param pszURL valid URL recognized by underlying download library (libcurl)
90 : * @param papszOptions option list as a NULL-terminated array of strings. May be NULL.
91 : * The following options are handled :
92 : * <ul>
93 : * <li>TIMEOUT=val, where val is in seconds</li>
94 : * <li>HEADERS=val, where val is an extra header to use when getting a web page.
95 : * For example "Accept: application/x-ogcwkt"
96 : * <li>HTTPAUTH=[BASIC/NTLM/ANY] to specify an authentication scheme to use.
97 : * <li>USERPWD=userid:password to specify a user and password for authentication
98 : * </ul>
99 : *
100 : * @return a CPLHTTPResult* structure that must be freed by CPLHTTPDestroyResult(),
101 : * or NULL if libcurl support is diabled
102 : */
103 3 : CPLHTTPResult *CPLHTTPFetch( const char *pszURL, char **papszOptions )
104 :
105 : {
106 : #ifndef HAVE_CURL
107 : CPLError( CE_Failure, CPLE_NotSupported,
108 : "GDAL/OGR not compiled with libcurl support, remote requests not supported." );
109 : return NULL;
110 : #else
111 : CURL *http_handle;
112 : char szCurlErrBuf[CURL_ERROR_SIZE+1];
113 : CPLHTTPResult *psResult;
114 3 : struct curl_slist *headers=NULL;
115 :
116 :
117 3 : CPLDebug( "HTTP", "Fetch(%s)", pszURL );
118 :
119 3 : psResult = (CPLHTTPResult *) CPLCalloc(1,sizeof(CPLHTTPResult));
120 :
121 3 : http_handle = curl_easy_init();
122 :
123 3 : curl_easy_setopt(http_handle, CURLOPT_URL, pszURL );
124 :
125 : /* Support control over HTTPAUTH */
126 3 : const char *pszHttpAuth = CSLFetchNameValue( papszOptions, "HTTPAUTH" );
127 3 : if( pszHttpAuth == NULL )
128 : /* do nothing */;
129 :
130 : /* CURLOPT_HTTPAUTH is defined in curl 7.11.0 or newer */
131 : #if LIBCURL_VERSION_NUM >= 0x70B00
132 0 : else if( EQUAL(pszHttpAuth,"BASIC") )
133 0 : curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC );
134 0 : else if( EQUAL(pszHttpAuth,"NTLM") )
135 0 : curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH, CURLAUTH_NTLM );
136 0 : else if( EQUAL(pszHttpAuth,"ANY") )
137 0 : curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY );
138 : else
139 : {
140 : CPLError( CE_Warning, CPLE_AppDefined,
141 : "Unsupported HTTPAUTH value '%s', ignored.",
142 0 : pszHttpAuth );
143 : }
144 : #else
145 : else
146 : {
147 : CPLError( CE_Warning, CPLE_AppDefined,
148 : "HTTPAUTH option needs curl >= 7.11.0" );
149 : }
150 : #endif
151 :
152 : /* Support setting userid:password */
153 3 : const char *pszUserPwd = CSLFetchNameValue( papszOptions, "USERPWD" );
154 3 : if( pszUserPwd != NULL )
155 0 : curl_easy_setopt(http_handle, CURLOPT_USERPWD, pszUserPwd );
156 :
157 : /* Enable following redirections. Requires libcurl 7.10.1 at least */
158 3 : curl_easy_setopt(http_handle, CURLOPT_FOLLOWLOCATION, 1 );
159 3 : curl_easy_setopt(http_handle, CURLOPT_MAXREDIRS, 10 );
160 :
161 : /* Set timeout.*/
162 3 : const char *pszTimeout = CSLFetchNameValue( papszOptions, "TIMEOUT" );
163 3 : if( pszTimeout != NULL )
164 : curl_easy_setopt(http_handle, CURLOPT_TIMEOUT,
165 2 : atoi(pszTimeout) );
166 :
167 : /* Set Headers.*/
168 3 : const char *pszHeaders = CSLFetchNameValue( papszOptions, "HEADERS" );
169 3 : if( pszHeaders != NULL ) {
170 2 : CPLDebug ("HTTP", "These HTTP headers were set: %s", pszHeaders);
171 2 : headers = curl_slist_append(headers, pszHeaders);
172 2 : curl_easy_setopt(http_handle, CURLOPT_HTTPHEADER, headers);
173 : }
174 :
175 : /* NOSIGNAL should be set to true for timeout to work in multithread
176 : * environments on Unix, requires libcurl 7.10 or more recent.
177 : * (this force avoiding the use of sgnal handlers)
178 : */
179 : #ifdef CURLOPT_NOSIGNAL
180 : curl_easy_setopt(http_handle, CURLOPT_NOSIGNAL, 1 );
181 : #endif
182 :
183 3 : curl_easy_setopt(http_handle, CURLOPT_WRITEDATA, psResult );
184 3 : curl_easy_setopt(http_handle, CURLOPT_WRITEFUNCTION, CPLWriteFct );
185 :
186 3 : szCurlErrBuf[0] = '\0';
187 :
188 3 : curl_easy_setopt(http_handle, CURLOPT_ERRORBUFFER, szCurlErrBuf );
189 :
190 3 : psResult->nStatus = (int) curl_easy_perform( http_handle );
191 :
192 : /* -------------------------------------------------------------------- */
193 : /* Fetch content-type if possible. */
194 : /* -------------------------------------------------------------------- */
195 : CURLcode err;
196 :
197 3 : psResult->pszContentType = NULL;
198 : err = curl_easy_getinfo( http_handle, CURLINFO_CONTENT_TYPE,
199 3 : &(psResult->pszContentType) );
200 3 : if( psResult->pszContentType != NULL )
201 3 : psResult->pszContentType = CPLStrdup(psResult->pszContentType);
202 :
203 : /* -------------------------------------------------------------------- */
204 : /* Have we encountered some sort of error? */
205 : /* -------------------------------------------------------------------- */
206 3 : if( strlen(szCurlErrBuf) > 0 )
207 : {
208 0 : psResult->pszErrBuf = CPLStrdup(szCurlErrBuf);
209 : CPLError( CE_Failure, CPLE_AppDefined,
210 0 : "%s", szCurlErrBuf );
211 : }
212 :
213 3 : curl_easy_cleanup( http_handle );
214 3 : curl_slist_free_all(headers);
215 :
216 3 : return psResult;
217 : #endif /* def HAVE_CURL */
218 : }
219 :
220 : /************************************************************************/
221 : /* CPLHTTPEnabled() */
222 : /************************************************************************/
223 :
224 : /**
225 : * \brief Return if CPLHTTP services can be usefull
226 : *
227 : * Those services depend on GDAL being build with libcurl support.
228 : *
229 : * @return TRUE if libcurl support is enabled
230 : */
231 0 : int CPLHTTPEnabled()
232 :
233 : {
234 : #ifdef HAVE_CURL
235 0 : return TRUE;
236 : #else
237 : return FALSE;
238 : #endif
239 : }
240 :
241 : /************************************************************************/
242 : /* CPLHTTPCleanup() */
243 : /************************************************************************/
244 :
245 : /**
246 : * \brief Cleanup function to call at application termination
247 : */
248 0 : void CPLHTTPCleanup()
249 :
250 : {
251 : /* nothing for now, but if we use the more complicated api later,
252 : we will need to do cleanup, like mapserver maphttp.c does. */
253 0 : }
254 :
255 : /************************************************************************/
256 : /* CPLHTTPDestroyResult() */
257 : /************************************************************************/
258 :
259 : /**
260 : * \brief Clean the memory associated with the return value of CPLHTTPFetch()
261 : *
262 : * @param psResult pointer to the return value of CPLHTTPFetch()
263 : */
264 3 : void CPLHTTPDestroyResult( CPLHTTPResult *psResult )
265 :
266 : {
267 3 : if( psResult )
268 : {
269 3 : CPLFree( psResult->pabyData );
270 3 : CPLFree( psResult->pszErrBuf );
271 3 : CPLFree( psResult->pszContentType );
272 3 : CPLFree( psResult );
273 : }
274 3 : }
275 :
276 : /************************************************************************/
277 : /* CPLHTTPParseMultipartMime() */
278 : /************************************************************************/
279 :
280 : /**
281 : * \brief Parses a a MIME multipart message
282 : *
283 : * This function will iterate over each part and put it in a separate
284 : * element of the pasMimePart array of the provided psResult structure.
285 : *
286 : * @param psResult pointer to the return value of CPLHTTPFetch()
287 : * @return TRUE if the message contains MIME multipart message.
288 : */
289 0 : int CPLHTTPParseMultipartMime( CPLHTTPResult *psResult )
290 :
291 : {
292 : /* -------------------------------------------------------------------- */
293 : /* Is it already done? */
294 : /* -------------------------------------------------------------------- */
295 0 : if( psResult->nMimePartCount > 0 )
296 0 : return TRUE;
297 :
298 : /* -------------------------------------------------------------------- */
299 : /* Find the boundary setting in the content type. */
300 : /* -------------------------------------------------------------------- */
301 0 : const char *pszBound = NULL;
302 :
303 0 : if( psResult->pszContentType != NULL )
304 0 : pszBound = strstr(psResult->pszContentType,"boundary=");
305 :
306 0 : if( pszBound == NULL )
307 : {
308 : CPLError( CE_Failure, CPLE_AppDefined,
309 0 : "Unable to parse multi-part mime, no boundary setting." );
310 0 : return FALSE;
311 : }
312 :
313 0 : CPLString osBoundary;
314 : char **papszTokens =
315 : CSLTokenizeStringComplex( pszBound + 9, "\n ;",
316 0 : TRUE, FALSE );
317 :
318 0 : if( CSLCount(papszTokens) == 0 || strlen(papszTokens[0]) == 0 )
319 : {
320 : CPLError( CE_Failure, CPLE_AppDefined,
321 0 : "Unable to parse multi-part mime, boundary not parsable." );
322 0 : return FALSE;
323 : }
324 :
325 0 : osBoundary = "--";
326 0 : osBoundary += papszTokens[0];
327 0 : CSLDestroy( papszTokens );
328 :
329 : /* -------------------------------------------------------------------- */
330 : /* Find the start of the first chunk. */
331 : /* -------------------------------------------------------------------- */
332 : char *pszNext;
333 : pszNext = (char *)
334 0 : strstr((const char *) psResult->pabyData,osBoundary.c_str());
335 :
336 0 : if( pszNext == NULL )
337 : {
338 0 : CPLError( CE_Failure, CPLE_AppDefined, "No parts found." );
339 0 : return FALSE;
340 : }
341 :
342 0 : pszNext += strlen(osBoundary);
343 0 : while( *pszNext != '\n' && *pszNext != '\0' )
344 0 : pszNext++;
345 0 : if( *pszNext == '\n' )
346 0 : pszNext++;
347 :
348 : /* -------------------------------------------------------------------- */
349 : /* Loop over parts... */
350 : /* -------------------------------------------------------------------- */
351 0 : while( TRUE )
352 : {
353 0 : psResult->nMimePartCount++;
354 : psResult->pasMimePart = (CPLMimePart *)
355 : CPLRealloc(psResult->pasMimePart,
356 0 : sizeof(CPLMimePart) * psResult->nMimePartCount );
357 :
358 0 : CPLMimePart *psPart = psResult->pasMimePart+psResult->nMimePartCount-1;
359 :
360 0 : memset( psPart, 0, sizeof(CPLMimePart) );
361 :
362 : /* -------------------------------------------------------------------- */
363 : /* Collect headers. */
364 : /* -------------------------------------------------------------------- */
365 0 : while( *pszNext != '\n' && *pszNext != '\0' )
366 : {
367 0 : char *pszEOL = strstr(pszNext,"\n");
368 :
369 0 : if( pszEOL == NULL )
370 : {
371 : CPLAssert( FALSE );
372 0 : break;
373 : }
374 :
375 0 : *pszEOL = '\0';
376 : psPart->papszHeaders =
377 0 : CSLAddString( psPart->papszHeaders, pszNext );
378 0 : *pszEOL = '\n';
379 :
380 0 : pszNext = pszEOL + 1;
381 : }
382 :
383 0 : if( *pszNext == '\n' )
384 0 : pszNext++;
385 :
386 : /* -------------------------------------------------------------------- */
387 : /* Work out the data block size. */
388 : /* -------------------------------------------------------------------- */
389 0 : psPart->pabyData = (GByte *) pszNext;
390 :
391 : int nBytesAvail = psResult->nDataLen -
392 0 : (pszNext - (const char *) psResult->pabyData);
393 :
394 0 : while( nBytesAvail > 0
395 : && (*pszNext != '-'
396 : || strncmp(pszNext,osBoundary,strlen(osBoundary)) != 0) )
397 : {
398 0 : pszNext++;
399 0 : nBytesAvail--;
400 : }
401 :
402 0 : if( nBytesAvail == 0 )
403 : {
404 : CPLAssert( FALSE );
405 0 : break;
406 : }
407 :
408 0 : psPart->nDataLen = pszNext - (const char *) psPart->pabyData;
409 0 : pszNext += strlen(osBoundary);
410 :
411 0 : if( strncmp(pszNext,"--",2) == 0 )
412 : {
413 0 : break;
414 : }
415 0 : else if( *pszNext == '\n' )
416 0 : pszNext++;
417 : else
418 : {
419 : CPLAssert( FALSE );
420 0 : break;
421 : }
422 : }
423 :
424 0 : return TRUE;
425 : }
|