]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/file/src/is_json.c
Update apr to 1.7.0. See contrib/apr/CHANGES for a summary of changes.
[FreeBSD/FreeBSD.git] / contrib / file / src / is_json.c
1 /*-
2  * Copyright (c) 2018 Christos Zoulas
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
15  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
16  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
18  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24  * POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 /*
28  * Parse JSON object serialization format (RFC-7159)
29  */
30
31 #ifndef TEST
32 #include "file.h"
33
34 #ifndef lint
35 FILE_RCSID("@(#)$File: is_json.c,v 1.13 2019/03/02 01:08:10 christos Exp $")
36 #endif
37
38 #include <string.h>
39 #include "magic.h"
40 #endif
41
42 #ifdef DEBUG
43 #include <stdio.h>
44 #define DPRINTF(a, b, c)        \
45     printf("%s [%.2x/%c] %.20s\n", (a), *(b), *(b), (const char *)(c))
46 #else
47 #define DPRINTF(a, b, c)        do { } while (/*CONSTCOND*/0)
48 #endif
49
50 #define JSON_ARRAY      0
51 #define JSON_CONSTANT   1
52 #define JSON_NUMBER     2
53 #define JSON_OBJECT     3
54 #define JSON_STRING     4
55 #define JSON_ARRAYN     5
56 #define JSON_MAX        6
57
58 /*
59  * if JSON_COUNT != 0:
60  *      count all the objects, require that we have the whole data file
61  * otherwise:
62  *      stop if we find an object or an array
63  */
64 #ifndef JSON_COUNT
65 #define JSON_COUNT 0
66 #endif
67
68 static int json_parse(const unsigned char **, const unsigned char *, size_t *,
69         size_t);
70
71 static int
72 json_isspace(const unsigned char uc)
73 {
74         switch (uc) {
75         case ' ':
76         case '\n':
77         case '\r':
78         case '\t':
79                 return 1;
80         default:
81                 return 0;
82         }
83 }
84
85 static int
86 json_isdigit(unsigned char uc)
87 {
88         switch (uc) {
89         case '0': case '1': case '2': case '3': case '4':
90         case '5': case '6': case '7': case '8': case '9':
91                 return 1;
92         default:
93                 return 0;
94         }
95 }
96
97 static int
98 json_isxdigit(unsigned char uc)
99 {
100         if (json_isdigit(uc))
101                 return 1;
102         switch (uc) {
103         case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
104         case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
105                 return 1;
106         default:
107                 return 0;
108         }
109 }
110
111 static const unsigned char *
112 json_skip_space(const unsigned char *uc, const unsigned char *ue)
113 {
114         while (uc < ue && json_isspace(*uc))
115                 uc++;
116         return uc;
117 }
118
119 static int
120 json_parse_string(const unsigned char **ucp, const unsigned char *ue)
121 {
122         const unsigned char *uc = *ucp;
123         size_t i;
124
125         DPRINTF("Parse string: ", uc, *ucp);
126         while (uc < ue) {
127                 switch (*uc++) {
128                 case '\0':
129                         goto out;
130                 case '\\':
131                         if (uc == ue)
132                                 goto out;
133                         switch (*uc++) {
134                         case '\0':
135                                 goto out;
136                         case '"':
137                         case '\\':
138                         case '/':
139                         case 'b':
140                         case 'f':
141                         case 'n':
142                         case 'r':
143                         case 't':
144                                 continue;
145                         case 'u':
146                                 if (ue - uc < 4) {
147                                         uc = ue;
148                                         goto out;
149                                 }
150                                 for (i = 0; i < 4; i++)
151                                         if (!json_isxdigit(*uc++))
152                                                 goto out;
153                                 continue;
154                         default:
155                                 goto out;
156                         }
157                 case '"':
158                         *ucp = uc;
159                         return 1;
160                 default:
161                         continue;
162                 }
163         }
164 out:
165         DPRINTF("Bad string: ", uc, *ucp);
166         *ucp = uc;
167         return 0;
168 }
169
170 static int
171 json_parse_array(const unsigned char **ucp, const unsigned char *ue,
172         size_t *st, size_t lvl)
173 {
174         const unsigned char *uc = *ucp;
175         int more = 0;   /* Array has more than 1 element */
176
177         DPRINTF("Parse array: ", uc, *ucp);
178         while (uc < ue) {
179                 if (!json_parse(&uc, ue, st, lvl + 1))
180                         goto out;
181                 if (uc == ue)
182                         goto out;
183                 switch (*uc) {
184                 case ',':
185                         more++;
186                         uc++;
187                         continue;
188                 case ']':
189                         if (more)
190                                 st[JSON_ARRAYN]++;
191                         *ucp = uc + 1;
192                         return 1;
193                 default:
194                         goto out;
195                 }
196         }
197 out:
198         DPRINTF("Bad array: ", uc,  *ucp);
199         *ucp = uc;
200         return 0;
201 }
202
203 static int
204 json_parse_object(const unsigned char **ucp, const unsigned char *ue,
205         size_t *st, size_t lvl)
206 {
207         const unsigned char *uc = *ucp;
208         DPRINTF("Parse object: ", uc, *ucp);
209         while (uc < ue) {
210                 uc = json_skip_space(uc, ue);
211                 if (uc == ue)
212                         goto out;
213                 if (*uc++ != '"') {
214                         DPRINTF("not string", uc, *ucp);
215                         goto out;
216                 }
217                 DPRINTF("next field", uc, *ucp);
218                 if (!json_parse_string(&uc, ue)) {
219                         DPRINTF("not string", uc, *ucp);
220                         goto out;
221                 }
222                 uc = json_skip_space(uc, ue);
223                 if (uc == ue)
224                         goto out;
225                 if (*uc++ != ':') {
226                         DPRINTF("not colon", uc, *ucp);
227                         goto out;
228                 }
229                 if (!json_parse(&uc, ue, st, lvl + 1)) {
230                         DPRINTF("not json", uc, *ucp);
231                         goto out;
232                 }
233                 if (uc == ue)
234                         goto out;
235                 switch (*uc++) {
236                 case ',':
237                         continue;
238                 case '}': /* { */
239                         *ucp = uc;
240                         DPRINTF("Good object: ", uc, *ucp);
241                         return 1;
242                 default:
243                         *ucp = uc - 1;
244                         DPRINTF("not more", uc, *ucp);
245                         goto out;
246                 }
247         }
248 out:
249         DPRINTF("Bad object: ", uc, *ucp);
250         *ucp = uc;
251         return 0;
252 }
253
254 static int
255 json_parse_number(const unsigned char **ucp, const unsigned char *ue)
256 {
257         const unsigned char *uc = *ucp;
258         int got = 0;
259
260         DPRINTF("Parse number: ", uc, *ucp);
261         if (uc == ue)
262                 return 0;
263         if (*uc == '-')
264                 uc++;
265
266         for (; uc < ue; uc++) {
267                 if (!json_isdigit(*uc))
268                         break;
269                 got = 1;
270         }
271         if (uc == ue)
272                 goto out;
273         if (*uc == '.')
274                 uc++;
275         for (; uc < ue; uc++) {
276                 if (!json_isdigit(*uc))
277                         break;
278                 got = 1;
279         }
280         if (uc == ue)
281                 goto out;
282         if (got && (*uc == 'e' || *uc == 'E')) {
283                 uc++;
284                 got = 0;
285                 if (uc == ue)
286                         goto out;
287                 if (*uc == '+' || *uc == '-')
288                         uc++;
289                 for (; uc < ue; uc++) {
290                         if (!json_isdigit(*uc))
291                                 break;
292                         got = 1;
293                 }
294         }
295 out:
296         if (!got)
297                 DPRINTF("Bad number: ", uc, *ucp);
298         else
299                 DPRINTF("Good number: ", uc, *ucp);
300         *ucp = uc;
301         return got;
302 }
303
304 static int
305 json_parse_const(const unsigned char **ucp, const unsigned char *ue,
306     const char *str, size_t len)
307 {
308         const unsigned char *uc = *ucp;
309
310         DPRINTF("Parse const: ", uc, *ucp);
311         for (len--; uc < ue && --len;) {
312                 if (*uc++ == *++str)
313                         continue;
314         }
315         if (len)
316                 DPRINTF("Bad const: ", uc, *ucp);
317         *ucp = uc;
318         return len == 0;
319 }
320
321 static int
322 json_parse(const unsigned char **ucp, const unsigned char *ue,
323     size_t *st, size_t lvl)
324 {
325         const unsigned char *uc;
326         int rv = 0;
327         int t;
328
329         uc = json_skip_space(*ucp, ue);
330         if (uc == ue)
331                 goto out;
332
333         // Avoid recursion
334         if (lvl > 20)
335                 return 0;
336 #if JSON_COUNT
337         /* bail quickly if not counting */
338         if (lvl > 1 && (st[JSON_OBJECT] || st[JSON_ARRAYN]))
339                 return 1;
340 #endif
341
342         DPRINTF("Parse general: ", uc, *ucp);
343         switch (*uc++) {
344         case '"':
345                 rv = json_parse_string(&uc, ue);
346                 t = JSON_STRING;
347                 break;
348         case '[':
349                 rv = json_parse_array(&uc, ue, st, lvl + 1);
350                 t = JSON_ARRAY;
351                 break;
352         case '{': /* '}' */
353                 rv = json_parse_object(&uc, ue, st, lvl + 1);
354                 t = JSON_OBJECT;
355                 break;
356         case 't':
357                 rv = json_parse_const(&uc, ue, "true", sizeof("true"));
358                 t = JSON_CONSTANT;
359                 break;
360         case 'f':
361                 rv = json_parse_const(&uc, ue, "false", sizeof("false"));
362                 t = JSON_CONSTANT;
363                 break;
364         case 'n':
365                 rv = json_parse_const(&uc, ue, "null", sizeof("null"));
366                 t = JSON_CONSTANT;
367                 break;
368         default:
369                 --uc;
370                 rv = json_parse_number(&uc, ue);
371                 t = JSON_NUMBER;
372                 break;
373         }
374         if (rv)
375                 st[t]++;
376         uc = json_skip_space(uc, ue);
377 out:
378         *ucp = uc;
379         DPRINTF("End general: ", uc, *ucp);
380         if (lvl == 0)
381                 return rv && (st[JSON_ARRAYN] || st[JSON_OBJECT]);
382         return rv;
383 }
384
385 #ifndef TEST
386 int
387 file_is_json(struct magic_set *ms, const struct buffer *b)
388 {
389         const unsigned char *uc = CAST(const unsigned char *, b->fbuf);
390         const unsigned char *ue = uc + b->flen;
391         size_t st[JSON_MAX];
392         int mime = ms->flags & MAGIC_MIME;
393
394
395         if ((ms->flags & (MAGIC_APPLE|MAGIC_EXTENSION)) != 0)
396                 return 0;
397
398         memset(st, 0, sizeof(st));
399
400         if (!json_parse(&uc, ue, st, 0))
401                 return 0;
402
403         if (mime == MAGIC_MIME_ENCODING)
404                 return 1;
405         if (mime) {
406                 if (file_printf(ms, "application/json") == -1)
407                         return -1;
408                 return 1;
409         }
410         if (file_printf(ms, "JSON data") == -1)
411                 return -1;
412 #if JSON_COUNT
413 #define P(n) st[n], st[n] > 1 ? "s" : ""
414         if (file_printf(ms, " (%" SIZE_T_FORMAT "u object%s, %" SIZE_T_FORMAT
415             "u array%s, %" SIZE_T_FORMAT "u string%s, %" SIZE_T_FORMAT
416             "u constant%s, %" SIZE_T_FORMAT "u number%s, %" SIZE_T_FORMAT
417             "u >1array%s)",
418             P(JSON_OBJECT), P(JSON_ARRAY), P(JSON_STRING), P(JSON_CONSTANT),
419             P(JSON_NUMBER), P(JSON_ARRAYN))
420             == -1)
421                 return -1;
422 #endif
423         return 1;
424 }
425
426 #else
427
428 #include <sys/types.h>
429 #include <sys/stat.h>
430 #include <stdio.h>
431 #include <fcntl.h>
432 #include <unistd.h>
433 #include <stdlib.h>
434 #include <stdint.h>
435 #include <err.h>
436
437 int
438 main(int argc, char *argv[])
439 {
440         int fd, rv;
441         struct stat st;
442         unsigned char *p;
443         size_t stats[JSON_MAX];
444
445         if ((fd = open(argv[1], O_RDONLY)) == -1)
446                 err(EXIT_FAILURE, "Can't open `%s'", argv[1]);
447
448         if (fstat(fd, &st) == -1)
449                 err(EXIT_FAILURE, "Can't stat `%s'", argv[1]);
450
451         if ((p = malloc(st.st_size)) == NULL)
452                 err(EXIT_FAILURE, "Can't allocate %jd bytes",
453                     (intmax_t)st.st_size);
454         if (read(fd, p, st.st_size) != st.st_size)
455                 err(EXIT_FAILURE, "Can't read %jd bytes",
456                     (intmax_t)st.st_size);
457         memset(stats, 0, sizeof(stats));
458         printf("is json %d\n", json_parse((const unsigned char **)&p,
459             p + st.st_size, stats, 0));
460         return 0;
461 }
462 #endif