diff options
Diffstat (limited to '')
-rw-r--r-- | modules/md/md_json.c | 1311 |
1 files changed, 1311 insertions, 0 deletions
diff --git a/modules/md/md_json.c b/modules/md/md_json.c new file mode 100644 index 0000000..e0f977e --- /dev/null +++ b/modules/md/md_json.c @@ -0,0 +1,1311 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <assert.h> +#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 + * when undefining their INLINEs, we get static, unused functions, arg + */ +#if defined(__GNUC__) +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +#pragma GCC diagnostic push +#endif +#pragma GCC diagnostic ignored "-Wunused-function" +#pragma GCC diagnostic ignored "-Wunreachable-code" +#elif defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +#endif + +#include <jansson_config.h> +#undef JSON_INLINE +#define JSON_INLINE +#include <jansson.h> + +#if defined(__GNUC__) +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +#pragma GCC diagnostic pop +#endif +#elif defined(__clang__) +#pragma clang diagnostic pop +#endif + +struct md_json_t { + apr_pool_t *p; + json_t *j; +}; + +/**************************************************************************************************/ +/* lifecycle */ + +static apr_status_t json_pool_cleanup(void *data) +{ + md_json_t *json = data; + if (json) { + md_json_destroy(json); + } + return APR_SUCCESS; +} + +static md_json_t *json_create(apr_pool_t *pool, json_t *j) +{ + md_json_t *json; + + if (!j) { + apr_abortfunc_t abfn = apr_pool_abort_get(pool); + if (abfn) { + abfn(APR_ENOMEM); + } + assert(j != NULL); /* failsafe in case abort is unset */ + } + json = apr_pcalloc(pool, sizeof(*json)); + json->p = pool; + json->j = j; + apr_pool_cleanup_register(pool, json, json_pool_cleanup, apr_pool_cleanup_null); + + return json; +} + +md_json_t *md_json_create(apr_pool_t *pool) +{ + return json_create(pool, json_object()); +} + +md_json_t *md_json_create_s(apr_pool_t *pool, const char *s) +{ + return json_create(pool, json_string(s)); +} + +void md_json_destroy(md_json_t *json) +{ + if (json && json->j) { + assert(json->j->refcount > 0); + json_decref(json->j); + json->j = NULL; + } +} + +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, const md_json_t *json) +{ + return json_create(pool, json_deep_copy(json->j)); +} + +/**************************************************************************************************/ +/* selectors */ + + +static json_t *jselect(const md_json_t *json, va_list ap) +{ + json_t *j; + const char *key; + + j = json->j; + key = va_arg(ap, char *); + while (key && j) { + j = json_object_get(j, key); + key = va_arg(ap, char *); + } + return j; +} + +static json_t *jselect_parent(const char **child_key, int create, md_json_t *json, va_list ap) +{ + const char *key, *next; + json_t *j, *jn; + + *child_key = NULL; + j = json->j; + key = va_arg(ap, char *); + while (key && j) { + next = va_arg(ap, char *); + if (next) { + jn = json_object_get(j, key); + if (!jn && create) { + jn = json_object(); + json_object_set_new(j, key, jn); + } + j = jn; + } + else { + *child_key = key; + } + key = next; + } + return j; +} + +static apr_status_t jselect_add(json_t *val, 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)) { + 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; + } + + aj = json_object_get(j, key); + if (!aj) { + aj = json_array(); + json_object_set_new(j, key, aj); + } + + if (!json_is_array(aj)) { + json_decref(val); + return APR_EINVAL; + } + + if (json_array_size(aj) <= index) { + json_array_append(aj, val); + } + else { + json_array_insert(aj, index, val); + } + return APR_SUCCESS; +} + +static apr_status_t jselect_set(json_t *val, md_json_t *json, va_list ap) +{ + const char *key; + json_t *j; + + j = jselect_parent(&key, 1, json, ap); + + if (!j) { + return APR_EINVAL; + } + + if (key) { + if (!json_is_object(j)) { + return APR_EINVAL; + } + json_object_set(j, key, val); + } + else { + /* replace */ + if (json->j) { + json_decref(json->j); + } + json_incref(val); + json->j = val; + } + return APR_SUCCESS; +} + +static apr_status_t jselect_set_new(json_t *val, md_json_t *json, va_list ap) +{ + const char *key; + json_t *j; + + 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_new(j, key, val); + } + else { + /* replace */ + if (json->j) { + json_decref(json->j); + } + json->j = val; + } + return APR_SUCCESS; +} + +int md_json_has_key(const md_json_t *json, ...) +{ + json_t *j; + va_list ap; + + va_start(ap, json); + j = jselect(json, ap); + va_end(ap); + + return j != NULL; +} + +/**************************************************************************************************/ +/* 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(const md_json_t *json, ...) +{ + json_t *j; + va_list ap; + + va_start(ap, json); + j = jselect(json, ap); + va_end(ap); + + return j? json_is_true(j) : 0; +} + +apr_status_t md_json_setb(int value, md_json_t *json, ...) +{ + va_list ap; + apr_status_t rv; + + va_start(ap, json); + rv = jselect_set_new(json_boolean(value), json, ap); + va_end(ap); + return rv; +} + +/**************************************************************************************************/ +/* numbers */ + +double md_json_getn(const md_json_t *json, ...) +{ + json_t *j; + va_list ap; + + va_start(ap, json); + j = jselect(json, ap); + va_end(ap); + return (j && json_is_number(j))? json_number_value(j) : 0.0; +} + +apr_status_t md_json_setn(double value, md_json_t *json, ...) +{ + va_list ap; + apr_status_t rv; + + va_start(ap, json); + rv = jselect_set_new(json_real(value), json, ap); + va_end(ap); + return rv; +} + +/**************************************************************************************************/ +/* longs */ + +long md_json_getl(const md_json_t *json, ...) +{ + json_t *j; + va_list ap; + + va_start(ap, json); + j = jselect(json, ap); + va_end(ap); + return (long)((j && json_is_number(j))? json_integer_value(j) : 0L); +} + +apr_status_t md_json_setl(long value, md_json_t *json, ...) +{ + va_list ap; + apr_status_t rv; + + va_start(ap, json); + rv = jselect_set_new(json_integer(value), json, ap); + va_end(ap); + return rv; +} + +/**************************************************************************************************/ +/* strings */ + +const char *md_json_gets(const md_json_t *json, ...) +{ + json_t *j; + va_list ap; + + va_start(ap, json); + j = jselect(json, ap); + va_end(ap); + + return (j && json_is_string(j))? json_string_value(j) : NULL; +} + +const char *md_json_dups(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); + + return (j && json_is_string(j))? apr_pstrdup(p, json_string_value(j)) : NULL; +} + +apr_status_t md_json_sets(const char *value, md_json_t *json, ...) +{ + va_list ap; + apr_status_t rv; + + va_start(ap, json); + rv = jselect_set_new(json_string(value), json, ap); + va_end(ap); + return rv; +} + +/**************************************************************************************************/ +/* 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, ...) +{ + 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; +} + +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; + const char *key; + json_t *j; + + if (value) { + va_start(ap, json); + rv = jselect_set(value->j, json, ap); + va_end(ap); + } + else { + va_start(ap, json); + j = jselect_parent(&key, 1, json, ap); + va_end(ap); + + if (key && j && !json_is_object(j)) { + json_object_del(j, key); + rv = APR_SUCCESS; + } + else { + rv = APR_EINVAL; + } + } + return rv; +} + +apr_status_t md_json_addj(const md_json_t *value, md_json_t *json, ...) +{ + va_list ap; + apr_status_t rv; + + va_start(ap, json); + rv = jselect_add(value->j, json, ap); + va_end(ap); + 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 */ + +apr_status_t md_json_clr(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_object(j)) { + json_object_clear(j); + } + else if (j && json_is_array(j)) { + json_array_clear(j); + } + return APR_SUCCESS; +} + +apr_status_t md_json_del(md_json_t *json, ...) +{ + const char *key; + json_t *j; + va_list ap; + + 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; +} + +/**************************************************************************************************/ +/* object strings */ + +apr_status_t md_json_gets_dict(apr_table_t *dict, 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_object(j)) { + const char *key; + json_t *val; + + json_object_foreach(j, key, val) { + if (json_is_string(val)) { + apr_table_set(dict, key, json_string_value(val)); + } + } + return APR_SUCCESS; + } + return APR_ENOENT; +} + +static int object_set(void *data, const char *key, const char *val) +{ + json_t *j = data, *nj = json_string(val); + json_object_set(j, key, nj); + json_decref(nj); + return 1; +} + +apr_status_t md_json_sets_dict(apr_table_t *dict, md_json_t *json, ...) +{ + json_t *nj, *j; + va_list ap; + + va_start(ap, json); + j = jselect(json, ap); + va_end(ap); + + if (!j || !json_is_object(j)) { + const char *key; + + va_start(ap, json); + j = jselect_parent(&key, 1, json, ap); + va_end(ap); + + if (!key || !j || !json_is_object(j)) { + return APR_EINVAL; + } + nj = json_object(); + json_object_set_new(j, key, nj); + j = nj; + } + + apr_table_do(object_set, j, dict, NULL); + return APR_SUCCESS; +} + +/**************************************************************************************************/ +/* conversions */ + +apr_status_t md_json_pass_to(void *value, md_json_t *json, apr_pool_t *p, void *baton) +{ + (void)p; + (void)baton; + return md_json_setj(value, json, NULL); +} + +apr_status_t md_json_pass_from(void **pvalue, md_json_t *json, apr_pool_t *p, void *baton) +{ + (void)p; + (void)baton; + *pvalue = json; + return APR_SUCCESS; +} + +apr_status_t md_json_clone_to(void *value, md_json_t *json, apr_pool_t *p, void *baton) +{ + (void)baton; + return md_json_setj(md_json_clone(p, value), json, NULL); +} + +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); + return APR_SUCCESS; +} + +/**************************************************************************************************/ +/* array generic */ + +apr_status_t md_json_geta(apr_array_header_t *a, md_json_from_cb *cb, void *baton, + const md_json_t *json, ...) +{ + json_t *j; + va_list ap; + apr_status_t rv = APR_SUCCESS; + size_t index; + json_t *val; + md_json_t wrap; + void *element; + + va_start(ap, json); + j = jselect(json, ap); + va_end(ap); + + if (!j || !json_is_array(j)) { + return APR_ENOENT; + } + + wrap.p = a->pool; + json_array_foreach(j, index, val) { + wrap.j = val; + if (APR_SUCCESS == (rv = cb(&element, &wrap, wrap.p, baton))) { + if (element) { + APR_ARRAY_PUSH(a, void*) = element; + } + } + else if (APR_ENOENT == rv) { + rv = APR_SUCCESS; + } + else { + break; + } + } + return rv; +} + +apr_status_t md_json_seta(apr_array_header_t *a, md_json_to_cb *cb, void *baton, + md_json_t *json, ...) +{ + json_t *j, *nj; + md_json_t wrap; + apr_status_t rv = APR_SUCCESS; + va_list ap; + int i; + + va_start(ap, json); + j = jselect(json, ap); + va_end(ap); + + if (!j || !json_is_array(j)) { + const char *key; + + va_start(ap, json); + j = jselect_parent(&key, 1, json, ap); + va_end(ap); + + if (!key || !j || !json_is_object(j)) { + return APR_EINVAL; + } + nj = json_array(); + json_object_set_new(j, key, nj); + j = nj; + } + + json_array_clear(j); + wrap.p = json->p; + for (i = 0; i < a->nelts; ++i) { + if (!cb) { + return APR_EINVAL; + } + wrap.j = json_string(""); + if (APR_SUCCESS == (rv = cb(APR_ARRAY_IDX(a, i, void*), &wrap, json->p, baton))) { + json_array_append_new(j, wrap.j); + } + } + return rv; +} + +int md_json_itera(md_json_itera_cb *cb, void *baton, md_json_t *json, ...) +{ + json_t *j; + va_list ap; + size_t index; + json_t *val; + md_json_t wrap; + + va_start(ap, json); + j = jselect(json, ap); + va_end(ap); + + if (!j || !json_is_array(j)) { + return 0; + } + + wrap.p = json->p; + json_array_foreach(j, index, val) { + wrap.j = val; + if (!cb(baton, index, &wrap)) { + return 0; + } + } + 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, 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_array(j)) { + size_t index; + json_t *val; + + json_array_foreach(j, index, val) { + if (json_is_string(val)) { + APR_ARRAY_PUSH(a, const char *) = json_string_value(val); + } + } + return APR_SUCCESS; + } + return APR_ENOENT; +} + +apr_status_t md_json_dupsa(apr_array_header_t *a, apr_pool_t *p, 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_array(j)) { + 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)); + } + } + return APR_SUCCESS; + } + return APR_ENOENT; +} + +apr_status_t md_json_setsa(apr_array_header_t *a, md_json_t *json, ...) +{ + json_t *nj, *j; + va_list ap; + int i; + + va_start(ap, json); + j = jselect(json, ap); + va_end(ap); + + if (!j || !json_is_array(j)) { + const char *key; + + va_start(ap, json); + j = jselect_parent(&key, 1, json, ap); + va_end(ap); + + if (!key || !j || !json_is_object(j)) { + return APR_EINVAL; + } + nj = json_array(); + json_object_set_new(j, key, nj); + j = nj; + } + + json_array_clear(j); + for (i = 0; i < a->nelts; ++i) { + json_array_append_new(j, json_string(APR_ARRAY_IDX(a, i, const char*))); + } + return APR_SUCCESS; +} + +/**************************************************************************************************/ +/* formatting, parsing */ + +typedef struct { + const md_json_t *json; + md_json_fmt_t fmt; + const char *fname; + apr_file_t *f; +} j_write_ctx; + +/* Convert from md_json_fmt_t to the Jansson json_dumpX flags. */ +static size_t fmt_to_flags(md_json_fmt_t fmt) +{ + /* NOTE: JSON_PRESERVE_ORDER is off by default before Jansson 2.8. It + * doesn't have any semantic effect on the protocol, but it does let the + * md_json_writeX unit tests run deterministically. */ + return JSON_PRESERVE_ORDER | + ((fmt == MD_JSON_FMT_COMPACT) ? JSON_COMPACT : JSON_INDENT(2)); +} + +static int dump_cb(const char *buffer, size_t len, void *baton) +{ + apr_bucket_brigade *bb = baton; + apr_status_t rv; + + rv = apr_brigade_write(bb, NULL, NULL, buffer, len); + return (rv == APR_SUCCESS)? 0 : -1; +} + +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; +} + +static int chunk_cb(const char *buffer, size_t len, void *baton) +{ + apr_array_header_t *chunks = baton; + 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(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 (APR_SUCCESS != rv) { + md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, + "md_json_writep failed to dump JSON"); + return NULL; + } + + switch (chunks->nelts) { + case 0: + return ""; + case 1: + return APR_ARRAY_IDX(chunks, 0, const char*); + default: + return apr_array_pstrcat(p, chunks, 0); + } +} + +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; + + 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; + 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(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; + apr_file_t *f; + + rv = md_util_fcreatex(&f, fpath, perms, p); + if (APR_SUCCESS == rv) { + rv = md_json_writef(json, p, fmt, f); + apr_file_close(f); + } + return rv; +} + +static apr_status_t write_json(void *baton, apr_file_t *f, apr_pool_t *p) +{ + j_write_ctx *ctx = baton; + apr_status_t rv = md_json_writef(ctx->json, p, ctx->fmt, f); + if (APR_SUCCESS != rv) { + md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "freplace json in %s", ctx->fname); + } + return rv; +} + +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; + ctx.json = json; + ctx.fmt = fmt; + ctx.fname = fpath; + return md_util_freplace(fpath, perms, p, write_json, &ctx); +} + +apr_status_t md_json_readd(md_json_t **pjson, apr_pool_t *pool, const char *data, size_t data_len) +{ + json_error_t error; + json_t *j; + + j = json_loadb(data, data_len, 0, &error); + if (!j) { + return APR_EINVAL; + } + *pjson = json_create(pool, j); + return APR_SUCCESS; +} + +static size_t load_cb(void *data, size_t max_len, void *baton) +{ + apr_bucket_brigade *body = baton; + size_t blen, read_len = 0; + const char *bdata; + char *dest = data; + apr_bucket *b; + apr_status_t rv; + + while (body && !APR_BRIGADE_EMPTY(body) && max_len > 0) { + b = APR_BRIGADE_FIRST(body); + if (APR_BUCKET_IS_METADATA(b)) { + if (APR_BUCKET_IS_EOS(b)) { + body = NULL; + } + } + else { + rv = apr_bucket_read(b, &bdata, &blen, APR_BLOCK_READ); + if (rv == APR_SUCCESS) { + if (blen > max_len) { + apr_bucket_split(b, max_len); + blen = max_len; + } + memcpy(dest, bdata, blen); + read_len += blen; + max_len -= blen; + dest += blen; + } + else { + body = NULL; + if (!APR_STATUS_IS_EOF(rv)) { + /* everything beside EOF is an error */ + read_len = (size_t)-1; + } + } + } + APR_BUCKET_REMOVE(b); + apr_bucket_delete(b); + } + + return read_len; +} + +apr_status_t md_json_readb(md_json_t **pjson, apr_pool_t *pool, apr_bucket_brigade *bb) +{ + json_error_t error; + json_t *j; + + j = json_load_callback(load_cb, bb, 0, &error); + 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); + } + return (j && *pjson) ? APR_SUCCESS : APR_EINVAL; +} + +static size_t load_file_cb(void *data, size_t max_len, void *baton) +{ + apr_file_t *f = baton; + apr_size_t len = max_len; + apr_status_t rv; + + rv = apr_file_read(f, data, &len); + if (APR_SUCCESS == rv) { + return len; + } + else if (APR_EOF == rv) { + return 0; + } + return (size_t)-1; +} + +apr_status_t md_json_readf(md_json_t **pjson, apr_pool_t *p, const char *fpath) +{ + apr_file_t *f; + json_t *j; + apr_status_t rv; + json_error_t error; + + rv = apr_file_open(&f, fpath, APR_FOPEN_READ, 0, p); + if (rv != APR_SUCCESS) { + return rv; + } + + j = json_load_callback(load_file_cb, f, 0, &error); + if (j) { + *pjson = json_create(p, j); + } + else { + md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, + "failed to load JSON file %s: %s (line %d:%d)", + fpath, error.text, error.line, error.column); + } + + apr_file_close(f); + return (j && *pjson) ? APR_SUCCESS : APR_EINVAL; +} + +/**************************************************************************************************/ +/* http get */ + +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; + 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; +} + +typedef struct { + apr_status_t rv; + apr_pool_t *pool; + md_json_t *json; +} resp_data; + +static apr_status_t json_resp_cb(const md_http_response_t *res, void *data) +{ + 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) +{ + apr_status_t rv; + resp_data resp; + + memset(&resp, 0, sizeof(resp)); + resp.pool = pool; + + rv = md_http_GET_perform(http, url, NULL, json_resp_cb, &resp); + + if (rv == APR_SUCCESS) { + *pjson = resp.json; + return resp.rv; + } + *pjson = NULL; + 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; +} |