Coverage Report

Created: 2025-10-29 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libplist/src/bplist.c
Line
Count
Source
1
/*
2
 * bplist.c
3
 * Binary plist implementation
4
 *
5
 * Copyright (c) 2011-2017 Nikias Bassen, All Rights Reserved.
6
 * Copyright (c) 2008-2010 Jonathan Beck, All Rights Reserved.
7
 *
8
 * This library is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU Lesser General Public
10
 * License as published by the Free Software Foundation; either
11
 * version 2.1 of the License, or (at your option) any later version.
12
 *
13
 * This library 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 GNU
16
 * Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public
19
 * License along with this library; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21
 */
22
23
#ifdef HAVE_CONFIG_H
24
#include <config.h>
25
#endif
26
27
#include <stdlib.h>
28
#include <stdio.h>
29
#include <string.h>
30
#include <assert.h>
31
32
#include <ctype.h>
33
#include <inttypes.h>
34
35
#include "plist.h"
36
#include "hashtable.h"
37
#include "bytearray.h"
38
#include "ptrarray.h"
39
#include "plist/plist.h"
40
41
#include <node.h>
42
43
/* Magic marker and size. */
44
0
#define BPLIST_MAGIC            ((uint8_t*)"bplist")
45
0
#define BPLIST_MAGIC_SIZE       6
46
47
0
#define BPLIST_VERSION          ((uint8_t*)"00")
48
0
#define BPLIST_VERSION_SIZE     2
49
50
#pragma pack(push,1)
51
typedef struct {
52
    uint8_t unused[6];
53
    uint8_t offset_size;
54
    uint8_t ref_size;
55
    uint64_t num_objects;
56
    uint64_t root_object_index;
57
    uint64_t offset_table_offset;
58
} bplist_trailer_t;
59
#pragma pack(pop)
60
61
enum
62
{
63
    BPLIST_NULL = 0x00,
64
    BPLIST_FALSE = 0x08,
65
    BPLIST_TRUE = 0x09,
66
    BPLIST_FILL = 0x0F,     /* will be used for length grabbing */
67
    BPLIST_INT = 0x10,
68
    BPLIST_REAL = 0x20,
69
    BPLIST_DATE = 0x30,
70
    BPLIST_DATA = 0x40,
71
    BPLIST_STRING = 0x50,
72
    BPLIST_UNICODE = 0x60,
73
    BPLIST_UNK_0x70 = 0x70,
74
    BPLIST_UID = 0x80,
75
    BPLIST_ARRAY = 0xA0,
76
    BPLIST_SET = 0xC0,
77
    BPLIST_DICT = 0xD0,
78
    BPLIST_MASK = 0xF0
79
};
80
81
union plist_uint_ptr
82
{
83
    const void *src;
84
    uint8_t *u8ptr;
85
    uint16_t *u16ptr;
86
    uint32_t *u32ptr;
87
    uint64_t *u64ptr;
88
};
89
90
#ifdef _MSC_VER
91
uint64_t get_unaligned_64(uint64_t *ptr)
92
{
93
  uint64_t temp;
94
  memcpy(&temp, ptr, sizeof(temp));
95
  return temp;
96
}
97
98
uint32_t get_unaligned_32(uint32_t *ptr)
99
{
100
  uint32_t temp;
101
  memcpy(&temp, ptr, sizeof(temp));
102
  return temp;
103
}
104
105
uint16_t get_unaligned_16(uint16_t *ptr)
106
{
107
  uint16_t temp;
108
  memcpy(&temp, ptr, sizeof(temp));
109
  return temp;
110
}
111
#else
112
#define get_unaligned(ptr)        \
113
  ({                                              \
114
    struct __attribute__((packed)) {      \
115
      typeof(*(ptr)) __v;       \
116
    } *__p = (void *) (ptr);        \
117
    __p->__v;           \
118
  })
119
#endif
120
121
122
#ifndef bswap16
123
#define bswap16(x)   ((((x) & 0xFF00) >> 8) | (((x) & 0x00FF) << 8))
124
#endif
125
126
#ifndef bswap32
127
0
#define bswap32(x)   ((((x) & 0xFF000000) >> 24) \
128
0
                    | (((x) & 0x00FF0000) >>  8) \
129
0
                    | (((x) & 0x0000FF00) <<  8) \
130
0
                    | (((x) & 0x000000FF) << 24))
131
#endif
132
133
#ifndef bswap64
134
0
#define bswap64(x)   ((((x) & 0xFF00000000000000ull) >> 56) \
135
0
                    | (((x) & 0x00FF000000000000ull) >> 40) \
136
0
                    | (((x) & 0x0000FF0000000000ull) >> 24) \
137
0
                    | (((x) & 0x000000FF00000000ull) >>  8) \
138
0
                    | (((x) & 0x00000000FF000000ull) <<  8) \
139
0
                    | (((x) & 0x0000000000FF0000ull) << 24) \
140
0
                    | (((x) & 0x000000000000FF00ull) << 40) \
141
0
                    | (((x) & 0x00000000000000FFull) << 56))
142
#endif
143
144
#ifndef be16toh
145
#ifdef __BIG_ENDIAN__
146
#define be16toh(x) (x)
147
#else
148
#define be16toh(x) bswap16(x)
149
#endif
150
#endif
151
152
#ifndef be32toh
153
#ifdef __BIG_ENDIAN__
154
#define be32toh(x) (x)
155
#else
156
#define be32toh(x) bswap32(x)
157
#endif
158
#endif
159
160
#ifndef be64toh
161
#ifdef __BIG_ENDIAN__
162
#define be64toh(x) (x)
163
#else
164
#define be64toh(x) bswap64(x)
165
#endif
166
#endif
167
168
#ifdef __BIG_ENDIAN__
169
#define beNtoh(x,n) (x >> ((8-n) << 3))
170
#else
171
0
#define beNtoh(x,n) be64toh((x) << ((8-(n)) << 3))
172
#endif
173
174
#ifdef _MSC_VER
175
static uint64_t UINT_TO_HOST(const void* x, uint8_t n)
176
{
177
    union plist_uint_ptr __up;
178
    __up.src = (n > 8) ? (const char*)x + (n - 8) : (const char*)x;
179
    return (n >= 8 ? be64toh( get_unaligned_64(__up.u64ptr) ) :
180
    (n == 4 ? be32toh( get_unaligned_32(__up.u32ptr) ) :
181
    (n == 2 ? be16toh( get_unaligned_16(__up.u16ptr) ) :
182
    (n == 1 ? *__up.u8ptr :
183
    beNtoh( get_unaligned_64(__up.u64ptr), n)
184
    ))));
185
}
186
#else
187
#define UINT_TO_HOST(x, n) \
188
0
  ({ \
189
0
    union plist_uint_ptr __up; \
190
0
    __up.src = ((n) > 8) ? (const char*)(x) + ((n) - 8) : (const char*)(x); \
191
0
    ((n) >= 8 ? be64toh( get_unaligned(__up.u64ptr) ) : \
192
0
    ((n) == 4 ? be32toh( get_unaligned(__up.u32ptr) ) : \
193
0
    ((n) == 2 ? be16toh( get_unaligned(__up.u16ptr) ) : \
194
0
    ((n) == 1 ? *__up.u8ptr : \
195
0
    beNtoh( get_unaligned(__up.u64ptr), n) \
196
0
    )))); \
197
0
  })
198
#endif
199
200
#define get_needed_bytes(x) \
201
0
    ( ((uint64_t)(x)) < (1ULL << 8) ? 1 : \
202
0
    ( ((uint64_t)(x)) < (1ULL << 16) ? 2 : \
203
0
    ( ((uint64_t)(x)) < (1ULL << 24) ? 3 : \
204
0
    ( ((uint64_t)(x)) < (1ULL << 32) ? 4 : 8))))
205
206
0
#define get_real_bytes(x) ((x) == (float) (x) ? sizeof(float) : sizeof(double))
207
208
#if (defined(__BIG_ENDIAN__) && !defined(__FLOAT_WORD_ORDER__)) \
209
 || (defined(__FLOAT_WORD_ORDER__) && __FLOAT_WORD_ORDER__ == __ORDER_BIG_ENDIAN__)
210
#define float_bswap64(x) (x)
211
#define float_bswap32(x) (x)
212
#else
213
0
#define float_bswap64(x) bswap64(x)
214
0
#define float_bswap32(x) bswap32(x)
215
#endif
216
217
#ifndef __has_builtin
218
#define __has_builtin(x) 0
219
#endif
220
221
#if __has_builtin(__builtin_umulll_overflow) || __GNUC__ >= 5
222
0
#define uint64_mul_overflow(a, b, r) __builtin_umulll_overflow(a, b, (unsigned long long*)(r))
223
#else
224
static int uint64_mul_overflow(uint64_t a, uint64_t b, uint64_t *res)
225
{
226
    *res = a * b;
227
    return (a > UINT64_MAX / b);
228
}
229
#endif
230
231
#define NODE_IS_ROOT(x) (((node_t)(x))->isRoot)
232
233
struct bplist_data {
234
    const char* data;
235
    uint64_t size;
236
    uint64_t num_objects;
237
    uint8_t ref_size;
238
    uint8_t offset_size;
239
    const char* offset_table;
240
    uint32_t level;
241
    ptrarray_t* used_indexes;
242
};
243
244
#ifdef DEBUG
245
static int plist_bin_debug = 0;
246
0
#define PLIST_BIN_ERR(...) if (plist_bin_debug) { fprintf(stderr, "libplist[binparser] ERROR: " __VA_ARGS__); }
247
#else
248
#define PLIST_BIN_ERR(...)
249
#endif
250
251
void plist_bin_init(void)
252
2
{
253
    /* init binary plist stuff */
254
2
#ifdef DEBUG
255
2
    char *env_debug = getenv("PLIST_BIN_DEBUG");
256
2
    if (env_debug && !strcmp(env_debug, "1")) {
257
0
        plist_bin_debug = 1;
258
0
    }
259
2
#endif
260
2
}
261
262
void plist_bin_deinit(void)
263
0
{
264
    /* deinit binary plist stuff */
265
0
}
266
267
void plist_bin_set_debug(int debug)
268
0
{
269
0
#if DEBUG
270
0
    plist_bin_debug = debug;
271
0
#endif
272
0
}
273
274
static plist_t parse_bin_node_at_index(struct bplist_data *bplist, uint32_t node_index);
275
276
static plist_t parse_int_node(const char **bnode, uint8_t size)
277
0
{
278
0
    plist_data_t data = plist_new_plist_data();
279
280
0
    size = 1 << size;     // make length less misleading
281
0
    switch (size)
282
0
    {
283
0
    case sizeof(uint8_t):
284
0
    case sizeof(uint16_t):
285
0
    case sizeof(uint32_t):
286
0
    case sizeof(uint64_t):
287
0
        data->length = sizeof(uint64_t);
288
0
        break;
289
0
    case 16:
290
0
        data->length = size;
291
0
        break;
292
0
    default:
293
0
        free(data);
294
0
        PLIST_BIN_ERR("%s: Invalid byte size for integer node\n", __func__);
295
0
        return NULL;
296
0
    };
297
298
0
    data->intval = UINT_TO_HOST(*bnode, size);
299
300
0
    (*bnode) += size;
301
0
    data->type = PLIST_INT;
302
303
0
    return node_create(NULL, data);
304
0
}
305
306
static plist_t parse_real_node(const char **bnode, uint8_t size)
307
0
{
308
0
    plist_data_t data = plist_new_plist_data();
309
310
0
    size = 1 << size;     // make length less misleading
311
0
    switch (size)
312
0
    {
313
0
    case sizeof(uint32_t):
314
0
    {
315
0
        uint32_t ival;
316
0
        memcpy(&ival, *bnode, sizeof(uint32_t));
317
0
        ival = float_bswap32(ival);
318
0
        float fval;
319
0
        memcpy(&fval, &ival, sizeof(float));
320
0
        data->realval = fval;
321
0
    }
322
0
    break;
323
324
0
    case sizeof(uint64_t):
325
0
    {
326
0
        uint64_t ival;
327
0
        memcpy(&ival, *bnode, sizeof(uint64_t));
328
0
        ival = float_bswap64(ival);
329
0
        memcpy(&data->realval, &ival, sizeof(double));
330
0
        break;
331
0
    }
332
333
0
    default:
334
0
        free(data);
335
0
        PLIST_BIN_ERR("%s: Invalid byte size for real node\n", __func__);
336
0
        return NULL;
337
0
    }
338
0
    data->type = PLIST_REAL;
339
0
    data->length = sizeof(double);
340
341
0
    return node_create(NULL, data);
342
0
}
343
344
static plist_t parse_date_node(const char **bnode, uint8_t size)
345
0
{
346
0
    plist_t node = parse_real_node(bnode, size);
347
0
    plist_data_t data = plist_get_data(node);
348
349
0
    data->type = PLIST_DATE;
350
351
0
    return node;
352
0
}
353
354
static plist_t parse_string_node(const char **bnode, uint64_t size)
355
0
{
356
0
    plist_data_t data = plist_new_plist_data();
357
358
0
    data->type = PLIST_STRING;
359
0
    data->strval = (char *) malloc(sizeof(char) * (size + 1));
360
0
    if (!data->strval) {
361
0
        plist_free_data(data);
362
0
        PLIST_BIN_ERR("%s: Could not allocate %" PRIu64 " bytes\n", __func__, sizeof(char) * (size + 1));
363
0
        return NULL;
364
0
    }
365
0
    memcpy(data->strval, *bnode, size);
366
0
    data->strval[size] = '\0';
367
0
    data->length = strlen(data->strval);
368
369
0
    return node_create(NULL, data);
370
0
}
371
372
static char *plist_utf16be_to_utf8(uint16_t *unistr, long len, long *items_read, long *items_written)
373
0
{
374
0
  if (!unistr || (len <= 0)) return NULL;
375
0
  char* outbuf;
376
0
  char* outbuf_new;
377
0
  int p = 0;
378
0
  long i = 0;
379
380
0
  uint16_t wc;
381
0
  uint32_t w;
382
0
  int read_lead_surrogate = 0;
383
384
  /* allocate with enough space */
385
0
  outbuf = (char*)malloc(4*(len+1));
386
0
  if (!outbuf) {
387
0
    PLIST_BIN_ERR("%s: Could not allocate %" PRIu64 " bytes\n", __func__, (uint64_t)(4*(len+1)));
388
0
    return NULL;
389
0
  }
390
391
0
  while (i < len) {
392
0
    wc = UINT_TO_HOST(unistr + i, sizeof(wc));
393
0
    i++;
394
0
    if (wc >= 0xD800 && wc <= 0xDBFF) {
395
0
      if (!read_lead_surrogate) {
396
0
        read_lead_surrogate = 1;
397
0
        w = 0x010000 + ((wc & 0x3FF) << 10);
398
0
      } else {
399
        // This is invalid, the next 16 bit char should be a trail surrogate.
400
        // Handling error by skipping.
401
0
        read_lead_surrogate = 0;
402
0
      }
403
0
    } else if (wc >= 0xDC00 && wc <= 0xDFFF) {
404
0
      if (read_lead_surrogate) {
405
0
        read_lead_surrogate = 0;
406
0
        w = w | (wc & 0x3FF);
407
0
        outbuf[p++] = (char)(0xF0 + ((w >> 18) & 0x7));
408
0
        outbuf[p++] = (char)(0x80 + ((w >> 12) & 0x3F));
409
0
        outbuf[p++] = (char)(0x80 + ((w >> 6) & 0x3F));
410
0
        outbuf[p++] = (char)(0x80 + (w & 0x3F));
411
0
      } else {
412
        // This is invalid.  A trail surrogate should always follow a lead surrogate.
413
        // Handling error by skipping
414
0
      }
415
0
    } else if (wc >= 0x800) {
416
0
      outbuf[p++] = (char)(0xE0 + ((wc >> 12) & 0xF));
417
0
      outbuf[p++] = (char)(0x80 + ((wc >> 6) & 0x3F));
418
0
      outbuf[p++] = (char)(0x80 + (wc & 0x3F));
419
0
    } else if (wc >= 0x80) {
420
0
      outbuf[p++] = (char)(0xC0 + ((wc >> 6) & 0x1F));
421
0
      outbuf[p++] = (char)(0x80 + (wc & 0x3F));
422
0
    } else {
423
0
      outbuf[p++] = (char)(wc & 0x7F);
424
0
    }
425
0
  }
426
0
  if (items_read) {
427
0
    *items_read = i;
428
0
  }
429
0
  if (items_written) {
430
0
    *items_written = p;
431
0
  }
432
0
  outbuf[p] = 0;
433
434
  /* reduce the size to the actual size */
435
0
  outbuf_new = (char*)realloc(outbuf, p+1);
436
0
  if (outbuf_new) {
437
0
    outbuf = outbuf_new;
438
0
  }
439
440
0
  return outbuf;
441
0
}
442
443
static plist_t parse_unicode_node(const char **bnode, uint64_t size)
444
0
{
445
0
    plist_data_t data = plist_new_plist_data();
446
0
    long items_read = 0;
447
0
    long items_written = 0;
448
449
0
    data->type = PLIST_STRING;
450
0
    data->strval = plist_utf16be_to_utf8((uint16_t*)(*bnode), size, &items_read, &items_written);
451
0
    if (!data->strval) {
452
0
        plist_free_data(data);
453
0
        return NULL;
454
0
    }
455
0
    data->length = items_written;
456
457
0
    return node_create(NULL, data);
458
0
}
459
460
static plist_t parse_data_node(const char **bnode, uint64_t size)
461
0
{
462
0
    plist_data_t data = plist_new_plist_data();
463
464
0
    data->type = PLIST_DATA;
465
0
    data->length = size;
466
0
    data->buff = (uint8_t *) malloc(sizeof(uint8_t) * size);
467
0
    if (!data->strval) {
468
0
        plist_free_data(data);
469
0
        PLIST_BIN_ERR("%s: Could not allocate %" PRIu64 " bytes\n", __func__, sizeof(uint8_t) * size);
470
0
        return NULL;
471
0
    }
472
0
    memcpy(data->buff, *bnode, sizeof(uint8_t) * size);
473
474
0
    return node_create(NULL, data);
475
0
}
476
477
static plist_t parse_dict_node(struct bplist_data *bplist, const char** bnode, uint64_t size)
478
0
{
479
0
    uint64_t j;
480
0
    uint64_t str_i = 0, str_j = 0;
481
0
    uint64_t index1, index2;
482
0
    plist_data_t data = plist_new_plist_data();
483
0
    const char *index1_ptr = NULL;
484
0
    const char *index2_ptr = NULL;
485
486
0
    data->type = PLIST_DICT;
487
0
    data->length = size;
488
489
0
    plist_t node = node_create(NULL, data);
490
491
0
    for (j = 0; j < data->length; j++) {
492
0
        str_i = j * bplist->ref_size;
493
0
        str_j = (j + size) * bplist->ref_size;
494
0
        index1_ptr = (*bnode) + str_i;
495
0
        index2_ptr = (*bnode) + str_j;
496
497
0
        if ((index1_ptr < bplist->data || index1_ptr + bplist->ref_size > bplist->offset_table) ||
498
0
            (index2_ptr < bplist->data || index2_ptr + bplist->ref_size > bplist->offset_table)) {
499
0
            plist_free(node);
500
0
            PLIST_BIN_ERR("%s: dict entry %" PRIu64 " is outside of valid range\n", __func__, j);
501
0
            return NULL;
502
0
        }
503
504
0
        index1 = UINT_TO_HOST(index1_ptr, bplist->ref_size);
505
0
        index2 = UINT_TO_HOST(index2_ptr, bplist->ref_size);
506
507
0
        if (index1 >= bplist->num_objects) {
508
0
            plist_free(node);
509
0
            PLIST_BIN_ERR("%s: dict entry %" PRIu64 ": key index (%" PRIu64 ") must be smaller than the number of objects (%" PRIu64 ")\n", __func__, j, index1, bplist->num_objects);
510
0
            return NULL;
511
0
        }
512
0
        if (index2 >= bplist->num_objects) {
513
0
            plist_free(node);
514
0
            PLIST_BIN_ERR("%s: dict entry %" PRIu64 ": value index (%" PRIu64 ") must be smaller than the number of objects (%" PRIu64 ")\n", __func__, j, index1, bplist->num_objects);
515
0
            return NULL;
516
0
        }
517
518
        /* process key node */
519
0
        plist_t key = parse_bin_node_at_index(bplist, index1);
520
0
        if (!key) {
521
0
            plist_free(node);
522
0
            return NULL;
523
0
        }
524
525
0
        if (plist_get_data(key)->type != PLIST_STRING) {
526
0
            PLIST_BIN_ERR("%s: dict entry %" PRIu64 ": invalid node type for key\n", __func__, j);
527
0
            plist_free(key);
528
0
            plist_free(node);
529
0
            return NULL;
530
0
        }
531
532
        /* enforce key type */
533
0
        plist_get_data(key)->type = PLIST_KEY;
534
0
        if (!plist_get_data(key)->strval) {
535
0
            PLIST_BIN_ERR("%s: dict entry %" PRIu64 ": key must not be NULL\n", __func__, j);
536
0
            plist_free(key);
537
0
            plist_free(node);
538
0
            return NULL;
539
0
        }
540
541
        /* process value node */
542
0
        plist_t val = parse_bin_node_at_index(bplist, index2);
543
0
        if (!val) {
544
0
            plist_free(key);
545
0
            plist_free(node);
546
0
            return NULL;
547
0
        }
548
549
0
        node_attach((node_t)node, (node_t)key);
550
0
        node_attach((node_t)node, (node_t)val);
551
0
    }
552
553
0
    return node;
554
0
}
555
556
static plist_t parse_array_node(struct bplist_data *bplist, const char** bnode, uint64_t size)
557
0
{
558
0
    uint64_t j;
559
0
    uint64_t str_j = 0;
560
0
    uint64_t index1;
561
0
    plist_data_t data = plist_new_plist_data();
562
0
    const char *index1_ptr = NULL;
563
564
0
    data->type = PLIST_ARRAY;
565
0
    data->length = size;
566
567
0
    plist_t node = node_create(NULL, data);
568
569
0
    for (j = 0; j < data->length; j++) {
570
0
        str_j = j * bplist->ref_size;
571
0
        index1_ptr = (*bnode) + str_j;
572
573
0
        if (index1_ptr < bplist->data || index1_ptr + bplist->ref_size > bplist->offset_table) {
574
0
            plist_free(node);
575
0
            PLIST_BIN_ERR("%s: array item %" PRIu64 " is outside of valid range\n", __func__, j);
576
0
            return NULL;
577
0
        }
578
579
0
        index1 = UINT_TO_HOST(index1_ptr, bplist->ref_size);
580
581
0
        if (index1 >= bplist->num_objects) {
582
0
            plist_free(node);
583
0
            PLIST_BIN_ERR("%s: array item %" PRIu64 " object index (%" PRIu64 ") must be smaller than the number of objects (%" PRIu64 ")\n", __func__, j, index1, bplist->num_objects);
584
0
            return NULL;
585
0
        }
586
587
        /* process value node */
588
0
        plist_t val = parse_bin_node_at_index(bplist, index1);
589
0
        if (!val) {
590
0
            plist_free(node);
591
0
            return NULL;
592
0
        }
593
594
0
        node_attach((node_t)node, (node_t)val);
595
0
    }
596
597
0
    return node;
598
0
}
599
600
static plist_t parse_uid_node(const char **bnode, uint8_t size)
601
0
{
602
0
    plist_data_t data = plist_new_plist_data();
603
0
    size = size + 1;
604
0
    data->intval = UINT_TO_HOST(*bnode, size);
605
0
    if (data->intval > UINT32_MAX) {
606
0
        PLIST_BIN_ERR("%s: value %" PRIu64 " too large for UID node (must be <= %u)\n", __func__, (uint64_t)data->intval, UINT32_MAX);
607
0
        free(data);
608
0
        return NULL;
609
0
    }
610
611
0
    (*bnode) += size;
612
0
    data->type = PLIST_UID;
613
0
    data->length = sizeof(uint64_t);
614
615
0
    return node_create(NULL, data);
616
0
}
617
618
static plist_t parse_bin_node(struct bplist_data *bplist, const char** object)
619
0
{
620
0
    uint16_t type = 0;
621
0
    uint64_t size = 0;
622
0
    uint64_t pobject = 0;
623
0
    uint64_t poffset_table = (uint64_t)(uintptr_t)bplist->offset_table;
624
625
0
    if (!object)
626
0
        return NULL;
627
628
0
    type = (**object) & BPLIST_MASK;
629
0
    size = (**object) & BPLIST_FILL;
630
0
    (*object)++;
631
632
0
    if (size == BPLIST_FILL) {
633
0
        switch (type) {
634
0
        case BPLIST_DATA:
635
0
        case BPLIST_STRING:
636
0
        case BPLIST_UNICODE:
637
0
        case BPLIST_ARRAY:
638
0
        case BPLIST_SET:
639
0
        case BPLIST_DICT:
640
0
        {
641
0
            uint16_t next_size = **object & BPLIST_FILL;
642
0
            if ((**object & BPLIST_MASK) != BPLIST_INT) {
643
0
                PLIST_BIN_ERR("%s: invalid size node type for node type 0x%02x: found 0x%02x, expected 0x%02x\n", __func__, type, **object & BPLIST_MASK, BPLIST_INT);
644
0
                return NULL;
645
0
            }
646
0
            (*object)++;
647
0
            next_size = 1 << next_size;
648
0
            if (*object + next_size > bplist->offset_table) {
649
0
                PLIST_BIN_ERR("%s: size node data bytes for node type 0x%02x point outside of valid range\n", __func__, type);
650
0
                return NULL;
651
0
            }
652
0
            size = UINT_TO_HOST(*object, next_size);
653
0
            (*object) += next_size;
654
0
            break;
655
0
        }
656
0
        default:
657
0
            break;
658
0
        }
659
0
    }
660
661
0
    pobject = (uint64_t)(uintptr_t)*object;
662
663
0
    switch (type)
664
0
    {
665
666
0
    case BPLIST_NULL:
667
0
        switch (size)
668
0
        {
669
670
0
        case BPLIST_TRUE:
671
0
        {
672
0
            plist_data_t data = plist_new_plist_data();
673
0
            data->type = PLIST_BOOLEAN;
674
0
            data->boolval = TRUE;
675
0
            data->length = 1;
676
0
            return node_create(NULL, data);
677
0
        }
678
679
0
        case BPLIST_FALSE:
680
0
        {
681
0
            plist_data_t data = plist_new_plist_data();
682
0
            data->type = PLIST_BOOLEAN;
683
0
            data->boolval = FALSE;
684
0
            data->length = 1;
685
0
            return node_create(NULL, data);
686
0
        }
687
688
0
        case BPLIST_NULL:
689
0
        {
690
0
            plist_data_t data = plist_new_plist_data();
691
0
            data->type = PLIST_NULL;
692
0
            data->length = 0;
693
0
            return node_create(NULL, data);
694
0
        }
695
696
0
        default:
697
0
            return NULL;
698
0
        }
699
700
0
    case BPLIST_INT:
701
0
        if (pobject + (uint64_t)(1 << size) > poffset_table) {
702
0
            PLIST_BIN_ERR("%s: BPLIST_INT data bytes point outside of valid range\n", __func__);
703
0
            return NULL;
704
0
        }
705
0
        return parse_int_node(object, size);
706
707
0
    case BPLIST_REAL:
708
0
        if (pobject + (uint64_t)(1 << size) > poffset_table) {
709
0
            PLIST_BIN_ERR("%s: BPLIST_REAL data bytes point outside of valid range\n", __func__);
710
0
            return NULL;
711
0
        }
712
0
        return parse_real_node(object, size);
713
714
0
    case BPLIST_DATE:
715
0
        if (3 != size) {
716
0
            PLIST_BIN_ERR("%s: invalid data size for BPLIST_DATE node\n", __func__);
717
0
            return NULL;
718
0
        }
719
0
        if (pobject + (uint64_t)(1 << size) > poffset_table) {
720
0
            PLIST_BIN_ERR("%s: BPLIST_DATE data bytes point outside of valid range\n", __func__);
721
0
            return NULL;
722
0
        }
723
0
        return parse_date_node(object, size);
724
725
0
    case BPLIST_DATA:
726
0
        if (pobject + size < pobject || pobject + size > poffset_table) {
727
0
            PLIST_BIN_ERR("%s: BPLIST_DATA data bytes point outside of valid range\n", __func__);
728
0
            return NULL;
729
0
        }
730
0
        return parse_data_node(object, size);
731
732
0
    case BPLIST_STRING:
733
0
        if (pobject + size < pobject || pobject + size > poffset_table) {
734
0
            PLIST_BIN_ERR("%s: BPLIST_STRING data bytes point outside of valid range\n", __func__);
735
0
            return NULL;
736
0
        }
737
0
        return parse_string_node(object, size);
738
739
0
    case BPLIST_UNICODE:
740
0
        if (size*2 < size) {
741
0
            PLIST_BIN_ERR("%s: Integer overflow when calculating BPLIST_UNICODE data size.\n", __func__);
742
0
            return NULL;
743
0
        }
744
0
        if (pobject + size*2 < pobject || pobject + size*2 > poffset_table) {
745
0
            PLIST_BIN_ERR("%s: BPLIST_UNICODE data bytes point outside of valid range\n", __func__);
746
0
            return NULL;
747
0
        }
748
0
        return parse_unicode_node(object, size);
749
750
0
    case BPLIST_SET:
751
0
    case BPLIST_ARRAY:
752
0
        if (pobject + size < pobject || pobject + size > poffset_table) {
753
0
            PLIST_BIN_ERR("%s: BPLIST_ARRAY data bytes point outside of valid range\n", __func__);
754
0
            return NULL;
755
0
        }
756
0
        return parse_array_node(bplist, object, size);
757
758
0
    case BPLIST_UID:
759
0
        if (pobject + size+1 > poffset_table) {
760
0
            PLIST_BIN_ERR("%s: BPLIST_UID data bytes point outside of valid range\n", __func__);
761
0
            return NULL;
762
0
        }
763
0
        return parse_uid_node(object, size);
764
765
0
    case BPLIST_DICT:
766
0
        if (pobject + size < pobject || pobject + size > poffset_table) {
767
0
            PLIST_BIN_ERR("%s: BPLIST_DICT data bytes point outside of valid range\n", __func__);
768
0
            return NULL;
769
0
        }
770
0
        return parse_dict_node(bplist, object, size);
771
772
0
    default:
773
0
        PLIST_BIN_ERR("%s: unexpected node type 0x%02x\n", __func__, type);
774
0
        return NULL;
775
0
    }
776
0
    return NULL;
777
0
}
778
779
static plist_t parse_bin_node_at_index(struct bplist_data *bplist, uint32_t node_index)
780
0
{
781
0
    int i = 0;
782
0
    const char* ptr = NULL;
783
0
    plist_t plist = NULL;
784
0
    const char* idx_ptr = NULL;
785
786
0
    if (node_index >= bplist->num_objects) {
787
0
        PLIST_BIN_ERR("node index (%u) must be smaller than the number of objects (%" PRIu64 ")\n", node_index, bplist->num_objects);
788
0
        return NULL;
789
0
    }
790
791
0
    idx_ptr = bplist->offset_table + node_index * bplist->offset_size;
792
0
    if (idx_ptr < bplist->offset_table ||
793
0
        idx_ptr >= bplist->offset_table + bplist->num_objects * bplist->offset_size) {
794
0
        PLIST_BIN_ERR("node index %u points outside of valid range\n", node_index);
795
0
        return NULL;
796
0
    }
797
798
0
    ptr = bplist->data + UINT_TO_HOST(idx_ptr, bplist->offset_size);
799
    /* make sure the node offset is in a sane range */
800
0
    if ((ptr < bplist->data+BPLIST_MAGIC_SIZE+BPLIST_VERSION_SIZE) || (ptr >= bplist->offset_table)) {
801
0
        PLIST_BIN_ERR("offset for node index %u points outside of valid range\n", node_index);
802
0
        return NULL;
803
0
    }
804
805
    /* store node_index for current recursion level */
806
0
    if ((uint32_t)ptr_array_size(bplist->used_indexes) < bplist->level+1) {
807
0
        while ((uint32_t)ptr_array_size(bplist->used_indexes) < bplist->level+1) {
808
0
            ptr_array_add(bplist->used_indexes, (void*)(uintptr_t)node_index);
809
0
        }
810
0
    } else {
811
0
  ptr_array_set(bplist->used_indexes, (void*)(uintptr_t)node_index, bplist->level);
812
0
    }
813
814
    /* recursion check */
815
0
    if (bplist->level > 0) {
816
0
        for (i = bplist->level-1; i >= 0; i--) {
817
0
            void *node_i = ptr_array_index(bplist->used_indexes, i);
818
0
            void *node_level = ptr_array_index(bplist->used_indexes, bplist->level);
819
0
            if (node_i == node_level) {
820
0
                PLIST_BIN_ERR("recursion detected in binary plist\n");
821
0
                return NULL;
822
0
            }
823
0
        }
824
0
    }
825
826
    /* finally parse node */
827
0
    bplist->level++;
828
0
    plist = parse_bin_node(bplist, &ptr);
829
0
    bplist->level--;
830
0
    return plist;
831
0
}
832
833
plist_err_t plist_from_bin(const char *plist_bin, uint32_t length, plist_t * plist)
834
0
{
835
0
    bplist_trailer_t *trailer = NULL;
836
0
    uint8_t offset_size = 0;
837
0
    uint8_t ref_size = 0;
838
0
    uint64_t num_objects = 0;
839
0
    uint64_t root_object = 0;
840
0
    const char *offset_table = NULL;
841
0
    uint64_t offset_table_size = 0;
842
0
    const char *start_data = NULL;
843
0
    const char *end_data = NULL;
844
845
0
    if (!plist) {
846
0
        return PLIST_ERR_INVALID_ARG;
847
0
    }
848
0
    *plist = NULL;
849
0
    if (!plist_bin || length == 0) {
850
0
        return PLIST_ERR_INVALID_ARG;
851
0
    }
852
853
    //first check we have enough data
854
0
    if (!(length >= BPLIST_MAGIC_SIZE + BPLIST_VERSION_SIZE + sizeof(bplist_trailer_t))) {
855
0
        PLIST_BIN_ERR("plist data is to small to hold a binary plist\n");
856
0
        return PLIST_ERR_PARSE;
857
0
    }
858
    //check that plist_bin in actually a plist
859
0
    if (memcmp(plist_bin, BPLIST_MAGIC, BPLIST_MAGIC_SIZE) != 0) {
860
0
        PLIST_BIN_ERR("bplist magic mismatch\n");
861
0
        return PLIST_ERR_PARSE;
862
0
    }
863
    //check for known version
864
0
    if (memcmp(plist_bin + BPLIST_MAGIC_SIZE, BPLIST_VERSION, BPLIST_VERSION_SIZE) != 0) {
865
0
        PLIST_BIN_ERR("unsupported binary plist version '%.2s\n", plist_bin+BPLIST_MAGIC_SIZE);
866
0
        return PLIST_ERR_PARSE;
867
0
    }
868
869
0
    start_data = plist_bin + BPLIST_MAGIC_SIZE + BPLIST_VERSION_SIZE;
870
0
    end_data = plist_bin + length - sizeof(bplist_trailer_t);
871
872
    //now parse trailer
873
0
    trailer = (bplist_trailer_t*)end_data;
874
875
0
    offset_size = trailer->offset_size;
876
0
    ref_size = trailer->ref_size;
877
0
    num_objects = be64toh(trailer->num_objects);
878
0
    root_object = be64toh(trailer->root_object_index);
879
0
    offset_table = (char *)(plist_bin + be64toh(trailer->offset_table_offset));
880
881
0
    if (num_objects == 0) {
882
0
        PLIST_BIN_ERR("number of objects must be larger than 0\n");
883
0
        return PLIST_ERR_PARSE;
884
0
    }
885
886
0
    if (offset_size == 0) {
887
0
        PLIST_BIN_ERR("offset size in trailer must be larger than 0\n");
888
0
        return PLIST_ERR_PARSE;
889
0
    }
890
891
0
    if (ref_size == 0) {
892
0
        PLIST_BIN_ERR("object reference size in trailer must be larger than 0\n");
893
0
        return PLIST_ERR_PARSE;
894
0
    }
895
896
0
    if (root_object >= num_objects) {
897
0
        PLIST_BIN_ERR("root object index (%" PRIu64 ") must be smaller than number of objects (%" PRIu64 ")\n", root_object, num_objects);
898
0
        return PLIST_ERR_PARSE;
899
0
    }
900
901
0
    if (offset_table < start_data || offset_table >= end_data) {
902
0
        PLIST_BIN_ERR("offset table offset points outside of valid range\n");
903
0
        return PLIST_ERR_PARSE;
904
0
    }
905
906
0
    if (uint64_mul_overflow(num_objects, offset_size, &offset_table_size)) {
907
0
        PLIST_BIN_ERR("integer overflow when calculating offset table size\n");
908
0
        return PLIST_ERR_PARSE;
909
0
    }
910
911
0
    if (offset_table_size > (uint64_t)(end_data - offset_table)) {
912
0
        PLIST_BIN_ERR("offset table points outside of valid range\n");
913
0
        return PLIST_ERR_PARSE;
914
0
    }
915
916
0
    struct bplist_data bplist;
917
0
    bplist.data = plist_bin;
918
0
    bplist.size = length;
919
0
    bplist.num_objects = num_objects;
920
0
    bplist.ref_size = ref_size;
921
0
    bplist.offset_size = offset_size;
922
0
    bplist.offset_table = offset_table;
923
0
    bplist.level = 0;
924
0
    bplist.used_indexes = ptr_array_new(16);
925
926
0
    if (!bplist.used_indexes) {
927
0
        PLIST_BIN_ERR("failed to create array to hold used node indexes. Out of memory?\n");
928
0
        return PLIST_ERR_NO_MEM;
929
0
    }
930
931
0
    *plist = parse_bin_node_at_index(&bplist, root_object);
932
933
0
    ptr_array_free(bplist.used_indexes);
934
935
0
    if (!*plist) {
936
0
        return PLIST_ERR_PARSE;
937
0
    }
938
939
0
    return PLIST_ERR_SUCCESS;
940
0
}
941
942
static unsigned int plist_data_hash(const void* key)
943
0
{
944
0
    plist_data_t data = plist_get_data((plist_t) key);
945
946
0
    unsigned int hash = data->type;
947
0
    unsigned int i = 0;
948
949
0
    char *buff = NULL;
950
0
    unsigned int size = 0;
951
952
0
    switch (data->type)
953
0
    {
954
0
    case PLIST_BOOLEAN:
955
0
    case PLIST_NULL:
956
0
    case PLIST_INT:
957
0
    case PLIST_REAL:
958
0
    case PLIST_DATE:
959
0
    case PLIST_UID:
960
0
        buff = (char *) &data->intval;  //works also for real as we use an union
961
0
        size = 8;
962
0
        break;
963
0
    case PLIST_KEY:
964
0
    case PLIST_STRING:
965
0
        buff = data->strval;
966
0
        size = data->length;
967
0
        break;
968
0
    case PLIST_DATA:
969
0
    case PLIST_ARRAY:
970
0
    case PLIST_DICT:
971
        //for these types only hash pointer
972
0
        buff = (char *) &key;
973
0
        size = sizeof(const void*);
974
0
        break;
975
0
    default:
976
0
        break;
977
0
    }
978
979
    // now perform hash using djb2 hashing algorithm
980
    // see: http://www.cse.yorku.ca/~oz/hash.html
981
0
    hash += 5381;
982
0
    for (i = 0; i < size; buff++, i++) {
983
0
        hash = ((hash << 5) + hash) + *buff;
984
0
    }
985
986
0
    return hash;
987
0
}
988
989
struct serialize_s
990
{
991
    ptrarray_t* objects;
992
    hashtable_t* ref_table;
993
};
994
995
static void serialize_plist(node_t node, void* data)
996
0
{
997
0
    uint64_t *index_val = NULL;
998
0
    struct serialize_s *ser = (struct serialize_s *) data;
999
0
    uint64_t current_index = ser->objects->len;
1000
1001
    //first check that node is not yet in objects
1002
0
    void* val = hash_table_lookup(ser->ref_table, node);
1003
0
    if (val)
1004
0
    {
1005
        //data is already in table
1006
0
        return;
1007
0
    }
1008
    //insert new ref
1009
0
    index_val = (uint64_t *) malloc(sizeof(uint64_t));
1010
0
    assert(index_val != NULL);
1011
0
    *index_val = current_index;
1012
0
    hash_table_insert(ser->ref_table, node, index_val);
1013
1014
    //now append current node to object array
1015
0
    ptr_array_add(ser->objects, node);
1016
1017
    //now recurse on children
1018
0
    node_t ch;
1019
0
    for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) {
1020
0
        serialize_plist(ch, data);
1021
0
    }
1022
0
}
1023
1024
0
#define Log2(x) ((x) == 8 ? 3 : ((x) == 4 ? 2 : ((x) == 2 ? 1 : 0)))
1025
1026
static void write_int(bytearray_t * bplist, uint64_t val)
1027
0
{
1028
0
    int size = get_needed_bytes(val);
1029
0
    uint8_t sz;
1030
    //do not write 3bytes int node
1031
0
    if (size == 3)
1032
0
        size++;
1033
0
    sz = BPLIST_INT | Log2(size);
1034
1035
0
    val = be64toh(val);
1036
0
    byte_array_append(bplist, &sz, 1);
1037
0
    byte_array_append(bplist, (uint8_t*)&val + (8-size), size);
1038
0
}
1039
1040
static void write_uint(bytearray_t * bplist, uint64_t val)
1041
0
{
1042
0
    uint8_t sz = BPLIST_INT | 4;
1043
0
    uint64_t zero = 0;
1044
1045
0
    val = be64toh(val);
1046
0
    byte_array_append(bplist, &sz, 1);
1047
0
    byte_array_append(bplist, &zero, sizeof(uint64_t));
1048
0
    byte_array_append(bplist, &val, sizeof(uint64_t));
1049
0
}
1050
1051
static void write_real(bytearray_t * bplist, double val)
1052
0
{
1053
0
    int size = get_real_bytes(val);  //cheat to know used space
1054
0
    uint8_t buff[16];
1055
0
    buff[7] = BPLIST_REAL | Log2(size);
1056
0
    if (size == sizeof(float)) {
1057
0
        float floatval = (float)val;
1058
0
        uint32_t intval;
1059
0
        memcpy(&intval, &floatval, sizeof(float));
1060
0
        *(uint32_t*)(buff+8) = float_bswap32(intval);
1061
0
    } else {
1062
0
        uint64_t intval;
1063
0
        memcpy(&intval, &val, sizeof(double));
1064
0
        *(uint64_t*)(buff+8) = float_bswap64(intval);
1065
0
    }
1066
0
    byte_array_append(bplist, buff+7, size+1);
1067
0
}
1068
1069
static void write_date(bytearray_t * bplist, double val)
1070
0
{
1071
0
    uint64_t intval;
1072
0
    memcpy(&intval, &val, sizeof(double));
1073
0
    uint8_t buff[16];
1074
0
    buff[7] = BPLIST_DATE | 3;
1075
0
    *(uint64_t*)(buff+8) = float_bswap64(intval);
1076
0
    byte_array_append(bplist, buff+7, 9);
1077
0
}
1078
1079
static void write_raw_data(bytearray_t * bplist, uint8_t mark, uint8_t * val, uint64_t size)
1080
0
{
1081
0
    uint8_t marker = mark | (size < 15 ? size : 0xf);
1082
0
    byte_array_append(bplist, &marker, sizeof(uint8_t));
1083
0
    if (size >= 15) {
1084
0
        write_int(bplist, size);
1085
0
    }
1086
0
    if (BPLIST_UNICODE==mark) size <<= 1;
1087
0
    byte_array_append(bplist, val, size);
1088
0
}
1089
1090
static void write_data(bytearray_t * bplist, uint8_t * val, uint64_t size)
1091
0
{
1092
0
    write_raw_data(bplist, BPLIST_DATA, val, size);
1093
0
}
1094
1095
static void write_string(bytearray_t * bplist, char *val, uint64_t size)
1096
0
{
1097
0
    write_raw_data(bplist, BPLIST_STRING, (uint8_t *) val, size);
1098
0
}
1099
1100
static uint16_t *plist_utf8_to_utf16be(char *unistr, long size, long *items_read, long *items_written)
1101
0
{
1102
0
  uint16_t *outbuf;
1103
0
  int p = 0;
1104
0
  long i = 0;
1105
1106
0
  unsigned char c0;
1107
0
  unsigned char c1;
1108
0
  unsigned char c2;
1109
0
  unsigned char c3;
1110
1111
0
  uint32_t w;
1112
1113
0
  outbuf = (uint16_t*)malloc(((size*2)+1)*sizeof(uint16_t));
1114
0
  if (!outbuf) {
1115
0
    PLIST_BIN_ERR("%s: Could not allocate %" PRIu64 " bytes\n", __func__, (uint64_t)((size*2)+1)*sizeof(uint16_t));
1116
0
    return NULL;
1117
0
  }
1118
1119
0
  while (i < size) {
1120
0
    c0 = unistr[i];
1121
0
    c1 = (i < size-1) ? unistr[i+1] : 0;
1122
0
    c2 = (i < size-2) ? unistr[i+2] : 0;
1123
0
    c3 = (i < size-3) ? unistr[i+3] : 0;
1124
0
    if ((c0 >= 0xF0) && (i < size-3) && (c1 >= 0x80) && (c2 >= 0x80) && (c3 >= 0x80)) {
1125
      // 4 byte sequence.  Need to generate UTF-16 surrogate pair
1126
0
      w = ((((c0 & 7) << 18) + ((c1 & 0x3F) << 12) + ((c2 & 0x3F) << 6) + (c3 & 0x3F)) & 0x1FFFFF) - 0x010000;
1127
0
      outbuf[p++] = be16toh(0xD800 + (w >> 10));
1128
0
      outbuf[p++] = be16toh(0xDC00 + (w & 0x3FF));
1129
0
      i+=4;
1130
0
    } else if ((c0 >= 0xE0) && (i < size-2) && (c1 >= 0x80) && (c2 >= 0x80)) {
1131
      // 3 byte sequence
1132
0
      outbuf[p++] = be16toh(((c2 & 0x3F) + ((c1 & 3) << 6)) + (((c1 >> 2) & 15) << 8) + ((c0 & 15) << 12));
1133
0
      i+=3;
1134
0
    } else if ((c0 >= 0xC0) && (i < size-1) && (c1 >= 0x80)) {
1135
      // 2 byte sequence
1136
0
      outbuf[p++] = be16toh(((c1 & 0x3F) + ((c0 & 3) << 6)) + (((c0 >> 2) & 7) << 8));
1137
0
      i+=2;
1138
0
    } else if (c0 < 0x80) {
1139
      // 1 byte sequence
1140
0
      outbuf[p++] = be16toh(c0);
1141
0
      i+=1;
1142
0
    } else {
1143
      // invalid character
1144
0
      PLIST_BIN_ERR("%s: invalid utf8 sequence in string at index %lu\n", __func__, i);
1145
0
      break;
1146
0
    }
1147
0
  }
1148
0
  if (items_read) {
1149
0
    *items_read = i;
1150
0
  }
1151
0
  if (items_written) {
1152
0
    *items_written = p;
1153
0
  }
1154
0
  outbuf[p] = 0;
1155
1156
0
  return outbuf;
1157
0
}
1158
1159
static void write_unicode(bytearray_t * bplist, char *val, uint64_t size)
1160
0
{
1161
0
    long items_read = 0;
1162
0
    long items_written = 0;
1163
0
    uint16_t *unicodestr = NULL;
1164
1165
0
    unicodestr = plist_utf8_to_utf16be(val, size, &items_read, &items_written);
1166
0
    write_raw_data(bplist, BPLIST_UNICODE, (uint8_t*)unicodestr, items_written);
1167
0
    free(unicodestr);
1168
0
}
1169
1170
static void write_array(bytearray_t * bplist, node_t node, hashtable_t* ref_table, uint8_t ref_size)
1171
0
{
1172
0
    node_t cur = NULL;
1173
0
    uint64_t i = 0;
1174
1175
0
    uint64_t size = node_n_children(node);
1176
0
    uint8_t marker = BPLIST_ARRAY | (size < 15 ? size : 0xf);
1177
0
    byte_array_append(bplist, &marker, sizeof(uint8_t));
1178
0
    if (size >= 15) {
1179
0
        write_int(bplist, size);
1180
0
    }
1181
1182
0
    for (i = 0, cur = node_first_child(node); cur && i < size; cur = node_next_sibling(cur), i++) {
1183
0
        uint64_t idx = *(uint64_t *) (hash_table_lookup(ref_table, cur));
1184
0
        idx = be64toh(idx);
1185
0
        byte_array_append(bplist, (uint8_t*)&idx + (sizeof(uint64_t) - ref_size), ref_size);
1186
0
    }
1187
0
}
1188
1189
static void write_dict(bytearray_t * bplist, node_t node, hashtable_t* ref_table, uint8_t ref_size)
1190
0
{
1191
0
    node_t cur = NULL;
1192
0
    uint64_t i = 0;
1193
1194
0
    uint64_t size = node_n_children(node) / 2;
1195
0
    uint8_t marker = BPLIST_DICT | (size < 15 ? size : 0xf);
1196
0
    byte_array_append(bplist, &marker, sizeof(uint8_t));
1197
0
    if (size >= 15) {
1198
0
        write_int(bplist, size);
1199
0
    }
1200
1201
0
    for (i = 0, cur = node_first_child(node); cur && i < size; cur = node_next_sibling(node_next_sibling(cur)), i++) {
1202
0
        uint64_t idx1 = *(uint64_t *) (hash_table_lookup(ref_table, cur));
1203
0
        idx1 = be64toh(idx1);
1204
0
        byte_array_append(bplist, (uint8_t*)&idx1 + (sizeof(uint64_t) - ref_size), ref_size);
1205
0
    }
1206
1207
0
    for (i = 0, cur = node_first_child(node); cur && i < size; cur = node_next_sibling(node_next_sibling(cur)), i++) {
1208
0
        uint64_t idx2 = *(uint64_t *) (hash_table_lookup(ref_table, cur->next));
1209
0
        idx2 = be64toh(idx2);
1210
0
        byte_array_append(bplist, (uint8_t*)&idx2 + (sizeof(uint64_t) - ref_size), ref_size);
1211
0
    }
1212
0
}
1213
1214
static void write_uid(bytearray_t * bplist, uint64_t val)
1215
0
{
1216
0
    val = (uint32_t)val;
1217
0
    int size = get_needed_bytes(val);
1218
0
    uint8_t sz;
1219
    //do not write 3bytes int node
1220
0
    if (size == 3)
1221
0
        size++;
1222
0
    sz = BPLIST_UID | (size-1); // yes, this is what Apple does...
1223
1224
0
    val = be64toh(val);
1225
0
    byte_array_append(bplist, &sz, 1);
1226
0
    byte_array_append(bplist, (uint8_t*)&val + (8-size), size);
1227
0
}
1228
1229
static int is_ascii_string(char* s, int len)
1230
0
{
1231
0
  int ret = 1, i = 0;
1232
0
  for(i = 0; i < len; i++)
1233
0
  {
1234
0
      if ( !isascii( s[i] ) )
1235
0
      {
1236
0
          ret = 0;
1237
0
          break;
1238
0
      }
1239
0
  }
1240
0
  return ret;
1241
0
}
1242
1243
plist_err_t plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length)
1244
0
{
1245
0
    ptrarray_t* objects = NULL;
1246
0
    hashtable_t* ref_table = NULL;
1247
0
    struct serialize_s ser_s;
1248
0
    uint8_t offset_size = 0;
1249
0
    uint8_t ref_size = 0;
1250
0
    uint64_t num_objects = 0;
1251
0
    uint64_t root_object = 0;
1252
0
    uint64_t offset_table_index = 0;
1253
0
    bytearray_t *bplist_buff = NULL;
1254
0
    uint64_t i = 0;
1255
0
    uint64_t buff_len = 0;
1256
0
    uint64_t *offsets = NULL;
1257
0
    bplist_trailer_t trailer;
1258
0
    uint64_t objects_len = 0;
1259
1260
    //check for valid input
1261
0
    if (!plist || !plist_bin || !length) {
1262
0
        return PLIST_ERR_INVALID_ARG;
1263
0
    }
1264
1265
    //list of objects
1266
0
    objects = ptr_array_new(4096);
1267
0
    if (!objects) {
1268
0
        return PLIST_ERR_NO_MEM;
1269
0
    }
1270
    //hashtable to write only once same nodes
1271
0
    ref_table = hash_table_new(plist_data_hash, plist_data_compare, free);
1272
0
    if (!ref_table) {
1273
0
        ptr_array_free(objects);
1274
0
        return PLIST_ERR_NO_MEM;
1275
0
    }
1276
1277
    //serialize plist
1278
0
    ser_s.objects = objects;
1279
0
    ser_s.ref_table = ref_table;
1280
0
    serialize_plist((node_t)plist, &ser_s);
1281
1282
    //now stream to output buffer
1283
0
    offset_size = 0;      //unknown yet
1284
0
    objects_len = objects->len;
1285
0
    ref_size = get_needed_bytes(objects_len);
1286
0
    num_objects = objects->len;
1287
0
    root_object = 0;      //root is first in list
1288
0
    offset_table_index = 0;   //unknown yet
1289
1290
    //figure out the storage size required
1291
0
    uint64_t req = 0;
1292
0
    for (i = 0; i < num_objects; i++)
1293
0
    {
1294
0
        node_t node = (node_t)ptr_array_index(objects, i);
1295
0
        plist_data_t data = plist_get_data(node);
1296
0
        uint64_t size;
1297
0
        uint8_t bsize;
1298
0
        switch (data->type)
1299
0
        {
1300
0
        case PLIST_NULL:
1301
0
        case PLIST_BOOLEAN:
1302
0
            req += 1;
1303
0
            break;
1304
0
        case PLIST_KEY:
1305
0
        case PLIST_STRING:
1306
0
            req += 1;
1307
0
            if (data->length >= 15) {
1308
0
                bsize = get_needed_bytes(data->length);
1309
0
                if (bsize == 3) bsize = 4;
1310
0
                req += 1;
1311
0
                req += bsize;
1312
0
            }
1313
0
            if ( is_ascii_string(data->strval, data->length) )
1314
0
            {
1315
0
                req += data->length;
1316
0
            }
1317
0
            else
1318
0
            {
1319
0
                req += data->length * 2;
1320
0
            }
1321
0
            break;
1322
0
        case PLIST_REAL:
1323
0
            size = get_real_bytes(data->realval);
1324
0
            req += 1;
1325
0
            req += size;
1326
0
            break;
1327
0
        case PLIST_DATE:
1328
0
            req += 9;
1329
0
            break;
1330
0
        case PLIST_ARRAY:
1331
0
            size = node_n_children(node);
1332
0
            req += 1;
1333
0
            if (size >= 15) {
1334
0
                bsize = get_needed_bytes(size);
1335
0
                if (bsize == 3) bsize = 4;
1336
0
                req += 1;
1337
0
                req += bsize;
1338
0
            }
1339
0
            req += size * ref_size;
1340
0
            break;
1341
0
        case PLIST_DICT:
1342
0
            size = node_n_children(node) / 2;
1343
0
            req += 1;
1344
0
            if (size >= 15) {
1345
0
                bsize = get_needed_bytes(size);
1346
0
                if (bsize == 3) bsize = 4;
1347
0
                req += 1;
1348
0
                req += bsize;
1349
0
            }
1350
0
            req += size * 2 * ref_size;
1351
0
            break;
1352
0
        default:
1353
0
            size = data->length;
1354
0
            req += 1;
1355
0
            if (size >= 15) {
1356
0
                bsize = get_needed_bytes(size);
1357
0
                if (bsize == 3) bsize = 4;
1358
0
                req += 1;
1359
0
                req += bsize;
1360
0
            }
1361
0
            req += data->length;
1362
0
            break;
1363
0
        }
1364
0
    }
1365
    // add size of magic
1366
0
    req += BPLIST_MAGIC_SIZE;
1367
0
    req += BPLIST_VERSION_SIZE;
1368
    // add size of offset table
1369
0
    req += get_needed_bytes(req) * num_objects;
1370
    // add size of trailer
1371
0
    req += sizeof(bplist_trailer_t);
1372
1373
    //setup a dynamic bytes array to store bplist in
1374
0
    bplist_buff = byte_array_new(req);
1375
0
    if (!bplist_buff) {
1376
0
        ptr_array_free(objects);
1377
0
        hash_table_destroy(ref_table);
1378
0
        return PLIST_ERR_NO_MEM;
1379
0
    }
1380
1381
    //set magic number and version
1382
0
    byte_array_append(bplist_buff, BPLIST_MAGIC, BPLIST_MAGIC_SIZE);
1383
0
    byte_array_append(bplist_buff, BPLIST_VERSION, BPLIST_VERSION_SIZE);
1384
1385
    //write objects and table
1386
0
    offsets = (uint64_t *) malloc(num_objects * sizeof(uint64_t));
1387
0
    assert(offsets != NULL);
1388
0
    for (i = 0; i < num_objects; i++)
1389
0
    {
1390
1391
0
        plist_data_t data = plist_get_data(ptr_array_index(objects, i));
1392
0
        offsets[i] = bplist_buff->len;
1393
1394
0
        switch (data->type)
1395
0
        {
1396
0
        case PLIST_NULL: {
1397
0
            uint8_t b = 0;
1398
0
            byte_array_append(bplist_buff, &b, 1);
1399
0
            break;
1400
0
        }
1401
0
        case PLIST_BOOLEAN: {
1402
0
            uint8_t b = data->boolval ? BPLIST_TRUE : BPLIST_FALSE;
1403
0
            byte_array_append(bplist_buff, &b, 1);
1404
0
            break;
1405
0
        }
1406
0
        case PLIST_INT:
1407
0
            if (data->length == 16) {
1408
0
                write_uint(bplist_buff, data->intval);
1409
0
            } else {
1410
0
                write_int(bplist_buff, data->intval);
1411
0
            }
1412
0
            break;
1413
1414
0
        case PLIST_REAL:
1415
0
            write_real(bplist_buff, data->realval);
1416
0
            break;
1417
1418
0
        case PLIST_KEY:
1419
0
        case PLIST_STRING:
1420
0
            if ( is_ascii_string(data->strval, data->length) )
1421
0
            {
1422
0
                write_string(bplist_buff, data->strval, data->length);
1423
0
            }
1424
0
            else
1425
0
            {
1426
0
                write_unicode(bplist_buff, data->strval, data->length);
1427
0
            }
1428
0
            break;
1429
0
        case PLIST_DATA:
1430
0
            write_data(bplist_buff, data->buff, data->length);
1431
0
            break;
1432
0
        case PLIST_ARRAY:
1433
0
            write_array(bplist_buff, (node_t)ptr_array_index(objects, i), ref_table, ref_size);
1434
0
            break;
1435
0
        case PLIST_DICT:
1436
0
            write_dict(bplist_buff, (node_t)ptr_array_index(objects, i), ref_table, ref_size);
1437
0
            break;
1438
0
        case PLIST_DATE:
1439
0
            write_date(bplist_buff, data->realval);
1440
0
            break;
1441
0
        case PLIST_UID:
1442
0
            write_uid(bplist_buff, data->intval);
1443
0
            break;
1444
0
        default:
1445
0
            break;
1446
0
        }
1447
0
    }
1448
1449
    //free intermediate objects
1450
0
    ptr_array_free(objects);
1451
0
    hash_table_destroy(ref_table);
1452
1453
    //write offsets
1454
0
    buff_len = bplist_buff->len;
1455
0
    offset_size = get_needed_bytes(buff_len);
1456
0
    offset_table_index = bplist_buff->len;
1457
0
    for (i = 0; i < num_objects; i++) {
1458
0
        uint64_t offset = be64toh(offsets[i]);
1459
0
        byte_array_append(bplist_buff, (uint8_t*)&offset + (sizeof(uint64_t) - offset_size), offset_size);
1460
0
    }
1461
0
    free(offsets);
1462
1463
    //setup trailer
1464
0
    memset(trailer.unused, '\0', sizeof(trailer.unused));
1465
0
    trailer.offset_size = offset_size;
1466
0
    trailer.ref_size = ref_size;
1467
0
    trailer.num_objects = be64toh(num_objects);
1468
0
    trailer.root_object_index = be64toh(root_object);
1469
0
    trailer.offset_table_offset = be64toh(offset_table_index);
1470
1471
0
    byte_array_append(bplist_buff, &trailer, sizeof(bplist_trailer_t));
1472
1473
    //set output buffer and size
1474
0
    *plist_bin = (char*)bplist_buff->data;
1475
0
    *length = bplist_buff->len;
1476
1477
0
    bplist_buff->data = NULL; // make sure we don't free the output buffer
1478
0
    byte_array_free(bplist_buff);
1479
1480
0
    return PLIST_ERR_SUCCESS;
1481
0
}