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 if (parent_pointer) {
964 child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/');
965 }
966 if (child_pointer != NULL)
967 {
968 child_pointer[0] = '\0';
969 child_pointer++;
970 }
971 parent = get_item_from_pointer(object, (char*)parent_pointer, case_sensitive);
972 decode_pointer_inplace(child_pointer);
973
974 /* add, remove, replace, move, copy, test. */
975 if ((parent == NULL) || (child_pointer == NULL))
976 {
977 /* Couldn't find object to add to. */
978 status = 9;
979 goto cleanup;
980 }
981 else if (cJSON_IsArray(parent))
982 {
983 if (strcmp((char*)child_pointer, "-") == 0)
984 {
985 cJSON_AddItemToArray(parent, value);
986 value = NULL;
987 }
988 else
989 {
990 size_t index = 0;
991 if (!decode_array_index_from_pointer(child_pointer, &index))
992 {
993 status = 11;
994 goto cleanup;
995 }
996
997 if (!insert_item_in_array(parent, index, value))
998 {
999 status = 10;
1000 goto cleanup;
1001 }
1002 value = NULL;
1003 }
1004 }
1005 else if (cJSON_IsObject(parent))
1006 {
1007 if (case_sensitive)
1008 {
1009 cJSON_DeleteItemFromObjectCaseSensitive(parent, (char*)child_pointer);
1010 }
1011 else
1012 {
1013 cJSON_DeleteItemFromObject(parent, (char*)child_pointer);
1014 }
1015 cJSON_AddItemToObject(parent, (char*)child_pointer, value);
1016 value = NULL;
1017 }
1018 else /* parent is not an object */
1019 {
1020 /* Couldn't find object to add to. */
1021 status = 9;
1022 goto cleanup;
1023 }
1024
1025 cleanup:
1026 if (value != NULL)
1027 {
1028 cJSON_Delete(value);
1029 }
1030 if (parent_pointer != NULL)
1031 {
1032 cJSON_free(parent_pointer);
1033 }
1034
1035 return status;
1036 }
1037
cJSONUtils_ApplyPatches(cJSON * const object,const cJSON * const patches)1038 CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON * const object, const cJSON * const patches)
1039 {
1040 const cJSON *current_patch = NULL;
1041 int status = 0;
1042
1043 if (!cJSON_IsArray(patches))
1044 {
1045 /* malformed patches. */
1046 return 1;
1047 }
1048
1049 if (patches != NULL)
1050 {
1051 current_patch = patches->child;
1052 }
1053
1054 while (current_patch != NULL)
1055 {
1056 status = apply_patch(object, current_patch, false);
1057 if (status != 0)
1058 {
1059 return status;
1060 }
1061 current_patch = current_patch->next;
1062 }
1063
1064 return 0;
1065 }
1066
cJSONUtils_ApplyPatchesCaseSensitive(cJSON * const object,const cJSON * const patches)1067 CJSON_PUBLIC(int) cJSONUtils_ApplyPatchesCaseSensitive(cJSON * const object, const cJSON * const patches)
1068 {
1069 const cJSON *current_patch = NULL;
1070 int status = 0;
1071
1072 if (!cJSON_IsArray(patches))
1073 {
1074 /* malformed patches. */
1075 return 1;
1076 }
1077
1078 if (patches != NULL)
1079 {
1080 current_patch = patches->child;
1081 }
1082
1083 while (current_patch != NULL)
1084 {
1085 status = apply_patch(object, current_patch, true);
1086 if (status != 0)
1087 {
1088 return status;
1089 }
1090 current_patch = current_patch->next;
1091 }
1092
1093 return 0;
1094 }
1095
compose_patch(cJSON * const patches,const unsigned char * const operation,const unsigned char * const path,const unsigned char * suffix,const cJSON * const value)1096 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)
1097 {
1098 cJSON *patch = NULL;
1099
1100 if ((patches == NULL) || (operation == NULL) || (path == NULL))
1101 {
1102 return;
1103 }
1104
1105 patch = cJSON_CreateObject();
1106 if (patch == NULL)
1107 {
1108 return;
1109 }
1110 cJSON_AddItemToObject(patch, "op", cJSON_CreateString((const char*)operation));
1111
1112 if (suffix == NULL)
1113 {
1114 cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)path));
1115 }
1116 else
1117 {
1118 size_t suffix_length = pointer_encoded_length(suffix);
1119 size_t path_length = strlen((const char*)path);
1120 unsigned char *full_path = (unsigned char*)cJSON_malloc(path_length + suffix_length + sizeof("/"));
1121
1122 sprintf((char*)full_path, "%s/", (const char*)path);
1123 encode_string_as_pointer(full_path + path_length + 1, suffix);
1124
1125 cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)full_path));
1126 cJSON_free(full_path);
1127 }
1128
1129 if (value != NULL)
1130 {
1131 cJSON_AddItemToObject(patch, "value", cJSON_Duplicate(value, 1));
1132 }
1133 cJSON_AddItemToArray(patches, patch);
1134 }
1135
cJSONUtils_AddPatchToArray(cJSON * const array,const char * const operation,const char * const path,const cJSON * const value)1136 CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON * const array, const char * const operation, const char * const path, const cJSON * const value)
1137 {
1138 compose_patch(array, (const unsigned char*)operation, (const unsigned char*)path, NULL, value);
1139 }
1140
create_patches(cJSON * const patches,const unsigned char * const path,cJSON * const from,cJSON * const to,const cJSON_bool case_sensitive)1141 static void create_patches(cJSON * const patches, const unsigned char * const path, cJSON * const from, cJSON * const to, const cJSON_bool case_sensitive)
1142 {
1143 if ((from == NULL) || (to == NULL))
1144 {
1145 return;
1146 }
1147
1148 if ((from->type & 0xFF) != (to->type & 0xFF))
1149 {
1150 compose_patch(patches, (const unsigned char*)"replace", path, 0, to);
1151 return;
1152 }
1153
1154 switch (from->type & 0xFF)
1155 {
1156 case cJSON_Number:
1157 if ((from->valueint != to->valueint) || !compare_double(from->valuedouble, to->valuedouble))
1158 {
1159 compose_patch(patches, (const unsigned char*)"replace", path, NULL, to);
1160 }
1161 return;
1162
1163 case cJSON_String:
1164 if (strcmp(from->valuestring, to->valuestring) != 0)
1165 {
1166 compose_patch(patches, (const unsigned char*)"replace", path, NULL, to);
1167 }
1168 return;
1169
1170 case cJSON_Array:
1171 {
1172 size_t index = 0;
1173 cJSON *from_child = from->child;
1174 cJSON *to_child = to->child;
1175 unsigned char *new_path = (unsigned char*)cJSON_malloc(strlen((const char*)path) + 20 + sizeof("/")); /* Allow space for 64bit int. log10(2^64) = 20 */
1176
1177 /* generate patches for all array elements that exist in both "from" and "to" */
1178 for (index = 0; (from_child != NULL) && (to_child != NULL); (void)(from_child = from_child->next), (void)(to_child = to_child->next), index++)
1179 {
1180 /* check if conversion to unsigned long is valid
1181 * This should be eliminated at compile time by dead code elimination
1182 * if size_t is an alias of unsigned long, or if it is bigger */
1183 if (index > ULONG_MAX)
1184 {
1185 cJSON_free(new_path);
1186 return;
1187 }
1188 sprintf((char*)new_path, "%s/%lu", path, (unsigned long)index); /* path of the current array element */
1189 create_patches(patches, new_path, from_child, to_child, case_sensitive);
1190 }
1191
1192 /* remove leftover elements from 'from' that are not in 'to' */
1193 for (; (from_child != NULL); (void)(from_child = from_child->next))
1194 {
1195 /* check if conversion to unsigned long is valid
1196 * This should be eliminated at compile time by dead code elimination
1197 * if size_t is an alias of unsigned long, or if it is bigger */
1198 if (index > ULONG_MAX)
1199 {
1200 cJSON_free(new_path);
1201 return;
1202 }
1203 sprintf((char*)new_path, "%lu", (unsigned long)index);
1204 compose_patch(patches, (const unsigned char*)"remove", path, new_path, NULL);
1205 }
1206 /* add new elements in 'to' that were not in 'from' */
1207 for (; (to_child != NULL); (void)(to_child = to_child->next), index++)
1208 {
1209 compose_patch(patches, (const unsigned char*)"add", path, (const unsigned char*)"-", to_child);
1210 }
1211 cJSON_free(new_path);
1212 return;
1213 }
1214
1215 case cJSON_Object:
1216 {
1217 cJSON *from_child = NULL;
1218 cJSON *to_child = NULL;
1219 sort_object(from, case_sensitive);
1220 sort_object(to, case_sensitive);
1221
1222 from_child = from->child;
1223 to_child = to->child;
1224 /* for all object values in the object with more of them */
1225 while ((from_child != NULL) || (to_child != NULL))
1226 {
1227 int diff;
1228 if (from_child == NULL)
1229 {
1230 diff = 1;
1231 }
1232 else if (to_child == NULL)
1233 {
1234 diff = -1;
1235 }
1236 else
1237 {
1238 diff = compare_strings((unsigned char*)from_child->string, (unsigned char*)to_child->string, case_sensitive);
1239 }
1240
1241 if (diff == 0)
1242 {
1243 /* both object keys are the same */
1244 size_t path_length = strlen((const char*)path);
1245 size_t from_child_name_length = pointer_encoded_length((unsigned char*)from_child->string);
1246 unsigned char *new_path = (unsigned char*)cJSON_malloc(path_length + from_child_name_length + sizeof("/"));
1247
1248 sprintf((char*)new_path, "%s/", path);
1249 encode_string_as_pointer(new_path + path_length + 1, (unsigned char*)from_child->string);
1250
1251 /* create a patch for the element */
1252 create_patches(patches, new_path, from_child, to_child, case_sensitive);
1253 cJSON_free(new_path);
1254
1255 from_child = from_child->next;
1256 to_child = to_child->next;
1257 }
1258 else if (diff < 0)
1259 {
1260 /* object element doesn't exist in 'to' --> remove it */
1261 compose_patch(patches, (const unsigned char*)"remove", path, (unsigned char*)from_child->string, NULL);
1262
1263 from_child = from_child->next;
1264 }
1265 else
1266 {
1267 /* object element doesn't exist in 'from' --> add it */
1268 compose_patch(patches, (const unsigned char*)"add", path, (unsigned char*)to_child->string, to_child);
1269
1270 to_child = to_child->next;
1271 }
1272 }
1273 return;
1274 }
1275
1276 default:
1277 break;
1278 }
1279 }
1280
cJSONUtils_GeneratePatches(cJSON * const from,cJSON * const to)1281 CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON * const from, cJSON * const to)
1282 {
1283 cJSON *patches = NULL;
1284
1285 if ((from == NULL) || (to == NULL))
1286 {
1287 return NULL;
1288 }
1289
1290 patches = cJSON_CreateArray();
1291 create_patches(patches, (const unsigned char*)"", from, to, false);
1292
1293 return patches;
1294 }
1295
cJSONUtils_GeneratePatchesCaseSensitive(cJSON * const from,cJSON * const to)1296 CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatchesCaseSensitive(cJSON * const from, cJSON * const to)
1297 {
1298 cJSON *patches = NULL;
1299
1300 if ((from == NULL) || (to == NULL))
1301 {
1302 return NULL;
1303 }
1304
1305 patches = cJSON_CreateArray();
1306 create_patches(patches, (const unsigned char*)"", from, to, true);
1307
1308 return patches;
1309 }
1310
cJSONUtils_SortObject(cJSON * const object)1311 CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON * const object)
1312 {
1313 sort_object(object, false);
1314 }
1315
cJSONUtils_SortObjectCaseSensitive(cJSON * const object)1316 CJSON_PUBLIC(void) cJSONUtils_SortObjectCaseSensitive(cJSON * const object)
1317 {
1318 sort_object(object, true);
1319 }
1320
merge_patch(cJSON * target,const cJSON * const patch,const cJSON_bool case_sensitive)1321 static cJSON *merge_patch(cJSON *target, const cJSON * const patch, const cJSON_bool case_sensitive)
1322 {
1323 cJSON *patch_child = NULL;
1324
1325 if (!cJSON_IsObject(patch))
1326 {
1327 /* scalar value, array or NULL, just duplicate */
1328 cJSON_Delete(target);
1329 return cJSON_Duplicate(patch, 1);
1330 }
1331
1332 if (!cJSON_IsObject(target))
1333 {
1334 cJSON_Delete(target);
1335 target = cJSON_CreateObject();
1336 }
1337
1338 patch_child = patch->child;
1339 while (patch_child != NULL)
1340 {
1341 if (cJSON_IsNull(patch_child))
1342 {
1343 /* NULL is the indicator to remove a value, see RFC7396 */
1344 if (case_sensitive)
1345 {
1346 cJSON_DeleteItemFromObjectCaseSensitive(target, patch_child->string);
1347 }
1348 else
1349 {
1350 cJSON_DeleteItemFromObject(target, patch_child->string);
1351 }
1352 }
1353 else
1354 {
1355 cJSON *replace_me = NULL;
1356 cJSON *replacement = NULL;
1357
1358 if (case_sensitive)
1359 {
1360 replace_me = cJSON_DetachItemFromObjectCaseSensitive(target, patch_child->string);
1361 }
1362 else
1363 {
1364 replace_me = cJSON_DetachItemFromObject(target, patch_child->string);
1365 }
1366
1367 replacement = merge_patch(replace_me, patch_child, case_sensitive);
1368 if (replacement == NULL)
1369 {
1370 return NULL;
1371 }
1372
1373 cJSON_AddItemToObject(target, patch_child->string, replacement);
1374 }
1375 patch_child = patch_child->next;
1376 }
1377 return target;
1378 }
1379
cJSONUtils_MergePatch(cJSON * target,const cJSON * const patch)1380 CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, const cJSON * const patch)
1381 {
1382 return merge_patch(target, patch, false);
1383 }
1384
cJSONUtils_MergePatchCaseSensitive(cJSON * target,const cJSON * const patch)1385 CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatchCaseSensitive(cJSON *target, const cJSON * const patch)
1386 {
1387 return merge_patch(target, patch, true);
1388 }
1389
generate_merge_patch(cJSON * const from,cJSON * const to,const cJSON_bool case_sensitive)1390 static cJSON *generate_merge_patch(cJSON * const from, cJSON * const to, const cJSON_bool case_sensitive)
1391 {
1392 cJSON *from_child = NULL;
1393 cJSON *to_child = NULL;
1394 cJSON *patch = NULL;
1395 if (to == NULL)
1396 {
1397 /* patch to delete everything */
1398 return cJSON_CreateNull();
1399 }
1400 if (!cJSON_IsObject(to) || !cJSON_IsObject(from))
1401 {
1402 return cJSON_Duplicate(to, 1);
1403 }
1404
1405 sort_object(from, case_sensitive);
1406 sort_object(to, case_sensitive);
1407
1408 from_child = from->child;
1409 to_child = to->child;
1410 patch = cJSON_CreateObject();
1411 if (patch == NULL)
1412 {
1413 return NULL;
1414 }
1415 while (from_child || to_child)
1416 {
1417 int diff;
1418 if (from_child != NULL)
1419 {
1420 if (to_child != NULL)
1421 {
1422 diff = strcmp(from_child->string, to_child->string);
1423 }
1424 else
1425 {
1426 diff = -1;
1427 }
1428 }
1429 else
1430 {
1431 diff = 1;
1432 }
1433
1434 if (diff < 0)
1435 {
1436 /* from has a value that to doesn't have -> remove */
1437 cJSON_AddItemToObject(patch, from_child->string, cJSON_CreateNull());
1438
1439 from_child = from_child->next;
1440 }
1441 else if (diff > 0)
1442 {
1443 /* to has a value that from doesn't have -> add to patch */
1444 cJSON_AddItemToObject(patch, to_child->string, cJSON_Duplicate(to_child, 1));
1445
1446 to_child = to_child->next;
1447 }
1448 else
1449 {
1450 /* object key exists in both objects */
1451 if (!compare_json(from_child, to_child, case_sensitive))
1452 {
1453 /* not identical --> generate a patch */
1454 cJSON_AddItemToObject(patch, to_child->string, cJSONUtils_GenerateMergePatch(from_child, to_child));
1455 }
1456
1457 /* next key in the object */
1458 from_child = from_child->next;
1459 to_child = to_child->next;
1460 }
1461 }
1462 if (patch->child == NULL)
1463 {
1464 /* no patch generated */
1465 cJSON_Delete(patch);
1466 return NULL;
1467 }
1468
1469 return patch;
1470 }
1471
cJSONUtils_GenerateMergePatch(cJSON * const from,cJSON * const to)1472 CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON * const from, cJSON * const to)
1473 {
1474 return generate_merge_patch(from, to, false);
1475 }
1476
cJSONUtils_GenerateMergePatchCaseSensitive(cJSON * const from,cJSON * const to)1477 CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatchCaseSensitive(cJSON * const from, cJSON * const to)
1478 {
1479 return generate_merge_patch(from, to, true);
1480 }
1481