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 158 : while (papszIter && *papszIter)
44 : {
45 : /* For multipart, we have in raw format, but without end-of-line
46 : * characters */
47 142 : 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 142 : 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 140 : papszIter++;
67 : }
68 16 : return std::string();
69 : }
70 :
71 : /************************************************************************/
72 : /* HTTPOpen() */
73 : /************************************************************************/
74 :
75 33448 : static GDALDataset *HTTPOpen(GDALOpenInfo *poOpenInfo)
76 :
77 : {
78 33448 : if (poOpenInfo->nHeaderBytes != 0)
79 1728 : return nullptr;
80 :
81 31720 : if (!STARTS_WITH_CI(poOpenInfo->pszFilename, "http:") &&
82 31703 : !STARTS_WITH_CI(poOpenInfo->pszFilename, "https:") &&
83 31697 : !STARTS_WITH_CI(poOpenInfo->pszFilename, "ftp:"))
84 31697 : return nullptr;
85 :
86 : /* -------------------------------------------------------------------- */
87 : /* Fetch the result. */
88 : /* -------------------------------------------------------------------- */
89 23 : CPLErrorReset();
90 23 : CPLHTTPResult *psResult = CPLHTTPFetch(poOpenInfo->pszFilename, nullptr);
91 :
92 : /* -------------------------------------------------------------------- */
93 : /* Try to handle errors. */
94 : /* -------------------------------------------------------------------- */
95 45 : if (psResult == nullptr || psResult->nDataLen == 0 ||
96 22 : CPLGetLastErrorNo() != 0)
97 : {
98 5 : CPLHTTPDestroyResult(psResult);
99 5 : return nullptr;
100 : }
101 :
102 : /* -------------------------------------------------------------------- */
103 : /* Create a memory file from the result. */
104 : /* -------------------------------------------------------------------- */
105 : std::string osFilename =
106 36 : HTTPFetchContentDispositionFilename(psResult->papszHeaders);
107 18 : if (osFilename.empty())
108 : {
109 16 : osFilename = CPLGetFilename(poOpenInfo->pszFilename);
110 : /* If we have special characters, let's default to a fixed name */
111 16 : if (strchr(osFilename.c_str(), '?') || strchr(osFilename.c_str(), '&'))
112 0 : osFilename = "file.dat";
113 : }
114 :
115 : // If changing the _gdal_http_ marker, change jpgdataset.cpp that tests for it
116 : const CPLString osResultFilename = VSIMemGenerateHiddenFilename(
117 54 : std::string("_gdal_http_").append(osFilename).c_str());
118 :
119 18 : VSILFILE *fp = VSIFileFromMemBuffer(osResultFilename, psResult->pabyData,
120 18 : psResult->nDataLen, TRUE);
121 :
122 18 : if (fp == nullptr)
123 0 : return nullptr;
124 :
125 18 : VSIFCloseL(fp);
126 :
127 : /* -------------------------------------------------------------------- */
128 : /* Steal the memory buffer from HTTP result before destroying */
129 : /* it. */
130 : /* -------------------------------------------------------------------- */
131 18 : psResult->pabyData = nullptr;
132 18 : psResult->nDataLen = 0;
133 18 : psResult->nDataAlloc = 0;
134 :
135 18 : CPLHTTPDestroyResult(psResult);
136 :
137 36 : CPLStringList aosOpenOptions;
138 0 : for (const char *pszStr :
139 18 : cpl::Iterate(const_cast<CSLConstList>(poOpenInfo->papszOpenOptions)))
140 : {
141 0 : if (STARTS_WITH_CI(pszStr, "NATIVE_DATA="))
142 : {
143 : // Avoid warning with "ogr2ogr out http://example.com/in.gpkg"
144 0 : aosOpenOptions.push_back(std::string("@").append(pszStr).c_str());
145 : }
146 : else
147 : {
148 0 : aosOpenOptions.push_back(pszStr);
149 : }
150 : }
151 :
152 : /* -------------------------------------------------------------------- */
153 : /* Try opening this result as a gdaldataset. */
154 : /* -------------------------------------------------------------------- */
155 : /* suppress errors as not all drivers support /vsimem */
156 :
157 : GDALDataset *poDS;
158 36 : CPLErrorAccumulator oErrorAccumulator;
159 : {
160 36 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
161 18 : auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
162 18 : CPL_IGNORE_RET_VAL(oAccumulator);
163 18 : poDS = GDALDataset::Open(
164 : osResultFilename,
165 18 : poOpenInfo->nOpenFlags & ~(GDAL_OF_SHARED | GDAL_OF_VERBOSE_ERROR),
166 18 : poOpenInfo->papszAllowedDrivers, aosOpenOptions.List(), nullptr);
167 : }
168 :
169 : // Re-emit silenced errors if open was successful
170 18 : if (poDS)
171 : {
172 7 : oErrorAccumulator.ReplayErrors();
173 : }
174 :
175 : // The JP2OpenJPEG driver may need to reopen the file, hence this special
176 : // behavior
177 25 : if (poDS != nullptr && poDS->GetDriver() != nullptr &&
178 7 : EQUAL(poDS->GetDriver()->GetDescription(), "JP2OpenJPEG"))
179 : {
180 1 : poDS->MarkSuppressOnClose();
181 1 : return poDS;
182 : }
183 :
184 : /* -------------------------------------------------------------------- */
185 : /* If opening it in memory didn't work, perhaps we need to */
186 : /* write to a temp file on disk? */
187 : /* -------------------------------------------------------------------- */
188 17 : if (poDS == nullptr)
189 : {
190 22 : CPLString osTempFilename;
191 :
192 : #ifdef _WIN32
193 : const std::string osPath =
194 : CPLGetPathSafe(CPLGenerateTempFilenameSafe(NULL).c_str());
195 : #else
196 22 : const std::string osPath = "/tmp";
197 : #endif
198 11 : osTempFilename = CPLFormFilenameSafe(
199 11 : osPath.c_str(), CPLGetFilename(osResultFilename), nullptr);
200 11 : if (CPLCopyFile(osTempFilename, osResultFilename) != 0)
201 : {
202 0 : CPLError(CE_Failure, CPLE_OpenFailed,
203 : "Failed to create temporary file:%s",
204 : osTempFilename.c_str());
205 : }
206 : else
207 : {
208 : poDS =
209 11 : GDALDataset::Open(osTempFilename,
210 11 : poOpenInfo->nOpenFlags &
211 : ~(GDAL_OF_SHARED | GDAL_OF_VERBOSE_ERROR),
212 : poOpenInfo->papszAllowedDrivers,
213 11 : aosOpenOptions.List(), nullptr);
214 11 : if (VSIUnlink(osTempFilename) != 0 && poDS != nullptr)
215 0 : poDS->MarkSuppressOnClose(); /* VSIUnlink() may not work on
216 : windows */
217 11 : if (poDS && strcmp(poDS->GetDescription(), osTempFilename) == 0)
218 0 : poDS->SetDescription(poOpenInfo->pszFilename);
219 : }
220 : }
221 6 : else if (strcmp(poDS->GetDescription(), osResultFilename) == 0)
222 6 : poDS->SetDescription(poOpenInfo->pszFilename);
223 :
224 : /* -------------------------------------------------------------------- */
225 : /* Release our hold on the vsi memory file, though if it is */
226 : /* held open by a dataset it will continue to exist till that */
227 : /* lets it go. */
228 : /* -------------------------------------------------------------------- */
229 17 : VSIUnlink(osResultFilename);
230 :
231 17 : return poDS;
232 : }
233 :
234 : /************************************************************************/
235 : /* GDALRegister_HTTP() */
236 : /************************************************************************/
237 :
238 2063 : void GDALRegister_HTTP()
239 :
240 : {
241 2063 : if (GDALGetDriverByName("HTTP") != nullptr)
242 283 : return;
243 :
244 1780 : GDALDriver *poDriver = new GDALDriver();
245 :
246 1780 : poDriver->SetDescription("HTTP");
247 1780 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
248 1780 : poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
249 1780 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "HTTP Fetching Wrapper");
250 :
251 1780 : poDriver->pfnOpen = HTTPOpen;
252 :
253 1780 : GetGDALDriverManager()->RegisterDriver(poDriver);
254 : }
|