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 15 : static std::string HTTPFetchContentDispositionFilename(char **papszHeaders)
41 : {
42 15 : char **papszIter = papszHeaders;
43 129 : while (papszIter && *papszIter)
44 : {
45 : /* For multipart, we have in raw format, but without end-of-line
46 : * characters */
47 116 : 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 116 : 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 114 : papszIter++;
67 : }
68 13 : return std::string();
69 : }
70 :
71 : /************************************************************************/
72 : /* HTTPOpen() */
73 : /************************************************************************/
74 :
75 32390 : static GDALDataset *HTTPOpen(GDALOpenInfo *poOpenInfo)
76 :
77 : {
78 32390 : if (poOpenInfo->nHeaderBytes != 0)
79 1681 : return nullptr;
80 :
81 30709 : if (!STARTS_WITH_CI(poOpenInfo->pszFilename, "http:") &&
82 30695 : !STARTS_WITH_CI(poOpenInfo->pszFilename, "https:") &&
83 30689 : !STARTS_WITH_CI(poOpenInfo->pszFilename, "ftp:"))
84 30689 : return nullptr;
85 :
86 : /* -------------------------------------------------------------------- */
87 : /* Fetch the result. */
88 : /* -------------------------------------------------------------------- */
89 20 : CPLErrorReset();
90 20 : CPLHTTPResult *psResult = CPLHTTPFetch(poOpenInfo->pszFilename, nullptr);
91 :
92 : /* -------------------------------------------------------------------- */
93 : /* Try to handle errors. */
94 : /* -------------------------------------------------------------------- */
95 39 : if (psResult == nullptr || psResult->nDataLen == 0 ||
96 19 : 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 30 : HTTPFetchContentDispositionFilename(psResult->papszHeaders);
107 15 : if (osFilename.empty())
108 : {
109 13 : osFilename = CPLGetFilename(poOpenInfo->pszFilename);
110 : /* If we have special characters, let's default to a fixed name */
111 13 : 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 45 : std::string("_gdal_http_").append(osFilename).c_str());
118 :
119 15 : VSILFILE *fp = VSIFileFromMemBuffer(osResultFilename, psResult->pabyData,
120 15 : psResult->nDataLen, TRUE);
121 :
122 15 : if (fp == nullptr)
123 0 : return nullptr;
124 :
125 15 : VSIFCloseL(fp);
126 :
127 : /* -------------------------------------------------------------------- */
128 : /* Steal the memory buffer from HTTP result before destroying */
129 : /* it. */
130 : /* -------------------------------------------------------------------- */
131 15 : psResult->pabyData = nullptr;
132 15 : psResult->nDataLen = 0;
133 15 : psResult->nDataAlloc = 0;
134 :
135 15 : CPLHTTPDestroyResult(psResult);
136 :
137 30 : CPLStringList aosOpenOptions;
138 0 : for (const char *pszStr :
139 15 : 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 30 : CPLErrorAccumulator oErrorAccumulator;
159 : {
160 30 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
161 15 : auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
162 15 : CPL_IGNORE_RET_VAL(oAccumulator);
163 15 : poDS = GDALDataset::Open(
164 15 : osResultFilename, poOpenInfo->nOpenFlags & ~GDAL_OF_SHARED,
165 15 : poOpenInfo->papszAllowedDrivers, aosOpenOptions.List(), nullptr);
166 : }
167 :
168 : // Re-emit silenced errors if open was successful
169 15 : if (poDS)
170 : {
171 5 : oErrorAccumulator.ReplayErrors();
172 : }
173 :
174 : // The JP2OpenJPEG driver may need to reopen the file, hence this special
175 : // behavior
176 20 : if (poDS != nullptr && poDS->GetDriver() != nullptr &&
177 5 : EQUAL(poDS->GetDriver()->GetDescription(), "JP2OpenJPEG"))
178 : {
179 1 : poDS->MarkSuppressOnClose();
180 1 : return poDS;
181 : }
182 :
183 : /* -------------------------------------------------------------------- */
184 : /* If opening it in memory didn't work, perhaps we need to */
185 : /* write to a temp file on disk? */
186 : /* -------------------------------------------------------------------- */
187 14 : if (poDS == nullptr)
188 : {
189 20 : CPLString osTempFilename;
190 :
191 : #ifdef _WIN32
192 : const std::string osPath =
193 : CPLGetPathSafe(CPLGenerateTempFilenameSafe(NULL).c_str());
194 : #else
195 20 : const std::string osPath = "/tmp";
196 : #endif
197 10 : osTempFilename = CPLFormFilenameSafe(
198 10 : osPath.c_str(), CPLGetFilename(osResultFilename), nullptr);
199 10 : if (CPLCopyFile(osTempFilename, osResultFilename) != 0)
200 : {
201 0 : CPLError(CE_Failure, CPLE_OpenFailed,
202 : "Failed to create temporary file:%s",
203 : osTempFilename.c_str());
204 : }
205 : else
206 : {
207 10 : poDS = GDALDataset::Open(osTempFilename,
208 10 : poOpenInfo->nOpenFlags & ~GDAL_OF_SHARED,
209 : poOpenInfo->papszAllowedDrivers,
210 10 : aosOpenOptions.List(), nullptr);
211 10 : if (VSIUnlink(osTempFilename) != 0 && poDS != nullptr)
212 0 : poDS->MarkSuppressOnClose(); /* VSIUnlink() may not work on
213 : windows */
214 10 : if (poDS && strcmp(poDS->GetDescription(), osTempFilename) == 0)
215 0 : poDS->SetDescription(poOpenInfo->pszFilename);
216 : }
217 : }
218 4 : else if (strcmp(poDS->GetDescription(), osResultFilename) == 0)
219 4 : poDS->SetDescription(poOpenInfo->pszFilename);
220 :
221 : /* -------------------------------------------------------------------- */
222 : /* Release our hold on the vsi memory file, though if it is */
223 : /* held open by a dataset it will continue to exist till that */
224 : /* lets it go. */
225 : /* -------------------------------------------------------------------- */
226 14 : VSIUnlink(osResultFilename);
227 :
228 14 : return poDS;
229 : }
230 :
231 : /************************************************************************/
232 : /* GDALRegister_HTTP() */
233 : /************************************************************************/
234 :
235 2033 : void GDALRegister_HTTP()
236 :
237 : {
238 2033 : if (GDALGetDriverByName("HTTP") != nullptr)
239 283 : return;
240 :
241 1750 : GDALDriver *poDriver = new GDALDriver();
242 :
243 1750 : poDriver->SetDescription("HTTP");
244 1750 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
245 1750 : poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
246 1750 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "HTTP Fetching Wrapper");
247 :
248 1750 : poDriver->pfnOpen = HTTPOpen;
249 :
250 1750 : GetGDALDriverManager()->RegisterDriver(poDriver);
251 : }
|