cJSON_Utils.c 34.8 KB
Newer Older
M
Max Bruckner 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*
  Copyright (c) 2009-2017 Dave Gamble and cJSON contributors

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.
*/

23
#pragma GCC visibility push(default)
24
#include <ctype.h>
25 26
#include <string.h>
#include <stdlib.h>
27
#include <stdio.h>
M
Max Bruckner 已提交
28
#include <limits.h>
29
#pragma GCC visibility pop
M
Max Bruckner 已提交
30

31 32
#include "cJSON_Utils.h"

M
Max Bruckner 已提交
33 34 35 36
/* define our own boolean type */
#define true ((cJSON_bool)1)
#define false ((cJSON_bool)0)

M
Max Bruckner 已提交
37
static unsigned char* cJSONUtils_strdup(const unsigned char* const string)
38
{
M
Max Bruckner 已提交
39
    size_t length = 0;
40
    unsigned char *copy = NULL;
41

M
Max Bruckner 已提交
42 43 44
    length = strlen((const char*)string) + sizeof("");
    copy = (unsigned char*) cJSON_malloc(length);
    if (copy == NULL)
45
    {
46
        return NULL;
47
    }
M
Max Bruckner 已提交
48
    memcpy(copy, string, length);
49 50 51 52

    return copy;
}

53 54
/* string comparison which doesn't consider NULL pointers equal */
static int compare_strings(const unsigned char *string1, const unsigned char *string2, const cJSON_bool case_sensitive)
55
{
M
Max Bruckner 已提交
56
    if ((string1 == NULL) || (string2 == NULL))
M
Max Bruckner 已提交
57
    {
M
Max Bruckner 已提交
58
        return 1;
M
Max Bruckner 已提交
59
    }
M
Max Bruckner 已提交
60 61

    if (string1 == string2)
M
Max Bruckner 已提交
62
    {
M
Max Bruckner 已提交
63
        return 0;
M
Max Bruckner 已提交
64
    }
M
Max Bruckner 已提交
65

66 67 68 69 70
    if (case_sensitive)
    {
        return strcmp((const char*)string1, (const char*)string2);
    }

M
Max Bruckner 已提交
71
    for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++)
M
Max Bruckner 已提交
72
    {
M
Max Bruckner 已提交
73
        if (*string1 == '\0')
M
Max Bruckner 已提交
74 75 76 77 78
        {
            return 0;
        }
    }

M
Max Bruckner 已提交
79
    return tolower(*string1) - tolower(*string2);
80 81
}

M
Max Bruckner 已提交
82
/* Compare the next path element of two JSON pointers, two NULL pointers are considered unequal: */
83
static cJSON_bool compare_pointers(const unsigned char *name, const unsigned char *pointer, const cJSON_bool case_sensitive)
84
{
M
Max Bruckner 已提交
85
    if ((name == NULL) || (pointer == NULL))
86
    {
87
        return false;
88
    }
M
Max Bruckner 已提交
89 90

    for (; (*name != '\0') && (*pointer != '\0') && (*pointer != '/'); (void)name++, pointer++) /* compare until next '/' */
91
    {
M
Max Bruckner 已提交
92
        if (*pointer == '~')
93 94
        {
            /* check for escaped '~' (~0) and '/' (~1) */
M
Max Bruckner 已提交
95
            if (((pointer[1] != '0') || (*name != '~')) && ((pointer[1] != '1') || (*name != '/')))
96
            {
M
Max Bruckner 已提交
97
                /* invalid escape sequence or wrong character in *name */
98
                return false;
99 100 101
            }
            else
            {
M
Max Bruckner 已提交
102
                pointer++;
103 104
            }
        }
105
        else if ((!case_sensitive && (tolower(*name) != tolower(*pointer))) || (case_sensitive && (*name != *pointer)))
106
        {
107
            return false;
108 109
        }
    }
M
Max Bruckner 已提交
110
    if (((*pointer != 0) && (*pointer != '/')) != (*name != 0))
111 112
    {
        /* one string has ended, the other not */
113
        return false;;
114 115
    }

116
    return true;
117 118
}

119
/* calculate the length of a string if encoded as JSON pointer with ~0 and ~1 escape sequences */
120
static size_t pointer_encoded_length(const unsigned char *string)
121
{
122 123
    size_t length;
    for (length = 0; *string != '\0'; (void)string++, length++)
124
    {
125 126
        /* character needs to be escaped? */
        if ((*string == '~') || (*string == '/'))
127
        {
128
            length++;
129 130 131
        }
    }

132
    return length;
133
}
134

135
/* copy a string while escaping '~' and '/' with ~0 and ~1 JSON pointer escape codes */
136
static void encode_string_as_pointer(unsigned char *destination, const unsigned char *source)
137
{
138
    for (; source[0] != '\0'; (void)source++, destination++)
139
    {
140
        if (source[0] == '/')
141
        {
142 143
            destination[1] = '1';
            destination++;
144
        }
145
        else if (source[0] == '~')
146
        {
147 148 149
            destination[0] = '~';
            destination[1] = '1';
            destination++;
150 151 152
        }
        else
        {
153
            destination[0] = source[0];
154 155 156
        }
    }

157
    destination[0] = '\0';
158 159
}

160
CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(const cJSON * const object, const cJSON * const target)
161
{
162 163
    size_t child_index = 0;
    cJSON *current_child = 0;
164

165 166 167
    if (object == target)
    {
        /* found */
168
        return (char*)cJSONUtils_strdup((const unsigned char*)"");
169
    }
170

171 172
    /* recursively search all children of the object or array */
    for (current_child = object->child; current_child != NULL; (void)(current_child = current_child->next), child_index++)
173
    {
174 175 176
        unsigned char *target_pointer = (unsigned char*)cJSONUtils_FindPointerFromObjectTo(current_child, target);
        /* found the target? */
        if (target_pointer != NULL)
177
        {
178
            if (cJSON_IsArray(object))
179 180
            {
                /* reserve enough memory for a 64 bit integer + '/' and '\0' */
181
                unsigned char *full_pointer = (unsigned char*)cJSON_malloc(strlen((char*)target_pointer) + 20 + sizeof("/"));
182 183 184
                /* check if conversion to unsigned long is valid
                 * This should be eliminated at compile time by dead code elimination
                 * if size_t is an alias of unsigned long, or if it is bigger */
185
                if (child_index > ULONG_MAX)
186
                {
187
                    cJSON_free(target_pointer);
188 189
                    return NULL;
                }
190 191
                sprintf((char*)full_pointer, "/%lu%s", (unsigned long)child_index, target_pointer); /* /<array_index><path> */
                cJSON_free(target_pointer);
192

193
                return (char*)full_pointer;
194
            }
195 196

            if (cJSON_IsObject(object))
197
            {
198
                unsigned char *full_pointer = (unsigned char*)cJSON_malloc(strlen((char*)target_pointer) + pointer_encoded_length((unsigned char*)current_child->string) + 2);
199
                full_pointer[0] = '/';
200
                encode_string_as_pointer(full_pointer + 1, (unsigned char*)current_child->string);
201 202
                strcat((char*)full_pointer, (char*)target_pointer);
                cJSON_free(target_pointer);
203

204
                return (char*)full_pointer;
205 206 207
            }

            /* reached leaf of the tree, found nothing */
208
            cJSON_free(target_pointer);
209
            return NULL;
210 211 212 213
        }
    }

    /* not found */
214
    return NULL;
215 216
}

217 218 219 220 221 222 223 224 225 226 227 228 229
/* non broken version of cJSON_GetArrayItem */
static cJSON *get_array_item(const cJSON *array, size_t item)
{
    cJSON *child = array ? array->child : NULL;
    while ((child != NULL) && (item > 0))
    {
        item--;
        child = child->next;
    }

    return child;
}

230 231 232 233 234 235 236 237 238 239 240
static cJSON_bool decode_array_index_from_pointer(const unsigned char * const pointer, size_t * const index)
{
    size_t parsed_index = 0;
    size_t position = 0;

    if ((pointer[0] == '0') && ((pointer[1] != '\0') && (pointer[1] != '/')))
    {
        /* leading zeroes are not permitted */
        return 0;
    }

241
    for (position = 0; (pointer[position] >= '0') && (pointer[0] <= '9'); position++)
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
    {
        parsed_index = (10 * parsed_index) + (size_t)(pointer[position] - '0');

    }

    if ((pointer[position] != '\0') && (pointer[position] != '/'))
    {
        return 0;
    }

    *index = parsed_index;

    return 1;
}

257
static cJSON *get_item_from_pointer(cJSON * const object, const char * pointer, const cJSON_bool case_sensitive)
258
{
M
Max Bruckner 已提交
259
    cJSON *current_element = object;
260
    /* follow path of the pointer */
M
Max Bruckner 已提交
261
    while ((pointer[0] == '/') && (current_element != NULL))
262
    {
M
Max Bruckner 已提交
263 264
        pointer++;
        if (cJSON_IsArray(current_element))
265
        {
266
            size_t index = 0;
267
            if (!decode_array_index_from_pointer((const unsigned char*)pointer, &index))
268
            {
269
                return NULL;
270
            }
271

M
Max Bruckner 已提交
272
            current_element = get_array_item(current_element, index);
273
        }
M
Max Bruckner 已提交
274
        else if (cJSON_IsObject(current_element))
275
        {
M
Max Bruckner 已提交
276
            current_element = current_element->child;
277
            /* GetObjectItem. */
278
            while ((current_element != NULL) && !compare_pointers((unsigned char*)current_element->string, (const unsigned char*)pointer, case_sensitive))
279
            {
M
Max Bruckner 已提交
280
                current_element = current_element->next;
281 282
            }
            /* skip to the next path token or end of string */
M
Max Bruckner 已提交
283
            while ((pointer[0] != '\0') && (pointer[0] != '/'))
284 285 286 287 288 289
            {
                pointer++;
            }
        }
        else
        {
290
            return NULL;
291 292 293
        }
    }

M
Max Bruckner 已提交
294
    return current_element;
295 296
}

297 298 299 300 301
CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON * const object, const char *pointer)
{
    return get_item_from_pointer(object, pointer, false);
}

302
/* JSON Patch implementation. */
303
static void decode_pointer_inplace(unsigned char *string)
304
{
305
    unsigned char *decoded_string = string;
306 307 308 309 310

    if (string == NULL) {
        return;
    }

311
    for (; *string; (void)decoded_string++, string++)
312
    {
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
        if (string[0] == '~')
        {
            if (string[1] == '0')
            {
                decoded_string[0] = '~';
            }
            else if (string[1] == '1')
            {
                decoded_string[1] = '/';
            }
            else
            {
                /* invalid escape sequence */
                return;
            }

            string++;
        }
331 332
    }

333
    decoded_string[0] = '\0';
334 335
}

336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
/* non-broken cJSON_DetachItemFromArray */
static cJSON *detach_item_from_array(cJSON *array, size_t which)
{
    cJSON *c = array->child;
    while (c && (which > 0))
    {
        c = c->next;
        which--;
    }
    if (!c)
    {
        /* item doesn't exist */
        return NULL;
    }
    if (c->prev)
    {
        /* not the first element */
        c->prev->next = c->next;
    }
    if (c->next)
    {
        c->next->prev = c->prev;
    }
    if (c==array->child)
    {
        array->child = c->next;
    }
    /* make sure the detached item doesn't point anywhere anymore */
    c->prev = c->next = NULL;

    return c;
}

369 370
/* detach an item at the given path */
static cJSON *detach_path(cJSON *object, const unsigned char *path)
371
{
372
    cJSON_bool case_sensitive = false;
M
Max Bruckner 已提交
373 374
    unsigned char *parent_pointer = NULL;
    unsigned char *child_pointer = NULL;
375
    cJSON *parent = NULL;
M
Max Bruckner 已提交
376
    cJSON *detached_item = NULL;
377 378

    /* copy path and split it in parent and child */
M
Max Bruckner 已提交
379 380 381
    parent_pointer = cJSONUtils_strdup(path);
    if (parent_pointer == NULL) {
        goto cleanup;
382 383
    }

M
Max Bruckner 已提交
384 385
    child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/'); /* last '/' */
    if (child_pointer == NULL)
386
    {
M
Max Bruckner 已提交
387
        goto cleanup;
388
    }
389
    /* split strings */
M
Max Bruckner 已提交
390 391
    child_pointer[0] = '\0';
    child_pointer++;
392

393
    parent = get_item_from_pointer(object, (char*)parent_pointer, case_sensitive);
394
    decode_pointer_inplace(child_pointer);
395

M
Max Bruckner 已提交
396
    if (cJSON_IsArray(parent))
397
    {
398
        size_t index = 0;
M
Max Bruckner 已提交
399
        if (!decode_array_index_from_pointer(child_pointer, &index))
400
        {
M
Max Bruckner 已提交
401
            goto cleanup;
402
        }
M
Max Bruckner 已提交
403
        detached_item = detach_item_from_array(parent, index);
404
    }
405
    else if (cJSON_IsObject(parent))
406
    {
M
Max Bruckner 已提交
407 408 409 410 411 412 413 414 415 416 417 418
        detached_item = cJSON_DetachItemFromObject(parent, (char*)child_pointer);
    }
    else
    {
        /* Couldn't find object to remove child from. */
        goto cleanup;
    }

cleanup:
    if (parent_pointer != NULL)
    {
        cJSON_free(parent_pointer);
419
    }
420

M
Max Bruckner 已提交
421
    return detached_item;
422 423
}

424
static cJSON_bool compare_json(cJSON *a, cJSON *b)
425
{
426
    if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)))
M
Max Bruckner 已提交
427 428
    {
        /* mismatched type. */
429
        return false;
M
Max Bruckner 已提交
430
    }
431
    switch (a->type & 0xFF)
M
Max Bruckner 已提交
432 433 434
    {
        case cJSON_Number:
            /* numeric mismatch. */
M
Max Bruckner 已提交
435 436
            if ((a->valueint != b->valueint) || (a->valuedouble != b->valuedouble))
            {
437
                return false;
M
Max Bruckner 已提交
438 439 440
            }
            else
            {
441
                return true;
M
Max Bruckner 已提交
442 443
            }

M
Max Bruckner 已提交
444 445
        case cJSON_String:
            /* string mismatch. */
M
Max Bruckner 已提交
446
            if (strcmp(a->valuestring, b->valuestring) != 0)
M
Max Bruckner 已提交
447
            {
448
                return false;
M
Max Bruckner 已提交
449 450 451
            }
            else
            {
452
                return true;
M
Max Bruckner 已提交
453 454
            }

M
Max Bruckner 已提交
455
        case cJSON_Array:
M
Max Bruckner 已提交
456
            for ((void)(a = a->child), b = b->child; (a != NULL) && (b != NULL); (void)(a = a->next), b = b->next)
M
Max Bruckner 已提交
457
            {
458 459
                cJSON_bool identical = compare_json(a, b);
                if (!identical)
M
Max Bruckner 已提交
460
                {
461
                    return false;
M
Max Bruckner 已提交
462 463
                }
            }
M
Max Bruckner 已提交
464

M
Max Bruckner 已提交
465
            /* array size mismatch? (one of both children is not NULL) */
M
Max Bruckner 已提交
466 467
            if ((a != NULL) || (b != NULL))
            {
468
                return false;
M
Max Bruckner 已提交
469 470 471
            }
            else
            {
472
                return true;
M
Max Bruckner 已提交
473 474
            }

M
Max Bruckner 已提交
475 476 477
        case cJSON_Object:
            cJSONUtils_SortObject(a);
            cJSONUtils_SortObject(b);
M
Max Bruckner 已提交
478
            for ((void)(a = a->child), b = b->child; (a != NULL) && (b != NULL); (void)(a = a->next), b = b->next)
M
Max Bruckner 已提交
479
            {
480
                cJSON_bool identical = false;
M
Max Bruckner 已提交
481
                /* compare object keys */
482
                if (compare_strings((unsigned char*)a->string, (unsigned char*)b->string, false))
M
Max Bruckner 已提交
483 484
                {
                    /* missing member */
485
                    return false;
M
Max Bruckner 已提交
486
                }
487 488
                identical = compare_json(a, b);
                if (!identical)
M
Max Bruckner 已提交
489
                {
490
                    return false;
M
Max Bruckner 已提交
491 492
                }
            }
M
Max Bruckner 已提交
493

M
Max Bruckner 已提交
494
            /* object length mismatch (one of both children is not null) */
M
Max Bruckner 已提交
495 496
            if ((a != NULL) || (b != NULL))
            {
497
                return false;
M
Max Bruckner 已提交
498 499 500
            }
            else
            {
501
                return true;
M
Max Bruckner 已提交
502
            }
503

M
Max Bruckner 已提交
504 505 506
        default:
            break;
    }
M
Max Bruckner 已提交
507

M
Max Bruckner 已提交
508
    /* null, true or false */
509
    return true;
510 511
}

512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549
/* non broken version of cJSON_InsertItemInArray */
static cJSON_bool insert_item_in_array(cJSON *array, size_t which, cJSON *newitem)
{
    cJSON *child = array->child;
    while (child && (which > 0))
    {
        child = child->next;
        which--;
    }
    if (which > 0)
    {
        /* item is after the end of the array */
        return 0;
    }
    if (child == NULL)
    {
        cJSON_AddItemToArray(array, newitem);
        return 1;
    }

    /* insert into the linked list */
    newitem->next = child;
    newitem->prev = child->prev;
    child->prev = newitem;

    /* was it at the beginning */
    if (child == array->child)
    {
        array->child = newitem;
    }
    else
    {
        newitem->prev->next = newitem;
    }

    return 1;
}

M
Max Bruckner 已提交
550 551
enum patch_operation { INVALID, ADD, REMOVE, REPLACE, MOVE, COPY, TEST };

M
Max Bruckner 已提交
552
static enum patch_operation decode_patch_operation(const cJSON * const patch)
553
{
M
Max Bruckner 已提交
554 555 556 557 558
    cJSON *operation = cJSON_GetObjectItem(patch, "op");
    if (!cJSON_IsString(operation))
    {
        return INVALID;
    }
559

M
Max Bruckner 已提交
560
    if (strcmp(operation->valuestring, "add") == 0)
M
Max Bruckner 已提交
561
    {
M
Max Bruckner 已提交
562
        return ADD;
M
Max Bruckner 已提交
563
    }
564

M
Max Bruckner 已提交
565
    if (strcmp(operation->valuestring, "remove") == 0)
M
Max Bruckner 已提交
566
    {
M
Max Bruckner 已提交
567
        return REMOVE;
M
Max Bruckner 已提交
568
    }
M
Max Bruckner 已提交
569 570

    if (strcmp(operation->valuestring, "replace") == 0)
M
Max Bruckner 已提交
571
    {
M
Max Bruckner 已提交
572
        return REPLACE;
M
Max Bruckner 已提交
573
    }
M
Max Bruckner 已提交
574 575

    if (strcmp(operation->valuestring, "move") == 0)
M
Max Bruckner 已提交
576
    {
M
Max Bruckner 已提交
577
        return MOVE;
M
Max Bruckner 已提交
578
    }
M
Max Bruckner 已提交
579 580

    if (strcmp(operation->valuestring, "copy") == 0)
M
Max Bruckner 已提交
581
    {
M
Max Bruckner 已提交
582
        return COPY;
M
Max Bruckner 已提交
583
    }
M
Max Bruckner 已提交
584 585

    if (strcmp(operation->valuestring, "test") == 0)
M
Max Bruckner 已提交
586
    {
M
Max Bruckner 已提交
587
        return TEST;
M
Max Bruckner 已提交
588
    }
M
Max Bruckner 已提交
589 590 591 592 593 594 595 596

    return INVALID;
}

/* overwrite and existing item with another one and free resources on the way */
static void overwrite_item(cJSON * const root, const cJSON replacement)
{
    if (root == NULL)
M
Max Bruckner 已提交
597
    {
M
Max Bruckner 已提交
598
        return;
M
Max Bruckner 已提交
599
    }
M
Max Bruckner 已提交
600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616

    if (root->string != NULL)
    {
        cJSON_free(root->string);
    }
    if (root->valuestring != NULL)
    {
        cJSON_free(root->valuestring);
    }
    if (root->child != NULL)
    {
        cJSON_Delete(root->child);
    }

    memcpy(root, &replacement, sizeof(cJSON));
}

617
static int apply_patch(cJSON *object, const cJSON *patch)
M
Max Bruckner 已提交
618
{
619
    cJSON_bool case_sensitive = false;
M
Max Bruckner 已提交
620 621 622 623 624 625 626 627 628 629
    cJSON *path = NULL;
    cJSON *value = NULL;
    cJSON *parent = NULL;
    enum patch_operation opcode = INVALID;
    unsigned char *parent_pointer = NULL;
    unsigned char *child_pointer = NULL;
    int status = 0;

    path = cJSON_GetObjectItem(patch, "path");
    if (!cJSON_IsString(path))
M
Max Bruckner 已提交
630
    {
M
Max Bruckner 已提交
631 632 633 634 635 636 637 638 639 640 641 642 643 644
        /* malformed patch. */
        status = 2;
        goto cleanup;
    }

    opcode = decode_patch_operation(patch);
    if (opcode == INVALID)
    {
        status = 3;
        goto cleanup;
    }
    else if (opcode == TEST)
    {
        /* compare value: {...} with the given path */
645
        status = !compare_json(get_item_from_pointer(object, path->valuestring, case_sensitive), cJSON_GetObjectItem(patch, "value"));
M
Max Bruckner 已提交
646
        goto cleanup;
M
Max Bruckner 已提交
647
    }
648

649 650 651 652 653
    /* special case for replacing the root */
    if (path->valuestring[0] == '\0')
    {
        if (opcode == REMOVE)
        {
M
Max Bruckner 已提交
654
            static const cJSON invalid = { NULL, NULL, NULL, cJSON_Invalid, NULL, 0, 0, NULL};
655

M
Max Bruckner 已提交
656
            overwrite_item(object, invalid);
657

M
Max Bruckner 已提交
658 659
            status = 0;
            goto cleanup;
660 661 662 663 664 665 666 667
        }

        if ((opcode == REPLACE) || (opcode == ADD))
        {
            value = cJSON_GetObjectItem(patch, "value");
            if (value == NULL)
            {
                /* missing "value" for add/replace. */
M
Max Bruckner 已提交
668 669
                status = 7;
                goto cleanup;
670 671 672 673 674 675
            }

            value = cJSON_Duplicate(value, 1);
            if (value == NULL)
            {
                /* out of memory for add/replace. */
M
Max Bruckner 已提交
676 677
                status = 8;
                goto cleanup;
678 679
            }

M
Max Bruckner 已提交
680
            overwrite_item(object, *value);
681 682 683

            /* delete the duplicated value */
            cJSON_free(value);
M
Max Bruckner 已提交
684
            value = NULL;
685

M
Max Bruckner 已提交
686 687 688 689 690 691 692 693 694
            /* the string "value" isn't needed */
            if (object->string != NULL)
            {
                cJSON_free(object->string);
                object->string = NULL;
            }

            status = 0;
            goto cleanup;
695 696 697
        }
    }

M
Max Bruckner 已提交
698
    if ((opcode == REMOVE) || (opcode == REPLACE))
M
Max Bruckner 已提交
699 700
    {
        /* Get rid of old. */
701
        cJSON *old_item = detach_path(object, (unsigned char*)path->valuestring);
702 703
        if (old_item == NULL)
        {
M
Max Bruckner 已提交
704 705
            status = 13;
            goto cleanup;
706 707
        }
        cJSON_Delete(old_item);
M
Max Bruckner 已提交
708
        if (opcode == REMOVE)
M
Max Bruckner 已提交
709
        {
710
            /* For Remove, this job is done. */
M
Max Bruckner 已提交
711 712
            status = 0;
            goto cleanup;
M
Max Bruckner 已提交
713 714
        }
    }
715

M
Max Bruckner 已提交
716
    /* Copy/Move uses "from". */
M
Max Bruckner 已提交
717
    if ((opcode == MOVE) || (opcode == COPY))
M
Max Bruckner 已提交
718 719
    {
        cJSON *from = cJSON_GetObjectItem(patch, "from");
M
Max Bruckner 已提交
720
        if (from == NULL)
M
Max Bruckner 已提交
721 722
        {
            /* missing "from" for copy/move. */
M
Max Bruckner 已提交
723 724
            status = 4;
            goto cleanup;
M
Max Bruckner 已提交
725
        }
726

M
Max Bruckner 已提交
727
        if (opcode == MOVE)
M
Max Bruckner 已提交
728
        {
729
            value = detach_path(object, (unsigned char*)from->valuestring);
M
Max Bruckner 已提交
730
        }
M
Max Bruckner 已提交
731
        if (opcode == COPY)
M
Max Bruckner 已提交
732
        {
733
            value = get_item_from_pointer(object, from->valuestring, case_sensitive);
M
Max Bruckner 已提交
734
        }
M
Max Bruckner 已提交
735
        if (value == NULL)
M
Max Bruckner 已提交
736 737
        {
            /* missing "from" for copy/move. */
M
Max Bruckner 已提交
738 739
            status = 5;
            goto cleanup;
M
Max Bruckner 已提交
740
        }
M
Max Bruckner 已提交
741
        if (opcode == COPY)
M
Max Bruckner 已提交
742 743 744
        {
            value = cJSON_Duplicate(value, 1);
        }
M
Max Bruckner 已提交
745
        if (value == NULL)
M
Max Bruckner 已提交
746 747
        {
            /* out of memory for copy/move. */
M
Max Bruckner 已提交
748 749
            status = 6;
            goto cleanup;
M
Max Bruckner 已提交
750 751 752 753 754
        }
    }
    else /* Add/Replace uses "value". */
    {
        value = cJSON_GetObjectItem(patch, "value");
M
Max Bruckner 已提交
755
        if (value == NULL)
M
Max Bruckner 已提交
756 757
        {
            /* missing "value" for add/replace. */
M
Max Bruckner 已提交
758 759
            status = 7;
            goto cleanup;
M
Max Bruckner 已提交
760 761
        }
        value = cJSON_Duplicate(value, 1);
M
Max Bruckner 已提交
762
        if (value == NULL)
M
Max Bruckner 已提交
763 764
        {
            /* out of memory for add/replace. */
M
Max Bruckner 已提交
765 766
            status = 8;
            goto cleanup;
M
Max Bruckner 已提交
767 768
        }
    }
769

M
Max Bruckner 已提交
770
    /* Now, just add "value" to "path". */
771

M
Max Bruckner 已提交
772
    /* split pointer in parent and child */
M
Max Bruckner 已提交
773 774 775
    parent_pointer = cJSONUtils_strdup((unsigned char*)path->valuestring);
    child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/');
    if (child_pointer != NULL)
M
Max Bruckner 已提交
776
    {
M
Max Bruckner 已提交
777 778
        child_pointer[0] = '\0';
        child_pointer++;
M
Max Bruckner 已提交
779
    }
780
    parent = get_item_from_pointer(object, (char*)parent_pointer, case_sensitive);
781
    decode_pointer_inplace(child_pointer);
782

M
Max Bruckner 已提交
783
    /* add, remove, replace, move, copy, test. */
M
Max Bruckner 已提交
784
    if ((parent == NULL) || (child_pointer == NULL))
M
Max Bruckner 已提交
785 786
    {
        /* Couldn't find object to add to. */
M
Max Bruckner 已提交
787 788
        status = 9;
        goto cleanup;
M
Max Bruckner 已提交
789
    }
790
    else if (cJSON_IsArray(parent))
M
Max Bruckner 已提交
791
    {
M
Max Bruckner 已提交
792
        if (strcmp((char*)child_pointer, "-") == 0)
M
Max Bruckner 已提交
793 794
        {
            cJSON_AddItemToArray(parent, value);
M
Max Bruckner 已提交
795
            value = NULL;
M
Max Bruckner 已提交
796 797 798
        }
        else
        {
799
            size_t index = 0;
M
Max Bruckner 已提交
800
            if (!decode_array_index_from_pointer(child_pointer, &index))
801
            {
M
Max Bruckner 已提交
802 803
                status = 11;
                goto cleanup;
804 805
            }

806
            if (!insert_item_in_array(parent, index, value))
807
            {
M
Max Bruckner 已提交
808 809
                status = 10;
                goto cleanup;
810
            }
M
Max Bruckner 已提交
811
            value = NULL;
M
Max Bruckner 已提交
812 813
        }
    }
814
    else if (cJSON_IsObject(parent))
M
Max Bruckner 已提交
815
    {
M
Max Bruckner 已提交
816 817 818
        cJSON_DeleteItemFromObject(parent, (char*)child_pointer);
        cJSON_AddItemToObject(parent, (char*)child_pointer, value);
        value = NULL;
M
Max Bruckner 已提交
819
    }
M
Max Bruckner 已提交
820 821 822

cleanup:
    if (value != NULL)
M
Max Bruckner 已提交
823 824 825
    {
        cJSON_Delete(value);
    }
M
Max Bruckner 已提交
826 827 828 829
    if (parent_pointer != NULL)
    {
        cJSON_free(parent_pointer);
    }
M
Max Bruckner 已提交
830

M
Max Bruckner 已提交
831
    return status;
M
Max Bruckner 已提交
832
}
833

M
Max Bruckner 已提交
834
CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON * const object, const cJSON * const patches)
835
{
M
Max Bruckner 已提交
836 837
    const cJSON *current_patch = NULL;
    int status = 0;
838

839
    if (!cJSON_IsArray(patches))
840 841 842 843
    {
        /* malformed patches. */
        return 1;
    }
M
Max Bruckner 已提交
844 845

    if (patches != NULL)
846
    {
M
Max Bruckner 已提交
847
        current_patch = patches->child;
848
    }
M
Max Bruckner 已提交
849 850

    while (current_patch != NULL)
851
    {
852
        status = apply_patch(object, current_patch);
M
Max Bruckner 已提交
853
        if (status != 0)
854
        {
M
Max Bruckner 已提交
855
            return status;
856
        }
M
Max Bruckner 已提交
857
        current_patch = current_patch->next;
858 859 860
    }

    return 0;
861
}
862

863
static void compose_patch(cJSON * const patches, const unsigned char * const operation, const unsigned char * const path, const unsigned char *suffix, const cJSON * const value)
864
{
865
    cJSON *patch = cJSON_CreateObject();
M
Max Bruckner 已提交
866
    if (patch == NULL)
867
    {
M
Max Bruckner 已提交
868
        return;
869
    }
M
Max Bruckner 已提交
870 871 872
    cJSON_AddItemToObject(patch, "op", cJSON_CreateString((const char*)operation));

    if (suffix == NULL)
873
    {
874
        cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)path));
875
    }
M
Max Bruckner 已提交
876 877
    else
    {
878
        size_t suffix_length = pointer_encoded_length(suffix);
M
Max Bruckner 已提交
879 880 881 882
        size_t path_length = strlen((const char*)path);
        unsigned char *full_path = (unsigned char*)cJSON_malloc(path_length + suffix_length + sizeof("/"));

        sprintf((char*)full_path, "%s/", (const char*)path);
883
        encode_string_as_pointer(full_path + path_length + 1, suffix);
M
Max Bruckner 已提交
884 885 886 887 888 889

        cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)full_path));
        free(full_path);
    }

    if (value != NULL)
890
    {
M
Max Bruckner 已提交
891
        cJSON_AddItemToObject(patch, "value", cJSON_Duplicate(value, 1));
892 893
    }
    cJSON_AddItemToArray(patches, patch);
894 895
}

896
CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON * const array, const char * const operation, const char * const path, const cJSON * const value)
M
Max Bruckner 已提交
897
{
898
    compose_patch(array, (const unsigned char*)operation, (const unsigned char*)path, NULL, value);
M
Max Bruckner 已提交
899
}
900

901
static void create_patches(cJSON * const patches, const unsigned char * const path, cJSON * const from, cJSON * const to)
902
{
903 904 905 906 907
    if ((from == NULL) || (to == NULL))
    {
        return;
    }

908
    if ((from->type & 0xFF) != (to->type & 0xFF))
909
    {
910
        compose_patch(patches, (const unsigned char*)"replace", path, 0, to);
911 912 913
        return;
    }

914
    switch (from->type & 0xFF)
915 916 917 918
    {
        case cJSON_Number:
            if ((from->valueint != to->valueint) || (from->valuedouble != to->valuedouble))
            {
919
                compose_patch(patches, (const unsigned char*)"replace", path, NULL, to);
920 921 922 923
            }
            return;

        case cJSON_String:
M
Max Bruckner 已提交
924
            if (strcmp(from->valuestring, to->valuestring) != 0)
925
            {
926
                compose_patch(patches, (const unsigned char*)"replace", path, NULL, to);
927 928
            }
            return;
929

930 931
        case cJSON_Array:
        {
932 933 934 935 936 937 938
            size_t index = 0;
            cJSON *from_child = from->child;
            cJSON *to_child = to->child;
            unsigned char *new_path = (unsigned char*)cJSON_malloc(strlen((const char*)path) + 20 + sizeof("/")); /* Allow space for 64bit int. log10(2^64) = 20 */

            /* generate patches for all array elements that exist in both "from" and "to" */
            for (index = 0; (from_child != NULL) && (to_child != NULL); (void)(from_child = from_child->next), (void)(to_child = to_child->next), index++)
939
            {
940 941 942
                /* check if conversion to unsigned long is valid
                 * This should be eliminated at compile time by dead code elimination
                 * if size_t is an alias of unsigned long, or if it is bigger */
943
                if (index > ULONG_MAX)
944
                {
945
                    free(new_path);
946 947
                    return;
                }
948
                sprintf((char*)new_path, "%s/%lu", path, (unsigned long)index); /* path of the current array element */
949
                create_patches(patches, new_path, from_child, to_child);
950
            }
951

952
            /* remove leftover elements from 'from' that are not in 'to' */
953
            for (; (from_child != NULL); (void)(from_child = from_child->next))
954
            {
955 956 957
                /* check if conversion to unsigned long is valid
                 * This should be eliminated at compile time by dead code elimination
                 * if size_t is an alias of unsigned long, or if it is bigger */
958
                if (index > ULONG_MAX)
959
                {
960
                    free(new_path);
961 962
                    return;
                }
963
                sprintf((char*)new_path, "%lu", (unsigned long)index);
964
                compose_patch(patches, (const unsigned char*)"remove", path, new_path, NULL);
965 966
            }
            /* add new elements in 'to' that were not in 'from' */
967
            for (; (to_child != NULL); (void)(to_child = to_child->next), index++)
968
            {
969
                compose_patch(patches, (const unsigned char*)"add", path, (const unsigned char*)"-", to_child);
970
            }
971
            free(new_path);
972 973 974 975 976
            return;
        }

        case cJSON_Object:
        {
977 978
            cJSON *from_child = NULL;
            cJSON *to_child = NULL;
979 980 981
            cJSONUtils_SortObject(from);
            cJSONUtils_SortObject(to);

982 983
            from_child = from->child;
            to_child = to->child;
984
            /* for all object values in the object with more of them */
985
            while ((from_child != NULL) || (to_child != NULL))
986
            {
987 988 989 990 991 992 993 994 995 996 997
                int diff;
                if (from_child == NULL)
                {
                    diff = 1;
                }
                else if (to_child == NULL)
                {
                    diff = -1;
                }
                else
                {
998
                    diff = compare_strings((unsigned char*)from_child->string, (unsigned char*)to_child->string, false);
999 1000 1001
                }

                if (diff == 0)
1002 1003
                {
                    /* both object keys are the same */
1004
                    size_t path_length = strlen((const char*)path);
1005
                    size_t from_child_name_length = pointer_encoded_length((unsigned char*)from_child->string);
1006 1007 1008
                    unsigned char *new_path = (unsigned char*)cJSON_malloc(path_length + from_child_name_length + sizeof("/"));

                    sprintf((char*)new_path, "%s/", path);
1009
                    encode_string_as_pointer(new_path + path_length + 1, (unsigned char*)from_child->string);
1010

1011
                    /* create a patch for the element */
1012
                    create_patches(patches, new_path, from_child, to_child);
1013 1014 1015 1016
                    free(new_path);

                    from_child = from_child->next;
                    to_child = to_child->next;
1017 1018 1019 1020
                }
                else if (diff < 0)
                {
                    /* object element doesn't exist in 'to' --> remove it */
1021
                    compose_patch(patches, (const unsigned char*)"remove", path, (unsigned char*)from_child->string, NULL);
1022 1023

                    from_child = from_child->next;
1024 1025 1026 1027
                }
                else
                {
                    /* object element doesn't exist in 'from' --> add it */
1028
                    compose_patch(patches, (const unsigned char*)"add", path, (unsigned char*)to_child->string, to_child);
1029 1030

                    to_child = to_child->next;
1031 1032 1033 1034 1035 1036 1037 1038 1039
                }
            }
            return;
        }

        default:
            break;
    }
}
1040

1041
CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON * const from, cJSON * const to)
1042
{
1043
    cJSON *patches = cJSON_CreateArray();
1044
    create_patches(patches, (const unsigned char*)"", from, to);
1045

1046 1047
    return patches;
}
1048

M
Max Bruckner 已提交
1049
/* sort lists using mergesort */
M
Max Bruckner 已提交
1050
static cJSON *sort_list(cJSON *list)
1051
{
M
Max Bruckner 已提交
1052 1053
    cJSON *first = list;
    cJSON *second = list;
M
Max Bruckner 已提交
1054 1055 1056
    cJSON *current_item = list;
    cJSON *result = list;
    cJSON *result_tail = NULL;
1057

M
Max Bruckner 已提交
1058
    if ((list == NULL) || (list->next == NULL))
M
Max Bruckner 已提交
1059 1060
    {
        /* One entry is sorted already. */
M
Max Bruckner 已提交
1061
        return result;
M
Max Bruckner 已提交
1062
    }
1063

1064
    while ((current_item != NULL) && (current_item->next != NULL) && (compare_strings((unsigned char*)current_item->string, (unsigned char*)current_item->next->string, false) < 0))
M
Max Bruckner 已提交
1065 1066
    {
        /* Test for list sorted. */
M
Max Bruckner 已提交
1067
        current_item = current_item->next;
M
Max Bruckner 已提交
1068
    }
M
Max Bruckner 已提交
1069
    if ((current_item == NULL) || (current_item->next == NULL))
M
Max Bruckner 已提交
1070 1071
    {
        /* Leave sorted lists unmodified. */
M
Max Bruckner 已提交
1072
        return result;
M
Max Bruckner 已提交
1073
    }
1074

M
Max Bruckner 已提交
1075 1076 1077
    /* reset pointer to the beginning */
    current_item = list;
    while (current_item != NULL)
M
Max Bruckner 已提交
1078 1079 1080
    {
        /* Walk two pointers to find the middle. */
        second = second->next;
M
Max Bruckner 已提交
1081 1082 1083
        current_item = current_item->next;
        /* advances current_item two steps at a time */
        if (current_item != NULL)
M
Max Bruckner 已提交
1084
        {
M
Max Bruckner 已提交
1085
            current_item = current_item->next;
M
Max Bruckner 已提交
1086 1087
        }
    }
M
Max Bruckner 已提交
1088
    if ((second != NULL) && (second->prev != NULL))
M
Max Bruckner 已提交
1089 1090
    {
        /* Split the lists */
1091
        second->prev->next = NULL;
M
Max Bruckner 已提交
1092
    }
1093

M
Max Bruckner 已提交
1094
    /* Recursively sort the sub-lists. */
M
Max Bruckner 已提交
1095 1096
    first = sort_list(first);
    second = sort_list(second);
M
Max Bruckner 已提交
1097
    result = NULL;
M
Max Bruckner 已提交
1098

M
Max Bruckner 已提交
1099 1100
    /* Merge the sub-lists */
    while ((first != NULL) && (second != NULL))
M
Max Bruckner 已提交
1101
    {
M
Max Bruckner 已提交
1102
        cJSON *smaller = NULL;
1103
        if (compare_strings((unsigned char*)first->string, (unsigned char*)second->string, false) < 0)
M
Max Bruckner 已提交
1104
        {
M
Max Bruckner 已提交
1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127
            smaller = first;
        }
        else
        {
            smaller = second;
        }

        if (result == NULL)
        {
            /* start merged list with the smaller element */
            result_tail = smaller;
            result = smaller;
        }
        else
        {
            /* add smaller element to the list */
            result_tail->next = smaller;
            smaller->prev = result_tail;
            result_tail = smaller;
        }

        if (first == smaller)
        {
M
Max Bruckner 已提交
1128 1129 1130 1131 1132 1133 1134
            first = first->next;
        }
        else
        {
            second = second->next;
        }
    }
M
Max Bruckner 已提交
1135 1136

    if (first != NULL)
M
Max Bruckner 已提交
1137 1138
    {
        /* Append rest of first list. */
M
Max Bruckner 已提交
1139
        if (result == NULL)
M
Max Bruckner 已提交
1140 1141 1142
        {
            return first;
        }
M
Max Bruckner 已提交
1143 1144
        result_tail->next = first;
        first->prev = result_tail;
M
Max Bruckner 已提交
1145
    }
M
Max Bruckner 已提交
1146
    if (second != NULL)
M
Max Bruckner 已提交
1147 1148
    {
        /* Append rest of second list */
M
Max Bruckner 已提交
1149
        if (result == NULL)
M
Max Bruckner 已提交
1150 1151 1152
        {
            return second;
        }
M
Max Bruckner 已提交
1153 1154
        result_tail->next = second;
        second->prev = result_tail;
M
Max Bruckner 已提交
1155 1156
    }

M
Max Bruckner 已提交
1157
    return result;
1158 1159
}

M
Max Bruckner 已提交
1160
CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON * const object)
M
Max Bruckner 已提交
1161
{
M
Max Bruckner 已提交
1162
    object->child = sort_list(object->child);
M
Max Bruckner 已提交
1163
}
1164

M
Max Bruckner 已提交
1165
CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, const cJSON * const patch)
1166
{
M
Max Bruckner 已提交
1167 1168
    cJSON *patch_child = NULL;

1169
    if (!cJSON_IsObject(patch))
M
Max Bruckner 已提交
1170 1171 1172 1173 1174
    {
        /* scalar value, array or NULL, just duplicate */
        cJSON_Delete(target);
        return cJSON_Duplicate(patch, 1);
    }
1175

1176
    if (!cJSON_IsObject(target))
M
Max Bruckner 已提交
1177 1178 1179 1180 1181
    {
        cJSON_Delete(target);
        target = cJSON_CreateObject();
    }

M
Max Bruckner 已提交
1182 1183
    patch_child = patch->child;
    while (patch_child != NULL)
M
Max Bruckner 已提交
1184
    {
M
Max Bruckner 已提交
1185
        if (cJSON_IsNull(patch_child))
M
Max Bruckner 已提交
1186 1187
        {
            /* NULL is the indicator to remove a value, see RFC7396 */
M
Max Bruckner 已提交
1188
            cJSON_DeleteItemFromObject(target, patch_child->string);
M
Max Bruckner 已提交
1189 1190 1191
        }
        else
        {
M
Max Bruckner 已提交
1192 1193
            cJSON *replace_me = cJSON_DetachItemFromObject(target, patch_child->string);
            cJSON_AddItemToObject(target, patch_child->string, cJSONUtils_MergePatch(replace_me, patch_child));
M
Max Bruckner 已提交
1194
        }
M
Max Bruckner 已提交
1195
        patch_child = patch_child->next;
M
Max Bruckner 已提交
1196 1197
    }
    return target;
1198
}
1199

1200
CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON * const from, cJSON * const to)
1201
{
1202 1203
    cJSON *from_child = NULL;
    cJSON *to_child = NULL;
1204
    cJSON *patch = NULL;
1205
    if (to == NULL)
1206 1207 1208 1209
    {
        /* patch to delete everything */
        return cJSON_CreateNull();
    }
1210
    if (!cJSON_IsObject(to) || !cJSON_IsObject(from))
1211 1212 1213 1214 1215 1216 1217
    {
        return cJSON_Duplicate(to, 1);
    }

    cJSONUtils_SortObject(from);
    cJSONUtils_SortObject(to);

1218 1219
    from_child = from->child;
    to_child = to->child;
1220
    patch = cJSON_CreateObject();
1221
    while (from_child || to_child)
1222
    {
1223 1224 1225 1226 1227
        int diff;
        if (from_child != NULL)
        {
            if (to_child != NULL)
            {
M
Max Bruckner 已提交
1228
                diff = strcmp(from_child->string, to_child->string);
1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240
            }
            else
            {
                diff = -1;
            }
        }
        else
        {
            diff = 1;
        }

        if (diff < 0)
1241 1242
        {
            /* from has a value that to doesn't have -> remove */
1243 1244 1245
            cJSON_AddItemToObject(patch, from_child->string, cJSON_CreateNull());

            from_child = from_child->next;
1246
        }
1247
        else if (diff > 0)
1248 1249
        {
            /* to has a value that from doesn't have -> add to patch */
1250 1251 1252
            cJSON_AddItemToObject(patch, to_child->string, cJSON_Duplicate(to_child, 1));

            to_child = to_child->next;
1253 1254 1255 1256
        }
        else
        {
            /* object key exists in both objects */
1257
            if (!compare_json(from_child, to_child))
1258 1259
            {
                /* not identical --> generate a patch */
1260
                cJSON_AddItemToObject(patch, to_child->string, cJSONUtils_GenerateMergePatch(from_child, to_child));
1261
            }
1262

1263
            /* next key in the object */
1264 1265
            from_child = from_child->next;
            to_child = to_child->next;
1266 1267
        }
    }
1268
    if (patch->child == NULL)
1269
    {
1270
        /* no patch generated */
1271
        cJSON_Delete(patch);
1272
        return NULL;
1273 1274 1275
    }

    return patch;
M
Max Bruckner 已提交
1276
}