Coverage Report

Created: 2026-08-14 09:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/nitf/rpftocfile.cpp
Line
Count
Source
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
static void RPFTOCTrim(char *str)
48
0
{
49
0
    char *c = str;
50
0
    if (str == nullptr || *str == 0)
51
0
        return;
52
53
0
    while (*c == ' ')
54
0
    {
55
0
        c++;
56
0
    }
57
0
    if (c != str)
58
0
    {
59
0
        memmove(str, c, strlen(c) + 1);
60
0
    }
61
62
0
    int i = static_cast<int>(strlen(str)) - 1;
63
0
    while (i >= 0 && str[i] == ' ')
64
0
    {
65
0
        str[i] = 0;
66
0
        i--;
67
0
    }
68
0
}
69
70
/************************************************************************/
71
/*                             RPFTOCRead()                             */
72
/************************************************************************/
73
74
RPFToc *RPFTOCRead(const char *pszFilename, NITFFile *psFile)
75
0
{
76
0
    int nTRESize;
77
0
    const char *pachTRE =
78
0
        NITFFindTRE(psFile->pachTRE, psFile->nTREBytes, "RPFHDR", &nTRESize);
79
0
    if (pachTRE == nullptr)
80
0
    {
81
0
        CPLError(CE_Failure, CPLE_NotSupported,
82
0
                 "Invalid TOC file. Can't find RPFHDR.");
83
0
        return nullptr;
84
0
    }
85
86
0
    if (nTRESize != 48)
87
0
    {
88
0
        CPLError(CE_Failure, CPLE_NotSupported, "RPFHDR TRE wrong size.");
89
0
        return nullptr;
90
0
    }
91
92
0
    return RPFTOCReadFromBuffer(pszFilename, psFile->fp, pachTRE);
93
0
}
94
95
/* This function is directly inspired by function parse_toc coming from
96
 * ogdi/driver/rpf/utils.c */
97
98
RPFToc *RPFTOCReadFromBuffer(const char *pszFilename, VSILFILE *fp,
99
                             const char *tocHeader)
100
18
{
101
18
    tocHeader += 1;  /* skip endian */
102
18
    tocHeader += 2;  /* skip header length */
103
18
    tocHeader += 12; /* skip file name : this should be A.TOC (padded) */
104
18
    tocHeader += 1;  /* skip new  */
105
18
    tocHeader += 15; /* skip standard_num  */
106
18
    tocHeader += 8;  /* skip standard_date  */
107
18
    tocHeader += 1;  /* skip classification  */
108
18
    tocHeader += 2;  /* skip country  */
109
18
    tocHeader += 2;  /* skip release  */
110
111
18
    unsigned int locationSectionPhysicalLocation;
112
18
    memcpy(&locationSectionPhysicalLocation, tocHeader, sizeof(unsigned int));
113
18
    CPL_MSBPTR32(&locationSectionPhysicalLocation);
114
115
18
    if (VSIFSeekL(fp,
116
18
                  static_cast<vsi_l_offset>(locationSectionPhysicalLocation),
117
18
                  SEEK_SET) != 0)
118
0
    {
119
0
        CPLError(CE_Failure, CPLE_NotSupported,
120
0
                 "Invalid TOC file. Unable to seek to "
121
0
                 "locationSectionPhysicalLocation at offset %d.",
122
0
                 locationSectionPhysicalLocation);
123
0
        return nullptr;
124
0
    }
125
126
18
    int nSections;
127
18
    NITFLocation *pasLocations = NITFReadRPFLocationTable(fp, &nSections);
128
129
18
    unsigned int boundaryRectangleSectionSubHeaderPhysIndex = 0;
130
18
    unsigned int boundaryRectangleTablePhysIndex = 0;
131
18
    unsigned int frameFileIndexSectionSubHeaderPhysIndex = 0;
132
18
    unsigned int frameFileIndexSubsectionPhysIndex = 0;
133
134
18
    for (int i = 0; i < nSections; i++)
135
0
    {
136
0
        if (pasLocations[i].nLocId == LID_BoundaryRectangleSectionSubheader)
137
0
        {
138
0
            boundaryRectangleSectionSubHeaderPhysIndex =
139
0
                pasLocations[i].nLocOffset;
140
0
        }
141
0
        else if (pasLocations[i].nLocId == LID_BoundaryRectangleTable)
142
0
        {
143
0
            boundaryRectangleTablePhysIndex = pasLocations[i].nLocOffset;
144
0
        }
145
0
        else if (pasLocations[i].nLocId == LID_FrameFileIndexSectionSubHeader)
146
0
        {
147
0
            frameFileIndexSectionSubHeaderPhysIndex =
148
0
                pasLocations[i].nLocOffset;
149
0
        }
150
0
        else if (pasLocations[i].nLocId == LID_FrameFileIndexSubsection)
151
0
        {
152
0
            frameFileIndexSubsectionPhysIndex = pasLocations[i].nLocOffset;
153
0
        }
154
0
    }
155
156
18
    CPLFree(pasLocations);
157
158
18
    if (boundaryRectangleSectionSubHeaderPhysIndex == 0)
159
18
    {
160
18
        CPLError(CE_Failure, CPLE_NotSupported,
161
18
                 "Invalid TOC file. Can't find "
162
18
                 "LID_BoundaryRectangleSectionSubheader.");
163
18
        return nullptr;
164
18
    }
165
0
    if (boundaryRectangleTablePhysIndex == 0)
166
0
    {
167
0
        CPLError(CE_Failure, CPLE_NotSupported,
168
0
                 "Invalid TOC file. Can't find LID_BoundaryRectangleTable.");
169
0
        return nullptr;
170
0
    }
171
0
    if (frameFileIndexSectionSubHeaderPhysIndex == 0)
172
0
    {
173
0
        CPLError(
174
0
            CE_Failure, CPLE_NotSupported,
175
0
            "Invalid TOC file. Can't find LID_FrameFileIndexSectionSubHeader.");
176
0
        return nullptr;
177
0
    }
178
0
    if (frameFileIndexSubsectionPhysIndex == 0)
179
0
    {
180
0
        CPLError(CE_Failure, CPLE_NotSupported,
181
0
                 "Invalid TOC file. Can't find LID_FrameFileIndexSubsection.");
182
0
        return nullptr;
183
0
    }
184
185
0
    if (VSIFSeekL(fp,
186
0
                  static_cast<vsi_l_offset>(
187
0
                      boundaryRectangleSectionSubHeaderPhysIndex),
188
0
                  SEEK_SET) != 0)
189
0
    {
190
0
        CPLError(CE_Failure, CPLE_NotSupported,
191
0
                 "Invalid TOC file. Unable to seek to "
192
0
                 "boundaryRectangleSectionSubHeaderPhysIndex at offset %d.",
193
0
                 boundaryRectangleSectionSubHeaderPhysIndex);
194
0
        return nullptr;
195
0
    }
196
197
0
    unsigned int boundaryRectangleTableOffset;
198
0
    bool bOK = VSIFReadL(&boundaryRectangleTableOffset,
199
0
                         sizeof(boundaryRectangleTableOffset), 1, fp) == 1;
200
0
    CPL_MSBPTR32(&boundaryRectangleTableOffset);
201
202
0
    unsigned short boundaryRectangleCount;
203
0
    bOK &= VSIFReadL(&boundaryRectangleCount, sizeof(boundaryRectangleCount), 1,
204
0
                     fp) == 1;
205
0
    CPL_MSBPTR16(&boundaryRectangleCount);
206
207
0
    if (!bOK ||
208
0
        VSIFSeekL(fp,
209
0
                  static_cast<vsi_l_offset>(boundaryRectangleTablePhysIndex),
210
0
                  SEEK_SET) != 0)
211
0
    {
212
0
        CPLError(CE_Failure, CPLE_NotSupported,
213
0
                 "Invalid TOC file. Unable to seek to "
214
0
                 "boundaryRectangleTablePhysIndex at offset %d.",
215
0
                 boundaryRectangleTablePhysIndex);
216
0
        return nullptr;
217
0
    }
218
219
0
    RPFToc *toc = static_cast<RPFToc *>(CPLMalloc(sizeof(RPFToc)));
220
0
    toc->nEntries = boundaryRectangleCount;
221
0
    toc->entries = reinterpret_cast<RPFTocEntry *>(
222
0
        CPLMalloc(boundaryRectangleCount * sizeof(RPFTocEntry)));
223
0
    memset(toc->entries, 0, boundaryRectangleCount * sizeof(RPFTocEntry));
224
225
0
    for (int i = 0; i < toc->nEntries; i++)
226
0
    {
227
0
        toc->entries[i].isOverviewOrLegend = 0;
228
229
0
        bOK &= VSIFReadL(toc->entries[i].type, 1, 5, fp) == 5;
230
0
        toc->entries[i].type[5] = 0;
231
0
        RPFTOCTrim(toc->entries[i].type);
232
233
0
        bOK &= VSIFReadL(toc->entries[i].compression, 1, 5, fp) == 5;
234
0
        toc->entries[i].compression[5] = 0;
235
0
        RPFTOCTrim(toc->entries[i].compression);
236
237
0
        bOK &= VSIFReadL(toc->entries[i].scale, 1, 12, fp) == 12;
238
0
        toc->entries[i].scale[12] = 0;
239
0
        RPFTOCTrim(toc->entries[i].scale);
240
0
        if (toc->entries[i].scale[0] == '1' && toc->entries[i].scale[1] == ':')
241
0
        {
242
0
            memmove(toc->entries[i].scale, toc->entries[i].scale + 2,
243
0
                    strlen(toc->entries[i].scale + 2) + 1);
244
0
        }
245
246
0
        bOK &= VSIFReadL(toc->entries[i].zone, 1, 1, fp) == 1;
247
0
        toc->entries[i].zone[1] = 0;
248
0
        RPFTOCTrim(toc->entries[i].zone);
249
250
0
        bOK &= VSIFReadL(toc->entries[i].producer, 1, 5, fp) == 5;
251
0
        toc->entries[i].producer[5] = 0;
252
0
        RPFTOCTrim(toc->entries[i].producer);
253
254
0
        bOK &= VSIFReadL(&toc->entries[i].nwLat, sizeof(double), 1, fp) == 1;
255
0
        CPL_MSBPTR64(&toc->entries[i].nwLat);
256
257
0
        bOK &= VSIFReadL(&toc->entries[i].nwLong, sizeof(double), 1, fp) == 1;
258
0
        CPL_MSBPTR64(&toc->entries[i].nwLong);
259
260
0
        bOK &= VSIFReadL(&toc->entries[i].swLat, sizeof(double), 1, fp) == 1;
261
0
        CPL_MSBPTR64(&toc->entries[i].swLat);
262
263
0
        bOK &= VSIFReadL(&toc->entries[i].swLong, sizeof(double), 1, fp) == 1;
264
0
        CPL_MSBPTR64(&toc->entries[i].swLong);
265
266
0
        bOK &= VSIFReadL(&toc->entries[i].neLat, sizeof(double), 1, fp) == 1;
267
0
        CPL_MSBPTR64(&toc->entries[i].neLat);
268
269
0
        bOK &= VSIFReadL(&toc->entries[i].neLong, sizeof(double), 1, fp) == 1;
270
0
        CPL_MSBPTR64(&toc->entries[i].neLong);
271
272
0
        bOK &= VSIFReadL(&toc->entries[i].seLat, sizeof(double), 1, fp) == 1;
273
0
        CPL_MSBPTR64(&toc->entries[i].seLat);
274
275
0
        bOK &= VSIFReadL(&toc->entries[i].seLong, sizeof(double), 1, fp) == 1;
276
0
        CPL_MSBPTR64(&toc->entries[i].seLong);
277
278
0
        bOK &= VSIFReadL(&toc->entries[i].vertResolution, sizeof(double), 1,
279
0
                         fp) == 1;
280
0
        CPL_MSBPTR64(&toc->entries[i].vertResolution);
281
282
0
        bOK &= VSIFReadL(&toc->entries[i].horizResolution, sizeof(double), 1,
283
0
                         fp) == 1;
284
0
        CPL_MSBPTR64(&toc->entries[i].horizResolution);
285
286
0
        bOK &= VSIFReadL(&toc->entries[i].vertInterval, sizeof(double), 1,
287
0
                         fp) == 1;
288
0
        CPL_MSBPTR64(&toc->entries[i].vertInterval);
289
290
0
        bOK &= VSIFReadL(&toc->entries[i].horizInterval, sizeof(double), 1,
291
0
                         fp) == 1;
292
0
        CPL_MSBPTR64(&toc->entries[i].horizInterval);
293
294
0
        bOK &= VSIFReadL(&toc->entries[i].nVertFrames, sizeof(int), 1, fp) == 1;
295
0
        CPL_MSBPTR32(&toc->entries[i].nVertFrames);
296
297
0
        bOK &=
298
0
            VSIFReadL(&toc->entries[i].nHorizFrames, sizeof(int), 1, fp) == 1;
299
0
        CPL_MSBPTR32(&toc->entries[i].nHorizFrames);
300
301
0
        if (!bOK)
302
0
        {
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
0
        }
309
310
        // do some basic plausibility checks for all entries
311
0
        if (toc->entries[i].vertInterval <= 1e-10 ||
312
0
            !std::isfinite(toc->entries[i].vertInterval) ||
313
0
            toc->entries[i].horizInterval <= 1e-10 ||
314
0
            !std::isfinite(toc->entries[i].horizInterval) ||
315
0
            toc->entries[i].nHorizFrames == 0 ||
316
0
            toc->entries[i].nVertFrames == 0 ||
317
0
            toc->entries[i].nHorizFrames >
318
0
                INT_MAX / toc->entries[i].nVertFrames)
319
0
        {
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
0
        }
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
0
        toc->entries[i].isOverviewOrLegend =
332
0
            (toc->entries[i].zone[0] == 'R' ||   // Overview
333
0
             (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
0
                     sizeof(toc->entries[i].nwLat)) == 0));
338
339
0
        bool isPolarZone = (toc->entries[i].zone[0] == '9') ||
340
0
                           (toc->entries[i].zone[0] == 'J');
341
342
        // make additional checks of the bounding for charts (without Legends
343
        // and Overviews)
344
0
        if (!toc->entries[i].isOverviewOrLegend)
345
0
        {
346
0
            if (!(fabs(toc->entries[i].seLong) <= 360.0) ||
347
0
                !(fabs(toc->entries[i].nwLong) <= 360.0) ||
348
0
                !(fabs(toc->entries[i].nwLat) <= 90.0) ||
349
0
                !(fabs(toc->entries[i].seLat) <= 90.0) ||
350
                // check only for non-polar zones, because the values are not
351
                // always correct here
352
0
                (!isPolarZone &&
353
0
                 (toc->entries[i].seLong < toc->entries[i].nwLong ||
354
0
                  toc->entries[i].nwLat < toc->entries[i].seLat)))
355
0
            {
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
0
            }
362
0
        }
363
364
        // TODO: We could probably use another data structure, like a list,
365
        // instead of an array referenced by the frame coordinate...
366
0
        if (static_cast<int>(toc->entries[i].nHorizFrames *
367
0
                             toc->entries[i].nVertFrames) >
368
0
            atoi(CPLGetConfigOption("RPFTOC_MAX_FRAME_COUNT", "1000000")))
369
0
        {
370
0
            CPLError(
371
0
                CE_Failure, CPLE_AppDefined,
372
0
                "nHorizFrames=%d x nVertFrames=%d > %d. Please raise "
373
0
                "the value of the RPFTOC_MAX_FRAME_COUNT configuration "
374
0
                "option to more than %d if this dataset is legitimate.",
375
0
                toc->entries[i].nHorizFrames, toc->entries[i].nVertFrames,
376
0
                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
0
        }
380
0
        else
381
0
        {
382
0
            toc->entries[i].frameEntries =
383
0
                static_cast<RPFTocFrameEntry *>(VSI_CALLOC_VERBOSE(
384
0
                    static_cast<size_t>(toc->entries[i].nVertFrames) *
385
0
                        toc->entries[i].nHorizFrames,
386
0
                    sizeof(RPFTocFrameEntry)));
387
0
        }
388
0
        if (toc->entries[i].frameEntries == nullptr)
389
0
        {
390
0
            toc->entries[i].nVertFrames = 0;
391
0
            toc->entries[i].nHorizFrames = 0;
392
0
            RPFTOCFree(toc);
393
0
            return nullptr;
394
0
        }
395
396
0
        CPLDebug("RPFTOC",
397
0
                 "[%d] type=%s, compression=%s, scale=%s, zone=%s, "
398
0
                 "producer=%s, nVertFrames=%d, nHorizFrames=%d",
399
0
                 i, toc->entries[i].type, toc->entries[i].compression,
400
0
                 toc->entries[i].scale, toc->entries[i].zone,
401
0
                 toc->entries[i].producer, toc->entries[i].nVertFrames,
402
0
                 toc->entries[i].nHorizFrames);
403
0
    }
404
405
0
    if (VSIFSeekL(
406
0
            fp,
407
0
            static_cast<vsi_l_offset>(frameFileIndexSectionSubHeaderPhysIndex),
408
0
            SEEK_SET) != 0)
409
0
    {
410
0
        CPLError(CE_Failure, CPLE_NotSupported,
411
0
                 "Invalid TOC file. Unable to seek to "
412
0
                 "frameFileIndexSectionSubHeaderPhysIndex at offset %d.",
413
0
                 frameFileIndexSectionSubHeaderPhysIndex);
414
0
        RPFTOCFree(toc);
415
0
        return nullptr;
416
0
    }
417
418
    /* Skip 1 byte security classification */
419
0
    bOK &= VSIFSeekL(fp, 1, SEEK_CUR) == 0;
420
421
0
    unsigned int frameIndexTableOffset;
422
0
    bOK &= VSIFReadL(&frameIndexTableOffset, sizeof(frameIndexTableOffset), 1,
423
0
                     fp) == 1;
424
0
    CPL_MSBPTR32(&frameIndexTableOffset);
425
426
0
    unsigned int nFrameFileIndexRecords;
427
0
    bOK &= VSIFReadL(&nFrameFileIndexRecords, sizeof(nFrameFileIndexRecords), 1,
428
0
                     fp) == 1;
429
0
    CPL_MSBPTR32(&nFrameFileIndexRecords);
430
431
0
    unsigned short nFrameFilePathnameRecords;
432
0
    bOK &= VSIFReadL(&nFrameFilePathnameRecords,
433
0
                     sizeof(nFrameFilePathnameRecords), 1, fp) == 1;
434
0
    CPL_MSBPTR16(&nFrameFilePathnameRecords);
435
436
0
    unsigned short frameFileIndexRecordLength;
437
0
    bOK &= VSIFReadL(&frameFileIndexRecordLength,
438
0
                     sizeof(frameFileIndexRecordLength), 1, fp) == 1;
439
0
    CPL_MSBPTR16(&frameFileIndexRecordLength);
440
0
    if (frameFileIndexRecordLength < 3 * sizeof(short))
441
0
    {
442
0
        CPLError(CE_Failure, CPLE_FileIO, "Invalid file");
443
0
        RPFTOCFree(toc);
444
0
        return nullptr;
445
0
    }
446
447
0
    if (!bOK)
448
0
    {
449
0
        CPLError(CE_Failure, CPLE_FileIO, "I/O error");
450
0
        RPFTOCFree(toc);
451
0
        return nullptr;
452
0
    }
453
454
0
    int newBoundaryId = 0;
455
456
0
    for (int i = 0; i < static_cast<int>(nFrameFileIndexRecords); i++)
457
0
    {
458
0
        vsi_l_offset nFrameOffset =
459
0
            static_cast<vsi_l_offset>(frameFileIndexSubsectionPhysIndex) +
460
0
            static_cast<vsi_l_offset>(frameFileIndexRecordLength) * i;
461
0
        if (VSIFSeekL(fp, nFrameOffset, SEEK_SET) != 0)
462
0
        {
463
0
            CPLError(
464
0
                CE_Failure, CPLE_NotSupported,
465
0
                "Invalid TOC file. Unable to seek to "
466
0
                "frameFileIndexSubsectionPhysIndex(%d) at offset " CPL_FRMT_GUIB
467
0
                ".",
468
0
                i, static_cast<GUIntBig>(nFrameOffset));
469
0
            RPFTOCFree(toc);
470
0
            return nullptr;
471
0
        }
472
473
0
        unsigned short boundaryId;
474
0
        if (VSIFReadL(&boundaryId, sizeof(boundaryId), 1, fp) != 1)
475
0
        {
476
0
            CPLError(CE_Failure, CPLE_FileIO, "I/O error");
477
0
            RPFTOCFree(toc);
478
0
            return nullptr;
479
0
        }
480
0
        CPL_MSBPTR16(&boundaryId);
481
482
0
        if (i == 0 && boundaryId == 0)
483
0
            newBoundaryId = 1;
484
0
        if (newBoundaryId == 0)
485
0
            boundaryId--;
486
487
0
        if (boundaryId >= toc->nEntries)
488
0
        {
489
0
            CPLError(CE_Failure, CPLE_NotSupported,
490
0
                     "Invalid TOC file. Bad boundary id (%d) for frame file "
491
0
                     "index %d.",
492
0
                     boundaryId, i);
493
0
            RPFTOCFree(toc);
494
0
            return nullptr;
495
0
        }
496
497
0
        RPFTocEntry *entry = &toc->entries[boundaryId];
498
0
        entry->boundaryId = boundaryId;
499
500
0
        unsigned short frameRow;
501
0
        bOK &= VSIFReadL(&frameRow, sizeof(frameRow), 1, fp) == 1;
502
0
        CPL_MSBPTR16(&frameRow);
503
504
0
        unsigned short frameCol;
505
0
        bOK &= VSIFReadL(&frameCol, sizeof(frameCol), 1, fp) == 1;
506
0
        CPL_MSBPTR16(&frameCol);
507
0
        if (!bOK)
508
0
        {
509
0
            CPLError(CE_Failure, CPLE_FileIO, "I/O error");
510
0
            RPFTOCFree(toc);
511
0
            return nullptr;
512
0
        }
513
514
0
        if (newBoundaryId == 0)
515
0
        {
516
0
            frameRow--;
517
0
            frameCol--;
518
0
        }
519
0
        else
520
0
        {
521
            /* Trick so that frames are numbered north to south */
522
0
            if (entry->nVertFrames - 1 < frameRow)
523
0
            {
524
0
                CPLError(CE_Failure, CPLE_FileIO,
525
0
                         "Invalid nVertFrames vs frameRow");
526
0
                RPFTOCFree(toc);
527
0
                return nullptr;
528
0
            }
529
0
            frameRow = static_cast<unsigned short>((entry->nVertFrames - 1) -
530
0
                                                   frameRow);
531
0
        }
532
533
0
        if (frameRow >= entry->nVertFrames)
534
0
        {
535
0
            CPLError(
536
0
                CE_Failure, CPLE_NotSupported,
537
0
                "Invalid TOC file. Bad row num (%d) for frame file index %d.",
538
0
                frameRow, i);
539
0
            RPFTOCFree(toc);
540
0
            return nullptr;
541
0
        }
542
543
0
        if (frameCol >= entry->nHorizFrames)
544
0
        {
545
0
            CPLError(
546
0
                CE_Failure, CPLE_NotSupported,
547
0
                "Invalid TOC file. Bad col num (%d) for frame file index %d.",
548
0
                frameCol, i);
549
0
            RPFTOCFree(toc);
550
0
            return nullptr;
551
0
        }
552
553
0
        RPFTocFrameEntry *frameEntry =
554
0
            &entry->frameEntries[frameRow * entry->nHorizFrames + frameCol];
555
0
        frameEntry->frameRow = frameRow;
556
0
        frameEntry->frameCol = frameCol;
557
558
0
        if (frameEntry->exists)
559
0
        {
560
0
            CPLError(
561
0
                CE_Warning, CPLE_AppDefined,
562
0
                "Frame entry(%d,%d) for frame file index %d was already found.",
563
0
                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
0
        }
570
571
0
        unsigned int offsetFrameFilePathName;
572
0
        bOK &= VSIFReadL(&offsetFrameFilePathName,
573
0
                         sizeof(offsetFrameFilePathName), 1, fp) == 1;
574
0
        CPL_MSBPTR32(&offsetFrameFilePathName);
575
576
0
        bOK &= VSIFReadL(frameEntry->filename, 1, 12, fp) == 12;
577
0
        if (!bOK)
578
0
        {
579
0
            CPLError(CE_Failure, CPLE_FileIO, "I/O error");
580
0
            RPFTOCFree(toc);
581
0
            return nullptr;
582
0
        }
583
0
        frameEntry->filename[12] = '\0';
584
0
        bOK &= strlen(frameEntry->filename) > 0;
585
586
0
        if (CPLHasPathTraversal(frameEntry->filename))
587
0
        {
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
0
        }
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
0
        const std::string fileExt = CPLGetExtensionSafe(frameEntry->filename);
599
0
        if (EQUALN(fileExt.c_str(), "ovr", 3) ||
600
0
            EQUALN(fileExt.c_str(), "lgd", 3))
601
0
        {
602
0
            entry->isOverviewOrLegend = TRUE;
603
0
        }
604
605
        /* Extract series code */
606
0
        if (entry->seriesAbbreviation == nullptr)
607
0
        {
608
0
            const NITFSeries *series = NITFGetSeriesInfo(frameEntry->filename);
609
0
            if (series)
610
0
            {
611
0
                entry->seriesAbbreviation = series->abbreviation;
612
0
                entry->seriesName = series->name;
613
0
            }
614
0
        }
615
616
        /* Get file geo reference */
617
0
        bOK &= VSIFReadL(frameEntry->georef, 1, 6, fp) == 6;
618
0
        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
0
        if (!bOK || VSIFSeekL(fp,
625
0
                              static_cast<vsi_l_offset>(
626
0
                                  frameFileIndexSubsectionPhysIndex) +
627
0
                                  offsetFrameFilePathName,
628
0
                              SEEK_SET) != 0)
629
0
        {
630
0
            CPLError(CE_Failure, CPLE_NotSupported,
631
0
                     "Invalid TOC file. Unable to seek to "
632
0
                     "frameFileIndexSubsectionPhysIndex + "
633
0
                     "offsetFrameFilePathName(%d) at offset " CPL_FRMT_GUIB ".",
634
0
                     i,
635
0
                     static_cast<GUIntBig>(frameFileIndexSubsectionPhysIndex) +
636
0
                         offsetFrameFilePathName);
637
0
            RPFTOCFree(toc);
638
0
            return nullptr;
639
0
        }
640
641
0
        unsigned short pathLength;
642
0
        bOK &= VSIFReadL(&pathLength, sizeof(pathLength), 1, fp) == 1;
643
0
        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
0
        if (!bOK || pathLength == 0 || pathLength > 256)
648
0
        {
649
0
            CPLError(
650
0
                CE_Failure, CPLE_NotSupported,
651
0
                "Path length is invalid : %d. Probably corrupted TOC file.",
652
0
                static_cast<int>(pathLength));
653
0
            RPFTOCFree(toc);
654
0
            return nullptr;
655
0
        }
656
657
0
        frameEntry->directory = static_cast<char *>(CPLMalloc(pathLength + 1));
658
0
        bOK &=
659
0
            VSIFReadL(frameEntry->directory, 1, pathLength, fp) == pathLength;
660
0
        if (!bOK)
661
0
        {
662
0
            CPLError(CE_Failure, CPLE_FileIO, "I/O error");
663
0
            RPFTOCFree(toc);
664
0
            return nullptr;
665
0
        }
666
0
        frameEntry->directory[pathLength] = 0;
667
0
        if (frameEntry->directory[pathLength - 1] == '/')
668
0
            frameEntry->directory[pathLength - 1] = 0;
669
670
0
        if (CPLHasPathTraversal(frameEntry->directory))
671
0
        {
672
0
            CPLError(CE_Failure, CPLE_AppDefined,
673
0
                     "Path traversal detected in %s", frameEntry->directory);
674
0
            RPFTOCFree(toc);
675
0
            return nullptr;
676
0
        }
677
678
0
        if (frameEntry->directory[0] == '.' && frameEntry->directory[1] == '/')
679
0
        {
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
0
            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
0
                        .c_str(),
691
0
                    &sStatBuf) == 0 &&
692
0
                VSI_ISDIR(sStatBuf.st_mode))
693
0
            {
694
0
                memmove(frameEntry->directory, frameEntry->directory + 1,
695
0
                        strlen(frameEntry->directory + 1) + 1);
696
0
            }
697
0
        }
698
699
0
        {
700
0
            char *baseDir = CPLStrdup(CPLGetDirnameSafe(pszFilename).c_str());
701
0
            VSIStatBufL sStatBuf;
702
0
            char *subdir = nullptr;
703
0
            if (CPLIsFilenameRelative(frameEntry->directory) == FALSE)
704
0
                subdir = CPLStrdup(frameEntry->directory);
705
0
            else if (frameEntry->directory[0] == '.' &&
706
0
                     frameEntry->directory[1] == 0)
707
0
                subdir = CPLStrdup(baseDir);
708
0
            else
709
0
                subdir = CPLStrdup(
710
0
                    CPLFormFilenameSafe(baseDir, frameEntry->directory, nullptr)
711
0
                        .c_str());
712
0
#if !defined(_WIN32) && !defined(_WIN32_CE)
713
0
            if (VSIStatL(subdir, &sStatBuf) != 0 &&
714
0
                strlen(subdir) > strlen(baseDir))
715
0
            {
716
0
                char *c = subdir + strlen(baseDir) + 1;
717
0
                while (*c)
718
0
                {
719
0
                    if (*c >= 'A' && *c <= 'Z')
720
0
                        *c += 'a' - 'A';
721
0
                    c++;
722
0
                }
723
0
            }
724
0
#endif
725
0
            frameEntry->fullFilePath = CPLStrdup(
726
0
                CPLFormFilenameSafe(subdir, frameEntry->filename, nullptr)
727
0
                    .c_str());
728
0
            if (VSIStatL(frameEntry->fullFilePath, &sStatBuf) != 0)
729
0
            {
730
0
#if !defined(_WIN32) && !defined(_WIN32_CE)
731
0
                if (strlen(frameEntry->fullFilePath) > strlen(subdir))
732
0
                {
733
0
                    char *c = frameEntry->fullFilePath + strlen(subdir) + 1;
734
0
                    while (*c)
735
0
                    {
736
0
                        if (*c >= 'A' && *c <= 'Z')
737
0
                            *c += 'a' - 'A';
738
0
                        c++;
739
0
                    }
740
0
                }
741
0
                if (VSIStatL(frameEntry->fullFilePath, &sStatBuf) != 0)
742
0
#endif
743
0
                {
744
0
                    frameEntry->fileExists = 0;
745
0
                    CPLError(CE_Warning, CPLE_AppDefined,
746
0
                             "File %s does not exist.",
747
0
                             frameEntry->fullFilePath);
748
0
                }
749
0
#if !defined(_WIN32) && !defined(_WIN32_CE)
750
0
                else
751
0
                {
752
0
                    frameEntry->fileExists = 1;
753
0
                }
754
0
#endif
755
0
            }
756
0
            else
757
0
            {
758
0
                frameEntry->fileExists = 1;
759
0
            }
760
0
            CPLFree(subdir);
761
0
            CPLFree(baseDir);
762
0
        }
763
764
0
        CPLDebug("RPFTOC", "Entry %d : %s,%s (%d, %d)", boundaryId,
765
0
                 frameEntry->directory, frameEntry->filename, frameRow,
766
0
                 frameCol);
767
768
0
        frameEntry->exists = 1;
769
0
    }
770
771
0
    return toc;
772
0
}
773
774
/************************************************************************/
775
/*                             RPFTOCFree()                             */
776
/************************************************************************/
777
778
void RPFTOCFree(RPFToc *toc)
779
0
{
780
0
    if (!toc)
781
0
        return;
782
783
0
    for (int i = 0; i < toc->nEntries; i++)
784
0
    {
785
0
        for (int j = 0; j < static_cast<int>(toc->entries[i].nVertFrames *
786
0
                                             toc->entries[i].nHorizFrames);
787
0
             j++)
788
0
        {
789
0
            CPLFree(toc->entries[i].frameEntries[j].fullFilePath);
790
0
            CPLFree(toc->entries[i].frameEntries[j].directory);
791
0
        }
792
0
        CPLFree(toc->entries[i].frameEntries);
793
0
    }
794
795
0
    CPLFree(toc->entries);
796
0
    CPLFree(toc);
797
0
}