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 27527 : static GDALDataset *HTTPOpen(GDALOpenInfo *poOpenInfo) 72 : 73 : { 74 27527 : if (poOpenInfo->nHeaderBytes != 0) 75 1335 : return nullptr; 76 : 77 26192 : if (!STARTS_WITH_CI(poOpenInfo->pszFilename, "http:") && 78 26152 : !STARTS_WITH_CI(poOpenInfo->pszFilename, "https:") && 79 26156 : !STARTS_WITH_CI(poOpenInfo->pszFilename, "ftp:")) 80 26162 : 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 : CPLErrorAccumulator oErrorAccumulator; 140 : { 141 30 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler); 142 15 : auto oAccumulator = oErrorAccumulator.InstallForCurrentScope(); 143 15 : CPL_IGNORE_RET_VAL(oAccumulator); 144 15 : poDS = GDALDataset::Open(osResultFilename, 145 15 : poOpenInfo->nOpenFlags & ~GDAL_OF_SHARED, 146 : poOpenInfo->papszAllowedDrivers, 147 15 : poOpenInfo->papszOpenOptions, nullptr); 148 : } 149 : 150 : // Re-emit silenced errors if open was successful 151 15 : if (poDS) 152 : { 153 5 : oErrorAccumulator.ReplayErrors(); 154 : } 155 : 156 : // The JP2OpenJPEG driver may need to reopen the file, hence this special 157 : // behavior 158 20 : if (poDS != nullptr && poDS->GetDriver() != nullptr && 159 5 : EQUAL(poDS->GetDriver()->GetDescription(), "JP2OpenJPEG")) 160 : { 161 1 : poDS->MarkSuppressOnClose(); 162 1 : return poDS; 163 : } 164 : 165 : /* -------------------------------------------------------------------- */ 166 : /* If opening it in memory didn't work, perhaps we need to */ 167 : /* write to a temp file on disk? */ 168 : /* -------------------------------------------------------------------- */ 169 14 : if (poDS == nullptr) 170 : { 171 20 : CPLString osTempFilename; 172 : 173 : #ifdef _WIN32 174 : const std::string osPath = 175 : CPLGetPathSafe(CPLGenerateTempFilenameSafe(NULL).c_str()); 176 : #else 177 20 : const std::string osPath = "/tmp"; 178 : #endif 179 10 : osTempFilename = CPLFormFilenameSafe( 180 10 : osPath.c_str(), CPLGetFilename(osResultFilename), nullptr); 181 10 : if (CPLCopyFile(osTempFilename, osResultFilename) != 0) 182 : { 183 0 : CPLError(CE_Failure, CPLE_OpenFailed, 184 : "Failed to create temporary file:%s", 185 : osTempFilename.c_str()); 186 : } 187 : else 188 : { 189 10 : poDS = GDALDataset::Open(osTempFilename, 190 10 : poOpenInfo->nOpenFlags & ~GDAL_OF_SHARED, 191 : poOpenInfo->papszAllowedDrivers, 192 10 : poOpenInfo->papszOpenOptions, nullptr); 193 10 : if (VSIUnlink(osTempFilename) != 0 && poDS != nullptr) 194 0 : poDS->MarkSuppressOnClose(); /* VSIUnlink() may not work on 195 : windows */ 196 10 : if (poDS && strcmp(poDS->GetDescription(), osTempFilename) == 0) 197 0 : poDS->SetDescription(poOpenInfo->pszFilename); 198 : } 199 : } 200 4 : else if (strcmp(poDS->GetDescription(), osResultFilename) == 0) 201 4 : poDS->SetDescription(poOpenInfo->pszFilename); 202 : 203 : /* -------------------------------------------------------------------- */ 204 : /* Release our hold on the vsi memory file, though if it is */ 205 : /* held open by a dataset it will continue to exist till that */ 206 : /* lets it go. */ 207 : /* -------------------------------------------------------------------- */ 208 14 : VSIUnlink(osResultFilename); 209 : 210 14 : return poDS; 211 : } 212 : 213 : /************************************************************************/ 214 : /* GDALRegister_HTTP() */ 215 : /************************************************************************/ 216 : 217 1667 : void GDALRegister_HTTP() 218 : 219 : { 220 1667 : if (GDALGetDriverByName("HTTP") != nullptr) 221 282 : return; 222 : 223 1385 : GDALDriver *poDriver = new GDALDriver(); 224 : 225 1385 : poDriver->SetDescription("HTTP"); 226 1385 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES"); 227 1385 : poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES"); 228 1385 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "HTTP Fetching Wrapper"); 229 : 230 1385 : poDriver->pfnOpen = HTTPOpen; 231 : 232 1385 : GetGDALDriverManager()->RegisterDriver(poDriver); 233 : }