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