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