Coverage Report

Created: 2026-01-16 06:16

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
0
#define PLIST_BIN_WRITE_ERR(...) if (plist_bin_debug) { fprintf(stderr, "libplist[binwriter] ERROR: " __VA_ARGS__); }
248
#else
249
#define PLIST_BIN_ERR(...)
250
#define PLIST_BIN_WRITE_ERR(...)
251
#endif
252
253
void plist_bin_init(void)
254
2
{
255
    /* init binary plist stuff */
256
2
#ifdef DEBUG
257
2
    char *env_debug = getenv("PLIST_BIN_DEBUG");
258
2
    if (env_debug && !strcmp(env_debug, "1")) {
259
0
        plist_bin_debug = 1;
260
0
    }
261
2
#endif
262
2
}
263
264
void plist_bin_deinit(void)
265
0
{
266
    /* deinit binary plist stuff */
267
0
}
268
269
void plist_bin_set_debug(int debug)
270
0
{
271
0
#if DEBUG
272
0
    plist_bin_debug = debug;
273
0
#endif
274
0
}
275
276
static plist_t parse_bin_node_at_index(struct bplist_data *bplist, uint32_t node_index);
277
278
static plist_t parse_int_node(const char **bnode, uint8_t size)
279
0
{
280
0
    plist_data_t data = plist_new_plist_data();
281
282
0
    size = 1 << size;     // make length less misleading
283
0
    switch (size)
284
0
    {
285
0
    case sizeof(uint8_t):
286
0
    case sizeof(uint16_t):
287
0
    case sizeof(uint32_t):
288
0
    case sizeof(uint64_t):
289
0
        data->length = sizeof(uint64_t);
290
0
        break;
291
0
    case 16:
292
0
        data->length = size;
293
0
        break;
294
0
    default:
295
0
        free(data);
296
0
        PLIST_BIN_ERR("%s: Invalid byte size for integer node\n", __func__);
297
0
        return NULL;
298
0
    };
299
300
0
    data->intval = UINT_TO_HOST(*bnode, size);
301
302
0
    (*bnode) += size;
303
0
    data->type = PLIST_INT;
304
305
0
    return node_create(NULL, data);
306
0
}
307
308
static plist_t parse_real_node(const char **bnode, uint8_t size)
309
0
{
310
0
    plist_data_t data = plist_new_plist_data();
311
312
0
    size = 1 << size;     // make length less misleading
313
0
    switch (size)
314
0
    {
315
0
    case sizeof(uint32_t):
316
0
    {
317
0
        uint32_t ival;
318
0
        memcpy(&ival, *bnode, sizeof(uint32_t));
319
0
        ival = float_bswap32(ival);
320
0
        float fval;
321
0
        memcpy(&fval, &ival, sizeof(float));
322
0
        data->realval = fval;
323
0
    }
324
0
    break;
325
326
0
    case sizeof(uint64_t):
327
0
    {
328
0
        uint64_t ival;
329
0
        memcpy(&ival, *bnode, sizeof(uint64_t));
330
0
        ival = float_bswap64(ival);
331
0
        memcpy(&data->realval, &ival, sizeof(double));
332
0
        break;
333
0
    }
334
335
0
    default:
336
0
        free(data);
337
0
        PLIST_BIN_ERR("%s: Invalid byte size for real node\n", __func__);
338
0
        return NULL;
339
0
    }
340
0
    data->type = PLIST_REAL;
341
0
    data->length = sizeof(double);
342
343
0
    return node_create(NULL, data);
344
0
}
345
346
static plist_t parse_date_node(const char **bnode, uint8_t size)
347
0
{
348
0
    plist_t node = parse_real_node(bnode, size);
349
0
    plist_data_t data = plist_get_data(node);
350
351
0
    data->type = PLIST_DATE;
352
353
0
    return node;
354
0
}
355
356
static plist_t parse_string_node(const char **bnode, uint64_t size)
357
0
{
358
0
    plist_data_t data = plist_new_plist_data();
359
360
0
    data->type = PLIST_STRING;
361
0
    data->strval = (char *) malloc(sizeof(char) * (size + 1));
362
0
    if (!data->strval) {
363
0
        plist_free_data(data);
364
0
        PLIST_BIN_ERR("%s: Could not allocate %" PRIu64 " bytes\n", __func__, sizeof(char) * (size + 1));
365
0
        return NULL;
366
0
    }
367
0
    memcpy(data->strval, *bnode, size);
368
0
    data->strval[size] = '\0';
369
0
    data->length = strlen(data->strval);
370
371
0
    return node_create(NULL, data);
372
0
}
373
374
static char *plist_utf16be_to_utf8(uint16_t *unistr, size_t len, size_t *items_read, size_t *items_written)
375
0
{
376
0
  if (!unistr || (len <= 0)) return NULL;
377
0
  char* outbuf;
378
0
  char* outbuf_new;
379
0
  size_t p = 0;
380
0
  size_t i = 0;
381
382
0
  uint16_t wc;
383
0
  uint32_t w;
384
0
  int read_lead_surrogate = 0;
385
386
  /* allocate with enough space */
387
0
  outbuf = (char*)malloc(4*(len+1));
388
0
  if (!outbuf) {
389
0
    PLIST_BIN_ERR("%s: Could not allocate %" PRIu64 " bytes\n", __func__, (uint64_t)(4*(len+1)));
390
0
    return NULL;
391
0
  }
392
393
0
  while (i < len) {
394
0
    wc = UINT_TO_HOST(unistr + i, sizeof(wc));
395
0
    i++;
396
0
    if (wc >= 0xD800 && wc <= 0xDBFF) {
397
0
      if (!read_lead_surrogate) {
398
0
        read_lead_surrogate = 1;
399
0
        w = 0x010000 + ((wc & 0x3FF) << 10);
400
0
      } else {
401
        // This is invalid, the next 16 bit char should be a trail surrogate.
402
        // Handling error by skipping.
403
0
        read_lead_surrogate = 0;
404
0
      }
405
0
    } else if (wc >= 0xDC00 && wc <= 0xDFFF) {
406
0
      if (read_lead_surrogate) {
407
0
        read_lead_surrogate = 0;
408
0
        w = w | (wc & 0x3FF);
409
0
        outbuf[p++] = (char)(0xF0 + ((w >> 18) & 0x7));
410
0
        outbuf[p++] = (char)(0x80 + ((w >> 12) & 0x3F));
411
0
        outbuf[p++] = (char)(0x80 + ((w >> 6) & 0x3F));
412
0
        outbuf[p++] = (char)(0x80 + (w & 0x3F));
413
0
      } else {
414
        // This is invalid.  A trail surrogate should always follow a lead surrogate.
415
        // Handling error by skipping
416
0
      }
417
0
    } else if (wc >= 0x800) {
418
0
      outbuf[p++] = (char)(0xE0 + ((wc >> 12) & 0xF));
419
0
      outbuf[p++] = (char)(0x80 + ((wc >> 6) & 0x3F));
420
0
      outbuf[p++] = (char)(0x80 + (wc & 0x3F));
421
0
    } else if (wc >= 0x80) {
422
0
      outbuf[p++] = (char)(0xC0 + ((wc >> 6) & 0x1F));
423
0
      outbuf[p++] = (char)(0x80 + (wc & 0x3F));
424
0
    } else {
425
0
      outbuf[p++] = (char)(wc & 0x7F);
426
0
    }
427
0
  }
428
0
  if (items_read) {
429
0
    *items_read = i;
430
0
  }
431
0
  if (items_written) {
432
0
    *items_written = p;
433
0
  }
434
0
  outbuf[p] = 0;
435
436
  /* reduce the size to the actual size */
437
0
  outbuf_new = (char*)realloc(outbuf, p+1);
438
0
  if (outbuf_new) {
439
0
    outbuf = outbuf_new;
440
0
  }
441
442
0
  return outbuf;
443
0
}
444
445
static plist_t parse_unicode_node(const char **bnode, uint64_t size)
446
0
{
447
0
    plist_data_t data = plist_new_plist_data();
448
0
    size_t items_read = 0;
449
0
    size_t items_written = 0;
450
451
0
    data->type = PLIST_STRING;
452
0
    data->strval = plist_utf16be_to_utf8((uint16_t*)(*bnode), size, &items_read, &items_written);
453
0
    if (!data->strval) {
454
0
        plist_free_data(data);
455
0
        return NULL;
456
0
    }
457
0
    data->length = items_written;
458
459
0
    return node_create(NULL, data);
460
0
}
461
462
static plist_t parse_data_node(const char **bnode, uint64_t size)
463
0
{
464
0
    plist_data_t data = plist_new_plist_data();
465
466
0
    data->type = PLIST_DATA;
467
0
    data->length = size;
468
0
    data->buff = (uint8_t *) malloc(sizeof(uint8_t) * size);
469
0
    if (!data->strval) {
470
0
        plist_free_data(data);
471
0
        PLIST_BIN_ERR("%s: Could not allocate %" PRIu64 " bytes\n", __func__, sizeof(uint8_t) * size);
472
0
        return NULL;
473
0
    }
474
0
    memcpy(data->buff, *bnode, sizeof(uint8_t) * size);
475
476
0
    return node_create(NULL, data);
477
0
}
478
479
static plist_t parse_dict_node(struct bplist_data *bplist, const char** bnode, uint64_t size)
480
0
{
481
0
    uint64_t j;
482
0
    uint64_t str_i = 0, str_j = 0;
483
0
    uint64_t index1, index2;
484
0
    plist_data_t data = plist_new_plist_data();
485
0
    const char *index1_ptr = NULL;
486
0
    const char *index2_ptr = NULL;
487
488
0
    data->type = PLIST_DICT;
489
0
    data->length = size;
490
491
0
    plist_t node = node_create(NULL, data);
492
493
0
    for (j = 0; j < data->length; j++) {
494
0
        str_i = j * bplist->ref_size;
495
0
        str_j = (j + size) * bplist->ref_size;
496
0
        index1_ptr = (*bnode) + str_i;
497
0
        index2_ptr = (*bnode) + str_j;
498
499
0
        if ((index1_ptr < bplist->data || index1_ptr + bplist->ref_size > bplist->offset_table) ||
500
0
            (index2_ptr < bplist->data || index2_ptr + bplist->ref_size > bplist->offset_table)) {
501
0
            plist_free(node);
502
0
            PLIST_BIN_ERR("%s: dict entry %" PRIu64 " is outside of valid range\n", __func__, j);
503
0
            return NULL;
504
0
        }
505
506
0
        index1 = UINT_TO_HOST(index1_ptr, bplist->ref_size);
507
0
        index2 = UINT_TO_HOST(index2_ptr, bplist->ref_size);
508
509
0
        if (index1 >= bplist->num_objects) {
510
0
            plist_free(node);
511
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);
512
0
            return NULL;
513
0
        }
514
0
        if (index2 >= bplist->num_objects) {
515
0
            plist_free(node);
516
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);
517
0
            return NULL;
518
0
        }
519
520
        /* process key node */
521
0
        plist_t key = parse_bin_node_at_index(bplist, index1);
522
0
        if (!key) {
523
0
            plist_free(node);
524
0
            return NULL;
525
0
        }
526
527
0
        if (plist_get_data(key)->type != PLIST_STRING) {
528
0
            PLIST_BIN_ERR("%s: dict entry %" PRIu64 ": invalid node type for key\n", __func__, j);
529
0
            plist_free(key);
530
0
            plist_free(node);
531
0
            return NULL;
532
0
        }
533
534
        /* enforce key type */
535
0
        plist_get_data(key)->type = PLIST_KEY;
536
0
        if (!plist_get_data(key)->strval) {
537
0
            PLIST_BIN_ERR("%s: dict entry %" PRIu64 ": key must not be NULL\n", __func__, j);
538
0
            plist_free(key);
539
0
            plist_free(node);
540
0
            return NULL;
541
0
        }
542
543
        /* process value node */
544
0
        plist_t val = parse_bin_node_at_index(bplist, index2);
545
0
        if (!val) {
546
0
            plist_free(key);
547
0
            plist_free(node);
548
0
            return NULL;
549
0
        }
550
551
0
        node_attach((node_t)node, (node_t)key);
552
0
        node_attach((node_t)node, (node_t)val);
553
0
    }
554
555
0
    return node;
556
0
}
557
558
static plist_t parse_array_node(struct bplist_data *bplist, const char** bnode, uint64_t size)
559
0
{
560
0
    uint64_t j;
561
0
    uint64_t str_j = 0;
562
0
    uint64_t index1;
563
0
    plist_data_t data = plist_new_plist_data();
564
0
    const char *index1_ptr = NULL;
565
566
0
    data->type = PLIST_ARRAY;
567
0
    data->length = size;
568
569
0
    plist_t node = node_create(NULL, data);
570
571
0
    for (j = 0; j < data->length; j++) {
572
0
        str_j = j * bplist->ref_size;
573
0
        index1_ptr = (*bnode) + str_j;
574
575
0
        if (index1_ptr < bplist->data || index1_ptr + bplist->ref_size > bplist->offset_table) {
576
0
            plist_free(node);
577
0
            PLIST_BIN_ERR("%s: array item %" PRIu64 " is outside of valid range\n", __func__, j);
578
0
            return NULL;
579
0
        }
580
581
0
        index1 = UINT_TO_HOST(index1_ptr, bplist->ref_size);
582
583
0
        if (index1 >= bplist->num_objects) {
584
0
            plist_free(node);
585
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);
586
0
            return NULL;
587
0
        }
588
589
        /* process value node */
590
0
        plist_t val = parse_bin_node_at_index(bplist, index1);
591
0
        if (!val) {
592
0
            plist_free(node);
593
0
            return NULL;
594
0
        }
595
596
0
        node_attach((node_t)node, (node_t)val);
597
0
    }
598
599
0
    return node;
600
0
}
601
602
static plist_t parse_uid_node(const char **bnode, uint8_t size)
603
0
{
604
0
    plist_data_t data = plist_new_plist_data();
605
0
    size = size + 1;
606
0
    data->intval = UINT_TO_HOST(*bnode, size);
607
0
    if (data->intval > UINT32_MAX) {
608
0
        PLIST_BIN_ERR("%s: value %" PRIu64 " too large for UID node (must be <= %u)\n", __func__, (uint64_t)data->intval, UINT32_MAX);
609
0
        free(data);
610
0
        return NULL;
611
0
    }
612
613
0
    (*bnode) += size;
614
0
    data->type = PLIST_UID;
615
0
    data->length = sizeof(uint64_t);
616
617
0
    return node_create(NULL, data);
618
0
}
619
620
static plist_t parse_bin_node(struct bplist_data *bplist, const char** object)
621
0
{
622
0
    uint16_t type = 0;
623
0
    uint64_t size = 0;
624
0
    uint64_t pobject = 0;
625
0
    uint64_t poffset_table = (uint64_t)(uintptr_t)bplist->offset_table;
626
627
0
    if (!object)
628
0
        return NULL;
629
630
0
    type = (**object) & BPLIST_MASK;
631
0
    size = (**object) & BPLIST_FILL;
632
0
    (*object)++;
633
634
0
    if (size == BPLIST_FILL) {
635
0
        switch (type) {
636
0
        case BPLIST_DATA:
637
0
        case BPLIST_STRING:
638
0
        case BPLIST_UNICODE:
639
0
        case BPLIST_ARRAY:
640
0
        case BPLIST_SET:
641
0
        case BPLIST_DICT:
642
0
        {
643
0
            uint16_t next_size = **object & BPLIST_FILL;
644
0
            if ((**object & BPLIST_MASK) != BPLIST_INT) {
645
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);
646
0
                return NULL;
647
0
            }
648
0
            (*object)++;
649
0
            next_size = 1 << next_size;
650
0
            if (*object + next_size > bplist->offset_table) {
651
0
                PLIST_BIN_ERR("%s: size node data bytes for node type 0x%02x point outside of valid range\n", __func__, type);
652
0
                return NULL;
653
0
            }
654
0
            size = UINT_TO_HOST(*object, next_size);
655
0
            (*object) += next_size;
656
0
            break;
657
0
        }
658
0
        default:
659
0
            break;
660
0
        }
661
0
    }
662
663
0
    pobject = (uint64_t)(uintptr_t)*object;
664
665
0
    switch (type)
666
0
    {
667
668
0
    case BPLIST_NULL:
669
0
        switch (size)
670
0
        {
671
672
0
        case BPLIST_TRUE:
673
0
        {
674
0
            plist_data_t data = plist_new_plist_data();
675
0
            data->type = PLIST_BOOLEAN;
676
0
            data->boolval = TRUE;
677
0
            data->length = 1;
678
0
            return node_create(NULL, data);
679
0
        }
680
681
0
        case BPLIST_FALSE:
682
0
        {
683
0
            plist_data_t data = plist_new_plist_data();
684
0
            data->type = PLIST_BOOLEAN;
685
0
            data->boolval = FALSE;
686
0
            data->length = 1;
687
0
            return node_create(NULL, data);
688
0
        }
689
690
0
        case BPLIST_NULL:
691
0
        {
692
0
            plist_data_t data = plist_new_plist_data();
693
0
            data->type = PLIST_NULL;
694
0
            data->length = 0;
695
0
            return node_create(NULL, data);
696
0
        }
697
698
0
        default:
699
0
            return NULL;
700
0
        }
701
702
0
    case BPLIST_INT:
703
0
        if (pobject + (uint64_t)(1 << size) > poffset_table) {
704
0
            PLIST_BIN_ERR("%s: BPLIST_INT data bytes point outside of valid range\n", __func__);
705
0
            return NULL;
706
0
        }
707
0
        return parse_int_node(object, size);
708
709
0
    case BPLIST_REAL:
710
0
        if (pobject + (uint64_t)(1 << size) > poffset_table) {
711
0
            PLIST_BIN_ERR("%s: BPLIST_REAL data bytes point outside of valid range\n", __func__);
712
0
            return NULL;
713
0
        }
714
0
        return parse_real_node(object, size);
715
716
0
    case BPLIST_DATE:
717
0
        if (3 != size) {
718
0
            PLIST_BIN_ERR("%s: invalid data size for BPLIST_DATE node\n", __func__);
719
0
            return NULL;
720
0
        }
721
0
        if (pobject + (uint64_t)(1 << size) > poffset_table) {
722
0
            PLIST_BIN_ERR("%s: BPLIST_DATE data bytes point outside of valid range\n", __func__);
723
0
            return NULL;
724
0
        }
725
0
        return parse_date_node(object, size);
726
727
0
    case BPLIST_DATA:
728
0
        if (pobject + size < pobject || pobject + size > poffset_table) {
729
0
            PLIST_BIN_ERR("%s: BPLIST_DATA data bytes point outside of valid range\n", __func__);
730
0
            return NULL;
731
0
        }
732
0
        return parse_data_node(object, size);
733
734
0
    case BPLIST_STRING:
735
0
        if (pobject + size < pobject || pobject + size > poffset_table) {
736
0
            PLIST_BIN_ERR("%s: BPLIST_STRING data bytes point outside of valid range\n", __func__);
737
0
            return NULL;
738
0
        }
739
0
        return parse_string_node(object, size);
740
741
0
    case BPLIST_UNICODE:
742
0
        if (size*2 < size) {
743
0
            PLIST_BIN_ERR("%s: Integer overflow when calculating BPLIST_UNICODE data size.\n", __func__);
744
0
            return NULL;
745
0
        }
746
0
        if (pobject + size*2 < pobject || pobject + size*2 > poffset_table) {
747
0
            PLIST_BIN_ERR("%s: BPLIST_UNICODE data bytes point outside of valid range\n", __func__);
748
0
            return NULL;
749
0
        }
750
0
        return parse_unicode_node(object, size);
751
752
0
    case BPLIST_SET:
753
0
    case BPLIST_ARRAY:
754
0
        if (pobject + size < pobject || pobject + size > poffset_table) {
755
0
            PLIST_BIN_ERR("%s: BPLIST_ARRAY data bytes point outside of valid range\n", __func__);
756
0
            return NULL;
757
0
        }
758
0
        return parse_array_node(bplist, object, size);
759
760
0
    case BPLIST_UID:
761
0
        if (pobject + size+1 > poffset_table) {
762
0
            PLIST_BIN_ERR("%s: BPLIST_UID data bytes point outside of valid range\n", __func__);
763
0
            return NULL;
764
0
        }
765
0
        return parse_uid_node(object, size);
766
767
0
    case BPLIST_DICT:
768
0
        if (pobject + size < pobject || pobject + size > poffset_table) {
769
0
            PLIST_BIN_ERR("%s: BPLIST_DICT data bytes point outside of valid range\n", __func__);
770
0
            return NULL;
771
0
        }
772
0
        return parse_dict_node(bplist, object, size);
773
774
0
    default:
775
0
        PLIST_BIN_ERR("%s: unexpected node type 0x%02x\n", __func__, type);
776
0
        return NULL;
777
0
    }
778
0
    return NULL;
779
0
}
780
781
static plist_t parse_bin_node_at_index(struct bplist_data *bplist, uint32_t node_index)
782
0
{
783
0
    int i = 0;
784
0
    const char* ptr = NULL;
785
0
    plist_t plist = NULL;
786
0
    const char* idx_ptr = NULL;
787
788
0
    if (node_index >= bplist->num_objects) {
789
0
        PLIST_BIN_ERR("node index (%u) must be smaller than the number of objects (%" PRIu64 ")\n", node_index, bplist->num_objects);
790
0
        return NULL;
791
0
    }
792
793
0
    idx_ptr = bplist->offset_table + node_index * bplist->offset_size;
794
0
    if (idx_ptr < bplist->offset_table ||
795
0
        idx_ptr >= bplist->offset_table + bplist->num_objects * bplist->offset_size) {
796
0
        PLIST_BIN_ERR("node index %u points outside of valid range\n", node_index);
797
0
        return NULL;
798
0
    }
799
800
0
    ptr = bplist->data + UINT_TO_HOST(idx_ptr, bplist->offset_size);
801
    /* make sure the node offset is in a sane range */
802
0
    if ((ptr < bplist->data+BPLIST_MAGIC_SIZE+BPLIST_VERSION_SIZE) || (ptr >= bplist->offset_table)) {
803
0
        PLIST_BIN_ERR("offset for node index %u points outside of valid range\n", node_index);
804
0
        return NULL;
805
0
    }
806
807
    /* store node_index for current recursion level */
808
0
    if ((uint32_t)ptr_array_size(bplist->used_indexes) < bplist->level+1) {
809
0
        while ((uint32_t)ptr_array_size(bplist->used_indexes) < bplist->level+1) {
810
0
            ptr_array_add(bplist->used_indexes, (void*)(uintptr_t)node_index);
811
0
        }
812
0
    } else {
813
0
  ptr_array_set(bplist->used_indexes, (void*)(uintptr_t)node_index, bplist->level);
814
0
    }
815
816
    /* recursion check */
817
0
    if (bplist->level > 0) {
818
0
        for (i = bplist->level-1; i >= 0; i--) {
819
0
            void *node_i = ptr_array_index(bplist->used_indexes, i);
820
0
            void *node_level = ptr_array_index(bplist->used_indexes, bplist->level);
821
0
            if (node_i == node_level) {
822
0
                PLIST_BIN_ERR("recursion detected in binary plist\n");
823
0
                return NULL;
824
0
            }
825
0
        }
826
0
    }
827
828
    /* finally parse node */
829
0
    bplist->level++;
830
0
    plist = parse_bin_node(bplist, &ptr);
831
0
    bplist->level--;
832
0
    return plist;
833
0
}
834
835
plist_err_t plist_from_bin(const char *plist_bin, uint32_t length, plist_t * plist)
836
0
{
837
0
    bplist_trailer_t *trailer = NULL;
838
0
    uint8_t offset_size = 0;
839
0
    uint8_t ref_size = 0;
840
0
    uint64_t num_objects = 0;
841
0
    uint64_t root_object = 0;
842
0
    const char *offset_table = NULL;
843
0
    uint64_t offset_table_size = 0;
844
0
    const char *start_data = NULL;
845
0
    const char *end_data = NULL;
846
847
0
    if (!plist) {
848
0
        return PLIST_ERR_INVALID_ARG;
849
0
    }
850
0
    *plist = NULL;
851
0
    if (!plist_bin || length == 0) {
852
0
        return PLIST_ERR_INVALID_ARG;
853
0
    }
854
855
    //first check we have enough data
856
0
    if (!(length >= BPLIST_MAGIC_SIZE + BPLIST_VERSION_SIZE + sizeof(bplist_trailer_t))) {
857
0
        PLIST_BIN_ERR("plist data is to small to hold a binary plist\n");
858
0
        return PLIST_ERR_PARSE;
859
0
    }
860
    //check that plist_bin in actually a plist
861
0
    if (memcmp(plist_bin, BPLIST_MAGIC, BPLIST_MAGIC_SIZE) != 0) {
862
0
        PLIST_BIN_ERR("bplist magic mismatch\n");
863
0
        return PLIST_ERR_PARSE;
864
0
    }
865
    //check for known version
866
0
    if (memcmp(plist_bin + BPLIST_MAGIC_SIZE, BPLIST_VERSION, BPLIST_VERSION_SIZE) != 0) {
867
0
        PLIST_BIN_ERR("unsupported binary plist version '%.2s\n", plist_bin+BPLIST_MAGIC_SIZE);
868
0
        return PLIST_ERR_PARSE;
869
0
    }
870
871
0
    start_data = plist_bin + BPLIST_MAGIC_SIZE + BPLIST_VERSION_SIZE;
872
0
    end_data = plist_bin + length - sizeof(bplist_trailer_t);
873
874
    //now parse trailer
875
0
    trailer = (bplist_trailer_t*)end_data;
876
877
0
    offset_size = trailer->offset_size;
878
0
    ref_size = trailer->ref_size;
879
0
    num_objects = be64toh(trailer->num_objects);
880
0
    root_object = be64toh(trailer->root_object_index);
881
882
0
    uint64_t offset_table_offset = be64toh(trailer->offset_table_offset);
883
0
    uint64_t max_valid_offset = (uint64_t)length - sizeof(bplist_trailer_t);
884
0
    if (offset_table_offset > max_valid_offset) {
885
0
        PLIST_BIN_ERR("offset table offset outside of valid range\n");
886
0
        return PLIST_ERR_PARSE;
887
0
    }
888
0
    offset_table = (char *)(plist_bin + offset_table_offset);
889
890
0
    if (num_objects == 0) {
891
0
        PLIST_BIN_ERR("number of objects must be larger than 0\n");
892
0
        return PLIST_ERR_PARSE;
893
0
    }
894
895
0
    if (offset_size == 0) {
896
0
        PLIST_BIN_ERR("offset size in trailer must be larger than 0\n");
897
0
        return PLIST_ERR_PARSE;
898
0
    }
899
900
0
    if (ref_size == 0) {
901
0
        PLIST_BIN_ERR("object reference size in trailer must be larger than 0\n");
902
0
        return PLIST_ERR_PARSE;
903
0
    }
904
905
0
    if (root_object >= num_objects) {
906
0
        PLIST_BIN_ERR("root object index (%" PRIu64 ") must be smaller than number of objects (%" PRIu64 ")\n", root_object, num_objects);
907
0
        return PLIST_ERR_PARSE;
908
0
    }
909
910
0
    if (offset_table < start_data || offset_table >= end_data) {
911
0
        PLIST_BIN_ERR("offset table offset points outside of valid range\n");
912
0
        return PLIST_ERR_PARSE;
913
0
    }
914
915
0
    if (uint64_mul_overflow(num_objects, offset_size, &offset_table_size)) {
916
0
        PLIST_BIN_ERR("integer overflow when calculating offset table size\n");
917
0
        return PLIST_ERR_PARSE;
918
0
    }
919
920
0
    if (offset_table_size > (uint64_t)(end_data - offset_table)) {
921
0
        PLIST_BIN_ERR("offset table points outside of valid range\n");
922
0
        return PLIST_ERR_PARSE;
923
0
    }
924
925
0
    struct bplist_data bplist;
926
0
    bplist.data = plist_bin;
927
0
    bplist.size = length;
928
0
    bplist.num_objects = num_objects;
929
0
    bplist.ref_size = ref_size;
930
0
    bplist.offset_size = offset_size;
931
0
    bplist.offset_table = offset_table;
932
0
    bplist.level = 0;
933
0
    bplist.used_indexes = ptr_array_new(16);
934
935
0
    if (!bplist.used_indexes) {
936
0
        PLIST_BIN_ERR("failed to create array to hold used node indexes. Out of memory?\n");
937
0
        return PLIST_ERR_NO_MEM;
938
0
    }
939
940
0
    *plist = parse_bin_node_at_index(&bplist, root_object);
941
942
0
    ptr_array_free(bplist.used_indexes);
943
944
0
    if (!*plist) {
945
0
        return PLIST_ERR_PARSE;
946
0
    }
947
948
0
    return PLIST_ERR_SUCCESS;
949
0
}
950
951
static unsigned int plist_data_hash(const void* key)
952
0
{
953
0
    plist_data_t data = plist_get_data((plist_t) key);
954
955
0
    unsigned int hash = data->type;
956
0
    unsigned int i = 0;
957
958
0
    char *buff = NULL;
959
0
    unsigned int size = 0;
960
961
0
    switch (data->type)
962
0
    {
963
0
    case PLIST_BOOLEAN:
964
0
    case PLIST_NULL:
965
0
    case PLIST_INT:
966
0
    case PLIST_REAL:
967
0
    case PLIST_DATE:
968
0
    case PLIST_UID:
969
0
        buff = (char *) &data->intval;  //works also for real as we use an union
970
0
        size = 8;
971
0
        break;
972
0
    case PLIST_KEY:
973
0
    case PLIST_STRING:
974
0
        buff = data->strval;
975
0
        size = data->length;
976
0
        break;
977
0
    case PLIST_DATA:
978
0
    case PLIST_ARRAY:
979
0
    case PLIST_DICT:
980
        //for these types only hash pointer
981
0
        buff = (char *) &key;
982
0
        size = sizeof(const void*);
983
0
        break;
984
0
    default:
985
0
        break;
986
0
    }
987
988
    // now perform hash using djb2 hashing algorithm
989
    // see: http://www.cse.yorku.ca/~oz/hash.html
990
0
    hash += 5381;
991
0
    for (i = 0; i < size; buff++, i++) {
992
0
        hash = ((hash << 5) + hash) + *buff;
993
0
    }
994
995
0
    return hash;
996
0
}
997
998
struct serialize_s
999
{
1000
    ptrarray_t* objects;
1001
    hashtable_t* ref_table;
1002
    hashtable_t* in_stack;
1003
};
1004
1005
static plist_err_t serialize_plist(node_t node, void* data)
1006
0
{
1007
0
    uint64_t *index_val = NULL;
1008
0
    struct serialize_s *ser = (struct serialize_s *) data;
1009
1010
    // circular reference check: is node on current recursion stack?
1011
0
    if (hash_table_lookup(ser->in_stack, node)) {
1012
0
        PLIST_BIN_WRITE_ERR("circular reference detected\n");
1013
0
        return PLIST_ERR_CIRCULAR_REF;
1014
0
    }
1015
1016
    // first check that node is not yet in objects
1017
0
    void* val = hash_table_lookup(ser->ref_table, node);
1018
0
    if (val) {
1019
        // data is already in table
1020
0
        return PLIST_ERR_SUCCESS;
1021
0
    }
1022
1023
    // mark as active
1024
0
    hash_table_insert(ser->in_stack, node, (void*)1);
1025
1026
    // insert new ref
1027
0
    index_val = (uint64_t *) malloc(sizeof(uint64_t));
1028
0
    assert(index_val != NULL);
1029
0
    *index_val = ser->objects->len;
1030
0
    hash_table_insert(ser->ref_table, node, index_val);
1031
1032
    // now append current node to object array
1033
0
    ptr_array_add(ser->objects, node);
1034
1035
    // now recurse on children
1036
0
    node_t ch;
1037
0
    plist_err_t err = PLIST_ERR_SUCCESS;
1038
0
    for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) {
1039
0
        err = serialize_plist(ch, data);
1040
0
        if (err != PLIST_ERR_SUCCESS) {
1041
0
            break;
1042
0
        }
1043
0
    }
1044
1045
    // leave recursion stack
1046
0
    hash_table_remove(ser->in_stack, node);
1047
1048
0
    return err;
1049
0
}
1050
1051
0
#define Log2(x) ((x) == 8 ? 3 : ((x) == 4 ? 2 : ((x) == 2 ? 1 : 0)))
1052
1053
static void write_int(bytearray_t * bplist, uint64_t val)
1054
0
{
1055
0
    int size = get_needed_bytes(val);
1056
0
    uint8_t sz;
1057
    //do not write 3bytes int node
1058
0
    if (size == 3)
1059
0
        size++;
1060
0
    sz = BPLIST_INT | Log2(size);
1061
1062
0
    val = be64toh(val);
1063
0
    byte_array_append(bplist, &sz, 1);
1064
0
    byte_array_append(bplist, (uint8_t*)&val + (8-size), size);
1065
0
}
1066
1067
static void write_uint(bytearray_t * bplist, uint64_t val)
1068
0
{
1069
0
    uint8_t sz = BPLIST_INT | 4;
1070
0
    uint64_t zero = 0;
1071
1072
0
    val = be64toh(val);
1073
0
    byte_array_append(bplist, &sz, 1);
1074
0
    byte_array_append(bplist, &zero, sizeof(uint64_t));
1075
0
    byte_array_append(bplist, &val, sizeof(uint64_t));
1076
0
}
1077
1078
static void write_real(bytearray_t * bplist, double val)
1079
0
{
1080
0
    int size = get_real_bytes(val);  //cheat to know used space
1081
0
    uint8_t buff[16];
1082
0
    buff[7] = BPLIST_REAL | Log2(size);
1083
0
    if (size == sizeof(float)) {
1084
0
        float floatval = (float)val;
1085
0
        uint32_t intval;
1086
0
        memcpy(&intval, &floatval, sizeof(float));
1087
0
        *(uint32_t*)(buff+8) = float_bswap32(intval);
1088
0
    } else {
1089
0
        uint64_t intval;
1090
0
        memcpy(&intval, &val, sizeof(double));
1091
0
        *(uint64_t*)(buff+8) = float_bswap64(intval);
1092
0
    }
1093
0
    byte_array_append(bplist, buff+7, size+1);
1094
0
}
1095
1096
static void write_date(bytearray_t * bplist, double val)
1097
0
{
1098
0
    uint64_t intval;
1099
0
    memcpy(&intval, &val, sizeof(double));
1100
0
    uint8_t buff[16];
1101
0
    buff[7] = BPLIST_DATE | 3;
1102
0
    *(uint64_t*)(buff+8) = float_bswap64(intval);
1103
0
    byte_array_append(bplist, buff+7, 9);
1104
0
}
1105
1106
static void write_raw_data(bytearray_t * bplist, uint8_t mark, uint8_t * val, uint64_t size)
1107
0
{
1108
0
    uint8_t marker = mark | (size < 15 ? size : 0xf);
1109
0
    byte_array_append(bplist, &marker, sizeof(uint8_t));
1110
0
    if (size >= 15) {
1111
0
        write_int(bplist, size);
1112
0
    }
1113
0
    if (BPLIST_UNICODE==mark) size <<= 1;
1114
0
    byte_array_append(bplist, val, size);
1115
0
}
1116
1117
static void write_data(bytearray_t * bplist, uint8_t * val, uint64_t size)
1118
0
{
1119
0
    write_raw_data(bplist, BPLIST_DATA, val, size);
1120
0
}
1121
1122
static void write_string(bytearray_t * bplist, char *val, uint64_t size)
1123
0
{
1124
0
    write_raw_data(bplist, BPLIST_STRING, (uint8_t *) val, size);
1125
0
}
1126
1127
static uint16_t *plist_utf8_to_utf16be(char *unistr, size_t size, size_t *items_read, size_t *items_written)
1128
0
{
1129
0
  uint16_t *outbuf;
1130
0
  size_t p = 0;
1131
0
  size_t i = 0;
1132
1133
0
  unsigned char c0;
1134
0
  unsigned char c1;
1135
0
  unsigned char c2;
1136
0
  unsigned char c3;
1137
1138
0
  uint32_t w;
1139
1140
0
  outbuf = (uint16_t*)malloc(((size*2)+1)*sizeof(uint16_t));
1141
0
  if (!outbuf) {
1142
0
    PLIST_BIN_ERR("%s: Could not allocate %" PRIu64 " bytes\n", __func__, (uint64_t)((size*2)+1)*sizeof(uint16_t));
1143
0
    return NULL;
1144
0
  }
1145
1146
0
  while (i < size) {
1147
0
    c0 = unistr[i];
1148
0
    c1 = (i < size-1) ? unistr[i+1] : 0;
1149
0
    c2 = (i < size-2) ? unistr[i+2] : 0;
1150
0
    c3 = (i < size-3) ? unistr[i+3] : 0;
1151
0
    if ((c0 >= 0xF0) && (i < size-3) && (c1 >= 0x80) && (c2 >= 0x80) && (c3 >= 0x80)) {
1152
      // 4 byte sequence.  Need to generate UTF-16 surrogate pair
1153
0
      w = ((((c0 & 7) << 18) + ((c1 & 0x3F) << 12) + ((c2 & 0x3F) << 6) + (c3 & 0x3F)) & 0x1FFFFF) - 0x010000;
1154
0
      outbuf[p++] = be16toh(0xD800 + (w >> 10));
1155
0
      outbuf[p++] = be16toh(0xDC00 + (w & 0x3FF));
1156
0
      i+=4;
1157
0
    } else if ((c0 >= 0xE0) && (i < size-2) && (c1 >= 0x80) && (c2 >= 0x80)) {
1158
      // 3 byte sequence
1159
0
      outbuf[p++] = be16toh(((c2 & 0x3F) + ((c1 & 3) << 6)) + (((c1 >> 2) & 15) << 8) + ((c0 & 15) << 12));
1160
0
      i+=3;
1161
0
    } else if ((c0 >= 0xC0) && (i < size-1) && (c1 >= 0x80)) {
1162
      // 2 byte sequence
1163
0
      outbuf[p++] = be16toh(((c1 & 0x3F) + ((c0 & 3) << 6)) + (((c0 >> 2) & 7) << 8));
1164
0
      i+=2;
1165
0
    } else if (c0 < 0x80) {
1166
      // 1 byte sequence
1167
0
      outbuf[p++] = be16toh(c0);
1168
0
      i+=1;
1169
0
    } else {
1170
      // invalid character
1171
0
      PLIST_BIN_ERR("%s: invalid utf8 sequence in string at index %zu\n", __func__, i);
1172
0
      break;
1173
0
    }
1174
0
  }
1175
0
  if (items_read) {
1176
0
    *items_read = i;
1177
0
  }
1178
0
  if (items_written) {
1179
0
    *items_written = p;
1180
0
  }
1181
0
  outbuf[p] = 0;
1182
1183
0
  return outbuf;
1184
0
}
1185
1186
static void write_unicode(bytearray_t * bplist, char *val, size_t size)
1187
0
{
1188
0
    size_t items_read = 0;
1189
0
    size_t items_written = 0;
1190
0
    uint16_t *unicodestr = NULL;
1191
1192
0
    unicodestr = plist_utf8_to_utf16be(val, size, &items_read, &items_written);
1193
0
    write_raw_data(bplist, BPLIST_UNICODE, (uint8_t*)unicodestr, items_written);
1194
0
    free(unicodestr);
1195
0
}
1196
1197
static void write_array(bytearray_t * bplist, node_t node, hashtable_t* ref_table, uint8_t ref_size)
1198
0
{
1199
0
    node_t cur = NULL;
1200
0
    uint64_t i = 0;
1201
1202
0
    uint64_t size = node_n_children(node);
1203
0
    uint8_t marker = BPLIST_ARRAY | (size < 15 ? size : 0xf);
1204
0
    byte_array_append(bplist, &marker, sizeof(uint8_t));
1205
0
    if (size >= 15) {
1206
0
        write_int(bplist, size);
1207
0
    }
1208
1209
0
    for (i = 0, cur = node_first_child(node); cur && i < size; cur = node_next_sibling(cur), i++) {
1210
0
        uint64_t idx = *(uint64_t *) (hash_table_lookup(ref_table, cur));
1211
0
        idx = be64toh(idx);
1212
0
        byte_array_append(bplist, (uint8_t*)&idx + (sizeof(uint64_t) - ref_size), ref_size);
1213
0
    }
1214
0
}
1215
1216
static void write_dict(bytearray_t * bplist, node_t node, hashtable_t* ref_table, uint8_t ref_size)
1217
0
{
1218
0
    node_t cur = NULL;
1219
0
    uint64_t i = 0;
1220
1221
0
    uint64_t size = node_n_children(node) / 2;
1222
0
    uint8_t marker = BPLIST_DICT | (size < 15 ? size : 0xf);
1223
0
    byte_array_append(bplist, &marker, sizeof(uint8_t));
1224
0
    if (size >= 15) {
1225
0
        write_int(bplist, size);
1226
0
    }
1227
1228
0
    for (i = 0, cur = node_first_child(node); cur && i < size; cur = node_next_sibling(node_next_sibling(cur)), i++) {
1229
0
        uint64_t idx1 = *(uint64_t *) (hash_table_lookup(ref_table, cur));
1230
0
        idx1 = be64toh(idx1);
1231
0
        byte_array_append(bplist, (uint8_t*)&idx1 + (sizeof(uint64_t) - ref_size), ref_size);
1232
0
    }
1233
1234
0
    for (i = 0, cur = node_first_child(node); cur && i < size; cur = node_next_sibling(node_next_sibling(cur)), i++) {
1235
0
        uint64_t idx2 = *(uint64_t *) (hash_table_lookup(ref_table, cur->next));
1236
0
        idx2 = be64toh(idx2);
1237
0
        byte_array_append(bplist, (uint8_t*)&idx2 + (sizeof(uint64_t) - ref_size), ref_size);
1238
0
    }
1239
0
}
1240
1241
static void write_uid(bytearray_t * bplist, uint64_t val)
1242
0
{
1243
0
    val = (uint32_t)val;
1244
0
    int size = get_needed_bytes(val);
1245
0
    uint8_t sz;
1246
    //do not write 3bytes int node
1247
0
    if (size == 3)
1248
0
        size++;
1249
0
    sz = BPLIST_UID | (size-1); // yes, this is what Apple does...
1250
1251
0
    val = be64toh(val);
1252
0
    byte_array_append(bplist, &sz, 1);
1253
0
    byte_array_append(bplist, (uint8_t*)&val + (8-size), size);
1254
0
}
1255
1256
static int is_ascii_string(char* s, int len)
1257
0
{
1258
0
  int ret = 1, i = 0;
1259
0
  for(i = 0; i < len; i++)
1260
0
  {
1261
0
      if ( !isascii( s[i] ) )
1262
0
      {
1263
0
          ret = 0;
1264
0
          break;
1265
0
      }
1266
0
  }
1267
0
  return ret;
1268
0
}
1269
1270
plist_err_t plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length)
1271
0
{
1272
0
    ptrarray_t* objects = NULL;
1273
0
    hashtable_t* ref_table = NULL;
1274
0
    hashtable_t* in_stack = NULL;
1275
0
    struct serialize_s ser_s;
1276
0
    uint8_t offset_size = 0;
1277
0
    uint8_t ref_size = 0;
1278
0
    uint64_t num_objects = 0;
1279
0
    uint64_t root_object = 0;
1280
0
    uint64_t offset_table_index = 0;
1281
0
    bytearray_t *bplist_buff = NULL;
1282
0
    uint64_t i = 0;
1283
0
    uint64_t buff_len = 0;
1284
0
    uint64_t *offsets = NULL;
1285
0
    bplist_trailer_t trailer;
1286
0
    uint64_t objects_len = 0;
1287
1288
    //check for valid input
1289
0
    if (!plist || !plist_bin || !length) {
1290
0
        return PLIST_ERR_INVALID_ARG;
1291
0
    }
1292
1293
    //list of objects
1294
0
    objects = ptr_array_new(4096);
1295
0
    if (!objects) {
1296
0
        return PLIST_ERR_NO_MEM;
1297
0
    }
1298
    //hashtable to write only once same nodes
1299
0
    ref_table = hash_table_new(plist_data_hash, plist_data_compare, free);
1300
0
    if (!ref_table) {
1301
0
        ptr_array_free(objects);
1302
0
        return PLIST_ERR_NO_MEM;
1303
0
    }
1304
    //hashtable for circular reference detection
1305
0
    in_stack = hash_table_new(plist_node_ptr_hash, plist_node_ptr_compare, NULL);
1306
0
    if (!in_stack) {
1307
0
        ptr_array_free(objects);
1308
0
        hash_table_destroy(ref_table);
1309
0
        return PLIST_ERR_NO_MEM;
1310
0
    }
1311
1312
    //serialize plist
1313
0
    ser_s.objects = objects;
1314
0
    ser_s.ref_table = ref_table;
1315
0
    ser_s.in_stack = in_stack;
1316
0
    plist_err_t err = serialize_plist((node_t)plist, &ser_s);
1317
0
    if (err != PLIST_ERR_SUCCESS) {
1318
0
        ptr_array_free(objects);
1319
0
        hash_table_destroy(ref_table);
1320
0
        hash_table_destroy(in_stack);
1321
0
        return err;
1322
0
    }
1323
    //no longer needed
1324
0
    hash_table_destroy(in_stack);
1325
0
    ser_s.in_stack = NULL;
1326
1327
    //now stream to output buffer
1328
0
    offset_size = 0;      //unknown yet
1329
0
    objects_len = objects->len;
1330
0
    ref_size = get_needed_bytes(objects_len);
1331
0
    num_objects = objects->len;
1332
0
    root_object = 0;      //root is first in list
1333
0
    offset_table_index = 0;   //unknown yet
1334
1335
    //figure out the storage size required
1336
0
    uint64_t req = 0;
1337
0
    for (i = 0; i < num_objects; i++)
1338
0
    {
1339
0
        node_t node = (node_t)ptr_array_index(objects, i);
1340
0
        plist_data_t data = plist_get_data(node);
1341
0
        uint64_t size;
1342
0
        uint8_t bsize;
1343
0
        switch (data->type)
1344
0
        {
1345
0
        case PLIST_NULL:
1346
0
        case PLIST_BOOLEAN:
1347
0
            req += 1;
1348
0
            break;
1349
0
        case PLIST_KEY:
1350
0
        case PLIST_STRING:
1351
0
            req += 1;
1352
0
            if (data->length >= 15) {
1353
0
                bsize = get_needed_bytes(data->length);
1354
0
                if (bsize == 3) bsize = 4;
1355
0
                req += 1;
1356
0
                req += bsize;
1357
0
            }
1358
0
            if ( is_ascii_string(data->strval, data->length) )
1359
0
            {
1360
0
                req += data->length;
1361
0
            }
1362
0
            else
1363
0
            {
1364
0
                req += data->length * 2;
1365
0
            }
1366
0
            break;
1367
0
        case PLIST_REAL:
1368
0
            size = get_real_bytes(data->realval);
1369
0
            req += 1;
1370
0
            req += size;
1371
0
            break;
1372
0
        case PLIST_DATE:
1373
0
            req += 9;
1374
0
            break;
1375
0
        case PLIST_ARRAY:
1376
0
            size = node_n_children(node);
1377
0
            req += 1;
1378
0
            if (size >= 15) {
1379
0
                bsize = get_needed_bytes(size);
1380
0
                if (bsize == 3) bsize = 4;
1381
0
                req += 1;
1382
0
                req += bsize;
1383
0
            }
1384
0
            req += size * ref_size;
1385
0
            break;
1386
0
        case PLIST_DICT:
1387
0
            size = node_n_children(node) / 2;
1388
0
            req += 1;
1389
0
            if (size >= 15) {
1390
0
                bsize = get_needed_bytes(size);
1391
0
                if (bsize == 3) bsize = 4;
1392
0
                req += 1;
1393
0
                req += bsize;
1394
0
            }
1395
0
            req += size * 2 * ref_size;
1396
0
            break;
1397
0
        default:
1398
0
            size = data->length;
1399
0
            req += 1;
1400
0
            if (size >= 15) {
1401
0
                bsize = get_needed_bytes(size);
1402
0
                if (bsize == 3) bsize = 4;
1403
0
                req += 1;
1404
0
                req += bsize;
1405
0
            }
1406
0
            req += data->length;
1407
0
            break;
1408
0
        }
1409
0
    }
1410
    // add size of magic
1411
0
    req += BPLIST_MAGIC_SIZE;
1412
0
    req += BPLIST_VERSION_SIZE;
1413
    // add size of offset table
1414
0
    req += get_needed_bytes(req) * num_objects;
1415
    // add size of trailer
1416
0
    req += sizeof(bplist_trailer_t);
1417
1418
    //setup a dynamic bytes array to store bplist in
1419
0
    bplist_buff = byte_array_new(req);
1420
0
    if (!bplist_buff) {
1421
0
        ptr_array_free(objects);
1422
0
        hash_table_destroy(ref_table);
1423
0
        return PLIST_ERR_NO_MEM;
1424
0
    }
1425
1426
    //set magic number and version
1427
0
    byte_array_append(bplist_buff, BPLIST_MAGIC, BPLIST_MAGIC_SIZE);
1428
0
    byte_array_append(bplist_buff, BPLIST_VERSION, BPLIST_VERSION_SIZE);
1429
1430
    //write objects and table
1431
0
    offsets = (uint64_t *) malloc(num_objects * sizeof(uint64_t));
1432
0
    assert(offsets != NULL);
1433
0
    for (i = 0; i < num_objects; i++)
1434
0
    {
1435
1436
0
        plist_data_t data = plist_get_data(ptr_array_index(objects, i));
1437
0
        offsets[i] = bplist_buff->len;
1438
1439
0
        switch (data->type)
1440
0
        {
1441
0
        case PLIST_NULL: {
1442
0
            uint8_t b = 0;
1443
0
            byte_array_append(bplist_buff, &b, 1);
1444
0
            break;
1445
0
        }
1446
0
        case PLIST_BOOLEAN: {
1447
0
            uint8_t b = data->boolval ? BPLIST_TRUE : BPLIST_FALSE;
1448
0
            byte_array_append(bplist_buff, &b, 1);
1449
0
            break;
1450
0
        }
1451
0
        case PLIST_INT:
1452
0
            if (data->length == 16) {
1453
0
                write_uint(bplist_buff, data->intval);
1454
0
            } else {
1455
0
                write_int(bplist_buff, data->intval);
1456
0
            }
1457
0
            break;
1458
1459
0
        case PLIST_REAL:
1460
0
            write_real(bplist_buff, data->realval);
1461
0
            break;
1462
1463
0
        case PLIST_KEY:
1464
0
        case PLIST_STRING:
1465
0
            if ( is_ascii_string(data->strval, data->length) )
1466
0
            {
1467
0
                write_string(bplist_buff, data->strval, data->length);
1468
0
            }
1469
0
            else
1470
0
            {
1471
0
                write_unicode(bplist_buff, data->strval, data->length);
1472
0
            }
1473
0
            break;
1474
0
        case PLIST_DATA:
1475
0
            write_data(bplist_buff, data->buff, data->length);
1476
0
            break;
1477
0
        case PLIST_ARRAY:
1478
0
            write_array(bplist_buff, (node_t)ptr_array_index(objects, i), ref_table, ref_size);
1479
0
            break;
1480
0
        case PLIST_DICT:
1481
0
            write_dict(bplist_buff, (node_t)ptr_array_index(objects, i), ref_table, ref_size);
1482
0
            break;
1483
0
        case PLIST_DATE:
1484
0
            write_date(bplist_buff, data->realval);
1485
0
            break;
1486
0
        case PLIST_UID:
1487
0
            write_uid(bplist_buff, data->intval);
1488
0
            break;
1489
0
        default:
1490
0
            break;
1491
0
        }
1492
0
    }
1493
1494
    //free intermediate objects
1495
0
    ptr_array_free(objects);
1496
0
    hash_table_destroy(ref_table);
1497
1498
    //write offsets
1499
0
    buff_len = bplist_buff->len;
1500
0
    offset_size = get_needed_bytes(buff_len);
1501
0
    offset_table_index = bplist_buff->len;
1502
0
    for (i = 0; i < num_objects; i++) {
1503
0
        uint64_t offset = be64toh(offsets[i]);
1504
0
        byte_array_append(bplist_buff, (uint8_t*)&offset + (sizeof(uint64_t) - offset_size), offset_size);
1505
0
    }
1506
0
    free(offsets);
1507
1508
    //setup trailer
1509
0
    memset(trailer.unused, '\0', sizeof(trailer.unused));
1510
0
    trailer.offset_size = offset_size;
1511
0
    trailer.ref_size = ref_size;
1512
0
    trailer.num_objects = be64toh(num_objects);
1513
0
    trailer.root_object_index = be64toh(root_object);
1514
0
    trailer.offset_table_offset = be64toh(offset_table_index);
1515
1516
0
    byte_array_append(bplist_buff, &trailer, sizeof(bplist_trailer_t));
1517
1518
    //set output buffer and size
1519
0
    *plist_bin = (char*)bplist_buff->data;
1520
0
    *length = bplist_buff->len;
1521
1522
0
    bplist_buff->data = NULL; // make sure we don't free the output buffer
1523
0
    byte_array_free(bplist_buff);
1524
1525
0
    return PLIST_ERR_SUCCESS;
1526
0
}