]> git.ipfire.org Git - thirdparty/krb5.git/commitdiff
Make internal JSON functions return error codes
authorGreg Hudson <ghudson@mit.edu>
Tue, 30 Oct 2012 21:17:45 +0000 (17:17 -0400)
committerGreg Hudson <ghudson@mit.edu>
Wed, 13 Feb 2013 20:53:29 +0000 (15:53 -0500)
Return error codes (0, ENOMEM, or EINVAL) from JSON support functions
instead of returning results directly.  This makes error handling
simpler for functions which assemble JSON objects and then return a
krb5_error_code values.  Adjust all callers.  Use shims in
export_cred.c to minimize changes there; it will be redesigned
internally in a subsequent commit.

src/include/k5-json.h
src/lib/gssapi/krb5/export_cred.c
src/lib/gssapi/krb5/import_cred.c
src/lib/krb5/krb/get_in_tkt.c
src/lib/krb5/krb/init_creds_ctx.h
src/lib/krb5/krb/preauth2.c
src/lib/krb5/krb/preauth_otp.c
src/util/support/json.c
src/util/support/t_json.c

index 42dcfa3d38565388d579f618a9b7cc614ddc843a..4b9b8fec3bcce95e7bd1109dd5354d8e31f4b9a8 100644 (file)
@@ -93,13 +93,12 @@ k5_json_tid k5_json_get_tid(k5_json_value val);
  * decrement the refcount, possibly freeing the value.  k5_json_retain returns
  * its argument and always succeeds.  Both functions gracefully accept NULL.
  */
-void *k5_json_retain(k5_json_value val);
+k5_json_value k5_json_retain(k5_json_value val);
 void k5_json_release(k5_json_value val);
 
 /*
- * Unless otherwise specified, the following functions return NULL on error
- * (generally only if out of memory) if they return a pointer type, or 0 on
- * success and -1 on failure if they return int.
+ * If a k5_json_* function can fail, it returns 0 on success and an errno value
+ * on failure.
  */
 
 /*
@@ -108,7 +107,7 @@ void k5_json_release(k5_json_value val);
 
 typedef struct k5_json_null_st *k5_json_null;
 
-k5_json_null k5_json_null_create(void);
+int k5_json_null_create(k5_json_null *null_out);
 
 /*
  * Boolean
@@ -116,7 +115,7 @@ k5_json_null k5_json_null_create(void);
 
 typedef struct k5_json_bool_st *k5_json_bool;
 
-k5_json_bool k5_json_bool_create(int truth);
+int k5_json_bool_create(int truth, k5_json_bool *val_out);
 int k5_json_bool_value(k5_json_bool bval);
 
 /*
@@ -125,7 +124,7 @@ int k5_json_bool_value(k5_json_bool bval);
 
 typedef struct k5_json_array_st *k5_json_array;
 
-k5_json_array k5_json_array_create(void);
+int k5_json_array_create(k5_json_array *val_out);
 size_t k5_json_array_length(k5_json_array array);
 
 /* Both of these functions increment the reference count on val. */
@@ -144,7 +143,7 @@ typedef struct k5_json_object_st *k5_json_object;
 typedef void (*k5_json_object_iterator_fn)(void *arg, const char *key,
                                            k5_json_value val);
 
-k5_json_object k5_json_object_create(void);
+int k5_json_object_create(k5_json_object *val_out);
 void k5_json_object_iterate(k5_json_object obj,
                             k5_json_object_iterator_fn func, void *arg);
 
@@ -164,16 +163,19 @@ k5_json_value k5_json_object_get(k5_json_object obj, const char *key);
 
 typedef struct k5_json_string_st *k5_json_string;
 
-k5_json_string k5_json_string_create(const char *string);
-k5_json_string k5_json_string_create_len(const void *data, size_t len);
+int k5_json_string_create(const char *cstring, k5_json_string *val_out);
+int k5_json_string_create_len(const void *data, size_t len,
+                              k5_json_string *val_out);
 const char *k5_json_string_utf8(k5_json_string string);
 
+
 /* Create a base64 string value from binary data. */
-k5_json_string k5_json_string_create_base64(const void *data, size_t len);
+int k5_json_string_create_base64(const void *data, size_t len,
+                                 k5_json_string *val_out);
 
-/* Decode a base64 string.  Returns NULL and *len_out == 0 if out of memory,
- * NULL and *len == SIZE_MAX if string's contents aren't valid base64. */
-void *k5_json_string_unbase64(k5_json_string string, size_t *len_out);
+/* Decode the base64 contents of string. */
+int k5_json_string_unbase64(k5_json_string string, unsigned char **data_out,
+                            size_t *len_out);
 
 /*
  * Number
@@ -181,14 +183,14 @@ void *k5_json_string_unbase64(k5_json_string string, size_t *len_out);
 
 typedef struct k5_json_number_st *k5_json_number;
 
-k5_json_number k5_json_number_create(long long number);
+int k5_json_number_create(long long number, k5_json_number *val_out);
 long long k5_json_number_value(k5_json_number number);
 
 /*
  * JSON encoding and decoding
  */
 
-char *k5_json_encode(k5_json_value val);
-k5_json_value k5_json_decode(const char *str);
+int k5_json_encode(k5_json_value val, char **json_out);
+int k5_json_decode(const char *str, k5_json_value *val_out);
 
 #endif /* K5_JSON_H */
index 162547966b884e636117632b29d168c59ff7341f..e5ffaf54aaef61fdba7b251c85d4ba179fc15f16 100644 (file)
@@ -44,12 +44,51 @@ add(k5_json_array array, k5_json_value 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)k5_json_null_create() :
-        (k5_json_value)k5_json_string_create(str);
+    return (str == NULL) ? (k5_json_value)null() : string(str);
 }
 
 /* Return a JSON null or array value representing princ. */
@@ -60,10 +99,10 @@ json_principal(krb5_context context, krb5_principal princ)
     k5_json_string str;
 
     if (princ == NULL)
-        return k5_json_null_create();
+        return null();
     if (krb5_unparse_name(context, princ, &princname))
         return NULL;
-    str = k5_json_string_create(princname);
+    str = string(princname);
     krb5_free_unparsed_name(context, princname);
     return str;
 }
@@ -75,12 +114,11 @@ json_etypes(krb5_enctype *etypes)
     k5_json_array array;
 
     if (etypes == NULL)
-        return k5_json_null_create();
-    array = k5_json_array_create();
-    if (array == NULL)
+        return null();
+    if (k5_json_array_create(&array))
         return NULL;
     for (; *etypes != 0; etypes++) {
-        if (add(array, k5_json_number_create(*etypes)))
+        if (add(array, number(*etypes)))
             goto oom;
     }
     return array;
@@ -96,9 +134,8 @@ json_kgname(krb5_context context, krb5_gss_name_t name)
     k5_json_array array;
 
     if (name == NULL)
-        return k5_json_null_create();
-    array = k5_json_array_create();
-    if (array == NULL)
+        return null();
+    if (k5_json_array_create(&array))
         return NULL;
     if (add(array, json_principal(context, name->princ)))
         goto oom;
@@ -119,10 +156,10 @@ json_keytab(krb5_context context, krb5_keytab keytab)
     char name[1024];
 
     if (keytab == NULL)
-        return k5_json_null_create();
+        return null();
     if (krb5_kt_get_name(context, keytab, name, sizeof(name)))
         return NULL;
-    return k5_json_string_create(name);
+    return string(name);
 }
 
 /* Return a JSON null or string value representing rcache. */
@@ -133,11 +170,11 @@ json_rcache(krb5_context context, krb5_rcache rcache)
     k5_json_string str;
 
     if (rcache == NULL)
-        return k5_json_null_create();
+        return null();
     if (asprintf(&name, "%s:%s", krb5_rc_get_type(context, rcache),
                  krb5_rc_get_name(context, rcache)) < 0)
         return NULL;
-    str = k5_json_string_create(name);
+    str = string(name);
     free(name);
     return str;
 }
@@ -148,13 +185,11 @@ json_keyblock(krb5_keyblock *keyblock)
 {
     k5_json_array array;
 
-    array = k5_json_array_create();
-    if (array == NULL)
+    if (k5_json_array_create(&array))
         return NULL;
-    if (add(array, k5_json_number_create(keyblock->enctype)))
+    if (add(array, number(keyblock->enctype)))
         goto oom;
-    if (add(array, k5_json_string_create_base64(keyblock->contents,
-                                                keyblock->length)))
+    if (add(array, base64string(keyblock->contents, keyblock->length)))
         goto oom;
     return array;
 oom:
@@ -168,12 +203,11 @@ json_address(krb5_address *addr)
 {
     k5_json_array array;
 
-    array = k5_json_array_create();
-    if (array == NULL)
+    if (k5_json_array_create(&array))
         return NULL;
-    if (add(array, k5_json_number_create(addr->addrtype)))
+    if (add(array, number(addr->addrtype)))
         goto oom;
-    if (add(array, k5_json_string_create_base64(addr->contents, addr->length)))
+    if (add(array, base64string(addr->contents, addr->length)))
         goto oom;
     return array;
 oom:
@@ -188,9 +222,8 @@ json_addresses(krb5_address **addrs)
     k5_json_array array;
 
     if (addrs == NULL)
-        return k5_json_null_create();
-    array = k5_json_array_create();
-    if (array == NULL)
+        return null();
+    if (k5_json_array_create(&array))
         return NULL;
     for (; *addrs != NULL; addrs++) {
         if (add(array, json_address(*addrs))) {
@@ -207,12 +240,11 @@ json_authdata_element(krb5_authdata *ad)
 {
     k5_json_array array;
 
-    array = k5_json_array_create();
-    if (array == NULL)
+    if (k5_json_array_create(&array))
         return NULL;
-    if (add(array, k5_json_number_create(ad->ad_type)))
+    if (add(array, number(ad->ad_type)))
         goto oom;
-    if (add(array, k5_json_string_create_base64(ad->contents, ad->length)))
+    if (add(array, base64string(ad->contents, ad->length)))
         goto oom;
     return array;
 oom:
@@ -227,9 +259,8 @@ json_authdata(krb5_authdata **authdata)
     k5_json_array array;
 
     if (authdata == NULL)
-        return k5_json_null_create();
-    array = k5_json_array_create();
-    if (array == NULL)
+        return null();
+    if (k5_json_array_create(&array))
         return NULL;
     for (; *authdata != NULL; authdata++) {
         if (add(array, json_authdata_element(*authdata))) {
@@ -246,8 +277,7 @@ json_creds(krb5_context context, krb5_creds *creds)
 {
     k5_json_array array;
 
-    array = k5_json_array_create();
-    if (array == NULL)
+    if (k5_json_array_create(&array))
         return NULL;
     if (add(array, json_principal(context, creds->client)))
         goto eom;
@@ -255,25 +285,24 @@ json_creds(krb5_context context, krb5_creds *creds)
         goto eom;
     if (add(array, json_keyblock(&creds->keyblock)))
         goto eom;
-    if (add(array, k5_json_number_create(creds->times.authtime)))
+    if (add(array, number(creds->times.authtime)))
         goto eom;
-    if (add(array, k5_json_number_create(creds->times.starttime)))
+    if (add(array, number(creds->times.starttime)))
         goto eom;
-    if (add(array, k5_json_number_create(creds->times.endtime)))
+    if (add(array, number(creds->times.endtime)))
         goto eom;
-    if (add(array, k5_json_number_create(creds->times.renew_till)))
+    if (add(array, number(creds->times.renew_till)))
         goto eom;
-    if (add(array, k5_json_bool_create(creds->is_skey)))
+    if (add(array, bool(creds->is_skey)))
         goto eom;
-    if (add(array, k5_json_number_create(creds->ticket_flags)))
+    if (add(array, number(creds->ticket_flags)))
         goto eom;
     if (add(array, json_addresses(creds->addresses)))
         goto eom;
-    if (add(array, k5_json_string_create_base64(creds->ticket.data,
-                                                creds->ticket.length)))
+    if (add(array, base64string(creds->ticket.data, creds->ticket.length)))
         goto eom;
-    if (add(array, k5_json_string_create_base64(creds->second_ticket.data,
-                                                creds->second_ticket.length)))
+    if (add(array, base64string(creds->second_ticket.data,
+                                creds->second_ticket.length)))
         goto eom;
     if (add(array, json_authdata(creds->authdata)))
         goto eom;
@@ -294,8 +323,7 @@ json_ccache_contents(krb5_context context, krb5_ccache ccache)
     k5_json_array array;
     int st;
 
-    array = k5_json_array_create();
-    if (array == NULL)
+    if (k5_json_array_create(&array))
         return NULL;
 
     /* Put the principal in the first array entry. */
@@ -334,13 +362,13 @@ json_ccache(krb5_context context, krb5_ccache ccache)
     k5_json_string str;
 
     if (ccache == NULL)
-        return k5_json_null_create();
+        return null();
     if (strcmp(krb5_cc_get_type(context, ccache), "MEMORY") == 0) {
         return json_ccache_contents(context, ccache);
     } else {
         if (krb5_cc_get_full_name(context, ccache, &name))
             return NULL;
-        str = k5_json_string_create(name);
+        str = string(name);
         free(name);
         return str;
     }
@@ -352,18 +380,17 @@ json_kgcred(krb5_context context, krb5_gss_cred_id_t cred)
 {
     k5_json_array array;
 
-    array = k5_json_array_create();
-    if (array == NULL)
+    if (k5_json_array_create(&array))
         return NULL;
-    if (add(array, k5_json_number_create(cred->usage)))
+    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, k5_json_bool_create(cred->default_identity)))
+    if (add(array, bool(cred->default_identity)))
         goto oom;
-    if (add(array, k5_json_bool_create(cred->iakerb_mech)))
+    if (add(array, bool(cred->iakerb_mech)))
         goto oom;
     /* Don't marshal cred->destroy_ccache. */
     if (add(array, json_keytab(context, cred->keytab)))
@@ -374,11 +401,11 @@ json_kgcred(krb5_context context, krb5_gss_cred_id_t cred)
         goto oom;
     if (add(array, json_keytab(context, cred->client_keytab)))
         goto oom;
-    if (add(array, k5_json_bool_create(cred->have_tgt)))
+    if (add(array, bool(cred->have_tgt)))
         goto oom;
-    if (add(array, k5_json_number_create(cred->expire)))
+    if (add(array, number(cred->expire)))
         goto oom;
-    if (add(array, k5_json_number_create(cred->refresh_time)))
+    if (add(array, number(cred->refresh_time)))
         goto oom;
     if (add(array, json_etypes(cred->req_enctypes)))
         goto oom;
@@ -414,16 +441,14 @@ krb5_gss_export_cred(OM_uint32 *minor_status, gss_cred_id_t cred_handle,
         return status;
     cred = (krb5_gss_cred_id_t)cred_handle;
 
-    array = k5_json_array_create();
-    if (array == NULL)
+    if (k5_json_array_create(&array))
         goto oom;
-    if (add(array, k5_json_string_create(CRED_EXPORT_MAGIC)))
+    if (add(array, string(CRED_EXPORT_MAGIC)))
         goto oom;
     if (add(array, json_kgcred(context, cred)))
         goto oom;
 
-    str = k5_json_encode(array);
-    if (str == NULL)
+    if (k5_json_encode(array, &str))
         goto oom;
     d = string2data(str);
     if (data_to_gss(&d, token))
index ad9a1110e9e829117f3af7c0792084787c58f9a8..973b9d01521815e9a8f3611ffdf8a8df7104c696 100644 (file)
@@ -205,8 +205,7 @@ json_to_keyblock(k5_json_value v, krb5_keyblock *keyblock)
     s = check_element(array, 1, K5_JSON_TID_STRING);
     if (s == NULL)
         return -1;
-    keyblock->contents = k5_json_string_unbase64(s, &len);
-    if (keyblock->contents == NULL)
+    if (k5_json_string_unbase64(s, &keyblock->contents, &len))
         return -1;
     keyblock->length = len;
     keyblock->magic = KV5M_KEYBLOCK;
@@ -241,8 +240,7 @@ json_to_address(k5_json_value v, krb5_address **addr_out)
     if (addr == NULL)
         return -1;
     addr->addrtype = k5_json_number_value(n);
-    addr->contents = k5_json_string_unbase64(s, &len);
-    if (addr->contents == NULL) {
+    if (k5_json_string_unbase64(s, &addr->contents, &len)) {
         free(addr);
         return -1;
     }
@@ -311,8 +309,7 @@ json_to_authdata_element(k5_json_value v, krb5_authdata **ad_out)
     if (ad == NULL)
         return -1;
     ad->ad_type = k5_json_number_value(n);
-    ad->contents = k5_json_string_unbase64(s, &len);
-    if (ad->contents == NULL) {
+    if (k5_json_string_unbase64(s, &ad->contents, &len)) {
         free(ad);
         return -1;
     }
@@ -361,6 +358,7 @@ json_to_creds(krb5_context context, k5_json_value v, krb5_creds *creds)
     k5_json_number n;
     k5_json_bool b;
     k5_json_string s;
+    unsigned char *data;
     size_t len;
 
     memset(creds, 0, sizeof(*creds));
@@ -418,17 +416,17 @@ json_to_creds(krb5_context context, k5_json_value v, krb5_creds *creds)
     s = check_element(array, 10, K5_JSON_TID_STRING);
     if (s == NULL)
         goto invalid;
-    creds->ticket.data = k5_json_string_unbase64(s, &len);
-    if (creds->ticket.data == NULL)
+    if (k5_json_string_unbase64(s, &data, &len))
         goto invalid;
+    creds->ticket.data = (char *)data;
     creds->ticket.length = len;
 
     s = check_element(array, 11, K5_JSON_TID_STRING);
     if (s == NULL)
         goto invalid;
-    creds->second_ticket.data = k5_json_string_unbase64(s, &len);
-    if (creds->second_ticket.data == NULL)
+    if (k5_json_string_unbase64(s, &data, &len))
         goto invalid;
+    creds->second_ticket.data = (char *)data;
     creds->second_ticket.length = len;
 
     if (json_to_authdata(context, k5_json_array_get(array, 12),
@@ -620,8 +618,7 @@ krb5_gss_import_cred(OM_uint32 *minor_status, gss_buffer_t token,
         *minor_status = ret;
         goto cleanup;
     }
-    v = k5_json_decode(copy);
-    if (v == NULL)
+    if (k5_json_decode(copy, &v))
         goto invalid;
 
     /* Decode the CRED_EXPORT_MAGIC array wrapper. */
index c88b67a6e8d67b8d9b4926a44b13428ce28ffd7e..dfec99179b8755ed85ad67a3d35524711d7fe309 100644 (file)
@@ -1124,24 +1124,21 @@ save_selected_preauth_type(krb5_context context, krb5_ccache ccache,
 static krb5_error_code
 clear_cc_config_out_data(krb5_context context, krb5_init_creds_context ctx)
 {
-    if (ctx->cc_config_out != NULL)
-        k5_json_release(ctx->cc_config_out);
-    ctx->cc_config_out = k5_json_object_create();
-    if (ctx->cc_config_out == NULL)
-        return ENOMEM;
-    return 0;
+    k5_json_release(ctx->cc_config_out);
+    ctx->cc_config_out = NULL;
+    return k5_json_object_create(&ctx->cc_config_out);
 }
 
 static krb5_error_code
 read_cc_config_in_data(krb5_context context, krb5_init_creds_context ctx)
 {
+    k5_json_value val;
     krb5_data config;
     char *encoded;
     krb5_error_code code;
     int i;
 
-    if (ctx->cc_config_in != NULL)
-        k5_json_release(ctx->cc_config_in);
+    k5_json_release(ctx->cc_config_in);
     ctx->cc_config_in = NULL;
 
     if (ctx->opte->opt_private->in_ccache == NULL)
@@ -1159,16 +1156,15 @@ read_cc_config_in_data(krb5_context context, krb5_init_creds_context ctx)
     if (i < 0)
         return ENOMEM;
 
-    ctx->cc_config_in = k5_json_decode(encoded);
+    code = k5_json_decode(encoded, &val);
     free(encoded);
-    if (ctx->cc_config_in == NULL)
-        return ENOMEM;
-    if (k5_json_get_tid(ctx->cc_config_in) != K5_JSON_TID_OBJECT) {
-        k5_json_release(ctx->cc_config_in);
-        ctx->cc_config_in = NULL;
+    if (code)
+        return code;
+    if (k5_json_get_tid(val) != K5_JSON_TID_OBJECT) {
+        k5_json_release(val);
         return EINVAL;
     }
-
+    ctx->cc_config_in = val;
     return 0;
 }
 
@@ -1183,9 +1179,9 @@ save_cc_config_out_data(krb5_context context, krb5_ccache ccache,
     if (ctx->cc_config_out == NULL ||
         k5_json_object_count(ctx->cc_config_out) == 0)
         return 0;
-    encoded = k5_json_encode(ctx->cc_config_out);
-    if (encoded == NULL)
-        return ENOMEM;
+    code = k5_json_encode(ctx->cc_config_out, &encoded);
+    if (code)
+        return code;
     config = string2data(encoded);
     code = krb5_cc_set_config(context, ccache, ctx->cred.server,
                               KRB5_CC_CONF_PA_CONFIG_DATA, &config);
index 5d813914a40ee7a580add26f47e2aa6c07d85d9d..1bc90a528eeaf50f86bb0f66128b4ba2a9419919 100644 (file)
@@ -50,8 +50,8 @@ struct _krb5_init_creds_context {
     struct krb5_responder_context_st rctx;
     krb5_preauthtype selected_preauth_type;
     krb5_preauthtype allowed_preauth_type;
-    void *cc_config_in;
-    void *cc_config_out;
+    k5_json_object cc_config_in;
+    k5_json_object cc_config_out;
     /* Discovered offset of server time during preauth */
     krb5_timestamp pa_offset;
     krb5_int32 pa_offset_usec;
index 060f98aa008b95a11d00eb28234f2e3057c179a0..74a4f27bebda909acfb538d6b0548df0b53496f0 100644 (file)
@@ -469,22 +469,19 @@ set_cc_config(krb5_context context, krb5_clpreauth_rock rock,
               const char *key, const char *data)
 {
     krb5_init_creds_context ctx = (krb5_init_creds_context)rock;
-    k5_json_value value;
-    int i;
+    krb5_error_code ret;
+    k5_json_string str;
 
     if (ctx->cc_config_out == NULL)
         return ENOENT;
 
-    value = k5_json_string_create(data);
-    if (value == NULL)
-        return ENOMEM;
-
-    i = k5_json_object_set(ctx->cc_config_out, key, value);
-    k5_json_release(value);
-    if (i < 0)
-        return ENOMEM;
+    ret = k5_json_string_create(data, &str);
+    if (ret)
+        return ret;
 
-    return 0;
+    ret = k5_json_object_set(ctx->cc_config_out, key, str);
+    k5_json_release(str);
+    return ret;
 }
 
 static struct krb5_clpreauth_callbacks_st callbacks = {
index e610d47a9414884bd9f3d182e41f744e13620082..d343683c029cc795e27c0d6be25c6990480f03a9 100644 (file)
@@ -99,13 +99,13 @@ codec_data_to_value(krb5_data *data, k5_json_object obj, const char *key)
     if (data->data == NULL)
         return 0;
 
-    str = k5_json_string_create_len(data->data, data->length);
-    if (str == NULL)
-        return ENOMEM;
+    retval = k5_json_string_create_len(data->data, data->length, &str);
+    if (retval)
+        return retval;
 
     retval = k5_json_object_set(obj, key, str);
     k5_json_release(str);
-    return retval == 0 ? 0 : ENOMEM;
+    return retval;
 }
 
 /* Converts a property of a json object into a krb5_int32. */
@@ -135,25 +135,25 @@ codec_int32_to_value(krb5_int32 int32, k5_json_object obj, const char *key)
     if (int32 == -1)
         return 0;
 
-    num = k5_json_number_create(int32);
-    if (num == NULL)
-        return ENOMEM;
+    retval = k5_json_number_create(int32, &num);
+    if (retval)
+        return retval;
 
     retval = k5_json_object_set(obj, key, num);
     k5_json_release(num);
-    return retval == 0 ? 0 : ENOMEM;
+    return retval;
 }
 
 /* Converts a krb5_otp_tokeninfo into a JSON object. */
 static krb5_error_code
 codec_encode_tokeninfo(krb5_otp_tokeninfo *ti, k5_json_object *out)
 {
-    krb5_error_code retval = 0;
+    krb5_error_code retval;
     k5_json_object obj;
     krb5_flags flags;
 
-    obj = k5_json_object_create();
-    if (obj == NULL)
+    retval = k5_json_object_create(&obj);
+    if (retval != 0)
         goto error;
 
     flags = KRB5_RESPONDER_OTP_FLAGS_COLLECT_TOKEN;
@@ -212,60 +212,51 @@ codec_encode_challenge(krb5_context ctx, krb5_pa_otp_challenge *chl,
     k5_json_object obj = NULL, tmp = NULL;
     k5_json_string str = NULL;
     k5_json_array arr = NULL;
-    krb5_error_code retval = 0;
+    krb5_error_code retval;
     int i;
 
-    obj = k5_json_object_create();
-    if (obj == NULL)
-        goto error;
+    retval = k5_json_object_create(&obj);
+    if (retval != 0)
+        goto cleanup;
 
     if (chl->service.data) {
-        str = k5_json_string_create_len(chl->service.data,
-                                        chl->service.length);
-        if (str == NULL)
-            goto error;
+        retval = k5_json_string_create_len(chl->service.data,
+                                           chl->service.length, &str);
+        if (retval != 0)
+            goto cleanup;
         retval = k5_json_object_set(obj, "service", str);
         k5_json_release(str);
-        if (retval != 0) {
-            retval = ENOMEM;
-            goto error;
-        }
+        if (retval != 0)
+            goto cleanup;
     }
 
-    arr = k5_json_array_create();
-    if (arr == NULL)
-        goto error;
+    retval = k5_json_array_create(&arr);
+    if (retval != 0)
+        goto cleanup;
 
     for (i = 0; chl->tokeninfo[i] != NULL ; i++) {
         retval = codec_encode_tokeninfo(chl->tokeninfo[i], &tmp);
         if (retval != 0)
-            goto error;
+            goto cleanup;
 
         retval = k5_json_array_add(arr, tmp);
         k5_json_release(tmp);
-        if (retval != 0) {
-            retval = ENOMEM;
-            goto error;
-        }
-    }
-
-    if (k5_json_object_set(obj, "tokenInfo", arr) != 0) {
-        retval = ENOMEM;
-        goto error;
+        if (retval != 0)
+            goto cleanup;
     }
 
-    *json = k5_json_encode(obj);
-    if (*json == NULL)
-        goto error;
+    retval = k5_json_object_set(obj, "tokenInfo", arr);
+    if (retval != 0)
+        goto cleanup;
 
-    k5_json_release(arr);
-    k5_json_release(obj);
-    return 0;
+    retval = k5_json_encode(obj, json);
+    if (retval)
+        goto cleanup;
 
-error:
+cleanup:
     k5_json_release(arr);
     k5_json_release(obj);
-    return retval == 0 ? ENOMEM : retval;
+    return retval;
 }
 
 /* Converts a JSON object into a krb5_responder_otp_tokeninfo. */
@@ -327,8 +318,8 @@ codec_decode_challenge(krb5_context ctx, const char *json)
     krb5_error_code retval;
     size_t i;
 
-    obj = k5_json_decode(json);
-    if (obj == NULL)
+    retval = k5_json_decode(json, &obj);
+    if (retval != 0)
         goto error;
 
     if (k5_json_get_tid(obj) != K5_JSON_TID_OBJECT)
@@ -384,7 +375,7 @@ codec_decode_answer(krb5_context context, const char *answer,
                     krb5_otp_tokeninfo **tis, krb5_otp_tokeninfo **ti,
                     krb5_data *value, krb5_data *pin)
 {
-    krb5_error_code retval = EBADMSG;
+    krb5_error_code retval;
     k5_json_value val = NULL;
     krb5_int32 indx, i;
     krb5_data tmp;
@@ -392,8 +383,8 @@ codec_decode_answer(krb5_context context, const char *answer,
     if (answer == NULL)
         return EBADMSG;
 
-    val = k5_json_decode(answer);
-    if (val == NULL)
+    retval = k5_json_decode(answer, &val);
+    if (retval != 0)
         goto cleanup;
 
     if (k5_json_get_tid(val) != K5_JSON_TID_OBJECT)
@@ -1196,48 +1187,49 @@ krb5_responder_otp_set_answer(krb5_context ctx, krb5_responder_context rctx,
 {
     krb5_error_code retval;
     k5_json_object obj = NULL;
-    k5_json_value val = NULL;
+    k5_json_number num;
+    k5_json_string str;
     char *tmp;
 
-    obj = k5_json_object_create();
-    if (obj == NULL)
+    retval = k5_json_object_create(&obj);
+    if (retval != 0)
         goto error;
 
-    val = k5_json_number_create(ti);
-    if (val == NULL)
+    retval = k5_json_number_create(ti, &num);
+    if (retval != 0)
         goto error;
 
-    retval = k5_json_object_set(obj, "tokeninfo", val);
-    k5_json_release(val);
+    retval = k5_json_object_set(obj, "tokeninfo", num);
+    k5_json_release(num);
     if (retval != 0)
         goto error;
 
     if (value != NULL) {
-        val = k5_json_string_create(value);
-        if (val == NULL)
+        retval = k5_json_string_create(value, &str);
+        if (retval != 0)
             goto error;
 
-        retval = k5_json_object_set(obj, "value", val);
-        k5_json_release(val);
+        retval = k5_json_object_set(obj, "value", str);
+        k5_json_release(str);
         if (retval != 0)
             goto error;
     }
 
     if (pin != NULL) {
-        val = k5_json_string_create(pin);
-        if (val == NULL)
+        retval = k5_json_string_create(pin, &str);
+        if (retval != 0)
             goto error;
 
-        retval = k5_json_object_set(obj, "pin", val);
-        k5_json_release(val);
+        retval = k5_json_object_set(obj, "pin", str);
+        k5_json_release(str);
         if (retval != 0)
             goto error;
     }
 
-    tmp = k5_json_encode(obj);
-    k5_json_release(obj);
-    if (tmp == NULL)
+    retval = k5_json_encode(obj, &tmp);
+    if (retval != 0)
         goto error;
+    k5_json_release(obj);
 
     retval = krb5_responder_set_answer(ctx, rctx, KRB5_RESPONDER_QUESTION_OTP,
                                        tmp);
@@ -1246,7 +1238,7 @@ krb5_responder_otp_set_answer(krb5_context ctx, krb5_responder_context rctx,
 
 error:
     k5_json_release(obj);
-    return ENOMEM;
+    return retval;
 }
 
 void KRB5_CALLCONV
index f02fe263df6c1cdbc59b507e60aef8528a765b3b..5151b84159d6eef463059f826459ca5962a420d8 100644 (file)
@@ -96,7 +96,7 @@ struct value_base {
 #define PTR2BASE(ptr) (((struct value_base *)ptr) - 1)
 #define BASE2PTR(ptr) ((void *)(((struct value_base *)ptr) + 1))
 
-void *
+k5_json_value
 k5_json_retain(k5_json_value val)
 {
     struct value_base *p;
@@ -160,24 +160,29 @@ alloc_value(json_type type, size_t size)
 
 static struct json_type_st null_type = { K5_JSON_TID_NULL, "null", NULL };
 
-k5_json_null
-k5_json_null_create(void)
+int
+k5_json_null_create(k5_json_null *val_out)
 {
-    return alloc_value(&null_type, 0);
+    *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 };
 
-k5_json_bool
-k5_json_bool_create(int truth)
+int
+k5_json_bool_create(int truth, k5_json_bool *val_out)
 {
     k5_json_bool b;
 
+    *val_out = NULL;
     b = alloc_value(&bool_type, 1);
+    if (b == NULL)
+        return ENOMEM;
     *(unsigned char *)b = !!truth;
-    return b;
+    *val_out = b;
+    return 0;
 }
 
 int
@@ -209,10 +214,11 @@ static struct json_type_st array_type = {
     K5_JSON_TID_ARRAY, "array", array_dealloc
 };
 
-k5_json_array
-k5_json_array_create(void)
+int
+k5_json_array_create(k5_json_array *val_out)
 {
-    return alloc_value(&array_type, sizeof(struct k5_json_array_st));
+    *val_out = alloc_value(&array_type, sizeof(struct k5_json_array_st));
+    return (*val_out == NULL) ? ENOMEM : 0;
 }
 
 int
@@ -289,10 +295,11 @@ static struct json_type_st object_type = {
     K5_JSON_TID_OBJECT, "object", object_dealloc
 };
 
-k5_json_object
-k5_json_object_create(void)
+int
+k5_json_object_create(k5_json_object *val_out)
 {
-    return alloc_value(&object_type, sizeof(struct k5_json_object_st));
+    *val_out = alloc_value(&object_type, sizeof(struct k5_json_object_st));
+    return (*val_out == NULL) ? ENOMEM : 0;
 }
 
 size_t
@@ -373,37 +380,42 @@ static struct json_type_st string_type = {
     K5_JSON_TID_STRING, "string", NULL
 };
 
-k5_json_string
-k5_json_string_create(const char *string)
+int
+k5_json_string_create(const char *cstring, k5_json_string *val_out)
 {
-    return k5_json_string_create_len(string, strlen(string));
+    return k5_json_string_create_len(cstring, strlen(cstring), val_out);
 }
 
-k5_json_string
-k5_json_string_create_len(const void *data, size_t len)
+int
+k5_json_string_create_len(const void *data, size_t len,
+                          k5_json_string *val_out)
 {
     char *s;
 
+    *val_out = NULL;
     s = alloc_value(&string_type, len + 1);
     if (s == NULL)
-        return NULL;
+        return ENOMEM;
     memcpy(s, data, len);
     s[len] = '\0';
-    return (k5_json_string)s;
+    *val_out = (k5_json_string)s;
+    return 0;
 }
 
-k5_json_string
-k5_json_string_create_base64(const void *data, size_t len)
+int
+k5_json_string_create_base64(const void *data, size_t len,
+                             k5_json_string *val_out)
 {
     char *base64;
-    k5_json_string s;
+    int ret;
 
+    *val_out = NULL;
     base64 = k5_base64_encode(data, len);
     if (base64 == NULL)
-        return NULL;
-    s = k5_json_string_create(base64);
+        return ENOMEM;
+    ret = k5_json_string_create(base64, val_out);
     free(base64);
-    return s;
+    return ret;
 }
 
 const char *
@@ -412,10 +424,21 @@ k5_json_string_utf8(k5_json_string string)
     return (const char *)string;
 }
 
-void *
-k5_json_string_unbase64(k5_json_string string, size_t *len_out)
+int
+k5_json_string_unbase64(k5_json_string string, unsigned char **data_out,
+                        size_t *len_out)
 {
-    return k5_base64_decode((const char *)string, len_out);
+    unsigned char *data;
+    size_t len;
+
+    *data_out = NULL;
+    *len_out = 0;
+    data = k5_base64_decode((const char *)string, &len);
+    if (data == NULL)
+        return (len == 0) ? ENOMEM : EINVAL;
+    *data_out = data;
+    *len_out = len;
+    return 0;
 }
 
 /*** Number type ***/
@@ -424,15 +447,18 @@ static struct json_type_st number_type = {
     K5_JSON_TID_NUMBER, "number", NULL
 };
 
-k5_json_number
-k5_json_number_create(long long number)
+int
+k5_json_number_create(long long number, k5_json_number *val_out)
 {
     k5_json_number n;
 
+    *val_out = NULL;
     n = alloc_value(&number_type, sizeof(long long));
-    if (n)
-        *((long long *)n) = number;
-    return n;
+    if (n == NULL)
+        return ENOMEM;
+    *((long long *)n) = number;
+    *val_out = n;
+    return 0;
 }
 
 long long
@@ -448,62 +474,61 @@ static const char quotemap_c[] = "\"\\/\b\f\n\r\t";
 static const char needs_quote[] = "\\\"\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17"
     "\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37";
 
-struct encode_ctx {
-    struct k5buf buf;
-    int ret;
-    int first;
-};
-
-static int encode_value(struct encode_ctx *j, k5_json_value val);
+static int encode_value(struct k5buf *buf, k5_json_value val);
 
 static void
-encode_string(struct encode_ctx *j, const char *str)
+encode_string(struct k5buf *buf, const char *str)
 {
     size_t n;
     const char *p;
 
-    krb5int_buf_add(&j->buf, "\"");
+    krb5int_buf_add(buf, "\"");
     while (*str != '\0') {
         n = strcspn(str, needs_quote);
-        krb5int_buf_add_len(&j->buf, str, n);
+        krb5int_buf_add_len(buf, str, n);
         str += n;
         if (*str == '\0')
             break;
-        krb5int_buf_add(&j->buf, "\\");
+        krb5int_buf_add(buf, "\\");
         p = strchr(quotemap_c, *str);
         if (p != NULL)
-            krb5int_buf_add_len(&j->buf, quotemap_json + (p - quotemap_c), 1);
+            krb5int_buf_add_len(buf, quotemap_json + (p - quotemap_c), 1);
         else
-            krb5int_buf_add_fmt(&j->buf, "u00%02X", (unsigned int)*str);
+            krb5int_buf_add_fmt(buf, "u00%02X", (unsigned int)*str);
         str++;
     }
-    krb5int_buf_add(&j->buf, "\"");
+    krb5int_buf_add(buf, "\"");
 }
 
+struct obj_ctx {
+    struct k5buf *buf;
+    int ret;
+    int first;
+};
+
 static void
-encode_dict_entry(void *ctx, const char *key, k5_json_value value)
+encode_obj_entry(void *ctx, const char *key, k5_json_value value)
 {
-    struct encode_ctx *j = ctx;
+    struct obj_ctx *j = ctx;
 
     if (j->ret)
         return;
     if (j->first)
         j->first = 0;
     else
-        krb5int_buf_add(&j->buf, ",");
-    encode_string(j, key);
-    krb5int_buf_add(&j->buf, ":");
-    j->ret = encode_value(j, value);
-    if (j->ret)
-        return;
+        krb5int_buf_add(j->buf, ",");
+    encode_string(j->buf, key);
+    krb5int_buf_add(j->buf, ":");
+    j->ret = encode_value(j->buf, value);
 }
 
 static int
-encode_value(struct encode_ctx *j, k5_json_value val)
+encode_value(struct k5buf *buf, k5_json_value val)
 {
     k5_json_tid type;
-    int first = 0, ret;
+    int ret;
     size_t i, len;
+    struct obj_ctx ctx;
 
     if (val == NULL)
         return EINVAL;
@@ -511,62 +536,63 @@ encode_value(struct encode_ctx *j, k5_json_value val)
     type = k5_json_get_tid(val);
     switch (type) {
     case K5_JSON_TID_ARRAY:
-        krb5int_buf_add(&j->buf, "[");
+        krb5int_buf_add(buf, "[");
         len = k5_json_array_length(val);
         for (i = 0; i < len; i++) {
             if (i != 0)
-                krb5int_buf_add(&j->buf, ",");
-            ret = encode_value(j, k5_json_array_get(val, i));
+                krb5int_buf_add(buf, ",");
+            ret = encode_value(buf, k5_json_array_get(val, i));
             if (ret)
                 return ret;
         }
-        krb5int_buf_add(&j->buf, "]");
-        break;
+        krb5int_buf_add(buf, "]");
+        return 0;
 
     case K5_JSON_TID_OBJECT:
-        krb5int_buf_add(&j->buf, "{");
-        first = j->first;
-        j->first = 1;
-        k5_json_object_iterate(val, encode_dict_entry, j);
-        krb5int_buf_add(&j->buf, "}");
-        j->first = first;
-        break;
+        krb5int_buf_add(buf, "{");
+        ctx.buf = buf;
+        ctx.ret = 0;
+        ctx.first = 1;
+        k5_json_object_iterate(val, encode_obj_entry, &ctx);
+        krb5int_buf_add(buf, "}");
+        return ctx.ret;
 
     case K5_JSON_TID_STRING:
-        encode_string(j, k5_json_string_utf8(val));
-        break;
+        encode_string(buf, k5_json_string_utf8(val));
+        return 0;
 
     case K5_JSON_TID_NUMBER:
-        krb5int_buf_add_fmt(&j->buf, "%lld", k5_json_number_value(val));
-        break;
+        krb5int_buf_add_fmt(buf, "%lld", k5_json_number_value(val));
+        return 0;
 
     case K5_JSON_TID_NULL:
-        krb5int_buf_add(&j->buf, "null");
-        break;
+        krb5int_buf_add(buf, "null");
+        return 0;
 
     case K5_JSON_TID_BOOL:
-        krb5int_buf_add(&j->buf, k5_json_bool_value(val) ? "true" : "false");
-        break;
+        krb5int_buf_add(buf, k5_json_bool_value(val) ? "true" : "false");
+        return 0;
 
     default:
-        return 1;
+        return EINVAL;
     }
-    return 0;
 }
 
-char *
-k5_json_encode(k5_json_value val)
+int
+k5_json_encode(k5_json_value val, char **json_out)
 {
-    struct encode_ctx j;
+    struct k5buf buf;
+    int ret;
 
-    j.ret = 0;
-    j.first = 1;
-    krb5int_buf_init_dynamic(&j.buf);
-    if (encode_value(&j, val)) {
-        krb5int_free_buf(&j.buf);
-        return NULL;
+    *json_out = NULL;
+    krb5int_buf_init_dynamic(&buf);
+    ret = encode_value(&buf, val);
+    if (ret) {
+        krb5int_free_buf(&buf);
+        return ret;
     }
-    return krb5int_buf_data(&j.buf);
+    *json_out = krb5int_buf_data(&buf);
+    return (*json_out == NULL) ? ENOMEM : 0;
 }
 
 /*** JSON decoding ***/
@@ -576,8 +602,7 @@ struct decode_ctx {
     size_t depth;
 };
 
-static k5_json_value
-parse_value(struct decode_ctx *ctx);
+static int parse_value(struct decode_ctx *ctx, k5_json_value *val_out);
 
 /* Consume whitespace.  Return 0 if there is anything left to parse after the
  * whitespace, -1 if not. */
@@ -621,66 +646,70 @@ hexval(unsigned char c)
 
 /* Parse a JSON number (which must be an integer in the signed 64-bit range; we
  * do not allow floating-point numbers). */
-static k5_json_number
-parse_number(struct decode_ctx *ctx)
+static int
+parse_number(struct decode_ctx *ctx, k5_json_number *val_out)
 {
     const unsigned long long umax = ~0ULL, smax = (1ULL << 63) - 1;
     unsigned long long number = 0;
     int neg = 1;
 
+    *val_out = NULL;
+
     if (*ctx->p == '-') {
         neg = -1;
         ctx->p++;
     }
 
     if (!is_digit(*ctx->p))
-        return NULL;
+        return EINVAL;
 
     /* Read the number into an unsigned 64-bit container, ensuring that we
      * don't overflow it. */
     while (is_digit(*ctx->p)) {
         if (number + 1 > umax / 10)
-            return NULL;
+            return EOVERFLOW;
         number = (number * 10) + (*ctx->p - '0');
         ctx->p++;
     }
 
     /* Make sure the unsigned 64-bit value fits in the signed 64-bit range. */
     if (number > smax + 1 || (number > smax && neg == 1))
-        return NULL;
+        return EOVERFLOW;
 
-    return k5_json_number_create(number * neg);
+    return k5_json_number_create(number * neg, val_out);
 }
 
 /* Parse a JSON string (which must not quote Unicode code points above 256). */
-static char *
-parse_string(struct decode_ctx *ctx)
+static int
+parse_string(struct decode_ctx *ctx, char **str_out)
 {
     const unsigned char *p, *start, *end = NULL;
     const char *q;
     char *buf, *pos;
     unsigned int code;
 
+    *str_out = NULL;
+
     /* Find the start and end of the string. */
     if (*ctx->p != '"')
-        return NULL;
+        return EINVAL;
     start = ++ctx->p;
     for (; *ctx->p != '\0'; ctx->p++) {
         if (*ctx->p == '\\') {
             ctx->p++;
             if (*ctx->p == '\0')
-                return NULL;
+                return EINVAL;
         } else if (*ctx->p == '"') {
             end = ctx->p++;
             break;
         }
     }
     if (end == NULL)
-        return NULL;
+        return EINVAL;
 
     pos = buf = malloc(end - start + 1);
     if (buf == NULL)
-        return NULL;
+        return ENOMEM;
     for (p = start; p < end;) {
         if (*p == '\\') {
             p++;
@@ -694,7 +723,7 @@ parse_string(struct decode_ctx *ctx)
                     /* Code points above 0xff don't need to be quoted, so we
                      * don't implement translating those into UTF-8. */
                     free(buf);
-                    return NULL;
+                    return EINVAL;
                 }
                 p += 5;
             } else {
@@ -703,7 +732,7 @@ parse_string(struct decode_ctx *ctx)
                     *pos++ = quotemap_c[q - quotemap_json];
                 } else {
                     free(buf);
-                    return NULL;
+                    return EINVAL;
                 }
                 p++;
             }
@@ -712,198 +741,245 @@ parse_string(struct decode_ctx *ctx)
         }
     }
     *pos = '\0';
-    return buf;
+    *str_out = buf;
+    return 0;
 }
 
-/*
- * Parse an object association and the following comma.  Return 1 if an
- * association was parsed, 0 if the end of the object was reached, and -1 on
- * error.
- */
+/* Parse an object association and place it into obj. */
 static int
-parse_pair(k5_json_object obj, struct decode_ctx *ctx)
+parse_object_association(k5_json_object obj, struct decode_ctx *ctx)
 {
     char *key = NULL;
-    k5_json_value value;
-
-    if (white_spaces(ctx))
-        goto err;
-
-    /* Check for the end of the object. */
-    if (*ctx->p == '}') {
-        ctx->p++;
-        return 0;
-    }
+    k5_json_value val;
+    int ret;
 
     /* Parse the key and value. */
-    key = parse_string(ctx);
-    if (key == NULL)
-        goto err;
+    ret = parse_string(ctx, &key);
+    if (ret)
+        return ret;
     if (white_spaces(ctx))
-        goto err;
+        goto invalid;
     if (*ctx->p != ':')
-        goto err;
+        goto invalid;
     ctx->p++;
     if (white_spaces(ctx))
-        goto err;
-    value = parse_value(ctx);
-    if (value == NULL) {
+        goto invalid;
+    ret = parse_value(ctx, &val);
+    if (ret) {
         free(key);
-        return -1;
+        return ret;
     }
 
-    /* Add the key and value to the object. */
-    k5_json_object_set(obj, key, value);
+    /* Add the key and value to obj. */
+    ret = k5_json_object_set(obj, key, val);
     free(key);
-    key = NULL;
-    k5_json_release(value);
-
-    /* Consume the following comma if this isn't the last item. */
-    if (white_spaces(ctx))
-        goto err;
-    if (*ctx->p == ',')
-        ctx->p++;
-    else if (*ctx->p != '}')
-        goto err;
+    k5_json_release(val);
+    return ret;
 
-    return 1;
-
-err:
+invalid:
     free(key);
-    return -1;
+    return EINVAL;
 }
 
 /* Parse a JSON object. */
-static k5_json_object
-parse_object(struct decode_ctx *ctx)
+static int
+parse_object(struct decode_ctx *ctx, k5_json_object *val_out)
 {
-    k5_json_object obj;
+    k5_json_object obj = NULL;
     int ret;
 
-    obj = k5_json_object_create();
-    if (obj == NULL)
-        return NULL;
+    *val_out = NULL;
 
+    /* Parse past the opening brace. */
+    if (*ctx->p != '{')
+        return EINVAL;
     ctx->p++;
-    while ((ret = parse_pair(obj, ctx)) > 0)
-        ;
-    if (ret < 0) {
-        k5_json_release(obj);
-        return NULL;
-    }
-    return obj;
-}
-
-/* Parse a JSON array item and the following comma.  Return 1 if an item was
- * parsed, 0 if the end of the array was reached, and -1 on error. */
-static int
-parse_item(k5_json_array array, struct decode_ctx *ctx)
-{
-    k5_json_value value;
-
     if (white_spaces(ctx))
-        return -1;
+        return EINVAL;
 
-    if (*ctx->p == ']') {
-        ctx->p++;
-        return 0;
-    }
+    ret = k5_json_object_create(&obj);
+    if (ret)
+        return ret;
 
-    value = parse_value(ctx);
-    if (value == NULL)
-        return -1;
+    /* Pairs associations until we reach the terminating brace. */
+    if (*ctx->p != '}') {
+        while (1) {
+            ret = parse_object_association(obj, ctx);
+            if (ret) {
+                k5_json_release(obj);
+                return ret;
+            }
+            if (white_spaces(ctx))
+                goto invalid;
+            if (*ctx->p == '}')
+                break;
+            if (*ctx->p != ',')
+                goto invalid;
+            ctx->p++;
+            if (white_spaces(ctx))
+                goto invalid;
+        }
+    }
+    ctx->p++;
+    *val_out = obj;
+    return 0;
 
-    k5_json_array_add(array, value);
-    k5_json_release(value);
+invalid:
+    k5_json_release(obj);
+    return EINVAL;
+}
 
-    if (white_spaces(ctx))
-        return -1;
+/* Parse an value and place it into array. */
+static int
+parse_array_item(k5_json_array array, struct decode_ctx *ctx)
+{
+    k5_json_value val;
+    int ret;
 
-    if (*ctx->p == ',')
-        ctx->p++;
-    else if (*ctx->p != ']')
-        return -1;
-    return 1;
+    ret = parse_value(ctx, &val);
+    if (ret)
+        return ret;
+    ret = k5_json_array_add(array, val);
+    k5_json_release(val);
+    return ret;
 }
 
 /* Parse a JSON array. */
-static k5_json_array
-parse_array(struct decode_ctx *ctx)
+static int
+parse_array(struct decode_ctx *ctx, k5_json_array *val_out)
 {
-    k5_json_array array = k5_json_array_create();
+    k5_json_array array = NULL;
     int ret;
 
-    assert(*ctx->p == '[');
-    ctx->p += 1;
+    *val_out = NULL;
 
-    while ((ret = parse_item(array, ctx)) > 0)
-        ;
-    if (ret < 0) {
-        k5_json_release(array);
-        return NULL;
+    /* Parse past the opening bracket. */
+    if (*ctx->p != '[')
+        return EINVAL;
+    ctx->p++;
+    if (white_spaces(ctx))
+        return EINVAL;
+
+    ret = k5_json_array_create(&array);
+    if (ret)
+        return ret;
+
+    /* Pairs values until we reach the terminating bracket. */
+    if (*ctx->p != ']') {
+        while (1) {
+            ret = parse_array_item(array, ctx);
+            if (ret) {
+                k5_json_release(array);
+                return ret;
+            }
+            if (white_spaces(ctx))
+                goto invalid;
+            if (*ctx->p == ']')
+                break;
+            if (*ctx->p != ',')
+                goto invalid;
+            ctx->p++;
+            if (white_spaces(ctx))
+                goto invalid;
+        }
     }
-    return array;
+    ctx->p++;
+    *val_out = array;
+    return 0;
+
+invalid:
+    k5_json_release(array);
+    return EINVAL;
 }
 
 /* Parse a JSON value of any type. */
-static k5_json_value
-parse_value(struct decode_ctx *ctx)
+static int
+parse_value(struct decode_ctx *ctx, k5_json_value *val_out)
 {
-    k5_json_value v;
-    char *str;
+    k5_json_null null;
+    k5_json_bool bval;
+    k5_json_number num;
+    k5_json_string str;
+    k5_json_object obj;
+    k5_json_array array;
+    char *cstring;
+    int ret;
+
+    *val_out = NULL;
 
     if (white_spaces(ctx))
-        return NULL;
+        return EINVAL;
 
     if (*ctx->p == '"') {
-        str = parse_string(ctx);
-        if (str == NULL)
-            return NULL;
-        v = k5_json_string_create(str);
-        free(str);
-        return v;
+        ret = parse_string(ctx, &cstring);
+        if (ret)
+            return ret;
+        ret = k5_json_string_create(cstring, &str);
+        free(cstring);
+        if (ret)
+            return ret;
+        *val_out = str;
     } else if (*ctx->p == '{') {
         if (ctx->depth-- == 1)
-            return NULL;
-        v = parse_object(ctx);
+            return EINVAL;
+        ret = parse_object(ctx, &obj);
+        if (ret)
+            return ret;
         ctx->depth++;
-        return v;
+        *val_out = obj;
     } else if (*ctx->p == '[') {
         if (ctx->depth-- == 1)
-            return NULL;
-        v = parse_array(ctx);
+            return EINVAL;
+        ret = parse_array(ctx, &array);
         ctx->depth++;
-        return v;
+        *val_out = array;
     } else if (is_digit(*ctx->p) || *ctx->p == '-') {
-        return parse_number(ctx);
-    }
-
-    if (strncmp((char *)ctx->p, "null", 4) == 0) {
+        ret = parse_number(ctx, &num);
+        if (ret)
+            return ret;
+        *val_out = num;
+    } else if (strncmp((char *)ctx->p, "null", 4) == 0) {
         ctx->p += 4;
-        return k5_json_null_create();
+        ret = k5_json_null_create(&null);
+        if (ret)
+            return ret;
+        *val_out = null;
     } else if (strncmp((char *)ctx->p, "true", 4) == 0) {
         ctx->p += 4;
-        return k5_json_bool_create(1);
+        ret = k5_json_bool_create(1, &bval);
+        if (ret)
+            return ret;
+        *val_out = bval;
     } else if (strncmp((char *)ctx->p, "false", 5) == 0) {
         ctx->p += 5;
-        return k5_json_bool_create(0);
+        ret = k5_json_bool_create(0, &bval);
+        if (ret)
+            return ret;
+        *val_out = bval;
+    } else {
+        return EINVAL;
     }
 
-    return NULL;
+    return 0;
 }
 
-k5_json_value
-k5_json_decode(const char *string)
+int
+k5_json_decode(const char *string, k5_json_value *val_out)
 {
     struct decode_ctx ctx;
-    k5_json_value v;
+    k5_json_value val;
+    int ret;
 
+    *val_out = NULL;
     ctx.p = (unsigned char *)string;
     ctx.depth = MAX_DECODE_DEPTH;
-    v = parse_value(&ctx);
+    ret = parse_value(&ctx, &val);
+    if (ret)
+        return ret;
     if (white_spaces(&ctx) == 0) {
-        k5_json_release(v);
-        return NULL;
+        k5_json_release(val);
+        return EINVAL;
     }
-    return v;
+    *val_out = val;
+    return 0;
 }
index 7b30007afddd838a3019c1d4fd9d98b4b8938705..afb02ee6144197e6b14b98c6eb6a0df97ed9263e 100644 (file)
@@ -88,14 +88,18 @@ check(int pred, const char *str)
 static void
 test_array()
 {
-    k5_json_string v1 = k5_json_string_create("abc");
-    k5_json_number v2 = k5_json_number_create(2);
-    k5_json_null v3 = k5_json_null_create();
-    k5_json_array a = k5_json_array_create();
+    k5_json_string v1;
+    k5_json_number v2;
+    k5_json_null v3;
+    k5_json_array a;
     k5_json_value v;
 
+    k5_json_array_create(&a);
+    k5_json_string_create("abc", &v1);
     k5_json_array_add(a, v1);
+    k5_json_number_create(2, &v2);
     k5_json_array_add(a, v2);
+    k5_json_null_create(&v3);
     k5_json_array_add(a, v3);
 
     check(k5_json_array_length(a) == 3, "array length");
@@ -118,11 +122,13 @@ static void
 test_object(void)
 {
     k5_json_object object;
-    k5_json_number n, v1 = k5_json_number_create(1);
-    k5_json_string s, v2 = k5_json_string_create("hejsan");
+    k5_json_number n, v1;
+    k5_json_string s, v2;
 
-    object = k5_json_object_create();
+    k5_json_object_create(&object);
+    k5_json_number_create(1, &v1);
     k5_json_object_set(object, "key1", v1);
+    k5_json_string_create("hejsan", &v2);
     k5_json_object_set(object, "key2", v2);
 
     n = k5_json_object_get(object, "key1");
@@ -142,20 +148,21 @@ static void
 test_string(void)
 {
     k5_json_string s1, s2, s3;
-    void *data;
+    unsigned char *data;
     size_t len;
 
-    s1 = k5_json_string_create("hejsan");
-    s2 = k5_json_string_create("hejsan");
-    s3 = k5_json_string_create_base64("55555", 5);
+    k5_json_string_create("hejsan", &s1);
+    k5_json_string_create("hejsan", &s2);
+    k5_json_string_create_base64("55555", 5, &s3);
 
     if (strcmp(k5_json_string_utf8(s1), k5_json_string_utf8(s2)) != 0)
         err("Identical strings are not identical");
     if (strcmp(k5_json_string_utf8(s3), "NTU1NTU=") != 0)
         err("base64 string has incorrect value");
-    data = k5_json_string_unbase64(s3, &len);
-    if (data == NULL || len != 5 || memcmp(data, "55555", 5) != 0)
+    k5_json_string_unbase64(s3, &data, &len);
+    if (len != 5 || memcmp(data, "55555", 5) != 0)
         err("base64 string doesn't decode to correct value");
+    free(data);
 
     k5_json_release(s1);
     k5_json_release(s2);
@@ -181,20 +188,17 @@ test_json(void)
     int i;
     k5_json_value v, v2;
 
-    v = k5_json_decode("\"string\"");
-    check(v != NULL, "string1");
+    check(k5_json_decode("\"string\"", &v) == 0, "string1");
     check(k5_json_get_tid(v) == K5_JSON_TID_STRING, "string1 tid");
     check(strcmp(k5_json_string_utf8(v), "string") == 0, "string1 utf8");
     k5_json_release(v);
 
-    v = k5_json_decode("\t \"foo\\\"bar\" ");
-    check(v != NULL, "string2");
+    check(k5_json_decode("\t \"foo\\\"bar\" ", &v) == 0, "string2");
     check(k5_json_get_tid(v) == K5_JSON_TID_STRING, "string2 tid");
     check(strcmp(k5_json_string_utf8(v), "foo\"bar") == 0, "string2 utf8");
     k5_json_release(v);
 
-    v = k5_json_decode(" { \"key\" : \"value\" }");
-    check(v != NULL, "object1");
+    check(k5_json_decode(" { \"key\" : \"value\" }", &v) == 0, "object1");
     check(k5_json_get_tid(v) == K5_JSON_TID_OBJECT, "object1 tid");
     v2 = k5_json_object_get(v, "key");
     check(v2 != NULL, "object[key]");
@@ -202,9 +206,8 @@ test_json(void)
     check(strcmp(k5_json_string_utf8(v2), "value") == 0, "object1[key] utf8");
     k5_json_release(v);
 
-    v = k5_json_decode("{ \"k1\" : { \"k2\" : \"s2\", \"k3\" : \"s3\" }, "
-                       "\"k4\" : \"s4\" }");
-    check(v != NULL, "object2");
+    check(k5_json_decode("{ \"k1\" : { \"k2\" : \"s2\", \"k3\" : \"s3\" }, "
+                         "\"k4\" : \"s4\" }", &v) == 0, "object2");
     v2 = k5_json_object_get(v, "k1");
     check(v2 != NULL, "object2[k1]");
     check(k5_json_get_tid(v2) == K5_JSON_TID_OBJECT, "object2[k1] tid");
@@ -214,28 +217,24 @@ test_json(void)
     check(strcmp(k5_json_string_utf8(v2), "s3") == 0, "object2[k1][k3] utf8");
     k5_json_release(v);
 
-    v = k5_json_decode("{ \"k1\" : 1 }");
-    check(v != NULL, "object3");
+    check(k5_json_decode("{ \"k1\" : 1 }", &v) == 0, "object3");
     check(k5_json_get_tid(v) == K5_JSON_TID_OBJECT, "object3 id");
     v2 = k5_json_object_get(v, "k1");
     check(k5_json_get_tid(v2) == K5_JSON_TID_NUMBER, "object3[k1] tid");
     check(k5_json_number_value(v2) == 1, "object3[k1] value");
     k5_json_release(v);
 
-    v = k5_json_decode("-10");
-    check(v != NULL, "number1");
+    check(k5_json_decode("-10", &v) == 0, "number1");
     check(k5_json_get_tid(v) == K5_JSON_TID_NUMBER, "number1 tid");
     check(k5_json_number_value(v) == -10, "number1 value");
     k5_json_release(v);
 
-    v = k5_json_decode("99");
-    check(v != NULL, "number2");
+    check(k5_json_decode("99", &v) == 0, "number2");
     check(k5_json_get_tid(v) == K5_JSON_TID_NUMBER, "number2 tid");
     check(k5_json_number_value(v) == 99, "number2 value");
     k5_json_release(v);
 
-    v = k5_json_decode(" [ 1 ]");
-    check(v != NULL, "array1");
+    check(k5_json_decode(" [ 1 ]", &v) == 0, "array1");
     check(k5_json_get_tid(v) == K5_JSON_TID_ARRAY, "array1 tid");
     check(k5_json_array_length(v) == 1, "array1 len");
     v2 = k5_json_array_get(v, 0);
@@ -244,8 +243,7 @@ test_json(void)
     check(k5_json_number_value(v2) == 1, "array1[0] value");
     k5_json_release(v);
 
-    v = k5_json_decode(" [ -1 ]");
-    check(v != NULL, "array2");
+    check(k5_json_decode(" [ -1 ]", &v) == 0, "array2");
     check(k5_json_get_tid(v) == K5_JSON_TID_ARRAY, "array2 tid");
     check(k5_json_array_length(v) == 1, "array2 len");
     v2 = k5_json_array_get(v, 0);
@@ -254,20 +252,18 @@ test_json(void)
     check(k5_json_number_value(v2) == -1, "array2[0] value");
     k5_json_release(v);
 
-    v = k5_json_decode("18446744073709551616");
-    check(v == NULL, "unsigned 64-bit overflow");
-    v = k5_json_decode("9223372036854775808");
-    check(v == NULL, "signed 64-bit positive overflow");
-    v = k5_json_decode("-9223372036854775809");
-    check(v == NULL, "signed 64-bit negative overflow");
+    check(k5_json_decode("18446744073709551616", &v) == EOVERFLOW,
+          "unsigned 64-bit overflow");
+    check(k5_json_decode("9223372036854775808", &v) == EOVERFLOW,
+          "signed 64-bit positive overflow");
+    check(k5_json_decode("-9223372036854775809", &v) == EOVERFLOW,
+          "signed 64-bit negative overflow");
 
     for (tptr = tests; *tptr != NULL; tptr++) {
         s = strdup(*tptr);
-        v = k5_json_decode(s);
-        if (v == NULL)
+        if (k5_json_decode(s, &v))
             err(s);
-        enc = k5_json_encode(v);
-        if (enc == NULL || strcmp(enc, s) != 0)
+        if (k5_json_encode(v, &enc) || strcmp(enc, s) != 0)
             err(s);
         free(enc);
         k5_json_release(v);
@@ -278,7 +274,8 @@ test_json(void)
             orig = *p;
             for (i = 0; i <= 255; i++) {
                 *p = i;
-                k5_json_release(k5_json_decode(s));
+                k5_json_decode(s, &v);
+                k5_json_release(v);
             }
             *p = orig;
         }