1 /*
2 Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 THE SOFTWARE.
21 */
22
23 /* disable warnings about old C89 functions in MSVC */
24 #if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER)
25 #define _CRT_SECURE_NO_DEPRECATE
26 #endif
27
28 #ifdef __GNUCC__
29 #pragma GCC visibility push(default)
30 #endif
31 #if defined(_MSC_VER)
32 #pragma warning (push)
33 /* disable warning about single line comments in system headers */
34 #pragma warning (disable : 4001)
35 #endif
36
37 #include <ctype.h>
38 #include <string.h>
39 #include <stdlib.h>
40 #include <stdio.h>
41 #include <limits.h>
42
43 #if defined(_MSC_VER)
44 #pragma warning (pop)
45 #endif
46 #ifdef __GNUCC__
47 #pragma GCC visibility pop
48 #endif
49
50 #include "cJSON_Utils.h"
51
52 /* define our own boolean type */
53 #define true ((cJSON_bool)1)
54 #define false ((cJSON_bool)0)
55
cJSONUtils_strdup(const unsigned char * const string)56 static unsigned char* cJSONUtils_strdup(const unsigned char* const string)
57 {
58 size_t length = 0;
59 unsigned char *copy = NULL;
60
61 length = strlen((const char*)string) + sizeof("");
62 copy = (unsigned char*) cJSON_malloc(length);
63 if (copy == NULL)
64 {
65 return NULL;
66 }
67 memcpy(copy, string, length);
68
69 return copy;
70 }
71
72 /* string comparison which doesn't consider NULL pointers equal */
compare_strings(const unsigned char * string1,const unsigned char * string2,const cJSON_bool case_sensitive)73 static int compare_strings(const unsigned char *string1, const unsigned char *string2, const cJSON_bool case_sensitive)
74 {
75 if ((string1 == NULL) || (string2 == NULL))
76 {
77 return 1;
78 }
79
80 if (string1 == string2)
81 {
82 return 0;
83 }
84
85 if (case_sensitive)
86 {
87 return strcmp((const char*)string1, (const char*)string2);
88 }
89
90 for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++)
91 {
92 if (*string1 == '\0')
93 {
94 return 0;
95 }
96 }
97
98 return tolower(*string1) - tolower(*string2);
99 }
100
101 /* Compare the next path element of two JSON pointers, two NULL pointers are considered unequal: */
compare_pointers(const unsigned char * name,const unsigned char * pointer,const cJSON_bool case_sensitive)102 static cJSON_bool compare_pointers(const unsigned char *name, const unsigned char *pointer, const cJSON_bool case_sensitive)
103 {
104 if ((name == NULL) || (pointer == NULL))
105 {
106 return false;
107 }
108
109 for (; (*name != '\0') && (*pointer != '\0') && (*pointer != '/'); (void)name++, pointer++) /* compare until next '/' */
110 {
111 if (*pointer == '~')
112 {
113 /* check for escaped '~' (~0) and '/' (~1) */
114 if (((pointer[1] != '0') || (*name != '~')) && ((pointer[1] != '1') || (*name != '/')))
115 {
116 /* invalid escape sequence or wrong character in *name */
117 return false;
118 }
119 else
120 {
121 pointer++;
122 }
123 }
124 else if ((!case_sensitive && (tolower(*name) != tolower(*pointer))) || (case_sensitive && (*name != *pointer)))
125 {
126 return false;
127 }
128 }
129 if (((*pointer != 0) && (*pointer != '/')) != (*name != 0))
130 {
131 /* one string has ended, the other not */
132 return false;;
133 }
134
135 return true;
136 }
137
138 /* calculate the length of a string if encoded as JSON pointer with ~0 and ~1 escape sequences */
pointer_encoded_length(const unsigned char * string)139 static size_t pointer_encoded_length(const unsigned char *string)
140 {
141 size_t length;
142 for (length = 0; *string != '\0'; (void)string++, length++)
143 {
144 /* character needs to be escaped? */
145 if ((*string == '~') || (*string == '/'))
146 {
147 length++;
148 }
149 }
150
151 return length;
152 }
153
154 /* copy a string while escaping '~' and '/' with ~0 and ~1 JSON pointer escape codes */
encode_string_as_pointer(unsigned char * destination,const unsigned char * source)155 static void encode_string_as_pointer(unsigned char *destination, const unsigned char *source)
156 {
157 for (; source[0] != '\0'; (void)source++, destination++)
158 {
159 if (source[0] == '/')
160 {
161 destination[1] = '1';
162 destination++;
163 }
164 else if (source[0] == '~')
165 {
166 destination[0] = '~';
167 destination[1] = '1';
168 destination++;
169 }
170 else
171 {
172 destination[0] = source[0];
173 }
174 }
175
176 destination[0] = '\0';
177 }
178
cJSONUtils_FindPointerFromObjectTo(const cJSON * const object,const cJSON * const target)179 CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(const cJSON * const object, const cJSON * const target)
180 {
181 size_t child_index = 0;
182 cJSON *current_child = 0;
183
184 if ((object == NULL) || (target == NULL))
185 {
186 return NULL;
187 }
188
189 if (object == target)
190 {
191 /* found */
192 return (char*)cJSONUtils_strdup((const unsigned char*)"");
193 }
194
195 /* recursively search all children of the object or array */
196 for (current_child = object->child; current_child != NULL; (void)(current_child = current_child->next), child_index++)
197 {
198 unsigned char *target_pointer = (unsigned char*)cJSONUtils_FindPointerFromObjectTo(current_child, target);
199 /* found the target? */
200 if (target_pointer != NULL)
201 {
202 if (cJSON_IsArray(object))
203 {
204 /* reserve enough memory for a 64 bit integer + '/' and '\0' */
205 unsigned char *full_pointer = (unsigned char*)cJSON_malloc(strlen((char*)target_pointer) + 20 + sizeof("/"));
206 /* check if conversion to unsigned long is valid
207 * This should be eliminated at compile time by dead code elimination
208 * if size_t is an alias of unsigned long, or if it is bigger */
209 if (child_index > ULONG_MAX)
210 {
211 cJSON_free(target_pointer);
212 return NULL;
213 }
214 sprintf((char*)full_pointer, "/%lu%s", (unsigned long)child_index, target_pointer); /* /<array_index><path> */
215 cJSON_free(target_pointer);
216
217 return (char*)full_pointer;
218 }
219
220 if (cJSON_IsObject(object))
221 {
222 unsigned char *full_pointer = (unsigned char*)cJSON_malloc(strlen((char*)target_pointer) + pointer_encoded_length((unsigned char*)current_child->string) + 2);
223 full_pointer[0] = '/';
224 encode_string_as_pointer(full_pointer + 1, (unsigned char*)current_child->string);
225 strcat((char*)full_pointer, (char*)target_pointer);
226 cJSON_free(target_pointer);
227
228 return (char*)full_pointer;
229 }
230
231 /* reached leaf of the tree, found nothing */
232 cJSON_free(target_pointer);
233 return NULL;
234 }
235 }
236
237 /* not found */
238 return NULL;
239 }
240
241 /* non broken version of cJSON_GetArrayItem */
get_array_item(const cJSON * array,size_t item)242 static cJSON *get_array_item(const cJSON *array, size_t item)
243 {
244 cJSON *child = array ? array->child : NULL;
245 while ((child != NULL) && (item > 0))
246 {
247 item--;
248 child = child->next;
249 }
250
251 return child;
252 }
253
decode_array_index_from_pointer(const unsigned char * const pointer,size_t * const index)254 static cJSON_bool decode_array_index_from_pointer(const unsigned char * const pointer, size_t * const index)
255 {
256 size_t parsed_index = 0;
257 size_t position = 0;
258
259 if ((pointer[0] == '0') && ((pointer[1] != '\0') && (pointer[1] != '/')))
260 {
261 /* leading zeroes are not permitted */
262 return 0;
263 }
264
265 for (position = 0; (pointer[position] >= '0') && (pointer[0] <= '9'); position++)
266 {
267 parsed_index = (10 * parsed_index) + (size_t)(pointer[position] - '0');
268
269 }
270
271 if ((pointer[position] != '\0') && (pointer[position] != '/'))
272 {
273 return 0;
274 }
275
276 *index = parsed_index;
277
278 return 1;
279 }
280
get_item_from_pointer(cJSON * const object,const char * pointer,const cJSON_bool case_sensitive)281 static cJSON *get_item_from_pointer(cJSON * const object, const char * pointer, const cJSON_bool case_sensitive)
282 {
283 cJSON *current_element = object;
284
285 if (pointer == NULL)
286 {
287 return NULL;
288 }
289
290 /* follow path of the pointer */
291 while ((pointer[0] == '/') && (current_element != NULL))
292 {
293 pointer++;
294 if (cJSON_IsArray(current_element))
295 {
296 size_t index = 0;
297 if (!decode_array_index_from_pointer((const unsigned char*)pointer, &index))
298 {
299 return NULL;
300 }
301
302 current_element = get_array_item(current_element, index);
303 }
304 else if (cJSON_IsObject(current_element))
305 {
306 current_element = current_element->child;
307 /* GetObjectItem. */
308 while ((current_element != NULL) && !compare_pointers((unsigned char*)current_element->string, (const unsigned char*)pointer, case_sensitive))
309 {
310 current_element = current_element->next;
311 }
312 }
313 else
314 {
315 return NULL;
316 }
317
318 /* skip to the next path token or end of string */
319 while ((pointer[0] != '\0') && (pointer[0] != '/'))
320 {
321 pointer++;
322 }
323 }
324
325 return current_element;
326 }
327
cJSONUtils_GetPointer(cJSON * const object,const char * pointer)328 CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON * const object, const char *pointer)
329 {
330 return get_item_from_pointer(object, pointer, false);
331 }
332
cJSONUtils_GetPointerCaseSensitive(cJSON * const object,const char * pointer)333 CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointerCaseSensitive(cJSON * const object, const char *pointer)
334 {
335 return get_item_from_pointer(object, pointer, true);
336 }
337
338 /* JSON Patch implementation. */
decode_pointer_inplace(unsigned char * string)339 static void decode_pointer_inplace(unsigned char *string)
340 {
341 unsigned char *decoded_string = string;
342
343 if (string == NULL) {
344 return;
345 }
346
347 for (; *string; (void)decoded_string++, string++)
348 {
349 if (string[0] == '~')
350 {
351 if (string[1] == '0')
352 {
353 decoded_string[0] = '~';
354 }
355 else if (string[1] == '1')
356 {
357 decoded_string[1] = '/';
358 }
359 else
360 {
361 /* invalid escape sequence */
362 return;
363 }
364
365 string++;
366 }
367 }
368
369 decoded_string[0] = '\0';
370 }
371
372 /* non-broken cJSON_DetachItemFromArray */
detach_item_from_array(cJSON * array,size_t which)373 static cJSON *detach_item_from_array(cJSON *array, size_t which)
374 {
375 cJSON *c = array->child;
376 while (c && (which > 0))
377 {
378 c = c->next;
379 which--;
380 }
381 if (!c)
382 {
383 /* item doesn't exist */
384 return NULL;
385 }
386 if (c->prev)
387 {
388 /* not the first element */
389 c->prev->next = c->next;
390 }
391 if (c->next)
392 {
393 c->next->prev = c->prev;
394 }
395 if (c==array->child)
396 {
397 array->child = c->next;
398 }
399 /* make sure the detached item doesn't point anywhere anymore */
400 c->prev = c->next = NULL;
401
402 return c;
403 }
404
405 /* detach an item at the given path */
detach_path(cJSON * object,const unsigned char * path,const cJSON_bool case_sensitive)406 static cJSON *detach_path(cJSON *object, const unsigned char *path, const cJSON_bool case_sensitive)
407 {
408 unsigned char *parent_pointer = NULL;
409 unsigned char *child_pointer = NULL;
410 cJSON *parent = NULL;
411 cJSON *detached_item = NULL;
412
413 /* copy path and split it in parent and child */
414 parent_pointer = cJSONUtils_strdup(path);
415 if (parent_pointer == NULL) {
416 goto cleanup;
417 }
418
419 child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/'); /* last '/' */
420 if (child_pointer == NULL)
421 {
422 goto cleanup;
423 }
424 /* split strings */
425 child_pointer[0] = '\0';
426 child_pointer++;
427
428 parent = get_item_from_pointer(object, (char*)parent_pointer, case_sensitive);
429 decode_pointer_inplace(child_pointer);
430
431 if (cJSON_IsArray(parent))
432 {
433 size_t index = 0;
434 if (!decode_array_index_from_pointer(child_pointer, &index))
435 {
436 goto cleanup;
437 }
438 detached_item = detach_item_from_array(parent, index);
439 }
440 else if (cJSON_IsObject(parent))
441 {
442 detached_item = cJSON_DetachItemFromObject(parent, (char*)child_pointer);
443 }
444 else
445 {
446 /* Couldn't find object to remove child from. */
447 goto cleanup;
448 }
449
450 cleanup:
451 if (parent_pointer != NULL)
452 {
453 cJSON_free(parent_pointer);
454 }
455
456 return detached_item;
457 }
458
459 /* sort lists using mergesort */
sort_list(cJSON * list,const cJSON_bool case_sensitive)460 static cJSON *sort_list(cJSON *list, const cJSON_bool case_sensitive)
461 {
462 cJSON *first = list;
463 cJSON *second = list;
464 cJSON *current_item = list;
465 cJSON *result = list;
466 cJSON *result_tail = NULL;
467
468 if ((list == NULL) || (list->next == NULL))
469 {
470 /* One entry is sorted already. */
471 return result;
472 }
473
474 while ((current_item != NULL) && (current_item->next != NULL) && (compare_strings((unsigned char*)current_item->string, (unsigned char*)current_item->next->string, case_sensitive) < 0))
475 {
476 /* Test for list sorted. */
477 current_item = current_item->next;
478 }
479 if ((current_item == NULL) || (current_item->next == NULL))
480 {
481 /* Leave sorted lists unmodified. */
482 return result;
483 }
484
485 /* reset pointer to the beginning */
486 current_item = list;
487 while (current_item != NULL)
488 {
489 /* Walk two pointers to find the middle. */
490 second = second->next;
491 current_item = current_item->next;
492 /* advances current_item two steps at a time */
493 if (current_item != NULL)
494 {
495 current_item = current_item->next;
496 }
497 }
498 if ((second != NULL) && (second->prev != NULL))
499 {
500 /* Split the lists */
501 second->prev->next = NULL;
502 }
503
504 /* Recursively sort the sub-lists. */
505 first = sort_list(first, case_sensitive);
506 second = sort_list(second, case_sensitive);
507 result = NULL;
508
509 /* Merge the sub-lists */
510 while ((first != NULL) && (second != NULL))
511 {
512 cJSON *smaller = NULL;
513 if (compare_strings((unsigned char*)first->string, (unsigned char*)second->string, false) < 0)
514 {
515 smaller = first;
516 }
517 else
518 {
519 smaller = second;
520 }
521
522 if (result == NULL)
523 {
524 /* start merged list with the smaller element */
525 result_tail = smaller;
526 result = smaller;
527 }
528 else
529 {
530 /* add smaller element to the list */
531 result_tail->next = smaller;
532 smaller->prev = result_tail;
533 result_tail = smaller;
534 }
535
536 if (first == smaller)
537 {
538 first = first->next;
539 }
540 else
541 {
542 second = second->next;
543 }
544 }
545
546 if (first != NULL)
547 {
548 /* Append rest of first list. */
549 if (result == NULL)
550 {
551 return first;
552 }
553 result_tail->next = first;
554 first->prev = result_tail;
555 }
556 if (second != NULL)
557 {
558 /* Append rest of second list */
559 if (result == NULL)
560 {
561 return second;
562 }
563 result_tail->next = second;
564 second->prev = result_tail;
565 }
566
567 return result;
568 }
569
sort_object(cJSON * const object,const cJSON_bool case_sensitive)570 static void sort_object(cJSON * const object, const cJSON_bool case_sensitive)
571 {
572 if (object == NULL)
573 {
574 return;
575 }
576 object->child = sort_list(object->child, case_sensitive);
577 }
578
compare_json(cJSON * a,cJSON * b,const cJSON_bool case_sensitive)579 static cJSON_bool compare_json(cJSON *a, cJSON *b, const cJSON_bool case_sensitive)
580 {
581 if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)))
582 {
583 /* mismatched type. */
584 return false;
585 }
586 switch (a->type & 0xFF)
587 {
588 case cJSON_Number:
589 /* numeric mismatch. */
590 if ((a->valueint != b->valueint) || (a->valuedouble != b->valuedouble))
591 {
592 return false;
593 }
594 else
595 {
596 return true;
597 }
598
599 case cJSON_String:
600 /* string mismatch. */
601 if (strcmp(a->valuestring, b->valuestring) != 0)
602 {
603 return false;
604 }
605 else
606 {
607 return true;
608 }
609
610 case cJSON_Array:
611 for ((void)(a = a->child), b = b->child; (a != NULL) && (b != NULL); (void)(a = a->next), b = b->next)
612 {
613 cJSON_bool identical = compare_json(a, b, case_sensitive);
614 if (!identical)
615 {
616 return false;
617 }
618 }
619
620 /* array size mismatch? (one of both children is not NULL) */
621 if ((a != NULL) || (b != NULL))
622 {
623 return false;
624 }
625 else
626 {
627 return true;
628 }
629
630 case cJSON_Object:
631 sort_object(a, case_sensitive);
632 sort_object(b, case_sensitive);
633 for ((void)(a = a->child), b = b->child; (a != NULL) && (b != NULL); (void)(a = a->next), b = b->next)
634 {
635 cJSON_bool identical = false;
636 /* compare object keys */
637 if (compare_strings((unsigned char*)a->string, (unsigned char*)b->string, case_sensitive))
638 {
639 /* missing member */
640 return false;
641 }
642 identical = compare_json(a, b, case_sensitive);
643 if (!identical)
644 {
645 return false;
646 }
647 }
648
649 /* object length mismatch (one of both children is not null) */
650 if ((a != NULL) || (b != NULL))
651 {
652 return false;
653 }
654 else
655 {
656 return true;
657 }
658
659 default:
660 break;
661 }
662
663 /* null, true or false */
664 return true;
665 }
666
667 /* non broken version of cJSON_InsertItemInArray */
insert_item_in_array(cJSON * array,size_t which,cJSON * newitem)668 static cJSON_bool insert_item_in_array(cJSON *array, size_t which, cJSON *newitem)
669 {
670 cJSON *child = array->child;
671 while (child && (which > 0))
672 {
673 child = child->next;
674 which--;
675 }
676 if (which > 0)
677 {
678 /* item is after the end of the array */
679 return 0;
680 }
681 if (child == NULL)
682 {
683 cJSON_AddItemToArray(array, newitem);
684 return 1;
685 }
686
687 /* insert into the linked list */
688 newitem->next = child;
689 newitem->prev = child->prev;
690 child->prev = newitem;
691
692 /* was it at the beginning */
693 if (child == array->child)
694 {
695 array->child = newitem;
696 }
697 else
698 {
699 newitem->prev->next = newitem;
700 }
701
702 return 1;
703 }
704
get_object_item(const cJSON * const object,const char * name,const cJSON_bool case_sensitive)705 static cJSON *get_object_item(const cJSON * const object, const char* name, const cJSON_bool case_sensitive)
706 {
707 if (case_sensitive)
708 {
709 return cJSON_GetObjectItemCaseSensitive(object, name);
710 }
711
712 return cJSON_GetObjectItem(object, name);
713 }
714
715 enum patch_operation { INVALID, ADD, REMOVE, REPLACE, MOVE, COPY, TEST };
716
decode_patch_operation(const cJSON * const patch,const cJSON_bool case_sensitive)717 static enum patch_operation decode_patch_operation(const cJSON * const patch, const cJSON_bool case_sensitive)
718 {
719 cJSON *operation = get_object_item(patch, "op", case_sensitive);
720 if (!cJSON_IsString(operation))
721 {
722 return INVALID;
723 }
724
725 if (strcmp(operation->valuestring, "add") == 0)
726 {
727 return ADD;
728 }
729
730 if (strcmp(operation->valuestring, "remove") == 0)
731 {
732 return REMOVE;
733 }
734
735 if (strcmp(operation->valuestring, "replace") == 0)
736 {
737 return REPLACE;
738 }
739
740 if (strcmp(operation->valuestring, "move") == 0)
741 {
742 return MOVE;
743 }
744
745 if (strcmp(operation->valuestring, "copy") == 0)
746 {
747 return COPY;
748 }
749
750 if (strcmp(operation->valuestring, "test") == 0)
751 {
752 return TEST;
753 }
754
755 return INVALID;
756 }
757
758 /* overwrite and existing item with another one and free resources on the way */
overwrite_item(cJSON * const root,const cJSON replacement)759 static void overwrite_item(cJSON * const root, const cJSON replacement)
760 {
761 if (root == NULL)
762 {
763 return;
764 }
765
766 if (root->string != NULL)
767 {
768 cJSON_free(root->string);
769 }
770 if (root->valuestring != NULL)
771 {
772 cJSON_free(root->valuestring);
773 }
774 if (root->child != NULL)
775 {
776 cJSON_Delete(root->child);
777 }
778
779 memcpy(root, &replacement, sizeof(cJSON));
780 }
781
apply_patch(cJSON * object,const cJSON * patch,const cJSON_bool case_sensitive)782 static int apply_patch(cJSON *object, const cJSON *patch, const cJSON_bool case_sensitive)
783 {
784 cJSON *path = NULL;
785 cJSON *value = NULL;
786 cJSON *parent = NULL;
787 enum patch_operation opcode = INVALID;
788 unsigned char *parent_pointer = NULL;
789 unsigned char *child_pointer = NULL;
790 int status = 0;
791
792 path = get_object_item(patch, "path", case_sensitive);
793 if (!cJSON_IsString(path))
794 {
795 /* malformed patch. */
796 status = 2;
797 goto cleanup;
798 }
799
800 opcode = decode_patch_operation(patch, case_sensitive);
801 if (opcode == INVALID)
802 {
803 status = 3;
804 goto cleanup;
805 }
806 else if (opcode == TEST)
807 {
808 /* compare value: {...} with the given path */
809 status = !compare_json(get_item_from_pointer(object, path->valuestring, case_sensitive), get_object_item(patch, "value", case_sensitive), case_sensitive);
810 goto cleanup;
811 }
812
813 /* special case for replacing the root */
814 if (path->valuestring[0] == '\0')
815 {
816 if (opcode == REMOVE)
817 {
818 static const cJSON invalid = { NULL, NULL, NULL, cJSON_Invalid, NULL, 0, 0, NULL};
819
820 overwrite_item(object, invalid);
821
822 status = 0;
823 goto cleanup;
824 }
825
826 if ((opcode == REPLACE) || (opcode == ADD))
827 {
828 value = get_object_item(patch, "value", case_sensitive);
829 if (value == NULL)
830 {
831 /* missing "value" for add/replace. */
832 status = 7;
833 goto cleanup;
834 }
835
836 value = cJSON_Duplicate(value, 1);
837 if (value == NULL)
838 {
839 /* out of memory for add/replace. */
840 status = 8;
841 goto cleanup;
842 }
843
844 overwrite_item(object, *value);
845
846 /* delete the duplicated value */
847 cJSON_free(value);
848 value = NULL;
849
850 /* the string "value" isn't needed */
851 if (object->string != NULL)
852 {
853 cJSON_free(object->string);
854 object->string = NULL;
855 }
856
857 status = 0;
858 goto cleanup;
859 }
860 }
861
862 if ((opcode == REMOVE) || (opcode == REPLACE))
863 {
864 /* Get rid of old. */
865 cJSON *old_item = detach_path(object, (unsigned char*)path->valuestring, case_sensitive);
866 if (old_item == NULL)
867 {
868 status = 13;
869 goto cleanup;
870 }
871 cJSON_Delete(old_item);
872 if (opcode == REMOVE)
873 {
874 /* For Remove, this job is done. */
875 status = 0;
876 goto cleanup;
877 }
878 }
879
880 /* Copy/Move uses "from". */
881 if ((opcode == MOVE) || (opcode == COPY))
882 {
883 cJSON *from = get_object_item(patch, "from", case_sensitive);
884 if (from == NULL)
885 {
886 /* missing "from" for copy/move. */
887 status = 4;
888 goto cleanup;
889 }
890
891 if (opcode == MOVE)
892 {
893 value = detach_path(object, (unsigned char*)from->valuestring, case_sensitive);
894 }
895 if (opcode == COPY)
896 {
897 value = get_item_from_pointer(object, from->valuestring, case_sensitive);
898 }
899 if (value == NULL)
900 {
901 /* missing "from" for copy/move. */
902 status = 5;
903 goto cleanup;
904 }
905 if (opcode == COPY)
906 {
907 value = cJSON_Duplicate(value, 1);
908 }
909 if (value == NULL)
910 {
911 /* out of memory for copy/move. */
912 status = 6;
913 goto cleanup;
914 }
915 }
916 else /* Add/Replace uses "value". */
917 {
918 value = get_object_item(patch, "value", case_sensitive);
919 if (value == NULL)
920 {
921 /* missing "value" for add/replace. */
922 status = 7;
923 goto cleanup;
924 }
925 value = cJSON_Duplicate(value, 1);
926 if (value == NULL)
927 {
928 /* out of memory for add/replace. */
929 status = 8;
930 goto cleanup;
931 }
932 }
933
934 /* Now, just add "value" to "path". */
935
936 /* split pointer in parent and child */
937 parent_pointer = cJSONUtils_strdup((unsigned char*)path->valuestring);
938 child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/');
939 if (child_pointer != NULL)
940 {
941 child_pointer[0] = '\0';
942 child_pointer++;
943 }
944 parent = get_item_from_pointer(object, (char*)parent_pointer, case_sensitive);
945 decode_pointer_inplace(child_pointer);
946
947 /* add, remove, replace, move, copy, test. */
948 if ((parent == NULL) || (child_pointer == NULL))
949 {
950 /* Couldn't find object to add to. */
951 status = 9;
952 goto cleanup;
953 }
954 else if (cJSON_IsArray(parent))
955 {
956 if (strcmp((char*)child_pointer, "-") == 0)
957 {
958 cJSON_AddItemToArray(parent, value);
959 value = NULL;
960 }
961 else
962 {
963 size_t index = 0;
964 if (!decode_array_index_from_pointer(child_pointer, &index))
965 {
966 status = 11;
967 goto cleanup;
968 }
969
970 if (!insert_item_in_array(parent, index, value))
971 {
972 status = 10;
973 goto cleanup;
974 }
975 value = NULL;
976 }
977 }
978 else if (cJSON_IsObject(parent))
979 {
980 if (case_sensitive)
981 {
982 cJSON_DeleteItemFromObjectCaseSensitive(parent, (char*)child_pointer);
983 }
984 else
985 {
986 cJSON_DeleteItemFromObject(parent, (char*)child_pointer);
987 }
988 cJSON_AddItemToObject(parent, (char*)child_pointer, value);
989 value = NULL;
990 }
991 else /* parent is not an object */
992 {
993 /* Couldn't find object to add to. */
994 status = 9;
995 goto cleanup;
996 }
997
998 cleanup:
999 if (value != NULL)
1000 {
1001 cJSON_Delete(value);
1002 }
1003 if (parent_pointer != NULL)
1004 {
1005 cJSON_free(parent_pointer);
1006 }
1007
1008 return status;
1009 }
1010
cJSONUtils_ApplyPatches(cJSON * const object,const cJSON * const patches)1011 CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON * const object, const cJSON * const patches)
1012 {
1013 const cJSON *current_patch = NULL;
1014 int status = 0;
1015
1016 if (!cJSON_IsArray(patches))
1017 {
1018 /* malformed patches. */
1019 return 1;
1020 }
1021
1022 if (patches != NULL)
1023 {
1024 current_patch = patches->child;
1025 }
1026
1027 while (current_patch != NULL)
1028 {
1029 status = apply_patch(object, current_patch, false);
1030 if (status != 0)
1031 {
1032 return status;
1033 }
1034 current_patch = current_patch->next;
1035 }
1036
1037 return 0;
1038 }
1039
cJSONUtils_ApplyPatchesCaseSensitive(cJSON * const object,const cJSON * const patches)1040 CJSON_PUBLIC(int) cJSONUtils_ApplyPatchesCaseSensitive(cJSON * const object, const cJSON * const patches)
1041 {
1042 const cJSON *current_patch = NULL;
1043 int status = 0;
1044
1045 if (!cJSON_IsArray(patches))
1046 {
1047 /* malformed patches. */
1048 return 1;
1049 }
1050
1051 if (patches != NULL)
1052 {
1053 current_patch = patches->child;
1054 }
1055
1056 while (current_patch != NULL)
1057 {
1058 status = apply_patch(object, current_patch, true);
1059 if (status != 0)
1060 {
1061 return status;
1062 }
1063 current_patch = current_patch->next;
1064 }
1065
1066 return 0;
1067 }
1068
compose_patch(cJSON * const patches,const unsigned char * const operation,const unsigned char * const path,const unsigned char * suffix,const cJSON * const value)1069 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)
1070 {
1071 cJSON *patch = NULL;
1072
1073 if ((patches == NULL) || (operation == NULL) || (path == NULL))
1074 {
1075 return;
1076 }
1077
1078 patch = cJSON_CreateObject();
1079 if (patch == NULL)
1080 {
1081 return;
1082 }
1083 cJSON_AddItemToObject(patch, "op", cJSON_CreateString((const char*)operation));
1084
1085 if (suffix == NULL)
1086 {
1087 cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)path));
1088 }
1089 else
1090 {
1091 size_t suffix_length = pointer_encoded_length(suffix);
1092 size_t path_length = strlen((const char*)path);
1093 unsigned char *full_path = (unsigned char*)cJSON_malloc(path_length + suffix_length + sizeof("/"));
1094
1095 sprintf((char*)full_path, "%s/", (const char*)path);
1096 encode_string_as_pointer(full_path + path_length + 1, suffix);
1097
1098 cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)full_path));
1099 cJSON_free(full_path);
1100 }
1101
1102 if (value != NULL)
1103 {
1104 cJSON_AddItemToObject(patch, "value", cJSON_Duplicate(value, 1));
1105 }
1106 cJSON_AddItemToArray(patches, patch);
1107 }
1108
cJSONUtils_AddPatchToArray(cJSON * const array,const char * const operation,const char * const path,const cJSON * const value)1109 CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON * const array, const char * const operation, const char * const path, const cJSON * const value)
1110 {
1111 compose_patch(array, (const unsigned char*)operation, (const unsigned char*)path, NULL, value);
1112 }
1113
create_patches(cJSON * const patches,const unsigned char * const path,cJSON * const from,cJSON * const to,const cJSON_bool case_sensitive)1114 static void create_patches(cJSON * const patches, const unsigned char * const path, cJSON * const from, cJSON * const to, const cJSON_bool case_sensitive)
1115 {
1116 if ((from == NULL) || (to == NULL))
1117 {
1118 return;
1119 }
1120
1121 if ((from->type & 0xFF) != (to->type & 0xFF))
1122 {
1123 compose_patch(patches, (const unsigned char*)"replace", path, 0, to);
1124 return;
1125 }
1126
1127 switch (from->type & 0xFF)
1128 {
1129 case cJSON_Number:
1130 if ((from->valueint != to->valueint) || (from->valuedouble != to->valuedouble))
1131 {
1132 compose_patch(patches, (const unsigned char*)"replace", path, NULL, to);
1133 }
1134 return;
1135
1136 case cJSON_String:
1137 if (strcmp(from->valuestring, to->valuestring) != 0)
1138 {
1139 compose_patch(patches, (const unsigned char*)"replace", path, NULL, to);
1140 }
1141 return;
1142
1143 case cJSON_Array:
1144 {
1145 size_t index = 0;
1146 cJSON *from_child = from->child;
1147 cJSON *to_child = to->child;
1148 unsigned char *new_path = (unsigned char*)cJSON_malloc(strlen((const char*)path) + 20 + sizeof("/")); /* Allow space for 64bit int. log10(2^64) = 20 */
1149
1150 /* generate patches for all array elements that exist in both "from" and "to" */
1151 for (index = 0; (from_child != NULL) && (to_child != NULL); (void)(from_child = from_child->next), (void)(to_child = to_child->next), index++)
1152 {
1153 /* check if conversion to unsigned long is valid
1154 * This should be eliminated at compile time by dead code elimination
1155 * if size_t is an alias of unsigned long, or if it is bigger */
1156 if (index > ULONG_MAX)
1157 {
1158 cJSON_free(new_path);
1159 return;
1160 }
1161 sprintf((char*)new_path, "%s/%lu", path, (unsigned long)index); /* path of the current array element */
1162 create_patches(patches, new_path, from_child, to_child, case_sensitive);
1163 }
1164
1165 /* remove leftover elements from 'from' that are not in 'to' */
1166 for (; (from_child != NULL); (void)(from_child = from_child->next))
1167 {
1168 /* check if conversion to unsigned long is valid
1169 * This should be eliminated at compile time by dead code elimination
1170 * if size_t is an alias of unsigned long, or if it is bigger */
1171 if (index > ULONG_MAX)
1172 {
1173 cJSON_free(new_path);
1174 return;
1175 }
1176 sprintf((char*)new_path, "%lu", (unsigned long)index);
1177 compose_patch(patches, (const unsigned char*)"remove", path, new_path, NULL);
1178 }
1179 /* add new elements in 'to' that were not in 'from' */
1180 for (; (to_child != NULL); (void)(to_child = to_child->next), index++)
1181 {
1182 compose_patch(patches, (const unsigned char*)"add", path, (const unsigned char*)"-", to_child);
1183 }
1184 cJSON_free(new_path);
1185 return;
1186 }
1187
1188 case cJSON_Object:
1189 {
1190 cJSON *from_child = NULL;
1191 cJSON *to_child = NULL;
1192 sort_object(from, case_sensitive);
1193 sort_object(to, case_sensitive);
1194
1195 from_child = from->child;
1196 to_child = to->child;
1197 /* for all object values in the object with more of them */
1198 while ((from_child != NULL) || (to_child != NULL))
1199 {
1200 int diff;
1201 if (from_child == NULL)
1202 {
1203 diff = 1;
1204 }
1205 else if (to_child == NULL)
1206 {
1207 diff = -1;
1208 }
1209 else
1210 {
1211 diff = compare_strings((unsigned char*)from_child->string, (unsigned char*)to_child->string, case_sensitive);
1212 }
1213
1214 if (diff == 0)
1215 {
1216 /* both object keys are the same */
1217 size_t path_length = strlen((const char*)path);
1218 size_t from_child_name_length = pointer_encoded_length((unsigned char*)from_child->string);
1219 unsigned char *new_path = (unsigned char*)cJSON_malloc(path_length + from_child_name_length + sizeof("/"));
1220
1221 sprintf((char*)new_path, "%s/", path);
1222 encode_string_as_pointer(new_path + path_length + 1, (unsigned char*)from_child->string);
1223
1224 /* create a patch for the element */
1225 create_patches(patches, new_path, from_child, to_child, case_sensitive);
1226 cJSON_free(new_path);
1227
1228 from_child = from_child->next;
1229 to_child = to_child->next;
1230 }
1231 else if (diff < 0)
1232 {
1233 /* object element doesn't exist in 'to' --> remove it */
1234 compose_patch(patches, (const unsigned char*)"remove", path, (unsigned char*)from_child->string, NULL);
1235
1236 from_child = from_child->next;
1237 }
1238 else
1239 {
1240 /* object element doesn't exist in 'from' --> add it */
1241 compose_patch(patches, (const unsigned char*)"add", path, (unsigned char*)to_child->string, to_child);
1242
1243 to_child = to_child->next;
1244 }
1245 }
1246 return;
1247 }
1248
1249 default:
1250 break;
1251 }
1252 }
1253
cJSONUtils_GeneratePatches(cJSON * const from,cJSON * const to)1254 CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON * const from, cJSON * const to)
1255 {
1256 cJSON *patches = NULL;
1257
1258 if ((from == NULL) || (to == NULL))
1259 {
1260 return NULL;
1261 }
1262
1263 patches = cJSON_CreateArray();
1264 create_patches(patches, (const unsigned char*)"", from, to, false);
1265
1266 return patches;
1267 }
1268
cJSONUtils_GeneratePatchesCaseSensitive(cJSON * const from,cJSON * const to)1269 CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatchesCaseSensitive(cJSON * const from, cJSON * const to)
1270 {
1271 cJSON *patches = NULL;
1272
1273 if ((from == NULL) || (to == NULL))
1274 {
1275 return NULL;
1276 }
1277
1278 patches = cJSON_CreateArray();
1279 create_patches(patches, (const unsigned char*)"", from, to, true);
1280
1281 return patches;
1282 }
1283
cJSONUtils_SortObject(cJSON * const object)1284 CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON * const object)
1285 {
1286 sort_object(object, false);
1287 }
1288
cJSONUtils_SortObjectCaseSensitive(cJSON * const object)1289 CJSON_PUBLIC(void) cJSONUtils_SortObjectCaseSensitive(cJSON * const object)
1290 {
1291 sort_object(object, true);
1292 }
1293
merge_patch(cJSON * target,const cJSON * const patch,const cJSON_bool case_sensitive)1294 static cJSON *merge_patch(cJSON *target, const cJSON * const patch, const cJSON_bool case_sensitive)
1295 {
1296 cJSON *patch_child = NULL;
1297
1298 if (!cJSON_IsObject(patch))
1299 {
1300 /* scalar value, array or NULL, just duplicate */
1301 cJSON_Delete(target);
1302 return cJSON_Duplicate(patch, 1);
1303 }
1304
1305 if (!cJSON_IsObject(target))
1306 {
1307 cJSON_Delete(target);
1308 target = cJSON_CreateObject();
1309 }
1310
1311 patch_child = patch->child;
1312 while (patch_child != NULL)
1313 {
1314 if (cJSON_IsNull(patch_child))
1315 {
1316 /* NULL is the indicator to remove a value, see RFC7396 */
1317 if (case_sensitive)
1318 {
1319 cJSON_DeleteItemFromObjectCaseSensitive(target, patch_child->string);
1320 }
1321 else
1322 {
1323 cJSON_DeleteItemFromObject(target, patch_child->string);
1324 }
1325 }
1326 else
1327 {
1328 cJSON *replace_me = NULL;
1329 cJSON *replacement = NULL;
1330
1331 if (case_sensitive)
1332 {
1333 replace_me = cJSON_DetachItemFromObjectCaseSensitive(target, patch_child->string);
1334 }
1335 else
1336 {
1337 replace_me = cJSON_DetachItemFromObject(target, patch_child->string);
1338 }
1339
1340 replacement = merge_patch(replace_me, patch_child, case_sensitive);
1341 if (replacement == NULL)
1342 {
1343 return NULL;
1344 }
1345
1346 cJSON_AddItemToObject(target, patch_child->string, replacement);
1347 }
1348 patch_child = patch_child->next;
1349 }
1350 return target;
1351 }
1352
cJSONUtils_MergePatch(cJSON * target,const cJSON * const patch)1353 CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, const cJSON * const patch)
1354 {
1355 return merge_patch(target, patch, false);
1356 }
1357
cJSONUtils_MergePatchCaseSensitive(cJSON * target,const cJSON * const patch)1358 CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatchCaseSensitive(cJSON *target, const cJSON * const patch)
1359 {
1360 return merge_patch(target, patch, true);
1361 }
1362
generate_merge_patch(cJSON * const from,cJSON * const to,const cJSON_bool case_sensitive)1363 static cJSON *generate_merge_patch(cJSON * const from, cJSON * const to, const cJSON_bool case_sensitive)
1364 {
1365 cJSON *from_child = NULL;
1366 cJSON *to_child = NULL;
1367 cJSON *patch = NULL;
1368 if (to == NULL)
1369 {
1370 /* patch to delete everything */
1371 return cJSON_CreateNull();
1372 }
1373 if (!cJSON_IsObject(to) || !cJSON_IsObject(from))
1374 {
1375 return cJSON_Duplicate(to, 1);
1376 }
1377
1378 sort_object(from, case_sensitive);
1379 sort_object(to, case_sensitive);
1380
1381 from_child = from->child;
1382 to_child = to->child;
1383 patch = cJSON_CreateObject();
1384 while (from_child || to_child)
1385 {
1386 int diff;
1387 if (from_child != NULL)
1388 {
1389 if (to_child != NULL)
1390 {
1391 diff = strcmp(from_child->string, to_child->string);
1392 }
1393 else
1394 {
1395 diff = -1;
1396 }
1397 }
1398 else
1399 {
1400 diff = 1;
1401 }
1402
1403 if (diff < 0)
1404 {
1405 /* from has a value that to doesn't have -> remove */
1406 cJSON_AddItemToObject(patch, from_child->string, cJSON_CreateNull());
1407
1408 from_child = from_child->next;
1409 }
1410 else if (diff > 0)
1411 {
1412 /* to has a value that from doesn't have -> add to patch */
1413 cJSON_AddItemToObject(patch, to_child->string, cJSON_Duplicate(to_child, 1));
1414
1415 to_child = to_child->next;
1416 }
1417 else
1418 {
1419 /* object key exists in both objects */
1420 if (!compare_json(from_child, to_child, case_sensitive))
1421 {
1422 /* not identical --> generate a patch */
1423 cJSON_AddItemToObject(patch, to_child->string, cJSONUtils_GenerateMergePatch(from_child, to_child));
1424 }
1425
1426 /* next key in the object */
1427 from_child = from_child->next;
1428 to_child = to_child->next;
1429 }
1430 }
1431 if (patch->child == NULL)
1432 {
1433 /* no patch generated */
1434 cJSON_Delete(patch);
1435 return NULL;
1436 }
1437
1438 return patch;
1439 }
1440
cJSONUtils_GenerateMergePatch(cJSON * const from,cJSON * const to)1441 CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON * const from, cJSON * const to)
1442 {
1443 return generate_merge_patch(from, to, false);
1444 }
1445
cJSONUtils_GenerateMergePatchCaseSensitive(cJSON * const from,cJSON * const to)1446 CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatchCaseSensitive(cJSON * const from, cJSON * const to)
1447 {
1448 return generate_merge_patch(from, to, true);
1449 }
1450