LCOV - code coverage report
Current view: top level - frmts/nitf - rpftocfile.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 251 394 63.7 %
Date: 2026-01-21 22:42:07 Functions: 3 4 75.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  RPF A.TOC read Library
       4             :  * Purpose:  Module responsible for opening a RPF TOC file, populating RPFToc
       5             :  *           structure
       6             :  * Author:   Even Rouault, even.rouault at spatialys.com
       7             :  *
       8             :  **********************************************************************
       9             :  * Copyright (c) 2007-2010, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : /* Portions of code are placed under the following copyright : */
      15             : /*
      16             :  ******************************************************************************
      17             :  * Copyright (C) 1995 Logiciels et Applications Scientifiques (L.A.S.) Inc
      18             :  * Permission to use, copy, modify and distribute this software and
      19             :  * its documentation for any purpose and without fee is hereby granted,
      20             :  * provided that the above copyright notice appear in all copies, that
      21             :  * both the copyright notice and this permission notice appear in
      22             :  * supporting documentation, and that the name of L.A.S. Inc not be used
      23             :  * in advertising or publicity pertaining to distribution of the software
      24             :  * without specific, written prior permission. L.A.S. Inc. makes no
      25             :  * representations about the suitability of this software for any purpose.
      26             :  * It is provided "as is" without express or implied warranty.
      27             :  ******************************************************************************
      28             :  */
      29             : 
      30             : #include "cpl_port.h"
      31             : #include "rpftoclib.h"
      32             : 
      33             : #include <climits>
      34             : #include <cmath>
      35             : #include <cstring>
      36             : 
      37             : #include "cpl_conv.h"
      38             : #include "cpl_error.h"
      39             : #include "cpl_string.h"
      40             : #include "cpl_vsi.h"
      41             : #include "nitflib.h"
      42             : 
      43             : /************************************************************************/
      44             : /*                        RPFTOCTrim()                                    */
      45             : /************************************************************************/
      46             : 
      47          50 : static void RPFTOCTrim(char *str)
      48             : {
      49          50 :     char *c = str;
      50          50 :     if (str == nullptr || *str == 0)
      51           0 :         return;
      52             : 
      53          50 :     while (*c == ' ')
      54             :     {
      55           0 :         c++;
      56             :     }
      57          50 :     if (c != str)
      58             :     {
      59           0 :         memmove(str, c, strlen(c) + 1);
      60             :     }
      61             : 
      62          50 :     int i = static_cast<int>(strlen(str)) - 1;
      63          70 :     while (i >= 0 && str[i] == ' ')
      64             :     {
      65          20 :         str[i] = 0;
      66          20 :         i--;
      67             :     }
      68             : }
      69             : 
      70             : /************************************************************************/
      71             : /*                        RPFTOCRead()                                 */
      72             : /************************************************************************/
      73             : 
      74           0 : RPFToc *RPFTOCRead(const char *pszFilename, NITFFile *psFile)
      75             : {
      76             :     int nTRESize;
      77             :     const char *pachTRE =
      78           0 :         NITFFindTRE(psFile->pachTRE, psFile->nTREBytes, "RPFHDR", &nTRESize);
      79           0 :     if (pachTRE == nullptr)
      80             :     {
      81           0 :         CPLError(CE_Failure, CPLE_NotSupported,
      82             :                  "Invalid TOC file. Can't find RPFHDR.");
      83           0 :         return nullptr;
      84             :     }
      85             : 
      86           0 :     if (nTRESize != 48)
      87             :     {
      88           0 :         CPLError(CE_Failure, CPLE_NotSupported, "RPFHDR TRE wrong size.");
      89           0 :         return nullptr;
      90             :     }
      91             : 
      92           0 :     return RPFTOCReadFromBuffer(pszFilename, psFile->fp, pachTRE);
      93             : }
      94             : 
      95             : /* This function is directly inspired by function parse_toc coming from
      96             :  * ogdi/driver/rpf/utils.c */
      97             : 
      98          10 : RPFToc *RPFTOCReadFromBuffer(const char *pszFilename, VSILFILE *fp,
      99             :                              const char *tocHeader)
     100             : {
     101          10 :     tocHeader += 1;  /* skip endian */
     102          10 :     tocHeader += 2;  /* skip header length */
     103          10 :     tocHeader += 12; /* skip file name : this should be A.TOC (padded) */
     104          10 :     tocHeader += 1;  /* skip new  */
     105          10 :     tocHeader += 15; /* skip standard_num  */
     106          10 :     tocHeader += 8;  /* skip standard_date  */
     107          10 :     tocHeader += 1;  /* skip classification  */
     108          10 :     tocHeader += 2;  /* skip country  */
     109          10 :     tocHeader += 2;  /* skip release  */
     110             : 
     111             :     unsigned int locationSectionPhysicalLocation;
     112          10 :     memcpy(&locationSectionPhysicalLocation, tocHeader, sizeof(unsigned int));
     113          10 :     CPL_MSBPTR32(&locationSectionPhysicalLocation);
     114             : 
     115          10 :     if (VSIFSeekL(fp,
     116             :                   static_cast<vsi_l_offset>(locationSectionPhysicalLocation),
     117          10 :                   SEEK_SET) != 0)
     118             :     {
     119           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     120             :                  "Invalid TOC file. Unable to seek to "
     121             :                  "locationSectionPhysicalLocation at offset %d.",
     122             :                  locationSectionPhysicalLocation);
     123           0 :         return nullptr;
     124             :     }
     125             : 
     126             :     int nSections;
     127          10 :     NITFLocation *pasLocations = NITFReadRPFLocationTable(fp, &nSections);
     128             : 
     129          10 :     unsigned int boundaryRectangleSectionSubHeaderPhysIndex = 0;
     130          10 :     unsigned int boundaryRectangleTablePhysIndex = 0;
     131          10 :     unsigned int frameFileIndexSectionSubHeaderPhysIndex = 0;
     132          10 :     unsigned int frameFileIndexSubsectionPhysIndex = 0;
     133             : 
     134          50 :     for (int i = 0; i < nSections; i++)
     135             :     {
     136          40 :         if (pasLocations[i].nLocId == LID_BoundaryRectangleSectionSubheader)
     137             :         {
     138          10 :             boundaryRectangleSectionSubHeaderPhysIndex =
     139          10 :                 pasLocations[i].nLocOffset;
     140             :         }
     141          30 :         else if (pasLocations[i].nLocId == LID_BoundaryRectangleTable)
     142             :         {
     143          10 :             boundaryRectangleTablePhysIndex = pasLocations[i].nLocOffset;
     144             :         }
     145          20 :         else if (pasLocations[i].nLocId == LID_FrameFileIndexSectionSubHeader)
     146             :         {
     147          10 :             frameFileIndexSectionSubHeaderPhysIndex =
     148          10 :                 pasLocations[i].nLocOffset;
     149             :         }
     150          10 :         else if (pasLocations[i].nLocId == LID_FrameFileIndexSubsection)
     151             :         {
     152          10 :             frameFileIndexSubsectionPhysIndex = pasLocations[i].nLocOffset;
     153             :         }
     154             :     }
     155             : 
     156          10 :     CPLFree(pasLocations);
     157             : 
     158          10 :     if (boundaryRectangleSectionSubHeaderPhysIndex == 0)
     159             :     {
     160           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     161             :                  "Invalid TOC file. Can't find "
     162             :                  "LID_BoundaryRectangleSectionSubheader.");
     163           0 :         return nullptr;
     164             :     }
     165          10 :     if (boundaryRectangleTablePhysIndex == 0)
     166             :     {
     167           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     168             :                  "Invalid TOC file. Can't find LID_BoundaryRectangleTable.");
     169           0 :         return nullptr;
     170             :     }
     171          10 :     if (frameFileIndexSectionSubHeaderPhysIndex == 0)
     172             :     {
     173           0 :         CPLError(
     174             :             CE_Failure, CPLE_NotSupported,
     175             :             "Invalid TOC file. Can't find LID_FrameFileIndexSectionSubHeader.");
     176           0 :         return nullptr;
     177             :     }
     178          10 :     if (frameFileIndexSubsectionPhysIndex == 0)
     179             :     {
     180           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     181             :                  "Invalid TOC file. Can't find LID_FrameFileIndexSubsection.");
     182           0 :         return nullptr;
     183             :     }
     184             : 
     185          10 :     if (VSIFSeekL(fp,
     186             :                   static_cast<vsi_l_offset>(
     187             :                       boundaryRectangleSectionSubHeaderPhysIndex),
     188          10 :                   SEEK_SET) != 0)
     189             :     {
     190           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     191             :                  "Invalid TOC file. Unable to seek to "
     192             :                  "boundaryRectangleSectionSubHeaderPhysIndex at offset %d.",
     193             :                  boundaryRectangleSectionSubHeaderPhysIndex);
     194           0 :         return nullptr;
     195             :     }
     196             : 
     197             :     unsigned int boundaryRectangleTableOffset;
     198          10 :     bool bOK = VSIFReadL(&boundaryRectangleTableOffset,
     199          10 :                          sizeof(boundaryRectangleTableOffset), 1, fp) == 1;
     200          10 :     CPL_MSBPTR32(&boundaryRectangleTableOffset);
     201             : 
     202             :     unsigned short boundaryRectangleCount;
     203          10 :     bOK &= VSIFReadL(&boundaryRectangleCount, sizeof(boundaryRectangleCount), 1,
     204          10 :                      fp) == 1;
     205          10 :     CPL_MSBPTR16(&boundaryRectangleCount);
     206             : 
     207          20 :     if (!bOK ||
     208          10 :         VSIFSeekL(fp,
     209             :                   static_cast<vsi_l_offset>(boundaryRectangleTablePhysIndex),
     210             :                   SEEK_SET) != 0)
     211             :     {
     212           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     213             :                  "Invalid TOC file. Unable to seek to "
     214             :                  "boundaryRectangleTablePhysIndex at offset %d.",
     215             :                  boundaryRectangleTablePhysIndex);
     216           0 :         return nullptr;
     217             :     }
     218             : 
     219          10 :     RPFToc *toc = static_cast<RPFToc *>(CPLMalloc(sizeof(RPFToc)));
     220          10 :     toc->nEntries = boundaryRectangleCount;
     221          10 :     toc->entries = reinterpret_cast<RPFTocEntry *>(
     222          10 :         CPLMalloc(boundaryRectangleCount * sizeof(RPFTocEntry)));
     223          10 :     memset(toc->entries, 0, boundaryRectangleCount * sizeof(RPFTocEntry));
     224             : 
     225          20 :     for (int i = 0; i < toc->nEntries; i++)
     226             :     {
     227          10 :         toc->entries[i].isOverviewOrLegend = 0;
     228             : 
     229          10 :         bOK &= VSIFReadL(toc->entries[i].type, 1, 5, fp) == 5;
     230          10 :         toc->entries[i].type[5] = 0;
     231          10 :         RPFTOCTrim(toc->entries[i].type);
     232             : 
     233          10 :         bOK &= VSIFReadL(toc->entries[i].compression, 1, 5, fp) == 5;
     234          10 :         toc->entries[i].compression[5] = 0;
     235          10 :         RPFTOCTrim(toc->entries[i].compression);
     236             : 
     237          10 :         bOK &= VSIFReadL(toc->entries[i].scale, 1, 12, fp) == 12;
     238          10 :         toc->entries[i].scale[12] = 0;
     239          10 :         RPFTOCTrim(toc->entries[i].scale);
     240          10 :         if (toc->entries[i].scale[0] == '1' && toc->entries[i].scale[1] == ':')
     241             :         {
     242          10 :             memmove(toc->entries[i].scale, toc->entries[i].scale + 2,
     243          10 :                     strlen(toc->entries[i].scale + 2) + 1);
     244             :         }
     245             : 
     246          10 :         bOK &= VSIFReadL(toc->entries[i].zone, 1, 1, fp) == 1;
     247          10 :         toc->entries[i].zone[1] = 0;
     248          10 :         RPFTOCTrim(toc->entries[i].zone);
     249             : 
     250          10 :         bOK &= VSIFReadL(toc->entries[i].producer, 1, 5, fp) == 5;
     251          10 :         toc->entries[i].producer[5] = 0;
     252          10 :         RPFTOCTrim(toc->entries[i].producer);
     253             : 
     254          10 :         bOK &= VSIFReadL(&toc->entries[i].nwLat, sizeof(double), 1, fp) == 1;
     255          10 :         CPL_MSBPTR64(&toc->entries[i].nwLat);
     256             : 
     257          10 :         bOK &= VSIFReadL(&toc->entries[i].nwLong, sizeof(double), 1, fp) == 1;
     258          10 :         CPL_MSBPTR64(&toc->entries[i].nwLong);
     259             : 
     260          10 :         bOK &= VSIFReadL(&toc->entries[i].swLat, sizeof(double), 1, fp) == 1;
     261          10 :         CPL_MSBPTR64(&toc->entries[i].swLat);
     262             : 
     263          10 :         bOK &= VSIFReadL(&toc->entries[i].swLong, sizeof(double), 1, fp) == 1;
     264          10 :         CPL_MSBPTR64(&toc->entries[i].swLong);
     265             : 
     266          10 :         bOK &= VSIFReadL(&toc->entries[i].neLat, sizeof(double), 1, fp) == 1;
     267          10 :         CPL_MSBPTR64(&toc->entries[i].neLat);
     268             : 
     269          10 :         bOK &= VSIFReadL(&toc->entries[i].neLong, sizeof(double), 1, fp) == 1;
     270          10 :         CPL_MSBPTR64(&toc->entries[i].neLong);
     271             : 
     272          10 :         bOK &= VSIFReadL(&toc->entries[i].seLat, sizeof(double), 1, fp) == 1;
     273          10 :         CPL_MSBPTR64(&toc->entries[i].seLat);
     274             : 
     275          10 :         bOK &= VSIFReadL(&toc->entries[i].seLong, sizeof(double), 1, fp) == 1;
     276          10 :         CPL_MSBPTR64(&toc->entries[i].seLong);
     277             : 
     278          10 :         bOK &= VSIFReadL(&toc->entries[i].vertResolution, sizeof(double), 1,
     279          10 :                          fp) == 1;
     280          10 :         CPL_MSBPTR64(&toc->entries[i].vertResolution);
     281             : 
     282          10 :         bOK &= VSIFReadL(&toc->entries[i].horizResolution, sizeof(double), 1,
     283          10 :                          fp) == 1;
     284          10 :         CPL_MSBPTR64(&toc->entries[i].horizResolution);
     285             : 
     286          10 :         bOK &= VSIFReadL(&toc->entries[i].vertInterval, sizeof(double), 1,
     287          10 :                          fp) == 1;
     288          10 :         CPL_MSBPTR64(&toc->entries[i].vertInterval);
     289             : 
     290          10 :         bOK &= VSIFReadL(&toc->entries[i].horizInterval, sizeof(double), 1,
     291          10 :                          fp) == 1;
     292          10 :         CPL_MSBPTR64(&toc->entries[i].horizInterval);
     293             : 
     294          10 :         bOK &= VSIFReadL(&toc->entries[i].nVertFrames, sizeof(int), 1, fp) == 1;
     295          10 :         CPL_MSBPTR32(&toc->entries[i].nVertFrames);
     296             : 
     297          10 :         bOK &=
     298          10 :             VSIFReadL(&toc->entries[i].nHorizFrames, sizeof(int), 1, fp) == 1;
     299          10 :         CPL_MSBPTR32(&toc->entries[i].nHorizFrames);
     300             : 
     301          10 :         if (!bOK)
     302             :         {
     303           0 :             CPLError(CE_Failure, CPLE_FileIO, "I/O error");
     304           0 :             toc->entries[i].nVertFrames = 0;
     305           0 :             toc->entries[i].nHorizFrames = 0;
     306           0 :             RPFTOCFree(toc);
     307           0 :             return nullptr;
     308             :         }
     309             : 
     310             :         // do some basic plausibility checks for all entries
     311          30 :         if (toc->entries[i].vertInterval <= 1e-10 ||
     312          10 :             !std::isfinite(toc->entries[i].vertInterval) ||
     313          10 :             toc->entries[i].horizInterval <= 1e-10 ||
     314          10 :             !std::isfinite(toc->entries[i].horizInterval) ||
     315          10 :             toc->entries[i].nHorizFrames == 0 ||
     316          30 :             toc->entries[i].nVertFrames == 0 ||
     317          10 :             toc->entries[i].nHorizFrames >
     318          10 :                 INT_MAX / toc->entries[i].nVertFrames)
     319             :         {
     320           0 :             CPLError(CE_Failure, CPLE_FileIO, "Invalid TOC entry");
     321           0 :             toc->entries[i].nVertFrames = 0;
     322           0 :             toc->entries[i].nHorizFrames = 0;
     323           0 :             RPFTOCFree(toc);
     324           0 :             return nullptr;
     325             :         }
     326             : 
     327             :         // Overview has ZONE 'R' and Legend ZONE 'D' but because the Zone 'D' is
     328             :         // also a valid Zone we need an additional check. -> In all cases of
     329             :         // Overview/Legend the values of the BoundingBox are equal so we simply
     330             :         // check here that NW == SE is.
     331          10 :         toc->entries[i].isOverviewOrLegend =
     332          20 :             (toc->entries[i].zone[0] == 'R' ||   // Overview
     333          10 :              (toc->entries[i].zone[0] == 'D' &&  // Legend
     334           0 :               memcmp(&(toc->entries[i].seLong), &(toc->entries[i].nwLong),
     335           0 :                      sizeof(toc->entries[i].nwLong)) == 0 &&
     336           0 :               memcmp(&(toc->entries[i].seLat), &(toc->entries[i].nwLat),
     337             :                      sizeof(toc->entries[i].nwLat)) == 0));
     338             : 
     339          20 :         bool isPolarZone = (toc->entries[i].zone[0] == '9') ||
     340          10 :                            (toc->entries[i].zone[0] == 'J');
     341             : 
     342             :         // make additional checks of the bounding for charts (without Legends
     343             :         // and Overviews)
     344          10 :         if (!toc->entries[i].isOverviewOrLegend)
     345             :         {
     346          10 :             if (!(fabs(toc->entries[i].seLong) <= 360.0) ||
     347          10 :                 !(fabs(toc->entries[i].nwLong) <= 360.0) ||
     348          10 :                 !(fabs(toc->entries[i].nwLat) <= 90.0) ||
     349          10 :                 !(fabs(toc->entries[i].seLat) <= 90.0) ||
     350             :                 // check only for non-polar zones, because the values are not
     351             :                 // always correct here
     352          10 :                 (!isPolarZone &&
     353          10 :                  (toc->entries[i].seLong < toc->entries[i].nwLong ||
     354          10 :                   toc->entries[i].nwLat < toc->entries[i].seLat)))
     355             :             {
     356           0 :                 CPLError(CE_Failure, CPLE_FileIO, "Invalid TOC entry");
     357           0 :                 toc->entries[i].nVertFrames = 0;
     358           0 :                 toc->entries[i].nHorizFrames = 0;
     359           0 :                 RPFTOCFree(toc);
     360           0 :                 return nullptr;
     361             :             }
     362             :         }
     363             : 
     364             :         // TODO: We could probably use another data structure, like a list,
     365             :         // instead of an array referenced by the frame coordinate...
     366          20 :         if (static_cast<int>(toc->entries[i].nHorizFrames *
     367          20 :                              toc->entries[i].nVertFrames) >
     368          10 :             atoi(CPLGetConfigOption("RPFTOC_MAX_FRAME_COUNT", "1000000")))
     369             :         {
     370           0 :             CPLError(
     371             :                 CE_Failure, CPLE_AppDefined,
     372             :                 "nHorizFrames=%d x nVertFrames=%d > %d. Please raise "
     373             :                 "the value of the RPFTOC_MAX_FRAME_COUNT configuration "
     374             :                 "option to more than %d if this dataset is legitimate.",
     375           0 :                 toc->entries[i].nHorizFrames, toc->entries[i].nVertFrames,
     376             :                 atoi(CPLGetConfigOption("RPFTOC_MAX_FRAME_COUNT", "1000000")),
     377           0 :                 toc->entries[i].nHorizFrames * toc->entries[i].nVertFrames);
     378           0 :             toc->entries[i].frameEntries = nullptr;
     379             :         }
     380             :         else
     381             :         {
     382          20 :             toc->entries[i].frameEntries =
     383          10 :                 static_cast<RPFTocFrameEntry *>(VSI_CALLOC_VERBOSE(
     384             :                     static_cast<size_t>(toc->entries[i].nVertFrames) *
     385             :                         toc->entries[i].nHorizFrames,
     386             :                     sizeof(RPFTocFrameEntry)));
     387             :         }
     388          10 :         if (toc->entries[i].frameEntries == nullptr)
     389             :         {
     390           0 :             toc->entries[i].nVertFrames = 0;
     391           0 :             toc->entries[i].nHorizFrames = 0;
     392           0 :             RPFTOCFree(toc);
     393           0 :             return nullptr;
     394             :         }
     395             : 
     396          10 :         CPLDebug("RPFTOC",
     397             :                  "[%d] type=%s, compression=%s, scale=%s, zone=%s, "
     398             :                  "producer=%s, nVertFrames=%d, nHorizFrames=%d",
     399          10 :                  i, toc->entries[i].type, toc->entries[i].compression,
     400          10 :                  toc->entries[i].scale, toc->entries[i].zone,
     401          10 :                  toc->entries[i].producer, toc->entries[i].nVertFrames,
     402          10 :                  toc->entries[i].nHorizFrames);
     403             :     }
     404             : 
     405          10 :     if (VSIFSeekL(
     406             :             fp,
     407             :             static_cast<vsi_l_offset>(frameFileIndexSectionSubHeaderPhysIndex),
     408          10 :             SEEK_SET) != 0)
     409             :     {
     410           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     411             :                  "Invalid TOC file. Unable to seek to "
     412             :                  "frameFileIndexSectionSubHeaderPhysIndex at offset %d.",
     413             :                  frameFileIndexSectionSubHeaderPhysIndex);
     414           0 :         RPFTOCFree(toc);
     415           0 :         return nullptr;
     416             :     }
     417             : 
     418             :     /* Skip 1 byte security classification */
     419          10 :     bOK &= VSIFSeekL(fp, 1, SEEK_CUR) == 0;
     420             : 
     421             :     unsigned int frameIndexTableOffset;
     422          10 :     bOK &= VSIFReadL(&frameIndexTableOffset, sizeof(frameIndexTableOffset), 1,
     423          10 :                      fp) == 1;
     424          10 :     CPL_MSBPTR32(&frameIndexTableOffset);
     425             : 
     426             :     unsigned int nFrameFileIndexRecords;
     427          10 :     bOK &= VSIFReadL(&nFrameFileIndexRecords, sizeof(nFrameFileIndexRecords), 1,
     428          10 :                      fp) == 1;
     429          10 :     CPL_MSBPTR32(&nFrameFileIndexRecords);
     430             : 
     431             :     unsigned short nFrameFilePathnameRecords;
     432          10 :     bOK &= VSIFReadL(&nFrameFilePathnameRecords,
     433          10 :                      sizeof(nFrameFilePathnameRecords), 1, fp) == 1;
     434          10 :     CPL_MSBPTR16(&nFrameFilePathnameRecords);
     435             : 
     436             :     unsigned short frameFileIndexRecordLength;
     437          10 :     bOK &= VSIFReadL(&frameFileIndexRecordLength,
     438          10 :                      sizeof(frameFileIndexRecordLength), 1, fp) == 1;
     439          10 :     CPL_MSBPTR16(&frameFileIndexRecordLength);
     440          10 :     if (frameFileIndexRecordLength < 3 * sizeof(short))
     441             :     {
     442           0 :         CPLError(CE_Failure, CPLE_FileIO, "Invalid file");
     443           0 :         RPFTOCFree(toc);
     444           0 :         return nullptr;
     445             :     }
     446             : 
     447          10 :     if (!bOK)
     448             :     {
     449           0 :         CPLError(CE_Failure, CPLE_FileIO, "I/O error");
     450           0 :         RPFTOCFree(toc);
     451           0 :         return nullptr;
     452             :     }
     453             : 
     454          10 :     int newBoundaryId = 0;
     455             : 
     456          20 :     for (int i = 0; i < static_cast<int>(nFrameFileIndexRecords); i++)
     457             :     {
     458          10 :         vsi_l_offset nFrameOffset =
     459          10 :             static_cast<vsi_l_offset>(frameFileIndexSubsectionPhysIndex) +
     460          10 :             static_cast<vsi_l_offset>(frameFileIndexRecordLength) * i;
     461          10 :         if (VSIFSeekL(fp, nFrameOffset, SEEK_SET) != 0)
     462             :         {
     463           0 :             CPLError(
     464             :                 CE_Failure, CPLE_NotSupported,
     465             :                 "Invalid TOC file. Unable to seek to "
     466             :                 "frameFileIndexSubsectionPhysIndex(%d) at offset " CPL_FRMT_GUIB
     467             :                 ".",
     468             :                 i, static_cast<GUIntBig>(nFrameOffset));
     469           0 :             RPFTOCFree(toc);
     470           0 :             return nullptr;
     471             :         }
     472             : 
     473             :         unsigned short boundaryId;
     474          10 :         if (VSIFReadL(&boundaryId, sizeof(boundaryId), 1, fp) != 1)
     475             :         {
     476           0 :             CPLError(CE_Failure, CPLE_FileIO, "I/O error");
     477           0 :             RPFTOCFree(toc);
     478           0 :             return nullptr;
     479             :         }
     480          10 :         CPL_MSBPTR16(&boundaryId);
     481             : 
     482          10 :         if (i == 0 && boundaryId == 0)
     483          10 :             newBoundaryId = 1;
     484          10 :         if (newBoundaryId == 0)
     485           0 :             boundaryId--;
     486             : 
     487          10 :         if (boundaryId >= toc->nEntries)
     488             :         {
     489           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     490             :                      "Invalid TOC file. Bad boundary id (%d) for frame file "
     491             :                      "index %d.",
     492             :                      boundaryId, i);
     493           0 :             RPFTOCFree(toc);
     494           0 :             return nullptr;
     495             :         }
     496             : 
     497          10 :         RPFTocEntry *entry = &toc->entries[boundaryId];
     498          10 :         entry->boundaryId = boundaryId;
     499             : 
     500             :         unsigned short frameRow;
     501          10 :         bOK &= VSIFReadL(&frameRow, sizeof(frameRow), 1, fp) == 1;
     502          10 :         CPL_MSBPTR16(&frameRow);
     503             : 
     504             :         unsigned short frameCol;
     505          10 :         bOK &= VSIFReadL(&frameCol, sizeof(frameCol), 1, fp) == 1;
     506          10 :         CPL_MSBPTR16(&frameCol);
     507          10 :         if (!bOK)
     508             :         {
     509           0 :             CPLError(CE_Failure, CPLE_FileIO, "I/O error");
     510           0 :             RPFTOCFree(toc);
     511           0 :             return nullptr;
     512             :         }
     513             : 
     514          10 :         if (newBoundaryId == 0)
     515             :         {
     516           0 :             frameRow--;
     517           0 :             frameCol--;
     518             :         }
     519             :         else
     520             :         {
     521             :             /* Trick so that frames are numbered north to south */
     522          10 :             if (entry->nVertFrames - 1 < frameRow)
     523             :             {
     524           0 :                 CPLError(CE_Failure, CPLE_FileIO,
     525             :                          "Invalid nVertFrames vs frameRow");
     526           0 :                 RPFTOCFree(toc);
     527           0 :                 return nullptr;
     528             :             }
     529          10 :             frameRow = static_cast<unsigned short>((entry->nVertFrames - 1) -
     530             :                                                    frameRow);
     531             :         }
     532             : 
     533          10 :         if (frameRow >= entry->nVertFrames)
     534             :         {
     535           0 :             CPLError(
     536             :                 CE_Failure, CPLE_NotSupported,
     537             :                 "Invalid TOC file. Bad row num (%d) for frame file index %d.",
     538             :                 frameRow, i);
     539           0 :             RPFTOCFree(toc);
     540           0 :             return nullptr;
     541             :         }
     542             : 
     543          10 :         if (frameCol >= entry->nHorizFrames)
     544             :         {
     545           0 :             CPLError(
     546             :                 CE_Failure, CPLE_NotSupported,
     547             :                 "Invalid TOC file. Bad col num (%d) for frame file index %d.",
     548             :                 frameCol, i);
     549           0 :             RPFTOCFree(toc);
     550           0 :             return nullptr;
     551             :         }
     552             : 
     553          10 :         RPFTocFrameEntry *frameEntry =
     554          10 :             &entry->frameEntries[frameRow * entry->nHorizFrames + frameCol];
     555          10 :         frameEntry->frameRow = frameRow;
     556          10 :         frameEntry->frameCol = frameCol;
     557             : 
     558          10 :         if (frameEntry->exists)
     559             :         {
     560           0 :             CPLError(
     561             :                 CE_Warning, CPLE_AppDefined,
     562             :                 "Frame entry(%d,%d) for frame file index %d was already found.",
     563             :                 frameRow, frameCol, i);
     564           0 :             CPLFree(frameEntry->directory);
     565           0 :             frameEntry->directory = nullptr;
     566           0 :             CPLFree(frameEntry->fullFilePath);
     567           0 :             frameEntry->fullFilePath = nullptr;
     568           0 :             frameEntry->exists = 0;
     569             :         }
     570             : 
     571             :         unsigned int offsetFrameFilePathName;
     572          10 :         bOK &= VSIFReadL(&offsetFrameFilePathName,
     573          10 :                          sizeof(offsetFrameFilePathName), 1, fp) == 1;
     574          10 :         CPL_MSBPTR32(&offsetFrameFilePathName);
     575             : 
     576          10 :         bOK &= VSIFReadL(frameEntry->filename, 1, 12, fp) == 12;
     577          10 :         if (!bOK)
     578             :         {
     579           0 :             CPLError(CE_Failure, CPLE_FileIO, "I/O error");
     580           0 :             RPFTOCFree(toc);
     581           0 :             return nullptr;
     582             :         }
     583          10 :         frameEntry->filename[12] = '\0';
     584          10 :         bOK &= strlen(frameEntry->filename) > 0;
     585             : 
     586          10 :         if (CPLHasPathTraversal(frameEntry->filename))
     587             :         {
     588           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     589           0 :                      "Path traversal detected in %s", frameEntry->filename);
     590           0 :             RPFTOCFree(toc);
     591           0 :             return nullptr;
     592             :         }
     593             : 
     594             :         // Check (case insensitive) if the filename is an overview or legend
     595             :         // some CADRG maps have legend name smaller than 8.3 then the extension
     596             :         // has blanks (0x20) at the end -> check only the first 3 letters of the
     597             :         // extension.
     598          10 :         const std::string fileExt = CPLGetExtensionSafe(frameEntry->filename);
     599          20 :         if (EQUALN(fileExt.c_str(), "ovr", 3) ||
     600          10 :             EQUALN(fileExt.c_str(), "lgd", 3))
     601             :         {
     602           0 :             entry->isOverviewOrLegend = TRUE;
     603             :         }
     604             : 
     605             :         /* Extract series code */
     606          10 :         if (entry->seriesAbbreviation == nullptr)
     607             :         {
     608          10 :             const NITFSeries *series = NITFGetSeriesInfo(frameEntry->filename);
     609          10 :             if (series)
     610             :             {
     611          10 :                 entry->seriesAbbreviation = series->abbreviation;
     612          10 :                 entry->seriesName = series->name;
     613             :             }
     614             :         }
     615             : 
     616             :         /* Get file geo reference */
     617          10 :         bOK &= VSIFReadL(frameEntry->georef, 1, 6, fp) == 6;
     618          10 :         frameEntry->georef[6] = '\0';
     619             : 
     620             :         /* Go to start of pathname record */
     621             :         /* New path_off offset from start of frame file index section of TOC??
     622             :          */
     623             :         /* Add pathoffset wrt frame file index table subsection (loc[3]) */
     624          20 :         if (!bOK || VSIFSeekL(fp,
     625          10 :                               static_cast<vsi_l_offset>(
     626             :                                   frameFileIndexSubsectionPhysIndex) +
     627          10 :                                   offsetFrameFilePathName,
     628             :                               SEEK_SET) != 0)
     629             :         {
     630           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     631             :                      "Invalid TOC file. Unable to seek to "
     632             :                      "frameFileIndexSubsectionPhysIndex + "
     633             :                      "offsetFrameFilePathName(%d) at offset " CPL_FRMT_GUIB ".",
     634             :                      i,
     635           0 :                      static_cast<GUIntBig>(frameFileIndexSubsectionPhysIndex) +
     636           0 :                          offsetFrameFilePathName);
     637           0 :             RPFTOCFree(toc);
     638           0 :             return nullptr;
     639             :         }
     640             : 
     641             :         unsigned short pathLength;
     642          10 :         bOK &= VSIFReadL(&pathLength, sizeof(pathLength), 1, fp) == 1;
     643          10 :         CPL_MSBPTR16(&pathLength);
     644             : 
     645             :         /* if nFrameFileIndexRecords == 65535 and pathLength == 65535 for each
     646             :            record, this leads to 4 GB allocation... Protect against this case */
     647          10 :         if (!bOK || pathLength == 0 || pathLength > 256)
     648             :         {
     649           0 :             CPLError(
     650             :                 CE_Failure, CPLE_NotSupported,
     651             :                 "Path length is invalid : %d. Probably corrupted TOC file.",
     652             :                 static_cast<int>(pathLength));
     653           0 :             RPFTOCFree(toc);
     654           0 :             return nullptr;
     655             :         }
     656             : 
     657          10 :         frameEntry->directory = static_cast<char *>(CPLMalloc(pathLength + 1));
     658          10 :         bOK &=
     659          10 :             VSIFReadL(frameEntry->directory, 1, pathLength, fp) == pathLength;
     660          10 :         if (!bOK)
     661             :         {
     662           0 :             CPLError(CE_Failure, CPLE_FileIO, "I/O error");
     663           0 :             RPFTOCFree(toc);
     664           0 :             return nullptr;
     665             :         }
     666          10 :         frameEntry->directory[pathLength] = 0;
     667          10 :         if (frameEntry->directory[pathLength - 1] == '/')
     668          10 :             frameEntry->directory[pathLength - 1] = 0;
     669             : 
     670          10 :         if (CPLHasPathTraversal(frameEntry->directory))
     671             :         {
     672           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     673             :                      "Path traversal detected in %s", frameEntry->directory);
     674           0 :             RPFTOCFree(toc);
     675           0 :             return nullptr;
     676             :         }
     677             : 
     678          10 :         if (frameEntry->directory[0] == '.' && frameEntry->directory[1] == '/')
     679             :         {
     680           0 :             memmove(frameEntry->directory, frameEntry->directory + 2,
     681           0 :                     strlen(frameEntry->directory + 2) + 1);
     682             : 
     683             :             // Some A.TOC have subdirectory names like ".//X/" ... (#5979)
     684             :             // Check if it was not intended to be "./X/" instead.
     685             :             VSIStatBufL sStatBuf;
     686           0 :             if (frameEntry->directory[0] == '/' &&
     687           0 :                 VSIStatL(
     688           0 :                     CPLFormFilenameSafe(CPLGetDirnameSafe(pszFilename).c_str(),
     689           0 :                                         frameEntry->directory + 1, nullptr)
     690             :                         .c_str(),
     691           0 :                     &sStatBuf) == 0 &&
     692           0 :                 VSI_ISDIR(sStatBuf.st_mode))
     693             :             {
     694           0 :                 memmove(frameEntry->directory, frameEntry->directory + 1,
     695           0 :                         strlen(frameEntry->directory + 1) + 1);
     696             :             }
     697             :         }
     698             : 
     699             :         {
     700          10 :             char *baseDir = CPLStrdup(CPLGetDirnameSafe(pszFilename).c_str());
     701             :             VSIStatBufL sStatBuf;
     702          10 :             char *subdir = nullptr;
     703          10 :             if (CPLIsFilenameRelative(frameEntry->directory) == FALSE)
     704           0 :                 subdir = CPLStrdup(frameEntry->directory);
     705          10 :             else if (frameEntry->directory[0] == '.' &&
     706          10 :                      frameEntry->directory[1] == 0)
     707          10 :                 subdir = CPLStrdup(baseDir);
     708             :             else
     709           0 :                 subdir = CPLStrdup(
     710           0 :                     CPLFormFilenameSafe(baseDir, frameEntry->directory, nullptr)
     711             :                         .c_str());
     712             : #if !defined(_WIN32) && !defined(_WIN32_CE)
     713          10 :             if (VSIStatL(subdir, &sStatBuf) != 0 &&
     714           0 :                 strlen(subdir) > strlen(baseDir))
     715             :             {
     716           0 :                 char *c = subdir + strlen(baseDir) + 1;
     717           0 :                 while (*c)
     718             :                 {
     719           0 :                     if (*c >= 'A' && *c <= 'Z')
     720           0 :                         *c += 'a' - 'A';
     721           0 :                     c++;
     722             :                 }
     723             :             }
     724             : #endif
     725          10 :             frameEntry->fullFilePath = CPLStrdup(
     726          20 :                 CPLFormFilenameSafe(subdir, frameEntry->filename, nullptr)
     727             :                     .c_str());
     728          10 :             if (VSIStatL(frameEntry->fullFilePath, &sStatBuf) != 0)
     729             :             {
     730             : #if !defined(_WIN32) && !defined(_WIN32_CE)
     731           0 :                 if (strlen(frameEntry->fullFilePath) > strlen(subdir))
     732             :                 {
     733           0 :                     char *c = frameEntry->fullFilePath + strlen(subdir) + 1;
     734           0 :                     while (*c)
     735             :                     {
     736           0 :                         if (*c >= 'A' && *c <= 'Z')
     737           0 :                             *c += 'a' - 'A';
     738           0 :                         c++;
     739             :                     }
     740             :                 }
     741           0 :                 if (VSIStatL(frameEntry->fullFilePath, &sStatBuf) != 0)
     742             : #endif
     743             :                 {
     744           0 :                     frameEntry->fileExists = 0;
     745           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
     746             :                              "File %s does not exist.",
     747             :                              frameEntry->fullFilePath);
     748             :                 }
     749             : #if !defined(_WIN32) && !defined(_WIN32_CE)
     750             :                 else
     751             :                 {
     752           0 :                     frameEntry->fileExists = 1;
     753             :                 }
     754             : #endif
     755             :             }
     756             :             else
     757             :             {
     758          10 :                 frameEntry->fileExists = 1;
     759             :             }
     760          10 :             CPLFree(subdir);
     761          10 :             CPLFree(baseDir);
     762             :         }
     763             : 
     764          10 :         CPLDebug("RPFTOC", "Entry %d : %s,%s (%d, %d)", boundaryId,
     765          10 :                  frameEntry->directory, frameEntry->filename, frameRow,
     766             :                  frameCol);
     767             : 
     768          10 :         frameEntry->exists = 1;
     769             :     }
     770             : 
     771          10 :     return toc;
     772             : }
     773             : 
     774             : /************************************************************************/
     775             : /*                        RPFTOCFree()                                 */
     776             : /************************************************************************/
     777             : 
     778          10 : void RPFTOCFree(RPFToc *toc)
     779             : {
     780          10 :     if (!toc)
     781           0 :         return;
     782             : 
     783          20 :     for (int i = 0; i < toc->nEntries; i++)
     784             :     {
     785          20 :         for (int j = 0; j < static_cast<int>(toc->entries[i].nVertFrames *
     786          20 :                                              toc->entries[i].nHorizFrames);
     787             :              j++)
     788             :         {
     789          10 :             CPLFree(toc->entries[i].frameEntries[j].fullFilePath);
     790          10 :             CPLFree(toc->entries[i].frameEntries[j].directory);
     791             :         }
     792          10 :         CPLFree(toc->entries[i].frameEntries);
     793             :     }
     794             : 
     795          10 :     CPLFree(toc->entries);
     796          10 :     CPLFree(toc);
     797             : }

Generated by: LCOV version 1.14