1 /******************************************************************************
2  * $Id: nitfdes.c b33096582db6db97e24011418d0304d736c102fa 2020-11-06 16:52:08 -0500 matthew-baran $
3  *
4  * Project:  NITF Read/Write Library
5  * Purpose:  Module responsible for implementation of DE segments.
6  * Author:   Even Rouault, <even dot rouault at spatialys.com>
7  *
8  **********************************************************************
9  * Copyright (c) 2010-2011, Even Rouault <even dot rouault at spatialys.com>
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included
19  * in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  ****************************************************************************/
29 
30 #include "gdal.h"
31 #include "nitflib.h"
32 #include "cpl_vsi.h"
33 #include "cpl_conv.h"
34 #include "cpl_string.h"
35 
36 CPL_CVSID("$Id: nitfdes.c b33096582db6db97e24011418d0304d736c102fa 2020-11-06 16:52:08 -0500 matthew-baran $")
37 
38 CPL_INLINE static void CPL_IGNORE_RET_VAL_INT(CPL_UNUSED int unused) {}
39 
40 /************************************************************************/
41 /*                          NITFDESAccess()                             */
42 /************************************************************************/
43 
44 NITFDES *NITFDESAccess( NITFFile *psFile, int iSegment )
45 
46 {
47     NITFDES   *psDES;
48     char      *pachHeader;
49     NITFSegmentInfo *psSegInfo;
50     char       szDESID[26];
51     int        nOffset;
52     int        bHasDESOFLW;
53     int        nDESSHL;
54 
55 /* -------------------------------------------------------------------- */
56 /*      Verify segment, and return existing DES accessor if there       */
57 /*      is one.                                                         */
58 /* -------------------------------------------------------------------- */
59     if( iSegment < 0 || iSegment >= psFile->nSegmentCount )
60         return NULL;
61 
62     psSegInfo = psFile->pasSegmentInfo + iSegment;
63 
64     if( !EQUAL(psSegInfo->szSegmentType,"DE") )
65         return NULL;
66 
67     if( psSegInfo->hAccess != NULL )
68         return (NITFDES *) psSegInfo->hAccess;
69 
70 /* -------------------------------------------------------------------- */
71 /*      Read the DES subheader.                                         */
72 /* -------------------------------------------------------------------- */
73     if (psSegInfo->nSegmentHeaderSize < 200)
74     {
75         CPLError(CE_Failure, CPLE_AppDefined,
76                     "DES header too small");
77         return NULL;
78     }
79 
80     pachHeader = (char*) VSI_MALLOC_VERBOSE(psSegInfo->nSegmentHeaderSize);
81     if (pachHeader == NULL)
82     {
83         return NULL;
84     }
85 
86 retry:
87     if( VSIFSeekL( psFile->fp, psSegInfo->nSegmentHeaderStart,
88                   SEEK_SET ) != 0
89         || VSIFReadL( pachHeader, 1, psSegInfo->nSegmentHeaderSize,
90                      psFile->fp ) != psSegInfo->nSegmentHeaderSize )
91     {
92         CPLError( CE_Failure, CPLE_FileIO,
93                   "Failed to read %u byte DES subheader from " CPL_FRMT_GUIB ".",
94                   psSegInfo->nSegmentHeaderSize,
95                   psSegInfo->nSegmentHeaderStart );
96         CPLFree(pachHeader);
97         return NULL;
98     }
99 
100     if (!STARTS_WITH_CI(pachHeader, "DE"))
101     {
102         if (STARTS_WITH_CI(pachHeader + 4, "DERegistered"))
103         {
104             /* BAO_46_Ed1/rpf/conc/concz10/000fz010.ona and cie are buggy */
105             CPLDebug("NITF", "Patching nSegmentHeaderStart and nSegmentStart for DE segment %d", iSegment);
106             psSegInfo->nSegmentHeaderStart += 4;
107             psSegInfo->nSegmentStart += 4;
108             goto retry;
109         }
110 
111         CPLError(CE_Failure, CPLE_AppDefined,
112                  "Invalid segment prefix for DE segment %d", iSegment);
113 
114         CPLFree(pachHeader);
115         return NULL;
116     }
117 
118 /* -------------------------------------------------------------------- */
119 /*      Initialize DES object.                                          */
120 /* -------------------------------------------------------------------- */
121     psDES = (NITFDES *) CPLCalloc(sizeof(NITFDES),1);
122 
123     psDES->psFile = psFile;
ExposeUnderlyingJPEGDatasetOverviews()124     psDES->iSegment = iSegment;
125     psDES->pachHeader = pachHeader;
126 
127     psSegInfo->hAccess = psDES;
128 
129 /* -------------------------------------------------------------------- */
130 /*      Collect a variety of information as metadata.                   */
131 /* -------------------------------------------------------------------- */
132 #define GetMD( length, name )              \
133     do { NITFExtractMetadata( &(psDES->papszMetadata), pachHeader,    \
134                          nOffset, length,                        \
135                          "NITF_" #name ); \
136     nOffset += length; } while(0)
137 
138     nOffset = 2;
139     GetMD( 25, DESID  );
140     GetMD(  2, DESVER );
141     GetMD(  1, DECLAS );
142     GetMD(  2, DESCLSY );
143     GetMD( 11, DESCODE );
144     GetMD(  2, DESCTLH );
145     GetMD( 20, DESREL  );
146     GetMD(  2, DESDCTP );
GetSpatialRef()147     GetMD(  8, DESDCDT );
148     GetMD(  4, DESDCXM );
149     GetMD(  1, DESDG   );
150     GetMD(  8, DESDGDT );
SetSpatialRef(const OGRSpatialReference * poSRS)151     GetMD( 43, DESCLTX );
152     GetMD(  1, DESCATP );
153     GetMD( 40, DESCAUT );
154     GetMD(  1, DESCRSN );
155     GetMD(  8, DESSRDT );
156     GetMD( 15, DESCTLN );
157 
158     /* Load DESID */
159     NITFGetField( szDESID, pachHeader, 2, 25);
SetGCPs(int nGCPCountIn,const GDAL_GCP * pasGCPListIn,const OGRSpatialReference * poSRS)160 
161     /* For NITF < 02.10, we cannot rely on DESID=TRE_OVERFLOW to detect */
162     /* if DESOFLW and DESITEM are present. So if the next 4 bytes are non */
163     /* numeric, we'll assume that DESOFLW is there */
164     bHasDESOFLW = STARTS_WITH_CI(szDESID, "TRE_OVERFLOW") ||
165        (!((pachHeader[nOffset+0] >= '0' && pachHeader[nOffset+0] <= '9') &&
166           (pachHeader[nOffset+1] >= '0' && pachHeader[nOffset+1] <= '9') &&
167           (pachHeader[nOffset+2] >= '0' && pachHeader[nOffset+2] <= '9') &&
168           (pachHeader[nOffset+3] >= '0' && pachHeader[nOffset+3] <= '9')));
169 
170     if (bHasDESOFLW)
171     {
172         if ((int)psSegInfo->nSegmentHeaderSize < nOffset + 6 + 3 )
173         {
174             CPLError(CE_Failure, CPLE_AppDefined,
175                         "DES header too small");
176             NITFDESDeaccess(psDES);
177             return NULL;
178         }
179         GetMD(  6, DESOFLW );
180         GetMD(  3, DESITEM );
181     }
182 
183     if ((int)psSegInfo->nSegmentHeaderSize < nOffset + 4 )
184     {
185         CPLError(CE_Failure, CPLE_AppDefined,
186                     "DES header too small");
187         NITFDESDeaccess(psDES);
188         return NULL;
189     }
190 
191     GetMD( 4, DESSHL );
192     nDESSHL = atoi(CSLFetchNameValue( psDES->papszMetadata, "NITF_DESSHL" ) );
193 
194     if (nDESSHL < 0)
195     {
196         CPLError(CE_Failure, CPLE_AppDefined,
197                     "Invalid value for DESSHL");
198         NITFDESDeaccess(psDES);
199         return NULL;
200     }
201     if ( (int)psSegInfo->nSegmentHeaderSize < nOffset + nDESSHL)
202     {
203         CPLError(CE_Failure, CPLE_AppDefined,
204                     "DES header too small");
205         NITFDESDeaccess(psDES);
206         return NULL;
207     }
208 
209     if (STARTS_WITH_CI(szDESID, "CSSHPA DES"))
210     {
211         if ( nDESSHL != 62 && nDESSHL != 80)
212         {
213             CPLError(CE_Failure, CPLE_AppDefined,
214                      "Invalid DESSHL for CSSHPA DES");
215             NITFDESDeaccess(psDES);
216             return NULL;
217         }
218 
219         GetMD( 25, SHAPE_USE );
220         GetMD( 10, SHAPE_CLASS );
221         if (nDESSHL == 80)
222             GetMD( 18, CC_SOURCE );
223         GetMD(  3, SHAPE1_NAME );
224         GetMD(  6, SHAPE1_START );
225         GetMD(  3, SHAPE2_NAME );
226         GetMD(  6, SHAPE2_START );
227         GetMD(  3, SHAPE3_NAME );
228         GetMD(  6, SHAPE3_START );
229     }
230     else if (STARTS_WITH_CI(szDESID, "XML_DATA_CONTENT"))
231     {
232         /* TODO : handle nDESSHL = 0005 and 0283 */
233         if (nDESSHL >= 5)
234         {
235             GetMD( 5, DESCRC );
236             if (nDESSHL >= 283)
237             {
238                 GetMD( 8, DESSHFT );
239                 GetMD( 20, DESSHDT );
240                 GetMD( 40, DESSHRP );
241                 GetMD( 60, DESSHSI );
242                 GetMD( 10, DESSHSV );
243                 GetMD( 20, DESSHSD );
244                 GetMD( 120, DESSHTN );
245                 if (nDESSHL >= 773)
246                 {
247                     GetMD( 125, DESSHLPG );
248                     GetMD( 25, DESSHLPT );
249                     GetMD( 20, DESSHLI );
250                     GetMD( 120, DESSHLIN );
251                     GetMD( 200, DESSHABS );
252                 }
253             }
254         }
255     }
256     else if (STARTS_WITH_CI(szDESID, "CSATTA DES") && nDESSHL == 52)
257     {
258         GetMD( 12, ATT_TYPE );
259         GetMD( 14, DT_ATT );
260         GetMD( 8, DATE_ATT );
261         GetMD( 13, T0_ATT );
262         GetMD( 5, NUM_ATT );
263     }
264     else if (nDESSHL > 0)
265         GetMD(  nDESSHL, DESSHF );
266 
267     if ((int)psSegInfo->nSegmentHeaderSize > nOffset)
268     {
269         char* pszEscapedDESDATA =
270                 CPLEscapeString( pachHeader + nOffset,
271                                  (int)psSegInfo->nSegmentHeaderSize - nOffset,
272                                  CPLES_BackslashQuotable );
273         psDES->papszMetadata = CSLSetNameValue( psDES->papszMetadata,
274                                                 "NITF_DESDATA",
275                                                 pszEscapedDESDATA );
276         CPLFree(pszEscapedDESDATA);
277     }
278     else
279     {
280 
281 #define TEN_MEGABYTES 10485760
282 
283         if (psSegInfo->nSegmentSize > TEN_MEGABYTES)
284         {
285             const char* pszOffset = CPLSPrintf(CPL_FRMT_GUIB, psFile->pasSegmentInfo[iSegment].nSegmentStart);
286             const char* pszSize = CPLSPrintf(CPL_FRMT_GUIB, psFile->pasSegmentInfo[iSegment].nSegmentSize);
287 
288             psDES->papszMetadata = CSLSetNameValue( psDES->papszMetadata,
289                                                     "NITF_DESDATA_OFFSET",
290                                                     pszOffset );
291             psDES->papszMetadata = CSLSetNameValue( psDES->papszMetadata,
292                                                     "NITF_DESDATA_LENGTH",
293                                                     pszSize);
294         }
295         else
296         {
297             char* pachData = (char*)VSI_MALLOC_VERBOSE((size_t)psSegInfo->nSegmentSize);
298             if (pachData == NULL )
299             {
300                 /* nothing */
301             }
302             else if( VSIFSeekL( psFile->fp, psSegInfo->nSegmentStart,
303                         SEEK_SET ) != 0
304                 || VSIFReadL( pachData, 1, (size_t)psSegInfo->nSegmentSize,
305                             psFile->fp ) != psSegInfo->nSegmentSize )
306             {
307                 CPLDebug("NITF",
308                         "Failed to read " CPL_FRMT_GUIB" bytes DES data from " CPL_FRMT_GUIB ".",
309                         psSegInfo->nSegmentSize,
310                         psSegInfo->nSegmentStart );
311             }
312             else
313             {
314                 char* pszEscapedDESDATA =
315                         CPLEscapeString( pachData,
316                                         (int)psSegInfo->nSegmentSize,
317                                         CPLES_BackslashQuotable );
318                 psDES->papszMetadata = CSLSetNameValue( psDES->papszMetadata,
319                                                         "NITF_DESDATA",
320                                                         pszEscapedDESDATA );
321                 CPLFree(pszEscapedDESDATA);
322             }
323             CPLFree(pachData);
324         }
325 
326 #ifdef notdef
327         /* Disabled because might generate a huge amount of elements */
328         if (STARTS_WITH_CI(szDESID, "CSATTA DES"))
329         {
330             int nNumAtt = atoi(CSLFetchNameValueDef(psDES->papszMetadata, "NITF_NUM_ATT", "0"));
331             if (nNumAtt * 8 * 4 == psSegInfo->nSegmentSize)
332             {
333                 int nMDSize = CSLCount(psDES->papszMetadata);
334                 char** papszMD = (char**)VSIRealloc(psDES->papszMetadata, (nMDSize + nNumAtt * 4 + 1) * sizeof(char*));
335                 if (papszMD)
336                 {
337                     int i, j;
338                     const GByte* pachDataIter = pachData;
339 
340                     psDES->papszMetadata = papszMD;
341                     for(i=0;i<nNumAtt;i++)
342                     {
343                         char szAttrNameValue[64+1+256+1];
344                         double dfVal;
345                         for(j=0;j<4;j++)
346                         {
347                             memcpy(&dfVal, pachDataIter, 8);
348                             CPL_MSBPTR64(&dfVal);
349                             pachDataIter += 8;
350                             CPLsprintf(szAttrNameValue, "NITF_ATT_Q%d_%d=%.16g", j+1, i, dfVal);
351                             papszMD[nMDSize + i * 4 + j] = CPLStrdup(szAttrNameValue);
352                         }
353                     }
354                     papszMD[nMDSize + nNumAtt * 4] = NULL;
355                 }
356             }
357         }
358 #endif
359 
360     }
361 
362     return psDES;
363 }
364 
365 /************************************************************************/
366 /*                           NITFDESDeaccess()                          */
367 /************************************************************************/
368 
369 void NITFDESDeaccess( NITFDES *psDES )
370 
371 {
372     CPLAssert( psDES->psFile->pasSegmentInfo[psDES->iSegment].hAccess
373                == psDES );
374 
375     psDES->psFile->pasSegmentInfo[psDES->iSegment].hAccess = NULL;
376 
377     CPLFree( psDES->pachHeader );
378     CSLDestroy( psDES->papszMetadata );
379 
380     CPLFree( psDES );
381 }
382 
383 /************************************************************************/
384 /*                              NITFDESGetTRE()                         */
385 /************************************************************************/
386 
387 /**
388  * Return the TRE located at nOffset.
389  *
390  * @param psDES          descriptor of the DE segment
391  * @param nOffset        offset of the TRE relative to the beginning of the segment data
392  * @param szTREName      will be filled with the TRE name
393  * @param ppabyTREData   will be allocated by the function and filled with the TRE content (in raw form)
394  * @param pnFoundTRESize will be filled with the TRE size (excluding the first 11 bytes)
395  * @return TRUE if a TRE was found
396  */
397 
398 int   NITFDESGetTRE( NITFDES* psDES,
399                      int nOffset,
400                      char szTREName[7],
401                      char** ppabyTREData,
402                      int* pnFoundTRESize)
403 {
404     char szTREHeader[12];
405     char szTRETempName[7];
406     NITFSegmentInfo* psSegInfo;
407     VSILFILE* fp;
408     int nTRESize;
409 
410     memset(szTREName, '\0', 7);
411     if (ppabyTREData)
412         *ppabyTREData = NULL;
413     if (pnFoundTRESize)
414         *pnFoundTRESize = 0;
415 
416     if (nOffset < 0)
417         return FALSE;
418 
419     if (psDES == NULL)
420         return FALSE;
421 
422     if (CSLFetchNameValue(psDES->papszMetadata, "NITF_DESOFLW") == NULL)
423         return FALSE;
424 
425     psSegInfo = psDES->psFile->pasSegmentInfo + psDES->iSegment;
426     fp = psDES->psFile->fp;
427 
428     if ((size_t)nOffset >= psSegInfo->nSegmentSize)
429         return FALSE;
430 
431     if( VSIFSeekL(fp, psSegInfo->nSegmentStart + nOffset, SEEK_SET) != 0 ||
432         VSIFReadL(szTREHeader, 1, 11, fp) != 11)
433     {
434         /* Some files have a nSegmentSize larger than what it is in reality */
435         /* So exit silently if we're at end of file */
436         if( VSIFSeekL(fp, 0, SEEK_END) != 0 ||
437             VSIFTellL(fp) == psSegInfo->nSegmentStart + nOffset)
438             return FALSE;
439 
440         CPLError(CE_Failure, CPLE_FileIO,
441                  "Cannot get 11 bytes at offset " CPL_FRMT_GUIB ".",
442                  psSegInfo->nSegmentStart + nOffset );
443         return FALSE;
444     }
445     szTREHeader[11] = '\0';
446 
447     memcpy(szTRETempName, szTREHeader, 6);
448     szTRETempName[6] = '\0';
449 
450     nTRESize = atoi(szTREHeader + 6);
451     if (nTRESize < 0)
452     {
453         CPLError(CE_Failure, CPLE_AppDefined,
454                  "Invalid size (%d) for TRE %s",
455                  nTRESize, szTRETempName);
456         return FALSE;
457     }
458     if ((size_t)(nOffset + 11 + nTRESize) > psSegInfo->nSegmentSize)
459     {
460         CPLError(CE_Failure, CPLE_AppDefined,
461                  "Cannot read %s TRE. Not enough bytes : remaining %d, expected %d",
462                  szTRETempName,
463                  (int)(psSegInfo->nSegmentSize - (nOffset + 11)), nTRESize);
464         return FALSE;
465     }
466 
467     if (ppabyTREData)
468     {
469         /* Allocate one extra byte for the NULL terminating character */
470         *ppabyTREData = (char*) VSI_MALLOC_VERBOSE(nTRESize + 1);
471         if (*ppabyTREData  == NULL)
472         {
473             return FALSE;
474         }
475         (*ppabyTREData)[nTRESize] = '\0';
476 
477         if ((int)VSIFReadL(*ppabyTREData, 1, nTRESize, fp) != nTRESize)
478         {
479             CPLError(CE_Failure, CPLE_FileIO,
480                      "Cannot get %d bytes at offset " CPL_FRMT_GUIB ".",
481                      nTRESize, VSIFTellL(fp) );
482             VSIFree(*ppabyTREData);
483             *ppabyTREData = NULL;
484             return FALSE;
485         }
486     }
487 
488     strcpy(szTREName, szTRETempName);
489     if (pnFoundTRESize)
490         *pnFoundTRESize = nTRESize;
491 
492     return TRUE;
493 }
494 
495 /************************************************************************/
496 /*                           NITFDESFreeTREData()                       */
497 /************************************************************************/
498 
499 void NITFDESFreeTREData( char* pabyTREData )
500 {
501     VSIFree(pabyTREData);
502 }
503 
504 
505 /************************************************************************/
506 /*                        NITFDESExtractShapefile()                     */
507 /************************************************************************/
508 
509 int NITFDESExtractShapefile(NITFDES* psDES, const char* pszRadixFileName)
510 {
511     NITFSegmentInfo* psSegInfo;
512     const char* apszExt[3] = { NULL };
513     int anOffset[4] = { 0 };
514     int iShpFile;
515     char* pszFilename;
516     size_t nFilenameLen;
517 
518     if ( CSLFetchNameValue(psDES->papszMetadata, "NITF_SHAPE_USE") == NULL )
519         return FALSE;
520 
521     psSegInfo = psDES->psFile->pasSegmentInfo + psDES->iSegment;
522 
523     apszExt[0] = CSLFetchNameValue(psDES->papszMetadata, "NITF_SHAPE1_NAME");
524     anOffset[0] = atoi(CSLFetchNameValue(psDES->papszMetadata, "NITF_SHAPE1_START"));
525     apszExt[1] = CSLFetchNameValue(psDES->papszMetadata, "NITF_SHAPE2_NAME");
526     anOffset[1] = atoi(CSLFetchNameValue(psDES->papszMetadata, "NITF_SHAPE2_START"));
527     apszExt[2] = CSLFetchNameValue(psDES->papszMetadata, "NITF_SHAPE3_NAME");
528     anOffset[2] = atoi(CSLFetchNameValue(psDES->papszMetadata, "NITF_SHAPE3_START"));
529     anOffset[3] = (int) psSegInfo->nSegmentSize;
530 
531     for(iShpFile = 0; iShpFile < 3; iShpFile ++)
532     {
533         if (!EQUAL(apszExt[iShpFile], "SHP") &&
534             !EQUAL(apszExt[iShpFile], "SHX") &&
535             !EQUAL(apszExt[iShpFile], "DBF"))
536             return FALSE;
537 
538         if (anOffset[iShpFile] < 0 ||
539             anOffset[iShpFile] >= anOffset[iShpFile+1])
540             return FALSE;
541     }
542 
543     nFilenameLen = strlen(pszRadixFileName) + 4 + 1;
544     pszFilename = (char*) VSI_MALLOC_VERBOSE(nFilenameLen);
545     if (pszFilename == NULL)
546         return FALSE;
547 
548     for(iShpFile = 0; iShpFile < 3; iShpFile ++)
549     {
550         VSILFILE* fp;
551         GByte* pabyBuffer;
552         int nSize = anOffset[iShpFile+1] - anOffset[iShpFile];
553 
554         pabyBuffer = (GByte*) VSI_MALLOC_VERBOSE(nSize);
555         if (pabyBuffer == NULL)
556         {
557             VSIFree(pszFilename);
558             return FALSE;
559         }
560 
561         if( VSIFSeekL(psDES->psFile->fp, psSegInfo->nSegmentStart + anOffset[iShpFile], SEEK_SET) != 0 ||
562             VSIFReadL(pabyBuffer, 1, nSize, psDES->psFile->fp) != (size_t)nSize)
563         {
564             VSIFree(pabyBuffer);
565             VSIFree(pszFilename);
566             return FALSE;
567         }
568 
569         snprintf(pszFilename, nFilenameLen, "%s.%s", pszRadixFileName, apszExt[iShpFile]);
570         fp = VSIFOpenL(pszFilename, "wb");
571         if (fp == NULL)
572         {
573             VSIFree(pabyBuffer);
574             VSIFree(pszFilename);
575             return FALSE;
576         }
577 
578         if( (int) VSIFWriteL(pabyBuffer, 1, nSize, fp) != nSize )
579         {
580             CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp));
581             VSIFree(pabyBuffer);
582             VSIFree(pszFilename);
583             return FALSE;
584         }
585         CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp));
586         VSIFree(pabyBuffer);
587     }
588 
589     VSIFree(pszFilename);
590 
591     return TRUE;
592 }
593 
594 /************************************************************************/
595 /*                              NITFDESGetXml()                         */
596 /************************************************************************/
597 
598 CPLXMLNode* NITFDESGetXml(NITFFile* psFile, int iSegment)
599 {
600     CPLXMLNode* psDesNode;
601     char** papszTmp;
602     NITFDES* psDes = NITFDESAccess(psFile, iSegment);
603 
604     if (psDes == NULL)
605     {
606         return NULL;
607     }
608 
609     if (psDes->papszMetadata == NULL)
610     {
611         NITFDESDeaccess(psDes);
612         return NULL;
613     }
614 
615     psDesNode = CPLCreateXMLNode(NULL, CXT_Element, "des");
616     papszTmp = psDes->papszMetadata;
617 
618     while (papszTmp != NULL && *papszTmp != NULL)
619     {
620         CPLXMLNode* psFieldNode;
621         CPLXMLNode* psNameNode;
622         CPLXMLNode* psValueNode;
623 
624         const char* pszMDval;
625         const char* pszMDsep;
626 
627         if ((pszMDsep = strchr(*papszTmp, '=')) == NULL)
628         {
629             NITFDESDeaccess(psDes);
630             CPLDestroyXMLNode(psDesNode);
631             CPLError(CE_Failure, CPLE_AppDefined,
632                 "NITF DES metadata item missing separator");
633             return NULL;
634         }
635 
636         pszMDval = pszMDsep + 1;
637 
638         if (papszTmp == psDes->papszMetadata)
639         {
640             CPLCreateXMLNode(CPLCreateXMLNode(psDesNode, CXT_Attribute, "name"),
641                 CXT_Text, pszMDval);
642         }
643         else
644         {
645             char* pszMDname = (char*)CPLMalloc(pszMDsep - *papszTmp + 1);
646             CPLStrlcpy(pszMDname, *papszTmp, pszMDsep - *papszTmp + 1);
647 
648             psFieldNode = CPLCreateXMLNode(psDesNode, CXT_Element, "field");
649             psNameNode = CPLCreateXMLNode(psFieldNode, CXT_Attribute, "name");
650             CPLCreateXMLNode(psNameNode, CXT_Text, pszMDname);
651             psValueNode = CPLCreateXMLNode(psFieldNode, CXT_Attribute, "value");
652 
653             if (strcmp(pszMDname, "NITF_DESDATA") == 0)
654             {
655                 int nLen;
656                 char* pszUnescaped = CPLUnescapeString(pszMDval, &nLen, CPLES_BackslashQuotable);
657                 char* pszBase64 = CPLBase64Encode(nLen, (const GByte*)pszUnescaped);
658                 CPLFree(pszUnescaped);
659 
660                 if (pszBase64 == NULL)
661                 {
662                     NITFDESDeaccess(psDes);
663                     CPLDestroyXMLNode(psDesNode);
664                     CPLFree(pszMDname);
665                     CPLError(CE_Failure, CPLE_AppDefined,
666                         "NITF DES data could not be encoded");
667                     return NULL;
668                 }
669 
670                 CPLCreateXMLNode(psValueNode, CXT_Text, pszBase64);
671 
672                 CPLFree(pszBase64);
673             }
674             else
675             {
676                 CPLCreateXMLNode(psValueNode, CXT_Text, pszMDval);
677             }
678 
679             CPLFree(pszMDname);
680         }
681 
682         ++papszTmp;
683     }
684 
685     NITFDESDeaccess(psDes);
686 
687     return psDesNode;
688 }
689