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