1 /*
2  * Copyright (c) 2017 by Internet Systems Consortium, Inc. ("ISC")
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
14  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  *
16  *   Internet Systems Consortium, Inc.
17  *   950 Charter Street
18  *   Redwood City, CA 94063
19  *   <info@isc.org>
20  *   https://www.isc.org/
21  *
22  */
23 
24 /* From Kea src/lib/cc/data.cc fromJSON() */
25 
26 #include "keama.h"
27 
28 #include <stdlib.h>
29 #include <string.h>
30 
31 struct element *
json_parse(struct parse * cfile)32 json_parse(struct parse *cfile)
33 {
34 	struct element *elem;
35 	const char *val;
36 	enum dhcp_token token;
37 
38 	elem = create();
39 	stackPush(cfile, elem);
40 	cfile->stack[0] = elem;
41 	cfile->stack_top = 0;
42 
43 	token = next_token(&val, NULL, cfile);
44 	switch (token) {
45 	case NUMBER:
46 		elem = createInt(atoll(val));
47 		TAILQ_CONCAT(&elem->comments, &cfile->comments);
48 		break;
49 	case STRING:
50 		elem = createString(makeString(-1, val));
51 		TAILQ_CONCAT(&elem->comments, &cfile->comments);
52 		break;
53 	case NAME:
54 		if (strcmp(val, "null") == 0)
55 			elem = createNull();
56 		else if (strcmp(val, "true") == 0)
57 			elem = createBool(ISC_TRUE);
58 		else if (strcmp(val, "false") == 0) {
59 			elem = createBool(ISC_FALSE);
60 			elem->skip = ISC_TRUE;
61 		} else
62 			parse_error(cfile, "unknown name %s", val);
63 		TAILQ_CONCAT(&elem->comments, &cfile->comments);
64 		break;
65 	case LBRACKET:
66 		elem = json_list_parse(cfile);
67 		break;
68 	case LBRACE:
69 		elem = json_map_parse(cfile);
70 		break;
71 	case END_OF_FILE:
72 		parse_error(cfile, "unexpected end of file");
73 	default:
74 		parse_error(cfile, "unexpected %s", val);
75 	}
76 	return elem;
77 }
78 
79 struct element *
json_list_parse(struct parse * cfile)80 json_list_parse(struct parse *cfile)
81 {
82 	struct element *list;
83 	struct element *item;
84 	const char *val;
85 	enum dhcp_token token;
86 	isc_boolean_t done = ISC_FALSE;
87 
88 	list = createList();
89 	TAILQ_CONCAT(&list->comments, &cfile->comments);
90 	stackPush(cfile, list);
91 	do {
92 		token = peek_token(&val, NULL, cfile);
93 		switch (token) {
94 		case RBRACKET:
95 			done = ISC_TRUE;
96 			break;
97 		case END_OF_FILE:
98 			parse_error(cfile, "unexpected end of file");
99 		case COMMA:
100 			skip_token(&val, NULL, cfile);
101 			if (listSize(list) == 0)
102 				parse_error(cfile, "unexpected ','");
103 			item = json_parse(cfile);
104 			listPush(list, item);
105 			break;
106 		default:
107 			if (listSize(list) > 0)
108 				parse_error(cfile, "expected ','");
109 			item = json_parse(cfile);
110 			listPush(list, item);
111 			break;
112 		}
113 	} while (!done);
114 	skip_token(&val, NULL, cfile);
115 	cfile->stack_top--;
116 	return list;
117 }
118 
119 struct element *
json_map_parse(struct parse * cfile)120 json_map_parse(struct parse *cfile)
121 {
122 	struct element *map;
123 	struct element *item;
124 	const char *val;
125 	const char *key;
126 	enum dhcp_token token;
127 	isc_boolean_t done = ISC_FALSE;
128 
129 	map = createMap();
130 	TAILQ_CONCAT(&map->comments, &cfile->comments);
131 	stackPush(cfile, map);
132 	do {
133 		token = peek_token(&val, NULL, cfile);
134 		switch (token) {
135 		case RBRACE:
136 			done = ISC_TRUE;
137 			break;
138 		case END_OF_FILE:
139 			parse_error(cfile, "unexpected end of file");
140 		case COMMA:
141 			skip_token(&val, NULL, cfile);
142 			if (mapSize(map) == 0)
143 				parse_error(cfile, "unexpected ','");
144 			token = next_token(&val, NULL, cfile);
145 			if (token != STRING)
146 				parse_error(cfile, "unexpected %s, "
147 					    "expected \"key\":value", val);
148 			key = strdup(val);
149 			token = next_token(&val, NULL, cfile);
150 			if (token != COLON)
151 				parse_error(cfile, "unexpected %s, "
152 					    "expected ':'", val);
153 			item = json_parse(cfile);
154 			mapSet(map, item, key);
155 			break;
156 		case STRING:
157 			skip_token(&val, NULL, cfile);
158 			if (mapSize(map) > 0)
159 				parse_error(cfile, "unexpected \"%s\", "
160 					    "expected ','", val);
161 			key = strdup(val);
162 			token = next_token(&val, NULL, cfile);
163 			if (token != COLON)
164 				parse_error(cfile, "unexpected %s, "
165 					    "expected ':'", val);
166 			item = json_parse(cfile);
167 			mapSet(map, item, key);
168 			break;
169 		default:
170 			if (mapSize(map) == 0)
171 				parse_error(cfile, "unexpected %s, "
172 					    "expected \"key\":value or '}'",
173 					    val);
174 			else
175 				parse_error(cfile, "unexpected %s, "
176 					    "expected ',' or '}'", val);
177 		}
178 	} while (!done);
179 	skip_token(&val, NULL, cfile);
180 	cfile->stack_top--;
181 	return map;
182 }
183