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