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