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 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_error_internal.h"
15 : #include "cpl_string.h"
16 : #include "cpl_http.h"
17 : #include "gdal_frmts.h"
18 : #include "gdal_pam.h"
19 : #include "gdal_driver.h"
20 : #include "gdal_drivermanager.h"
21 : #include "gdal_openinfo.h"
22 : #include "gdal_cpp_functions.h"
23 :
24 2 : static std::string SanitizeDispositionFilename(const std::string &osVal)
25 : {
26 4 : std::string osRet(osVal);
27 2 : if (!osRet.empty() && osRet[0] == '"')
28 : {
29 2 : const auto nEnd = osRet.find('"', 1);
30 2 : if (nEnd != std::string::npos)
31 2 : return osRet.substr(1, nEnd - 1);
32 : }
33 0 : return osRet;
34 : }
35 :
36 : /************************************************************************/
37 : /* HTTPFetchContentDispositionFilename() */
38 : /************************************************************************/
39 :
40 18 : static std::string HTTPFetchContentDispositionFilename(char **papszHeaders)
41 : {
42 18 : char **papszIter = papszHeaders;
43 153 : while (papszIter && *papszIter)
44 : {
45 : /* For multipart, we have in raw format, but without end-of-line
46 : * characters */
47 137 : if (STARTS_WITH(*papszIter,
48 : "Content-Disposition: attachment; filename="))
49 : {
50 0 : return SanitizeDispositionFilename(*papszIter + 42);
51 : }
52 : /* For single part, the headers are in KEY=VAL format, but with e-o-l
53 : * ... */
54 137 : else if (STARTS_WITH(*papszIter,
55 : "Content-Disposition=attachment; filename="))
56 : {
57 2 : char *pszVal = (*papszIter + 41);
58 2 : char *pszEOL = strchr(pszVal, '\r');
59 2 : if (pszEOL)
60 0 : *pszEOL = 0;
61 2 : pszEOL = strchr(pszVal, '\n');
62 2 : if (pszEOL)
63 0 : *pszEOL = 0;
64 2 : return SanitizeDispositionFilename(pszVal);
65 : }
66 135 : papszIter++;
67 : }
68 16 : return std::string();
69 : }
70 :
71 : /************************************************************************/
72 : /* HTTPOpen() */
73 : /************************************************************************/
74 :
75 32961 : static GDALDataset *HTTPOpen(GDALOpenInfo *poOpenInfo)
76 :
77 : {
78 32961 : if (poOpenInfo->nHeaderBytes != 0)
79 1735 : return nullptr;
80 :
81 31226 : const char *pszFilename = poOpenInfo->pszFilename;
82 31226 : if (STARTS_WITH_CI(pszFilename, "HTTP:http://") ||
83 31226 : STARTS_WITH_CI(pszFilename, "HTTP:https://") ||
84 31226 : STARTS_WITH_CI(pszFilename, "HTTP:ftp://"))
85 : {
86 0 : pszFilename += strlen("HTTP:");
87 : }
88 31226 : else if (!STARTS_WITH(pszFilename, "http://") &&
89 31209 : !STARTS_WITH(pszFilename, "https://") &&
90 31203 : !STARTS_WITH(pszFilename, "ftp://"))
91 : {
92 31203 : return nullptr;
93 : }
94 :
95 : /* -------------------------------------------------------------------- */
96 : /* Fetch the result. */
97 : /* -------------------------------------------------------------------- */
98 23 : CPLErrorReset();
99 23 : CPLHTTPResult *psResult = CPLHTTPFetch(pszFilename, nullptr);
100 :
101 : /* -------------------------------------------------------------------- */
102 : /* Try to handle errors. */
103 : /* -------------------------------------------------------------------- */
104 45 : if (psResult == nullptr || psResult->nDataLen == 0 ||
105 22 : CPLGetLastErrorNo() != 0)
106 : {
107 5 : CPLHTTPDestroyResult(psResult);
108 5 : return nullptr;
109 : }
110 :
111 : /* -------------------------------------------------------------------- */
112 : /* Create a memory file from the result. */
113 : /* -------------------------------------------------------------------- */
114 : std::string osFilename =
115 36 : HTTPFetchContentDispositionFilename(psResult->papszHeaders);
116 18 : if (osFilename.empty())
117 : {
118 16 : osFilename = CPLGetFilename(pszFilename);
119 : /* If we have special characters, let's default to a fixed name */
120 16 : if (strchr(osFilename.c_str(), '?') || strchr(osFilename.c_str(), '&'))
121 0 : osFilename = "file.dat";
122 : }
123 :
124 : // If changing the _gdal_http_ marker, change jpgdataset.cpp that tests for it
125 : const CPLString osResultFilename = VSIMemGenerateHiddenFilename(
126 54 : std::string("_gdal_http_").append(osFilename).c_str());
127 :
128 18 : VSILFILE *fp = VSIFileFromMemBuffer(osResultFilename, psResult->pabyData,
129 18 : psResult->nDataLen, TRUE);
130 :
131 18 : if (fp == nullptr)
132 0 : return nullptr;
133 :
134 18 : VSIFCloseL(fp);
135 :
136 : /* -------------------------------------------------------------------- */
137 : /* Steal the memory buffer from HTTP result before destroying */
138 : /* it. */
139 : /* -------------------------------------------------------------------- */
140 18 : psResult->pabyData = nullptr;
141 18 : psResult->nDataLen = 0;
142 18 : psResult->nDataAlloc = 0;
143 :
144 18 : CPLHTTPDestroyResult(psResult);
145 :
146 36 : CPLStringList aosOpenOptions;
147 0 : for (const char *pszStr :
148 18 : cpl::Iterate(const_cast<CSLConstList>(poOpenInfo->papszOpenOptions)))
149 : {
150 0 : if (STARTS_WITH_CI(pszStr, "NATIVE_DATA="))
151 : {
152 : // Avoid warning with "ogr2ogr out http://example.com/in.gpkg"
153 0 : aosOpenOptions.push_back(std::string("@").append(pszStr).c_str());
154 : }
155 : else
156 : {
157 0 : aosOpenOptions.push_back(pszStr);
158 : }
159 : }
160 :
161 : /* -------------------------------------------------------------------- */
162 : /* Try opening this result as a gdaldataset. */
163 : /* -------------------------------------------------------------------- */
164 :
165 : /* suppress errors as not all drivers support /vsimem */
166 :
167 : GDALDataset *poDS;
168 36 : CPLErrorAccumulator oErrorAccumulator;
169 : {
170 36 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
171 18 : auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
172 18 : CPL_IGNORE_RET_VAL(oAccumulator);
173 18 : poDS = GDALDataset::Open(
174 : osResultFilename,
175 18 : poOpenInfo->nOpenFlags & ~(GDAL_OF_SHARED | GDAL_OF_VERBOSE_ERROR),
176 18 : poOpenInfo->papszAllowedDrivers, aosOpenOptions.List(), nullptr);
177 : }
178 :
179 : // Re-emit silenced errors if open was successful
180 18 : if (poDS)
181 : {
182 7 : oErrorAccumulator.ReplayErrors();
183 : }
184 :
185 : // The JP2OpenJPEG, XLSX or ODS drivers may need to reopen the file, hence this special
186 : // behavior
187 18 : if (poDS)
188 : {
189 7 : auto poDriver = poDS->GetDriver();
190 13 : if (poDriver && (EQUAL(poDriver->GetDescription(), "JP2OpenJPEG") ||
191 6 : EQUAL(poDriver->GetDescription(), "XLSX") ||
192 6 : EQUAL(poDriver->GetDescription(), "ODS")))
193 : {
194 1 : poDS->MarkSuppressOnClose();
195 1 : return poDS;
196 : }
197 : }
198 :
199 : /* -------------------------------------------------------------------- */
200 : /* If opening it in memory didn't work, perhaps we need to */
201 : /* write to a temp file on disk? */
202 : /* -------------------------------------------------------------------- */
203 17 : if (poDS == nullptr)
204 : {
205 22 : CPLString osTempFilename;
206 :
207 : #ifdef _WIN32
208 : const std::string osPath =
209 : CPLGetPathSafe(CPLGenerateTempFilenameSafe(NULL).c_str());
210 : #else
211 22 : const std::string osPath = "/tmp";
212 : #endif
213 11 : osTempFilename = CPLFormFilenameSafe(
214 11 : osPath.c_str(), CPLGetFilename(osResultFilename), nullptr);
215 11 : if (CPLCopyFile(osTempFilename, osResultFilename) != 0)
216 : {
217 0 : CPLError(CE_Failure, CPLE_OpenFailed,
218 : "Failed to create temporary file:%s",
219 : osTempFilename.c_str());
220 : }
221 : else
222 : {
223 : poDS =
224 11 : GDALDataset::Open(osTempFilename,
225 11 : poOpenInfo->nOpenFlags &
226 : ~(GDAL_OF_SHARED | GDAL_OF_VERBOSE_ERROR),
227 : poOpenInfo->papszAllowedDrivers,
228 11 : aosOpenOptions.List(), nullptr);
229 11 : if (VSIUnlink(osTempFilename) != 0 && poDS != nullptr)
230 0 : poDS->MarkSuppressOnClose(); /* VSIUnlink() may not work on
231 : windows */
232 11 : if (poDS && strcmp(poDS->GetDescription(), osTempFilename) == 0)
233 0 : poDS->SetDescription(poOpenInfo->pszFilename);
234 : }
235 : }
236 6 : else if (strcmp(poDS->GetDescription(), osResultFilename) == 0)
237 6 : poDS->SetDescription(poOpenInfo->pszFilename);
238 :
239 : /* -------------------------------------------------------------------- */
240 : /* Release our hold on the vsi memory file, though if it is */
241 : /* held open by a dataset it will continue to exist till that */
242 : /* lets it go. */
243 : /* -------------------------------------------------------------------- */
244 17 : VSIUnlink(osResultFilename);
245 :
246 17 : return poDS;
247 : }
248 :
249 : /************************************************************************/
250 : /* GDALRegister_HTTP() */
251 : /************************************************************************/
252 :
253 2066 : void GDALRegister_HTTP()
254 :
255 : {
256 2066 : if (GDALGetDriverByName("HTTP") != nullptr)
257 263 : return;
258 :
259 1803 : GDALDriver *poDriver = new GDALDriver();
260 :
261 1803 : poDriver->SetDescription("HTTP");
262 1803 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
263 1803 : poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
264 1803 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "HTTP Fetching Wrapper");
265 :
266 1803 : poDriver->pfnOpen = HTTPOpen;
267 :
268 1803 : GetGDALDriverManager()->RegisterDriver(poDriver);
269 : }
|