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