ksgi

Форк
0
/
kcgijson.c 
482 строки · 9.8 Кб
1
/*	$Id$ */
2
/*
3
 * Copyright (c) 2012, 2014, 2015, 2017, 2020 Kristaps Dzonsons <kristaps@bsd.lv>
4
 *
5
 * Permission to use, copy, modify, and distribute this software for any
6
 * purpose with or without fee is hereby granted, provided that the above
7
 * copyright notice and this permission notice appear in all copies.
8
 *
9
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
 */
17
#include "config.h"
18

19
#include <inttypes.h>
20
#include <math.h>
21
#include <stdarg.h>
22
#include <stdint.h>
23
#include <stdio.h>
24
#include <stdlib.h>
25
#include <string.h>
26

27
#include "kcgi.h"
28
#include "kcgijson.h"
29

30
enum kcgi_err
31
kjson_open(struct kjsonreq *r, struct kreq *req)
32
{
33

34
	memset(r, 0, sizeof(struct kjsonreq));
35
	if ((r->arg = kcgi_writer_get(req, 0)) == NULL)
36
		return KCGI_ENOMEM;
37

38
	r->stack[0].type = KJSON_ROOT;
39
	return KCGI_OK;
40
}
41

42
enum kcgi_err
43
kjson_close(struct kjsonreq *r)
44
{
45
	enum kcgi_err	 er = KCGI_OK;
46

47
	while (r->stackpos) {
48
		switch (r->stack[r->stackpos].type) {
49
		case KJSON_ARRAY:
50
			er = kcgi_writer_putc(r->arg, ']');
51
			break;
52
		case KJSON_STRING:
53
			er = kcgi_writer_putc(r->arg, '"');
54
			break;
55
		case KJSON_OBJECT:
56
			er = kcgi_writer_putc(r->arg, '}');
57
			break;
58
		default:
59
			abort();
60
		}
61
		if (er != KCGI_OK)
62
			break;
63
		r->stackpos--;
64
	}
65

66
	kcgi_writer_free(r->arg);
67
	r->arg = NULL;
68
	return er;
69
}
70

71
/*
72
 * Put a quoted JSON string into the output stream.
73
 * See RFC 7159, sec 7.
74
 */
75
static enum kcgi_err
76
kjson_write(struct kjsonreq *r, const char *cp, size_t sz, int quot)
77
{
78
	enum kcgi_err	e;
79
	char		enc[7];
80
	unsigned char	c;
81
	size_t		i;
82

83
	if (quot && (e = kcgi_writer_putc(r->arg, '"')) != KCGI_OK)
84
		return e;
85

86
	for (i = 0; i < sz; i++) {
87
		/* Make sure we're looking at the unsigned value. */
88
		c = cp[i];
89
		/* Encode control characters. */
90
		if (c <= 0x1f) {
91
			snprintf(enc, sizeof(enc), "\\u%.4X", c);
92
			e = kcgi_writer_puts(r->arg, enc);
93
			if (e != KCGI_OK)
94
				return e;
95
			continue;
96
		}
97
		/* Quote, solidus, reverse solidus. */
98
		switch (c) {
99
		case '"':
100
		case '\\':
101
		case '/':
102
			e = kcgi_writer_putc(r->arg, '\\');
103
			if (e != KCGI_OK)
104
				return e;
105
			break;
106
		default:
107
			break;
108
		}
109
		if ((e = kcgi_writer_putc(r->arg, cp[i])) != KCGI_OK)
110
			return e;
111
	}
112

113
	if (quot && (e = kcgi_writer_putc(r->arg, '"')) != KCGI_OK)
114
		return e;
115

116
	return KCGI_OK;
117
}
118

119
static enum kcgi_err
120
kjson_puts(struct kjsonreq *r, const char *cp)
121
{
122

123
	if (cp == NULL)
124
		return KCGI_OK;
125
	return kjson_write(r, cp, strlen(cp), 1);
126
}
127

128
/*
129
 * Check the parent of a new JSON element, which may or may not have a
130
 * key, which implies key-value instead of a value-only element.
131
 * An example of a key-value would be the inside of {foo: "bar"}, while
132
 * the value-only would be the surrounding object.
133
 * In short, we cannot have a key if our parent is the root
134
 * (KJSON_ROOT) or an array (KJSON_ARRAY), both of which accept
135
 * only values, e.g., [1, 2, 3] but not [a: b, c: d].
136
 * We must have a key, however, if our parent is an object, that is,
137
 * don't accept {4} and so on.
138
 * Moreover, we should never be in a string context.
139
 * Returns the value of kcgi_writer_puts() or KCGI_WRITER on check
140
 * violation.
141
 */
142
static enum kcgi_err
143
kjson_check(struct kjsonreq *r, const char *key)
144
{
145
	enum kcgi_err	 er;
146

147
	switch (r->stack[r->stackpos].type) {
148
	case KJSON_STRING:
149
		return KCGI_WRITER;
150
	case KJSON_OBJECT:
151
		if (key == NULL)
152
			return KCGI_WRITER;
153
		break;
154
	case KJSON_ROOT:
155
	case KJSON_ARRAY:
156
		if (key != NULL)
157
			return KCGI_WRITER;
158
		break;
159
	}
160

161
	if (r->stack[r->stackpos].elements++ > 0) 
162
		if ((er = kcgi_writer_puts(r->arg, ", ")) != KCGI_OK)
163
			return er;
164

165
	if (key != NULL) {
166
		if ((er = kjson_puts(r, key)) != KCGI_OK)
167
			return er;
168
		if ((er = kcgi_writer_puts(r->arg, ": ")) != KCGI_OK)
169
			return er;
170
	}
171
	return KCGI_OK;
172
}
173

174
/* 
175
 * Only allow normal, subnormal (real small), or zero numbers.
176
 * The rest don't have a consistent JSON encoding.
177
 */
178
static int
179
kjson_check_fp(double val)
180
{
181

182
	switch (fpclassify(val)) {
183
	case FP_ZERO:
184
	case FP_SUBNORMAL:
185
	case FP_NORMAL:
186
		break;
187
	default:
188
		return 0;
189
	}
190

191
	return 1;
192
}
193

194
/*
195
 * Emit a key-pair "key" with an unquoted value "val".
196
 * This should only be used for trusted values: numbers, boolean, null,
197
 * and the like.
198
 * It should never be passed anything untrusted.
199
 * Returns from kcgi_writer_puts and kjson_check.
200
 */
201
static enum kcgi_err
202
kjson_puttrustedp(struct kjsonreq *r, const char *key, const char *val)
203
{
204
	enum kcgi_err	 er;
205

206
	if ((er = kjson_check(r, key)) != KCGI_OK)
207
		return er;
208
	return kcgi_writer_puts(r->arg, val);
209
}
210

211
enum kcgi_err
212
kjson_putstring(struct kjsonreq *r, const char *val)
213
{
214

215
	return kjson_putstringp(r, NULL, val);
216
}
217

218
enum kcgi_err
219
kjson_putstringp(struct kjsonreq *r, const char *key, const char *val)
220
{
221
	enum kcgi_err	 er;
222

223
	if (val == NULL)
224
		return KCGI_OK;
225
	if ((er = kjson_check(r, key)) != KCGI_OK)
226
		return er;
227
	return kjson_puts(r, val);
228
}
229

230
enum kcgi_err
231
kjson_putdouble(struct kjsonreq *r, double val)
232
{
233

234
	return kjson_putdoublep(r, NULL, val);
235
}
236

237
enum kcgi_err
238
kjson_putdoublep(struct kjsonreq *r, const char *key, double val)
239
{
240
	char		 buf[256];
241

242
	if (!kjson_check_fp(val))
243
		return kjson_puttrustedp(r, key, "null");
244

245
	(void)snprintf(buf, sizeof(buf), "%g", val);
246
	return kjson_puttrustedp(r, key, buf);
247
}
248

249
enum kcgi_err
250
kjson_putint(struct kjsonreq *r, int64_t val)
251
{
252

253
	return kjson_putintp(r, NULL, val);
254
}
255

256
enum kcgi_err
257
kjson_putintstr(struct kjsonreq *r, int64_t val)
258
{
259

260
	return kjson_putintstrp(r, NULL, val);
261
}
262

263
enum kcgi_err
264
kjson_putnullp(struct kjsonreq *r, const char *key)
265
{
266

267
	return kjson_puttrustedp(r, key, "null");
268
}
269

270
enum kcgi_err
271
kjson_putboolp(struct kjsonreq *r, const char *key, int val)
272
{
273

274
	return kjson_puttrustedp(r, key, val ? "true" : "false");
275
}
276

277
enum kcgi_err
278
kjson_putnull(struct kjsonreq *r)
279
{
280

281
	return kjson_putnullp(r, NULL);
282
}
283

284
enum kcgi_err
285
kjson_putbool(struct kjsonreq *r, int val)
286
{
287

288
	return kjson_putboolp(r, NULL, val);
289
}
290

291
enum kcgi_err
292
kjson_putintstrp(struct kjsonreq *r, const char *key, int64_t val)
293
{
294
	char	buf[22];
295

296
	(void)snprintf(buf, sizeof(buf), "%" PRId64, val);
297
	return kjson_putstringp(r, key, buf);
298
}
299

300

301
enum kcgi_err
302
kjson_putintp(struct kjsonreq *r, const char *key, int64_t val)
303
{
304
	char	buf[22];
305

306
	(void)snprintf(buf, sizeof(buf), "%" PRId64, val);
307
	return kjson_puttrustedp(r, key, buf);
308
}
309

310
enum kcgi_err
311
kjson_array_open(struct kjsonreq *r)
312
{
313

314
	return kjson_arrayp_open(r, NULL);
315
}
316

317
enum kcgi_err
318
kjson_arrayp_open(struct kjsonreq *r, const char *key)
319
{
320
	enum kcgi_err	 er;
321

322
	if (r->stackpos >= KJSON_STACK_MAX - 1) {
323
		kutil_warnx(NULL, NULL, 
324
			"maximum json stack size exceeded");
325
		return KCGI_ENOMEM;
326
	}
327

328
	if ((er = kjson_check(r, key)) != KCGI_OK)
329
		return er;
330
	if ((er = kcgi_writer_putc(r->arg, '[')) != KCGI_OK)
331
		return er;
332

333
	r->stack[++r->stackpos].elements = 0;
334
	r->stack[r->stackpos].type = KJSON_ARRAY;
335
	return KCGI_OK;
336
}
337

338
enum kcgi_err
339
kjson_array_close(struct kjsonreq *r)
340
{
341
	enum kcgi_err	 er;
342

343
	if (r->stackpos == 0)
344
		return KCGI_WRITER;
345
	if (r->stack[r->stackpos].type != KJSON_ARRAY)
346
		return KCGI_WRITER;
347
	if ((er = kcgi_writer_putc(r->arg, ']')) != KCGI_OK)
348
		return er;
349
	r->stackpos--;
350
	return KCGI_OK;
351
}
352

353
enum kcgi_err
354
kjson_string_write(const char *p, size_t sz, void *arg)
355
{
356
	struct kjsonreq	*r = arg;
357

358
	if (r->stack[r->stackpos].type != KJSON_STRING)
359
		return KCGI_WRITER;
360

361
	if (p == NULL || sz == 0)
362
		return KCGI_OK;
363

364
	return kjson_write(r, p, sz, 0);
365
}
366

367
enum kcgi_err
368
kjson_string_puts(struct kjsonreq *r, const char *cp)
369
{
370

371
	if (cp == NULL)
372
		return KCGI_OK;
373

374
	return kjson_string_write(cp, strlen(cp), r);
375
}
376

377
enum kcgi_err
378
kjson_string_putdouble(struct kjsonreq *r, double val)
379
{
380
	char	buf[256];
381

382
	if (!kjson_check_fp(val))
383
		return kjson_string_write("null", 4, r);
384

385
	(void)snprintf(buf, sizeof(buf), "%g", val);
386
	return kjson_string_write(buf, strlen(buf), r);
387
}
388

389
enum kcgi_err
390
kjson_string_putint(struct kjsonreq *r, int64_t val)
391
{
392
	char	buf[22];
393

394
	(void)snprintf(buf, sizeof(buf), "%" PRId64, val);
395
	return kjson_string_write(buf, strlen(buf), r);
396
}
397

398
enum kcgi_err
399
kjson_string_open(struct kjsonreq *r)
400
{
401

402
	return kjson_stringp_open(r, NULL);
403
}
404

405
enum kcgi_err
406
kjson_stringp_open(struct kjsonreq *r, const char *key)
407
{
408
	enum kcgi_err	 er;
409

410
	if (r->stackpos >= KJSON_STACK_MAX - 1) {
411
		kutil_warnx(NULL, NULL, 
412
			"maximum json stack size exceeded");
413
		return KCGI_ENOMEM;
414
	}
415

416
	if ((er = kjson_check(r, key)) != KCGI_OK)
417
		return er;
418
	if ((er = kcgi_writer_putc(r->arg, '"')) != KCGI_OK)
419
		return er;
420

421
	r->stack[++r->stackpos].elements = 0;
422
	r->stack[r->stackpos].type = KJSON_STRING;
423
	return KCGI_OK;
424
}
425

426
enum kcgi_err
427
kjson_string_close(struct kjsonreq *r)
428
{
429
	enum kcgi_err	 er;
430

431
	if (r->stackpos == 0 ||
432
	    r->stack[r->stackpos].type != KJSON_STRING)
433
		return KCGI_WRITER;
434
	if ((er = kcgi_writer_putc(r->arg, '"')) != KCGI_OK)
435
		return(er);
436
	r->stackpos--;
437
	return KCGI_OK;
438
}
439

440
enum kcgi_err
441
kjson_obj_open(struct kjsonreq *r)
442
{
443

444
	return kjson_objp_open(r, NULL);
445
}
446

447
enum kcgi_err
448
kjson_objp_open(struct kjsonreq *r, const char *key)
449
{
450
	enum kcgi_err	 er;
451

452
	if (r->stackpos >= KJSON_STACK_MAX - 1) {
453
		kutil_warnx(NULL, NULL, 
454
			"maximum json stack size exceeded");
455
		return KCGI_ENOMEM;
456
	}
457

458
	if ((er = kjson_check(r, key)) != KCGI_OK)
459
		return er;
460
	if ((er = kcgi_writer_putc(r->arg, '{')) != KCGI_OK)
461
		return er;
462

463
	r->stack[++r->stackpos].elements = 0;
464
	r->stack[r->stackpos].type = KJSON_OBJECT;
465
	return KCGI_OK;
466
}
467

468
enum kcgi_err
469
kjson_obj_close(struct kjsonreq *r)
470
{
471
	enum kcgi_err	 er;
472

473
	if (r->stackpos == 0 ||
474
	    r->stack[r->stackpos].type != KJSON_OBJECT)
475
		return KCGI_WRITER;
476

477
	if ((er = kcgi_writer_putc(r->arg, '}')) != KCGI_OK)
478
		return er;
479

480
	r->stackpos--;
481
	return KCGI_OK;
482
}
483

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.