diff options
Diffstat (limited to 'modules/md/md_json.c')
-rw-r--r-- | modules/md/md_json.c | 376 |
1 files changed, 325 insertions, 51 deletions
diff --git a/modules/md/md_json.c b/modules/md/md_json.c index f73ab14..e0f977e 100644 --- a/modules/md/md_json.c +++ b/modules/md/md_json.c @@ -18,10 +18,12 @@ #include <apr_lib.h> #include <apr_strings.h> #include <apr_buckets.h> +#include <apr_date.h> #include "md_json.h" #include "md_log.h" #include "md_http.h" +#include "md_time.h" #include "md_util.h" /* jansson thinks everyone compiles with the platform's cc in its fullest capabilities @@ -106,12 +108,12 @@ void md_json_destroy(md_json_t *json) } } -md_json_t *md_json_copy(apr_pool_t *pool, md_json_t *json) +md_json_t *md_json_copy(apr_pool_t *pool, const md_json_t *json) { return json_create(pool, json_copy(json->j)); } -md_json_t *md_json_clone(apr_pool_t *pool, md_json_t *json) +md_json_t *md_json_clone(apr_pool_t *pool, const md_json_t *json) { return json_create(pool, json_deep_copy(json->j)); } @@ -120,7 +122,7 @@ md_json_t *md_json_clone(apr_pool_t *pool, md_json_t *json) /* selectors */ -static json_t *jselect(md_json_t *json, va_list ap) +static json_t *jselect(const md_json_t *json, va_list ap) { json_t *j; const char *key; @@ -168,6 +170,31 @@ static apr_status_t jselect_add(json_t *val, md_json_t *json, va_list ap) j = jselect_parent(&key, 1, json, ap); if (!j || !json_is_object(j)) { + return APR_EINVAL; + } + + aj = json_object_get(j, key); + if (!aj) { + aj = json_array(); + json_object_set_new(j, key, aj); + } + + if (!json_is_array(aj)) { + return APR_EINVAL; + } + + json_array_append(aj, val); + return APR_SUCCESS; +} + +static apr_status_t jselect_insert(json_t *val, size_t index, md_json_t *json, va_list ap) +{ + const char *key; + json_t *j, *aj; + + j = jselect_parent(&key, 1, json, ap); + + if (!j || !json_is_object(j)) { json_decref(val); return APR_EINVAL; } @@ -183,7 +210,12 @@ static apr_status_t jselect_add(json_t *val, md_json_t *json, va_list ap) return APR_EINVAL; } - json_array_append(aj, val); + if (json_array_size(aj) <= index) { + json_array_append(aj, val); + } + else { + json_array_insert(aj, index, val); + } return APR_SUCCESS; } @@ -195,13 +227,11 @@ static apr_status_t jselect_set(json_t *val, md_json_t *json, va_list ap) j = jselect_parent(&key, 1, json, ap); if (!j) { - json_decref(val); return APR_EINVAL; } if (key) { if (!json_is_object(j)) { - json_decref(val); return APR_EINVAL; } json_object_set(j, key, val); @@ -246,7 +276,7 @@ static apr_status_t jselect_set_new(json_t *val, md_json_t *json, va_list ap) return APR_SUCCESS; } -int md_json_has_key(md_json_t *json, ...) +int md_json_has_key(const md_json_t *json, ...) { json_t *j; va_list ap; @@ -259,9 +289,45 @@ int md_json_has_key(md_json_t *json, ...) } /**************************************************************************************************/ +/* type things */ + +int md_json_is(const md_json_type_t jtype, md_json_t *json, ...) +{ + json_t *j; + va_list ap; + + va_start(ap, json); + j = jselect(json, ap); + va_end(ap); + switch (jtype) { + case MD_JSON_TYPE_OBJECT: return (j && json_is_object(j)); + case MD_JSON_TYPE_ARRAY: return (j && json_is_array(j)); + case MD_JSON_TYPE_STRING: return (j && json_is_string(j)); + case MD_JSON_TYPE_REAL: return (j && json_is_real(j)); + case MD_JSON_TYPE_INT: return (j && json_is_integer(j)); + case MD_JSON_TYPE_BOOL: return (j && (json_is_true(j) || json_is_false(j))); + case MD_JSON_TYPE_NULL: return (j == NULL); + } + return 0; +} + +static const char *md_json_type_name(const md_json_t *json) +{ + json_t *j = json->j; + if (json_is_object(j)) return "object"; + if (json_is_array(j)) return "array"; + if (json_is_string(j)) return "string"; + if (json_is_real(j)) return "real"; + if (json_is_integer(j)) return "integer"; + if (json_is_true(j)) return "true"; + if (json_is_false(j)) return "false"; + return "unknown"; +} + +/**************************************************************************************************/ /* booleans */ -int md_json_getb(md_json_t *json, ...) +int md_json_getb(const md_json_t *json, ...) { json_t *j; va_list ap; @@ -287,7 +353,7 @@ apr_status_t md_json_setb(int value, md_json_t *json, ...) /**************************************************************************************************/ /* numbers */ -double md_json_getn(md_json_t *json, ...) +double md_json_getn(const md_json_t *json, ...) { json_t *j; va_list ap; @@ -312,7 +378,7 @@ apr_status_t md_json_setn(double value, md_json_t *json, ...) /**************************************************************************************************/ /* longs */ -long md_json_getl(md_json_t *json, ...) +long md_json_getl(const md_json_t *json, ...) { json_t *j; va_list ap; @@ -337,7 +403,7 @@ apr_status_t md_json_setl(long value, md_json_t *json, ...) /**************************************************************************************************/ /* strings */ -const char *md_json_gets(md_json_t *json, ...) +const char *md_json_gets(const md_json_t *json, ...) { json_t *j; va_list ap; @@ -349,7 +415,7 @@ const char *md_json_gets(md_json_t *json, ...) return (j && json_is_string(j))? json_string_value(j) : NULL; } -const char *md_json_dups(apr_pool_t *p, md_json_t *json, ...) +const char *md_json_dups(apr_pool_t *p, const md_json_t *json, ...) { json_t *j; va_list ap; @@ -373,6 +439,35 @@ apr_status_t md_json_sets(const char *value, md_json_t *json, ...) } /**************************************************************************************************/ +/* time */ + +apr_time_t md_json_get_time(const md_json_t *json, ...) +{ + json_t *j; + va_list ap; + + va_start(ap, json); + j = jselect(json, ap); + va_end(ap); + + if (!j || !json_is_string(j)) return 0; + return apr_date_parse_rfc(json_string_value(j)); +} + +apr_status_t md_json_set_time(apr_time_t value, md_json_t *json, ...) +{ + char ts[APR_RFC822_DATE_LEN]; + va_list ap; + apr_status_t rv; + + apr_rfc822_date(ts, value); + va_start(ap, json); + rv = jselect_set_new(json_string(ts), json, ap); + va_end(ap); + return rv; +} + +/**************************************************************************************************/ /* json itself */ md_json_t *md_json_getj(md_json_t *json, ...) @@ -394,7 +489,42 @@ md_json_t *md_json_getj(md_json_t *json, ...) return NULL; } -apr_status_t md_json_setj(md_json_t *value, md_json_t *json, ...) +md_json_t *md_json_dupj(apr_pool_t *p, const md_json_t *json, ...) +{ + json_t *j; + va_list ap; + + va_start(ap, json); + j = jselect(json, ap); + va_end(ap); + + if (j) { + json_incref(j); + return json_create(p, j); + } + return NULL; +} + +const md_json_t *md_json_getcj(const md_json_t *json, ...) +{ + json_t *j; + va_list ap; + + va_start(ap, json); + j = jselect(json, ap); + va_end(ap); + + if (j) { + if (j == json->j) { + return json; + } + json_incref(j); + return json_create(json->p, j); + } + return NULL; +} + +apr_status_t md_json_setj(const md_json_t *value, md_json_t *json, ...) { va_list ap; apr_status_t rv; @@ -422,7 +552,7 @@ apr_status_t md_json_setj(md_json_t *value, md_json_t *json, ...) return rv; } -apr_status_t md_json_addj(md_json_t *value, md_json_t *json, ...) +apr_status_t md_json_addj(const md_json_t *value, md_json_t *json, ...) { va_list ap; apr_status_t rv; @@ -433,6 +563,36 @@ apr_status_t md_json_addj(md_json_t *value, md_json_t *json, ...) return rv; } +apr_status_t md_json_insertj(md_json_t *value, size_t index, md_json_t *json, ...) +{ + va_list ap; + apr_status_t rv; + + va_start(ap, json); + rv = jselect_insert(value->j, index, json, ap); + va_end(ap); + return rv; +} + +apr_size_t md_json_limita(size_t max_elements, md_json_t *json, ...) +{ + json_t *j; + va_list ap; + apr_size_t n = 0; + + va_start(ap, json); + j = jselect(json, ap); + va_end(ap); + + if (j && json_is_array(j)) { + n = json_array_size(j); + while (n > max_elements) { + json_array_remove(j, n-1); + n = json_array_size(j); + } + } + return n; +} /**************************************************************************************************/ /* arrays / objects */ @@ -474,7 +634,7 @@ apr_status_t md_json_del(md_json_t *json, ...) /**************************************************************************************************/ /* object strings */ -apr_status_t md_json_gets_dict(apr_table_t *dict, md_json_t *json, ...) +apr_status_t md_json_gets_dict(apr_table_t *dict, const md_json_t *json, ...) { json_t *j; va_list ap; @@ -557,7 +717,7 @@ apr_status_t md_json_clone_to(void *value, md_json_t *json, apr_pool_t *p, void return md_json_setj(md_json_clone(p, value), json, NULL); } -apr_status_t md_json_clone_from(void **pvalue, md_json_t *json, apr_pool_t *p, void *baton) +apr_status_t md_json_clone_from(void **pvalue, const md_json_t *json, apr_pool_t *p, void *baton) { (void)baton; *pvalue = md_json_clone(p, json); @@ -568,7 +728,7 @@ apr_status_t md_json_clone_from(void **pvalue, md_json_t *json, apr_pool_t *p, v /* array generic */ apr_status_t md_json_geta(apr_array_header_t *a, md_json_from_cb *cb, void *baton, - md_json_t *json, ...) + const md_json_t *json, ...) { json_t *j; va_list ap; @@ -672,10 +832,36 @@ int md_json_itera(md_json_itera_cb *cb, void *baton, md_json_t *json, ...) return 1; } +int md_json_iterkey(md_json_iterkey_cb *cb, void *baton, md_json_t *json, ...) +{ + json_t *j; + va_list ap; + const char *key; + json_t *val; + md_json_t wrap; + + va_start(ap, json); + j = jselect(json, ap); + va_end(ap); + + if (!j || !json_is_object(j)) { + return 0; + } + + wrap.p = json->p; + json_object_foreach(j, key, val) { + wrap.j = val; + if (!cb(baton, key, &wrap)) { + return 0; + } + } + return 1; +} + /**************************************************************************************************/ /* array strings */ -apr_status_t md_json_getsa(apr_array_header_t *a, md_json_t *json, ...) +apr_status_t md_json_getsa(apr_array_header_t *a, const md_json_t *json, ...) { json_t *j; va_list ap; @@ -711,6 +897,7 @@ apr_status_t md_json_dupsa(apr_array_header_t *a, apr_pool_t *p, md_json_t *json size_t index; json_t *val; + apr_array_clear(a); json_array_foreach(j, index, val) { if (json_is_string(val)) { APR_ARRAY_PUSH(a, const char *) = apr_pstrdup(p, json_string_value(val)); @@ -757,7 +944,7 @@ apr_status_t md_json_setsa(apr_array_header_t *a, md_json_t *json, ...) /* formatting, parsing */ typedef struct { - md_json_t *json; + const md_json_t *json; md_json_fmt_t fmt; const char *fname; apr_file_t *f; @@ -782,7 +969,7 @@ static int dump_cb(const char *buffer, size_t len, void *baton) return (rv == APR_SUCCESS)? 0 : -1; } -apr_status_t md_json_writeb(md_json_t *json, md_json_fmt_t fmt, apr_bucket_brigade *bb) +apr_status_t md_json_writeb(const md_json_t *json, md_json_fmt_t fmt, apr_bucket_brigade *bb) { int rv = json_dump_callback(json->j, dump_cb, bb, fmt_to_flags(fmt)); return rv? APR_EGENERAL : APR_SUCCESS; @@ -791,22 +978,25 @@ apr_status_t md_json_writeb(md_json_t *json, md_json_fmt_t fmt, apr_bucket_briga static int chunk_cb(const char *buffer, size_t len, void *baton) { apr_array_header_t *chunks = baton; - char *chunk = apr_pcalloc(chunks->pool, len+1); + char *chunk; - memcpy(chunk, buffer, len); - APR_ARRAY_PUSH(chunks, const char *) = chunk; + if (len > 0) { + chunk = apr_palloc(chunks->pool, len+1); + memcpy(chunk, buffer, len); + chunk[len] = '\0'; + APR_ARRAY_PUSH(chunks, const char*) = chunk; + } return 0; } -const char *md_json_writep(md_json_t *json, apr_pool_t *p, md_json_fmt_t fmt) +const char *md_json_writep(const md_json_t *json, apr_pool_t *p, md_json_fmt_t fmt) { apr_array_header_t *chunks; int rv; chunks = apr_array_make(p, 10, sizeof(char *)); rv = json_dump_callback(json->j, chunk_cb, chunks, fmt_to_flags(fmt)); - - if (rv) { + if (APR_SUCCESS != rv) { md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "md_json_writep failed to dump JSON"); return NULL; @@ -816,33 +1006,32 @@ const char *md_json_writep(md_json_t *json, apr_pool_t *p, md_json_fmt_t fmt) case 0: return ""; case 1: - return APR_ARRAY_IDX(chunks, 0, const char *); + return APR_ARRAY_IDX(chunks, 0, const char*); default: return apr_array_pstrcat(p, chunks, 0); } } -apr_status_t md_json_writef(md_json_t *json, apr_pool_t *p, md_json_fmt_t fmt, apr_file_t *f) +apr_status_t md_json_writef(const md_json_t *json, apr_pool_t *p, md_json_fmt_t fmt, apr_file_t *f) { apr_status_t rv; const char *s; - s = md_json_writep(json, p, fmt); - - if (s) { + if ((s = md_json_writep(json, p, fmt))) { rv = apr_file_write_full(f, s, strlen(s), NULL); + if (APR_SUCCESS != rv) { + md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, json->p, "md_json_writef: error writing file"); + } } else { rv = APR_EINVAL; - } - - if (APR_SUCCESS != rv) { - md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, json->p, "md_json_writef"); + md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, json->p, + "md_json_writef: error dumping json (%s)", md_json_dump_state(json, p)); } return rv; } -apr_status_t md_json_fcreatex(md_json_t *json, apr_pool_t *p, md_json_fmt_t fmt, +apr_status_t md_json_fcreatex(const md_json_t *json, apr_pool_t *p, md_json_fmt_t fmt, const char *fpath, apr_fileperms_t perms) { apr_status_t rv; @@ -866,7 +1055,7 @@ static apr_status_t write_json(void *baton, apr_file_t *f, apr_pool_t *p) return rv; } -apr_status_t md_json_freplace(md_json_t *json, apr_pool_t *p, md_json_fmt_t fmt, +apr_status_t md_json_freplace(const md_json_t *json, apr_pool_t *p, md_json_fmt_t fmt, const char *fpath, apr_fileperms_t perms) { j_write_ctx ctx; @@ -938,11 +1127,14 @@ apr_status_t md_json_readb(md_json_t **pjson, apr_pool_t *pool, apr_bucket_briga json_t *j; j = json_load_callback(load_cb, bb, 0, &error); - if (!j) { - return APR_EINVAL; + if (j) { + *pjson = json_create(pool, j); + } else { + md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, pool, + "failed to load JSON file: %s (line %d:%d)", + error.text, error.line, error.column); } - *pjson = json_create(pool, j); - return APR_SUCCESS; + return (j && *pjson) ? APR_SUCCESS : APR_EINVAL; } static size_t load_file_cb(void *data, size_t max_len, void *baton) @@ -993,12 +1185,18 @@ apr_status_t md_json_readf(md_json_t **pjson, apr_pool_t *p, const char *fpath) apr_status_t md_json_read_http(md_json_t **pjson, apr_pool_t *pool, const md_http_response_t *res) { apr_status_t rv = APR_ENOENT; - if (res->rv == APR_SUCCESS) { - const char *ctype = apr_table_get(res->headers, "content-type"); - if (ctype && res->body && (strstr(ctype, "/json") || strstr(ctype, "+json"))) { - rv = md_json_readb(pjson, pool, res->body); - } + const char *ctype, *p; + + *pjson = NULL; + if (!res->body) goto cleanup; + ctype = md_util_parse_ct(res->req->pool, apr_table_get(res->headers, "content-type")); + if (!ctype) goto cleanup; + p = ctype + strlen(ctype) +1; + if (!strcmp(p - sizeof("/json"), "/json") + || !strcmp(p - sizeof("+json"), "+json")) { + rv = md_json_readb(pjson, pool, res->body); } +cleanup: return rv; } @@ -1008,26 +1206,24 @@ typedef struct { md_json_t *json; } resp_data; -static apr_status_t json_resp_cb(const md_http_response_t *res) +static apr_status_t json_resp_cb(const md_http_response_t *res, void *data) { - resp_data *resp = res->req->baton; + resp_data *resp = data; return md_json_read_http(&resp->json, resp->pool, res); } apr_status_t md_json_http_get(md_json_t **pjson, apr_pool_t *pool, struct md_http_t *http, const char *url) { - long req_id; apr_status_t rv; resp_data resp; memset(&resp, 0, sizeof(resp)); resp.pool = pool; - rv = md_http_GET(http, url, NULL, json_resp_cb, &resp, &req_id); + rv = md_http_GET_perform(http, url, NULL, json_resp_cb, &resp); if (rv == APR_SUCCESS) { - md_http_await(http, req_id); *pjson = resp.json; return resp.rv; } @@ -1035,3 +1231,81 @@ apr_status_t md_json_http_get(md_json_t **pjson, apr_pool_t *pool, return rv; } + +apr_status_t md_json_copy_to(md_json_t *dest, const md_json_t *src, ...) +{ + json_t *j; + va_list ap; + apr_status_t rv = APR_SUCCESS; + + va_start(ap, src); + j = jselect(src, ap); + va_end(ap); + + if (j) { + va_start(ap, src); + rv = jselect_set(j, dest, ap); + va_end(ap); + } + return rv; +} + +const char *md_json_dump_state(const md_json_t *json, apr_pool_t *p) +{ + if (!json) return "NULL"; + return apr_psprintf(p, "%s, refc=%ld", md_json_type_name(json), (long)json->j->refcount); +} + +apr_status_t md_json_set_timeperiod(const md_timeperiod_t *tp, md_json_t *json, ...) +{ + char ts[APR_RFC822_DATE_LEN]; + json_t *jn, *j; + va_list ap; + const char *key; + apr_status_t rv; + + if (tp && tp->start && tp->end) { + jn = json_object(); + apr_rfc822_date(ts, tp->start); + json_object_set_new(jn, "from", json_string(ts)); + apr_rfc822_date(ts, tp->end); + json_object_set_new(jn, "until", json_string(ts)); + + va_start(ap, json); + rv = jselect_set_new(jn, json, ap); + va_end(ap); + return rv; + } + else { + va_start(ap, json); + j = jselect_parent(&key, 0, json, ap); + va_end(ap); + + if (key && j && json_is_object(j)) { + json_object_del(j, key); + } + return APR_SUCCESS; + } +} + +apr_status_t md_json_get_timeperiod(md_timeperiod_t *tp, md_json_t *json, ...) +{ + json_t *j, *jts; + va_list ap; + + va_start(ap, json); + j = jselect(json, ap); + va_end(ap); + + memset(tp, 0, sizeof(*tp)); + if (!j) goto not_found; + jts = json_object_get(j, "from"); + if (!jts || !json_is_string(jts)) goto not_found; + tp->start = apr_date_parse_rfc(json_string_value(jts)); + jts = json_object_get(j, "until"); + if (!jts || !json_is_string(jts)) goto not_found; + tp->end = apr_date_parse_rfc(json_string_value(jts)); + return APR_SUCCESS; +not_found: + return APR_ENOENT; +} |