ksgi
/
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
30enum kcgi_err31kjson_open(struct kjsonreq *r, struct kreq *req)32{
33
34memset(r, 0, sizeof(struct kjsonreq));35if ((r->arg = kcgi_writer_get(req, 0)) == NULL)36return KCGI_ENOMEM;37
38r->stack[0].type = KJSON_ROOT;39return KCGI_OK;40}
41
42enum kcgi_err43kjson_close(struct kjsonreq *r)44{
45enum kcgi_err er = KCGI_OK;46
47while (r->stackpos) {48switch (r->stack[r->stackpos].type) {49case KJSON_ARRAY:50er = kcgi_writer_putc(r->arg, ']');51break;52case KJSON_STRING:53er = kcgi_writer_putc(r->arg, '"');54break;55case KJSON_OBJECT:56er = kcgi_writer_putc(r->arg, '}');57break;58default:59abort();60}61if (er != KCGI_OK)62break;63r->stackpos--;64}65
66kcgi_writer_free(r->arg);67r->arg = NULL;68return er;69}
70
71/*
72* Put a quoted JSON string into the output stream.
73* See RFC 7159, sec 7.
74*/
75static enum kcgi_err76kjson_write(struct kjsonreq *r, const char *cp, size_t sz, int quot)77{
78enum kcgi_err e;79char enc[7];80unsigned char c;81size_t i;82
83if (quot && (e = kcgi_writer_putc(r->arg, '"')) != KCGI_OK)84return e;85
86for (i = 0; i < sz; i++) {87/* Make sure we're looking at the unsigned value. */88c = cp[i];89/* Encode control characters. */90if (c <= 0x1f) {91snprintf(enc, sizeof(enc), "\\u%.4X", c);92e = kcgi_writer_puts(r->arg, enc);93if (e != KCGI_OK)94return e;95continue;96}97/* Quote, solidus, reverse solidus. */98switch (c) {99case '"':100case '\\':101case '/':102e = kcgi_writer_putc(r->arg, '\\');103if (e != KCGI_OK)104return e;105break;106default:107break;108}109if ((e = kcgi_writer_putc(r->arg, cp[i])) != KCGI_OK)110return e;111}112
113if (quot && (e = kcgi_writer_putc(r->arg, '"')) != KCGI_OK)114return e;115
116return KCGI_OK;117}
118
119static enum kcgi_err120kjson_puts(struct kjsonreq *r, const char *cp)121{
122
123if (cp == NULL)124return KCGI_OK;125return 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*/
142static enum kcgi_err143kjson_check(struct kjsonreq *r, const char *key)144{
145enum kcgi_err er;146
147switch (r->stack[r->stackpos].type) {148case KJSON_STRING:149return KCGI_WRITER;150case KJSON_OBJECT:151if (key == NULL)152return KCGI_WRITER;153break;154case KJSON_ROOT:155case KJSON_ARRAY:156if (key != NULL)157return KCGI_WRITER;158break;159}160
161if (r->stack[r->stackpos].elements++ > 0)162if ((er = kcgi_writer_puts(r->arg, ", ")) != KCGI_OK)163return er;164
165if (key != NULL) {166if ((er = kjson_puts(r, key)) != KCGI_OK)167return er;168if ((er = kcgi_writer_puts(r->arg, ": ")) != KCGI_OK)169return er;170}171return 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*/
178static int179kjson_check_fp(double val)180{
181
182switch (fpclassify(val)) {183case FP_ZERO:184case FP_SUBNORMAL:185case FP_NORMAL:186break;187default:188return 0;189}190
191return 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*/
201static enum kcgi_err202kjson_puttrustedp(struct kjsonreq *r, const char *key, const char *val)203{
204enum kcgi_err er;205
206if ((er = kjson_check(r, key)) != KCGI_OK)207return er;208return kcgi_writer_puts(r->arg, val);209}
210
211enum kcgi_err212kjson_putstring(struct kjsonreq *r, const char *val)213{
214
215return kjson_putstringp(r, NULL, val);216}
217
218enum kcgi_err219kjson_putstringp(struct kjsonreq *r, const char *key, const char *val)220{
221enum kcgi_err er;222
223if (val == NULL)224return KCGI_OK;225if ((er = kjson_check(r, key)) != KCGI_OK)226return er;227return kjson_puts(r, val);228}
229
230enum kcgi_err231kjson_putdouble(struct kjsonreq *r, double val)232{
233
234return kjson_putdoublep(r, NULL, val);235}
236
237enum kcgi_err238kjson_putdoublep(struct kjsonreq *r, const char *key, double val)239{
240char buf[256];241
242if (!kjson_check_fp(val))243return kjson_puttrustedp(r, key, "null");244
245(void)snprintf(buf, sizeof(buf), "%g", val);246return kjson_puttrustedp(r, key, buf);247}
248
249enum kcgi_err250kjson_putint(struct kjsonreq *r, int64_t val)251{
252
253return kjson_putintp(r, NULL, val);254}
255
256enum kcgi_err257kjson_putintstr(struct kjsonreq *r, int64_t val)258{
259
260return kjson_putintstrp(r, NULL, val);261}
262
263enum kcgi_err264kjson_putnullp(struct kjsonreq *r, const char *key)265{
266
267return kjson_puttrustedp(r, key, "null");268}
269
270enum kcgi_err271kjson_putboolp(struct kjsonreq *r, const char *key, int val)272{
273
274return kjson_puttrustedp(r, key, val ? "true" : "false");275}
276
277enum kcgi_err278kjson_putnull(struct kjsonreq *r)279{
280
281return kjson_putnullp(r, NULL);282}
283
284enum kcgi_err285kjson_putbool(struct kjsonreq *r, int val)286{
287
288return kjson_putboolp(r, NULL, val);289}
290
291enum kcgi_err292kjson_putintstrp(struct kjsonreq *r, const char *key, int64_t val)293{
294char buf[22];295
296(void)snprintf(buf, sizeof(buf), "%" PRId64, val);297return kjson_putstringp(r, key, buf);298}
299
300
301enum kcgi_err302kjson_putintp(struct kjsonreq *r, const char *key, int64_t val)303{
304char buf[22];305
306(void)snprintf(buf, sizeof(buf), "%" PRId64, val);307return kjson_puttrustedp(r, key, buf);308}
309
310enum kcgi_err311kjson_array_open(struct kjsonreq *r)312{
313
314return kjson_arrayp_open(r, NULL);315}
316
317enum kcgi_err318kjson_arrayp_open(struct kjsonreq *r, const char *key)319{
320enum kcgi_err er;321
322if (r->stackpos >= KJSON_STACK_MAX - 1) {323kutil_warnx(NULL, NULL,324"maximum json stack size exceeded");325return KCGI_ENOMEM;326}327
328if ((er = kjson_check(r, key)) != KCGI_OK)329return er;330if ((er = kcgi_writer_putc(r->arg, '[')) != KCGI_OK)331return er;332
333r->stack[++r->stackpos].elements = 0;334r->stack[r->stackpos].type = KJSON_ARRAY;335return KCGI_OK;336}
337
338enum kcgi_err339kjson_array_close(struct kjsonreq *r)340{
341enum kcgi_err er;342
343if (r->stackpos == 0)344return KCGI_WRITER;345if (r->stack[r->stackpos].type != KJSON_ARRAY)346return KCGI_WRITER;347if ((er = kcgi_writer_putc(r->arg, ']')) != KCGI_OK)348return er;349r->stackpos--;350return KCGI_OK;351}
352
353enum kcgi_err354kjson_string_write(const char *p, size_t sz, void *arg)355{
356struct kjsonreq *r = arg;357
358if (r->stack[r->stackpos].type != KJSON_STRING)359return KCGI_WRITER;360
361if (p == NULL || sz == 0)362return KCGI_OK;363
364return kjson_write(r, p, sz, 0);365}
366
367enum kcgi_err368kjson_string_puts(struct kjsonreq *r, const char *cp)369{
370
371if (cp == NULL)372return KCGI_OK;373
374return kjson_string_write(cp, strlen(cp), r);375}
376
377enum kcgi_err378kjson_string_putdouble(struct kjsonreq *r, double val)379{
380char buf[256];381
382if (!kjson_check_fp(val))383return kjson_string_write("null", 4, r);384
385(void)snprintf(buf, sizeof(buf), "%g", val);386return kjson_string_write(buf, strlen(buf), r);387}
388
389enum kcgi_err390kjson_string_putint(struct kjsonreq *r, int64_t val)391{
392char buf[22];393
394(void)snprintf(buf, sizeof(buf), "%" PRId64, val);395return kjson_string_write(buf, strlen(buf), r);396}
397
398enum kcgi_err399kjson_string_open(struct kjsonreq *r)400{
401
402return kjson_stringp_open(r, NULL);403}
404
405enum kcgi_err406kjson_stringp_open(struct kjsonreq *r, const char *key)407{
408enum kcgi_err er;409
410if (r->stackpos >= KJSON_STACK_MAX - 1) {411kutil_warnx(NULL, NULL,412"maximum json stack size exceeded");413return KCGI_ENOMEM;414}415
416if ((er = kjson_check(r, key)) != KCGI_OK)417return er;418if ((er = kcgi_writer_putc(r->arg, '"')) != KCGI_OK)419return er;420
421r->stack[++r->stackpos].elements = 0;422r->stack[r->stackpos].type = KJSON_STRING;423return KCGI_OK;424}
425
426enum kcgi_err427kjson_string_close(struct kjsonreq *r)428{
429enum kcgi_err er;430
431if (r->stackpos == 0 ||432r->stack[r->stackpos].type != KJSON_STRING)433return KCGI_WRITER;434if ((er = kcgi_writer_putc(r->arg, '"')) != KCGI_OK)435return(er);436r->stackpos--;437return KCGI_OK;438}
439
440enum kcgi_err441kjson_obj_open(struct kjsonreq *r)442{
443
444return kjson_objp_open(r, NULL);445}
446
447enum kcgi_err448kjson_objp_open(struct kjsonreq *r, const char *key)449{
450enum kcgi_err er;451
452if (r->stackpos >= KJSON_STACK_MAX - 1) {453kutil_warnx(NULL, NULL,454"maximum json stack size exceeded");455return KCGI_ENOMEM;456}457
458if ((er = kjson_check(r, key)) != KCGI_OK)459return er;460if ((er = kcgi_writer_putc(r->arg, '{')) != KCGI_OK)461return er;462
463r->stack[++r->stackpos].elements = 0;464r->stack[r->stackpos].type = KJSON_OBJECT;465return KCGI_OK;466}
467
468enum kcgi_err469kjson_obj_close(struct kjsonreq *r)470{
471enum kcgi_err er;472
473if (r->stackpos == 0 ||474r->stack[r->stackpos].type != KJSON_OBJECT)475return KCGI_WRITER;476
477if ((er = kcgi_writer_putc(r->arg, '}')) != KCGI_OK)478return er;479
480r->stackpos--;481return KCGI_OK;482}
483