json: Add keylist mode locate
[gpgme.git] / src / gpgme-json.c
index 90c9aea..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;
 
 
@@ -128,6 +126,19 @@ _my_stpcpy (char *a, const char *b)
 #endif /*!HAVE_STPCPY*/
 
 
+/* Free a NULL terminated array */
+static void
+xfree_array (char **array)
+{
+  if (array)
+    {
+      int idx;
+      for (idx = 0; array[idx]; idx++)
+        xfree (array[idx]);
+      xfree (array);
+    }
+}
+
 
 static void
 xoutofcore (const char *type)
@@ -180,6 +191,15 @@ xjson_AddStringToObject (cjson_t object, const char *name, const char *string)
 }
 
 
+/* Same as xjson_AddStringToObject but ignores NULL strings */
+static void
+xjson_AddStringToObject0 (cjson_t object, const char *name, const char *string)
+{
+  if (!string)
+    return;
+  xjson_AddStringToObject (object, name, string);
+}
+
 /* Wrapper around cJSON_AddBoolToObject which terminates the process
  * in case of an error.  */
 static void
@@ -200,6 +220,16 @@ xjson_AddNumberToObject (cjson_t object, const char *name, double dbl)
   return ;
 }
 
+/* Wrapper around cJSON_AddItemToObject which terminates the process
+ * in case of an error.  */
+static void
+xjson_AddItemToObject (cjson_t object, const char *name, cjson_t item)
+{
+  if (!cJSON_AddItemToObject (object, name, item))
+    xoutofcore ("cJSON_AddItemToObject");
+  return ;
+}
+
 /* This is similar to cJSON_AddStringToObject but takes (DATA,
  * DATALEN) and adds it under NAME as a base 64 encoded string to
  * OBJECT.  */
@@ -435,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;
@@ -449,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))
@@ -532,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)
     {
@@ -546,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)
@@ -561,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.  */
@@ -627,46 +680,114 @@ data_from_base64_string (gpgme_data_t *r_data, cjson_t json)
 }
 
 
-/* Helper for summary formatting */
-static void
-add_summary_to_object (cjson_t result, gpgme_sigsum_t summary)
+/* Create a keylist pattern array from a json keys object
+ * in the request. Returns either a malloced NULL terminated
+ * string array which can be used as patterns for
+ * op_keylist_ext or NULL. */
+static char **
+create_keylist_patterns (cjson_t request, const char *name)
+{
+  char *keystring;
+  char *p;
+  char *tmp;
+  char **ret;
+  int cnt = 1;
+  int i = 0;
+
+  if (get_keys (request, name, &keystring))
+    return NULL;
+
+  for (p = keystring; p; p++)
+    if (*p == '\n')
+      cnt++;
+
+  ret = xmalloc (cnt * sizeof *ret);
+
+  for (p = keystring, tmp = keystring; *p; p++)
+    {
+      if (*p != '\n')
+        continue;
+      *p = '\0';
+      ret[i++] = xstrdup (tmp);
+      tmp = p + 1;
+    }
+  /* The last key is not newline delimted. */
+  ret[i++] = *tmp ? xstrdup (tmp) : NULL;
+  ret[i] = NULL;
+
+  xfree (keystring);
+  return ret;
+}
+
+
+/* Create sigsum json array */
+static cjson_t
+sigsum_to_json (gpgme_sigsum_t summary)
 {
-  cjson_t response = xjson_CreateArray ();
+  cjson_t result = xjson_CreateObject ();
+  cjson_t sigsum_array = xjson_CreateArray ();
+
   if ( (summary & GPGME_SIGSUM_VALID      ))
-    cJSON_AddItemToArray (response,
+    cJSON_AddItemToArray (sigsum_array,
         cJSON_CreateString ("valid"));
   if ( (summary & GPGME_SIGSUM_GREEN      ))
-    cJSON_AddItemToArray (response,
+    cJSON_AddItemToArray (sigsum_array,
         cJSON_CreateString ("green"));
   if ( (summary & GPGME_SIGSUM_RED        ))
-    cJSON_AddItemToArray (response,
+    cJSON_AddItemToArray (sigsum_array,
         cJSON_CreateString ("red"));
   if ( (summary & GPGME_SIGSUM_KEY_REVOKED))
-    cJSON_AddItemToArray (response,
+    cJSON_AddItemToArray (sigsum_array,
         cJSON_CreateString ("revoked"));
   if ( (summary & GPGME_SIGSUM_KEY_EXPIRED))
-    cJSON_AddItemToArray (response,
+    cJSON_AddItemToArray (sigsum_array,
         cJSON_CreateString ("key-expired"));
   if ( (summary & GPGME_SIGSUM_SIG_EXPIRED))
-    cJSON_AddItemToArray (response,
+    cJSON_AddItemToArray (sigsum_array,
         cJSON_CreateString ("sig-expired"));
   if ( (summary & GPGME_SIGSUM_KEY_MISSING))
-    cJSON_AddItemToArray (response,
+    cJSON_AddItemToArray (sigsum_array,
         cJSON_CreateString ("key-missing"));
   if ( (summary & GPGME_SIGSUM_CRL_MISSING))
-    cJSON_AddItemToArray (response,
+    cJSON_AddItemToArray (sigsum_array,
         cJSON_CreateString ("crl-missing"));
   if ( (summary & GPGME_SIGSUM_CRL_TOO_OLD))
-    cJSON_AddItemToArray (response,
+    cJSON_AddItemToArray (sigsum_array,
         cJSON_CreateString ("crl-too-old"));
   if ( (summary & GPGME_SIGSUM_BAD_POLICY ))
-    cJSON_AddItemToArray (response,
+    cJSON_AddItemToArray (sigsum_array,
         cJSON_CreateString ("bad-policy"));
   if ( (summary & GPGME_SIGSUM_SYS_ERROR  ))
-    cJSON_AddItemToArray (response,
+    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  ));
 
-  cJSON_AddItemToObject (result, "summary", response);
+  return result;
 }
 
 
@@ -686,165 +807,481 @@ validity_to_string (gpgme_validity_t val)
     }
 }
 
-
-/* Add a single signature to a result */
-static gpg_error_t
-add_signature_to_object (cjson_t result, gpgme_signature_t sig)
+static const char *
+protocol_to_string (gpgme_protocol_t proto)
 {
-  gpg_error_t err = 0;
-
-  if (!cJSON_AddStringToObject (result, "status",
-                                gpgme_strerror (sig->status)))
+  switch (proto)
     {
-      err = gpg_error_from_syserror ();
-      goto leave;
+    case GPGME_PROTOCOL_OpenPGP: return "OpenPGP";
+    case GPGME_PROTOCOL_CMS:     return "CMS";
+    case GPGME_PROTOCOL_GPGCONF: return "gpgconf";
+    case GPGME_PROTOCOL_ASSUAN:  return "assuan";
+    case GPGME_PROTOCOL_G13:     return "g13";
+    case GPGME_PROTOCOL_UISERVER:return "uiserver";
+    case GPGME_PROTOCOL_SPAWN:   return "spawn";
+    default:
+                                 return "unknown";
     }
+}
+
+/* Create a sig_notation json object */
+static cjson_t
+sig_notation_to_json (gpgme_sig_notation_t not)
+{
+  cjson_t result = xjson_CreateObject ();
+  xjson_AddBoolToObject (result, "human_readable", not->human_readable);
+  xjson_AddBoolToObject (result, "critical", not->critical);
+
+  xjson_AddStringToObject0 (result, "name", not->name);
+  xjson_AddStringToObject0 (result, "value", not->value);
+
+  xjson_AddNumberToObject (result, "flags", not->flags);
+
+  return result;
+}
+
+/* Create a key_sig json object */
+static cjson_t
+key_sig_to_json (gpgme_key_sig_t sig)
+{
+  cjson_t result = xjson_CreateObject ();
+
+  xjson_AddBoolToObject (result, "revoked", sig->revoked);
+  xjson_AddBoolToObject (result, "expired", sig->expired);
+  xjson_AddBoolToObject (result, "invalid", sig->invalid);
+  xjson_AddBoolToObject (result, "exportable", sig->exportable);
 
-  if (!cJSON_AddNumberToObject (result, "code", sig->status))
+  xjson_AddStringToObject0 (result, "pubkey_algo_name",
+                            gpgme_pubkey_algo_name (sig->pubkey_algo));
+  xjson_AddStringToObject0 (result, "keyid", sig->keyid);
+  xjson_AddStringToObject0 (result, "status", gpgme_strerror (sig->status));
+  xjson_AddStringToObject0 (result, "name", sig->name);
+  xjson_AddStringToObject0 (result, "email", sig->email);
+  xjson_AddStringToObject0 (result, "comment", sig->comment);
+
+  xjson_AddNumberToObject (result, "pubkey_algo", sig->pubkey_algo);
+  xjson_AddNumberToObject (result, "timestamp", sig->timestamp);
+  xjson_AddNumberToObject (result, "expires", sig->expires);
+  xjson_AddNumberToObject (result, "status_code", sig->status);
+  xjson_AddNumberToObject (result, "sig_class", sig->sig_class);
+
+  if (sig->notations)
     {
-      err = gpg_error_from_syserror ();
-      goto leave;
+      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);
     }
 
-  add_summary_to_object (result, sig->summary);
+  return result;
+}
+
+/* Create a tofu info object */
+static cjson_t
+tofu_to_json (gpgme_tofu_info_t tofu)
+{
+  cjson_t result = xjson_CreateObject ();
+
+  xjson_AddStringToObject0 (result, "description", tofu->description);
+
+  xjson_AddNumberToObject (result, "validity", tofu->validity);
+  xjson_AddNumberToObject (result, "policy", tofu->policy);
+  xjson_AddNumberToObject (result, "signcount", tofu->signcount);
+  xjson_AddNumberToObject (result, "encrcount", tofu->encrcount);
+  xjson_AddNumberToObject (result, "signfirst", tofu->signfirst);
+  xjson_AddNumberToObject (result, "signlast", tofu->signlast);
+  xjson_AddNumberToObject (result, "encrfirst", tofu->encrfirst);
+  xjson_AddNumberToObject (result, "encrlast", tofu->encrlast);
+
+  return result;
+}
+
+/* Create a userid json object */
+static cjson_t
+uid_to_json (gpgme_user_id_t uid)
+{
+  cjson_t result = xjson_CreateObject ();
+
+  xjson_AddBoolToObject (result, "revoked", uid->revoked);
+  xjson_AddBoolToObject (result, "invalid", uid->invalid);
+
+  xjson_AddStringToObject0 (result, "validity",
+                            validity_to_string (uid->validity));
+  xjson_AddStringToObject0 (result, "uid", uid->uid);
+  xjson_AddStringToObject0 (result, "name", uid->name);
+  xjson_AddStringToObject0 (result, "email", uid->email);
+  xjson_AddStringToObject0 (result, "comment", uid->comment);
+  xjson_AddStringToObject0 (result, "address", uid->address);
+
+  xjson_AddNumberToObject (result, "origin", uid->origin);
+  xjson_AddNumberToObject (result, "last_update", uid->last_update);
 
-  if (!cJSON_AddStringToObject (result, "fingerprint", sig->fpr))
+  /* Key sigs */
+  if (uid->signatures)
     {
-      err = gpg_error_from_syserror ();
-      goto leave;
+      cjson_t sig_array = xjson_CreateArray ();
+      gpgme_key_sig_t sig;
+
+      for (sig = uid->signatures; sig; sig = sig->next)
+        cJSON_AddItemToArray (sig_array, key_sig_to_json (sig));
+
+      xjson_AddItemToObject (result, "signatures", sig_array);
     }
 
-  if (!cJSON_AddNumberToObject (result, "created", sig->timestamp))
+  /* TOFU info */
+  if (uid->tofu)
     {
-      err = gpg_error_from_syserror ();
-      goto leave;
+      gpgme_tofu_info_t tofu;
+      cjson_t array = xjson_CreateArray ();
+      for (tofu = uid->tofu; tofu; tofu = tofu->next)
+        cJSON_AddItemToArray (array, tofu_to_json (tofu));
+      xjson_AddItemToObject (result, "tofu", array);
     }
 
-  if (!cJSON_AddNumberToObject (result, "expired", sig->exp_timestamp))
+  return result;
+}
+
+/* Create a subkey json object */
+static cjson_t
+subkey_to_json (gpgme_subkey_t sub)
+{
+  cjson_t result = xjson_CreateObject ();
+
+  xjson_AddBoolToObject (result, "revoked", sub->revoked);
+  xjson_AddBoolToObject (result, "expired", sub->expired);
+  xjson_AddBoolToObject (result, "disabled", sub->disabled);
+  xjson_AddBoolToObject (result, "invalid", sub->invalid);
+  xjson_AddBoolToObject (result, "can_encrypt", sub->can_encrypt);
+  xjson_AddBoolToObject (result, "can_sign", sub->can_sign);
+  xjson_AddBoolToObject (result, "can_certify", sub->can_certify);
+  xjson_AddBoolToObject (result, "can_authenticate", sub->can_authenticate);
+  xjson_AddBoolToObject (result, "secret", sub->secret);
+  xjson_AddBoolToObject (result, "is_qualified", sub->is_qualified);
+  xjson_AddBoolToObject (result, "is_cardkey", sub->is_cardkey);
+  xjson_AddBoolToObject (result, "is_de_vs", sub->is_de_vs);
+
+  xjson_AddStringToObject0 (result, "pubkey_algo_name",
+                            gpgme_pubkey_algo_name (sub->pubkey_algo));
+  xjson_AddStringToObject0 (result, "pubkey_algo_string",
+                            gpgme_pubkey_algo_string (sub));
+  xjson_AddStringToObject0 (result, "keyid", sub->keyid);
+  xjson_AddStringToObject0 (result, "card_number", sub->card_number);
+  xjson_AddStringToObject0 (result, "curve", sub->curve);
+  xjson_AddStringToObject0 (result, "keygrip", sub->keygrip);
+
+  xjson_AddNumberToObject (result, "pubkey_algo", sub->pubkey_algo);
+  xjson_AddNumberToObject (result, "length", sub->length);
+  xjson_AddNumberToObject (result, "timestamp", sub->timestamp);
+  xjson_AddNumberToObject (result, "expires", sub->expires);
+
+  return result;
+}
+
+/* Create a key json object */
+static cjson_t
+key_to_json (gpgme_key_t key)
+{
+  cjson_t result = xjson_CreateObject ();
+
+  xjson_AddBoolToObject (result, "revoked", key->revoked);
+  xjson_AddBoolToObject (result, "expired", key->expired);
+  xjson_AddBoolToObject (result, "disabled", key->disabled);
+  xjson_AddBoolToObject (result, "invalid", key->invalid);
+  xjson_AddBoolToObject (result, "can_encrypt", key->can_encrypt);
+  xjson_AddBoolToObject (result, "can_sign", key->can_sign);
+  xjson_AddBoolToObject (result, "can_certify", key->can_certify);
+  xjson_AddBoolToObject (result, "can_authenticate", key->can_authenticate);
+  xjson_AddBoolToObject (result, "secret", key->secret);
+  xjson_AddBoolToObject (result, "is_qualified", key->is_qualified);
+
+  xjson_AddStringToObject0 (result, "protocol",
+                            protocol_to_string (key->protocol));
+  xjson_AddStringToObject0 (result, "issuer_serial", key->issuer_serial);
+  xjson_AddStringToObject0 (result, "issuer_name", key->issuer_name);
+  xjson_AddStringToObject0 (result, "fingerprint", key->fpr);
+  xjson_AddStringToObject0 (result, "chain_id", key->chain_id);
+  xjson_AddStringToObject0 (result, "owner_trust",
+                            validity_to_string (key->owner_trust));
+
+  xjson_AddNumberToObject (result, "origin", key->origin);
+  xjson_AddNumberToObject (result, "last_update", key->last_update);
+
+  /* Add subkeys */
+  if (key->subkeys)
     {
-      err = gpg_error_from_syserror ();
-      goto leave;
+      cjson_t subkey_array = xjson_CreateArray ();
+      gpgme_subkey_t sub;
+      for (sub = key->subkeys; sub; sub = sub->next)
+        cJSON_AddItemToArray (subkey_array, subkey_to_json (sub));
+
+      xjson_AddItemToObject (result, "subkeys", subkey_array);
     }
 
-  if (!cJSON_AddStringToObject (result, "validity",
-                                validity_to_string (sig->validity)))
+  /* User Ids */
+  if (key->uids)
     {
-      err = gpg_error_from_syserror ();
-      goto leave;
+      cjson_t uid_array = xjson_CreateArray ();
+      gpgme_user_id_t uid;
+      for (uid = key->uids; uid; uid = uid->next)
+        cJSON_AddItemToArray (uid_array, uid_to_json (uid));
+
+      xjson_AddItemToObject (result, "userids", uid_array);
     }
 
-leave:
-  return err;
+  return result;
 }
 
 
-/* Add multiple signatures as an array to a result */
-static gpg_error_t
-add_signatures_to_object (cjson_t result, gpgme_signature_t signatures)
+/* Create a signature json object */
+static cjson_t
+signature_to_json (gpgme_signature_t sig)
 {
-  cjson_t response = xjson_CreateArray ();
-  gpg_error_t err = 0;
-  gpgme_signature_t sig;
+  cjson_t result = xjson_CreateObject ();
 
-  for (sig = signatures; sig; sig = sig->next)
-    {
-      cjson_t sig_obj = xjson_CreateObject ();
-      err = add_signature_to_object (sig_obj, sig);
-      if (err)
-        {
-          cJSON_Delete (sig_obj);
-          sig_obj = NULL;
-          goto leave;
-        }
+  xjson_AddItemToObject (result, "summary", sigsum_to_json (sig->summary));
+
+  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);
 
-      cJSON_AddItemToArray (response, sig_obj);
+  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);
+
+  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);
     }
 
-  if (!cJSON_AddItemToObject (result, "signatures", response))
+  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 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)
     {
-      err = gpg_error_from_syserror ();
-      cJSON_Delete (response);
-      response = NULL;
-      return err;
+      cjson_t array = xjson_CreateArray ();
+      gpgme_signature_t sig;
+
+      for (sig = verify_result->signatures; sig; sig = sig->next)
+        cJSON_AddItemToArray (array, signature_to_json (sig));
+      xjson_AddItemToObject (result, "signatures", array);
     }
-  response = NULL;
 
-leave:
-  if (err && response)
+  return result;
+}
+
+
+/* Create a JSON object from an engine_info */
+static cjson_t
+engine_info_to_json (gpgme_engine_info_t info)
+{
+  cjson_t result = xjson_CreateObject ();
+
+  xjson_AddStringToObject0 (result, "protocol",
+                            protocol_to_string (info->protocol));
+  xjson_AddStringToObject0 (result, "fname", info->file_name);
+  xjson_AddStringToObject0 (result, "version", info->version);
+  xjson_AddStringToObject0 (result, "req_version", info->req_version);
+  xjson_AddStringToObject0 (result, "homedir", info->home_dir ?
+                                                info->home_dir :
+                                                "default");
+  return result;
+}
+
+
+/* Create a JSON object from an import_status */
+static cjson_t
+import_status_to_json (gpgme_import_status_t sts)
+{
+  cjson_t result = xjson_CreateObject ();
+
+  xjson_AddStringToObject0 (result, "fingerprint", sts->fpr);
+  xjson_AddStringToObject0 (result, "error_string",
+                            gpgme_strerror (sts->result));
+
+  xjson_AddNumberToObject (result, "status", sts->status);
+
+  return result;
+}
+
+/* Create a JSON object from an import result */
+static cjson_t
+import_result_to_json (gpgme_import_result_t imp)
+{
+  cjson_t result = xjson_CreateObject ();
+
+  xjson_AddNumberToObject (result, "considered", imp->considered);
+  xjson_AddNumberToObject (result, "no_user_id", imp->no_user_id);
+  xjson_AddNumberToObject (result, "imported", imp->imported);
+  xjson_AddNumberToObject (result, "imported_rsa", imp->imported_rsa);
+  xjson_AddNumberToObject (result, "unchanged", imp->unchanged);
+  xjson_AddNumberToObject (result, "new_user_ids", imp->new_user_ids);
+  xjson_AddNumberToObject (result, "new_sub_keys", imp->new_sub_keys);
+  xjson_AddNumberToObject (result, "new_signatures", imp->new_signatures);
+  xjson_AddNumberToObject (result, "new_revocations", imp->new_revocations);
+  xjson_AddNumberToObject (result, "secret_read", imp->secret_read);
+  xjson_AddNumberToObject (result, "secret_imported", imp->secret_imported);
+  xjson_AddNumberToObject (result, "secret_unchanged", imp->secret_unchanged);
+  xjson_AddNumberToObject (result, "skipped_new_keys", imp->skipped_new_keys);
+  xjson_AddNumberToObject (result, "not_imported", imp->not_imported);
+  xjson_AddNumberToObject (result, "skipped_v3_keys", imp->skipped_v3_keys);
+
+
+  if (imp->imports)
     {
-      cJSON_Delete (response);
-      response = NULL;
+      cjson_t array = xjson_CreateArray ();
+      gpgme_import_status_t status;
+
+      for (status = imp->imports; status; status = status->next)
+        cJSON_AddItemToArray (array, import_status_to_json (status));
+      xjson_AddItemToObject (result, "imports", array);
     }
-  return err;
+
+  return result;
 }
 
 
-/* Add an array of signature informations under the name "name". */
-static gpg_error_t
-add_signatures_object (cjson_t result, const char *name,
-                       gpgme_verify_result_t verify_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 response = xjson_CreateObject ();
-  gpg_error_t err = 0;
+  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;
+}
 
-  err = add_signatures_to_object (response, verify_result->signatures);
 
-  if (err)
+/* 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)
     {
-      goto leave;
+      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 (!cJSON_AddItemToObject (result, name, response))
+  if (opt->no_arg_value)
     {
-      err = gpg_error_from_syserror ();
-      goto leave;
+      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);
     }
- leave:
-  if (err)
+
+  if (opt->value)
     {
-      cJSON_Delete (response);
-      response = NULL;
+      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 err;
+  return result;
 }
 
-static const char *
-protocol_to_string (gpgme_protocol_t proto)
+
+/* Create a JSON object from a gpgconf component*/
+static cjson_t
+conf_comp_to_json (gpgme_conf_comp_t cmp)
 {
-  switch (proto)
+  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)
     {
-    case GPGME_PROTOCOL_OpenPGP: return "OpenPGP";
-    case GPGME_PROTOCOL_CMS:     return "CMS";
-    case GPGME_PROTOCOL_GPGCONF: return "gpgconf";
-    case GPGME_PROTOCOL_ASSUAN:  return "assuan";
-    case GPGME_PROTOCOL_G13:     return "g13";
-    case GPGME_PROTOCOL_UISERVER:return "uiserver";
-    case GPGME_PROTOCOL_SPAWN:   return "spawn";
-    default:
-                                 return "unknown";
+      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);
     }
-}
 
-static gpg_error_t
-add_ei_to_object (cjson_t result, gpgme_engine_info_t info)
-{
-  if (!cJSON_AddStringToObject (result, "protocol",
-                                protocol_to_string (info->protocol)))
-    return gpg_error_from_syserror ();
-  if (!cJSON_AddStringToObject (result, "fname", info->file_name))
-    return gpg_error_from_syserror ();
-  if (!cJSON_AddStringToObject (result, "version", info->version))
-    return gpg_error_from_syserror ();
-  if (!cJSON_AddStringToObject (result, "req_version", info->req_version))
-    return gpg_error_from_syserror ();
-  if (!cJSON_AddStringToObject (result, "homedir",
-                                info->home_dir ?
-                                info->home_dir :
-                                "default"))
-    return gpg_error_from_syserror ();
-  return 0;
+  return result;
 }
 
+
 /* Create a gpgme_data from json string data named "name"
  * in the request. Takes the base64 option into account.
  *
@@ -896,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);
@@ -950,41 +1379,13 @@ 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);
-
-      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;
-
-      pending_data.buffer = buffer;
-      buffer = NULL;
-      pending_data.length = buflen;
-      pending_data.written = chunksize;
-      pending_data.type = type;
-      pending_data.base64 = base64;
-    }
+  if (base64)
+    err = add_base64_to_object (result, "data", buffer, buflen);
   else
-    {
-      if (base64)
-        err = add_base64_to_object (result, "data", buffer, buflen);
-      else
-        err = cjson_AddStringToObject (result, "data", buffer);
-    }
+    err = cjson_AddStringToObject (result, "data", buffer);
 
  leave:
   gpgme_free (buffer);
@@ -992,17 +1393,95 @@ make_data_object (cjson_t result, gpgme_data_t data, size_t chunksize,
 }
 
 
-\f
-static const char hlp_encrypt[] =
-  "op:     \"encrypt\"\n"
-  "keys:   Array of strings with the fingerprints or user-ids\n"
-  "        of the keys to encrypt the data.  For a single key\n"
-  "        a String may be used instead of an array.\n"
+/* 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)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+
+  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"
+  "        of the keys to encrypt the data.  For a single key\n"
+  "        a String may be used instead of an array.\n"
   "data:   Input data. \n"
   "\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"
@@ -1020,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;
@@ -1075,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.  */
@@ -1084,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;
 
@@ -1101,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)
     {
@@ -1114,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);
@@ -1134,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"
@@ -1144,15 +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 optional object with extra information.\n"
-  "more:   Optional boolean indicating that \"getmore\" is required.";
+  "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"
+  "   notations: Notation data and policy urls (gpgme_sig_notation_t)\n"
+  "    Boolean values:\n"
+  "     human_readable\n"
+  "     critical\n"
+  "    String values:\n"
+  "     name\n"
+  "     value\n"
+  "    Number values:\n"
+  "     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;
@@ -1161,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;
@@ -1196,17 +1758,11 @@ op_decrypt (cjson_t request, cjson_t result)
   verify_result = gpgme_op_verify_result (ctx);
   if (verify_result && verify_result->signatures)
     {
-      err = add_signatures_object (result, "info", verify_result);
-    }
-
-  if (err)
-    {
-      gpg_error_object (result, err, "Info output failed: %s",
-                        gpg_strerror (err));
-      goto leave;
+      xjson_AddItemToObject (result, "info",
+                             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)
@@ -1233,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"
@@ -1249,16 +1804,14 @@ 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 *keystring = NULL;
+  char **patterns = NULL;
   gpgme_data_t input = NULL;
   gpgme_data_t output = NULL;
   int abool;
@@ -1270,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;
@@ -1296,42 +1847,647 @@ op_sign (cjson_t request, cjson_t result)
       gpgme_set_sender (ctx, j_tmp->valuestring);
     }
 
+  patterns = create_keylist_patterns (request, "keys");
+  if (!patterns)
+    {
+      gpg_error_object (result, err, "Error getting keys: %s",
+                        gpg_strerror (gpg_error (GPG_ERR_NO_KEY)));
+      goto leave;
+    }
+
+  /* Do a keylisting and add the keys */
+  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 **) 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;
+    }
+
+  if ((err = get_string_data (request, result, "data", &input)))
+    goto leave;
+
+  /* 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;
+    }
+
+  /* Sign. */
+  err = gpgme_op_sign (ctx, input, output, mode);
+  if (err)
+    {
+      gpg_error_object (result, err, "Signing failed: %s",
+                        gpg_strerror (err));
+      goto leave;
+    }
+
+  gpgme_data_release (input);
+  input = NULL;
+
+  /* We need to base64 if armoring has not been requested.  */
+  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);
+  gpgme_data_release (input);
+  gpgme_data_release (output);
+  return err;
+}
+
+
+\f
+static const char hlp_verify[] =
+  "op:     \"verify\"\n"
+  "data:   The data to verify.\n"
+  "\n"
+  "Optional parameters:\n"
+  "protocol:      Either \"openpgp\" (default) or \"cms\".\n"
+  "signature:     A detached signature. If missing opaque is assumed.\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 verified data.  This may be base64 encoded.\n"
+  "base64: Boolean indicating whether data is base64 encoded.\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"
+  "   notations: Notation data and policy urls (gpgme_sig_notation_t)\n"
+  "    Boolean values:\n"
+  "     human_readable\n"
+  "     critical\n"
+  "    String values:\n"
+  "     name\n"
+  "     value\n"
+  "    Number values:\n"
+  "     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;
+  gpgme_data_t input = NULL;
+  gpgme_data_t signature = NULL;
+  gpgme_data_t output = NULL;
+  gpgme_verify_result_t verify_result;
+
+  if ((err = get_protocol (request, &protocol)))
+    goto leave;
+  ctx = get_context (protocol);
+
+  if ((err = get_string_data (request, result, "data", &input)))
+    goto leave;
+
+  err = get_string_data (request, result, "signature", &signature);
+  /* Signature data is optional otherwise we expect opaque or clearsigned. */
+  if (err && err != gpg_error (GPG_ERR_NO_DATA))
+    goto leave;
+
+  /* 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;
+    }
+
+  /* Verify.  */
+  if (signature)
+    {
+      err = gpgme_op_verify (ctx, signature, input, output);
+    }
+  else
+    {
+      err = gpgme_op_verify (ctx, input, 0, output);
+    }
+  if (err)
+    {
+      gpg_error_object (result, err, "Verify failed: %s", gpg_strerror (err));
+      goto leave;
+    }
+  gpgme_data_release (input);
+  input = NULL;
+  gpgme_data_release (signature);
+  signature = NULL;
+
+  verify_result = gpgme_op_verify_result (ctx);
+  if (verify_result && verify_result->signatures)
+    {
+      xjson_AddItemToObject (result, "info",
+                             verify_result_to_json (verify_result));
+    }
+
+  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:
+  release_context (ctx);
+  gpgme_data_release (input);
+  gpgme_data_release (output);
+  gpgme_data_release (signature);
+  return err;
+}
+
+
+\f
+static const char hlp_version[] =
+  "op:     \"version\"\n"
+  "\n"
+  "Response on success:\n"
+  "gpgme:  The GPGME Version.\n"
+  "info:   dump of engine info. containing:\n"
+  "        protocol: The protocol.\n"
+  "        fname:    The file name.\n"
+  "        version:  The version.\n"
+  "        req_ver:  The required version.\n"
+  "        homedir:  The homedir of the engine or \"default\".\n";
+static gpg_error_t
+op_version (cjson_t request, cjson_t result)
+{
+  gpg_error_t err = 0;
+  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);
+      return gpg_error_from_syserror ();
+    }
+
+  if ((err = gpgme_get_engine_info (&ei)))
+    {
+      cJSON_Delete (infos);
+      return err;
+    }
+
+  for (; ei; ei = ei->next)
+    cJSON_AddItemToArray (infos, engine_info_to_json (ei));
+
+  if (!cJSON_AddItemToObject (result, "info", infos))
+    {
+      err = gpg_error_from_syserror ();
+      cJSON_Delete (infos);
+      return err;
+    }
+
+  return 0;
+}
+
+
+\f
+static const char hlp_keylist[] =
+  "op:     \"keylist\"\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 lists all keys.\n"
+  "protocol:      Either \"openpgp\" (default) or \"cms\".\n"
+  "\n"
+  "Optional boolean flags (default is false):\n"
+  "secret:        List secret keys.\n"
+  "extern:        Add KEYLIST_MODE_EXTERN.\n"
+  "local:         Add KEYLIST_MODE_LOCAL. (default mode).\n"
+  "sigs:          Add KEYLIST_MODE_SIGS.\n"
+  "notations:     Add KEYLIST_MODE_SIG_NOTATIONS.\n"
+  "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"
+  "  Boolean values:\n"
+  "   revoked\n"
+  "   expired\n"
+  "   disabled\n"
+  "   invalid\n"
+  "   can_encrypt\n"
+  "   can_sign\n"
+  "   can_certify\n"
+  "   can_authenticate\n"
+  "   secret\n"
+  "   is_qualified\n"
+  "  String values:\n"
+  "   protocol\n"
+  "   issuer_serial (CMS Only)\n"
+  "   issuer_name (CMS Only)\n"
+  "   chain_id (CMS Only)\n"
+  "   owner_trust (OpenPGP only)\n"
+  "   fingerprint\n"
+  "  Number values:\n"
+  "   last_update\n"
+  "   origin\n"
+  "  Array values:\n"
+  "   subkeys\n"
+  "    Boolean values:\n"
+  "     revoked\n"
+  "     expired\n"
+  "     disabled\n"
+  "     invalid\n"
+  "     can_encrypt\n"
+  "     can_sign\n"
+  "     can_certify\n"
+  "     can_authenticate\n"
+  "     secret\n"
+  "     is_qualified\n"
+  "     is_cardkey\n"
+  "     is_de_vs\n"
+  "    String values:\n"
+  "     pubkey_algo_name\n"
+  "     pubkey_algo_string\n"
+  "     keyid\n"
+  "     card_number\n"
+  "     curve\n"
+  "     keygrip\n"
+  "    Number values:\n"
+  "     pubkey_algo\n"
+  "     length\n"
+  "     timestamp\n"
+  "     expires\n"
+  "   userids\n"
+  "    Boolean values:\n"
+  "     revoked\n"
+  "     invalid\n"
+  "    String values:\n"
+  "     validity\n"
+  "     uid\n"
+  "     name\n"
+  "     email\n"
+  "     comment\n"
+  "     address\n"
+  "    Number values:\n"
+  "     origin\n"
+  "     last_update\n"
+  "    Array values:\n"
+  "     signatures\n"
+  "      Boolean values:\n"
+  "       revoked\n"
+  "       expired\n"
+  "       invalid\n"
+  "       exportable\n"
+  "      String values:\n"
+  "       pubkey_algo_name\n"
+  "       keyid\n"
+  "       status\n"
+  "       uid\n"
+  "       name\n"
+  "       email\n"
+  "       comment\n"
+  "      Number values:\n"
+  "       pubkey_algo\n"
+  "       timestamp\n"
+  "       expires\n"
+  "       status_code\n"
+  "       sig_class\n"
+  "      Array values:\n"
+  "       notations\n"
+  "        Boolean values:\n"
+  "         human_readable\n"
+  "         critical\n"
+  "        String values:\n"
+  "         name\n"
+  "         value\n"
+  "        Number values:\n"
+  "         flags\n"
+  "     tofu\n"
+  "      String values:\n"
+  "       description\n"
+  "      Number values:\n"
+  "       validity\n"
+  "       policy\n"
+  "       signcount\n"
+  "       encrcount\n"
+  "       signfirst\n"
+  "       signlast\n"
+  "       encrfirst\n"
+  "       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;
+  char **patterns = NULL;
+  int abool;
+  gpgme_keylist_mode_t mode = 0;
+  gpgme_key_t key = NULL;
+  cjson_t keyarray = xjson_CreateArray ();
+
+  if ((err = get_protocol (request, &protocol)))
+    goto leave;
+  ctx = get_context (protocol);
+
+  /* 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;
+
+  if ((err = get_boolean_flag (request, "extern", 0, &abool)))
+    goto leave;
+  if (abool)
+    mode |= GPGME_KEYLIST_MODE_EXTERN;
+
+  if ((err = get_boolean_flag (request, "local", 0, &abool)))
+    goto leave;
+  if (abool)
+    mode |= GPGME_KEYLIST_MODE_LOCAL;
+
+  if ((err = get_boolean_flag (request, "sigs", 0, &abool)))
+    goto leave;
+  if (abool)
+    mode |= GPGME_KEYLIST_MODE_SIGS;
+
+  if ((err = get_boolean_flag (request, "notations", 0, &abool)))
+    goto leave;
+  if (abool)
+    mode |= GPGME_KEYLIST_MODE_SIG_NOTATIONS;
+
+  if ((err = get_boolean_flag (request, "tofu", 0, &abool)))
+    goto leave;
+  if (abool)
+    mode |= GPGME_KEYLIST_MODE_WITH_TOFU;
+
+  if ((err = get_boolean_flag (request, "ephemeral", 0, &abool)))
+    goto leave;
+  if (abool)
+    mode |= GPGME_KEYLIST_MODE_EPHEMERAL;
+
+  if ((err = get_boolean_flag (request, "validate", 0, &abool)))
+    goto leave;
+  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 */
+      mode = GPGME_KEYLIST_MODE_LOCAL;
+    }
+
   /* Get the keys.  */
-  err = get_keys (request, &keystring);
+  patterns = create_keylist_patterns (request, "keys");
+
+  /* Do a keylisting and add the keys */
+  gpgme_set_keylist_mode (ctx, mode);
+
+  err = gpgme_op_keylist_ext_start (ctx, (const char **) patterns,
+                                    (mode & GPGME_KEYLIST_MODE_WITH_SECRET),
+                                    0);
   if (err)
     {
-      /* Provide a custom error response.  */
-      gpg_error_object (result, err, "Error getting keys: %s",
-                        gpg_strerror (err));
+      gpg_error_object (result, err, "Error listing keys: %s",
+                        gpg_strerror (err));
+      goto leave;
+    }
+
+  while (!(err = gpgme_op_keylist_next (ctx, &key)))
+    {
+      cJSON_AddItemToArray (keyarray, key_to_json (key));
+      gpgme_key_unref (key);
+    }
+  err = 0;
+
+  if (!cJSON_AddItemToObject (result, "keys", keyarray))
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+
+ leave:
+  xfree_array (patterns);
+  if (err)
+    {
+      cJSON_Delete (keyarray);
+    }
+  return err;
+}
+
+
+\f
+static const char hlp_import[] =
+  "op:     \"import\"\n"
+  "data:   The data to import.\n"
+  "\n"
+  "Optional parameters:\n"
+  "protocol:      Either \"openpgp\" (default) or \"cms\".\n"
+  "\n"
+  "Optional boolean flags (default is false):\n"
+  "base64:        Input data is base64 encoded.\n"
+  "\n"
+  "Response on success:\n"
+  "result: The import result.\n"
+  "  Number values:\n"
+  "   considered\n"
+  "   no_user_id\n"
+  "   imported\n"
+  "   imported_rsa\n"
+  "   unchanged\n"
+  "   new_user_ids\n"
+  "   new_sub_keys\n"
+  "   new_signatures\n"
+  "   new_revocations\n"
+  "   secret_read\n"
+  "   secret_imported\n"
+  "   secret_unchanged\n"
+  "   skipped_new_keys\n"
+  "   not_imported\n"
+  "   skipped_v3_keys\n"
+  "  Array values:\n"
+  "   imports: List of keys for which an import was attempted\n"
+  "    String values:\n"
+  "     fingerprint\n"
+  "     error_string\n"
+  "    Number values:\n"
+  "     error_code\n"
+  "     status\n";
+static gpg_error_t
+op_import (cjson_t request, cjson_t result)
+{
+  gpg_error_t err;
+  gpgme_ctx_t ctx = NULL;
+  gpgme_data_t input = NULL;
+  gpgme_import_result_t import_result;
+  gpgme_protocol_t protocol;
+
+  if ((err = get_protocol (request, &protocol)))
+    goto leave;
+  ctx = get_context (protocol);
+
+  if ((err = get_string_data (request, result, "data", &input)))
+      goto leave;
+
+  /* Import.  */
+  err = gpgme_op_import (ctx, input);
+  import_result = gpgme_op_import_result (ctx);
+  if (err)
+    {
+      gpg_error_object (result, err, "Import failed: %s",
+                        gpg_strerror (err));
+      goto leave;
+    }
+  gpgme_data_release (input);
+  input = NULL;
+
+  xjson_AddItemToObject (result, "result",
+                         import_result_to_json (import_result));
+
+ leave:
+  release_context (ctx);
+  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;
     }
 
-  /* Do a keylisting and add the keys */
-  if ((err = gpgme_new (&keylist_ctx)))
+  if ((err = get_boolean_flag (request, "extern", 0, &abool)))
     goto leave;
-  gpgme_set_protocol (keylist_ctx, protocol);
-  gpgme_set_keylist_mode (keylist_ctx, GPGME_KEYLIST_MODE_LOCAL);
+  if (abool)
+    mode |= GPGME_EXPORT_MODE_EXTERN;
 
-  err = gpgme_op_keylist_start (ctx, keystring, 1);
-  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 ((err = gpgme_signers_add (ctx, key)))
-        {
-          gpg_error_object (result, err, "Error adding signer: %s",
-                            gpg_strerror (err));
-          goto leave;
-        }
-      gpgme_key_unref (key);
-    }
+  if ((err = get_boolean_flag (request, "minimal", 0, &abool)))
+    goto leave;
+  if (abool)
+    mode |= GPGME_EXPORT_MODE_MINIMAL;
 
-  if ((err = get_string_data (request, result, "data", &input)))
-      goto leave;
+  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);
@@ -1342,197 +2498,381 @@ op_sign (cjson_t request, cjson_t result)
       goto leave;
     }
 
-  /* Sign. */
-  err = gpgme_op_sign (ctx, input, output, mode);
+  err = gpgme_op_export_ext (ctx, (const char **) patterns,
+                             mode, output);
   if (err)
     {
-      gpg_error_object (result, err, "Signing failed: %s",
+      gpg_error_object (result, err, "Error exporting keys: %s",
                         gpg_strerror (err));
       goto leave;
     }
 
-  gpgme_data_release (input);
-  input = NULL;
-
   /* We need to base64 if armoring has not been requested.  */
-  err = make_data_object (result, output, chunksize,
-                          "signature", !gpgme_get_armor (ctx));
+  err = make_data_object (result, output,
+                          "keys", !gpgme_get_armor (ctx));
   output = NULL;
 
- leave:
-  xfree (keystring);
+leave:
+  xfree_array (patterns);
   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"
-  "data:   The data to verify.\n"
+
+
+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"
-  "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"
-  "base64:        Input data is base64 encoded.\n"
   "\n"
   "Response on success:\n"
-  "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"
-  "more:   Optional boolean indicating that \"getmore\" is required.";
+  "success:   Boolean true.\n";
 static gpg_error_t
-op_verify (cjson_t request, cjson_t result)
+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;
-  size_t chunksize;
-  gpgme_data_t input = NULL;
-  gpgme_data_t signature = NULL;
-  gpgme_data_t output = NULL;
-  gpgme_verify_result_t verify_result;
+  gpgme_key_t key;
+  int secret = 0;
+  cjson_t j_key = NULL;
 
   if ((err = get_protocol (request, &protocol)))
     goto leave;
   ctx = get_context (protocol);
-  if ((err = get_chunksize (request, &chunksize)))
-    goto leave;
+  keylist_ctx = get_context (protocol);
 
-  if ((err = get_string_data (request, result, "data", &input)))
+  if ((err = get_boolean_flag (request, "secret", 0, &secret)))
     goto leave;
+  if (secret)
+    {
+      err = gpg_error (GPG_ERR_FORBIDDEN);
+      goto leave;
+    }
 
-  err = get_string_data (request, result, "signature", &signature);
-  /* Signature data is optional otherwise we expect opaque or clearsigned. */
-  if (err && err != gpg_error (GPG_ERR_NO_DATA))
-    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;
+    }
 
-  /* Create an output data object.  */
-  err = gpgme_data_new (&output);
+  /* 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 creating output data object: %s",
+      gpg_error_object (result, err, "Error deleting key: %s",
                         gpg_strerror (err));
       goto leave;
     }
 
-  /* Verify.  */
-  if (signature)
+  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 = gpgme_op_verify (ctx, signature, input, output);
+      err = gpg_error (GPG_ERR_INV_VALUE);
+      goto leave;
     }
-  else
+  comp_name = j_tmp->valuestring;
+
+
+  j_tmp = cJSON_GetObjectItem (request, "option");
+  if (!j_tmp || !cjson_is_string (j_tmp))
     {
-      err = gpgme_op_verify (ctx, input, 0, output);
+      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)
     {
-      gpg_error_object (result, err, "Verify failed: %s", gpg_strerror (err));
       goto leave;
     }
-  gpgme_data_release (input);
-  input = NULL;
-  gpgme_data_release (signature);
-  signature = NULL;
 
-  verify_result = gpgme_op_verify_result (ctx);
-  if (verify_result && verify_result->signatures)
+  comp = conf;
+  for (comp = conf; comp; comp = comp->next)
     {
-      err = add_signatures_object (result, "info", verify_result);
+      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;
     }
 
-  if (err)
+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))
     {
-      gpg_error_object (result, err, "Info output failed: %s",
-                    gpg_strerror (err));
+      err = gpg_error (GPG_ERR_INV_VALUE);
       goto leave;
     }
 
-  err = make_data_object (result, output, chunksize, "plaintext", -1);
-  output = NULL;
-
+  /* Load the config */
+  err = gpgme_op_conf_load (ctx, &conf);
   if (err)
     {
-      gpg_error_object (result, err, "Plaintext output failed: %s",
-                        gpg_strerror (err));
       goto leave;
     }
 
- 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);
-  gpgme_data_release (input);
-  gpgme_data_release (output);
-  gpgme_data_release (signature);
+
   return err;
 }
+
+
 \f
-static const char hlp_version[] =
-  "op:     \"version\"\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"
+  "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"
-  "gpgme:  The GPGME Version.\n"
-  "info:   dump of engine info. containing:\n"
-  "        protocol: The protocol.\n"
-  "        fname:    The file name.\n"
-  "        version:  The version.\n"
-  "        req_ver:  The required version.\n"
-  "        homedir:  The homedir of the engine or \"default\".\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_version (cjson_t request, cjson_t result)
+op_createkey (cjson_t request, cjson_t result)
 {
-  gpg_error_t err = 0;
-  gpgme_engine_info_t ei = NULL;
-  cjson_t infos = xjson_CreateArray ();
+  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
 
-  if (!cJSON_AddStringToObject (result, "gpgme", gpgme_check_version (NULL)))
+  j_tmp = cJSON_GetObjectItem (request, "algo");
+  if (j_tmp && cjson_is_string (j_tmp))
     {
-      cJSON_Delete (infos);
-      return gpg_error_from_syserror ();
+      algo = j_tmp->valuestring;
     }
 
-  if ((err = gpgme_get_engine_info (&ei)))
+  j_tmp = cJSON_GetObjectItem (request, "userid");
+  if (!j_tmp || !cjson_is_string (j_tmp))
     {
-      cJSON_Delete (infos);
-      return err;
+      err = gpg_error (GPG_ERR_INV_VALUE);
+      goto leave;
     }
 
-  for (; ei; ei = ei->next)
+  userid = j_tmp->valuestring;
+
+  j_tmp = cJSON_GetObjectItem (request, "expires");
+  if (j_tmp)
     {
-      cjson_t obj = xjson_CreateObject ();
-      if ((err = add_ei_to_object (obj, ei)))
+      if (!cjson_is_number (j_tmp))
         {
-          cJSON_Delete (infos);
-          return err;
+          err = gpg_error (GPG_ERR_INV_VALUE);
+          goto leave;
         }
-      cJSON_AddItemToArray (infos, obj);
+      expires = j_tmp->valueint;
+
+      if (!expires)
+        flags |= GPGME_CREATE_NOEXPIRE;
     }
 
-  if (!cJSON_AddItemToObject (result, "info", infos))
+
+  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_from_syserror ();
-      cJSON_Delete (infos);
-      return err;
+      err = gpg_error (GPG_ERR_GENERAL);
+      goto leave;
     }
 
-  return 0;
+  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"
-  "Optional parameters:\n"
-  "chunksize:  Max number of bytes in the \"data\" object.\n"
-  "\n"
   "Response on success:\n"
-  "type:       Type of the pending data\n"
-  "data:       The next chunk of data\n"
-  "base64:     Boolean indicating whether data is base64 encoded\n"
-  "more:       Optional boolean requesting another \"getmore\".";
+  "response:       base64 encoded json response.\n"
+  "more:           Another getmore is required.\n"
+  "base64:         boolean if the response is base64 encoded.\n";
 static gpg_error_t
 op_getmore (cjson_t request, cjson_t result)
 {
@@ -1544,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)
@@ -1557,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)
     {
@@ -1567,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
     {
@@ -1582,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;
             }
         }
@@ -1616,11 +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"
-  "  getmore     Retrieve remaining data.\n"
-  "  help        Help overview.";
+  "  verify      Verify data.\n"
+  "  version     Get engine information.\n"
+  "  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)
 {
@@ -1657,13 +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 },
-    { "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;
@@ -1671,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;
@@ -1716,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)
@@ -1748,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;
 }
@@ -2254,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
@@ -2290,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://",