cJSON_Utils.c 34.5 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;
}

M
Max Bruckner 已提交
53
/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */
54
static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2)
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

    for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++)
M
Max Bruckner 已提交
67
    {
M
Max Bruckner 已提交
68
        if (*string1 == '\0')
M
Max Bruckner 已提交
69 70 71 72 73
        {
            return 0;
        }
    }

M
Max Bruckner 已提交
74
    return tolower(*string1) - tolower(*string2);
75 76
}

M
Max Bruckner 已提交
77
/* Compare the next path element of two JSON pointers, two NULL pointers are considered unequal: */
78
static cJSON_bool case_insensitive_pointer_comparison(const unsigned char *name, const unsigned char *pointer)
79
{
M
Max Bruckner 已提交
80
    if ((name == NULL) || (pointer == NULL))
81
    {
82
        return false;
83
    }
M
Max Bruckner 已提交
84 85

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

111
    return true;
112 113
}

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

127
    return length;
128
}
129

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

152
    destination[0] = '\0';
153 154
}

155
CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(const cJSON * const object, const cJSON * const target)
156
{
157 158
    size_t child_index = 0;
    cJSON *current_child = 0;
159

160 161 162
    if (object == target)
    {
        /* found */
163
        return (char*)cJSONUtils_strdup((const unsigned char*)"");
164
    }
165

166 167
    /* 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++)
168
    {
169 170 171
        unsigned char *target_pointer = (unsigned char*)cJSONUtils_FindPointerFromObjectTo(current_child, target);
        /* found the target? */
        if (target_pointer != NULL)
172
        {
173
            if (cJSON_IsArray(object))
174 175
            {
                /* reserve enough memory for a 64 bit integer + '/' and '\0' */
176
                unsigned char *full_pointer = (unsigned char*)cJSON_malloc(strlen((char*)target_pointer) + 20 + sizeof("/"));
177 178 179
                /* 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 */
180
                if (child_index > ULONG_MAX)
181
                {
182
                    cJSON_free(target_pointer);
183 184
                    return NULL;
                }
185 186
                sprintf((char*)full_pointer, "/%lu%s", (unsigned long)child_index, target_pointer); /* /<array_index><path> */
                cJSON_free(target_pointer);
187

188
                return (char*)full_pointer;
189
            }
190 191

            if (cJSON_IsObject(object))
192
            {
193
                unsigned char *full_pointer = (unsigned char*)cJSON_malloc(strlen((char*)target_pointer) + pointer_encoded_length((unsigned char*)current_child->string) + 2);
194
                full_pointer[0] = '/';
195
                encode_string_as_pointer(full_pointer + 1, (unsigned char*)current_child->string);
196 197
                strcat((char*)full_pointer, (char*)target_pointer);
                cJSON_free(target_pointer);
198

199
                return (char*)full_pointer;
200 201 202
            }

            /* reached leaf of the tree, found nothing */
203
            cJSON_free(target_pointer);
204
            return NULL;
205 206 207 208
        }
    }

    /* not found */
209
    return NULL;
210 211
}

212 213 214 215 216 217 218 219 220 221 222 223 224
/* 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;
}

225 226 227 228 229 230 231 232 233 234 235
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;
    }

236
    for (position = 0; (pointer[position] >= '0') && (pointer[0] <= '9'); position++)
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
    {
        parsed_index = (10 * parsed_index) + (size_t)(pointer[position] - '0');

    }

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

    *index = parsed_index;

    return 1;
}

M
Max Bruckner 已提交
252
CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON * const object, const char *pointer)
253
{
M
Max Bruckner 已提交
254
    cJSON *current_element = object;
255
    /* follow path of the pointer */
M
Max Bruckner 已提交
256
    while ((pointer[0] == '/') && (current_element != NULL))
257
    {
M
Max Bruckner 已提交
258 259
        pointer++;
        if (cJSON_IsArray(current_element))
260
        {
261
            size_t index = 0;
262
            if (!decode_array_index_from_pointer((const unsigned char*)pointer, &index))
263
            {
264
                return NULL;
265
            }
266

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

M
Max Bruckner 已提交
289
    return current_element;
290 291
}

292
/* JSON Patch implementation. */
293
static void decode_pointer_inplace(unsigned char *string)
294
{
295
    unsigned char *decoded_string = string;
296 297 298 299 300

    if (string == NULL) {
        return;
    }

301
    for (; *string; (void)decoded_string++, string++)
302
    {
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
        if (string[0] == '~')
        {
            if (string[1] == '0')
            {
                decoded_string[0] = '~';
            }
            else if (string[1] == '1')
            {
                decoded_string[1] = '/';
            }
            else
            {
                /* invalid escape sequence */
                return;
            }

            string++;
        }
321 322
    }

323
    decoded_string[0] = '\0';
324 325
}

326 327 328 329 330 331 332 333 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
/* 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;
}

359 360
/* detach an item at the given path */
static cJSON *detach_path(cJSON *object, const unsigned char *path)
361
{
M
Max Bruckner 已提交
362 363
    unsigned char *parent_pointer = NULL;
    unsigned char *child_pointer = NULL;
364
    cJSON *parent = NULL;
M
Max Bruckner 已提交
365
    cJSON *detached_item = NULL;
366 367

    /* copy path and split it in parent and child */
M
Max Bruckner 已提交
368 369 370
    parent_pointer = cJSONUtils_strdup(path);
    if (parent_pointer == NULL) {
        goto cleanup;
371 372
    }

M
Max Bruckner 已提交
373 374
    child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/'); /* last '/' */
    if (child_pointer == NULL)
375
    {
M
Max Bruckner 已提交
376
        goto cleanup;
377
    }
378
    /* split strings */
M
Max Bruckner 已提交
379 380
    child_pointer[0] = '\0';
    child_pointer++;
381

M
Max Bruckner 已提交
382
    parent = cJSONUtils_GetPointer(object, (char*)parent_pointer);
383
    decode_pointer_inplace(child_pointer);
384

M
Max Bruckner 已提交
385
    if (cJSON_IsArray(parent))
386
    {
387
        size_t index = 0;
M
Max Bruckner 已提交
388
        if (!decode_array_index_from_pointer(child_pointer, &index))
389
        {
M
Max Bruckner 已提交
390
            goto cleanup;
391
        }
M
Max Bruckner 已提交
392
        detached_item = detach_item_from_array(parent, index);
393
    }
394
    else if (cJSON_IsObject(parent))
395
    {
M
Max Bruckner 已提交
396 397 398 399 400 401 402 403 404 405 406 407
        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);
408
    }
409

M
Max Bruckner 已提交
410
    return detached_item;
411 412
}

413
static cJSON_bool compare_json(cJSON *a, cJSON *b)
414
{
415
    if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)))
M
Max Bruckner 已提交
416 417
    {
        /* mismatched type. */
418
        return false;
M
Max Bruckner 已提交
419
    }
420
    switch (a->type & 0xFF)
M
Max Bruckner 已提交
421 422 423
    {
        case cJSON_Number:
            /* numeric mismatch. */
M
Max Bruckner 已提交
424 425
            if ((a->valueint != b->valueint) || (a->valuedouble != b->valuedouble))
            {
426
                return false;
M
Max Bruckner 已提交
427 428 429
            }
            else
            {
430
                return true;
M
Max Bruckner 已提交
431 432
            }

M
Max Bruckner 已提交
433 434
        case cJSON_String:
            /* string mismatch. */
M
Max Bruckner 已提交
435 436
            if (strcmp(a->valuestring, b->valuestring) != 0)
            {
437
                return false;
M
Max Bruckner 已提交
438 439 440
            }
            else
            {
441
                return true;
M
Max Bruckner 已提交
442 443
            }

M
Max Bruckner 已提交
444
        case cJSON_Array:
M
Max Bruckner 已提交
445
            for ((void)(a = a->child), b = b->child; (a != NULL) && (b != NULL); (void)(a = a->next), b = b->next)
M
Max Bruckner 已提交
446
            {
447 448
                cJSON_bool identical = compare_json(a, b);
                if (!identical)
M
Max Bruckner 已提交
449
                {
450
                    return false;
M
Max Bruckner 已提交
451 452
                }
            }
M
Max Bruckner 已提交
453

M
Max Bruckner 已提交
454
            /* array size mismatch? (one of both children is not NULL) */
M
Max Bruckner 已提交
455 456
            if ((a != NULL) || (b != NULL))
            {
457
                return false;
M
Max Bruckner 已提交
458 459 460
            }
            else
            {
461
                return true;
M
Max Bruckner 已提交
462 463
            }

M
Max Bruckner 已提交
464 465 466
        case cJSON_Object:
            cJSONUtils_SortObject(a);
            cJSONUtils_SortObject(b);
M
Max Bruckner 已提交
467
            for ((void)(a = a->child), b = b->child; (a != NULL) && (b != NULL); (void)(a = a->next), b = b->next)
M
Max Bruckner 已提交
468
            {
469
                cJSON_bool identical = false;
M
Max Bruckner 已提交
470
                /* compare object keys */
471
                if (case_insensitive_strcmp((unsigned char*)a->string, (unsigned char*)b->string))
M
Max Bruckner 已提交
472 473
                {
                    /* missing member */
474
                    return false;
M
Max Bruckner 已提交
475
                }
476 477
                identical = compare_json(a, b);
                if (!identical)
M
Max Bruckner 已提交
478
                {
479
                    return false;
M
Max Bruckner 已提交
480 481
                }
            }
M
Max Bruckner 已提交
482

M
Max Bruckner 已提交
483
            /* object length mismatch (one of both children is not null) */
M
Max Bruckner 已提交
484 485
            if ((a != NULL) || (b != NULL))
            {
486
                return false;
M
Max Bruckner 已提交
487 488 489
            }
            else
            {
490
                return true;
M
Max Bruckner 已提交
491
            }
492

M
Max Bruckner 已提交
493 494 495
        default:
            break;
    }
M
Max Bruckner 已提交
496

M
Max Bruckner 已提交
497
    /* null, true or false */
498
    return true;
499 500
}

501 502 503 504 505 506 507 508 509 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
/* 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 已提交
539 540
enum patch_operation { INVALID, ADD, REMOVE, REPLACE, MOVE, COPY, TEST };

M
Max Bruckner 已提交
541
static enum patch_operation decode_patch_operation(const cJSON * const patch)
542
{
M
Max Bruckner 已提交
543 544 545 546 547
    cJSON *operation = cJSON_GetObjectItem(patch, "op");
    if (!cJSON_IsString(operation))
    {
        return INVALID;
    }
548

M
Max Bruckner 已提交
549
    if (strcmp(operation->valuestring, "add") == 0)
M
Max Bruckner 已提交
550
    {
M
Max Bruckner 已提交
551
        return ADD;
M
Max Bruckner 已提交
552
    }
553

M
Max Bruckner 已提交
554
    if (strcmp(operation->valuestring, "remove") == 0)
M
Max Bruckner 已提交
555
    {
M
Max Bruckner 已提交
556
        return REMOVE;
M
Max Bruckner 已提交
557
    }
M
Max Bruckner 已提交
558 559

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

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

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

    if (strcmp(operation->valuestring, "test") == 0)
M
Max Bruckner 已提交
575
    {
M
Max Bruckner 已提交
576
        return TEST;
M
Max Bruckner 已提交
577
    }
M
Max Bruckner 已提交
578 579 580 581 582 583 584 585

    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 已提交
586
    {
M
Max Bruckner 已提交
587
        return;
M
Max Bruckner 已提交
588
    }
M
Max Bruckner 已提交
589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617

    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));
}

static int cJSONUtils_ApplyPatch(cJSON *object, const cJSON *patch)
{
    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 已提交
618
    {
M
Max Bruckner 已提交
619 620 621 622 623 624 625 626 627 628 629 630 631 632
        /* 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 */
633
        status = !compare_json(cJSONUtils_GetPointer(object, path->valuestring), cJSON_GetObjectItem(patch, "value"));
M
Max Bruckner 已提交
634
        goto cleanup;
M
Max Bruckner 已提交
635
    }
636

637 638 639 640 641
    /* special case for replacing the root */
    if (path->valuestring[0] == '\0')
    {
        if (opcode == REMOVE)
        {
M
Max Bruckner 已提交
642
            static const cJSON invalid = { NULL, NULL, NULL, cJSON_Invalid, NULL, 0, 0, NULL};
643

M
Max Bruckner 已提交
644
            overwrite_item(object, invalid);
645

M
Max Bruckner 已提交
646 647
            status = 0;
            goto cleanup;
648 649 650 651 652 653 654 655
        }

        if ((opcode == REPLACE) || (opcode == ADD))
        {
            value = cJSON_GetObjectItem(patch, "value");
            if (value == NULL)
            {
                /* missing "value" for add/replace. */
M
Max Bruckner 已提交
656 657
                status = 7;
                goto cleanup;
658 659 660 661 662 663
            }

            value = cJSON_Duplicate(value, 1);
            if (value == NULL)
            {
                /* out of memory for add/replace. */
M
Max Bruckner 已提交
664 665
                status = 8;
                goto cleanup;
666 667
            }

M
Max Bruckner 已提交
668
            overwrite_item(object, *value);
669 670 671

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

M
Max Bruckner 已提交
674 675 676 677 678 679 680 681 682
            /* the string "value" isn't needed */
            if (object->string != NULL)
            {
                cJSON_free(object->string);
                object->string = NULL;
            }

            status = 0;
            goto cleanup;
683 684 685
        }
    }

M
Max Bruckner 已提交
686
    if ((opcode == REMOVE) || (opcode == REPLACE))
M
Max Bruckner 已提交
687 688
    {
        /* Get rid of old. */
689
        cJSON *old_item = detach_path(object, (unsigned char*)path->valuestring);
690 691
        if (old_item == NULL)
        {
M
Max Bruckner 已提交
692 693
            status = 13;
            goto cleanup;
694 695
        }
        cJSON_Delete(old_item);
M
Max Bruckner 已提交
696
        if (opcode == REMOVE)
M
Max Bruckner 已提交
697
        {
698
            /* For Remove, this job is done. */
M
Max Bruckner 已提交
699 700
            status = 0;
            goto cleanup;
M
Max Bruckner 已提交
701 702
        }
    }
703

M
Max Bruckner 已提交
704
    /* Copy/Move uses "from". */
M
Max Bruckner 已提交
705
    if ((opcode == MOVE) || (opcode == COPY))
M
Max Bruckner 已提交
706 707
    {
        cJSON *from = cJSON_GetObjectItem(patch, "from");
M
Max Bruckner 已提交
708
        if (from == NULL)
M
Max Bruckner 已提交
709 710
        {
            /* missing "from" for copy/move. */
M
Max Bruckner 已提交
711 712
            status = 4;
            goto cleanup;
M
Max Bruckner 已提交
713
        }
714

M
Max Bruckner 已提交
715
        if (opcode == MOVE)
M
Max Bruckner 已提交
716
        {
717
            value = detach_path(object, (unsigned char*)from->valuestring);
M
Max Bruckner 已提交
718
        }
M
Max Bruckner 已提交
719
        if (opcode == COPY)
M
Max Bruckner 已提交
720 721 722
        {
            value = cJSONUtils_GetPointer(object, from->valuestring);
        }
M
Max Bruckner 已提交
723
        if (value == NULL)
M
Max Bruckner 已提交
724 725
        {
            /* missing "from" for copy/move. */
M
Max Bruckner 已提交
726 727
            status = 5;
            goto cleanup;
M
Max Bruckner 已提交
728
        }
M
Max Bruckner 已提交
729
        if (opcode == COPY)
M
Max Bruckner 已提交
730 731 732
        {
            value = cJSON_Duplicate(value, 1);
        }
M
Max Bruckner 已提交
733
        if (value == NULL)
M
Max Bruckner 已提交
734 735
        {
            /* out of memory for copy/move. */
M
Max Bruckner 已提交
736 737
            status = 6;
            goto cleanup;
M
Max Bruckner 已提交
738 739 740 741 742
        }
    }
    else /* Add/Replace uses "value". */
    {
        value = cJSON_GetObjectItem(patch, "value");
M
Max Bruckner 已提交
743
        if (value == NULL)
M
Max Bruckner 已提交
744 745
        {
            /* missing "value" for add/replace. */
M
Max Bruckner 已提交
746 747
            status = 7;
            goto cleanup;
M
Max Bruckner 已提交
748 749
        }
        value = cJSON_Duplicate(value, 1);
M
Max Bruckner 已提交
750
        if (value == NULL)
M
Max Bruckner 已提交
751 752
        {
            /* out of memory for add/replace. */
M
Max Bruckner 已提交
753 754
            status = 8;
            goto cleanup;
M
Max Bruckner 已提交
755 756
        }
    }
757

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

M
Max Bruckner 已提交
760
    /* split pointer in parent and child */
M
Max Bruckner 已提交
761 762 763
    parent_pointer = cJSONUtils_strdup((unsigned char*)path->valuestring);
    child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/');
    if (child_pointer != NULL)
M
Max Bruckner 已提交
764
    {
M
Max Bruckner 已提交
765 766
        child_pointer[0] = '\0';
        child_pointer++;
M
Max Bruckner 已提交
767
    }
M
Max Bruckner 已提交
768
    parent = cJSONUtils_GetPointer(object, (char*)parent_pointer);
769
    decode_pointer_inplace(child_pointer);
770

M
Max Bruckner 已提交
771
    /* add, remove, replace, move, copy, test. */
M
Max Bruckner 已提交
772
    if ((parent == NULL) || (child_pointer == NULL))
M
Max Bruckner 已提交
773 774
    {
        /* Couldn't find object to add to. */
M
Max Bruckner 已提交
775 776
        status = 9;
        goto cleanup;
M
Max Bruckner 已提交
777
    }
778
    else if (cJSON_IsArray(parent))
M
Max Bruckner 已提交
779
    {
M
Max Bruckner 已提交
780
        if (strcmp((char*)child_pointer, "-") == 0)
M
Max Bruckner 已提交
781 782
        {
            cJSON_AddItemToArray(parent, value);
M
Max Bruckner 已提交
783
            value = NULL;
M
Max Bruckner 已提交
784 785 786
        }
        else
        {
787
            size_t index = 0;
M
Max Bruckner 已提交
788
            if (!decode_array_index_from_pointer(child_pointer, &index))
789
            {
M
Max Bruckner 已提交
790 791
                status = 11;
                goto cleanup;
792 793
            }

794
            if (!insert_item_in_array(parent, index, value))
795
            {
M
Max Bruckner 已提交
796 797
                status = 10;
                goto cleanup;
798
            }
M
Max Bruckner 已提交
799
            value = NULL;
M
Max Bruckner 已提交
800 801
        }
    }
802
    else if (cJSON_IsObject(parent))
M
Max Bruckner 已提交
803
    {
M
Max Bruckner 已提交
804 805 806
        cJSON_DeleteItemFromObject(parent, (char*)child_pointer);
        cJSON_AddItemToObject(parent, (char*)child_pointer, value);
        value = NULL;
M
Max Bruckner 已提交
807
    }
M
Max Bruckner 已提交
808 809 810

cleanup:
    if (value != NULL)
M
Max Bruckner 已提交
811 812 813
    {
        cJSON_Delete(value);
    }
M
Max Bruckner 已提交
814 815 816 817
    if (parent_pointer != NULL)
    {
        cJSON_free(parent_pointer);
    }
M
Max Bruckner 已提交
818

M
Max Bruckner 已提交
819
    return status;
M
Max Bruckner 已提交
820
}
821

M
Max Bruckner 已提交
822
CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON * const object, const cJSON * const patches)
823
{
M
Max Bruckner 已提交
824 825
    const cJSON *current_patch = NULL;
    int status = 0;
826

827
    if (!cJSON_IsArray(patches))
828 829 830 831
    {
        /* malformed patches. */
        return 1;
    }
M
Max Bruckner 已提交
832 833

    if (patches != NULL)
834
    {
M
Max Bruckner 已提交
835
        current_patch = patches->child;
836
    }
M
Max Bruckner 已提交
837 838

    while (current_patch != NULL)
839
    {
M
Max Bruckner 已提交
840 841
        status = cJSONUtils_ApplyPatch(object, current_patch);
        if (status != 0)
842
        {
M
Max Bruckner 已提交
843
            return status;
844
        }
M
Max Bruckner 已提交
845
        current_patch = current_patch->next;
846 847 848
    }

    return 0;
849
}
850

M
Max Bruckner 已提交
851
static void cJSONUtils_GeneratePatch(cJSON * const patches, const unsigned char * const operation, const unsigned char * const path, const unsigned char *suffix, const cJSON * const value)
852
{
853
    cJSON *patch = cJSON_CreateObject();
M
Max Bruckner 已提交
854
    if (patch == NULL)
855
    {
M
Max Bruckner 已提交
856
        return;
857
    }
M
Max Bruckner 已提交
858 859 860
    cJSON_AddItemToObject(patch, "op", cJSON_CreateString((const char*)operation));

    if (suffix == NULL)
861
    {
862
        cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)path));
863
    }
M
Max Bruckner 已提交
864 865
    else
    {
866
        size_t suffix_length = pointer_encoded_length(suffix);
M
Max Bruckner 已提交
867 868 869 870
        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);
871
        encode_string_as_pointer(full_path + path_length + 1, suffix);
M
Max Bruckner 已提交
872 873 874 875 876 877

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

    if (value != NULL)
878
    {
M
Max Bruckner 已提交
879
        cJSON_AddItemToObject(patch, "value", cJSON_Duplicate(value, 1));
880 881
    }
    cJSON_AddItemToArray(patches, patch);
882 883
}

884
CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON * const array, const char * const operation, const char * const path, const cJSON * const value)
M
Max Bruckner 已提交
885
{
886
    cJSONUtils_GeneratePatch(array, (const unsigned char*)operation, (const unsigned char*)path, NULL, value);
M
Max Bruckner 已提交
887
}
888

889
static void cJSONUtils_CompareToPatch(cJSON * const patches, const unsigned char * const path, cJSON * const from, cJSON * const to)
890
{
891 892 893 894 895
    if ((from == NULL) || (to == NULL))
    {
        return;
    }

896
    if ((from->type & 0xFF) != (to->type & 0xFF))
897
    {
898
        cJSONUtils_GeneratePatch(patches, (const unsigned char*)"replace", path, 0, to);
899 900 901
        return;
    }

902
    switch (from->type & 0xFF)
903 904 905 906
    {
        case cJSON_Number:
            if ((from->valueint != to->valueint) || (from->valuedouble != to->valuedouble))
            {
907
                cJSONUtils_GeneratePatch(patches, (const unsigned char*)"replace", path, NULL, to);
908 909 910 911 912 913
            }
            return;

        case cJSON_String:
            if (strcmp(from->valuestring, to->valuestring) != 0)
            {
914
                cJSONUtils_GeneratePatch(patches, (const unsigned char*)"replace", path, NULL, to);
915 916
            }
            return;
917

918 919
        case cJSON_Array:
        {
920 921 922 923 924 925 926
            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++)
927
            {
928 929 930
                /* 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 */
931
                if (index > ULONG_MAX)
932
                {
933
                    free(new_path);
934 935
                    return;
                }
936 937
                sprintf((char*)new_path, "%s/%lu", path, (unsigned long)index); /* path of the current array element */
                cJSONUtils_CompareToPatch(patches, new_path, from_child, to_child);
938
            }
939

940
            /* remove leftover elements from 'from' that are not in 'to' */
941
            for (; (from_child != NULL); (void)(from_child = from_child->next))
942
            {
943 944 945
                /* 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 */
946
                if (index > ULONG_MAX)
947
                {
948
                    free(new_path);
949 950
                    return;
                }
951 952
                sprintf((char*)new_path, "%lu", (unsigned long)index);
                cJSONUtils_GeneratePatch(patches, (const unsigned char*)"remove", path, new_path, NULL);
953 954
            }
            /* add new elements in 'to' that were not in 'from' */
955
            for (; (to_child != NULL); (void)(to_child = to_child->next), index++)
956
            {
957
                cJSONUtils_GeneratePatch(patches, (const unsigned char*)"add", path, (const unsigned char*)"-", to_child);
958
            }
959
            free(new_path);
960 961 962 963 964
            return;
        }

        case cJSON_Object:
        {
965 966
            cJSON *from_child = NULL;
            cJSON *to_child = NULL;
967 968 969
            cJSONUtils_SortObject(from);
            cJSONUtils_SortObject(to);

970 971
            from_child = from->child;
            to_child = to->child;
972
            /* for all object values in the object with more of them */
973
            while ((from_child != NULL) || (to_child != NULL))
974
            {
975 976 977 978 979 980 981 982 983 984 985
                int diff;
                if (from_child == NULL)
                {
                    diff = 1;
                }
                else if (to_child == NULL)
                {
                    diff = -1;
                }
                else
                {
986
                    diff = case_insensitive_strcmp((unsigned char*)from_child->string, (unsigned char*)to_child->string);
987 988 989
                }

                if (diff == 0)
990 991
                {
                    /* both object keys are the same */
992
                    size_t path_length = strlen((const char*)path);
993
                    size_t from_child_name_length = pointer_encoded_length((unsigned char*)from_child->string);
994 995 996
                    unsigned char *new_path = (unsigned char*)cJSON_malloc(path_length + from_child_name_length + sizeof("/"));

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

999
                    /* create a patch for the element */
1000 1001 1002 1003 1004
                    cJSONUtils_CompareToPatch(patches, new_path, from_child, to_child);
                    free(new_path);

                    from_child = from_child->next;
                    to_child = to_child->next;
1005 1006 1007 1008
                }
                else if (diff < 0)
                {
                    /* object element doesn't exist in 'to' --> remove it */
1009 1010 1011
                    cJSONUtils_GeneratePatch(patches, (const unsigned char*)"remove", path, (unsigned char*)from_child->string, NULL);

                    from_child = from_child->next;
1012 1013 1014 1015
                }
                else
                {
                    /* object element doesn't exist in 'from' --> add it */
1016 1017 1018
                    cJSONUtils_GeneratePatch(patches, (const unsigned char*)"add", path, (unsigned char*)to_child->string, to_child);

                    to_child = to_child->next;
1019 1020 1021 1022 1023 1024 1025 1026 1027
                }
            }
            return;
        }

        default:
            break;
    }
}
1028

1029
CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON * const from, cJSON * const to)
1030
{
1031
    cJSON *patches = cJSON_CreateArray();
1032
    cJSONUtils_CompareToPatch(patches, (const unsigned char*)"", from, to);
1033

1034 1035
    return patches;
}
1036

M
Max Bruckner 已提交
1037
/* sort lists using mergesort */
1038 1039
static cJSON *cJSONUtils_SortList(cJSON *list)
{
M
Max Bruckner 已提交
1040 1041
    cJSON *first = list;
    cJSON *second = list;
M
Max Bruckner 已提交
1042 1043 1044
    cJSON *current_item = list;
    cJSON *result = list;
    cJSON *result_tail = NULL;
1045

M
Max Bruckner 已提交
1046
    if ((list == NULL) || (list->next == NULL))
M
Max Bruckner 已提交
1047 1048
    {
        /* One entry is sorted already. */
M
Max Bruckner 已提交
1049
        return result;
M
Max Bruckner 已提交
1050
    }
1051

1052
    while ((current_item != NULL) && (current_item->next != NULL) && (case_insensitive_strcmp((unsigned char*)current_item->string, (unsigned char*)current_item->next->string) < 0))
M
Max Bruckner 已提交
1053 1054
    {
        /* Test for list sorted. */
M
Max Bruckner 已提交
1055
        current_item = current_item->next;
M
Max Bruckner 已提交
1056
    }
M
Max Bruckner 已提交
1057
    if ((current_item == NULL) || (current_item->next == NULL))
M
Max Bruckner 已提交
1058 1059
    {
        /* Leave sorted lists unmodified. */
M
Max Bruckner 已提交
1060
        return result;
M
Max Bruckner 已提交
1061
    }
1062

M
Max Bruckner 已提交
1063 1064 1065
    /* reset pointer to the beginning */
    current_item = list;
    while (current_item != NULL)
M
Max Bruckner 已提交
1066 1067 1068
    {
        /* Walk two pointers to find the middle. */
        second = second->next;
M
Max Bruckner 已提交
1069 1070 1071
        current_item = current_item->next;
        /* advances current_item two steps at a time */
        if (current_item != NULL)
M
Max Bruckner 已提交
1072
        {
M
Max Bruckner 已提交
1073
            current_item = current_item->next;
M
Max Bruckner 已提交
1074 1075
        }
    }
M
Max Bruckner 已提交
1076
    if ((second != NULL) && (second->prev != NULL))
M
Max Bruckner 已提交
1077 1078
    {
        /* Split the lists */
1079
        second->prev->next = NULL;
M
Max Bruckner 已提交
1080
    }
1081

M
Max Bruckner 已提交
1082 1083 1084
    /* Recursively sort the sub-lists. */
    first = cJSONUtils_SortList(first);
    second = cJSONUtils_SortList(second);
M
Max Bruckner 已提交
1085
    result = NULL;
M
Max Bruckner 已提交
1086

M
Max Bruckner 已提交
1087 1088
    /* Merge the sub-lists */
    while ((first != NULL) && (second != NULL))
M
Max Bruckner 已提交
1089
    {
M
Max Bruckner 已提交
1090
        cJSON *smaller = NULL;
1091
        if (case_insensitive_strcmp((unsigned char*)first->string, (unsigned char*)second->string) < 0)
M
Max Bruckner 已提交
1092
        {
M
Max Bruckner 已提交
1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115
            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 已提交
1116 1117 1118 1119 1120 1121 1122
            first = first->next;
        }
        else
        {
            second = second->next;
        }
    }
M
Max Bruckner 已提交
1123 1124

    if (first != NULL)
M
Max Bruckner 已提交
1125 1126
    {
        /* Append rest of first list. */
M
Max Bruckner 已提交
1127
        if (result == NULL)
M
Max Bruckner 已提交
1128 1129 1130
        {
            return first;
        }
M
Max Bruckner 已提交
1131 1132
        result_tail->next = first;
        first->prev = result_tail;
M
Max Bruckner 已提交
1133
    }
M
Max Bruckner 已提交
1134
    if (second != NULL)
M
Max Bruckner 已提交
1135 1136
    {
        /* Append rest of second list */
M
Max Bruckner 已提交
1137
        if (result == NULL)
M
Max Bruckner 已提交
1138 1139 1140
        {
            return second;
        }
M
Max Bruckner 已提交
1141 1142
        result_tail->next = second;
        second->prev = result_tail;
M
Max Bruckner 已提交
1143 1144
    }

M
Max Bruckner 已提交
1145
    return result;
1146 1147
}

M
Max Bruckner 已提交
1148
CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON * const object)
M
Max Bruckner 已提交
1149 1150 1151
{
    object->child = cJSONUtils_SortList(object->child);
}
1152

M
Max Bruckner 已提交
1153
CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, const cJSON * const patch)
1154
{
M
Max Bruckner 已提交
1155 1156
    cJSON *patch_child = NULL;

1157
    if (!cJSON_IsObject(patch))
M
Max Bruckner 已提交
1158 1159 1160 1161 1162
    {
        /* scalar value, array or NULL, just duplicate */
        cJSON_Delete(target);
        return cJSON_Duplicate(patch, 1);
    }
1163

1164
    if (!cJSON_IsObject(target))
M
Max Bruckner 已提交
1165 1166 1167 1168 1169
    {
        cJSON_Delete(target);
        target = cJSON_CreateObject();
    }

M
Max Bruckner 已提交
1170 1171
    patch_child = patch->child;
    while (patch_child != NULL)
M
Max Bruckner 已提交
1172
    {
M
Max Bruckner 已提交
1173
        if (cJSON_IsNull(patch_child))
M
Max Bruckner 已提交
1174 1175
        {
            /* NULL is the indicator to remove a value, see RFC7396 */
M
Max Bruckner 已提交
1176
            cJSON_DeleteItemFromObject(target, patch_child->string);
M
Max Bruckner 已提交
1177 1178 1179
        }
        else
        {
M
Max Bruckner 已提交
1180 1181
            cJSON *replace_me = cJSON_DetachItemFromObject(target, patch_child->string);
            cJSON_AddItemToObject(target, patch_child->string, cJSONUtils_MergePatch(replace_me, patch_child));
M
Max Bruckner 已提交
1182
        }
M
Max Bruckner 已提交
1183
        patch_child = patch_child->next;
M
Max Bruckner 已提交
1184 1185
    }
    return target;
1186
}
1187

1188
CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON * const from, cJSON * const to)
1189
{
1190 1191
    cJSON *from_child = NULL;
    cJSON *to_child = NULL;
1192
    cJSON *patch = NULL;
1193
    if (to == NULL)
1194 1195 1196 1197
    {
        /* patch to delete everything */
        return cJSON_CreateNull();
    }
1198
    if (!cJSON_IsObject(to) || !cJSON_IsObject(from))
1199 1200 1201 1202 1203 1204 1205
    {
        return cJSON_Duplicate(to, 1);
    }

    cJSONUtils_SortObject(from);
    cJSONUtils_SortObject(to);

1206 1207
    from_child = from->child;
    to_child = to->child;
1208
    patch = cJSON_CreateObject();
1209
    while (from_child || to_child)
1210
    {
1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228
        int diff;
        if (from_child != NULL)
        {
            if (to_child != NULL)
            {
                diff = strcmp(from_child->string, to_child->string);
            }
            else
            {
                diff = -1;
            }
        }
        else
        {
            diff = 1;
        }

        if (diff < 0)
1229 1230
        {
            /* from has a value that to doesn't have -> remove */
1231 1232 1233
            cJSON_AddItemToObject(patch, from_child->string, cJSON_CreateNull());

            from_child = from_child->next;
1234
        }
1235
        else if (diff > 0)
1236 1237
        {
            /* to has a value that from doesn't have -> add to patch */
1238 1239 1240
            cJSON_AddItemToObject(patch, to_child->string, cJSON_Duplicate(to_child, 1));

            to_child = to_child->next;
1241 1242 1243 1244
        }
        else
        {
            /* object key exists in both objects */
1245
            if (!compare_json(from_child, to_child))
1246 1247
            {
                /* not identical --> generate a patch */
1248
                cJSON_AddItemToObject(patch, to_child->string, cJSONUtils_GenerateMergePatch(from_child, to_child));
1249
            }
1250

1251
            /* next key in the object */
1252 1253
            from_child = from_child->next;
            to_child = to_child->next;
1254 1255
        }
    }
1256
    if (patch->child == NULL)
1257
    {
1258
        /* no patch generated */
1259
        cJSON_Delete(patch);
1260
        return NULL;
1261 1262 1263
    }

    return patch;
M
Max Bruckner 已提交
1264
}