diff --git a/src/gpgme-json.c b/src/gpgme-json.c index 75f1a095..6ba79e5a 100644 --- a/src/gpgme-json.c +++ b/src/gpgme-json.c @@ -64,6 +64,7 @@ static int opt_interactive; */ #define xtrymalloc(a) gpgrt_malloc ((a)) +#define xtrystrdup(a) gpgrt_strdup ((a)) #define xmalloc(a) ({ \ void *_r = gpgrt_malloc ((a)); \ if (!_r) \ @@ -140,6 +141,87 @@ xjson_AddBoolToObject (cjson_t object, const char *name, int abool) return ; } +/* This is similar to cJSON_AddStringToObject but takes a gpgme DATA + * object and adds it under NAME as a base 64 encoded string to + * OBJECT. Ownership of DATA is transferred to this function. */ +static gpg_error_t +add_base64_to_object (cjson_t object, const char *name, gpgme_data_t data) +{ + gpg_err_code_t err; + estream_t fp = NULL; + gpgrt_b64state_t state = NULL; + cjson_t j_str = NULL; + void *buffer = NULL; + size_t buflen; + + fp = es_fopenmem (0, "rwb"); + if (!fp) + { + err = gpg_err_code_from_syserror (); + goto leave; + } + state = gpgrt_b64enc_start (fp, ""); + if (!state) + { + err = gpg_err_code_from_syserror (); + goto leave; + } + + gpgme_data_write (data, "", 1); /* Make sure we have a string. */ + buffer = gpgme_data_release_and_get_mem (data, &buflen); + data = NULL; + if (!buffer) + { + err = gpg_error_from_syserror (); + goto leave; + } + + err = gpgrt_b64enc_write (state, buffer, buflen); + if (err) + goto leave; + xfree (buffer); + buffer = NULL; + + err = gpgrt_b64enc_finish (state); + state = NULL; + if (err) + return err; + + es_fputc (0, fp); + if (es_fclose_snatch (fp, &buffer, NULL)) + { + fp = NULL; + err = gpg_error_from_syserror (); + goto leave; + } + fp = NULL; + + j_str = cJSON_CreateStringConvey (buffer); + if (!j_str) + { + err = gpg_error_from_syserror (); + goto leave; + } + buffer = NULL; + + if (!cJSON_AddItemToObject (object, name, j_str)) + { + err = gpg_error_from_syserror (); + cJSON_Delete (j_str); + j_str = NULL; + goto leave; + } + j_str = NULL; + + leave: + xfree (buffer); + cJSON_Delete (j_str); + gpgrt_b64enc_finish (state); + es_fclose (fp); + gpgme_data_release (data); + return err; +} + /* Create a JSON error object. If JSON is not NULL the error message * is appended to that object. An existing "type" item will be replaced. */ @@ -386,6 +468,72 @@ release_context (gpgme_ctx_t ctx) } + +/* Given a Base-64 encoded string object in JSON return a gpgme data + * object at R_DATA. */ +static gpg_error_t +data_from_base64_string (gpgme_data_t *r_data, cjson_t json) +{ +#if GPGRT_VERSION_NUMBER < 0x011d00 /* 1.29 */ + *r_data = NULL; + return gpg_error (GPG_ERR_NOT_SUPPORTED); +#else + gpg_error_t err; + size_t len; + char *buf = NULL; + gpgrt_b64state_t state = NULL; + gpgme_data_t data = NULL; + + *r_data = NULL; + + /* A quick check on the JSON. */ + if (!cjson_is_string (json)) + { + err = gpg_error (GPG_ERR_INV_VALUE); + goto leave; + } + + state = gpgrt_b64dec_start (NULL); + if (!state) + { + err = gpg_err_code_from_syserror (); + goto leave; + } + + /* Fixme: Data duplication - we should see how to snatch the memory + * from the json object. */ + len = strlen (json->valuestring); + buf = xtrystrdup (json->valuestring); + if (!buf) + { + err = gpg_error_from_syserror (); + goto leave; + } + + err = gpgrt_b64dec_proc (state, buf, len, &len); + if (err) + goto leave; + + err = gpgrt_b64dec_finish (state); + state = NULL; + if (err) + goto leave; + + err = gpgme_data_new_from_mem (&data, buf, len, 1); + if (err) + goto leave; + *r_data = data; + data = NULL; + + leave: + xfree (data); + xfree (buf); + gpgrt_b64dec_finish (state); + return err; +#endif +} + + /* * Implementaion of the commands. @@ -487,16 +635,23 @@ op_encrypt (cjson_t request, cjson_t result) } if (opt_base64) { - err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); - goto leave; + err = data_from_base64_string (&input, j_input); + if (err) + { + error_object (result, "Error decoding Base-64 encoded 'data': %s", + gpg_strerror (err)); + goto leave; + } } - err = gpgme_data_new_from_mem (&input, j_input->valuestring, - strlen (j_input->valuestring), 0); - if (err) + else { - error_object (result, "Error creating input data object: %s", - gpg_strerror (err)); - goto leave; + err = gpgme_data_new_from_mem (&input, j_input->valuestring, + strlen (j_input->valuestring), 0); + if (err) + { + error_object (result, "Error getting 'data': %s", gpg_strerror (err)); + goto leave; + } } /* Create an output data object. */ @@ -541,9 +696,10 @@ op_encrypt (cjson_t request, cjson_t result) } else { - error_object (result, "Binary output is not yet supported"); - err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); - goto leave; + err = add_base64_to_object (result, "data", output); + output = NULL; + if (err) + goto leave; } leave: @@ -685,7 +841,10 @@ process_request (const char *request) leave: cJSON_Delete (json); json = NULL; - res = cJSON_Print (response); + if (opt_interactive) + res = cJSON_Print (response); + else + res = cJSON_PrintUnformatted (response); if (!res) log_error ("Printing JSON data failed\n"); cJSON_Delete (response);