krb5 commit: Add k5_json_array_fmt and use it in export_cred.c

Greg Hudson ghudson at MIT.EDU
Wed Feb 13 15:53:43 EST 2013


https://github.com/krb5/krb5/commit/80f53c8b2c745e75dc9d22acba63812d8533c133
commit 80f53c8b2c745e75dc9d22acba63812d8533c133
Author: Greg Hudson <ghudson at mit.edu>
Date:   Mon Feb 11 21:07:54 2013 -0500

    Add k5_json_array_fmt and use it in export_cred.c
    
    Add a template-based array constructor for convenient marshalling of
    structured values as JSON array values.  Use it to simplify
    export_cred.c.

 src/include/k5-json.h                         |   18 +
 src/lib/gssapi/krb5/export_cred.c             |  534 +++++++++++++------------
 src/util/support/json.c                       |   93 +++++
 src/util/support/libkrb5support-fixed.exports |    2 +
 src/util/support/t_json.c                     |   26 ++
 5 files changed, 412 insertions(+), 261 deletions(-)

diff --git a/src/include/k5-json.h b/src/include/k5-json.h
index 4b9b8fe..b01b8b8 100644
--- a/src/include/k5-json.h
+++ b/src/include/k5-json.h
@@ -109,6 +109,9 @@ typedef struct k5_json_null_st *k5_json_null;
 
 int k5_json_null_create(k5_json_null *null_out);
 
+/* Create a null value as a k5_json_value, for polymorphic convenience. */
+int k5_json_null_create_val(k5_json_value *val_out);
+
 /*
  * Boolean
  */
@@ -136,6 +139,21 @@ void k5_json_array_set(k5_json_array array, size_t idx, k5_json_value val);
 k5_json_value k5_json_array_get(k5_json_array array, size_t idx);
 
 /*
+ * Create an array from a template and a variable argument list.  template
+ * characters are:
+ *   v: a k5_json_value argument is read, retained, and stored
+ *   n: no argument is read; a null value is stored
+ *   b: an int argument is read and stored as a boolean value
+ *   i: an int argument is read and stored as a number value
+ *   L: a long long argument is read and stored as a number value
+ *   s: a const char * argument is read and stored as a null or string value
+ *   B: const void * and size_t arguments are read and stored as a base64
+ *      string value
+ */
+int
+k5_json_array_fmt(k5_json_array *array_out, const char *template, ...);
+
+/*
  * Object
  */
 
diff --git a/src/lib/gssapi/krb5/export_cred.c b/src/lib/gssapi/krb5/export_cred.c
index e5ffaf5..652b260 100644
--- a/src/lib/gssapi/krb5/export_cred.c
+++ b/src/lib/gssapi/krb5/export_cred.c
@@ -34,387 +34,400 @@
 #include "k5-json.h"
 #include "gssapiP_krb5.h"
 
-/* Add v to array and then release it.  Return -1 if v is NULL. */
-static int
-add(k5_json_array array, k5_json_value v)
-{
-    if (v == NULL || k5_json_array_add(array, v))
-        return -1;
-    k5_json_release(v);
-    return 0;
-}
-
-static inline k5_json_number
-number(long long nval)
-{
-    k5_json_number num;
-
-    return k5_json_number_create(nval, &num) ? NULL : num;
-}
-
-static inline k5_json_string
-string(const char *cstring)
-{
-    k5_json_string str;
-
-    return k5_json_string_create(cstring, &str) ? NULL : str;
-}
-
-static inline k5_json_string
-base64string(const void *data, size_t len)
-{
-    k5_json_string str;
-
-    return k5_json_string_create_base64(data, len, &str) ? NULL : str;
-}
-
-static inline k5_json_null
-null(void)
-{
-    k5_json_null n;
-
-    return k5_json_null_create(&n) ? NULL : n;
-}
-
-static inline k5_json_bool
-bool(int truth)
-{
-    k5_json_bool b;
-
-    return k5_json_bool_create(truth, &b) ? NULL : b;
-}
-
-/* Return a JSON null or string value representing str. */
-static k5_json_value
-json_optional_string(const char *str)
-{
-    return (str == NULL) ? (k5_json_value)null() : string(str);
-}
-
 /* Return a JSON null or array value representing princ. */
-static k5_json_value
-json_principal(krb5_context context, krb5_principal princ)
+static krb5_error_code
+json_principal(krb5_context context, krb5_principal princ,
+               k5_json_value *val_out)
 {
+    krb5_error_code ret;
+    k5_json_string str = NULL;
     char *princname;
-    k5_json_string str;
 
+    *val_out = NULL;
     if (princ == NULL)
-        return null();
-    if (krb5_unparse_name(context, princ, &princname))
-        return NULL;
-    str = string(princname);
+        return k5_json_null_create_val(val_out);
+    ret = krb5_unparse_name(context, princ, &princname);
+    if (ret)
+        return ret;
+    ret = k5_json_string_create(princname, &str);
     krb5_free_unparsed_name(context, princname);
-    return str;
+    *val_out = str;
+    return ret;
 }
 
 /* Return a json null or array value representing etypes. */
-static k5_json_value
-json_etypes(krb5_enctype *etypes)
+static krb5_error_code
+json_etypes(krb5_enctype *etypes, k5_json_value *val_out)
 {
+    krb5_error_code ret;
+    k5_json_number num;
     k5_json_array array;
 
+    *val_out = NULL;
     if (etypes == NULL)
-        return null();
-    if (k5_json_array_create(&array))
-        return NULL;
+        return k5_json_null_create_val(val_out);
+    ret = k5_json_array_create(&array);
+    if (ret)
+        return ret;
     for (; *etypes != 0; etypes++) {
-        if (add(array, number(*etypes)))
-            goto oom;
+        ret = k5_json_number_create(*etypes, &num);
+        if (ret)
+            goto err;
+        ret = k5_json_array_add(array, num);
+        k5_json_release(num);
+        if (ret)
+            goto err;
     }
-    return array;
-oom:
+    *val_out = array;
+    return 0;
+err:
     k5_json_release(array);
-    return NULL;
+    return ret;
 }
 
 /* Return a JSON null or array value representing name. */
-static k5_json_value
-json_kgname(krb5_context context, krb5_gss_name_t name)
+static krb5_error_code
+json_kgname(krb5_context context, krb5_gss_name_t name, k5_json_value *val_out)
 {
-    k5_json_array array;
+    krb5_error_code ret;
+    k5_json_array array = NULL;
+    k5_json_value princ;
 
+    *val_out = NULL;
     if (name == NULL)
-        return null();
-    if (k5_json_array_create(&array))
-        return NULL;
-    if (add(array, json_principal(context, name->princ)))
-        goto oom;
-    if (add(array, json_optional_string(name->service)))
-        goto oom;
-    if (add(array, json_optional_string(name->host)))
-        goto oom;
-    return array;
-oom:
-    k5_json_release(array);
-    return NULL;
+        return k5_json_null_create_val(val_out);
+    ret = json_principal(context, name->princ, &princ);
+    if (ret)
+        return ret;
+    ret = k5_json_array_fmt(&array, "vss", princ, name->service, name->host);
+    k5_json_release(princ);
+    *val_out = array;
+    return ret;
 }
 
 /* Return a JSON null or string value representing keytab. */
-static k5_json_value
-json_keytab(krb5_context context, krb5_keytab keytab)
+static krb5_error_code
+json_keytab(krb5_context context, krb5_keytab keytab, k5_json_value *val_out)
 {
+    krb5_error_code ret;
+    k5_json_string str;
     char name[1024];
 
+    *val_out = NULL;
     if (keytab == NULL)
-        return null();
-    if (krb5_kt_get_name(context, keytab, name, sizeof(name)))
-        return NULL;
-    return string(name);
+        return k5_json_null_create_val(val_out);
+    ret = krb5_kt_get_name(context, keytab, name, sizeof(name));
+    if (ret)
+        return ret;
+    ret = k5_json_string_create(name, &str);
+    *val_out = str;
+    return ret;
 }
 
 /* Return a JSON null or string value representing rcache. */
-static k5_json_value
-json_rcache(krb5_context context, krb5_rcache rcache)
+static krb5_error_code
+json_rcache(krb5_context context, krb5_rcache rcache, k5_json_value *val_out)
 {
+    krb5_error_code ret;
+    k5_json_string str = NULL;
     char *name;
-    k5_json_string str;
 
     if (rcache == NULL)
-        return null();
+        return k5_json_null_create_val(val_out);
     if (asprintf(&name, "%s:%s", krb5_rc_get_type(context, rcache),
                  krb5_rc_get_name(context, rcache)) < 0)
-        return NULL;
-    str = string(name);
+        return ENOMEM;
+    ret = k5_json_string_create(name, &str);
     free(name);
-    return str;
+    *val_out = str;
+    return ret;
 }
 
 /* Return a JSON array value representing keyblock. */
-static k5_json_value
-json_keyblock(krb5_keyblock *keyblock)
+static krb5_error_code
+json_keyblock(krb5_keyblock *kb, k5_json_value *val_out)
 {
+    krb5_error_code ret;
     k5_json_array array;
 
-    if (k5_json_array_create(&array))
-        return NULL;
-    if (add(array, number(keyblock->enctype)))
-        goto oom;
-    if (add(array, base64string(keyblock->contents, keyblock->length)))
-        goto oom;
-    return array;
-oom:
-    k5_json_release(array);
-    return NULL;
+    *val_out = NULL;
+    ret = k5_json_array_fmt(&array, "iB", kb->enctype, (void *)kb->contents,
+                            (size_t)kb->length);
+    if (ret)
+        return ret;
+    *val_out = array;
+    return 0;
 }
 
 /* Return a JSON array value representing addr. */
-static k5_json_value
-json_address(krb5_address *addr)
+static krb5_error_code
+json_address(krb5_address *addr, k5_json_value *val_out)
 {
+    krb5_error_code ret;
     k5_json_array array;
 
-    if (k5_json_array_create(&array))
-        return NULL;
-    if (add(array, number(addr->addrtype)))
-        goto oom;
-    if (add(array, base64string(addr->contents, addr->length)))
-        goto oom;
-    return array;
-oom:
-    k5_json_release(array);
-    return NULL;
+    *val_out = NULL;
+    ret = k5_json_array_fmt(&array, "iB", addr->addrtype,
+                            (void *)addr->contents, (size_t)addr->length);
+    if (ret)
+        return ret;
+    *val_out = array;
+    return 0;
 }
 
 /* Return a JSON null or array value representing addrs. */
-static k5_json_value
-json_addresses(krb5_address **addrs)
+static krb5_error_code
+json_addresses(krb5_address **addrs, k5_json_value *val_out)
 {
+    krb5_error_code ret;
     k5_json_array array;
+    k5_json_value val;
 
+    *val_out = NULL;
     if (addrs == NULL)
-        return null();
-    if (k5_json_array_create(&array))
-        return NULL;
+        return k5_json_null_create_val(val_out);
+    ret = k5_json_array_create(&array);
+    if (ret)
+        return ret;
     for (; *addrs != NULL; addrs++) {
-        if (add(array, json_address(*addrs))) {
-            k5_json_release(array);
-            return NULL;
-        }
+        ret = json_address(*addrs, &val);
+        if (ret)
+            goto err;
+        ret = k5_json_array_add(array, val);
+        k5_json_release(val);
+        if (ret)
+            goto err;
     }
-    return array;
+    *val_out = array;
+    return 0;
+err:
+    k5_json_release(array);
+    return ret;
 }
 
 /* Return a JSON array value representing ad. */
-static k5_json_value
-json_authdata_element(krb5_authdata *ad)
+static krb5_error_code
+json_authdata_element(krb5_authdata *ad, k5_json_value *val_out)
 {
+    krb5_error_code ret;
     k5_json_array array;
 
-    if (k5_json_array_create(&array))
-        return NULL;
-    if (add(array, number(ad->ad_type)))
-        goto oom;
-    if (add(array, base64string(ad->contents, ad->length)))
-        goto oom;
-    return array;
-oom:
-    k5_json_release(array);
-    return NULL;
+    *val_out = NULL;
+    ret = k5_json_array_fmt(&array, "iB", ad->ad_type, (void *)ad->contents,
+                            (size_t)ad->length);
+    if (ret)
+        return ret;
+    *val_out = array;
+    return 0;
 }
 
 /* Return a JSON null or array value representing authdata. */
-static k5_json_value
-json_authdata(krb5_authdata **authdata)
+static krb5_error_code
+json_authdata(krb5_authdata **authdata, k5_json_value *val_out)
 {
+    krb5_error_code ret;
     k5_json_array array;
+    k5_json_value val;
 
+    *val_out = NULL;
     if (authdata == NULL)
-        return null();
-    if (k5_json_array_create(&array))
-        return NULL;
+        return k5_json_null_create_val(val_out);
+    ret = k5_json_array_create(&array);
+    if (ret)
+        return ret;
     for (; *authdata != NULL; authdata++) {
-        if (add(array, json_authdata_element(*authdata))) {
-            k5_json_release(array);
-            return NULL;
-        }
+        ret = json_authdata_element(*authdata, &val);
+        if (ret)
+            goto err;
+        ret = k5_json_array_add(array, val);
+        k5_json_release(val);
+        if (ret)
+            goto err;
     }
-    return array;
+    *val_out = array;
+    return 0;
+err:
+    k5_json_release(array);
+    return ret;
 }
 
 /* Return a JSON array value representing creds. */
-static k5_json_value
-json_creds(krb5_context context, krb5_creds *creds)
+static krb5_error_code
+json_creds(krb5_context context, krb5_creds *creds, k5_json_value *val_out)
 {
+    krb5_error_code ret;
     k5_json_array array;
+    k5_json_value client = NULL, server = NULL, keyblock = NULL, addrs = NULL;
+    k5_json_value authdata = NULL;
+
+    *val_out = NULL;
+    ret = json_principal(context, creds->client, &client);
+    if (ret)
+        goto cleanup;
+    ret = json_principal(context, creds->server, &server);
+    if (ret)
+        goto cleanup;
+    ret = json_keyblock(&creds->keyblock, &keyblock);
+    if (ret)
+        goto cleanup;
+    ret = json_addresses(creds->addresses, &addrs);
+    if (ret)
+        goto cleanup;
+    ret = json_authdata(creds->authdata, &authdata);
+    if (ret)
+        goto cleanup;
+
+    ret = k5_json_array_fmt(&array, "vvviiiibivBBv", client, server, keyblock,
+                            creds->times.authtime, creds->times.starttime,
+                            creds->times.endtime, creds->times.renew_till,
+                            creds->is_skey, creds->ticket_flags, addrs,
+                            (void *)creds->ticket.data,
+                            (size_t)creds->ticket.length,
+                            (void *)creds->second_ticket.data,
+                            (size_t)creds->second_ticket.length, authdata);
+    if (ret)
+        goto cleanup;
+    *val_out = array;
 
-    if (k5_json_array_create(&array))
-        return NULL;
-    if (add(array, json_principal(context, creds->client)))
-        goto eom;
-    if (add(array, json_principal(context, creds->server)))
-        goto eom;
-    if (add(array, json_keyblock(&creds->keyblock)))
-        goto eom;
-    if (add(array, number(creds->times.authtime)))
-        goto eom;
-    if (add(array, number(creds->times.starttime)))
-        goto eom;
-    if (add(array, number(creds->times.endtime)))
-        goto eom;
-    if (add(array, number(creds->times.renew_till)))
-        goto eom;
-    if (add(array, bool(creds->is_skey)))
-        goto eom;
-    if (add(array, number(creds->ticket_flags)))
-        goto eom;
-    if (add(array, json_addresses(creds->addresses)))
-        goto eom;
-    if (add(array, base64string(creds->ticket.data, creds->ticket.length)))
-        goto eom;
-    if (add(array, base64string(creds->second_ticket.data,
-                                creds->second_ticket.length)))
-        goto eom;
-    if (add(array, json_authdata(creds->authdata)))
-        goto eom;
-    return array;
-eom:
-    k5_json_release(array);
-    return NULL;
+cleanup:
+    k5_json_release(client);
+    k5_json_release(server);
+    k5_json_release(keyblock);
+    k5_json_release(addrs);
+    k5_json_release(authdata);
+    return ret;
 }
 
 /* Return a JSON array value representing the contents of ccache. */
-static k5_json_value
-json_ccache_contents(krb5_context context, krb5_ccache ccache)
+static krb5_error_code
+json_ccache_contents(krb5_context context, krb5_ccache ccache,
+                     k5_json_value *val_out)
 {
     krb5_error_code ret;
     krb5_principal princ;
     krb5_cc_cursor cursor;
     krb5_creds creds;
     k5_json_array array;
-    int st;
+    k5_json_value val;
 
-    if (k5_json_array_create(&array))
-        return NULL;
+    *val_out = NULL;
+    ret = k5_json_array_create(&array);
+    if (ret)
+        return ret;
 
     /* Put the principal in the first array entry. */
-    if (krb5_cc_get_principal(context, ccache, &princ))
+    ret = krb5_cc_get_principal(context, ccache, &princ);
+    if (ret)
         goto err;
-    st = add(array, json_principal(context, princ));
+    ret = json_principal(context, princ, &val);
     krb5_free_principal(context, princ);
-    if (st)
+    if (ret)
+        goto err;
+    ret = k5_json_array_add(array, val);
+    k5_json_release(val);
+    if (ret)
         goto err;
 
     /* Put credentials in the remaining array entries. */
-    if (krb5_cc_start_seq_get(context, ccache, &cursor))
+    ret = krb5_cc_start_seq_get(context, ccache, &cursor);
+    if (ret)
         goto err;
     while ((ret = krb5_cc_next_cred(context, ccache, &cursor, &creds)) == 0) {
-        if (add(array, json_creds(context, &creds))) {
-            krb5_free_cred_contents(context, &creds);
-            break;
-        }
+        ret = json_creds(context, &creds, &val);
         krb5_free_cred_contents(context, &creds);
+        if (ret)
+            break;
+        ret = k5_json_array_add(array, val);
+        k5_json_release(val);
+        if (ret)
+            break;
     }
     krb5_cc_end_seq_get(context, ccache, &cursor);
     if (ret != KRB5_CC_END)
         goto err;
-    return array;
+    *val_out = array;
+    return 0;
 
 err:
     k5_json_release(array);
-    return NULL;
+    return ret;
 }
 
 /* Return a JSON null, string, or array value representing ccache. */
-static k5_json_value
-json_ccache(krb5_context context, krb5_ccache ccache)
+static krb5_error_code
+json_ccache(krb5_context context, krb5_ccache ccache, k5_json_value *val_out)
 {
-    char *name;
+    krb5_error_code ret;
     k5_json_string str;
+    char *name;
 
+    *val_out = NULL;
     if (ccache == NULL)
-        return null();
+        return k5_json_null_create_val(val_out);
     if (strcmp(krb5_cc_get_type(context, ccache), "MEMORY") == 0) {
-        return json_ccache_contents(context, ccache);
+        return json_ccache_contents(context, ccache, val_out);
     } else {
-        if (krb5_cc_get_full_name(context, ccache, &name))
-            return NULL;
-        str = string(name);
+        ret = krb5_cc_get_full_name(context, ccache, &name);
+        if (ret)
+            return ret;
+        ret = k5_json_string_create(name, &str);
         free(name);
-        return str;
+        *val_out = str;
+        return ret;
     }
 }
 
 /* Return a JSON array value representing cred. */
-static k5_json_value
-json_kgcred(krb5_context context, krb5_gss_cred_id_t cred)
+static krb5_error_code
+json_kgcred(krb5_context context, krb5_gss_cred_id_t cred,
+            k5_json_value *val_out)
 {
+    krb5_error_code ret;
     k5_json_array array;
+    k5_json_value name = NULL, imp = NULL, keytab = NULL, rcache = NULL;
+    k5_json_value ccache = NULL, ckeytab = NULL, etypes = NULL;
+
+    *val_out = NULL;
+    ret = json_kgname(context, cred->name, &name);
+    if (ret)
+        goto cleanup;
+    ret = json_principal(context, cred->impersonator, &imp);
+    if (ret)
+        goto cleanup;
+    ret = json_keytab(context, cred->keytab, &keytab);
+    if (ret)
+        goto cleanup;
+    ret = json_rcache(context, cred->rcache, &rcache);
+    if (ret)
+        goto cleanup;
+    ret = json_ccache(context, cred->ccache, &ccache);
+    if (ret)
+        goto cleanup;
+    ret = json_keytab(context, cred->client_keytab, &ckeytab);
+    if (ret)
+        goto cleanup;
+    ret = json_etypes(cred->req_enctypes, &etypes);
+    if (ret)
+        goto cleanup;
+
+    ret = k5_json_array_fmt(&array, "ivvbbvvvvbiivs", cred->usage, name, imp,
+                            cred->default_identity, cred->iakerb_mech, keytab,
+                            rcache, ccache, ckeytab, cred->have_tgt,
+                            cred->expire, cred->refresh_time, etypes,
+                            cred->password);
+    if (ret)
+        goto cleanup;
+    *val_out = array;
 
-    if (k5_json_array_create(&array))
-        return NULL;
-    if (add(array, number(cred->usage)))
-        goto oom;
-    if (add(array, json_kgname(context, cred->name)))
-        goto oom;
-    if (add(array, json_principal(context, cred->impersonator)))
-        goto oom;
-    if (add(array, bool(cred->default_identity)))
-        goto oom;
-    if (add(array, bool(cred->iakerb_mech)))
-        goto oom;
-    /* Don't marshal cred->destroy_ccache. */
-    if (add(array, json_keytab(context, cred->keytab)))
-        goto oom;
-    if (add(array, json_rcache(context, cred->rcache)))
-        goto oom;
-    if (add(array, json_ccache(context, cred->ccache)))
-        goto oom;
-    if (add(array, json_keytab(context, cred->client_keytab)))
-        goto oom;
-    if (add(array, bool(cred->have_tgt)))
-        goto oom;
-    if (add(array, number(cred->expire)))
-        goto oom;
-    if (add(array, number(cred->refresh_time)))
-        goto oom;
-    if (add(array, json_etypes(cred->req_enctypes)))
-        goto oom;
-    if (add(array, json_optional_string(cred->password)))
-        goto oom;
-    return array;
-oom:
-    k5_json_release(array);
-    return NULL;
+cleanup:
+    k5_json_release(name);
+    k5_json_release(imp);
+    k5_json_release(keytab);
+    k5_json_release(rcache);
+    k5_json_release(ccache);
+    k5_json_release(ckeytab);
+    k5_json_release(etypes);
+    return ret;
 }
 
 OM_uint32 KRB5_CALLCONV
@@ -426,6 +439,7 @@ krb5_gss_export_cred(OM_uint32 *minor_status, gss_cred_id_t cred_handle,
     krb5_error_code ret;
     krb5_gss_cred_id_t cred;
     k5_json_array array = NULL;
+    k5_json_value jcred = NULL;
     char *str = NULL;
     krb5_data d;
 
@@ -441,13 +455,10 @@ krb5_gss_export_cred(OM_uint32 *minor_status, gss_cred_id_t cred_handle,
         return status;
     cred = (krb5_gss_cred_id_t)cred_handle;
 
-    if (k5_json_array_create(&array))
-        goto oom;
-    if (add(array, string(CRED_EXPORT_MAGIC)))
+    if (json_kgcred(context, cred, &jcred))
         goto oom;
-    if (add(array, json_kgcred(context, cred)))
+    if (k5_json_array_fmt(&array, "sv", CRED_EXPORT_MAGIC, jcred))
         goto oom;
-
     if (k5_json_encode(array, &str))
         goto oom;
     d = string2data(str);
@@ -459,6 +470,7 @@ cleanup:
     free(str);
     k5_mutex_unlock(&cred->lock);
     k5_json_release(array);
+    k5_json_release(jcred);
     krb5_free_context(context);
     return status;
 
diff --git a/src/util/support/json.c b/src/util/support/json.c
index 5151b84..e3edffd 100644
--- a/src/util/support/json.c
+++ b/src/util/support/json.c
@@ -167,6 +167,13 @@ k5_json_null_create(k5_json_null *val_out)
     return (*val_out == NULL) ? ENOMEM : 0;
 }
 
+int
+k5_json_null_create_val(k5_json_value *val_out)
+{
+    *val_out = alloc_value(&null_type, 0);
+    return (*val_out == NULL) ? ENOMEM : 0;
+}
+
 /*** Boolean type ***/
 
 static struct json_type_st bool_type = { K5_JSON_TID_BOOL, "bool", NULL };
@@ -265,6 +272,92 @@ k5_json_array_set(k5_json_array array, size_t idx, k5_json_value val)
     array->values[idx] = k5_json_retain(val);
 }
 
+int
+k5_json_array_fmt(k5_json_array *array_out, const char *template, ...)
+{
+    const char *p;
+    va_list ap;
+    const char *cstring;
+    unsigned char *data;
+    size_t len;
+    long long nval;
+    k5_json_array array;
+    k5_json_value val;
+    k5_json_number num;
+    k5_json_string str;
+    k5_json_bool b;
+    k5_json_null null;
+    int truth, ret;
+
+    *array_out = NULL;
+    if (k5_json_array_create(&array))
+        return ENOMEM;
+    va_start(ap, template);
+    for (p = template; *p != '\0'; p++) {
+        switch (*p) {
+        case 'v':
+            val = k5_json_retain(va_arg(ap, k5_json_value));
+            break;
+        case 'n':
+            if (k5_json_null_create(&null))
+                goto err;
+            val = null;
+            break;
+        case 'b':
+            truth = va_arg(ap, int);
+            if (k5_json_bool_create(truth, &b))
+                goto err;
+            val = b;
+            break;
+        case 'i':
+            nval = va_arg(ap, int);
+            if (k5_json_number_create(nval, &num))
+                goto err;
+            val = num;
+            break;
+        case 'L':
+            nval = va_arg(ap, long long);
+            if (k5_json_number_create(nval, &num))
+                goto err;
+            val = num;
+            break;
+        case 's':
+            cstring = va_arg(ap, const char *);
+            if (cstring == NULL) {
+                if (k5_json_null_create(&null))
+                    goto err;
+                val = null;
+            } else {
+                if (k5_json_string_create(cstring, &str))
+                    goto err;
+                val = str;
+            }
+            break;
+        case 'B':
+            data = va_arg(ap, unsigned char *);
+            len = va_arg(ap, size_t);
+            if (k5_json_string_create_base64(data, len, &str))
+                goto err;
+            val = str;
+            break;
+        default:
+            goto err;
+        }
+        ret = k5_json_array_add(array, val);
+        k5_json_release(val);
+        if (ret)
+            goto err;
+    }
+    va_end(ap);
+    *array_out = array;
+    return 0;
+
+err:
+    va_end(ap);
+    k5_json_release(array);
+    return ENOMEM;
+}
+
 /*** Object type (string:value mapping) ***/
 
 struct entry {
diff --git a/src/util/support/libkrb5support-fixed.exports b/src/util/support/libkrb5support-fixed.exports
index c813310..41c8c6b 100644
--- a/src/util/support/libkrb5support-fixed.exports
+++ b/src/util/support/libkrb5support-fixed.exports
@@ -8,6 +8,7 @@ k5_clear_error
 k5_set_error_info_callout_fn
 k5_json_array_add
 k5_json_array_create
+k5_json_array_fmt
 k5_json_array_get
 k5_json_array_length
 k5_json_array_set
@@ -17,6 +18,7 @@ k5_json_decode
 k5_json_encode
 k5_json_get_tid
 k5_json_null_create
+k5_json_null_create_val
 k5_json_number_create
 k5_json_number_value
 k5_json_object_count
diff --git a/src/util/support/t_json.c b/src/util/support/t_json.c
index afb02ee..040a85a 100644
--- a/src/util/support/t_json.c
+++ b/src/util/support/t_json.c
@@ -114,6 +114,32 @@ test_array()
 
     k5_json_release(v1);
     k5_json_release(v2);
+    k5_json_release(a);
+
+    k5_json_array_fmt(&a, "vnbiLssB", v3, 1, 9, (long long)-6, "def", NULL,
+                      (void *)"ghij", (size_t)4);
+    v = k5_json_array_get(a, 0);
+    check(k5_json_get_tid(v) == K5_JSON_TID_NULL, "fmt array[0] tid");
+    v = k5_json_array_get(a, 1);
+    check(k5_json_get_tid(v) == K5_JSON_TID_NULL, "fmt array[1] tid");
+    v = k5_json_array_get(a, 2);
+    check(k5_json_get_tid(v) == K5_JSON_TID_BOOL, "fmt array[2] tid");
+    check(k5_json_bool_value(v), "fmt array[2] value");
+    v = k5_json_array_get(a, 3);
+    check(k5_json_get_tid(v) == K5_JSON_TID_NUMBER, "fmt array[3] tid");
+    check(k5_json_number_value(v) == 9, "fmt array[3] value");
+    v = k5_json_array_get(a, 4);
+    check(k5_json_get_tid(v) == K5_JSON_TID_NUMBER, "fmt array[4] tid");
+    check(k5_json_number_value(v) == -6, "fmt array[4] value");
+    v = k5_json_array_get(a, 5);
+    check(k5_json_get_tid(v) == K5_JSON_TID_STRING, "fmt array[5] tid");
+    check(strcmp(k5_json_string_utf8(v), "def") == 0, "fmt array[5] value");
+    v = k5_json_array_get(a, 6);
+    check(k5_json_get_tid(v) == K5_JSON_TID_NULL, "fmt array[6] tid");
+    v = k5_json_array_get(a, 7);
+    check(k5_json_get_tid(v) == K5_JSON_TID_STRING, "fmt array[7] tid");
+    check(strcmp(k5_json_string_utf8(v), "Z2hpag==") == 0,
+          "fmt array[7] value");
     k5_json_release(v3);
     k5_json_release(a);
 }


More information about the cvs-krb5 mailing list