spelling: fix misspellings
[gpgme.git] / src / gpgme-json.c
index de62dcc..ba80f08 100644 (file)
@@ -14,7 +14,7 @@
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
- * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * License along with this program; if not, see <https://www.gnu.org/licenses/>.
  * SPDX-License-Identifier: LGPL-2.1+
  */
 
@@ -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 being
+ * 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;
 
 
@@ -89,13 +87,7 @@ static struct
  * Helper functions and macros
  */
 
-#define xtrymalloc(a)  gpgrt_malloc ((a))
 #define xtrystrdup(a)  gpgrt_strdup ((a))
-#define xmalloc(a) ({                           \
-      void *_r = gpgrt_malloc ((a));            \
-      if (!_r)                                  \
-        xoutofcore ("malloc");                  \
-      _r; })
 #define xcalloc(a,b) ({                         \
       void *_r = gpgrt_calloc ((a), (b));       \
       if (!_r)                                  \
@@ -113,6 +105,21 @@ static struct
       _r; })
 #define xfree(a) gpgrt_free ((a))
 
+/* Only use calloc. */
+#define CALLOC_ONLY 1
+
+#if CALLOC_ONLY
+#define xtrymalloc(a)  gpgrt_calloc (1, (a))
+#define xmalloc(a) xcalloc(1, (a))
+#else
+#define xtrymalloc(a)  gpgrt_malloc ((a))
+#define xmalloc(a) ({                           \
+      void *_r = gpgrt_malloc ((a));            \
+      if (!_r)                                  \
+        xoutofcore ("malloc");                  \
+      _r; })
+#endif
+
 #define spacep(p)   (*(p) == ' ' || *(p) == '\t')
 
 #ifndef HAVE_STPCPY
@@ -693,17 +700,17 @@ create_keylist_patterns (cjson_t request, const char *name)
   char *p;
   char *tmp;
   char **ret;
-  int cnt = 1;
+  int cnt = 2; /* Last NULL and one is not newline delimited */
   int i = 0;
 
   if (get_keys (request, name, &keystring))
     return NULL;
 
-  for (p = keystring; p; p++)
+  for (p = keystring; *p; p++)
     if (*p == '\n')
       cnt++;
 
-  ret = xmalloc (cnt * sizeof *ret);
+  ret = xcalloc (cnt, sizeof *ret);
 
   for (p = keystring, tmp = keystring; *p; p++)
     {
@@ -713,54 +720,128 @@ create_keylist_patterns (cjson_t request, const char *name)
       ret[i++] = xstrdup (tmp);
       tmp = p + 1;
     }
-  /* The last key is not newline delimted. */
-  ret[i++] = *tmp ? xstrdup (tmp) : NULL;
-  ret[i] = NULL;
+  /* The last key is not newline delimited. */
+  ret[i] = *tmp ? xstrdup (tmp) : NULL;
 
   xfree (keystring);
   return ret;
 }
 
 
+/* Do a secret keylisting for protocol proto and add the fingerprints of
+   the secret keys for patterns to the result as "sec-fprs" array. */
+static gpg_error_t
+add_secret_fprs (const char **patterns, gpgme_protocol_t protocol,
+                 cjson_t result)
+{
+  gpgme_ctx_t ctx;
+  gpg_error_t err;
+  gpgme_key_t key = NULL;
+  cjson_t j_fprs = xjson_CreateArray ();
+
+  ctx = create_onetime_context (protocol);
+
+  gpgme_set_keylist_mode (ctx, GPGME_KEYLIST_MODE_LOCAL |
+                               GPGME_KEYLIST_MODE_WITH_SECRET);
+
+  err = gpgme_op_keylist_ext_start (ctx, patterns, 1, 0);
+
+  if (err)
+    {
+      gpg_error_object (result, err, "Error listing keys: %s",
+                        gpg_strerror (err));
+      goto leave;
+    }
+
+  while (!(err = gpgme_op_keylist_next (ctx, &key)))
+    {
+      if (!key || !key->fpr)
+        continue;
+      cJSON_AddItemToArray (j_fprs, cJSON_CreateString (key->fpr));
+      gpgme_key_unref (key);
+      key = NULL;
+    }
+  err = 0;
+
+  release_onetime_context (ctx);
+  ctx = NULL;
+
+  xjson_AddItemToObject (result, "sec-fprs", j_fprs);
+
+leave:
+  release_onetime_context (ctx);
+  gpgme_key_unref (key);
+
+  return err;
+}
+
+
 /* Create sigsum json array */
 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;
 }
@@ -1014,18 +1095,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;
 }
@@ -1035,7 +1136,9 @@ 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_AddBoolToObject (result, "is_mime", verify_result->is_mime);
 
   if (verify_result->signatures)
     {
@@ -1044,10 +1147,59 @@ 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;
+}
+
+/* Create a recipient json object */
+static cjson_t
+recipient_to_json (gpgme_recipient_t recp)
+{
+  cjson_t result = xjson_CreateObject ();
+
+  xjson_AddStringToObject0 (result, "keyid", recp->keyid);
+  xjson_AddStringToObject0 (result, "pubkey_algo_name",
+                            gpgme_pubkey_algo_name (recp->pubkey_algo));
+  xjson_AddStringToObject0 (result, "status_string",
+                            gpgme_strerror (recp->status));
+
+  xjson_AddNumberToObject (result, "status_code", recp->status);
+
+  return result;
+}
+
+
+/* Create a JSON object from a gpgme_decrypt result */
+static cjson_t
+decrypt_result_to_json (gpgme_decrypt_result_t decrypt_result)
+{
+  cjson_t result = xjson_CreateObject ();
+
+  xjson_AddStringToObject0 (result, "file_name", decrypt_result->file_name);
+  xjson_AddStringToObject0 (result, "symkey_algo",
+                            decrypt_result->symkey_algo);
+
+  xjson_AddBoolToObject (result, "wrong_key_usage",
+                         decrypt_result->wrong_key_usage);
+  xjson_AddBoolToObject (result, "is_de_vs",
+                         decrypt_result->is_de_vs);
+  xjson_AddBoolToObject (result, "is_mime", decrypt_result->is_mime);
+  xjson_AddBoolToObject (result, "legacy_cipher_nomdc",
+                         decrypt_result->legacy_cipher_nomdc);
+
+  if (decrypt_result->recipients)
+    {
+      cjson_t array = xjson_CreateArray ();
+      gpgme_recipient_t recp;
+
+      for (recp = decrypt_result->recipients; recp; recp = recp->next)
+        cJSON_AddItemToArray (array, recipient_to_json (recp));
+      xjson_AddItemToObject (result, "recipients", array);
+    }
+
+  return result;
 }
 
 
@@ -1286,29 +1438,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);
@@ -1340,49 +1483,117 @@ 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)
+  if (base64)
+    err = add_base64_to_object (result, "data", buffer, buflen);
+  else
+    err = cjson_AddStringToObject (result, "data", buffer);
+
+ leave:
+  gpgme_free (buffer);
+  return err;
+}
+
+
+/* Encode and chunk response.
+ *
+ * If necessary this base64 encodes and chunks the response
+ * for getmore so that we always return valid json independent
+ * of the chunksize.
+ *
+ * A chunked response 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 = 0;
+  char *getmore_request = NULL;
+
+  if (opt_interactive)
+    data = cJSON_Print (response);
+  else
+    data = cJSON_PrintUnformatted (response);
+
+  if (!data)
     {
-      xjson_AddBoolToObject (result, "more", 1);
+      err = GPG_ERR_NO_DATA;
+      goto leave;
+    }
 
-      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;
+  if (!request)
+    {
+      goto leave;
+    }
 
-      pending_data.buffer = buffer;
-      buffer = NULL;
-      pending_data.length = buflen;
-      pending_data.written = chunksize;
-      pending_data.type = type;
-      pending_data.base64 = base64;
+  if ((err = get_chunksize (request, &chunksize)))
+    {
+      err = GPG_ERR_INV_VALUE;
+      goto leave;
     }
-  else
+
+  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 && !data)
+    {
+      err = GPG_ERR_GENERAL;
+    }
+
+  if (err)
+    {
+      cjson_t err_obj = gpg_error_object (NULL, err,
+                                          "Encode and chunk failed: %s",
+                                          gpgme_strerror (err));
+      xfree (data);
+      if (opt_interactive)
+        data = cJSON_Print (err_obj);
+      data = cJSON_PrintUnformatted (err_obj);
+
+      cJSON_Delete (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"
@@ -1392,9 +1603,10 @@ 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)"
+  "file_name:     The file name associated with the data.\n"
+  "sender:        Sender info to embed in a signature.\n"
   "\n"
   "Optional boolean flags (default is false):\n"
   "base64:        Input data is base64 encoded.\n"
@@ -1412,8 +1624,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)
 {
@@ -1421,21 +1632,20 @@ 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;
+  char *file_name = NULL;
   gpgme_data_t input = NULL;
   gpgme_data_t output = NULL;
   int abool;
   gpgme_encrypt_flags_t encrypt_flags = 0;
   gpgme_ctx_t keylist_ctx = NULL;
   gpgme_key_t key = NULL;
+  cjson_t j_tmp = NULL;
 
   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;
@@ -1468,6 +1678,17 @@ op_encrypt (cjson_t request, cjson_t result)
   if (abool)
     encrypt_flags |= GPGME_ENCRYPT_WANT_ADDRESS;
 
+  j_tmp = cJSON_GetObjectItem (request, "file_name");
+  if (j_tmp && cjson_is_string (j_tmp))
+    {
+      file_name = j_tmp->valuestring;
+    }
+
+  j_tmp = cJSON_GetObjectItem (request, "sender");
+  if (j_tmp && cjson_is_string (j_tmp))
+    {
+      gpgme_set_sender (ctx, j_tmp->valuestring);
+    }
 
   /* Get the keys.  */
   err = get_keys (request, "keys", &keystring);
@@ -1516,6 +1737,10 @@ op_encrypt (cjson_t request, cjson_t result)
   if (opt_mime)
     gpgme_data_set_encoding (input, GPGME_DATA_ENCODING_MIME);
 
+  if (file_name)
+    {
+      gpgme_data_set_file_name (input, file_name);
+    }
 
   /* Create an output data object.  */
   err = gpgme_data_new (&output);
@@ -1549,7 +1774,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;
 
@@ -1557,6 +1782,8 @@ op_encrypt (cjson_t request, cjson_t result)
   xfree_array (signing_patterns);
   xfree (keystring);
   release_onetime_context (keylist_ctx);
+  /* Reset sender in case the context is reused */
+  gpgme_set_sender (ctx, NULL);
   gpgme_key_unref (key);
   gpgme_signers_clear (ctx);
   release_context (ctx);
@@ -1573,35 +1800,88 @@ 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"
   "\n"
   "Response on success:\n"
-  "type:   \"plaintext\"\n"
-  "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"
+  "type:     \"plaintext\"\n"
+  "data:     The decrypted data.  This may be base64 encoded.\n"
+  "base64:   Boolean indicating whether data is base64 encoded.\n"
+  "mime:     deprecated - use dec_info is_mime instead\n"
+  "dec_info: An object with decryption information. (gpgme_decrypt_result_t)\n"
+  " Boolean values:\n"
+  "  wrong_key_usage:     Key should not have been used for encryption.\n"
+  "  is_de_vs:            Message was encrypted in compliance to the de-vs\n"
+  "                       mode.\n"
+  "  is_mime:             Message claims that the content is a MIME Message.\n"
+  "  legacy_cipher_nomdc: The message was made by a legacy algorithm\n"
+  "                       without integrity protection.\n"
+  " String values:\n"
+  "  file_name:   The filename contained in the decrypt result.\n"
+  "  symkey_algo: A string with the symmetric encryption algorithm and\n"
+  "               mode using the format \"<algo>.<mode>\".\n"
+  " Array values:\n"
+  "  recipients:  The list of recipients (gpgme_recipient_t).\n"
+  "   String values:\n"
+  "    keyid:            The keyid of the recipient.\n"
+  "    pubkey_algo_name: gpgme_pubkey_algo_name of used algo.\n"
+  "    status_string:    The status code as localized gpg-error string\n"
+  "   Number values:\n"
+  "    status_code:      The status as a number. (gpg_error_t)\n"
+  "info:     Optional an object with verification information.\n"
+  "          (gpgme_verify_result_t)\n"
+  " file_name: The filename contained in the verify result.\n"
+  " is_mime:   The is_mime info contained in the verify result.\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;
@@ -1610,8 +1890,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;
@@ -1642,6 +1920,9 @@ op_decrypt (cjson_t request, cjson_t result)
   if (decrypt_result->is_mime)
     xjson_AddBoolToObject (result, "mime", 1);
 
+  xjson_AddItemToObject (result, "dec_info",
+                         decrypt_result_to_json (decrypt_result));
+
   verify_result = gpgme_op_verify_result (ctx);
   if (verify_result && verify_result->signatures)
     {
@@ -1649,7 +1930,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)
@@ -1676,7 +1957,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"
@@ -1692,15 +1972,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;
@@ -1713,8 +1991,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;
@@ -1796,7 +2072,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;
 
@@ -1819,7 +2095,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"
@@ -1829,25 +2104,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"
+  " is_mime:    Boolean that is true if the messages claims it is MIME.\n"
+  "             Note that this flag is not covered by the signature.)\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;
@@ -1856,8 +2164,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;
@@ -1867,24 +2173,24 @@ op_verify (cjson_t request, cjson_t result)
   if (err && err != gpg_error (GPG_ERR_NO_DATA))
     goto leave;
 
-  /* Create an output data object.  */
-  err = gpgme_data_new (&output);
-  if (err)
+  if (!signature)
     {
-      gpg_error_object (result, err, "Error creating output data object: %s",
-                        gpg_strerror (err));
-      goto leave;
-    }
-
-  /* Verify.  */
-  if (signature)
-    {
-      err = gpgme_op_verify (ctx, signature, input, output);
+      /* Verify opaque or clearsigned we need an output data object.  */
+      err = gpgme_data_new (&output);
+      if (err)
+        {
+          gpg_error_object (result, err,
+                            "Error creating output data object: %s",
+                            gpg_strerror (err));
+          goto leave;
+        }
+      err = gpgme_op_verify (ctx, input, 0, output);
     }
   else
     {
-      err = gpgme_op_verify (ctx, input, 0, output);
+      err = gpgme_op_verify (ctx, signature, input, NULL);
     }
+
   if (err)
     {
       gpg_error_object (result, err, "Verify failed: %s", gpg_strerror (err));
@@ -1902,14 +2208,17 @@ op_verify (cjson_t request, cjson_t result)
                              verify_result_to_json (verify_result));
     }
 
-  err = make_data_object (result, output, chunksize, "plaintext", -1);
-  output = NULL;
-
-  if (err)
+  if (output)
     {
-      gpg_error_object (result, err, "Plaintext output failed: %s",
-                        gpg_strerror (err));
-      goto leave;
+      err = make_data_object (result, output, "plaintext", -1);
+      output = NULL;
+
+      if (err)
+        {
+          gpg_error_object (result, err, "Plaintext output failed: %s",
+                            gpg_strerror (err));
+          goto leave;
+        }
     }
 
  leave:
@@ -1977,10 +2286,10 @@ 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"
+  "secret:        List only secret keys.\n"
+  "with-secret:   Add KEYLIST_MODE_WITH_SECRET.\n"
   "extern:        Add KEYLIST_MODE_EXTERN.\n"
   "local:         Add KEYLIST_MODE_LOCAL. (default mode).\n"
   "sigs:          Add KEYLIST_MODE_SIGS.\n"
@@ -1988,6 +2297,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"
@@ -2095,18 +2405,16 @@ 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;
+  int secret_only = 0;
   gpgme_keylist_mode_t mode = 0;
   gpgme_key_t key = NULL;
   cjson_t keyarray = xjson_CreateArray ();
@@ -2114,15 +2422,19 @@ 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)))
     goto leave;
   if (abool)
+    {
+      mode |= GPGME_KEYLIST_MODE_WITH_SECRET;
+      secret_only = 1;
+    }
+  if ((err = get_boolean_flag (request, "with-secret", 0, &abool)))
+    goto leave;
+  if (abool)
     mode |= GPGME_KEYLIST_MODE_WITH_SECRET;
-
   if ((err = get_boolean_flag (request, "extern", 0, &abool)))
     goto leave;
   if (abool)
@@ -2158,6 +2470,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 */
@@ -2171,8 +2488,7 @@ op_keylist (cjson_t request, cjson_t result)
   gpgme_set_keylist_mode (ctx, mode);
 
   err = gpgme_op_keylist_ext_start (ctx, (const char **) patterns,
-                                    (mode & GPGME_KEYLIST_MODE_WITH_SECRET),
-                                    0);
+                                    secret_only, 0);
   if (err)
     {
       gpg_error_object (result, err, "Error listing keys: %s",
@@ -2286,7 +2602,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"
@@ -2294,31 +2609,32 @@ static const char hlp_export[] =
   "minimal:       Add EXPORT_MODE_MINIMAL.\n"
   "raw:           Add EXPORT_MODE_RAW.\n"
   "pkcs12:        Add EXPORT_MODE_PKCS12.\n"
+  "with-sec-fprs: Add the sec-fprs array to the result.\n"
   "\n"
   "Response on success:\n"
-  "type:   \"keys\"\n"
-  "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.";
+  "type:     \"keys\"\n"
+  "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"
+  "sec-fprs: Optional, only if with-secret is set. An array containing\n"
+  "          the fingerprints of the keys in the export for which a secret\n"
+  "          key is available";
 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;
+  int with_secret = 0;
   gpgme_export_mode_t mode = 0;
   gpgme_data_t output = NULL;
 
   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;
@@ -2353,6 +2669,11 @@ op_export (cjson_t request, cjson_t result)
   if (abool)
     mode |= GPGME_EXPORT_MODE_PKCS12;
 
+  if ((err = get_boolean_flag (request, "with-sec-fprs", 0, &abool)))
+    goto leave;
+  if (abool)
+    with_secret = 1;
+
   /* Get the export patterns.  */
   patterns = create_keylist_patterns (request, "keys");
 
@@ -2375,10 +2696,15 @@ 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;
 
+  if (!err && with_secret)
+    {
+      err = add_secret_fprs ((const char **) patterns, protocol, result);
+    }
+
 leave:
   xfree_array (patterns);
   release_context (ctx);
@@ -2404,7 +2730,7 @@ op_delete (cjson_t request, cjson_t result)
   gpgme_ctx_t ctx = NULL;
   gpgme_ctx_t keylist_ctx = NULL;
   gpgme_protocol_t protocol;
-  gpgme_key_t key;
+  gpgme_key_t key = NULL;
   int secret = 0;
   cjson_t j_key = NULL;
 
@@ -2460,6 +2786,102 @@ 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"
@@ -2546,17 +2968,107 @@ leave:
 
 
 \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"
+  "             Supported values are \"default\" and \"future-default\".\n"
+  "expires:     Seconds from now to expiry as Number.  0 means no expiry.\n"
+  "             The default is to use a standard expiration interval.\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 = GPGME_CREATE_FORCE; /* Always force as the GUI should
+                                              handle checks, if required. */
+  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_THROUGH_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_THROUGH_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)
 {
@@ -2568,9 +3080,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)
@@ -2581,8 +3096,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)
     {
@@ -2591,7 +3106,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
     {
@@ -2606,21 +3121,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;
             }
         }
@@ -2641,17 +3151,26 @@ static const char hlp_help[] =
   "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)
 {
@@ -2688,18 +3207,20 @@ process_request (const char *request)
     gpg_error_t (*handler)(cjson_t request, cjson_t result);
     const char * const helpstr;
   } optbl[] = {
-    { "config",  op_config,  hlp_config },
-    { "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;
@@ -2707,8 +3228,9 @@ 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;
+  char *res = NULL;
   int idx;
 
   response = xjson_CreateObject ();
@@ -2752,7 +3274,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)
@@ -2784,14 +3306,37 @@ 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_t err_obj;
+
+      log_error ("printing JSON data failed\n");
+
+      err_obj = error_object (NULL, "Printing JSON data failed");
+      if (opt_interactive)
+        res = cJSON_Print (err_obj);
+      res = cJSON_PrintUnformatted (err_obj);
+      cJSON_Delete (err_obj);
+    }
+
+  cJSON_Delete (json);
   cJSON_Delete (response);
+
+  if (!res)
+    {
+      /* Can't happen unless we created a broken error_object above */
+      return xtrystrdup ("Bug: Fatal error in process request\n");
+    }
   return res;
 }
 
@@ -3136,7 +3681,7 @@ native_messaging_repl (void)
 
   for (;;)
     {
-      /* Read length.  Note that the protocol uses native endianess.
+      /* Read length.  Note that the protocol uses native endianness.
        * Is it allowed to call such a thing a well thought out
        * protocol?  */
       if (es_read (es_stdin, &nrequest, sizeof nrequest, &n))
@@ -3157,13 +3702,13 @@ native_messaging_repl (void)
           log_error ("error reading request: request too long (%zu MiB)\n",
                      (size_t)nrequest / (1024*1024));
           /* Fixme: Shall we read the request to the bit bucket and
-           * return an error reponse or just return an error reponse
+           * return an error response or just return an error response
            * and terminate?  Needs some testing.  */
           break;
         }
 
       /* Read request.  */
-      request = xtrymalloc (nrequest);
+      request = xtrymalloc (nrequest + 1);
       if (!request)
         {
           err = gpg_error_from_syserror ();
@@ -3188,6 +3733,7 @@ native_messaging_repl (void)
         }
       else /* Process request  */
         {
+          request[n] = '\0'; /* Ensure that request has an end */
           if (opt_debug)
             log_debug ("request='%s'\n", request);
           xfree (response);
@@ -3226,6 +3772,10 @@ native_messaging_repl (void)
           log_error ("error writing request: %s\n", gpg_strerror (err));
           break;
         }
+      xfree (response);
+      response = NULL;
+      xfree (request);
+      request = NULL;
     }
 
   xfree (response);
@@ -3290,6 +3840,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
@@ -3326,12 +3878,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://",