aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/gpgme-json.c266
1 files changed, 141 insertions, 125 deletions
diff --git a/src/gpgme-json.c b/src/gpgme-json.c
index d3309b8c..7bb1baf1 100644
--- a/src/gpgme-json.c
+++ b/src/gpgme-json.c
@@ -48,14 +48,13 @@ int main (void){fputs ("Build with Libgpg-error >= 1.28!\n", stderr);return 1;}
/* We don't allow a request with more than 64 MiB. */
#define MAX_REQUEST_SIZE (64 * 1024 * 1024)
-/* Minimal, default and maximum chunk size for returned data. The
- * first chunk is returned directly. If the "more" flag is also
- * returned, a "getmore" command needs to be used to get the next
- * chunk. Right now this value covers just the value of the "data"
- * element; so to cover for the other returned objects this values
- * needs to be lower than the maximum allowed size of the browser. */
-#define MIN_REPLY_CHUNK_SIZE 512
-#define DEF_REPLY_CHUNK_SIZE (512 * 1024)
+/* Minimal chunk size for returned data.*/
+#define MIN_REPLY_CHUNK_SIZE 30
+
+/* If no chunksize is provided we print everything. Changing
+ * this to a positive value will result in all messages beeing
+ * chunked. */
+#define DEF_REPLY_CHUNK_SIZE 0
#define MAX_REPLY_CHUNK_SIZE (10 * 1024 * 1024)
@@ -67,6 +66,7 @@ static cjson_t error_object (cjson_t json, const char *message,
...) GPGRT_ATTR_PRINTF(2,3);
static char *error_object_string (const char *message,
...) GPGRT_ATTR_PRINTF(1,2);
+static char *process_request (const char *request);
/* True if interactive mode is active. */
@@ -80,8 +80,6 @@ static struct
char *buffer; /* Malloced data or NULL if not used. */
size_t length; /* Length of that data. */
size_t written; /* # of already written bytes from BUFFER. */
- const char *type;/* The "type" of the data. */
- int base64; /* The "base64" flag of the data. */
} pending_data;
@@ -1336,29 +1334,20 @@ get_string_data (cjson_t request, cjson_t result, const char *name,
}
-
-/*
- * Implementation of the commands.
- */
-
-
-/* Create a "data" object and the "type", "base64" and "more" flags
+/* Create a "data" object and the "type" and "base64" flags
* from DATA and append them to RESULT. Ownership of DATA is
* transferred to this function. TYPE must be a fixed string.
- * CHUNKSIZE is the chunksize requested from the caller. If BASE64 is
- * -1 the need for base64 encoding is determined by the content of
- * DATA, all other values are taken as true or false. Note that
- * op_getmore has similar code but works on PENDING_DATA which is set
- * here. */
+ * If BASE64 is -1 the need for base64 encoding is determined
+ * by the content of DATA, all other values are taken as true
+ * or false. */
static gpg_error_t
-make_data_object (cjson_t result, gpgme_data_t data, size_t chunksize,
+make_data_object (cjson_t result, gpgme_data_t data,
const char *type, int base64)
{
gpg_error_t err;
char *buffer;
const char *s;
size_t buflen, n;
- int c;
if (!base64 || base64 == -1) /* Make sure that we really have a string. */
gpgme_data_write (data, "", 1);
@@ -1390,49 +1379,98 @@ make_data_object (cjson_t result, gpgme_data_t data, size_t chunksize,
}
}
- /* Adjust the chunksize if we need to do base64 conversion. */
- if (base64)
- chunksize = (chunksize / 4) * 3;
-
xjson_AddStringToObject (result, "type", type);
xjson_AddBoolToObject (result, "base64", base64);
- if (buflen > chunksize)
- {
- xjson_AddBoolToObject (result, "more", 1);
+ if (base64)
+ err = add_base64_to_object (result, "data", buffer, buflen);
+ else
+ err = cjson_AddStringToObject (result, "data", buffer);
- c = buffer[chunksize];
- buffer[chunksize] = 0;
- if (base64)
- err = add_base64_to_object (result, "data", buffer, chunksize);
- else
- err = cjson_AddStringToObject (result, "data", buffer);
- buffer[chunksize] = c;
- if (err)
- goto leave;
+ leave:
+ gpgme_free (buffer);
+ return err;
+}
- pending_data.buffer = buffer;
- buffer = NULL;
- pending_data.length = buflen;
- pending_data.written = chunksize;
- pending_data.type = type;
- pending_data.base64 = base64;
- }
+
+/* Encode and chunk response.
+ *
+ * If neccessary this base64 encodes and chunks the repsonse
+ * for getmore so that we always return valid json independent
+ * of the chunksize.
+ *
+ * A chunked repsonse contains the base64 encoded chunk
+ * as a string and a boolean if there is still more data
+ * available for getmore like:
+ * {
+ * chunk: "SGVsbG8gV29ybGQK"
+ * more: true
+ * }
+ *
+ * Chunking is only done if the response is larger then the
+ * chunksize.
+ *
+ * caller has to xfree the return value.
+ */
+static char *
+encode_and_chunk (cjson_t request, cjson_t response)
+{
+ char *data;
+ gpg_error_t err = 0;
+ size_t chunksize;
+ char *getmore_request = NULL;
+
+ if (opt_interactive)
+ data = cJSON_Print (response);
else
+ data = cJSON_PrintUnformatted (response);
+
+ if (!data)
+ goto leave;
+
+ if ((err = get_chunksize (request, &chunksize)))
+ goto leave;
+
+ if (!chunksize)
+ goto leave;
+
+ pending_data.buffer = data;
+ /* Data should already be encoded so that it does not
+ contain 0.*/
+ pending_data.length = strlen (data);
+ pending_data.written = 0;
+
+ if (gpgrt_asprintf (&getmore_request,
+ "{ \"op\":\"getmore\", \"chunksize\": %i }",
+ (int) chunksize) == -1)
{
- if (base64)
- err = add_base64_to_object (result, "data", buffer, buflen);
- else
- err = cjson_AddStringToObject (result, "data", buffer);
+ err = gpg_error_from_syserror ();
+ goto leave;
}
- leave:
- gpgme_free (buffer);
- return err;
+ data = process_request (getmore_request);
+
+leave:
+ xfree (getmore_request);
+
+ if (err)
+ {
+ cjson_t err_obj = gpg_error_object (NULL, err,
+ "Encode and chunk failed: %s",
+ gpgme_strerror (err));
+ if (opt_interactive)
+ return cJSON_Print (err_obj);
+ return cJSON_PrintUnformatted (err_obj);
+ }
+
+ return data;
}
+/*
+ * Implementation of the commands.
+ */
static const char hlp_encrypt[] =
"op: \"encrypt\"\n"
"keys: Array of strings with the fingerprints or user-ids\n"
@@ -1442,7 +1480,6 @@ static const char hlp_encrypt[] =
"\n"
"Optional parameters:\n"
"protocol: Either \"openpgp\" (default) or \"cms\".\n"
- "chunksize: Max number of bytes in the resulting \"data\".\n"
"signing_keys: Similar to the keys parameter for added signing.\n"
" (openpgp only)"
"\n"
@@ -1462,8 +1499,7 @@ static const char hlp_encrypt[] =
"data: Unless armor mode is used a Base64 encoded binary\n"
" ciphertext. In armor mode a string with an armored\n"
" OpenPGP or a PEM message.\n"
- "base64: Boolean indicating whether data is base64 encoded.\n"
- "more: Optional boolean indicating that \"getmore\" is required.";
+ "base64: Boolean indicating whether data is base64 encoded.";
static gpg_error_t
op_encrypt (cjson_t request, cjson_t result)
{
@@ -1471,7 +1507,6 @@ op_encrypt (cjson_t request, cjson_t result)
gpgme_ctx_t ctx = NULL;
gpgme_protocol_t protocol;
char **signing_patterns = NULL;
- size_t chunksize;
int opt_mime;
char *keystring = NULL;
gpgme_data_t input = NULL;
@@ -1484,8 +1519,6 @@ op_encrypt (cjson_t request, cjson_t result)
if ((err = get_protocol (request, &protocol)))
goto leave;
ctx = get_context (protocol);
- if ((err = get_chunksize (request, &chunksize)))
- goto leave;
if ((err = get_boolean_flag (request, "mime", 0, &opt_mime)))
goto leave;
@@ -1599,7 +1632,7 @@ op_encrypt (cjson_t request, cjson_t result)
input = NULL;
/* We need to base64 if armoring has not been requested. */
- err = make_data_object (result, output, chunksize,
+ err = make_data_object (result, output,
"ciphertext", !gpgme_get_armor (ctx));
output = NULL;
@@ -1623,7 +1656,6 @@ static const char hlp_decrypt[] =
"\n"
"Optional parameters:\n"
"protocol: Either \"openpgp\" (default) or \"cms\".\n"
- "chunksize: Max number of bytes in the resulting \"data\".\n"
"\n"
"Optional boolean flags (default is false):\n"
"base64: Input data is base64 encoded.\n"
@@ -1678,15 +1710,13 @@ static const char hlp_decrypt[] =
" name\n"
" value\n"
" Number values:\n"
- " flags\n"
- "more: Optional boolean indicating that \"getmore\" is required.";
+ " flags\n";
static gpg_error_t
op_decrypt (cjson_t request, cjson_t result)
{
gpg_error_t err;
gpgme_ctx_t ctx = NULL;
gpgme_protocol_t protocol;
- size_t chunksize;
gpgme_data_t input = NULL;
gpgme_data_t output = NULL;
gpgme_decrypt_result_t decrypt_result;
@@ -1695,8 +1725,6 @@ op_decrypt (cjson_t request, cjson_t result)
if ((err = get_protocol (request, &protocol)))
goto leave;
ctx = get_context (protocol);
- if ((err = get_chunksize (request, &chunksize)))
- goto leave;
if ((err = get_string_data (request, result, "data", &input)))
goto leave;
@@ -1734,7 +1762,7 @@ op_decrypt (cjson_t request, cjson_t result)
verify_result_to_json (verify_result));
}
- err = make_data_object (result, output, chunksize, "plaintext", -1);
+ err = make_data_object (result, output, "plaintext", -1);
output = NULL;
if (err)
@@ -1761,7 +1789,6 @@ static const char hlp_sign[] =
"\n"
"Optional parameters:\n"
"protocol: Either \"openpgp\" (default) or \"cms\".\n"
- "chunksize: Max number of bytes in the resulting \"data\".\n"
"sender: The mail address of the sender.\n"
"mode: A string with the signing mode can be:\n"
" detached (default)\n"
@@ -1777,15 +1804,13 @@ static const char hlp_sign[] =
"data: Unless armor mode is used a Base64 encoded binary\n"
" signature. In armor mode a string with an armored\n"
" OpenPGP or a PEM message.\n"
- "base64: Boolean indicating whether data is base64 encoded.\n"
- "more: Optional boolean indicating that \"getmore\" is required.";
+ "base64: Boolean indicating whether data is base64 encoded.\n";
static gpg_error_t
op_sign (cjson_t request, cjson_t result)
{
gpg_error_t err;
gpgme_ctx_t ctx = NULL;
gpgme_protocol_t protocol;
- size_t chunksize;
char **patterns = NULL;
gpgme_data_t input = NULL;
gpgme_data_t output = NULL;
@@ -1798,8 +1823,6 @@ op_sign (cjson_t request, cjson_t result)
if ((err = get_protocol (request, &protocol)))
goto leave;
ctx = get_context (protocol);
- if ((err = get_chunksize (request, &chunksize)))
- goto leave;
if ((err = get_boolean_flag (request, "armor", 0, &abool)))
goto leave;
@@ -1881,7 +1904,7 @@ op_sign (cjson_t request, cjson_t result)
input = NULL;
/* We need to base64 if armoring has not been requested. */
- err = make_data_object (result, output, chunksize,
+ err = make_data_object (result, output,
"signature", !gpgme_get_armor (ctx));
output = NULL;
@@ -1904,7 +1927,6 @@ static const char hlp_verify[] =
"\n"
"Optional parameters:\n"
"protocol: Either \"openpgp\" (default) or \"cms\".\n"
- "chunksize: Max number of bytes in the resulting \"data\".\n"
"signature: A detached signature. If missing opaque is assumed.\n"
"\n"
"Optional boolean flags (default is false):\n"
@@ -1959,15 +1981,13 @@ static const char hlp_verify[] =
" name\n"
" value\n"
" Number values:\n"
- " flags\n"
- "more: Optional boolean indicating that \"getmore\" is required.";
+ " flags\n";
static gpg_error_t
op_verify (cjson_t request, cjson_t result)
{
gpg_error_t err;
gpgme_ctx_t ctx = NULL;
gpgme_protocol_t protocol;
- size_t chunksize;
gpgme_data_t input = NULL;
gpgme_data_t signature = NULL;
gpgme_data_t output = NULL;
@@ -1976,8 +1996,6 @@ op_verify (cjson_t request, cjson_t result)
if ((err = get_protocol (request, &protocol)))
goto leave;
ctx = get_context (protocol);
- if ((err = get_chunksize (request, &chunksize)))
- goto leave;
if ((err = get_string_data (request, result, "data", &input)))
goto leave;
@@ -2022,7 +2040,7 @@ op_verify (cjson_t request, cjson_t result)
verify_result_to_json (verify_result));
}
- err = make_data_object (result, output, chunksize, "plaintext", -1);
+ err = make_data_object (result, output, "plaintext", -1);
output = NULL;
if (err)
@@ -2097,7 +2115,6 @@ static const char hlp_keylist[] =
" For a single key a String may be used instead of an array.\n"
" default lists all keys.\n"
"protocol: Either \"openpgp\" (default) or \"cms\".\n"
- "chunksize: Max number of bytes in the resulting \"data\".\n"
"\n"
"Optional boolean flags (default is false):\n"
"secret: List secret keys.\n"
@@ -2215,16 +2232,13 @@ static const char hlp_keylist[] =
" signfirst\n"
" signlast\n"
" encrfirst\n"
- " encrlast\n"
- "more: Optional boolean indicating that \"getmore\" is required.\n"
- " (not implemented)";
+ " encrlast\n";
static gpg_error_t
op_keylist (cjson_t request, cjson_t result)
{
gpg_error_t err;
gpgme_ctx_t ctx = NULL;
gpgme_protocol_t protocol;
- size_t chunksize;
char **patterns = NULL;
int abool;
gpgme_keylist_mode_t mode = 0;
@@ -2234,8 +2248,6 @@ op_keylist (cjson_t request, cjson_t result)
if ((err = get_protocol (request, &protocol)))
goto leave;
ctx = get_context (protocol);
- if ((err = get_chunksize (request, &chunksize)))
- goto leave;
/* Handle the various keylist mode bools. */
if ((err = get_boolean_flag (request, "secret", 0, &abool)))
@@ -2406,7 +2418,6 @@ static const char hlp_export[] =
" For a single key a String may be used instead of an array.\n"
" default exports all keys.\n"
"protocol: Either \"openpgp\" (default) or \"cms\".\n"
- "chunksize: Max number of bytes in the resulting \"data\".\n"
"\n"
"Optional boolean flags (default is false):\n"
"armor: Request output in armored format.\n"
@@ -2420,15 +2431,13 @@ static const char hlp_export[] =
"data: Unless armor mode is used a Base64 encoded binary.\n"
" In armor mode a string with an armored\n"
" OpenPGP or a PEM / PKCS12 key.\n"
- "base64: Boolean indicating whether data is base64 encoded.\n"
- "more: Optional boolean indicating that \"getmore\" is required.";
+ "base64: Boolean indicating whether data is base64 encoded.\n";
static gpg_error_t
op_export (cjson_t request, cjson_t result)
{
gpg_error_t err;
gpgme_ctx_t ctx = NULL;
gpgme_protocol_t protocol;
- size_t chunksize;
char **patterns = NULL;
int abool;
gpgme_export_mode_t mode = 0;
@@ -2437,8 +2446,6 @@ op_export (cjson_t request, cjson_t result)
if ((err = get_protocol (request, &protocol)))
goto leave;
ctx = get_context (protocol);
- if ((err = get_chunksize (request, &chunksize)))
- goto leave;
if ((err = get_boolean_flag (request, "armor", 0, &abool)))
goto leave;
@@ -2495,7 +2502,7 @@ op_export (cjson_t request, cjson_t result)
}
/* We need to base64 if armoring has not been requested. */
- err = make_data_object (result, output, chunksize,
+ err = make_data_object (result, output,
"keys", !gpgme_get_armor (ctx));
output = NULL;
@@ -2765,14 +2772,10 @@ leave:
static const char hlp_getmore[] =
"op: \"getmore\"\n"
"\n"
- "Optional parameters:\n"
- "chunksize: Max number of bytes in the \"data\" object.\n"
- "\n"
"Response on success:\n"
- "type: Type of the pending data\n"
- "data: The next chunk of data\n"
- "base64: Boolean indicating whether data is base64 encoded\n"
- "more: Optional boolean requesting another \"getmore\".";
+ "response: base64 encoded json response.\n"
+ "more: Another getmore is required.\n"
+ "base64: boolean if the response is base64 encoded.\n";
static gpg_error_t
op_getmore (cjson_t request, cjson_t result)
{
@@ -2784,9 +2787,12 @@ op_getmore (cjson_t request, cjson_t result)
if ((err = get_chunksize (request, &chunksize)))
goto leave;
- /* Adjust the chunksize if we need to do base64 conversion. */
- if (pending_data.base64)
- chunksize = (chunksize / 4) * 3;
+ /* For the meta data we need 41 bytes:
+ {"more":true,"base64":true,"response":""} */
+ chunksize -= 41;
+
+ /* Adjust the chunksize for the base64 conversion. */
+ chunksize = (chunksize / 4) * 3;
/* Do we have anything pending? */
if (!pending_data.buffer)
@@ -2797,8 +2803,8 @@ op_getmore (cjson_t request, cjson_t result)
goto leave;
}
- xjson_AddStringToObject (result, "type", pending_data.type);
- xjson_AddBoolToObject (result, "base64", pending_data.base64);
+ /* We currently always use base64 encoding for simplicity. */
+ xjson_AddBoolToObject (result, "base64", 1);
if (pending_data.written >= pending_data.length)
{
@@ -2807,7 +2813,7 @@ op_getmore (cjson_t request, cjson_t result)
gpgme_free (pending_data.buffer);
pending_data.buffer = NULL;
xjson_AddBoolToObject (result, "more", 0);
- err = cjson_AddStringToObject (result, "data", "");
+ err = cjson_AddStringToObject (result, "response", "");
}
else
{
@@ -2822,21 +2828,16 @@ op_getmore (cjson_t request, cjson_t result)
c = pending_data.buffer[pending_data.written + n];
pending_data.buffer[pending_data.written + n] = 0;
- if (pending_data.base64)
- err = add_base64_to_object (result, "data",
- (pending_data.buffer
- + pending_data.written), n);
- else
- err = cjson_AddStringToObject (result, "data",
- (pending_data.buffer
- + pending_data.written));
+ err = add_base64_to_object (result, "response",
+ (pending_data.buffer
+ + pending_data.written), n);
pending_data.buffer[pending_data.written + n] = c;
if (!err)
{
pending_data.written += n;
if (pending_data.written >= pending_data.length)
{
- gpgme_free (pending_data.buffer);
+ xfree (pending_data.buffer);
pending_data.buffer = NULL;
}
}
@@ -2866,8 +2867,15 @@ static const char hlp_help[] =
" sign Sign data.\n"
" verify Verify data.\n"
" version Get engine information.\n"
- " getmore Retrieve remaining data.\n"
- " help Help overview.";
+ " getmore Retrieve remaining data if chunksize was used.\n"
+ " help Help overview.\n"
+ "\n"
+ "If the data needs to be transferred in smaller chunks the\n"
+ "property \"chunksize\" with an integer value can be added.\n"
+ "When \"chunksize\" is set the response (including json) will\n"
+ "not be larger then \"chunksize\" but might be smaller.\n"
+ "The chunked result will be transferred in base64 encoded chunks\n"
+ "using the \"getmore\" operation. See help getmore for more info.";
static gpg_error_t
op_help (cjson_t request, cjson_t result)
{
@@ -2924,6 +2932,7 @@ process_request (const char *request)
cjson_t j_tmp, j_op;
cjson_t response;
int helpmode;
+ int is_getmore = 0;
const char *op;
char *res;
int idx;
@@ -2969,7 +2978,7 @@ process_request (const char *request)
else
{
gpg_error_t err;
-
+ is_getmore = optbl[idx].handler == op_getmore;
/* If this is not the "getmore" command and we have any
* pending data release that data. */
if (pending_data.buffer && optbl[idx].handler != op_getmore)
@@ -3001,13 +3010,20 @@ process_request (const char *request)
}
leave:
- cJSON_Delete (json);
- if (opt_interactive)
- res = cJSON_Print (response);
+ if (is_getmore)
+ {
+ /* For getmore we bypass the encode_and_chunk. */
+ if (opt_interactive)
+ res = cJSON_Print (response);
+ else
+ res = cJSON_PrintUnformatted (response);
+ }
else
- res = cJSON_PrintUnformatted (response);
+ res = encode_and_chunk (json, response);
if (!res)
log_error ("Printing JSON data failed\n");
+
+ cJSON_Delete (json);
cJSON_Delete (response);
return res;
}