json: Add keylist mode locate
[gpgme.git] / src / gpgme-json.c
index 3cdd744..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;
 
 
@@ -467,12 +465,13 @@ get_chunksize (cjson_t json, size_t *r_chunksize)
 }
 
 
-/* Extract the keys from the "keys" array in the JSON object.  On
- * success a string with the keys identifiers is stored at R_KEYS.
+/* Extract the keys from the array or string with the name "name"
+ * in the JSON object.  On success a string with the keys identifiers
+ * is stored at R_KEYS.
  * The keys in that string are LF delimited.  On failure an error code
  * is returned.  */
 static gpg_error_t
-get_keys (cjson_t json, char **r_keystring)
+get_keys (cjson_t json, const char *name, char **r_keystring)
 {
   cjson_t j_keys, j_item;
   int i, nkeys;
@@ -481,7 +480,7 @@ get_keys (cjson_t json, char **r_keystring)
 
   *r_keystring = NULL;
 
-  j_keys = cJSON_GetObjectItem (json, "keys");
+  j_keys = cJSON_GetObjectItem (json, name);
   if (!j_keys)
     return gpg_error (GPG_ERR_NO_KEY);
   if (!cjson_is_array (j_keys) && !cjson_is_string (j_keys))
@@ -564,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)
     {
@@ -578,12 +577,17 @@ 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);
 }
 
 
-
 /* Free context object retrieved by get_context.  */
 static void
 release_context (gpgme_ctx_t ctx)
@@ -593,6 +597,23 @@ release_context (gpgme_ctx_t ctx)
 }
 
 
+/* Create an addition context for short operations. */
+static gpgme_ctx_t
+create_onetime_context (gpgme_protocol_t proto)
+{
+  return _create_new_context (proto);
+
+}
+
+
+/* Release a one-time context.  */
+static void
+release_onetime_context (gpgme_ctx_t ctx)
+{
+  return gpgme_release (ctx);
+
+}
+
 
 /* Given a Base-64 encoded string object in JSON return a gpgme data
  * object at R_DATA.  */
@@ -664,7 +685,7 @@ data_from_base64_string (gpgme_data_t *r_data, cjson_t json)
  * string array which can be used as patterns for
  * op_keylist_ext or NULL. */
 static char **
-create_keylist_patterns (cjson_t request)
+create_keylist_patterns (cjson_t request, const char *name)
 {
   char *keystring;
   char *p;
@@ -673,14 +694,14 @@ create_keylist_patterns (cjson_t request)
   int cnt = 1;
   int i = 0;
 
-  if (get_keys (request, &keystring))
+  if (get_keys (request, name, &keystring))
     return NULL;
 
   for (p = keystring; p; p++)
     if (*p == '\n')
       cnt++;
 
-  ret = (char **) xmalloc (cnt * sizeof (char *));
+  ret = xmalloc (cnt * sizeof *ret);
 
   for (p = keystring, tmp = keystring; *p; p++)
     {
@@ -703,41 +724,68 @@ create_keylist_patterns (cjson_t request)
 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;
 }
@@ -984,33 +1032,58 @@ key_to_json (gpgme_key_t key)
   return result;
 }
 
+
 /* Create a signature json object */
 static cjson_t
 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;
 }
 
+
 /* Create a JSON object from a gpgme_verify result */
 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)
     {
@@ -1019,12 +1092,13 @@ 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 JSON object from an engine_info */
 static cjson_t
 engine_info_to_json (gpgme_engine_info_t info)
@@ -1041,6 +1115,8 @@ engine_info_to_json (gpgme_engine_info_t info)
                                                 "default");
   return result;
 }
+
+
 /* Create a JSON object from an import_status */
 static cjson_t
 import_status_to_json (gpgme_import_status_t sts)
@@ -1092,6 +1168,120 @@ import_result_to_json (gpgme_import_result_t imp)
   return result;
 }
 
+
+/* 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.
  *
@@ -1143,29 +1333,21 @@ get_string_data (cjson_t request, cjson_t result, const char *name,
   return 0;
 }
 
-\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);
@@ -1197,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"
@@ -1249,7 +1480,8 @@ 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"
   "Optional boolean flags (default is false):\n"
   "base64:        Input data is base64 encoded.\n"
@@ -1267,27 +1499,26 @@ 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)
 {
   gpg_error_t err;
   gpgme_ctx_t ctx = NULL;
   gpgme_protocol_t protocol;
-  size_t chunksize;
+  char **signing_patterns = NULL;
   int opt_mime;
   char *keystring = 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;
 
   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;
@@ -1322,7 +1553,7 @@ op_encrypt (cjson_t request, cjson_t result)
 
 
   /* Get the keys.  */
-  err = get_keys (request, &keystring);
+  err = get_keys (request, "keys", &keystring);
   if (err)
     {
       /* Provide a custom error response.  */
@@ -1331,6 +1562,37 @@ op_encrypt (cjson_t request, cjson_t result)
       goto leave;
     }
 
+  /* Do we have signing keys ? */
+  signing_patterns = create_keylist_patterns (request, "signing_keys");
+  if (signing_patterns)
+    {
+      keylist_ctx = create_onetime_context (protocol);
+      gpgme_set_keylist_mode (keylist_ctx, GPGME_KEYLIST_MODE_LOCAL);
+
+      err = gpgme_op_keylist_ext_start (keylist_ctx,
+                                        (const char **) signing_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 (keylist_ctx, &key)))
+        {
+          if ((err = gpgme_signers_add (ctx, key)))
+            {
+              gpg_error_object (result, err, "Error adding signer: %s",
+                                gpg_strerror (err));
+              goto leave;
+            }
+          gpgme_key_unref (key);
+          key = NULL;
+        }
+      release_onetime_context (keylist_ctx);
+      keylist_ctx = NULL;
+    }
+
   if ((err = get_string_data (request, result, "data", &input)))
       goto leave;
 
@@ -1348,8 +1610,17 @@ op_encrypt (cjson_t request, cjson_t result)
     }
 
   /* Encrypt.  */
-  err = gpgme_op_encrypt_ext (ctx, NULL, keystring, encrypt_flags,
-                              input, output);
+  if (!signing_patterns)
+    {
+      err = gpgme_op_encrypt_ext (ctx, NULL, keystring, encrypt_flags,
+                                  input, output);
+    }
+  else
+    {
+      err = gpgme_op_encrypt_sign_ext (ctx, NULL, keystring, encrypt_flags,
+                                       input, output);
+
+    }
   /* encrypt_result = gpgme_op_encrypt_result (ctx); */
   if (err)
     {
@@ -1361,12 +1632,16 @@ 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;
 
  leave:
+  xfree_array (signing_patterns);
   xfree (keystring);
+  release_onetime_context (keylist_ctx);
+  gpgme_key_unref (key);
+  gpgme_signers_clear (ctx);
   release_context (ctx);
   gpgme_data_release (input);
   gpgme_data_release (output);
@@ -1381,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"
@@ -1391,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;
@@ -1418,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;
@@ -1457,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)
@@ -1484,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"
@@ -1500,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;
@@ -1521,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;
@@ -1547,7 +1847,7 @@ op_sign (cjson_t request, cjson_t result)
       gpgme_set_sender (ctx, j_tmp->valuestring);
     }
 
-  patterns = create_keylist_patterns (request);
+  patterns = create_keylist_patterns (request, "keys");
   if (!patterns)
     {
       gpg_error_object (result, err, "Error getting keys: %s",
@@ -1556,19 +1856,18 @@ op_sign (cjson_t request, cjson_t result)
     }
 
   /* Do a keylisting and add the keys */
-  if ((err = gpgme_new (&keylist_ctx)))
-    goto leave;
-  gpgme_set_protocol (keylist_ctx, protocol);
+  keylist_ctx = create_onetime_context (protocol);
   gpgme_set_keylist_mode (keylist_ctx, GPGME_KEYLIST_MODE_LOCAL);
 
-  err = gpgme_op_keylist_ext_start (ctx, (const char **) patterns, 1, 0);
+  err = gpgme_op_keylist_ext_start (keylist_ctx,
+                                    (const char **) 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)))
+  while (!(err = gpgme_op_keylist_next (keylist_ctx, &key)))
     {
       if ((err = gpgme_signers_add (ctx, key)))
         {
@@ -1577,10 +1876,11 @@ op_sign (cjson_t request, cjson_t result)
           goto leave;
         }
       gpgme_key_unref (key);
+      key = NULL;
     }
 
   if ((err = get_string_data (request, result, "data", &input)))
-      goto leave;
+    goto leave;
 
   /* Create an output data object.  */
   err = gpgme_data_new (&output);
@@ -1604,18 +1904,22 @@ 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;
 
  leave:
   xfree_array (patterns);
+  gpgme_signers_clear (ctx);
+  gpgme_key_unref (key);
+  release_onetime_context (keylist_ctx);
   release_context (ctx);
-  release_context (keylist_ctx);
   gpgme_data_release (input);
   gpgme_data_release (output);
   return err;
 }
+
+
 \f
 static const char hlp_verify[] =
   "op:     \"verify\"\n"
@@ -1623,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"
@@ -1633,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;
@@ -1660,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;
@@ -1706,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)
@@ -1723,6 +2057,8 @@ op_verify (cjson_t request, cjson_t result)
   gpgme_data_release (signature);
   return err;
 }
+
+
 \f
 static const char hlp_version[] =
   "op:     \"version\"\n"
@@ -1742,6 +2078,8 @@ op_version (cjson_t request, cjson_t result)
   gpgme_engine_info_t ei = NULL;
   cjson_t infos = xjson_CreateArray ();
 
+  (void)request;
+
   if (!cJSON_AddStringToObject (result, "gpgme", gpgme_check_version (NULL)))
     {
       cJSON_Delete (infos);
@@ -1766,6 +2104,8 @@ op_version (cjson_t request, cjson_t result)
 
   return 0;
 }
+
+
 \f
 static const char hlp_keylist[] =
   "op:     \"keylist\"\n"
@@ -1775,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"
@@ -1786,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"
@@ -1893,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;
@@ -1912,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)))
@@ -1956,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 */
@@ -1963,12 +2303,9 @@ op_keylist (cjson_t request, cjson_t result)
     }
 
   /* Get the keys.  */
-  patterns = create_keylist_patterns (request);
+  patterns = create_keylist_patterns (request, "keys");
 
   /* Do a keylisting and add the keys */
-  if ((err = gpgme_new (&ctx)))
-    goto leave;
-  gpgme_set_protocol (ctx, protocol);
   gpgme_set_keylist_mode (ctx, mode);
 
   err = gpgme_op_keylist_ext_start (ctx, (const char **) patterns,
@@ -2002,6 +2339,8 @@ op_keylist (cjson_t request, cjson_t result)
     }
   return err;
 }
+
+
 \f
 static const char hlp_import[] =
   "op:     \"import\"\n"
@@ -2075,18 +2414,465 @@ op_import (cjson_t request, cjson_t result)
   gpgme_data_release (input);
   return err;
 }
+
+
+static const char hlp_export[] =
+  "op:     \"export\"\n"
+  "\n"
+  "Optional parameters:\n"
+  "keys:          Array of strings or fingerprints to lookup\n"
+  "               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"
+  "\n"
+  "Optional boolean flags (default is false):\n"
+  "armor:         Request output in armored format.\n"
+  "extern:        Add EXPORT_MODE_EXTERN.\n"
+  "minimal:       Add EXPORT_MODE_MINIMAL.\n"
+  "raw:           Add EXPORT_MODE_RAW.\n"
+  "pkcs12:        Add EXPORT_MODE_PKCS12.\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";
+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;
+  char **patterns = NULL;
+  int abool;
+  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_boolean_flag (request, "armor", 0, &abool)))
+    goto leave;
+  gpgme_set_armor (ctx, abool);
+
+  /* Handle the various export mode bools. */
+  if ((err = get_boolean_flag (request, "secret", 0, &abool)))
+    goto leave;
+  if (abool)
+    {
+      err = gpg_error (GPG_ERR_FORBIDDEN);
+      goto leave;
+    }
+
+  if ((err = get_boolean_flag (request, "extern", 0, &abool)))
+    goto leave;
+  if (abool)
+    mode |= GPGME_EXPORT_MODE_EXTERN;
+
+  if ((err = get_boolean_flag (request, "minimal", 0, &abool)))
+    goto leave;
+  if (abool)
+    mode |= GPGME_EXPORT_MODE_MINIMAL;
+
+  if ((err = get_boolean_flag (request, "raw", 0, &abool)))
+    goto leave;
+  if (abool)
+    mode |= GPGME_EXPORT_MODE_RAW;
+
+  if ((err = get_boolean_flag (request, "pkcs12", 0, &abool)))
+    goto leave;
+  if (abool)
+    mode |= GPGME_EXPORT_MODE_PKCS12;
+
+  /* Get the export patterns.  */
+  patterns = create_keylist_patterns (request, "keys");
+
+  /* Create 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_export_ext (ctx, (const char **) patterns,
+                             mode, output);
+  if (err)
+    {
+      gpg_error_object (result, err, "Error exporting keys: %s",
+                        gpg_strerror (err));
+      goto leave;
+    }
+
+  /* We need to base64 if armoring has not been requested.  */
+  err = make_data_object (result, output,
+                          "keys", !gpgme_get_armor (ctx));
+  output = NULL;
+
+leave:
+  xfree_array (patterns);
+  release_context (ctx);
+  gpgme_data_release (output);
+
+  return err;
+}
+
+
+static const char hlp_delete[] =
+  "op:     \"delete\"\n"
+  "key:    Fingerprint of the key to delete.\n"
+  "\n"
+  "Optional parameters:\n"
+  "protocol:      Either \"openpgp\" (default) or \"cms\".\n"
+  "\n"
+  "Response on success:\n"
+  "success:   Boolean true.\n";
+static gpg_error_t
+op_delete (cjson_t request, cjson_t result)
+{
+  gpg_error_t err;
+  gpgme_ctx_t ctx = NULL;
+  gpgme_ctx_t keylist_ctx = NULL;
+  gpgme_protocol_t protocol;
+  gpgme_key_t key;
+  int secret = 0;
+  cjson_t j_key = NULL;
+
+  if ((err = get_protocol (request, &protocol)))
+    goto leave;
+  ctx = get_context (protocol);
+  keylist_ctx = get_context (protocol);
+
+  if ((err = get_boolean_flag (request, "secret", 0, &secret)))
+    goto leave;
+  if (secret)
+    {
+      err = gpg_error (GPG_ERR_FORBIDDEN);
+      goto leave;
+    }
+
+  j_key = cJSON_GetObjectItem (request, "key");
+  if (!j_key)
+    {
+      err = gpg_error (GPG_ERR_NO_KEY);
+      goto leave;
+    }
+  if (!cjson_is_string (j_key))
+    {
+      err = gpg_error (GPG_ERR_INV_VALUE);
+      goto leave;
+    }
+
+  /* Get the key */
+  if ((err = gpgme_get_key (keylist_ctx, j_key->valuestring, &key, 0)))
+    {
+      gpg_error_object (result, err, "Error fetching key for delete: %s",
+                        gpg_strerror (err));
+      goto leave;
+    }
+
+  err = gpgme_op_delete (ctx, key, 0);
+  if (err)
+    {
+      gpg_error_object (result, err, "Error deleting key: %s",
+                        gpg_strerror (err));
+      goto leave;
+    }
+
+  xjson_AddBoolToObject (result, "success", 1);
+
+leave:
+  gpgme_key_unref (key);
+  release_context (ctx);
+  release_context (keylist_ctx);
+
+  return err;
+}
+
+
+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"
-  "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\".";
+  "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"
+  "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)
 {
@@ -2098,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)
@@ -2111,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)
     {
@@ -2121,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
     {
@@ -2136,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;
             }
         }
@@ -2170,15 +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"
-  "  encrypt     Encrypt data.\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)
 {
@@ -2215,15 +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 },
-    { "decrypt", op_decrypt, hlp_decrypt },
-    { "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;
@@ -2231,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;
@@ -2276,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)
@@ -2308,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;
 }
@@ -2814,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
@@ -2850,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://",