LCOV - code coverage report
Current view: top level - frmts/http - httpdriver.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 78 87 89.7 %
Date: 2024-11-25 13:07:18 Functions: 4 4 100.0 %

          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             : }

Generated by: LCOV version 1.14