json: Add keylist mode locate
[gpgme.git] / src / gpgme-json.c
index 56b4bc1..8d534c6 100644 (file)
@@ -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;
 
 
@@ -565,7 +563,7 @@ _create_new_context (gpgme_protocol_t proto)
 static gpgme_ctx_t
 get_context (gpgme_protocol_t proto)
 {
-  static gpgme_ctx_t ctx_openpgp, ctx_cms;
+  static gpgme_ctx_t ctx_openpgp, ctx_cms, ctx_conf;
 
   if (proto == GPGME_PROTOCOL_OpenPGP)
     {
@@ -579,6 +577,12 @@ get_context (gpgme_protocol_t proto)
         ctx_cms = _create_new_context (proto);
       return ctx_cms;
     }
+  else if (proto == GPGME_PROTOCOL_GPGCONF)
+    {
+      if (!ctx_conf)
+        ctx_conf = _create_new_context (proto);
+      return ctx_conf;
+    }
   else
     log_bug ("invalid protocol %d requested\n", proto);
 }
@@ -720,41 +724,68 @@ create_keylist_patterns (cjson_t request, const char *name)
 static cjson_t
 sigsum_to_json (gpgme_sigsum_t summary)
 {
-  cjson_t result = xjson_CreateArray ();
+  cjson_t result = xjson_CreateObject ();
+  cjson_t sigsum_array = xjson_CreateArray ();
 
   if ( (summary & GPGME_SIGSUM_VALID      ))
-    cJSON_AddItemToArray (result,
+    cJSON_AddItemToArray (sigsum_array,
         cJSON_CreateString ("valid"));
   if ( (summary & GPGME_SIGSUM_GREEN      ))
-    cJSON_AddItemToArray (result,
+    cJSON_AddItemToArray (sigsum_array,
         cJSON_CreateString ("green"));
   if ( (summary & GPGME_SIGSUM_RED        ))
-    cJSON_AddItemToArray (result,
+    cJSON_AddItemToArray (sigsum_array,
         cJSON_CreateString ("red"));
   if ( (summary & GPGME_SIGSUM_KEY_REVOKED))
-    cJSON_AddItemToArray (result,
+    cJSON_AddItemToArray (sigsum_array,
         cJSON_CreateString ("revoked"));
   if ( (summary & GPGME_SIGSUM_KEY_EXPIRED))
-    cJSON_AddItemToArray (result,
+    cJSON_AddItemToArray (sigsum_array,
         cJSON_CreateString ("key-expired"));
   if ( (summary & GPGME_SIGSUM_SIG_EXPIRED))
-    cJSON_AddItemToArray (result,
+    cJSON_AddItemToArray (sigsum_array,
         cJSON_CreateString ("sig-expired"));
   if ( (summary & GPGME_SIGSUM_KEY_MISSING))
-    cJSON_AddItemToArray (result,
+    cJSON_AddItemToArray (sigsum_array,
         cJSON_CreateString ("key-missing"));
   if ( (summary & GPGME_SIGSUM_CRL_MISSING))
-    cJSON_AddItemToArray (result,
+    cJSON_AddItemToArray (sigsum_array,
         cJSON_CreateString ("crl-missing"));
   if ( (summary & GPGME_SIGSUM_CRL_TOO_OLD))
-    cJSON_AddItemToArray (result,
+    cJSON_AddItemToArray (sigsum_array,
         cJSON_CreateString ("crl-too-old"));
   if ( (summary & GPGME_SIGSUM_BAD_POLICY ))
-    cJSON_AddItemToArray (result,
+    cJSON_AddItemToArray (sigsum_array,
         cJSON_CreateString ("bad-policy"));
   if ( (summary & GPGME_SIGSUM_SYS_ERROR  ))
-    cJSON_AddItemToArray (result,
+    cJSON_AddItemToArray (sigsum_array,
         cJSON_CreateString ("sys-error"));
+  /* The signature summary as string array. */
+  xjson_AddItemToObject (result, "sigsum", sigsum_array);
+
+  /* Bools for the same. */
+  xjson_AddBoolToObject (result, "valid",
+                         (summary & GPGME_SIGSUM_VALID      ));
+  xjson_AddBoolToObject (result, "green",
+                         (summary & GPGME_SIGSUM_GREEN      ));
+  xjson_AddBoolToObject (result, "red",
+                         (summary & GPGME_SIGSUM_RED        ));
+  xjson_AddBoolToObject (result, "revoked",
+                         (summary & GPGME_SIGSUM_KEY_REVOKED));
+  xjson_AddBoolToObject (result, "key-expired",
+                         (summary & GPGME_SIGSUM_KEY_EXPIRED));
+  xjson_AddBoolToObject (result, "sig-expired",
+                         (summary & GPGME_SIGSUM_SIG_EXPIRED));
+  xjson_AddBoolToObject (result, "key-missing",
+                         (summary & GPGME_SIGSUM_KEY_MISSING));
+  xjson_AddBoolToObject (result, "crl-missing",
+                         (summary & GPGME_SIGSUM_CRL_MISSING));
+  xjson_AddBoolToObject (result, "crl-too-old",
+                         (summary & GPGME_SIGSUM_CRL_TOO_OLD));
+  xjson_AddBoolToObject (result, "bad-policy",
+                         (summary & GPGME_SIGSUM_BAD_POLICY ));
+  xjson_AddBoolToObject (result, "sys-error",
+                         (summary & GPGME_SIGSUM_SYS_ERROR  ));
 
   return result;
 }
@@ -1008,18 +1039,38 @@ signature_to_json (gpgme_signature_t sig)
 {
   cjson_t result = xjson_CreateObject ();
 
-  xjson_AddStringToObject0 (result, "status",
-                            gpgme_strerror (sig->status));
+  xjson_AddItemToObject (result, "summary", sigsum_to_json (sig->summary));
 
-  xjson_AddStringToObject0 (result, "validity",
-                            validity_to_string (sig->validity));
+  xjson_AddBoolToObject (result, "wrong_key_usage", sig->wrong_key_usage);
+  xjson_AddBoolToObject (result, "chain_model", sig->chain_model);
+  xjson_AddBoolToObject (result, "is_de_vs", sig->is_de_vs);
+
+  xjson_AddStringToObject0 (result, "status_string",
+                            gpgme_strerror (sig->status));
   xjson_AddStringToObject0 (result, "fingerprint", sig->fpr);
+  xjson_AddStringToObject0 (result, "validity_string",
+                            validity_to_string (sig->validity));
+  xjson_AddStringToObject0 (result, "pubkey_algo_name",
+                            gpgme_pubkey_algo_name (sig->pubkey_algo));
+  xjson_AddStringToObject0 (result, "hash_algo_name",
+                            gpgme_hash_algo_name (sig->hash_algo));
+  xjson_AddStringToObject0 (result, "pka_address", sig->pka_address);
 
-  xjson_AddItemToObject (result, "summary", sigsum_to_json (sig->summary));
+  xjson_AddNumberToObject (result, "status_code", sig->status);
+  xjson_AddNumberToObject (result, "timestamp", sig->timestamp);
+  xjson_AddNumberToObject (result, "exp_timestamp", sig->exp_timestamp);
+  xjson_AddNumberToObject (result, "pka_trust", sig->pka_trust);
+  xjson_AddNumberToObject (result, "validity", sig->validity);
+  xjson_AddNumberToObject (result, "validity_reason", sig->validity_reason);
 
-  xjson_AddNumberToObject (result, "created", sig->timestamp);
-  xjson_AddNumberToObject (result, "expired", sig->exp_timestamp);
-  xjson_AddNumberToObject (result, "code", sig->status);
+  if (sig->notations)
+    {
+      gpgme_sig_notation_t not;
+      cjson_t array = xjson_CreateArray ();
+      for (not = sig->notations; not; not = not->next)
+        cJSON_AddItemToArray (array, sig_notation_to_json (not));
+      xjson_AddItemToObject (result, "notations", array);
+    }
 
   return result;
 }
@@ -1029,7 +1080,10 @@ signature_to_json (gpgme_signature_t sig)
 static cjson_t
 verify_result_to_json (gpgme_verify_result_t verify_result)
 {
-  cjson_t response = xjson_CreateObject ();
+  cjson_t result = xjson_CreateObject ();
+
+  xjson_AddStringToObject0 (result, "file_name", verify_result->file_name);
+  xjson_AddBoolToObject (result, "is_mime", verify_result->is_mime);
 
   if (verify_result->signatures)
     {
@@ -1038,10 +1092,10 @@ verify_result_to_json (gpgme_verify_result_t verify_result)
 
       for (sig = verify_result->signatures; sig; sig = sig->next)
         cJSON_AddItemToArray (array, signature_to_json (sig));
-      xjson_AddItemToObject (response, "signatures", array);
+      xjson_AddItemToObject (result, "signatures", array);
     }
 
-  return response;
+  return result;
 }
 
 
@@ -1115,6 +1169,119 @@ import_result_to_json (gpgme_import_result_t imp)
 }
 
 
+/* Create a JSON object from a gpgconf arg */
+static cjson_t
+conf_arg_to_json (gpgme_conf_arg_t arg, gpgme_conf_type_t type)
+{
+  cjson_t result = xjson_CreateObject ();
+  int is_none = 0;
+  switch (type)
+    {
+      case GPGME_CONF_STRING:
+      case GPGME_CONF_PATHNAME:
+      case GPGME_CONF_LDAP_SERVER:
+      case GPGME_CONF_KEY_FPR:
+      case GPGME_CONF_PUB_KEY:
+      case GPGME_CONF_SEC_KEY:
+      case GPGME_CONF_ALIAS_LIST:
+        xjson_AddStringToObject0 (result, "string", arg->value.string);
+        break;
+
+      case GPGME_CONF_UINT32:
+        xjson_AddNumberToObject (result, "number", arg->value.uint32);
+        break;
+
+      case GPGME_CONF_INT32:
+        xjson_AddNumberToObject (result, "number", arg->value.int32);
+        break;
+
+      case GPGME_CONF_NONE:
+      default:
+        is_none = 1;
+        break;
+    }
+  xjson_AddBoolToObject (result, "is_none", is_none);
+  return result;
+}
+
+
+/* Create a JSON object from a gpgconf option */
+static cjson_t
+conf_opt_to_json (gpgme_conf_opt_t opt)
+{
+  cjson_t result = xjson_CreateObject ();
+
+  xjson_AddStringToObject0 (result, "name", opt->name);
+  xjson_AddStringToObject0 (result, "description", opt->description);
+  xjson_AddStringToObject0 (result, "argname", opt->argname);
+  xjson_AddStringToObject0 (result, "default_description",
+                            opt->default_description);
+  xjson_AddStringToObject0 (result, "no_arg_description",
+                            opt->no_arg_description);
+
+  xjson_AddNumberToObject (result, "flags", opt->flags);
+  xjson_AddNumberToObject (result, "level", opt->level);
+  xjson_AddNumberToObject (result, "type", opt->type);
+  xjson_AddNumberToObject (result, "alt_type", opt->alt_type);
+
+  if (opt->default_value)
+    {
+      cjson_t array = xjson_CreateArray ();
+      gpgme_conf_arg_t arg;
+
+      for (arg = opt->default_value; arg; arg = arg->next)
+        cJSON_AddItemToArray (array, conf_arg_to_json (arg, opt->alt_type));
+      xjson_AddItemToObject (result, "default_value", array);
+    }
+
+  if (opt->no_arg_value)
+    {
+      cjson_t array = xjson_CreateArray ();
+      gpgme_conf_arg_t arg;
+
+      for (arg = opt->no_arg_value; arg; arg = arg->next)
+        cJSON_AddItemToArray (array, conf_arg_to_json (arg, opt->alt_type));
+      xjson_AddItemToObject (result, "no_arg_value", array);
+    }
+
+  if (opt->value)
+    {
+      cjson_t array = xjson_CreateArray ();
+      gpgme_conf_arg_t arg;
+
+      for (arg = opt->value; arg; arg = arg->next)
+        cJSON_AddItemToArray (array, conf_arg_to_json (arg, opt->alt_type));
+      xjson_AddItemToObject (result, "value", array);
+    }
+  return result;
+}
+
+
+/* Create a JSON object from a gpgconf component*/
+static cjson_t
+conf_comp_to_json (gpgme_conf_comp_t cmp)
+{
+  cjson_t result = xjson_CreateObject ();
+
+  xjson_AddStringToObject0 (result, "name", cmp->name);
+  xjson_AddStringToObject0 (result, "description", cmp->description);
+  xjson_AddStringToObject0 (result, "program_name", cmp->program_name);
+
+
+  if (cmp->options)
+    {
+      cjson_t array = xjson_CreateArray ();
+      gpgme_conf_opt_t opt;
+
+      for (opt = cmp->options; opt; opt = opt->next)
+        cJSON_AddItemToArray (array, conf_opt_to_json (opt));
+      xjson_AddItemToObject (result, "options", array);
+    }
+
+  return result;
+}
+
+
 /* Create a gpgme_data from json string data named "name"
  * in the request. Takes the base64 option into account.
  *
@@ -1167,29 +1334,20 @@ get_string_data (cjson_t request, cjson_t result, const char *name,
 }
 
 
-\f
-/*
- * 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);
@@ -1221,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;
 }
 
 
 \f
+/*
+ * Implementation of the commands.
+ */
 static const char hlp_encrypt[] =
   "op:     \"encrypt\"\n"
   "keys:   Array of strings with the fingerprints or user-ids\n"
@@ -1273,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"
@@ -1293,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)
 {
@@ -1302,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;
@@ -1315,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;
@@ -1430,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;
 
@@ -1454,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"
@@ -1464,25 +1665,58 @@ static const char hlp_decrypt[] =
   "data:   The decrypted data.  This may be base64 encoded.\n"
   "base64: Boolean indicating whether data is base64 encoded.\n"
   "mime:   A Boolean indicating whether the data is a MIME object.\n"
-  "info:   An object with optional signature information.\n"
+  "info:   An object with verification information. (gpgme_verify_result_t)\n"
+  " file_name: Optional string of the plaintext file name.\n"
+  " is_mime:    Boolean that is true if the messages claims it is MIME.\n"
+  " signatures: Array of signatures\n"
+  "  summary: Object containing summary information.\n"
+  "   Boolean values: (Check gpgme_sigsum_t doc for meaning)\n"
+  "    valid\n"
+  "    green\n"
+  "    red\n"
+  "    revoked\n"
+  "    key-expired\n"
+  "    sig-expired\n"
+  "    key-missing\n"
+  "    crl-missing\n"
+  "    crl-too-old\n"
+  "    bad-policy\n"
+  "    sys-error\n"
+  "   sigsum: Array of strings representing the sigsum.\n"
+  "  Boolean values:\n"
+  "   wrong_key_usage: Key should not have been used for signing.\n"
+  "   chain_model:     Validity has been verified using the chain model.\n"
+  "   is_de_vs:        signature is in compliance to the de-vs mode.\n"
+  "  String values:\n"
+  "   status_string:      The status code as localized gpg-error string\n"
+  "   fingerprint:        The fingerprint of the signing key.\n"
+  "   validity_string:    The validity as string.\n"
+  "   pubkey_algo_name:   gpgme_pubkey_algo_name of used algo.\n"
+  "   hash_algo_name:     gpgme_hash_algo_name of used hash algo\n"
+  "   pka_address:        The mailbox from the PKA information.\n"
+  "  Number values:\n"
+  "   status_code:     The status as a number. (gpg_error_t)\n"
+  "   timestamp:       Signature creation time. (secs since epoch)\n"
+  "   exp_timestamp:   Signature expiration or 0. (secs since epoch)\n"
+  "   pka_trust: PKA status: 0 = not available, 1 = bad, 2 = okay, 3 = RFU.\n"
+  "   validity: validity as number (gpgme_validity_t)\n"
+  "   validity_reason: (gpg_error_t)\n"
   "  Array values:\n"
-  "   signatures\n"
+  "   notations: Notation data and policy urls (gpgme_sig_notation_t)\n"
+  "    Boolean values:\n"
+  "     human_readable\n"
+  "     critical\n"
   "    String values:\n"
-  "     status: The status of the signature.\n"
-  "     fingerprint: The fingerprint of the signing key.\n"
-  "     validity: The validity as string.\n"
+  "     name\n"
+  "     value\n"
   "    Number values:\n"
-  "     code: The status as a number.\n"
-  "    Array values:\n"
-  "     summary: A string array of the sig summary.\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;
@@ -1491,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;
@@ -1530,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)
@@ -1557,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"
@@ -1573,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;
@@ -1594,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;
@@ -1677,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;
 
@@ -1700,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"
@@ -1710,25 +1936,58 @@ static const char hlp_verify[] =
   "type:   \"plaintext\"\n"
   "data:   The verified data.  This may be base64 encoded.\n"
   "base64: Boolean indicating whether data is base64 encoded.\n"
-  "info:   An object with signature information.\n"
+  "info:   An object with verification information (gpgme_verify_result_t).\n"
+  " file_name: Optional string of the plaintext file name.\n"
+  " is_mime:    Boolean that is true if the messages claims it is MIME.\n"
+  " signatures: Array of signatures\n"
+  "  summary: Object containing summary information.\n"
+  "   Boolean values: (Check gpgme_sigsum_t doc for meaning)\n"
+  "    valid\n"
+  "    green\n"
+  "    red\n"
+  "    revoked\n"
+  "    key-expired\n"
+  "    sig-expired\n"
+  "    key-missing\n"
+  "    crl-missing\n"
+  "    crl-too-old\n"
+  "    bad-policy\n"
+  "    sys-error\n"
+  "   sigsum: Array of strings representing the sigsum.\n"
+  "  Boolean values:\n"
+  "   wrong_key_usage: Key should not have been used for signing.\n"
+  "   chain_model:     Validity has been verified using the chain model.\n"
+  "   is_de_vs:        signature is in compliance to the de-vs mode.\n"
+  "  String values:\n"
+  "   status_string:      The status code as localized gpg-error string\n"
+  "   fingerprint:        The fingerprint of the signing key.\n"
+  "   validity_string:    The validity as string.\n"
+  "   pubkey_algo_name:   gpgme_pubkey_algo_name of used algo.\n"
+  "   hash_algo_name:     gpgme_hash_algo_name of used hash algo\n"
+  "   pka_address:        The mailbox from the PKA information.\n"
+  "  Number values:\n"
+  "   status_code:     The status as a number. (gpg_error_t)\n"
+  "   timestamp:       Signature creation time. (secs since epoch)\n"
+  "   exp_timestamp:   Signature expiration or 0. (secs since epoch)\n"
+  "   pka_trust: PKA status: 0 = not available, 1 = bad, 2 = okay, 3 = RFU.\n"
+  "   validity: validity as number (gpgme_validity_t)\n"
+  "   validity_reason: (gpg_error_t)\n"
   "  Array values:\n"
-  "   signatures\n"
+  "   notations: Notation data and policy urls (gpgme_sig_notation_t)\n"
+  "    Boolean values:\n"
+  "     human_readable\n"
+  "     critical\n"
   "    String values:\n"
-  "     status: The status of the signature.\n"
-  "     fingerprint: The fingerprint of the signing key.\n"
-  "     validity: The validity as string.\n"
+  "     name\n"
+  "     value\n"
   "    Number values:\n"
-  "     code: The status as a number.\n"
-  "    Array values:\n"
-  "     summary: A string array of the sig summary.\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;
@@ -1737,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;
@@ -1783,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)
@@ -1858,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"
@@ -1869,6 +2125,7 @@ static const char hlp_keylist[] =
   "tofu:          Add KEYLIST_MODE_WITH_TOFU.\n"
   "ephemeral:     Add KEYLIST_MODE_EPHEMERAL.\n"
   "validate:      Add KEYLIST_MODE_VALIDATE.\n"
+  "locate:        Add KEYLIST_MODE_LOCATE.\n"
   "\n"
   "Response on success:\n"
   "keys:   Array of keys.\n"
@@ -1976,16 +2233,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;
@@ -1995,8 +2249,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)))
@@ -2039,6 +2291,11 @@ op_keylist (cjson_t request, cjson_t result)
   if (abool)
     mode |= GPGME_KEYLIST_MODE_VALIDATE;
 
+  if ((err = get_boolean_flag (request, "locate", 0, &abool)))
+    goto leave;
+  if (abool)
+    mode |= GPGME_KEYLIST_MODE_LOCATE;
+
   if (!mode)
     {
       /* default to local */
@@ -2167,7 +2424,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"
@@ -2181,15 +2437,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;
@@ -2198,8 +2452,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;
@@ -2256,7 +2508,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;
 
@@ -2341,18 +2593,286 @@ leave:
 }
 
 
+static const char hlp_config_opt[] =
+  "op:       \"config_opt\"\n"
+  "component: The component of the option.\n"
+  "option:    The name of the option.\n"
+  "\n"
+  "Response on success:\n"
+  "\n"
+  "option: Information about the option.\n"
+  " String values:\n"
+  "  name: The name of the option\n"
+  "  description: Localized description of the opt.\n"
+  "  argname: Thhe argument name e.g. --verbose\n"
+  "  default_description\n"
+  "  no_arg_description\n"
+  " Number values:\n"
+  "  flags: Flags for this option.\n"
+  "  level: the level of the description. See gpgme_conf_level_t.\n"
+  "  type: The type of the option. See gpgme_conf_type_t.\n"
+  "  alt_type: Alternate type of the option. See gpgme_conf_type_t\n"
+  " Arg type values: (see desc. below)\n"
+  "  default_value: Array of the default value.\n"
+  "  no_arg_value: Array of the value if it is not set.\n"
+  "  value: Array for the current value if the option is set.\n"
+  "\n"
+  "If the response is empty the option was not found\n"
+  "";
+static gpg_error_t
+op_config_opt (cjson_t request, cjson_t result)
+{
+  gpg_error_t err;
+  gpgme_ctx_t ctx = NULL;
+  gpgme_conf_comp_t conf = NULL;
+  gpgme_conf_comp_t comp = NULL;
+  cjson_t j_tmp;
+  char *comp_name = NULL;
+  char *opt_name = NULL;
+
+  ctx = get_context (GPGME_PROTOCOL_GPGCONF);
+
+  j_tmp = cJSON_GetObjectItem (request, "component");
+  if (!j_tmp || !cjson_is_string (j_tmp))
+    {
+      err = gpg_error (GPG_ERR_INV_VALUE);
+      goto leave;
+    }
+  comp_name = j_tmp->valuestring;
+
+
+  j_tmp = cJSON_GetObjectItem (request, "option");
+  if (!j_tmp || !cjson_is_string (j_tmp))
+    {
+      err = gpg_error (GPG_ERR_INV_VALUE);
+      goto leave;
+    }
+  opt_name = j_tmp->valuestring;
+
+  /* Load the config */
+  err = gpgme_op_conf_load (ctx, &conf);
+  if (err)
+    {
+      goto leave;
+    }
+
+  comp = conf;
+  for (comp = conf; comp; comp = comp->next)
+    {
+      gpgme_conf_opt_t opt = NULL;
+      int found = 0;
+      if (!comp->name || strcmp (comp->name, comp_name))
+        {
+          /* Skip components if a single one is specified */
+          continue;
+        }
+      for (opt = comp->options; opt; opt = opt->next)
+        {
+          if (!opt->name || strcmp (opt->name, opt_name))
+            {
+              /* Skip components if a single one is specified */
+              continue;
+            }
+          xjson_AddItemToObject (result, "option", conf_opt_to_json (opt));
+          found = 1;
+          break;
+        }
+      if (found)
+        break;
+    }
+
+leave:
+  gpgme_conf_release (conf);
+  release_context (ctx);
+
+  return err;
+}
+
+
+static const char hlp_config[] =
+  "op:     \"config\"\n"
+  "\n"
+  "Optional parameters:\n"
+  "component:    Component of entries to list.\n"
+  "              Default: all\n"
+  "\n"
+  "Response on success:\n"
+  "   components: Array of the component program configs.\n"
+  "     name:         The component name.\n"
+  "     description:  Description of the component.\n"
+  "     program_name: The absolute path to the program.\n"
+  "     options: Array of config options\n"
+  "      String values:\n"
+  "       name: The name of the option\n"
+  "       description: Localized description of the opt.\n"
+  "       argname: Thhe argument name e.g. --verbose\n"
+  "       default_description\n"
+  "       no_arg_description\n"
+  "      Number values:\n"
+  "       flags: Flags for this option.\n"
+  "       level: the level of the description. See gpgme_conf_level_t.\n"
+  "       type: The type of the option. See gpgme_conf_type_t.\n"
+  "       alt_type: Alternate type of the option. See gpgme_conf_type_t\n"
+  "      Arg type values: (see desc. below)\n"
+  "       default_value: Array of the default value.\n"
+  "       no_arg_value: Array of the value if it is not set.\n"
+  "       value: Array for the current value if the option is set.\n"
+  "\n"
+  "Conf type values are an array of values that are either\n"
+  "of type number named \"number\" or of type string,\n"
+  "named \"string\".\n"
+  "If the type is none the bool value is_none is true.\n"
+  "";
+static gpg_error_t
+op_config (cjson_t request, cjson_t result)
+{
+  gpg_error_t err;
+  gpgme_ctx_t ctx = NULL;
+  gpgme_conf_comp_t conf = NULL;
+  gpgme_conf_comp_t comp = NULL;
+  cjson_t j_tmp;
+  char *comp_name = NULL;
+  cjson_t j_comps = xjson_CreateArray ();
+
+  ctx = get_context (GPGME_PROTOCOL_GPGCONF);
+
+  j_tmp = cJSON_GetObjectItem (request, "component");
+  if (j_tmp && cjson_is_string (j_tmp))
+    {
+      comp_name = j_tmp->valuestring;
+    }
+  else if (j_tmp && !cjson_is_string (j_tmp))
+    {
+      err = gpg_error (GPG_ERR_INV_VALUE);
+      goto leave;
+    }
+
+  /* Load the config */
+  err = gpgme_op_conf_load (ctx, &conf);
+  if (err)
+    {
+      goto leave;
+    }
+
+  comp = conf;
+  for (comp = conf; comp; comp = comp->next)
+    {
+      if (comp_name && comp->name && strcmp (comp->name, comp_name))
+        {
+          /* Skip components if a single one is specified */
+          continue;
+        }
+      cJSON_AddItemToArray (j_comps, conf_comp_to_json (comp));
+    }
+  xjson_AddItemToObject (result, "components", j_comps);
+
+leave:
+  gpgme_conf_release (conf);
+  release_context (ctx);
+
+  return err;
+}
+
+
 \f
-static const char hlp_getmore[] =
-  "op:     \"getmore\"\n"
+static const char hlp_createkey[] =
+  "op:      \"createkey\"\n"
+  "userid:  The user id. E.g. \"Foo Bar <foo@bar.baz>\"\n"
   "\n"
   "Optional parameters:\n"
-  "chunksize:  Max number of bytes in the \"data\" object.\n"
+  "algo:    Algo of the key as string. See doc for gpg --quick-gen-key.\n"
+  "expires: Seconds since epoch to expiry as Number. 0 means no expiry.\n"
+  "\n"
+  "Response on success:\n"
+  "fingerprint:   The fingerprint of the created key.\n"
+  "\n"
+  "Note: This interface does not allow key generation if the userid\n"
+  "of the new key already exists in the keyring.\n";
+static gpg_error_t
+op_createkey (cjson_t request, cjson_t result)
+{
+  gpg_error_t err;
+  gpgme_ctx_t ctx = NULL;
+  unsigned int flags = 0;
+  unsigned long expires = 0;
+  cjson_t j_tmp;
+  const char *algo = "default";
+  const char *userid;
+  gpgme_genkey_result_t res;
+
+#ifdef GPG_AGENT_ALLOWS_KEYGEN_TRHOUGH_BROWSER
+  /* GnuPG forbids keygen through the browser socket so for
+     this we create an unrestricted context.
+     See GnuPG-Bug-Id: T4010 for more info */
+  ctx = get_context (GPGME_PROTOCOL_OpenPGP);
+#else
+    err = gpgme_new (&ctx);
+  if (err)
+    log_fatal ("error creating GPGME context: %s\n", gpg_strerror (err));
+  gpgme_set_protocol (ctx, GPGME_PROTOCOL_OpenPGP);
+#endif
+
+  j_tmp = cJSON_GetObjectItem (request, "algo");
+  if (j_tmp && cjson_is_string (j_tmp))
+    {
+      algo = j_tmp->valuestring;
+    }
+
+  j_tmp = cJSON_GetObjectItem (request, "userid");
+  if (!j_tmp || !cjson_is_string (j_tmp))
+    {
+      err = gpg_error (GPG_ERR_INV_VALUE);
+      goto leave;
+    }
+
+  userid = j_tmp->valuestring;
+
+  j_tmp = cJSON_GetObjectItem (request, "expires");
+  if (j_tmp)
+    {
+      if (!cjson_is_number (j_tmp))
+        {
+          err = gpg_error (GPG_ERR_INV_VALUE);
+          goto leave;
+        }
+      expires = j_tmp->valueint;
+
+      if (!expires)
+        flags |= GPGME_CREATE_NOEXPIRE;
+    }
+
+
+  if ((err = gpgme_op_createkey (ctx, userid, algo, 0, expires, NULL, flags)))
+    goto leave;
+
+  res = gpgme_op_genkey_result (ctx);
+  if (!res)
+    {
+      err = gpg_error (GPG_ERR_GENERAL);
+      goto leave;
+    }
+
+  xjson_AddStringToObject0 (result, "fingerprint", res->fpr);
+
+leave:
+#ifdef GPG_AGENT_ALLOWS_KEYGEN_TRHOUGH_BROWSER
+  release_context (ctx);
+#else
+  gpgme_release (ctx);
+#endif
+
+  return err;
+}
+
+
+\f
+static const char hlp_getmore[] =
+  "op:     \"getmore\"\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)
 {
@@ -2364,9 +2884,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)
@@ -2377,8 +2900,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)
     {
@@ -2387,7 +2910,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
     {
@@ -2402,21 +2925,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;
             }
         }
@@ -2436,17 +2954,27 @@ static const char hlp_help[] =
   "operation is not performned but a string with the documentation\n"
   "returned.  To list all operations it is allowed to leave out \"op\" in\n"
   "help mode.  Supported values for \"op\" are:\n\n"
+  "  config      Read configuration values.\n"
+  "  config_opt  Read a single configuration value.\n"
   "  decrypt     Decrypt data.\n"
   "  delete      Delete a key.\n"
   "  encrypt     Encrypt data.\n"
   "  export      Export keys.\n"
+  "  createkey   Generate a keypair (OpenPGP only).\n"
   "  import      Import data.\n"
   "  keylist     List keys.\n"
   "  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)
 {
@@ -2483,17 +3011,20 @@ process_request (const char *request)
     gpg_error_t (*handler)(cjson_t request, cjson_t result);
     const char * const helpstr;
   } optbl[] = {
-    { "encrypt", op_encrypt, hlp_encrypt },
-    { "export",  op_export,  hlp_export },
-    { "decrypt", op_decrypt, hlp_decrypt },
-    { "delete",  op_delete,  hlp_delete },
-    { "keylist", op_keylist, hlp_keylist },
-    { "import",  op_import,  hlp_import },
-    { "sign",    op_sign,    hlp_sign },
-    { "verify",  op_verify,  hlp_verify },
-    { "version", op_version, hlp_version },
-    { "getmore", op_getmore, hlp_getmore },
-    { "help",    op_help,    hlp_help },
+    { "config",     op_config,     hlp_config },
+    { "config_opt", op_config_opt, hlp_config_opt },
+    { "encrypt",    op_encrypt,    hlp_encrypt },
+    { "export",     op_export,     hlp_export },
+    { "decrypt",    op_decrypt,    hlp_decrypt },
+    { "delete",     op_delete,     hlp_delete },
+    { "createkey",  op_createkey,  hlp_createkey },
+    { "keylist",    op_keylist,    hlp_keylist },
+    { "import",     op_import,     hlp_import },
+    { "sign",       op_sign,       hlp_sign },
+    { "verify",     op_verify,     hlp_verify },
+    { "version",    op_version,    hlp_version },
+    { "getmore",    op_getmore,    hlp_getmore },
+    { "help",       op_help,       hlp_help },
     { NULL }
   };
   size_t erroff;
@@ -2501,6 +3032,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;
@@ -2546,7 +3078,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)
@@ -2578,13 +3110,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;
 }
@@ -3084,6 +3623,8 @@ main (int argc, char *argv[])
   };
   gpgrt_argparse_t pargs = { &argc, &argv};
 
+  int log_file_set = 0;
+
   gpgrt_set_strusage (my_strusage);
 
 #ifdef HAVE_SETLOCALE
@@ -3120,12 +3661,24 @@ main (int argc, char *argv[])
 
   if (!opt_debug)
     {
+      /* Handling is similar to GPGME_DEBUG */
       const char *s = getenv ("GPGME_JSON_DEBUG");
+      const char *s1;
+
       if (s && atoi (s) > 0)
-        opt_debug = 1;
+        {
+          opt_debug = 1;
+          s1 = strchr (s, PATHSEP_C);
+          if (s1 && strlen (s1) > 2)
+            {
+              s1++;
+              log_set_file (s1);
+              log_file_set = 1;
+            }
+        }
     }
 
-  if (opt_debug)
+  if (opt_debug && !log_file_set)
     {
       const char *home = getenv ("HOME");
       char *file = xstrconcat ("socket://",