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 #ifdef __CJSON_USE_INT64
614 case cJSON_Number:
615 /* numeric mismatch. */
616 if ((a->valueint != b->valueint) || (!compare_double(a->valuedouble, b->valuedouble)))
617 {
618 return false;
619 }
620
621 if ((a->type & cJSON_IsInt64) != (b->type & cJSON_IsInt64))
622 {
623 /* cJSON_IsInt64 should also be considered */
624 return false;
625 }
626
627 return true;
628 #else
629 case cJSON_Number:
630 /* numeric mismatch. */
631 if ((a->valueint != b->valueint) || (!compare_double(a->valuedouble, b->valuedouble)))
632 {
633 return false;
634 }
635 else
636 {
637 return true;
638 }
639 #endif /* __CJSON_USE_INT64 */
640
641 case cJSON_String:
642 /* string mismatch. */
643 if (strcmp(a->valuestring, b->valuestring) != 0)
644 {
645 return false;
646 }
647 else
648 {
649 return true;
650 }
651
652 case cJSON_Array:
653 for ((void)(a = a->child), b = b->child; (a != NULL) && (b != NULL); (void)(a = a->next), b = b->next)
654 {
655 cJSON_bool identical = compare_json(a, b, case_sensitive);
656 if (!identical)
657 {
658 return false;
659 }
660 }
661
662 /* array size mismatch? (one of both children is not NULL) */
663 if ((a != NULL) || (b != NULL))
664 {
665 return false;
666 }
667 else
668 {
669 return true;
670 }
671
672 case cJSON_Object:
673 sort_object(a, case_sensitive);
674 sort_object(b, case_sensitive);
675 for ((void)(a = a->child), b = b->child; (a != NULL) && (b != NULL); (void)(a = a->next), b = b->next)
676 {
677 cJSON_bool identical = false;
678 /* compare object keys */
679 if (compare_strings((unsigned char*)a->string, (unsigned char*)b->string, case_sensitive))
680 {
681 /* missing member */
682 return false;
683 }
684 identical = compare_json(a, b, case_sensitive);
685 if (!identical)
686 {
687 return false;
688 }
689 }
690
691 /* object length mismatch (one of both children is not null) */
692 if ((a != NULL) || (b != NULL))
693 {
694 return false;
695 }
696 else
697 {
698 return true;
699 }
700
701 default:
702 break;
703 }
704
705 /* null, true or false */
706 return true;
707 }
708
709 /* non broken version of cJSON_InsertItemInArray */
insert_item_in_array(cJSON *array, size_t which, cJSON *newitem)710 static cJSON_bool insert_item_in_array(cJSON *array, size_t which, cJSON *newitem)
711 {
712 cJSON *child = array->child;
713 while (child && (which > 0))
714 {
715 child = child->next;
716 which--;
717 }
718 if (which > 0)
719 {
720 /* item is after the end of the array */
721 return 0;
722 }
723 if (child == NULL)
724 {
725 cJSON_AddItemToArray(array, newitem);
726 return 1;
727 }
728
729 /* insert into the linked list */
730 newitem->next = child;
731 newitem->prev = child->prev;
732 child->prev = newitem;
733
734 /* was it at the beginning */
735 if (child == array->child)
736 {
737 array->child = newitem;
738 }
739 else
740 {
741 newitem->prev->next = newitem;
742 }
743
744 return 1;
745 }
746
get_object_item(const cJSON * const object, const char* name, const cJSON_bool case_sensitive)747 static cJSON *get_object_item(const cJSON * const object, const char* name, const cJSON_bool case_sensitive)
748 {
749 if (case_sensitive)
750 {
751 return cJSON_GetObjectItemCaseSensitive(object, name);
752 }
753
754 return cJSON_GetObjectItem(object, name);
755 }
756
757 enum patch_operation { INVALID, ADD, REMOVE, REPLACE, MOVE, COPY, TEST };
758
decode_patch_operation(const cJSON * const patch, const cJSON_bool case_sensitive)759 static enum patch_operation decode_patch_operation(const cJSON * const patch, const cJSON_bool case_sensitive)
760 {
761 cJSON *operation = get_object_item(patch, "op", case_sensitive);
762 if (!cJSON_IsString(operation))
763 {
764 return INVALID;
765 }
766
767 if (strcmp(operation->valuestring, "add") == 0)
768 {
769 return ADD;
770 }
771
772 if (strcmp(operation->valuestring, "remove") == 0)
773 {
774 return REMOVE;
775 }
776
777 if (strcmp(operation->valuestring, "replace") == 0)
778 {
779 return REPLACE;
780 }
781
782 if (strcmp(operation->valuestring, "move") == 0)
783 {
784 return MOVE;
785 }
786
787 if (strcmp(operation->valuestring, "copy") == 0)
788 {
789 return COPY;
790 }
791
792 if (strcmp(operation->valuestring, "test") == 0)
793 {
794 return TEST;
795 }
796
797 return INVALID;
798 }
799
800 /* overwrite and existing item with another one and free resources on the way */
overwrite_item(cJSON * const root, const cJSON replacement)801 static void overwrite_item(cJSON * const root, const cJSON replacement)
802 {
803 if (root == NULL)
804 {
805 return;
806 }
807
808 if (root->string != NULL)
809 {
810 cJSON_free(root->string);
811 }
812 if (root->valuestring != NULL)
813 {
814 cJSON_free(root->valuestring);
815 }
816 if (root->child != NULL)
817 {
818 cJSON_Delete(root->child);
819 }
820
821 memcpy(root, &replacement, sizeof(cJSON));
822 }
823
apply_patch(cJSON *object, const cJSON *patch, const cJSON_bool case_sensitive)824 static int apply_patch(cJSON *object, const cJSON *patch, const cJSON_bool case_sensitive)
825 {
826 cJSON *path = NULL;
827 cJSON *value = NULL;
828 cJSON *parent = NULL;
829 enum patch_operation opcode = INVALID;
830 unsigned char *parent_pointer = NULL;
831 unsigned char *child_pointer = NULL;
832 int status = 0;
833
834 path = get_object_item(patch, "path", case_sensitive);
835 if (!cJSON_IsString(path))
836 {
837 /* malformed patch. */
838 status = 2;
839 goto cleanup;
840 }
841
842 opcode = decode_patch_operation(patch, case_sensitive);
843 if (opcode == INVALID)
844 {
845 status = 3;
846 goto cleanup;
847 }
848 else if (opcode == TEST)
849 {
850 /* compare value: {...} with the given path */
851 status = !compare_json(get_item_from_pointer(object, path->valuestring, case_sensitive), get_object_item(patch, "value", case_sensitive), case_sensitive);
852 goto cleanup;
853 }
854
855 /* special case for replacing the root */
856 if (path->valuestring[0] == '\0')
857 {
858 if (opcode == REMOVE)
859 {
860 static const cJSON invalid = { NULL, NULL, NULL, cJSON_Invalid, NULL, 0, 0, NULL};
861
862 overwrite_item(object, invalid);
863
864 status = 0;
865 goto cleanup;
866 }
867
868 if ((opcode == REPLACE) || (opcode == ADD))
869 {
870 value = get_object_item(patch, "value", case_sensitive);
871 if (value == NULL)
872 {
873 /* missing "value" for add/replace. */
874 status = 7;
875 goto cleanup;
876 }
877
878 value = cJSON_Duplicate(value, 1);
879 if (value == NULL)
880 {
881 /* out of memory for add/replace. */
882 status = 8;
883 goto cleanup;
884 }
885
886 overwrite_item(object, *value);
887
888 /* delete the duplicated value */
889 cJSON_free(value);
890 value = NULL;
891
892 /* the string "value" isn't needed */
893 if (object->string != NULL)
894 {
895 cJSON_free(object->string);
896 object->string = NULL;
897 }
898
899 status = 0;
900 goto cleanup;
901 }
902 }
903
904 if ((opcode == REMOVE) || (opcode == REPLACE))
905 {
906 /* Get rid of old. */
907 cJSON *old_item = detach_path(object, (unsigned char*)path->valuestring, case_sensitive);
908 if (old_item == NULL)
909 {
910 status = 13;
911 goto cleanup;
912 }
913 cJSON_Delete(old_item);
914 if (opcode == REMOVE)
915 {
916 /* For Remove, this job is done. */
917 status = 0;
918 goto cleanup;
919 }
920 }
921
922 /* Copy/Move uses "from". */
923 if ((opcode == MOVE) || (opcode == COPY))
924 {
925 cJSON *from = get_object_item(patch, "from", case_sensitive);
926 if (from == NULL)
927 {
928 /* missing "from" for copy/move. */
929 status = 4;
930 goto cleanup;
931 }
932
933 if (opcode == MOVE)
934 {
935 value = detach_path(object, (unsigned char*)from->valuestring, case_sensitive);
936 }
937 if (opcode == COPY)
938 {
939 value = get_item_from_pointer(object, from->valuestring, case_sensitive);
940 }
941 if (value == NULL)
942 {
943 /* missing "from" for copy/move. */
944 status = 5;
945 goto cleanup;
946 }
947 if (opcode == COPY)
948 {
949 value = cJSON_Duplicate(value, 1);
950 }
951 if (value == NULL)
952 {
953 /* out of memory for copy/move. */
954 status = 6;
955 goto cleanup;
956 }
957 }
958 else /* Add/Replace uses "value". */
959 {
960 value = get_object_item(patch, "value", case_sensitive);
961 if (value == NULL)
962 {
963 /* missing "value" for add/replace. */
964 status = 7;
965 goto cleanup;
966 }
967 value = cJSON_Duplicate(value, 1);
968 if (value == NULL)
969 {
970 /* out of memory for add/replace. */
971 status = 8;
972 goto cleanup;
973 }
974 }
975
976 /* Now, just add "value" to "path". */
977
978 /* split pointer in parent and child */
979 parent_pointer = cJSONUtils_strdup((unsigned char*)path->valuestring);
980 if (parent_pointer) {
981 child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/');
982 }
983 if (child_pointer != NULL)
984 {
985 child_pointer[0] = '\0';
986 child_pointer++;
987 }
988 parent = get_item_from_pointer(object, (char*)parent_pointer, case_sensitive);
989 decode_pointer_inplace(child_pointer);
990
991 /* add, remove, replace, move, copy, test. */
992 if ((parent == NULL) || (child_pointer == NULL))
993 {
994 /* Couldn't find object to add to. */
995 status = 9;
996 goto cleanup;
997 }
998 else if (cJSON_IsArray(parent))
999 {
1000 if (strcmp((char*)child_pointer, "-") == 0)
1001 {
1002 cJSON_AddItemToArray(parent, value);
1003 value = NULL;
1004 }
1005 else
1006 {
1007 size_t index = 0;
1008 if (!decode_array_index_from_pointer(child_pointer, &index))
1009 {
1010 status = 11;
1011 goto cleanup;
1012 }
1013
1014 if (!insert_item_in_array(parent, index, value))
1015 {
1016 status = 10;
1017 goto cleanup;
1018 }
1019 value = NULL;
1020 }
1021 }
1022 else if (cJSON_IsObject(parent))
1023 {
1024 if (case_sensitive)
1025 {
1026 cJSON_DeleteItemFromObjectCaseSensitive(parent, (char*)child_pointer);
1027 }
1028 else
1029 {
1030 cJSON_DeleteItemFromObject(parent, (char*)child_pointer);
1031 }
1032 cJSON_AddItemToObject(parent, (char*)child_pointer, value);
1033 value = NULL;
1034 }
1035 else /* parent is not an object */
1036 {
1037 /* Couldn't find object to add to. */
1038 status = 9;
1039 goto cleanup;
1040 }
1041
1042 cleanup:
1043 if (value != NULL)
1044 {
1045 cJSON_Delete(value);
1046 }
1047 if (parent_pointer != NULL)
1048 {
1049 cJSON_free(parent_pointer);
1050 }
1051
1052 return status;
1053 }
1054
cJSONUtils_ApplyPatches(cJSON * const object, const cJSON * const patches)1055 CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON * const object, const cJSON * const patches)
1056 {
1057 const cJSON *current_patch = NULL;
1058 int status = 0;
1059
1060 if (!cJSON_IsArray(patches))
1061 {
1062 /* malformed patches. */
1063 return 1;
1064 }
1065
1066 if (patches != NULL)
1067 {
1068 current_patch = patches->child;
1069 }
1070
1071 while (current_patch != NULL)
1072 {
1073 status = apply_patch(object, current_patch, false);
1074 if (status != 0)
1075 {
1076 return status;
1077 }
1078 current_patch = current_patch->next;
1079 }
1080
1081 return 0;
1082 }
1083
cJSONUtils_ApplyPatchesCaseSensitive(cJSON * const object, const cJSON * const patches)1084 CJSON_PUBLIC(int) cJSONUtils_ApplyPatchesCaseSensitive(cJSON * const object, const cJSON * const patches)
1085 {
1086 const cJSON *current_patch = NULL;
1087 int status = 0;
1088
1089 if (!cJSON_IsArray(patches))
1090 {
1091 /* malformed patches. */
1092 return 1;
1093 }
1094
1095 if (patches != NULL)
1096 {
1097 current_patch = patches->child;
1098 }
1099
1100 while (current_patch != NULL)
1101 {
1102 status = apply_patch(object, current_patch, true);
1103 if (status != 0)
1104 {
1105 return status;
1106 }
1107 current_patch = current_patch->next;
1108 }
1109
1110 return 0;
1111 }
1112
compose_patch(cJSON * const patches, const unsigned char * const operation, const unsigned char * const path, const unsigned char *suffix, const cJSON * const value)1113 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)
1114 {
1115 cJSON *patch = NULL;
1116
1117 if ((patches == NULL) || (operation == NULL) || (path == NULL))
1118 {
1119 return;
1120 }
1121
1122 patch = cJSON_CreateObject();
1123 if (patch == NULL)
1124 {
1125 return;
1126 }
1127 cJSON_AddItemToObject(patch, "op", cJSON_CreateString((const char*)operation));
1128
1129 if (suffix == NULL)
1130 {
1131 cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)path));
1132 }
1133 else
1134 {
1135 size_t suffix_length = pointer_encoded_length(suffix);
1136 size_t path_length = strlen((const char*)path);
1137 unsigned char *full_path = (unsigned char*)cJSON_malloc(path_length + suffix_length + sizeof("/"));
1138
1139 sprintf((char*)full_path, "%s/", (const char*)path);
1140 encode_string_as_pointer(full_path + path_length + 1, suffix);
1141
1142 cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)full_path));
1143 cJSON_free(full_path);
1144 }
1145
1146 if (value != NULL)
1147 {
1148 cJSON_AddItemToObject(patch, "value", cJSON_Duplicate(value, 1));
1149 }
1150 cJSON_AddItemToArray(patches, patch);
1151 }
1152
cJSONUtils_AddPatchToArray(cJSON * const array, const char * const operation, const char * const path, const cJSON * const value)1153 CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON * const array, const char * const operation, const char * const path, const cJSON * const value)
1154 {
1155 compose_patch(array, (const unsigned char*)operation, (const unsigned char*)path, NULL, value);
1156 }
1157
create_patches(cJSON * const patches, const unsigned char * const path, cJSON * const from, cJSON * const to, const cJSON_bool case_sensitive)1158 static void create_patches(cJSON * const patches, const unsigned char * const path, cJSON * const from, cJSON * const to, const cJSON_bool case_sensitive)
1159 {
1160 if ((from == NULL) || (to == NULL))
1161 {
1162 return;
1163 }
1164
1165 if ((from->type & 0xFF) != (to->type & 0xFF))
1166 {
1167 compose_patch(patches, (const unsigned char*)"replace", path, 0, to);
1168 return;
1169 }
1170
1171 switch (from->type & 0xFF)
1172 {
1173 case cJSON_Number:
1174 if ((from->valueint != to->valueint) || !compare_double(from->valuedouble, to->valuedouble))
1175 {
1176 compose_patch(patches, (const unsigned char*)"replace", path, NULL, to);
1177 }
1178 return;
1179
1180 case cJSON_String:
1181 if (strcmp(from->valuestring, to->valuestring) != 0)
1182 {
1183 compose_patch(patches, (const unsigned char*)"replace", path, NULL, to);
1184 }
1185 return;
1186
1187 case cJSON_Array:
1188 {
1189 size_t index = 0;
1190 cJSON *from_child = from->child;
1191 cJSON *to_child = to->child;
1192 unsigned char *new_path = (unsigned char*)cJSON_malloc(strlen((const char*)path) + 20 + sizeof("/")); /* Allow space for 64bit int. log10(2^64) = 20 */
1193
1194 /* generate patches for all array elements that exist in both "from" and "to" */
1195 for (index = 0; (from_child != NULL) && (to_child != NULL); (void)(from_child = from_child->next), (void)(to_child = to_child->next), index++)
1196 {
1197 /* check if conversion to unsigned long is valid
1198 * This should be eliminated at compile time by dead code elimination
1199 * if size_t is an alias of unsigned long, or if it is bigger */
1200 if (index > ULONG_MAX)
1201 {
1202 cJSON_free(new_path);
1203 return;
1204 }
1205 sprintf((char*)new_path, "%s/%lu", path, (unsigned long)index); /* path of the current array element */
1206 create_patches(patches, new_path, from_child, to_child, case_sensitive);
1207 }
1208
1209 /* remove leftover elements from 'from' that are not in 'to' */
1210 for (; (from_child != NULL); (void)(from_child = from_child->next))
1211 {
1212 /* check if conversion to unsigned long is valid
1213 * This should be eliminated at compile time by dead code elimination
1214 * if size_t is an alias of unsigned long, or if it is bigger */
1215 if (index > ULONG_MAX)
1216 {
1217 cJSON_free(new_path);
1218 return;
1219 }
1220 sprintf((char*)new_path, "%lu", (unsigned long)index);
1221 compose_patch(patches, (const unsigned char*)"remove", path, new_path, NULL);
1222 }
1223 /* add new elements in 'to' that were not in 'from' */
1224 for (; (to_child != NULL); (void)(to_child = to_child->next), index++)
1225 {
1226 compose_patch(patches, (const unsigned char*)"add", path, (const unsigned char*)"-", to_child);
1227 }
1228 cJSON_free(new_path);
1229 return;
1230 }
1231
1232 case cJSON_Object:
1233 {
1234 cJSON *from_child = NULL;
1235 cJSON *to_child = NULL;
1236 sort_object(from, case_sensitive);
1237 sort_object(to, case_sensitive);
1238
1239 from_child = from->child;
1240 to_child = to->child;
1241 /* for all object values in the object with more of them */
1242 while ((from_child != NULL) || (to_child != NULL))
1243 {
1244 int diff;
1245 if (from_child == NULL)
1246 {
1247 diff = 1;
1248 }
1249 else if (to_child == NULL)
1250 {
1251 diff = -1;
1252 }
1253 else
1254 {
1255 diff = compare_strings((unsigned char*)from_child->string, (unsigned char*)to_child->string, case_sensitive);
1256 }
1257
1258 if (diff == 0)
1259 {
1260 /* both object keys are the same */
1261 size_t path_length = strlen((const char*)path);
1262 size_t from_child_name_length = pointer_encoded_length((unsigned char*)from_child->string);
1263 unsigned char *new_path = (unsigned char*)cJSON_malloc(path_length + from_child_name_length + sizeof("/"));
1264
1265 sprintf((char*)new_path, "%s/", path);
1266 encode_string_as_pointer(new_path + path_length + 1, (unsigned char*)from_child->string);
1267
1268 /* create a patch for the element */
1269 create_patches(patches, new_path, from_child, to_child, case_sensitive);
1270 cJSON_free(new_path);
1271
1272 from_child = from_child->next;
1273 to_child = to_child->next;
1274 }
1275 else if (diff < 0)
1276 {
1277 /* object element doesn't exist in 'to' --> remove it */
1278 compose_patch(patches, (const unsigned char*)"remove", path, (unsigned char*)from_child->string, NULL);
1279
1280 from_child = from_child->next;
1281 }
1282 else
1283 {
1284 /* object element doesn't exist in 'from' --> add it */
1285 compose_patch(patches, (const unsigned char*)"add", path, (unsigned char*)to_child->string, to_child);
1286
1287 to_child = to_child->next;
1288 }
1289 }
1290 return;
1291 }
1292
1293 default:
1294 break;
1295 }
1296 }
1297
cJSONUtils_GeneratePatches(cJSON * const from, cJSON * const to)1298 CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON * const from, cJSON * const to)
1299 {
1300 cJSON *patches = NULL;
1301
1302 if ((from == NULL) || (to == NULL))
1303 {
1304 return NULL;
1305 }
1306
1307 patches = cJSON_CreateArray();
1308 create_patches(patches, (const unsigned char*)"", from, to, false);
1309
1310 return patches;
1311 }
1312
cJSONUtils_GeneratePatchesCaseSensitive(cJSON * const from, cJSON * const to)1313 CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatchesCaseSensitive(cJSON * const from, cJSON * const to)
1314 {
1315 cJSON *patches = NULL;
1316
1317 if ((from == NULL) || (to == NULL))
1318 {
1319 return NULL;
1320 }
1321
1322 patches = cJSON_CreateArray();
1323 create_patches(patches, (const unsigned char*)"", from, to, true);
1324
1325 return patches;
1326 }
1327
cJSONUtils_SortObject(cJSON * const object)1328 CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON * const object)
1329 {
1330 sort_object(object, false);
1331 }
1332
cJSONUtils_SortObjectCaseSensitive(cJSON * const object)1333 CJSON_PUBLIC(void) cJSONUtils_SortObjectCaseSensitive(cJSON * const object)
1334 {
1335 sort_object(object, true);
1336 }
1337
merge_patch(cJSON *target, const cJSON * const patch, const cJSON_bool case_sensitive)1338 static cJSON *merge_patch(cJSON *target, const cJSON * const patch, const cJSON_bool case_sensitive)
1339 {
1340 cJSON *patch_child = NULL;
1341
1342 if (!cJSON_IsObject(patch))
1343 {
1344 /* scalar value, array or NULL, just duplicate */
1345 cJSON_Delete(target);
1346 return cJSON_Duplicate(patch, 1);
1347 }
1348
1349 if (!cJSON_IsObject(target))
1350 {
1351 cJSON_Delete(target);
1352 target = cJSON_CreateObject();
1353 }
1354
1355 patch_child = patch->child;
1356 while (patch_child != NULL)
1357 {
1358 if (cJSON_IsNull(patch_child))
1359 {
1360 /* NULL is the indicator to remove a value, see RFC7396 */
1361 if (case_sensitive)
1362 {
1363 cJSON_DeleteItemFromObjectCaseSensitive(target, patch_child->string);
1364 }
1365 else
1366 {
1367 cJSON_DeleteItemFromObject(target, patch_child->string);
1368 }
1369 }
1370 else
1371 {
1372 cJSON *replace_me = NULL;
1373 cJSON *replacement = NULL;
1374
1375 if (case_sensitive)
1376 {
1377 replace_me = cJSON_DetachItemFromObjectCaseSensitive(target, patch_child->string);
1378 }
1379 else
1380 {
1381 replace_me = cJSON_DetachItemFromObject(target, patch_child->string);
1382 }
1383
1384 replacement = merge_patch(replace_me, patch_child, case_sensitive);
1385 if (replacement == NULL)
1386 {
1387 cJSON_Delete(target);
1388 return NULL;
1389 }
1390
1391 cJSON_AddItemToObject(target, patch_child->string, replacement);
1392 }
1393 patch_child = patch_child->next;
1394 }
1395 return target;
1396 }
1397
cJSONUtils_MergePatch(cJSON *target, const cJSON * const patch)1398 CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, const cJSON * const patch)
1399 {
1400 return merge_patch(target, patch, false);
1401 }
1402
cJSONUtils_MergePatchCaseSensitive(cJSON *target, const cJSON * const patch)1403 CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatchCaseSensitive(cJSON *target, const cJSON * const patch)
1404 {
1405 return merge_patch(target, patch, true);
1406 }
1407
generate_merge_patch(cJSON * const from, cJSON * const to, const cJSON_bool case_sensitive)1408 static cJSON *generate_merge_patch(cJSON * const from, cJSON * const to, const cJSON_bool case_sensitive)
1409 {
1410 cJSON *from_child = NULL;
1411 cJSON *to_child = NULL;
1412 cJSON *patch = NULL;
1413 if (to == NULL)
1414 {
1415 /* patch to delete everything */
1416 return cJSON_CreateNull();
1417 }
1418 if (!cJSON_IsObject(to) || !cJSON_IsObject(from))
1419 {
1420 return cJSON_Duplicate(to, 1);
1421 }
1422
1423 sort_object(from, case_sensitive);
1424 sort_object(to, case_sensitive);
1425
1426 from_child = from->child;
1427 to_child = to->child;
1428 patch = cJSON_CreateObject();
1429 if (patch == NULL)
1430 {
1431 return NULL;
1432 }
1433 while (from_child || to_child)
1434 {
1435 int diff;
1436 if (from_child != NULL)
1437 {
1438 if (to_child != NULL)
1439 {
1440 diff = strcmp(from_child->string, to_child->string);
1441 }
1442 else
1443 {
1444 diff = -1;
1445 }
1446 }
1447 else
1448 {
1449 diff = 1;
1450 }
1451
1452 if (diff < 0)
1453 {
1454 /* from has a value that to doesn't have -> remove */
1455 cJSON_AddItemToObject(patch, from_child->string, cJSON_CreateNull());
1456
1457 from_child = from_child->next;
1458 }
1459 else if (diff > 0)
1460 {
1461 /* to has a value that from doesn't have -> add to patch */
1462 cJSON_AddItemToObject(patch, to_child->string, cJSON_Duplicate(to_child, 1));
1463
1464 to_child = to_child->next;
1465 }
1466 else
1467 {
1468 /* object key exists in both objects */
1469 if (!compare_json(from_child, to_child, case_sensitive))
1470 {
1471 /* not identical --> generate a patch */
1472 cJSON_AddItemToObject(patch, to_child->string, cJSONUtils_GenerateMergePatch(from_child, to_child));
1473 }
1474
1475 /* next key in the object */
1476 from_child = from_child->next;
1477 to_child = to_child->next;
1478 }
1479 }
1480 if (patch->child == NULL)
1481 {
1482 /* no patch generated */
1483 cJSON_Delete(patch);
1484 return NULL;
1485 }
1486
1487 return patch;
1488 }
1489
cJSONUtils_GenerateMergePatch(cJSON * const from, cJSON * const to)1490 CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON * const from, cJSON * const to)
1491 {
1492 return generate_merge_patch(from, to, false);
1493 }
1494
cJSONUtils_GenerateMergePatchCaseSensitive(cJSON * const from, cJSON * const to)1495 CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatchCaseSensitive(cJSON * const from, cJSON * const to)
1496 {
1497 return generate_merge_patch(from, to, true);
1498 }
1499