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