Coverage Report

Created: 2025-06-13 07:22

/src/libmodi/libfplist/libfplist_property_list.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Property list functions
3
 *
4
 * Copyright (C) 2016-2024, Joachim Metz <joachim.metz@gmail.com>
5
 *
6
 * Refer to AUTHORS for acknowledgements.
7
 *
8
 * This program is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU Lesser General Public License as published by
10
 * the Free Software Foundation, either version 3 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public License
19
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20
 */
21
22
#include <common.h>
23
#include <memory.h>
24
#include <types.h>
25
26
#include "libfplist_libcerror.h"
27
#include "libfplist_libcnotify.h"
28
#include "libfplist_property.h"
29
#include "libfplist_property_list.h"
30
#include "libfplist_types.h"
31
#include "libfplist_xml_parser.h"
32
33
extern \
34
int libfplist_xml_parser_parse_buffer(
35
     libfplist_property_list_t *property_list,
36
     const uint8_t *buffer,
37
     size_t buffer_size,
38
     libcerror_error_t **error );
39
40
/* Creates a property list
41
 * Make sure the value property_list is referencing, is set to NULL
42
 * Returns 1 if successful or -1 on error
43
 */
44
int libfplist_property_list_initialize(
45
    libfplist_property_list_t **property_list,
46
    libcerror_error_t **error )
47
4.09k
{
48
4.09k
  libfplist_internal_property_list_t *internal_property_list = NULL;
49
4.09k
  static char *function                                      = "libfplist_property_list_initialize";
50
51
4.09k
  if( property_list == NULL )
52
0
  {
53
0
    libcerror_error_set(
54
0
     error,
55
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
56
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
57
0
     "%s: invalid property list.",
58
0
     function );
59
60
0
    return( -1 );
61
0
  }
62
4.09k
  if( *property_list != NULL )
63
0
  {
64
0
    libcerror_error_set(
65
0
     error,
66
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
67
0
     LIBCERROR_RUNTIME_ERROR_VALUE_ALREADY_SET,
68
0
     "%s: invalid property list value already set.",
69
0
     function );
70
71
0
    return( -1 );
72
0
  }
73
4.09k
  internal_property_list = memory_allocate_structure(
74
4.09k
                            libfplist_internal_property_list_t );
75
76
4.09k
  if( internal_property_list == NULL )
77
0
  {
78
0
    libcerror_error_set(
79
0
     error,
80
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
81
0
     LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
82
0
     "%s: unable to create property list.",
83
0
     function );
84
85
0
    goto on_error;
86
0
  }
87
4.09k
  if( memory_set(
88
4.09k
       internal_property_list,
89
4.09k
       0,
90
4.09k
       sizeof( libfplist_internal_property_list_t ) ) == NULL )
91
0
  {
92
0
    libcerror_error_set(
93
0
     error,
94
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
95
0
     LIBCERROR_MEMORY_ERROR_SET_FAILED,
96
0
     "%s: unable to clear property list.",
97
0
     function );
98
  
99
0
    memory_free(
100
0
     internal_property_list );
101
102
0
    return( -1 );
103
0
  }
104
4.09k
  *property_list = (libfplist_property_list_t *) internal_property_list;
105
106
4.09k
  return( 1 );
107
108
0
on_error:
109
0
  if( internal_property_list != NULL )
110
0
  {
111
0
    memory_free(
112
0
     internal_property_list );
113
0
  }
114
0
  return( -1 );
115
4.09k
}
116
117
/* Frees a property list
118
 * Returns 1 if successful or -1 on error
119
 */
120
int libfplist_property_list_free(
121
    libfplist_property_list_t **property_list,
122
    libcerror_error_t **error )
123
4.09k
{
124
4.09k
  libfplist_internal_property_list_t *internal_property_list = NULL;
125
4.09k
  static char *function                                      = "libfplist_property_list_free";
126
4.09k
  int result                                                 = 1;
127
128
4.09k
  if( property_list == NULL )
129
0
  {
130
0
    libcerror_error_set(
131
0
     error,
132
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
133
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
134
0
     "%s: invalid property list.",
135
0
     function );
136
137
0
    return( -1 );
138
0
  }
139
4.09k
  if( *property_list != NULL )
140
4.09k
  {
141
4.09k
    internal_property_list = (libfplist_internal_property_list_t *) *property_list;
142
4.09k
    *property_list         = NULL;
143
144
    /* The root_tag and dict_tag are referenced and freed elsewhere */
145
146
4.09k
    if( libfplist_xml_tag_free(
147
4.09k
         &( internal_property_list->root_tag ),
148
4.09k
         error ) != 1 )
149
0
    {
150
0
      libcerror_error_set(
151
0
       error,
152
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
153
0
       LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
154
0
       "%s: unable to free root XML tag.",
155
0
       function );
156
157
0
      result = -1;
158
0
    }
159
4.09k
    memory_free(
160
4.09k
     internal_property_list );
161
4.09k
  }
162
4.09k
  return( result );
163
4.09k
}
164
165
/* Copies the property list from the byte stream
166
 * Returns 1 if successful, 0 if not a valid property list or -1 on error
167
 */
168
int libfplist_property_list_copy_from_byte_stream(
169
     libfplist_property_list_t *property_list,
170
     const uint8_t *byte_stream,
171
     size_t byte_stream_size,
172
     libcerror_error_t **error )
173
4.09k
{
174
4.09k
  libfplist_internal_property_list_t *internal_property_list = NULL;
175
4.09k
  libfplist_xml_tag_t *element_tag                           = NULL;
176
4.09k
  uint8_t *buffer                                            = NULL;
177
4.09k
  static char *function                                      = "libfplist_property_list_copy_from_byte_stream";
178
4.09k
  size_t buffer_size                                         = 0;
179
4.09k
  int element_index                                          = 0;
180
4.09k
  int number_of_elements                                     = 0;
181
4.09k
  int result                                                 = 0;
182
183
4.09k
  if( property_list == NULL )
184
0
  {
185
0
    libcerror_error_set(
186
0
     error,
187
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
188
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
189
0
     "%s: invalid property list.",
190
0
     function );
191
192
0
    return( -1 );
193
0
  }
194
4.09k
  internal_property_list = (libfplist_internal_property_list_t *) property_list;
195
196
4.09k
  if( internal_property_list->dict_tag != NULL )
197
0
  {
198
0
    libcerror_error_set(
199
0
     error,
200
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
201
0
     LIBCERROR_RUNTIME_ERROR_VALUE_ALREADY_SET,
202
0
     "%s: invalid property list - dict XML tag already set.",
203
0
     function );
204
205
0
    return( -1 );
206
0
  }
207
4.09k
  if( internal_property_list->root_tag != NULL )
208
0
  {
209
0
    libcerror_error_set(
210
0
     error,
211
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
212
0
     LIBCERROR_RUNTIME_ERROR_VALUE_ALREADY_SET,
213
0
     "%s: invalid property list - root XML tag already set.",
214
0
     function );
215
216
0
    return( -1 );
217
0
  }
218
4.09k
  if( byte_stream == NULL )
219
0
  {
220
0
    libcerror_error_set(
221
0
     error,
222
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
223
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
224
0
     "%s: invalid byte stream.",
225
0
     function );
226
227
0
    return( -1 );
228
0
  }
229
4.09k
  if( ( byte_stream_size < 2 )
230
4.09k
   || ( byte_stream_size > (size_t) ( MEMORY_MAXIMUM_ALLOCATION_SIZE - 2 ) ) )
231
5
  {
232
5
    libcerror_error_set(
233
5
     error,
234
5
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
235
5
     LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
236
5
     "%s: invalid byte stream size value out of bounds.",
237
5
     function );
238
239
5
    return( -1 );
240
5
  }
241
  /* Lex wants 2 zero bytes at the end of the buffer
242
   */
243
4.08k
  buffer_size = byte_stream_size;
244
245
4.08k
  if( byte_stream[ byte_stream_size - 1 ] != 0 )
246
3.62k
  {
247
3.62k
    buffer_size += 2;
248
3.62k
  }
249
463
  else if( byte_stream[ byte_stream_size - 2 ] != 0 )
250
437
  {
251
437
    buffer_size += 1;
252
437
  }
253
  /* Lex wants a buffer it can write to
254
   */
255
4.08k
  buffer = (uint8_t *) memory_allocate(
256
4.08k
                        sizeof( uint8_t ) * buffer_size );
257
258
4.08k
  if( buffer == NULL )
259
0
  {
260
0
    libcerror_error_set(
261
0
     error,
262
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
263
0
     LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
264
0
     "%s: unable to create buffer.",
265
0
     function );
266
267
0
    goto on_error;
268
0
  }
269
4.08k
  if( memory_copy(
270
4.08k
       buffer,
271
4.08k
       byte_stream,
272
4.08k
       byte_stream_size ) == NULL )
273
0
  {
274
0
    libcerror_error_set(
275
0
     error,
276
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
277
0
     LIBCERROR_MEMORY_ERROR_COPY_FAILED,
278
0
     "%s: unable to copy byte stream.",
279
0
     function );
280
281
0
    goto on_error;
282
0
  }
283
4.08k
  buffer[ buffer_size - 2 ] = 0;
284
4.08k
  buffer[ buffer_size - 1 ] = 0;
285
286
#if defined( HAVE_DEBUG_OUTPUT )
287
  if( libcnotify_verbose != 0 )
288
  {                
289
    if( ( buffer_size >= 6 )
290
     && ( buffer[ 0 ] == '<' )
291
     && ( buffer[ 1 ] == '?' )
292
     && ( buffer[ 2 ] == 'x' )
293
     && ( buffer[ 3 ] == 'm' )
294
     && ( buffer[ 4 ] == 'l' )
295
     && ( buffer[ buffer_size - 1 ] == 0 ) )
296
    {
297
      libcnotify_printf(
298
       "%s: XML plist:\n%s\n",
299
       function,
300
       buffer );
301
    }        
302
    else     
303
    {       
304
      libcnotify_printf(
305
       "%s: XML plist data:\n",
306
       function );
307
      libcnotify_print_data(
308
       buffer,
309
       buffer_size,
310
       LIBCNOTIFY_PRINT_DATA_FLAG_GROUP_DATA );
311
    }
312
  }                
313
#endif /* defined( HAVE_DEBUG_OUTPUT ) */
314
315
4.08k
  result = libfplist_xml_parser_parse_buffer(
316
4.08k
            property_list,
317
4.08k
            buffer,
318
4.08k
            buffer_size,
319
4.08k
            error );
320
321
4.08k
  if( result != 1 )
322
2.31k
  {
323
2.31k
    libcerror_error_set(
324
2.31k
     error,
325
2.31k
     LIBCERROR_ERROR_DOMAIN_IO,
326
2.31k
     LIBCERROR_IO_ERROR_READ_FAILED,
327
2.31k
     "%s: unable to parse XML.",
328
2.31k
     function );
329
330
2.31k
    goto on_error;
331
2.31k
  }
332
1.77k
  memory_free(
333
1.77k
   buffer );
334
335
1.77k
  buffer = NULL;
336
337
1.77k
  if( internal_property_list->root_tag == NULL )
338
0
  {
339
0
    libcerror_error_set(
340
0
     error,
341
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
342
0
     LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
343
0
     "%s: invalid property list - missing root XML tag.",
344
0
     function );
345
346
0
    goto on_error;
347
0
  }
348
1.77k
  result = libfplist_xml_tag_compare_name(
349
1.77k
            internal_property_list->root_tag,
350
1.77k
            (uint8_t *) "dict",
351
1.77k
            4,
352
1.77k
            error );
353
354
1.77k
  if( result == -1 )
355
0
  {
356
0
    libcerror_error_set(
357
0
     error,
358
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
359
0
     LIBCERROR_RUNTIME_ERROR_GET_FAILED,
360
0
     "%s: unable to compare name of root tag.",
361
0
     function );
362
363
0
    goto on_error;
364
0
  }
365
1.77k
  else if( result == 1 )
366
474
  {
367
474
    internal_property_list->dict_tag = internal_property_list->root_tag;
368
474
  }
369
1.29k
  else
370
1.29k
  {
371
    /* Ignore the plist XML node
372
     * <plist version="1.0">
373
     */
374
1.29k
    result = libfplist_xml_tag_compare_name(
375
1.29k
              internal_property_list->root_tag,
376
1.29k
              (uint8_t *) "plist",
377
1.29k
              5,
378
1.29k
              error );
379
380
1.29k
    if( result == -1 )
381
0
    {
382
0
      libcerror_error_set(
383
0
       error,
384
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
385
0
       LIBCERROR_RUNTIME_ERROR_GET_FAILED,
386
0
       "%s: unable to compare name of root tag.",
387
0
       function );
388
389
0
      goto on_error;
390
0
    }
391
1.29k
    else if( result == 1 )
392
1.23k
    {
393
1.23k
      internal_property_list->plist_tag = internal_property_list->root_tag;
394
395
1.23k
      if( libfplist_xml_tag_get_number_of_elements(
396
1.23k
           internal_property_list->root_tag,
397
1.23k
           &number_of_elements,
398
1.23k
           error ) != 1 )
399
0
      {
400
0
        libcerror_error_set(
401
0
         error,
402
0
         LIBCERROR_ERROR_DOMAIN_RUNTIME,
403
0
         LIBCERROR_RUNTIME_ERROR_GET_FAILED,
404
0
         "%s: unable to retrieve number of elements.",
405
0
         function );
406
407
0
        goto on_error;
408
0
      }
409
1.23k
      for( element_index = 0;
410
37.6k
           element_index < number_of_elements;
411
36.3k
           element_index++ )
412
36.5k
      {
413
36.5k
        if( libfplist_xml_tag_get_element(
414
36.5k
             internal_property_list->root_tag,
415
36.5k
             element_index,
416
36.5k
             &element_tag,
417
36.5k
             error ) != 1 )
418
0
        {
419
0
          libcerror_error_set(
420
0
           error,
421
0
           LIBCERROR_ERROR_DOMAIN_RUNTIME,
422
0
           LIBCERROR_RUNTIME_ERROR_GET_FAILED,
423
0
           "%s: unable to retrieve element: %d.",
424
0
           function,
425
0
           element_index );
426
427
0
          goto on_error;
428
0
        }
429
36.5k
        result = libfplist_xml_tag_compare_name(
430
36.5k
                  element_tag,
431
36.5k
                  (uint8_t *) "text",
432
36.5k
                  4,
433
36.5k
                  error );
434
435
36.5k
        if( result == -1 )
436
0
        {
437
0
          libcerror_error_set(
438
0
           error,
439
0
           LIBCERROR_ERROR_DOMAIN_RUNTIME,
440
0
           LIBCERROR_RUNTIME_ERROR_GET_FAILED,
441
0
           "%s: unable to compare name of element tag: %d.",
442
0
           function,
443
0
           element_index );
444
445
0
          goto on_error;
446
0
        }
447
36.5k
        else if( result != 0 )
448
11.4k
        {
449
          /* Ignore text nodes
450
           */
451
11.4k
          continue;
452
11.4k
        }
453
25.1k
        result = libfplist_xml_tag_compare_name(
454
25.1k
                  element_tag,
455
25.1k
                  (uint8_t *) "dict",
456
25.1k
                  4,
457
25.1k
                  error );
458
459
25.1k
        if( result == -1 )
460
0
        {
461
0
          libcerror_error_set(
462
0
           error,
463
0
           LIBCERROR_ERROR_DOMAIN_RUNTIME,
464
0
           LIBCERROR_RUNTIME_ERROR_GET_FAILED,
465
0
           "%s: unable to compare name of element tag: %d.",
466
0
           function,
467
0
           element_index );
468
469
0
          goto on_error;
470
0
        }
471
25.1k
        else if( result != 0 )
472
24.9k
        {
473
24.9k
          internal_property_list->dict_tag = element_tag;
474
24.9k
        }
475
151
        else
476
151
        {
477
151
          break;
478
151
        }
479
25.1k
      }
480
1.23k
    }
481
1.29k
  }
482
1.77k
  return( 1 );
483
484
2.31k
on_error:
485
2.31k
  if( buffer != NULL )
486
2.31k
  {
487
2.31k
    memory_free(
488
2.31k
     buffer );
489
2.31k
  }
490
2.31k
  if( internal_property_list->root_tag != NULL )
491
0
  {
492
0
    libfplist_xml_tag_free(
493
0
     &( internal_property_list->root_tag ),
494
0
     NULL );
495
0
  }
496
2.31k
  return( -1 );
497
1.77k
}
498
499
/* Determines if the property list is XML with a plist root element
500
 * Returns 1 if true, 0 if not or -1 on error
501
 */
502
int libfplist_property_list_has_plist_root_element(
503
     libfplist_property_list_t *property_list,
504
     libcerror_error_t **error )
505
0
{
506
0
  libfplist_internal_property_list_t *internal_property_list = NULL;
507
0
  static char *function                                      = "libfplist_property_list_has_plist_root_element";
508
509
0
  if( property_list == NULL )
510
0
  {
511
0
    libcerror_error_set(
512
0
     error,
513
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
514
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
515
0
     "%s: invalid property list.",
516
0
     function );
517
518
0
    return( -1 );
519
0
  }
520
0
  internal_property_list = (libfplist_internal_property_list_t *) property_list;
521
522
0
  if( internal_property_list->plist_tag == NULL )
523
0
  {
524
0
    return( 0 );
525
0
  }
526
0
  return( 1 );
527
0
}
528
529
/* Retrieves the root property
530
 * Returns 1 if successful, 0 if not available or -1 on error
531
 */
532
int libfplist_property_list_get_root_property(
533
     libfplist_property_list_t *property_list,
534
     libfplist_property_t **property,
535
     libcerror_error_t **error )
536
1.66k
{
537
1.66k
  libfplist_internal_property_list_t *internal_property_list = NULL;
538
1.66k
  static char *function                                      = "libfplist_property_list_get_root_property";
539
540
1.66k
  if( property_list == NULL )
541
0
  {
542
0
    libcerror_error_set(
543
0
     error,
544
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
545
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
546
0
     "%s: invalid property list.",
547
0
     function );
548
549
0
    return( -1 );
550
0
  }
551
1.66k
  internal_property_list = (libfplist_internal_property_list_t *) property_list;
552
553
1.66k
  if( property == NULL )
554
0
  {
555
0
    libcerror_error_set(
556
0
     error,
557
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
558
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
559
0
     "%s: invalid property.",
560
0
     function );
561
562
0
    return( -1 );
563
0
  }
564
1.66k
  if( *property != NULL )
565
0
  {
566
0
    libcerror_error_set(
567
0
     error,
568
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
569
0
     LIBCERROR_RUNTIME_ERROR_VALUE_ALREADY_SET,
570
0
     "%s: invalid property value already set.",
571
0
     function );
572
573
0
    return( -1 );
574
0
  }
575
1.66k
  if( internal_property_list->dict_tag == NULL )
576
113
  {
577
113
    return( 0 );
578
113
  }
579
1.55k
  if( libfplist_property_initialize(
580
1.55k
       property,
581
1.55k
       NULL,
582
1.55k
       internal_property_list->dict_tag,
583
1.55k
       error ) != 1 )
584
0
  {
585
0
    libcerror_error_set(
586
0
     error,
587
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
588
0
     LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
589
0
     "%s: unable to create property.",
590
0
     function );
591
592
0
    return( -1 );
593
0
  }
594
1.55k
  return( 1 );
595
1.55k
}
596
597
/* Sets the root tag
598
 * Returns 1 if successful or -1 on error
599
 */
600
int libfplist_property_list_set_root_tag(
601
     libfplist_property_list_t *property_list,
602
     libfplist_xml_tag_t *tag,
603
     libcerror_error_t **error )
604
1.77k
{
605
1.77k
  libfplist_internal_property_list_t *internal_property_list = NULL;
606
1.77k
  static char *function                                      = "libfplist_property_list_set_root_tag";
607
608
1.77k
  if( property_list == NULL )
609
0
  {
610
0
    libcerror_error_set(
611
0
     error,
612
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
613
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
614
0
     "%s: invalid property list.",
615
0
     function );
616
617
0
    return( -1 );
618
0
  }
619
1.77k
  internal_property_list = (libfplist_internal_property_list_t *) property_list;
620
621
1.77k
  if( tag == NULL )
622
0
  {
623
0
    libcerror_error_set(
624
0
     error,
625
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
626
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
627
0
     "%s: invalid tag.",
628
0
     function );
629
630
0
    return( -1 );
631
0
  }
632
1.77k
  internal_property_list->root_tag = tag;
633
634
1.77k
  return( 1 );
635
1.77k
}
636