summaryrefslogtreecommitdiffstats
path: root/modules/md/md_json.c
diff options
context:
space:
mode:
Diffstat (limited to 'modules/md/md_json.c')
-rw-r--r--modules/md/md_json.c1311
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;
+}