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 130 : while (papszIter && *papszIter)
40 : {
41 : /* For multipart, we have in raw format, but without end-of-line
42 : * characters */
43 117 : 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 117 : 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 115 : papszIter++;
63 : }
64 13 : return std::string();
65 : }
66 :
67 : /************************************************************************/
68 : /* HTTPOpen() */
69 : /************************************************************************/
70 :
71 28866 : static GDALDataset *HTTPOpen(GDALOpenInfo *poOpenInfo)
72 :
73 : {
74 28866 : if (poOpenInfo->nHeaderBytes != 0)
75 1404 : return nullptr;
76 :
77 27462 : if (!STARTS_WITH_CI(poOpenInfo->pszFilename, "http:") &&
78 27427 : !STARTS_WITH_CI(poOpenInfo->pszFilename, "https:") &&
79 27424 : !STARTS_WITH_CI(poOpenInfo->pszFilename, "ftp:"))
80 27432 : return nullptr;
81 :
82 : /* -------------------------------------------------------------------- */
83 : /* Fetch the result. */
84 : /* -------------------------------------------------------------------- */
85 30 : 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 : /* -------------------------------------------------------------------- */
134 : /* Try opening this result as a gdaldataset. */
135 : /* -------------------------------------------------------------------- */
136 : /* suppress errors as not all drivers support /vsimem */
137 :
138 : GDALDataset *poDS;
139 30 : std::vector<CPLErrorHandlerAccumulatorStruct> aoErrors;
140 : {
141 30 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
142 15 : CPLInstallErrorHandlerAccumulator(aoErrors);
143 15 : poDS = GDALDataset::Open(osResultFilename,
144 15 : poOpenInfo->nOpenFlags & ~GDAL_OF_SHARED,
145 : poOpenInfo->papszAllowedDrivers,
146 15 : poOpenInfo->papszOpenOptions, nullptr);
147 15 : CPLUninstallErrorHandlerAccumulator();
148 : }
149 :
150 : // Re-emit silenced errors if open was successful
151 15 : if (poDS)
152 : {
153 5 : for (const auto &oError : aoErrors)
154 : {
155 0 : CPLError(oError.type, oError.no, "%s", oError.msg.c_str());
156 : }
157 : }
158 :
159 : // The JP2OpenJPEG driver may need to reopen the file, hence this special
160 : // behavior
161 20 : if (poDS != nullptr && poDS->GetDriver() != nullptr &&
162 5 : EQUAL(poDS->GetDriver()->GetDescription(), "JP2OpenJPEG"))
163 : {
164 1 : poDS->MarkSuppressOnClose();
165 1 : return poDS;
166 : }
167 :
168 : /* -------------------------------------------------------------------- */
169 : /* If opening it in memory didn't work, perhaps we need to */
170 : /* write to a temp file on disk? */
171 : /* -------------------------------------------------------------------- */
172 14 : if (poDS == nullptr)
173 : {
174 20 : CPLString osTempFilename;
175 :
176 : #ifdef _WIN32
177 : const std::string osPath =
178 : CPLGetPathSafe(CPLGenerateTempFilenameSafe(NULL).c_str());
179 : #else
180 20 : const std::string osPath = "/tmp";
181 : #endif
182 10 : osTempFilename = CPLFormFilenameSafe(
183 10 : osPath.c_str(), CPLGetFilename(osResultFilename), nullptr);
184 10 : if (CPLCopyFile(osTempFilename, osResultFilename) != 0)
185 : {
186 0 : CPLError(CE_Failure, CPLE_OpenFailed,
187 : "Failed to create temporary file:%s",
188 : osTempFilename.c_str());
189 : }
190 : else
191 : {
192 10 : poDS = GDALDataset::Open(osTempFilename,
193 10 : poOpenInfo->nOpenFlags & ~GDAL_OF_SHARED,
194 : poOpenInfo->papszAllowedDrivers,
195 10 : poOpenInfo->papszOpenOptions, nullptr);
196 10 : if (VSIUnlink(osTempFilename) != 0 && poDS != nullptr)
197 0 : poDS->MarkSuppressOnClose(); /* VSIUnlink() may not work on
198 : windows */
199 10 : if (poDS && strcmp(poDS->GetDescription(), osTempFilename) == 0)
200 0 : poDS->SetDescription(poOpenInfo->pszFilename);
201 : }
202 : }
203 4 : else if (strcmp(poDS->GetDescription(), osResultFilename) == 0)
204 4 : poDS->SetDescription(poOpenInfo->pszFilename);
205 :
206 : /* -------------------------------------------------------------------- */
207 : /* Release our hold on the vsi memory file, though if it is */
208 : /* held open by a dataset it will continue to exist till that */
209 : /* lets it go. */
210 : /* -------------------------------------------------------------------- */
211 14 : VSIUnlink(osResultFilename);
212 :
213 14 : return poDS;
214 : }
215 :
216 : /************************************************************************/
217 : /* GDALRegister_HTTP() */
218 : /************************************************************************/
219 :
220 1682 : void GDALRegister_HTTP()
221 :
222 : {
223 1682 : if (GDALGetDriverByName("HTTP") != nullptr)
224 301 : return;
225 :
226 1381 : GDALDriver *poDriver = new GDALDriver();
227 :
228 1381 : poDriver->SetDescription("HTTP");
229 1381 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
230 1381 : poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
231 1381 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "HTTP Fetching Wrapper");
232 :
233 1381 : poDriver->pfnOpen = HTTPOpen;
234 :
235 1381 : GetGDALDriverManager()->RegisterDriver(poDriver);
236 : }
|