[rs-commit] r49 - in /redwax-tool/trunk: redwax-tool.c redwax-tool.h redwax_openssl.c redwax_p11kit.c redwax_util.c redwax_util.h

rs-commit at redwax.eu rs-commit at redwax.eu
Mon Nov 22 00:06:13 CET 2021


Author: minfrin at redwax.eu
Date: Mon Nov 22 00:06:12 2021
New Revision: 49

Log:
Add --metadata-out to print certificate metadata, and
--format-out to specify what format the metadata should
be in.

Modified:
    redwax-tool/trunk/redwax-tool.c
    redwax-tool/trunk/redwax-tool.h
    redwax-tool/trunk/redwax_openssl.c
    redwax-tool/trunk/redwax_p11kit.c
    redwax-tool/trunk/redwax_util.c
    redwax-tool/trunk/redwax_util.h

Modified: redwax-tool/trunk/redwax-tool.c
==============================================================================
--- redwax-tool/trunk/redwax-tool.c	(original)
+++ redwax-tool/trunk/redwax-tool.c	Mon Nov 22 00:06:12 2021
@@ -65,6 +65,9 @@
         APR_HOOK_LINK(process_pkcs11_out);
         APR_HOOK_LINK(complete_pkcs11_module_out);
         APR_HOOK_LINK(process_pkcs11_module_out);
+        APR_HOOK_LINK(process_metadata_out);
+        APR_HOOK_LINK(complete_format_out);
+        APR_HOOK_LINK(set_format_out);
         APR_HOOK_LINK(search_chain);
         APR_HOOK_LINK(search_key);
         APR_HOOK_LINK(compare_certificate);
@@ -109,6 +112,12 @@
         (redwax_tool_t * r, const char *arg), (r, arg), DECLINED);
 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, complete_pkcs11_module_out,
         (redwax_tool_t * r, const char *mod, redwax_token_quoted_e quoted), (r, mod, quoted), DECLINED);
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, process_metadata_out,
+        (redwax_tool_t * r, const char *arg), (r, arg), DECLINED);
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, set_format_out,
+        (redwax_tool_t * r, const char *arg), (r, arg), DECLINED);
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, complete_format_out,
+        (redwax_tool_t * r, apr_hash_t *formats), (r, formats), DECLINED);
 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, apr_status_t, search_chain,
         (redwax_tool_t * r, const redwax_certificate_t *cert), (r, cert), DECLINED);
 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, apr_status_t, search_key,
@@ -163,6 +172,8 @@
 #define REDWAX_TOOL_PKCS12_OUT 289
 #define REDWAX_TOOL_PKCS11_OUT 290
 #define REDWAX_TOOL_PKCS11_MODULE_OUT 291
+#define REDWAX_TOOL_METADATA_OUT 292
+#define REDWAX_TOOL_FORMAT_OUT 293
 
 #define REDWAX_EXIT_OK 0
 #define REDWAX_EXIT_INIT 1
@@ -239,6 +250,8 @@
     { "pkcs12-out", REDWAX_TOOL_PKCS12_OUT, 1, "  --pkcs12-out=file\t\tWrite certificates, intermediate certificates,\n\t\t\t\troot certificates, crls, and keys into a PKCS12\n\t\t\t\tfile. Use '-' for stdout." },
     { "pkcs11-out", REDWAX_TOOL_PKCS11_OUT, 1, "  --pkcs11-out=url\t\tWrite certificates, intermediate certificates,\n\t\t\t\troot certificates, crls, and keys into a PKCS11\n\t\t\t\ttoken identified by the given url." },
     { "pkcs11-module-out", REDWAX_TOOL_PKCS11_MODULE_OUT, 1, "  --pkcs11-module-out=mod\tSpecify the name of the PKCS11 module to be used,\n\t\t\t\toverriding system defaults. If relative, use the\n\t\t\t\tdefault PKCS11 module path, otherwise specify the\n\t\t\t\tabsolute path. Include the extension of the module." },
+    { "metadata-out", REDWAX_TOOL_METADATA_OUT, 1, "  --metadata-out=file\t\tWrite metadata of each certificate and key to the\n\t\t\t\tgiven file in the format given by the format\n\t\t\t\tparameter." },
+    { "format-out", REDWAX_TOOL_FORMAT_OUT, 1, "  --format-out=xml|json\t\tFormat of output metadata." },
     { NULL }
 };
 
@@ -1500,6 +1513,31 @@
     return APR_SUCCESS;
 }
 
+static apr_status_t redwax_complete_format_out(redwax_tool_t *r, const char *arg,
+        redwax_token_quoted_e quoted)
+{
+    apr_hash_t *formats = apr_hash_make(r->pool);
+
+    apr_hash_index_t *hi;
+    void *val;
+    int arglen =  strlen(arg);
+
+    rt_run_complete_format_out(r, formats);
+
+    for (hi = apr_hash_first(r->pool, formats); hi; hi = apr_hash_next(hi)) {
+        apr_hash_this(hi, NULL, NULL, &val);
+
+        if (!strncmp(arg, (const char *)val, arglen)) {
+
+            apr_file_printf(r->out, "%s \n",
+                    redwax_pescape_echo_quoted(r->pool,
+                            (const char *)val, quoted, 1));
+        }
+    }
+
+    return APR_SUCCESS;
+}
+
 apr_status_t redwax_file_out(redwax_tool_t *r, const char *path,
         apr_status_t (out)(redwax_tool_t *r, const char *path, const char *secret))
 {
@@ -1942,6 +1980,22 @@
     return APR_SUCCESS;
 }
 
+static apr_status_t redwax_metadata_out(redwax_tool_t *r, const char *arg)
+{
+
+    apr_status_t status = rt_run_process_metadata_out(r, arg);
+
+    return status;
+}
+
+static apr_status_t redwax_format_out(redwax_tool_t *r, const char *arg)
+{
+
+    apr_status_t status = rt_run_set_format_out(r, arg);
+
+    return status;
+}
+
 void redwax_add_default_hooks()
 {
     rt_hook_complete_filter(redwax_complete_filter_passthrough, NULL, NULL, APR_HOOK_MIDDLE);
@@ -2179,6 +2233,14 @@
             redwax_pkcs11_module_out(r, optarg);
             break;
         }
+        case REDWAX_TOOL_METADATA_OUT: {
+            redwax_metadata_out(r, optarg);
+            break;
+        }
+        case REDWAX_TOOL_FORMAT_OUT: {
+            redwax_format_out(r, optarg);
+            break;
+        }
         }
 
     }
@@ -2306,6 +2368,14 @@
             }
             case REDWAX_TOOL_SECRET_TOKEN_OUT: {
                 redwax_complete_file(r, optarg, state.isquoted);
+                break;
+            }
+            case REDWAX_TOOL_METADATA_OUT: {
+                redwax_complete_file(r, optarg, state.isquoted);
+                break;
+            }
+            case REDWAX_TOOL_FORMAT_OUT: {
+                redwax_complete_format_out(r, optarg, state.isquoted);
                 break;
             }
             }
@@ -2492,6 +2562,14 @@
             redwax_complete_file(r, "", state.isquoted);
             break;
         }
+        case REDWAX_TOOL_METADATA_OUT: {
+            redwax_complete_file(r, "", state.isquoted);
+            break;
+        }
+        case REDWAX_TOOL_FORMAT_OUT: {
+            redwax_complete_format_out(r, "", state.isquoted);
+            break;
+        }
         }
 
         break;
@@ -2578,6 +2656,7 @@
     apr_file_open_stdin(&r.in, r.pool);
     apr_file_open_stdout(&r.out, r.pool);
 
+    r.format = REDWAX_FORMAT_JSON;
     r.key_in = 1;
 
     r.certs_in = apr_array_make(r.pool, 10, sizeof(redwax_certificate_t));

Modified: redwax-tool/trunk/redwax-tool.h
==============================================================================
--- redwax-tool/trunk/redwax-tool.h	(original)
+++ redwax-tool/trunk/redwax-tool.h	Mon Nov 22 00:06:12 2021
@@ -41,6 +41,13 @@
     const char *token;
     int needs_write;
 } redwax_pkcs11_t;
+
+typedef enum redwax_format_e {
+    REDWAX_FORMAT_TEXT = 0,
+    REDWAX_FORMAT_XML,
+    REDWAX_FORMAT_JSON,
+    REDWAX_FORMAT_YAML,
+} redwax_format_e;
 
 typedef struct redwax_tool_t {
     apr_pool_t *pool;
@@ -79,6 +86,7 @@
     redwax_pkcs11_t pkcs11_in;
     redwax_pkcs11_t pkcs11_out;
     apr_time_t *now;
+    redwax_format_e format;
     int top;
     int cert_out;
     int chain_out;
@@ -137,6 +145,7 @@
     const char *header;
     const unsigned char *der;
     apr_size_t len;
+    const char *origin;
     const char *name;
     void *ctx;
     redwax_certificate_common_t common;
@@ -151,6 +160,7 @@
     const char *header;
     const unsigned char *der;
     apr_size_t len;
+    const char *origin;
     void *ctx;
 } redwax_crl_t;
 
@@ -198,6 +208,7 @@
     const char *header;
     const unsigned char *der;
     apr_size_t len;
+    const char *origin;
     const char *name;
     void *ctx;
     redwax_key_common_t common;
@@ -445,6 +456,30 @@
         (redwax_tool_t *r, const char *arg));
 
 /**
+ * Hook to handle the output of metadata.
+ *
+ * @param r The redwax-tool context.
+ */
+APR_DECLARE_EXTERNAL_HOOK(rt, REDWAX, apr_status_t, process_metadata_out,
+        (redwax_tool_t *r, const char *arg));
+
+/**
+ * Hook to complete the metadata format.
+ *
+ * @param r The redwax-tool context.
+ */
+APR_DECLARE_EXTERNAL_HOOK(rt, REDWAX, apr_status_t, complete_format_out,
+        (redwax_tool_t *r, apr_hash_t *formats));
+
+/**
+ * Hook to set the metadata format.
+ *
+ * @param r The redwax-tool context.
+ */
+APR_DECLARE_EXTERNAL_HOOK(rt, REDWAX, apr_status_t, set_format_out,
+        (redwax_tool_t *r, const char *arg));
+
+/**
  * Hook to complete verification parameters.
  *
  * @param r The redwax-tool context.

Modified: redwax-tool/trunk/redwax_openssl.c
==============================================================================
--- redwax-tool/trunk/redwax_openssl.c	(original)
+++ redwax-tool/trunk/redwax_openssl.c	Mon Nov 22 00:06:12 2021
@@ -26,6 +26,8 @@
 
 #include "config.h"
 #include "redwax-tool.h"
+
+#include "redwax_util.h"
 
 #if HAVE_OPENSSL_PEM_H
 
@@ -906,6 +908,8 @@
             cert->der = data;
             cert->len = len;
 
+            cert->origin = file;
+
             rt_run_normalise_certificate(r, cert, 1);
 
         }
@@ -940,6 +944,8 @@
                 cert->len = i2d_X509(x, &der);
                 cert->der = der;
 
+                cert->origin = file;
+
                 rt_run_normalise_certificate(r, cert, 1);
 
                 redwax_print_error(r, "pem-in: trusted: %s\n",
@@ -969,6 +975,8 @@
                 crl->header = header;
                 crl->der = data;
                 crl->len = len;
+
+                crl->origin = file;
 
 //                 X509_CRL_free(c);
              }
@@ -1075,6 +1083,8 @@
 
             key->header = header;
             key->len = BIO_get_mem_data(kbio, &key->der);
+
+            key->origin = file;
 
             rt_run_normalise_key(r, key, 1);
 
@@ -2074,6 +2084,191 @@
     }
 
     return APR_SUCCESS;
+}
+
+apr_status_t redwax_openssl_writev(void *ctx, const struct iovec *vec,
+                apr_size_t nvec)
+{
+    apr_size_t nbytes;
+
+    return apr_file_writev(ctx, vec, nvec, &nbytes);
+}
+
+static apr_status_t redwax_openssl_process_metadata_out(redwax_tool_t *r,
+        const char *file)
+{
+//    X509 *x = NULL;
+
+    redwax_metadata_t *m;
+
+//    EVP_PKEY *pkey = NULL;
+
+    apr_file_t *out;
+    int i;
+    apr_status_t status;
+
+    if (!strcmp(file, "-")) {
+        out = r->out;
+    }
+    else {
+        status = apr_file_open(&out, file, APR_FOPEN_WRITE | APR_FOPEN_CREATE | APR_FOPEN_TRUNCATE,
+                APR_FPROT_OS_DEFAULT, r->pool);
+        if (APR_SUCCESS != status) {
+            return status;
+        }
+
+    }
+
+    redwax_metadata_push_root(r->pool, "Vault", redwax_openssl_writev, out, r->format, &m);
+
+    if (r->cert_out) {
+
+        redwax_metadata_push_array(m, "Certificates", !r->certs_out->nelts);
+
+        for (i = 0; i < r->certs_out->nelts; i++)
+        {
+            const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->certs_out,
+                    i, const redwax_certificate_t);
+
+
+#if 0
+            const unsigned char *der = cert->der;
+
+            X509 *xi = d2i_X509(NULL, &der, cert->len);
+
+            if (!xi) {
+                redwax_openssl_print_errors(r);
+                X509_free(x);
+                return APR_ENOENT;
+            }
+#endif
+
+            redwax_print_error(r, "metadata-out: certificate: %s\n",
+                    cert->common.subject);
+
+            redwax_metadata_push_object(m, "Certificate", 0);
+            redwax_metadata_add_string(m, "Origin", cert->origin);
+            redwax_metadata_pop_object(m);
+
+        }
+
+        redwax_metadata_pop_array(m);
+
+    }
+
+    if (r->chain_out) {
+
+        redwax_metadata_push_array(m, "Chains", !r->intermediates_out->nelts);
+
+        for (i = 0; i < r->intermediates_out->nelts; i++)
+        {
+            const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->intermediates_out,
+                    i, const redwax_certificate_t);
+
+#if 0
+            const unsigned char *der = cert->der;
+
+            X509 *xi = d2i_X509(NULL, &der, cert->len);
+
+            if (!xi) {
+                redwax_openssl_print_errors(r);
+                X509_free(x);
+                return APR_ENOENT;
+            }
+#endif
+
+            redwax_print_error(r, "metadata-out: intermediate: %s\n",
+                    cert->common.subject);
+
+            redwax_metadata_push_object(m, "Certificate", 0);
+            redwax_metadata_add_string(m, "Origin", cert->origin);
+            redwax_metadata_pop_object(m);
+
+        }
+
+        redwax_metadata_pop_array(m);
+    }
+
+    if (r->root_out || r->trust_out) {
+        for (i = 0; i < r->trusted_out->nelts; i++)
+        {
+            const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->trusted_out, i, const redwax_certificate_t);
+
+#if 0
+            const unsigned char *der = cert->der;
+
+            X509 *xi = d2i_X509_AUX(NULL, &der, cert->len);
+
+            if (!xi) {
+                redwax_openssl_print_errors(r);
+                X509_free(x);
+                return APR_ENOENT;
+            }
+#endif
+
+            redwax_print_error(r, "metadata-out: trusted: %s\n",
+                    cert->common.subject);
+
+            redwax_metadata_push_object(m, "Certificate", 0);
+            redwax_metadata_add_string(m, "Origin", cert->origin);
+            redwax_metadata_pop_object(m);
+
+        }
+    }
+
+    if (r->key_out) {
+
+        redwax_metadata_push_array(m, "Keys", !r->keys_out->nelts);
+
+        for (i = 0; i < r->keys_out->nelts; i++)
+        {
+            const redwax_key_t
+                *key = &APR_ARRAY_IDX(r->keys_out, i, const redwax_key_t);
+
+            redwax_print_error(r, "metadata-out: key\n");
+
+            redwax_metadata_push_object(m, "Key", 0);
+            redwax_metadata_add_string(m, "Origin", key->origin);
+            redwax_metadata_pop_object(m);
+
+        }
+
+        redwax_metadata_pop_array(m);
+    }
+
+    redwax_metadata_pop_root(m);
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t redwax_openssl_complete_format_out(redwax_tool_t *r,
+        apr_hash_t *params)
+{
+    apr_hash_set(params, "xml", APR_HASH_KEY_STRING, "xml");
+    apr_hash_set(params, "json", APR_HASH_KEY_STRING, "json");
+    apr_hash_set(params, "yaml", APR_HASH_KEY_STRING, "yaml");
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t redwax_openssl_set_format_out(redwax_tool_t *r, const char *arg)
+{
+    if (!strcmp(arg, "xml")) {
+        r->format = REDWAX_FORMAT_XML;
+        return APR_SUCCESS;
+    }
+
+    if (!strcmp(arg, "json")) {
+        r->format = REDWAX_FORMAT_JSON;
+        return APR_SUCCESS;
+    }
+
+    if (!strcmp(arg, "yaml")) {
+        r->format = REDWAX_FORMAT_YAML;
+        return APR_SUCCESS;
+    }
+
+    return DECLINED;
 }
 
 static apr_status_t redwax_openssl_search_chain(redwax_tool_t *r,
@@ -2827,6 +3022,9 @@
     rt_hook_process_filter(redwax_openssl_process_filter_search, NULL, NULL, APR_HOOK_MIDDLE);
     rt_hook_process_pem_out(redwax_openssl_process_pem_out, NULL, NULL, APR_HOOK_MIDDLE);
     rt_hook_process_pkcs12_out(redwax_openssl_process_pkcs12_out, NULL, NULL, APR_HOOK_MIDDLE);
+    rt_hook_process_metadata_out(redwax_openssl_process_metadata_out, NULL, NULL, APR_HOOK_MIDDLE);
+    rt_hook_complete_format_out(redwax_openssl_complete_format_out, NULL, NULL, APR_HOOK_MIDDLE);
+    rt_hook_set_format_out(redwax_openssl_set_format_out, NULL, NULL, APR_HOOK_MIDDLE);
     rt_hook_search_chain(redwax_openssl_search_chain, NULL, NULL, APR_HOOK_MIDDLE);
     rt_hook_search_key(redwax_openssl_search_key, NULL, NULL, APR_HOOK_MIDDLE);
     rt_hook_compare_certificate(redwax_openssl_compare_certificate, NULL, NULL, APR_HOOK_MIDDLE);

Modified: redwax-tool/trunk/redwax_p11kit.c
==============================================================================
--- redwax-tool/trunk/redwax_p11kit.c	(original)
+++ redwax-tool/trunk/redwax_p11kit.c	Mon Nov 22 00:06:12 2021
@@ -39,6 +39,8 @@
 #if HAVE_LIBGEN_H
 #include <libgen.h>
 #endif
+
+#include <stdlib.h>
 
 #if HAVE_P11_KIT_MODULES_LOAD_AND_INITIALIZE
 
@@ -332,6 +334,16 @@
     return APR_SUCCESS;
 }
 
+static apr_status_t cleanup_free(void *dummy)
+{
+
+    if (dummy) {
+        free(dummy);
+    }
+
+    return APR_SUCCESS;
+}
+
 static apr_status_t redwax_p11kit_initialise(redwax_tool_t *r)
 {
     global_modules =
@@ -1089,6 +1101,59 @@
     return ret;
 }
 
+static const char *redwax_p11kit_origin(redwax_tool_t *r, apr_pool_t *pool,
+        CK_FUNCTION_LIST *module, CK_TOKEN_INFO *tokenInfo,
+                CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object)
+{
+    P11KitUri *origin_uri;
+    CK_TOKEN_INFO_PTR ck_token_info;
+    char *origin;
+
+    CK_ATTRIBUTE template[] = {
+      {CKA_ID, NULL_PTR, 0},
+      {CKA_LABEL, NULL_PTR, 0},
+      {CKA_CLASS, NULL_PTR, 0}
+    };
+
+    int template_len = 3;
+    int ret;
+
+    origin_uri = p11_kit_uri_new();
+
+    apr_pool_cleanup_register(r->pool, origin_uri, cleanup_p11kituri,
+            apr_pool_cleanup_null);
+
+    ret = redwax_p11kit_read_attributes(pool, module, session, object,
+            template, template_len);
+    if (ret != CKR_OK) {
+        return NULL;
+    }
+
+    /* set token */
+    ck_token_info = p11_kit_uri_get_token_info(origin_uri);
+    memcpy(ck_token_info->label, tokenInfo->label, sizeof(tokenInfo->label));
+
+    /* set id */
+    p11_kit_uri_set_attribute(origin_uri, &template[0]);
+
+    /* set object */
+    p11_kit_uri_set_attribute(origin_uri, &template[1]);
+
+    /* set type */
+    p11_kit_uri_set_attribute(origin_uri, &template[2]);
+
+    if (P11_KIT_URI_OK
+            == p11_kit_uri_format(origin_uri, P11_KIT_URI_FOR_ANY, &origin)) {
+
+        apr_pool_cleanup_register(r->pool, origin, cleanup_free,
+                apr_pool_cleanup_null);
+
+        return origin;
+    }
+
+    return NULL;
+}
+
 static apr_status_t redwax_p11kit_read_slot(redwax_tool_t *r,
         P11KitUri *parsed, CK_FUNCTION_LIST *module, CK_TOKEN_INFO *tokenInfo,
         CK_SLOT_ID_PTR slot_id, apr_hash_t *secrets)
@@ -1230,6 +1295,9 @@
                     cert->len = cert_template[0].ulValueLen;
 
                     rt_run_normalise_certificate(r, cert, 1);
+
+                    cert->origin = redwax_p11kit_origin(r, pool, module,
+                            tokenInfo, session, object);
 
                     if (REDWAX_CERTIFICATE_ROOT
                             == cert->common.category && trusted) {
@@ -1455,6 +1523,9 @@
                                     key->common.id_len, REDWAX_ENCODE_LOWER, NULL));
 
                 }
+
+                key->origin = redwax_p11kit_origin(r, pool, module, tokenInfo,
+                        session, object);
 
             }
             else {

Modified: redwax-tool/trunk/redwax_util.c
==============================================================================
--- redwax-tool/trunk/redwax_util.c	(original)
+++ redwax-tool/trunk/redwax_util.c	Mon Nov 22 00:06:12 2021
@@ -21,8 +21,11 @@
 
 #include "redwax_util.h"
 
+#include <apr.h>
 #include <apr_lib.h>
 #include <apr_strings.h>
+
+#include <stdlib.h>
 
 int redwax_certcmp(redwax_certificate_t *cert1, redwax_certificate_t *cert2)
 {
@@ -542,3 +545,711 @@
     return NULL;
 }
 
+#define XML_PREAMBLE "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+
+static apr_status_t cleanup_metadata(void *dummy)
+{
+    if (dummy) {
+        redwax_metadata_t *m = dummy;
+        free(m->prefix);
+    }
+
+    return APR_SUCCESS;
+}
+
+static void redwax_metadata_prefix(redwax_metadata_t *m, int diff)
+{
+    int i = m->prefix_len;
+
+    m->prefix_len += diff * 2;
+
+    if (m->prefix_len < 0) {
+        m->prefix_len = 0;
+    }
+
+    if (m->prefix) {
+        m->prefix = realloc(m->prefix, m->prefix_len);
+
+        if (m->prefix) {
+            for (; i < m->prefix_len; i++) {
+                m->prefix[i] = ' ';
+            }
+        }
+    }
+}
+
+apr_status_t redwax_metadata_push_root(apr_pool_t *pool, const char *k,
+        apr_status_t (*wv)(void *ctx, const struct iovec *vec, apr_size_t nvec),
+        void *ctx, redwax_format_e format, redwax_metadata_t **mm)
+{
+    redwax_metadata_t *m = apr_pcalloc(pool, sizeof(redwax_metadata_t));
+
+    redwax_metadata_level_t *ml;
+
+    apr_pool_create(&m->pool, pool);
+
+    m->levels = apr_array_make(m->pool, 16, sizeof(redwax_metadata_level_t));
+    m->wv = wv;
+    m->ctx = ctx;
+    m->format = format;
+    m->prefix = malloc(0);
+
+    apr_pool_cleanup_register(pool, m, cleanup_metadata,
+            apr_pool_cleanup_null);
+
+    *mm = m;
+
+    m->level = ml = apr_array_push(m->levels);
+    m->level->k = (void *)k; // escape?
+    m->level->klen = k ? strlen(k) : 0;
+    m->level->root = 1;
+    m->level->object = 1;
+
+    switch (m->format) {
+    case REDWAX_FORMAT_TEXT:
+        break;
+    case REDWAX_FORMAT_XML: {
+
+        const struct iovec vec[] = {
+                {XML_PREAMBLE, strlen(XML_PREAMBLE)},
+                {"<", 1},
+                {ml->k, ml->klen},
+                {">", 1},
+        };
+
+        return m->wv(ctx, vec, 4);
+    }
+    case REDWAX_FORMAT_JSON: {
+
+        const struct iovec vec[] = {
+                {"{", 1}
+        };
+
+        return wv(m->ctx, vec, 1);
+    }
+    case REDWAX_FORMAT_YAML: {
+
+        const struct iovec vec[] = {
+                {"---\n", 4},
+                {ml->k, ml->klen},
+                {": ", 2}
+        };
+
+        return wv(m->ctx, vec, 3);
+    }
+    default:
+        break;
+    }
+
+    return APR_SUCCESS;
+}
+
+apr_status_t redwax_metadata_pop_root(redwax_metadata_t *m)
+{
+    redwax_metadata_level_t *ml = apr_array_pop(m->levels);
+
+    if (!ml || !ml->root) {
+        return APR_EGENERAL;
+    }
+
+    switch (m->format) {
+    case REDWAX_FORMAT_TEXT:
+        break;
+    case REDWAX_FORMAT_XML: {
+
+        const struct iovec vec[] = {
+                {"\n", 1},
+                {m->prefix, m->prefix_len},
+                {"</", 2},
+                {ml->k, ml->klen},
+                {">\n", 2},
+        };
+
+        return m->wv(m->ctx, vec, 5);
+    }
+    case REDWAX_FORMAT_JSON: {
+
+        const struct iovec vec[] = {
+                {"\n", 1},
+                {m->prefix, m->prefix_len},
+                {"}\n", 2}
+        };
+
+        return m->wv(m->ctx, vec, 3);
+    }
+    case REDWAX_FORMAT_YAML: {
+
+        const struct iovec vec[] = {
+                {"\n...\n", 5}
+        };
+
+        return m->wv(m->ctx, vec, 1);
+    }
+    default:
+        break;
+    }
+
+    return APR_SUCCESS;
+}
+
+apr_status_t redwax_metadata_push_array(redwax_metadata_t *m, const char *k, int empty)
+{
+    redwax_metadata_level_t *ml = m->level;
+
+    int array = ml->array;
+    int object = ml->object;
+    int next = ml->next;
+
+    m->level->next = 1;
+
+    m->level = ml = apr_array_push(m->levels);
+    m->level->k = (void *)k;
+    m->level->klen = k ? strlen(k) : 0;
+    m->level->empty = empty;
+    m->level->array = 1;
+    m->level->object = 0;
+    m->level->next = 0;
+    m->level->root = 0;
+
+    redwax_metadata_prefix(m, 1);
+
+    switch (m->format) {
+    case REDWAX_FORMAT_TEXT:
+        break;
+    case REDWAX_FORMAT_XML: {
+        if (empty) {
+
+            const struct iovec vec[] = {
+                    {"\n", 1},
+                    {m->prefix, m->prefix_len},
+                    {"<", 1},
+                    {ml->k, ml->klen},
+                    {" />\n", 4},
+            };
+
+            return m->wv(m->ctx, vec, 5);
+        }
+        else {
+
+            const struct iovec vec[] = {
+                    {"\n", 1},
+                    {m->prefix, m->prefix_len},
+                    {"<", 1},
+                    {ml->k, ml->klen},
+                    {">", 1},
+            };
+
+            return m->wv(m->ctx, vec, 5);
+        }
+
+        break;
+    }
+    case REDWAX_FORMAT_JSON: {
+
+        if (object) {
+
+            const struct iovec vec[] = {
+                    {next ? "," : "", next ? 1 : 0},
+                    {"\n", 1},
+                    {m->prefix, m->prefix_len},
+                    {"\"", 1},
+                    {ml->k, ml->klen},
+                    {"\": [", 4}
+            };
+
+            return m->wv(m->ctx, vec, 6);
+        }
+        else if (array) {
+
+            const struct iovec vec[] = {
+                    {next ? "," : "", next ? 1 : 0},
+                    {"\n", 1},
+                    {m->prefix, m->prefix_len},
+                    {"[", 1}
+            };
+
+            return m->wv(m->ctx, vec, 4);
+        }
+
+        break;
+    }
+    case REDWAX_FORMAT_YAML: {
+
+        if (object) {
+
+            const struct iovec vec[] = {
+                    {"\n", 1},
+                    {m->prefix, m->prefix_len},
+                    {ml->k, ml->klen},
+                    {": ", 2}
+            };
+
+            return m->wv(m->ctx, vec, 4);
+        }
+        else if (array) {
+
+            const struct iovec vec[] = {
+                    {"\n", 1},
+                    {ml->k, ml->klen},
+                    {": ", 2}
+            };
+
+            return m->wv(m->ctx, vec, 3);
+        }
+
+        break;
+    }
+    default:
+        break;
+    }
+
+    return APR_SUCCESS;
+}
+
+apr_status_t redwax_metadata_pop_array(redwax_metadata_t *m)
+{
+    redwax_metadata_level_t *ml = apr_array_pop(m->levels);
+
+    char *prefix = m->prefix;
+    int prefix_len = m->prefix_len;
+
+    int empty = m->level->empty;
+
+    if (!ml->array) {
+        return APR_EGENERAL;
+    }
+
+    if (m->levels->nelts) {
+        m->level = &APR_ARRAY_IDX(m->levels, m->levels->nelts - 1,
+                redwax_metadata_level_t);
+    }
+
+    redwax_metadata_prefix(m, -1);
+
+    switch (m->format) {
+    case REDWAX_FORMAT_TEXT:
+        break;
+    case REDWAX_FORMAT_XML: {
+
+        if (empty) {
+
+            return APR_SUCCESS;
+        }
+        else {
+
+            const struct iovec vec[] = {
+                    {"\n", 1},
+                    {prefix, prefix_len},
+                    {"</", 2},
+                    {ml->k, ml->klen},
+                    {">", 1},
+            };
+
+            return m->wv(m->ctx, vec, 5);
+        }
+
+        break;
+    }
+    case REDWAX_FORMAT_JSON: {
+
+        if (empty) {
+
+            const struct iovec vec[] = {
+                    {"]", 1}
+            };
+
+            return m->wv(m->ctx, vec, 1);
+        }
+        else {
+
+            const struct iovec vec[] = {
+                    {"\n", 1},
+                    {prefix, prefix_len},
+                    {"]", 1}
+            };
+
+            return m->wv(m->ctx, vec, 3);
+        }
+
+        break;
+    }
+    default:
+        break;
+    }
+
+    return APR_SUCCESS;
+}
+
+apr_status_t redwax_metadata_push_object(redwax_metadata_t *m, const char *k, int empty)
+{
+    redwax_metadata_level_t *ml = m->level;
+
+    int array = ml->array;
+    int object = ml->object;
+    int next = ml->next;
+
+    m->level->next = 1;
+
+    m->level = ml = apr_array_push(m->levels);
+    m->level->k = (void *)k;
+    m->level->klen = k ? strlen(k) : 0;
+    m->level->empty = empty;
+    m->level->array = 0;
+    m->level->object = 1;
+    m->level->next = 0;
+    m->level->root = 0;
+
+    redwax_metadata_prefix(m, 1);
+
+    switch (m->format) {
+    case REDWAX_FORMAT_TEXT:
+        break;
+    case REDWAX_FORMAT_XML: {
+        if (empty) {
+
+            const struct iovec vec[] = {
+                    {"\n", 1},
+                    {m->prefix, m->prefix_len},
+                    {"<", 1},
+                    {ml->k, ml->klen},
+                    {" />\n", 4},
+            };
+
+            return m->wv(m->ctx, vec, 5);
+        }
+        else if (array) {
+
+            const struct iovec vec[] = {
+                    {"\n", 1},
+                    {m->prefix, m->prefix_len},
+                    {"<", 1},
+                    {ml->k, ml->klen},
+                    {">", 1},
+            };
+
+            return m->wv(m->ctx, vec, 5);
+        }
+
+        break;
+    }
+    case REDWAX_FORMAT_JSON: {
+
+        if (object) {
+
+            const struct iovec vec[] = {
+                    {next ? "," : "", next ? 1 : 0},
+                    {"\n", 1},
+                    {m->prefix, m->prefix_len},
+                    {"\"", 1},
+                    {ml->k, ml->klen},
+                    {"\": {", 4}
+            };
+
+            return m->wv(m->ctx, vec, 6);
+        }
+        else if (array) {
+
+            const struct iovec vec[] = {
+                    {next ? "," : "", next ? 1 : 0},
+                    {"\n", 1},
+                    {m->prefix, m->prefix_len},
+                    {"{", 1}
+            };
+
+            return m->wv(m->ctx, vec, 4);
+        }
+
+        break;
+    }
+    case REDWAX_FORMAT_YAML: {
+
+        if (object) {
+
+            const struct iovec vec[] = {
+                    {"\n", 1},
+                    {m->prefix, m->prefix_len},
+                    {ml->k, ml->klen},
+                    {": ", 2}
+            };
+
+            return m->wv(m->ctx, vec, 4);
+        }
+        else if (array) {
+
+            const struct iovec vec[] = {
+                    {"\n", 1},
+                    {m->prefix, m->prefix_len},
+                    {"- ", 2}
+            };
+
+            return m->wv(m->ctx, vec, 3);
+        }
+
+        break;
+    }
+    default:
+        break;
+    }
+
+    return APR_SUCCESS;
+}
+
+apr_status_t redwax_metadata_pop_object(redwax_metadata_t *m)
+{
+    redwax_metadata_level_t *ml = apr_array_pop(m->levels);
+
+    char *prefix = m->prefix;
+    int prefix_len = m->prefix_len;
+
+    int empty = m->level->empty;
+
+    if (!ml->object) {
+        return APR_EGENERAL;
+    }
+
+    if (m->levels->nelts) {
+        m->level = &APR_ARRAY_IDX(m->levels, m->levels->nelts - 1,
+                redwax_metadata_level_t);
+    }
+
+    redwax_metadata_prefix(m, -1);
+
+    switch (m->format) {
+    case REDWAX_FORMAT_TEXT:
+        break;
+    case REDWAX_FORMAT_XML: {
+
+        if (empty) {
+
+            return APR_SUCCESS;
+        }
+        else {
+
+            const struct iovec vec[] = {
+                    {"\n", 1},
+                    {prefix, prefix_len},
+                    {"</", 2},
+                    {ml->k, ml->klen},
+                    {">", 1},
+            };
+
+            return m->wv(m->ctx, vec, 5);
+        }
+
+        break;
+    }
+    case REDWAX_FORMAT_JSON: {
+
+        if (empty) {
+
+            const struct iovec vec[] = {
+                    {"}", 1}
+            };
+
+            return m->wv(m->ctx, vec, 1);
+        }
+        else {
+
+            const struct iovec vec[] = {
+                    {"\n", 1},
+                    {prefix, prefix_len},
+                    {"}", 1}
+            };
+
+            return m->wv(m->ctx, vec, 3);
+        }
+
+        break;
+    }
+    default:
+        break;
+    }
+
+    return APR_SUCCESS;
+}
+
+apr_status_t redwax_metadata_add_string(redwax_metadata_t *m, const char *key, const char *val)
+{
+    redwax_metadata_level_t *ml = m->level;
+
+    apr_status_t status = APR_SUCCESS;
+
+    void *k = (void *)key;
+    int klen = k ? strlen(k) : 0;
+    void *v = (void *)val;
+    int vlen = v ? strlen(v) : 0;
+
+    int array = ml->array;
+    int object = ml->object;
+    int next = ml->next;
+
+    ml->next = 1;
+
+    redwax_metadata_prefix(m, 1);
+
+    switch (m->format) {
+    case REDWAX_FORMAT_TEXT:
+        break;
+    case REDWAX_FORMAT_XML: {
+// FIXME: handle escaping of the value
+        if (!v) {
+
+            const struct iovec vec[] = {
+                    {"\n", 1},
+                    {m->prefix, m->prefix_len},
+                    {"<", 1},
+                    {k, klen},
+                    {" />", 3},
+            };
+
+            status = m->wv(m->ctx, vec, 5);
+
+        }
+        else {
+
+            const struct iovec vec[] = {
+                    {"\n", 1},
+                    {m->prefix, m->prefix_len},
+                    {"<", 1},
+                    {k, klen},
+                    {">", 1},
+                    {v, vlen},
+                    {"</", 2},
+                    {k, klen},
+                    {">", 1},
+            };
+
+            status = m->wv(m->ctx, vec, 9);
+        }
+
+        break;
+    }
+    case REDWAX_FORMAT_JSON: {
+
+        if (object) {
+
+            if (!v) {
+
+                const struct iovec vec[] = {
+                        {next ? "," : "", next ? 1 : 0},
+                        {"\n", 1},
+                        {m->prefix, m->prefix_len},
+                        {"\"", 1},
+                        {k, klen},
+                        {"\": null", 7}
+                };
+
+                status = m->wv(m->ctx, vec, 6);
+            }
+            else {
+
+                const struct iovec vec[] = {
+                        {next ? "," : "", next ? 1 : 0},
+                        {"\n", 1},
+                        {m->prefix, m->prefix_len},
+                        {"\"", 1},
+                        {k, klen},
+                        {"\": \"", 4},
+                        {v, vlen},
+                        {"\"", 1}
+                };
+
+                status = m->wv(m->ctx, vec, 8);
+            }
+
+        }
+        else if (array) {
+
+            if (!v) {
+
+                const struct iovec vec[] = {
+                        {next ? "," : "", next ? 1 : 0},
+                        {"\n", 1},
+                        {m->prefix, m->prefix_len},
+                        {"null", 4}
+                };
+
+                status = m->wv(m->ctx, vec, 4);
+            }
+            else {
+
+                const struct iovec vec[] = {
+                        {next ? "," : "", next ? 1 : 0},
+                        {"\n", 1},
+                        {m->prefix, m->prefix_len},
+                        {"\"", 1},
+                        {v, vlen},
+                        {"\"", 1}
+                };
+
+                status = m->wv(m->ctx, vec, 6);
+            }
+
+        }
+
+        break;
+    }
+    case REDWAX_FORMAT_YAML: {
+
+        if (object) {
+
+            if (!v) {
+
+                const struct iovec vec[] = {
+                        {"\n", 1},
+                        {m->prefix, m->prefix_len},
+                        {k, klen},
+                        {": ", 2},
+                };
+
+                status = m->wv(m->ctx, vec, 4);
+            }
+            else {
+
+                const struct iovec vec[] = {
+                        {"\n", 1},
+                        {m->prefix, m->prefix_len},
+                        {k, klen},
+                        {": ", 2},
+                        {v, vlen},
+                };
+
+                status = m->wv(m->ctx, vec, 5);
+            }
+
+        }
+        else if (array) {
+// FIXME: yaml indentation still not 100%
+            if (!v) {
+
+                const struct iovec vec[] = {
+                        {"\n", 1},
+                        {k, klen},
+                        {":\n", 2}
+                };
+
+                status = m->wv(m->ctx, vec, 3);
+            }
+            else {
+
+                const struct iovec vec[] = {
+                        {"\n", 1},
+                        {k, klen},
+                        {":\n", 2}
+                };
+
+                status = m->wv(m->ctx, vec, 3);
+            }
+
+        }
+
+        break;
+    }
+    default:
+        break;
+    }
+
+    redwax_metadata_prefix(m, -1);
+
+    return APR_SUCCESS;
+}

Modified: redwax-tool/trunk/redwax_util.h
==============================================================================
--- redwax-tool/trunk/redwax_util.h	(original)
+++ redwax-tool/trunk/redwax_util.h	Mon Nov 22 00:06:12 2021
@@ -70,4 +70,48 @@
  */
 #define REDWAX_ENCODE_LOWER 32
 
+/**
+ * Keep track of nesting level.
+ */
+typedef struct redwax_metadata_level_t {
+    void *k;
+    int klen;
+    int root:1;
+    int object:1;
+    int array:1;
+    int empty:1;
+    int next:1;
+} redwax_metadata_level_t;
+
+/**
+ * A structure to hold metadata context.
+ */
+typedef struct redwax_metadata_t {
+    apr_pool_t *pool;
+    apr_array_header_t *levels;
+    redwax_metadata_level_t *level;
+    apr_status_t (*wv)(void *ctx, const struct iovec *vec,
+                    apr_size_t nvec);
+    void *ctx;
+    redwax_format_e format;
+    char *prefix;
+    int prefix_len;
+} redwax_metadata_t;
+
+apr_status_t redwax_metadata_push_root(apr_pool_t *pool, const char *k,
+        apr_status_t (*wv)(void *ctx, const struct iovec *vec, apr_size_t nvec),
+        void *ctx, redwax_format_e format, redwax_metadata_t **mm);
+
+apr_status_t redwax_metadata_pop_root(redwax_metadata_t *m);
+
+apr_status_t redwax_metadata_push_array(redwax_metadata_t *m, const char *k, int empty);
+
+apr_status_t redwax_metadata_pop_array(redwax_metadata_t *m);
+
+apr_status_t redwax_metadata_push_object(redwax_metadata_t *m, const char *k, int empty);
+
+apr_status_t redwax_metadata_pop_object(redwax_metadata_t *m);
+
+apr_status_t redwax_metadata_add_string(redwax_metadata_t *m, const char *k, const char *v);
+
 #endif



More information about the rs-commit mailing list