• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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