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