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