Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: WCS Client Driver
4 : * Purpose: Implementation of an HTTP fetching driver.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2007, 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_string.h"
31 : #include "cpl_http.h"
32 : #include "cpl_atomic_ops.h"
33 : #include "gdal_frmts.h"
34 : #include "gdal_pam.h"
35 :
36 : /************************************************************************/
37 : /* HTTPFetchContentDispositionFilename() */
38 : /************************************************************************/
39 :
40 5 : static const char *HTTPFetchContentDispositionFilename(char **papszHeaders)
41 : {
42 5 : char **papszIter = papszHeaders;
43 92 : while (papszIter && *papszIter)
44 : {
45 : /* For multipart, we have in raw format, but without end-of-line
46 : * characters */
47 87 : if (STARTS_WITH(*papszIter,
48 : "Content-Disposition: attachment; filename="))
49 : {
50 0 : return *papszIter + 42;
51 : }
52 : /* For single part, the headers are in KEY=VAL format, but with e-o-l
53 : * ... */
54 87 : else if (STARTS_WITH(*papszIter,
55 : "Content-Disposition=attachment; filename="))
56 : {
57 0 : char *pszVal = (char *)(*papszIter + 41);
58 0 : char *pszEOL = strchr(pszVal, '\r');
59 0 : if (pszEOL)
60 0 : *pszEOL = 0;
61 0 : pszEOL = strchr(pszVal, '\n');
62 0 : if (pszEOL)
63 0 : *pszEOL = 0;
64 0 : return pszVal;
65 : }
66 87 : papszIter++;
67 : }
68 5 : return nullptr;
69 : }
70 :
71 : /************************************************************************/
72 : /* HTTPOpen() */
73 : /************************************************************************/
74 :
75 27147 : static GDALDataset *HTTPOpen(GDALOpenInfo *poOpenInfo)
76 :
77 : {
78 : static volatile int nCounter = 0;
79 :
80 27147 : if (poOpenInfo->nHeaderBytes != 0)
81 1284 : return nullptr;
82 :
83 25863 : if (!STARTS_WITH_CI(poOpenInfo->pszFilename, "http:") &&
84 25860 : !STARTS_WITH_CI(poOpenInfo->pszFilename, "https:") &&
85 25854 : !STARTS_WITH_CI(poOpenInfo->pszFilename, "ftp:"))
86 25852 : return nullptr;
87 :
88 : /* -------------------------------------------------------------------- */
89 : /* Fetch the result. */
90 : /* -------------------------------------------------------------------- */
91 11 : CPLErrorReset();
92 8 : CPLHTTPResult *psResult = CPLHTTPFetch(poOpenInfo->pszFilename, nullptr);
93 :
94 : /* -------------------------------------------------------------------- */
95 : /* Try to handle errors. */
96 : /* -------------------------------------------------------------------- */
97 15 : if (psResult == nullptr || psResult->nDataLen == 0 ||
98 7 : CPLGetLastErrorNo() != 0)
99 : {
100 3 : CPLHTTPDestroyResult(psResult);
101 3 : return nullptr;
102 : }
103 :
104 : /* -------------------------------------------------------------------- */
105 : /* Create a memory file from the result. */
106 : /* -------------------------------------------------------------------- */
107 10 : CPLString osResultFilename;
108 :
109 5 : int nNewCounter = CPLAtomicInc(&nCounter);
110 :
111 : const char *pszFilename =
112 5 : HTTPFetchContentDispositionFilename(psResult->papszHeaders);
113 5 : if (pszFilename == nullptr)
114 : {
115 5 : pszFilename = CPLGetFilename(poOpenInfo->pszFilename);
116 : /* If we have special characters, let's default to a fixed name */
117 5 : if (strchr(pszFilename, '?') || strchr(pszFilename, '&'))
118 0 : pszFilename = "file.dat";
119 : }
120 :
121 5 : osResultFilename.Printf("/vsimem/http_%d/%s", nNewCounter, pszFilename);
122 :
123 5 : VSILFILE *fp = VSIFileFromMemBuffer(osResultFilename, psResult->pabyData,
124 5 : psResult->nDataLen, TRUE);
125 :
126 5 : if (fp == nullptr)
127 0 : return nullptr;
128 :
129 5 : VSIFCloseL(fp);
130 :
131 : /* -------------------------------------------------------------------- */
132 : /* Steal the memory buffer from HTTP result before destroying */
133 : /* it. */
134 : /* -------------------------------------------------------------------- */
135 5 : psResult->pabyData = nullptr;
136 5 : psResult->nDataLen = 0;
137 5 : psResult->nDataAlloc = 0;
138 :
139 5 : CPLHTTPDestroyResult(psResult);
140 :
141 : /* -------------------------------------------------------------------- */
142 : /* Try opening this result as a gdaldataset. */
143 : /* -------------------------------------------------------------------- */
144 : /* suppress errors as not all drivers support /vsimem */
145 5 : CPLPushErrorHandler(CPLQuietErrorHandler);
146 5 : GDALDataset *poDS = (GDALDataset *)GDALOpenEx(
147 5 : osResultFilename, poOpenInfo->nOpenFlags & ~GDAL_OF_SHARED,
148 5 : poOpenInfo->papszAllowedDrivers, poOpenInfo->papszOpenOptions, nullptr);
149 5 : CPLPopErrorHandler();
150 :
151 : // The JP2OpenJPEG driver may need to reopen the file, hence this special
152 : // behavior
153 8 : if (poDS != nullptr && poDS->GetDriver() != nullptr &&
154 3 : EQUAL(poDS->GetDriver()->GetDescription(), "JP2OpenJPEG"))
155 : {
156 1 : poDS->MarkSuppressOnClose();
157 1 : return poDS;
158 : }
159 :
160 : /* -------------------------------------------------------------------- */
161 : /* If opening it in memory didn't work, perhaps we need to */
162 : /* write to a temp file on disk? */
163 : /* -------------------------------------------------------------------- */
164 4 : if (poDS == nullptr)
165 : {
166 4 : CPLString osTempFilename;
167 :
168 : #ifdef _WIN32
169 : const char *pszPath = CPLGetPath(CPLGenerateTempFilename(NULL));
170 : #else
171 2 : const char *pszPath = "/tmp";
172 : #endif
173 : osTempFilename =
174 2 : CPLFormFilename(pszPath, CPLGetFilename(osResultFilename), nullptr);
175 2 : if (CPLCopyFile(osTempFilename, osResultFilename) != 0)
176 : {
177 0 : CPLError(CE_Failure, CPLE_OpenFailed,
178 : "Failed to create temporary file:%s",
179 : osTempFilename.c_str());
180 : }
181 : else
182 : {
183 2 : poDS = (GDALDataset *)GDALOpenEx(
184 2 : osTempFilename, poOpenInfo->nOpenFlags & ~GDAL_OF_SHARED,
185 2 : poOpenInfo->papszAllowedDrivers, poOpenInfo->papszOpenOptions,
186 : nullptr);
187 2 : if (VSIUnlink(osTempFilename) != 0 && poDS != nullptr)
188 0 : poDS->MarkSuppressOnClose(); /* VSIUnlink() may not work on
189 : windows */
190 2 : if (poDS && strcmp(poDS->GetDescription(), osTempFilename) == 0)
191 0 : poDS->SetDescription(poOpenInfo->pszFilename);
192 : }
193 : }
194 2 : else if (strcmp(poDS->GetDescription(), osResultFilename) == 0)
195 2 : poDS->SetDescription(poOpenInfo->pszFilename);
196 :
197 : /* -------------------------------------------------------------------- */
198 : /* Release our hold on the vsi memory file, though if it is */
199 : /* held open by a dataset it will continue to exist till that */
200 : /* lets it go. */
201 : /* -------------------------------------------------------------------- */
202 4 : VSIUnlink(osResultFilename);
203 :
204 4 : return poDS;
205 : }
206 :
207 : /************************************************************************/
208 : /* GDALRegister_HTTP() */
209 : /************************************************************************/
210 :
211 1523 : void GDALRegister_HTTP()
212 :
213 : {
214 1523 : if (GDALGetDriverByName("HTTP") != nullptr)
215 301 : return;
216 :
217 1222 : GDALDriver *poDriver = new GDALDriver();
218 :
219 1222 : poDriver->SetDescription("HTTP");
220 1222 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
221 1222 : poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
222 1222 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "HTTP Fetching Wrapper");
223 :
224 1222 : poDriver->pfnOpen = HTTPOpen;
225 :
226 1222 : GetGDALDriverManager()->RegisterDriver(poDriver);
227 : }
|