Coverage Report

Created: 2025-12-03 08:24

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