core: Add GPGME_KEYLIST_MODE_WITH_TOFU.
[gpgme.git] / src / engine-gpg.c
index 8be7600..7036ee0 100644 (file)
@@ -1,20 +1,20 @@
 /* engine-gpg.c - Gpg Engine.
    Copyright (C) 2000 Werner Koch (dd9jn)
    Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007,
-                 2009 g10 Code GmbH
+                 2009, 2010, 2012, 2013 g10 Code GmbH
+
    This file is part of GPGME.
+
    GPGME is free software; you can redistribute it and/or modify it
    under the terms of the GNU Lesser General Public License as
    published by the Free Software Foundation; either version 2.1 of
    the License, or (at your option) any later version.
-   
+
    GPGME is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.
-   
+
    You should have received a copy of the GNU Lesser General Public
    License along with this program; if not, see <http://www.gnu.org/licenses/>.
 */
 #include <string.h>
 #include <assert.h>
 #include <errno.h>
-#include <unistd.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_LOCALE_H
 #include <locale.h>
+#endif
 
 #include "gpgme.h"
 #include "util.h"
@@ -38,8 +42,8 @@
 #include "priv-io.h"
 #include "sema.h"
 #include "debug.h"
+#include "data.h"
 
-#include "status-table.h"
 #include "engine-backend.h"
 
 
@@ -75,6 +79,7 @@ typedef gpgme_error_t (*colon_preprocessor_t) (char *line, char **rline);
 struct engine_gpg
 {
   char *file_name;
+  char *version;
 
   char *lc_messages;
   char *lc_ctype;
@@ -84,7 +89,7 @@ struct engine_gpg
 
   struct
   {
-    int fd[2];  
+    int fd[2];
     int arg_loc;
     size_t bufsize;
     char *buffer;
@@ -92,13 +97,15 @@ struct engine_gpg
     int eof;
     engine_status_handler_t fnc;
     void *fnc_value;
+    gpgme_status_cb_t mon_cb;
+    void *mon_cb_value;
     void *tag;
   } status;
 
   /* This is a kludge - see the comment at colon_line_handler.  */
   struct
   {
-    int fd[2];  
+    int fd[2];
     int arg_loc;
     size_t bufsize;
     char *buffer;
@@ -110,7 +117,7 @@ struct engine_gpg
     colon_preprocessor_t preprocess_fnc;
   } colon;
 
-  char **argv;  
+  char **argv;
   struct fd_data_map_s *fd_data_map;
 
   /* stuff needed for interactive (command) mode */
@@ -122,7 +129,7 @@ struct engine_gpg
     int idx;           /* Index in fd_data_map */
     gpgme_status_code_t code;  /* last code */
     char *keyword;       /* what has been requested (malloced) */
-    engine_command_handler_t fnc; 
+    engine_command_handler_t fnc;
     void *fnc_value;
     /* The kludges never end.  This is used to couple command handlers
        with output data in edit key mode.  */
@@ -131,6 +138,7 @@ struct engine_gpg
   } cmd;
 
   struct gpgme_io_cbs io_cbs;
+  gpgme_pinentry_mode_t pinentry_mode;
 };
 
 typedef struct engine_gpg *engine_gpg_t;
@@ -171,6 +179,8 @@ close_notify_handler (int fd, void *opaque)
     }
   else if (gpg->colon.fd[1] == fd)
     gpg->colon.fd[1] = -1;
+  else if (gpg->cmd.fd == fd)
+    gpg->cmd.fd = -1;
   else if (gpg->fd_data_map)
     {
       int i;
@@ -205,7 +215,7 @@ _add_arg (engine_gpg_t gpg, const char *arg, int front, int *arg_locp)
 
   a = malloc (sizeof *a + strlen (arg));
   if (!a)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
 
   a->data = NULL;
   a->dup_to = -1;
@@ -264,7 +274,7 @@ add_data (engine_gpg_t gpg, gpgme_data_t data, int dup_to, int inbound)
 
   a = malloc (sizeof *a - 1);
   if (!a)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
   a->next = NULL;
   a->data = data;
   a->inbound = inbound;
@@ -285,19 +295,28 @@ add_data (engine_gpg_t gpg, gpgme_data_t data, int dup_to, int inbound)
   return 0;
 }
 
+
+/* Return true if the engine's version is at least VERSION.  */
+static int
+have_gpg_version (engine_gpg_t gpg, const char *version)
+{
+  return _gpgme_compare_versions (gpg->version, version);
+}
+
+
 \f
 static char *
 gpg_get_version (const char *file_name)
 {
   return _gpgme_get_program_version (file_name ? file_name
-                                    : _gpgme_get_gpg_path ());
+                                    : _gpgme_get_default_gpg_name ());
 }
 
 
 static const char *
 gpg_get_req_version (void)
 {
-  return NEED_GPG_VERSION;
+  return "1.4.0";
 }
 
 
@@ -380,6 +399,8 @@ gpg_release (void *engine)
 
   if (gpg->file_name)
     free (gpg->file_name);
+  if (gpg->version)
+    free (gpg->version);
 
   if (gpg->lc_messages)
     free (gpg->lc_messages);
@@ -390,8 +411,7 @@ gpg_release (void *engine)
     {
       struct arg_and_data_s *next = gpg->arglist->next;
 
-      if (gpg->arglist)
-       free (gpg->arglist);
+      free (gpg->arglist);
       gpg->arglist = next;
     }
 
@@ -409,7 +429,8 @@ gpg_release (void *engine)
 
 
 static gpgme_error_t
-gpg_new (void **engine, const char *file_name, const char *home_dir)
+gpg_new (void **engine, const char *file_name, const char *home_dir,
+         const char *version)
 {
   engine_gpg_t gpg;
   gpgme_error_t rc = 0;
@@ -419,14 +440,24 @@ gpg_new (void **engine, const char *file_name, const char *home_dir)
 
   gpg = calloc (1, sizeof *gpg);
   if (!gpg)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
 
   if (file_name)
     {
       gpg->file_name = strdup (file_name);
       if (!gpg->file_name)
        {
-         rc = gpg_error_from_errno (errno);
+         rc = gpg_error_from_syserror ();
+         goto leave;
+       }
+    }
+
+  if (version)
+    {
+      gpg->version = strdup (version);
+      if (!gpg->version)
+       {
+         rc = gpg_error_from_syserror ();
          goto leave;
        }
     }
@@ -447,14 +478,14 @@ gpg_new (void **engine, const char *file_name, const char *home_dir)
   gpg->status.buffer = malloc (gpg->status.bufsize);
   if (!gpg->status.buffer)
     {
-      rc = gpg_error_from_errno (errno);
+      rc = gpg_error_from_syserror ();
       goto leave;
     }
   /* In any case we need a status pipe - create it right here and
      don't handle it with our generic gpgme_data_t mechanism.  */
   if (_gpgme_io_pipe (gpg->status.fd, 1) == -1)
     {
-      rc = gpg_error_from_errno (errno);
+      rc = gpg_error_from_syserror ();
       goto leave;
     }
   if (_gpgme_io_set_close_notify (gpg->status.fd[0],
@@ -508,6 +539,8 @@ gpg_new (void **engine, const char *file_name, const char *home_dir)
        rc = add_arg (gpg, dft_display);
 
       free (dft_display);
+      if (rc)
+       goto leave;
     }
 
   if (isatty (1))
@@ -515,9 +548,10 @@ gpg_new (void **engine, const char *file_name, const char *home_dir)
       int err;
 
       err = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
-      if (err)
-       rc = gpg_error_from_errno (err);
-      else
+
+      /* Even though isatty() returns 1, ttyname_r() may fail in many
+        ways, e.g., when /dev/pts is not accessible under chroot.  */
+      if (!err)
        {
           if (*dft_ttyname)
             {
@@ -532,7 +566,7 @@ gpg_new (void **engine, const char *file_name, const char *home_dir)
              rc = _gpgme_getenv ("TERM", &dft_ttytype);
              if (rc)
                goto leave;
-              
+
               if (dft_ttytype)
                 {
                   rc = add_arg (gpg, "--ttytype");
@@ -542,9 +576,9 @@ gpg_new (void **engine, const char *file_name, const char *home_dir)
 
              free (dft_ttytype);
            }
+         if (rc)
+           goto leave;
        }
-      if (rc)
-       goto leave;
     }
 
  leave:
@@ -561,7 +595,10 @@ gpg_set_locale (void *engine, int category, const char *value)
 {
   engine_gpg_t gpg = engine;
 
-  if (category == LC_CTYPE)
+  if (0)
+    ;
+#ifdef LC_CTYPE
+  else if (category == LC_CTYPE)
     {
       if (gpg->lc_ctype)
         {
@@ -575,6 +612,7 @@ gpg_set_locale (void *engine, int category, const char *value)
            return gpg_error_from_syserror ();
        }
     }
+#endif
 #ifdef LC_MESSAGES
   else if (category == LC_MESSAGES)
     {
@@ -597,6 +635,17 @@ gpg_set_locale (void *engine, int category, const char *value)
   return 0;
 }
 
+/* This sets a status callback for monitoring status lines before they
+ * are passed to a caller set handler.  */
+static void
+gpg_set_status_cb (void *engine, gpgme_status_cb_t cb, void *cb_value)
+{
+  engine_gpg_t gpg = engine;
+
+  gpg->status.mon_cb = cb;
+  gpg->status.mon_cb_value = cb_value;
+}
+
 
 /* Note, that the status_handler is allowed to modifiy the args
    value.  */
@@ -621,14 +670,14 @@ gpg_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
   gpg->colon.readpos = 0;
   gpg->colon.buffer = malloc (gpg->colon.bufsize);
   if (!gpg->colon.buffer)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
 
-  if (_gpgme_io_pipe (gpg->colon.fd, 1) == -1) 
+  if (_gpgme_io_pipe (gpg->colon.fd, 1) == -1)
     {
-      int saved_errno = errno;
+      int saved_err = gpg_error_from_syserror ();
       free (gpg->colon.buffer);
       gpg->colon.buffer = NULL;
-      return gpg_error_from_errno (saved_errno);
+      return saved_err;
     }
   if (_gpgme_io_set_close_notify (gpg->colon.fd[0], close_notify_handler, gpg)
       || _gpgme_io_set_close_notify (gpg->colon.fd[1],
@@ -644,10 +693,10 @@ gpg_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
 static gpgme_error_t
 command_handler (void *opaque, int fd)
 {
+  struct io_cb_data *data = (struct io_cb_data *) opaque;
+  engine_gpg_t gpg = (engine_gpg_t) data->handler_value;
   gpgme_error_t err;
-  engine_gpg_t gpg = (engine_gpg_t) opaque;
   int processed = 0;
-
   assert (gpg->cmd.used);
   assert (gpg->cmd.code);
   assert (gpg->cmd.fnc);
@@ -676,7 +725,7 @@ command_handler (void *opaque, int fd)
 
 
 /* The Fnc will be called to get a value for one of the commands with
-   a key KEY.  If the Code pssed to FNC is 0, the function may release
+   a key KEY.  If the Code passed to FNC is 0, the function may release
    resources associated with the returned value from another call.  To
    match such a second call to a first call, the returned value from
    the first call is passed as keyword.  */
@@ -708,26 +757,31 @@ gpg_set_command_handler (void *engine, engine_command_handler_t fnc,
 
 
 static gpgme_error_t
-build_argv (engine_gpg_t gpg)
+build_argv (engine_gpg_t gpg, const char *pgmname)
 {
   gpgme_error_t err;
   struct arg_and_data_s *a;
   struct fd_data_map_s *fd_data_map;
-  size_t datac=0, argc=0;  
+  size_t datac=0, argc=0;
   char **argv;
   int need_special = 0;
   int use_agent = 0;
   char *p;
 
-  /* We don't want to use the agent with a malformed environment
-     variable.  This is only a very basic test but sufficient to make
-     our life in the regression tests easier. */
-  err = _gpgme_getenv ("GPG_AGENT_INFO", &p);
-  if (err)
-    return err;
-  use_agent = (p && strchr (p, ':'));
-  if (p)
-    free (p);
+  if (_gpgme_in_gpg_one_mode ())
+    {
+      /* In GnuPG-1 mode we don't want to use the agent with a
+         malformed environment variable.  This is only a very basic
+         test but sufficient to make our life in the regression tests
+         easier.  With GnuPG-2 the agent is anyway required and on
+         modern installations GPG_AGENT_INFO is optional.  */
+      err = _gpgme_getenv ("GPG_AGENT_INFO", &p);
+      if (err)
+        return err;
+      use_agent = (p && strchr (p, ':'));
+      if (p)
+        free (p);
+    }
 
   if (gpg->argv)
     {
@@ -760,29 +814,31 @@ build_argv (engine_gpg_t gpg)
     argc++;
   if (use_agent)
     argc++;
+  if (gpg->pinentry_mode)
+    argc++;
   if (!gpg->cmd.used)
     argc++;    /* --batch */
-  argc += 1;   /* --no-sk-comment */
+  argc += 1;   /* --no-sk-comments */
 
   argv = calloc (argc + 1, sizeof *argv);
   if (!argv)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
   fd_data_map = calloc (datac + 1, sizeof *fd_data_map);
   if (!fd_data_map)
     {
-      int saved_errno = errno;
+      int saved_err = gpg_error_from_syserror ();
       free_argv (argv);
-      return gpg_error_from_errno (saved_errno);
+      return saved_err;
     }
 
   argc = datac = 0;
-  argv[argc] = strdup ("gpg"); /* argv[0] */
+  argv[argc] = strdup (_gpgme_get_basename (pgmname)); /* argv[0] */
   if (!argv[argc])
     {
-      int saved_errno = errno;
+      int saved_err = gpg_error_from_syserror ();
       free (fd_data_map);
       free_argv (argv);
-      return gpg_error_from_errno (saved_errno);
+      return saved_err;
     }
   argc++;
   if (need_special)
@@ -790,10 +846,10 @@ build_argv (engine_gpg_t gpg)
       argv[argc] = strdup ("--enable-special-filenames");
       if (!argv[argc])
        {
-         int saved_errno = errno;
+          int saved_err = gpg_error_from_syserror ();
          free (fd_data_map);
          free_argv (argv);
-         return gpg_error_from_errno (saved_errno);
+         return saved_err;
         }
       argc++;
     }
@@ -802,32 +858,58 @@ build_argv (engine_gpg_t gpg)
       argv[argc] = strdup ("--use-agent");
       if (!argv[argc])
        {
-         int saved_errno = errno;
+          int saved_err = gpg_error_from_syserror ();
          free (fd_data_map);
          free_argv (argv);
-         return gpg_error_from_errno (saved_errno);
+         return saved_err;
         }
       argc++;
     }
+
+  if (gpg->pinentry_mode)
+    {
+      const char *s = NULL;
+      switch (gpg->pinentry_mode)
+        {
+        case GPGME_PINENTRY_MODE_DEFAULT: break;
+        case GPGME_PINENTRY_MODE_ASK:     s = "--pinentry-mode=ask"; break;
+        case GPGME_PINENTRY_MODE_CANCEL:  s = "--pinentry-mode=cancel"; break;
+        case GPGME_PINENTRY_MODE_ERROR:   s = "--pinentry-mode=error"; break;
+        case GPGME_PINENTRY_MODE_LOOPBACK:s = "--pinentry-mode=loopback"; break;
+        }
+      if (s)
+        {
+          argv[argc] = strdup (s);
+          if (!argv[argc])
+            {
+              int saved_err = gpg_error_from_syserror ();
+              free (fd_data_map);
+              free_argv (argv);
+              return saved_err;
+            }
+          argc++;
+        }
+    }
+
   if (!gpg->cmd.used)
     {
       argv[argc] = strdup ("--batch");
       if (!argv[argc])
        {
-         int saved_errno = errno;
+          int saved_err = gpg_error_from_syserror ();
          free (fd_data_map);
          free_argv (argv);
-         return gpg_error_from_errno (saved_errno);
+         return saved_err;
         }
       argc++;
     }
-  argv[argc] = strdup ("--no-sk-comment");
+  argv[argc] = strdup ("--no-sk-comments");
   if (!argv[argc])
     {
-      int saved_errno = errno;
+      int saved_err = gpg_error_from_syserror ();
       free (fd_data_map);
       free_argv (argv);
-      return gpg_error_from_errno (saved_errno);
+      return saved_err;
     }
   argc++;
   for (a = gpg->arglist; a; a = a->next)
@@ -841,9 +923,9 @@ build_argv (engine_gpg_t gpg)
          fd_data_map[datac].inbound = a->inbound;
 
          /* Create a pipe.  */
-         {   
+         {
            int fds[2];
-           
+
            if (_gpgme_io_pipe (fds, fd_data_map[datac].inbound ? 1 : 0)
                == -1)
              {
@@ -858,6 +940,10 @@ build_argv (engine_gpg_t gpg)
                                               close_notify_handler,
                                               gpg))
              {
+                /* We leak fd_data_map and the fds.  This is not easy
+                   to avoid and given that we reach this here only
+                   after a malloc failure for a small object, it is
+                   probably better not to do anything.  */
                return gpg_error (GPG_ERR_GENERAL);
              }
            /* If the data_type is FD, we have to do a dup2 here.  */
@@ -899,10 +985,10 @@ build_argv (engine_gpg_t gpg)
              argv[argc] = malloc (buflen);
              if (!argv[argc])
                {
-                 int saved_errno = errno;
+                  int saved_err = gpg_error_from_syserror ();
                  free (fd_data_map);
                  free_argv (argv);
-                 return gpg_error_from_errno (saved_errno);
+                 return saved_err;
                 }
 
              ptr = argv[argc];
@@ -924,10 +1010,10 @@ build_argv (engine_gpg_t gpg)
          argv[argc] = strdup (a->arg);
          if (!argv[argc])
            {
-             int saved_errno = errno;
+              int saved_err = gpg_error_from_syserror ();
              free (fd_data_map);
              free_argv (argv);
-             return gpg_error_from_errno (saved_errno);
+             return saved_err;
             }
             argc++;
         }
@@ -955,16 +1041,6 @@ add_io_cb (engine_gpg_t gpg, int fd, int dir, gpgme_io_cb_t handler, void *data,
 }
 
 
-static int
-status_cmp (const void *ap, const void *bp)
-{
-  const struct status_table_s *a = ap;
-  const struct status_table_s *b = bp;
-
-  return strcmp (a->name, b->name);
-}
-
-
 /* Handle the status output of GnuPG.  This function does read entire
    lines and passes them as C strings to the callback function (we can
    use C Strings because the status output is always UTF-8 encoded).
@@ -977,36 +1053,37 @@ read_status (engine_gpg_t gpg)
 {
   char *p;
   int nread;
-  size_t bufsize = gpg->status.bufsize; 
+  size_t bufsize = gpg->status.bufsize;
   char *buffer = gpg->status.buffer;
-  size_t readpos = gpg->status.readpos; 
+  size_t readpos = gpg->status.readpos;
+  gpgme_error_t err;
 
   assert (buffer);
   if (bufsize - readpos < 256)
-    { 
+    {
       /* Need more room for the read.  */
       bufsize += 1024;
       buffer = realloc (buffer, bufsize);
       if (!buffer)
-       return gpg_error_from_errno (errno);
+       return gpg_error_from_syserror ();
     }
 
   nread = _gpgme_io_read (gpg->status.fd[0],
                          buffer + readpos, bufsize-readpos);
   if (nread == -1)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
 
   if (!nread)
     {
+      err = 0;
       gpg->status.eof = 1;
+      if (gpg->status.mon_cb)
+        err = gpg->status.mon_cb (gpg->status.mon_cb_value,
+                                  GPGME_STATUS_EOF, "");
       if (gpg->status.fnc)
-       {
-         gpgme_error_t err;
-         err = gpg->status.fnc (gpg->status.fnc_value, GPGME_STATUS_EOF, "");
-         if (err)
-           return err;
-       }
-      return 0;
+        err = gpg->status.fnc (gpg->status.fnc_value, GPGME_STATUS_EOF, "");
+
+      return err;
     }
 
   while (nread > 0)
@@ -1022,32 +1099,38 @@ read_status (engine_gpg_t gpg)
              if (!strncmp (buffer, "[GNUPG:] ", 9)
                  && buffer[9] >= 'A' && buffer[9] <= 'Z')
                {
-                 struct status_table_s t, *r;
                  char *rest;
+                 gpgme_status_code_t r;
 
                  rest = strchr (buffer + 9, ' ');
                  if (!rest)
                    rest = p; /* Set to an empty string.  */
                  else
                    *rest++ = 0;
-                    
-                 t.name = buffer+9;
-                 /* (the status table has one extra element) */
-                 r = bsearch (&t, status_table, DIM(status_table) - 1,
-                              sizeof t, status_cmp);
-                 if (r)
+
+                 r = _gpgme_parse_status (buffer + 9);
+                  if (gpg->status.mon_cb && r != GPGME_STATUS_PROGRESS)
+                    {
+                      /* Note that we call the monitor even if we do
+                       * not know the status code (r < 0).  */
+                      err = gpg->status.mon_cb (gpg->status.mon_cb_value,
+                                                buffer + 9, rest);
+                      if (err)
+                        return err;
+                    }
+                 if (r >= 0)
                    {
                      if (gpg->cmd.used
-                         && (r->code == GPGME_STATUS_GET_BOOL
-                             || r->code == GPGME_STATUS_GET_LINE
-                             || r->code == GPGME_STATUS_GET_HIDDEN))
+                         && (r == GPGME_STATUS_GET_BOOL
+                             || r == GPGME_STATUS_GET_LINE
+                             || r == GPGME_STATUS_GET_HIDDEN))
                        {
-                         gpg->cmd.code = r->code;
+                         gpg->cmd.code = r;
                          if (gpg->cmd.keyword)
                            free (gpg->cmd.keyword);
                          gpg->cmd.keyword = strdup (rest);
                          if (!gpg->cmd.keyword)
-                           return gpg_error_from_errno (errno);
+                           return gpg_error_from_syserror ();
                          /* This should be the last thing we have
                             received and the next thing will be that
                             the command handler does its action.  */
@@ -1063,14 +1146,13 @@ read_status (engine_gpg_t gpg)
                         }
                      else if (gpg->status.fnc)
                        {
-                         gpgme_error_t err;
-                         err = gpg->status.fnc (gpg->status.fnc_value, 
-                                                r->code, rest);
+                         err = gpg->status.fnc (gpg->status.fnc_value,
+                                                r, rest);
                          if (err)
                            return err;
                         }
-                    
-                     if (r->code == GPGME_STATUS_END_STREAM)
+
+                     if (r == GPGME_STATUS_END_STREAM)
                        {
                          if (gpg->cmd.used)
                            {
@@ -1124,7 +1206,7 @@ read_status (engine_gpg_t gpg)
          else
            readpos++;
         }
-    } 
+    }
 
   /* Update the gpg object.  */
   gpg->status.bufsize = bufsize;
@@ -1137,7 +1219,8 @@ read_status (engine_gpg_t gpg)
 static gpgme_error_t
 status_handler (void *opaque, int fd)
 {
-  engine_gpg_t gpg = opaque;
+  struct io_cb_data *data = (struct io_cb_data *) opaque;
+  engine_gpg_t gpg = (engine_gpg_t) data->handler_value;
   int err;
 
   assert (fd == gpg->status.fd[0]);
@@ -1155,23 +1238,23 @@ read_colon_line (engine_gpg_t gpg)
 {
   char *p;
   int nread;
-  size_t bufsize = gpg->colon.bufsize; 
+  size_t bufsize = gpg->colon.bufsize;
   char *buffer = gpg->colon.buffer;
-  size_t readpos = gpg->colon.readpos; 
+  size_t readpos = gpg->colon.readpos;
 
   assert (buffer);
   if (bufsize - readpos < 256)
-    { 
+    {
       /* Need more room for the read.  */
       bufsize += 1024;
       buffer = realloc (buffer, bufsize);
-      if (!buffer) 
-       return gpg_error_from_errno (errno);
+      if (!buffer)
+       return gpg_error_from_syserror ();
     }
 
   nread = _gpgme_io_read (gpg->colon.fd[0], buffer+readpos, bufsize-readpos);
   if (nread == -1)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
 
   if (!nread)
     {
@@ -1207,11 +1290,27 @@ read_colon_line (engine_gpg_t gpg)
                    }
 
                  assert (gpg->colon.fnc);
-                 gpg->colon.fnc (gpg->colon.fnc_value, line ? line : buffer);
-                 if (line)
-                   free (line);
-               }
-            
+                  if (line)
+                    {
+                      char *linep = line;
+                      char *endp;
+
+                      do
+                        {
+                          endp = strchr (linep, '\n');
+                          if (endp)
+                            *endp++ = 0;
+                          gpg->colon.fnc (gpg->colon.fnc_value, linep);
+                          linep = endp;
+                        }
+                      while (linep && *linep);
+
+                      free (line);
+                    }
+                  else
+                    gpg->colon.fnc (gpg->colon.fnc_value, buffer);
+                }
+
              /* To reuse the buffer for the next line we have to
                 shift the remaining data to the buffer start and
                 restart the loop Hmmm: We can optimize this function
@@ -1227,7 +1326,7 @@ read_colon_line (engine_gpg_t gpg)
          else
            readpos++;
         }
-    } 
+    }
 
   /* Update the gpg object.  */
   gpg->colon.bufsize = bufsize;
@@ -1244,7 +1343,8 @@ read_colon_line (engine_gpg_t gpg)
 static gpgme_error_t
 colon_line_handler (void *opaque, int fd)
 {
-  engine_gpg_t gpg = opaque;
+  struct io_cb_data *data = (struct io_cb_data *) opaque;
+  engine_gpg_t gpg = (engine_gpg_t) data->handler_value;
   gpgme_error_t rc = 0;
 
   assert (fd == gpg->colon.fd[0]);
@@ -1261,17 +1361,17 @@ static gpgme_error_t
 start (engine_gpg_t gpg)
 {
   gpgme_error_t rc;
-  int saved_errno;
   int i, n;
   int status;
   struct spawn_fd_item_s *fd_list;
   pid_t pid;
+  const char *pgmname;
 
   if (!gpg)
     return gpg_error (GPG_ERR_INV_VALUE);
 
-  if (!gpg->file_name && !_gpgme_get_gpg_path ())
-    return gpg_error (GPG_ERR_INV_ENGINE);
+  if (!gpg->file_name && !_gpgme_get_default_gpg_name ())
+    return trace_gpg_error (GPG_ERR_INV_ENGINE);
 
   if (gpg->lc_ctype)
     {
@@ -1291,17 +1391,18 @@ start (engine_gpg_t gpg)
        return rc;
     }
 
-  rc = build_argv (gpg);
+  pgmname = gpg->file_name ? gpg->file_name : _gpgme_get_default_gpg_name ();
+  rc = build_argv (gpg, pgmname);
   if (rc)
     return rc;
 
   /* status_fd, colon_fd and end of list.  */
   n = 3;
-  for (i = 0; gpg->fd_data_map[i].data; i++) 
+  for (i = 0; gpg->fd_data_map[i].data; i++)
     n++;
   fd_list = calloc (n, sizeof *fd_list);
   if (! fd_list)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
 
   /* Build the fd list for the child.  */
   n = 0;
@@ -1311,7 +1412,7 @@ start (engine_gpg_t gpg)
   n++;
   if (gpg->colon.fnc)
     {
-      fd_list[n].fd = gpg->colon.fd[1]; 
+      fd_list[n].fd = gpg->colon.fd[1];
       fd_list[n].dup_to = 1;
       n++;
     }
@@ -1325,13 +1426,15 @@ start (engine_gpg_t gpg)
   fd_list[n].fd = -1;
   fd_list[n].dup_to = -1;
 
-  status = _gpgme_io_spawn (gpg->file_name ? gpg->file_name :
-                           _gpgme_get_gpg_path (), gpg->argv, fd_list, &pid);
-  saved_errno = errno;
-
-  free (fd_list);
-  if (status == -1)
-    return gpg_error_from_errno (saved_errno);
+  status = _gpgme_io_spawn (pgmname, gpg->argv,
+                            (IOSPAWN_FLAG_DETACHED |IOSPAWN_FLAG_ALLOW_SET_FG),
+                            fd_list, NULL, NULL, &pid);
+  {
+    int saved_err = gpg_error_from_syserror ();
+    free (fd_list);
+    if (status == -1)
+      return saved_err;
+  }
 
   /*_gpgme_register_term_handler ( closure, closure_value, pid );*/
 
@@ -1367,22 +1470,49 @@ start (engine_gpg_t gpg)
                          ? _gpgme_data_inbound_handler
                          : _gpgme_data_outbound_handler,
                          gpg->fd_data_map[i].data, &gpg->fd_data_map[i].tag);
-         
+
          if (rc)
            /* FIXME: kill the child */
            return rc;
        }
     }
 
-  _gpgme_allow_set_foregound_window (pid);
-
   gpg_io_event (gpg, GPGME_EVENT_START, NULL);
-  
+
   /* fixme: check what data we can release here */
   return 0;
 }
 
 
+/* Add the --input-size-hint option if requested.  */
+static gpgme_error_t
+add_input_size_hint (engine_gpg_t gpg, gpgme_data_t data)
+{
+  gpgme_error_t err;
+  gpgme_off_t value = _gpgme_data_get_size_hint (data);
+  char numbuf[50];  /* Large enough for even 2^128 in base-10.  */
+  char *p;
+
+  if (!value || !have_gpg_version (gpg, "2.1.15"))
+    return 0;
+
+  err = add_arg (gpg, "--input-size-hint");
+  if (!err)
+    {
+      p = numbuf + sizeof numbuf;
+      *--p = 0;
+      do
+        {
+          *--p = '0' + (value % 10);
+          value /= 10;
+        }
+      while (value);
+      err = add_arg (gpg, p);
+    }
+  return err;
+}
+
+
 static gpgme_error_t
 gpg_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
 {
@@ -1399,12 +1529,14 @@ gpg_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
   if (!err)
     err = add_data (gpg, plain, 1, 1);
   if (!err)
+    err = add_input_size_hint (gpg, ciph);
+  if (!err)
     err = add_arg (gpg, "--");
   if (!err)
     err = add_data (gpg, ciph, -1, 0);
 
   if (!err)
-    start (gpg);
+    err = start (gpg);
   return err;
 }
 
@@ -1427,7 +1559,25 @@ gpg_delete (void *engine, gpgme_key_t key, int allow_secret)
     }
 
   if (!err)
-    start (gpg);
+    err = start (gpg);
+  return err;
+}
+
+
+static gpgme_error_t
+gpg_passwd (void *engine, gpgme_key_t key, unsigned int flags)
+{
+  engine_gpg_t gpg = engine;
+  gpgme_error_t err;
+
+  if (!key || !key->subkeys || !key->subkeys->fpr)
+    return gpg_error (GPG_ERR_INV_CERT_OBJ);
+
+  err = add_arg (gpg, "--passwd");
+  if (!err)
+    err = add_arg (gpg, key->subkeys->fpr);
+  if (!err)
+    err = start (gpg);
   return err;
 }
 
@@ -1479,7 +1629,7 @@ append_args_from_sig_notations (engine_gpg_t gpg, gpgme_ctx_t ctx /* FIXME */)
 
          arg = malloc (1 + notation->name_len + 1 + notation->value_len + 1);
          if (!arg)
-           err = gpg_error_from_errno (errno);
+           err = gpg_error_from_syserror ();
 
          if (!err)
            {
@@ -1515,7 +1665,7 @@ append_args_from_sig_notations (engine_gpg_t gpg, gpgme_ctx_t ctx /* FIXME */)
            {
              value = malloc (1 + notation->value_len + 1);
              if (!value)
-               err = gpg_error_from_errno (errno);
+               err = gpg_error_from_syserror ();
              else
                {
                  value[0] = '!';
@@ -1589,7 +1739,7 @@ append_args_from_recipients (engine_gpg_t gpg, gpgme_key_t recp[])
       if (err)
        break;
       i++;
-    }    
+    }
   return err;
 }
 
@@ -1599,21 +1749,34 @@ gpg_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
             gpgme_data_t plain, gpgme_data_t ciph, int use_armor)
 {
   engine_gpg_t gpg = engine;
-  gpgme_error_t err;
-  int symmetric = !recp;
+  gpgme_error_t err = 0;
+
+  if (recp)
+    err = add_arg (gpg, "--encrypt");
 
-  err = add_arg (gpg, symmetric ? "--symmetric" : "--encrypt");
+  if (!err && ((flags & GPGME_ENCRYPT_SYMMETRIC) || !recp))
+    err = add_arg (gpg, "--symmetric");
 
   if (!err && use_armor)
     err = add_arg (gpg, "--armor");
 
-  if (!symmetric)
+  if (!err && (flags & GPGME_ENCRYPT_NO_COMPRESS))
+    err = add_arg (gpg, "--compress-algo=none");
+
+  if (gpgme_data_get_encoding (plain) == GPGME_DATA_ENCODING_MIME
+      && have_gpg_version (gpg, "2.1.14"))
+    err = add_arg (gpg, "--mimemode");
+
+  if (recp)
     {
       /* If we know that all recipients are valid (full or ultimate trust)
         we can suppress further checks.  */
-      if (!err && !symmetric && (flags & GPGME_ENCRYPT_ALWAYS_TRUST))
+      if (!err && (flags & GPGME_ENCRYPT_ALWAYS_TRUST))
        err = add_arg (gpg, "--always-trust");
 
+      if (!err && (flags & GPGME_ENCRYPT_NO_ENCRYPT_TO))
+       err = add_arg (gpg, "--no-encrypt-to");
+
       if (!err)
        err = append_args_from_recipients (gpg, recp);
     }
@@ -1633,6 +1796,8 @@ gpg_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
        err = add_arg (gpg, gpgme_data_get_file_name (plain));
     }
   if (!err)
+    err = add_input_size_hint (gpg, plain);
+  if (!err)
     err = add_arg (gpg, "--");
   if (!err)
     err = add_data (gpg, plain, -1, 0);
@@ -1651,24 +1816,43 @@ gpg_encrypt_sign (void *engine, gpgme_key_t recp[],
                  gpgme_ctx_t ctx /* FIXME */)
 {
   engine_gpg_t gpg = engine;
-  gpgme_error_t err;
+  gpgme_error_t err = 0;
+
+  if (recp)
+    err = add_arg (gpg, "--encrypt");
+
+  if (!err && ((flags & GPGME_ENCRYPT_SYMMETRIC) || !recp))
+    err = add_arg (gpg, "--symmetric");
 
-  err = add_arg (gpg, "--encrypt");
   if (!err)
     err = add_arg (gpg, "--sign");
   if (!err && use_armor)
     err = add_arg (gpg, "--armor");
 
-  /* If we know that all recipients are valid (full or ultimate trust)
-     we can suppress further checks.  */
-  if (!err && (flags & GPGME_ENCRYPT_ALWAYS_TRUST))
-    err = add_arg (gpg, "--always-trust");
+  if (!err && (flags & GPGME_ENCRYPT_NO_COMPRESS))
+    err = add_arg (gpg, "--compress-algo=none");
 
-  if (!err)
-    err = append_args_from_recipients (gpg, recp);
+  if (gpgme_data_get_encoding (plain) == GPGME_DATA_ENCODING_MIME
+      && have_gpg_version (gpg, "2.1.14"))
+    err = add_arg (gpg, "--mimemode");
+
+  if (recp)
+    {
+      /* If we know that all recipients are valid (full or ultimate trust)
+        we can suppress further checks.  */
+      if (!err && (flags & GPGME_ENCRYPT_ALWAYS_TRUST))
+       err = add_arg (gpg, "--always-trust");
+
+      if (!err && (flags & GPGME_ENCRYPT_NO_ENCRYPT_TO))
+       err = add_arg (gpg, "--no-encrypt-to");
+
+      if (!err)
+       err = append_args_from_recipients (gpg, recp);
+    }
 
   if (!err)
     err = append_args_from_signers (gpg, ctx);
+
   if (!err)
     err = append_args_from_sig_notations (gpg, ctx);
 
@@ -1687,6 +1871,8 @@ gpg_encrypt_sign (void *engine, gpgme_key_t recp[],
        err = add_arg (gpg, gpgme_data_get_file_name (plain));
     }
   if (!err)
+    err = add_input_size_hint (gpg, plain);
+  if (!err)
     err = add_arg (gpg, "--");
   if (!err)
     err = add_data (gpg, plain, -1, 0);
@@ -1699,23 +1885,52 @@ gpg_encrypt_sign (void *engine, gpgme_key_t recp[],
 
 
 static gpgme_error_t
-gpg_export (void *engine, const char *pattern, unsigned int reserved,
-           gpgme_data_t keydata, int use_armor)
+export_common (engine_gpg_t gpg, gpgme_export_mode_t mode,
+               gpgme_data_t keydata, int use_armor)
 {
-  engine_gpg_t gpg = engine;
-  gpgme_error_t err;
+  gpgme_error_t err = 0;
 
-  if (reserved)
-    return gpg_error (GPG_ERR_INV_VALUE);
+  if ((mode & ~(GPGME_EXPORT_MODE_EXTERN
+                |GPGME_EXPORT_MODE_MINIMAL
+                |GPGME_EXPORT_MODE_SECRET)))
+    return gpg_error (GPG_ERR_NOT_SUPPORTED);
 
-  err = add_arg (gpg, "--export");
-  if (!err && use_armor)
-    err = add_arg (gpg, "--armor");
-  if (!err)
-    err = add_data (gpg, keydata, 1, 1);
+  if ((mode & GPGME_EXPORT_MODE_MINIMAL))
+    err = add_arg (gpg, "--export-options=export-minimal");
+
+  if (err)
+    ;
+  else if ((mode & GPGME_EXPORT_MODE_EXTERN))
+    {
+      err = add_arg (gpg, "--send-keys");
+    }
+  else
+    {
+      if ((mode & GPGME_EXPORT_MODE_SECRET))
+        err = add_arg (gpg, "--export-secret-keys");
+      else
+        err = add_arg (gpg, "--export");
+      if (!err && use_armor)
+        err = add_arg (gpg, "--armor");
+      if (!err)
+        err = add_data (gpg, keydata, 1, 1);
+    }
   if (!err)
     err = add_arg (gpg, "--");
 
+  return err;
+}
+
+
+static gpgme_error_t
+gpg_export (void *engine, const char *pattern, gpgme_export_mode_t mode,
+           gpgme_data_t keydata, int use_armor)
+{
+  engine_gpg_t gpg = engine;
+  gpgme_error_t err;
+
+  err = export_common (gpg, mode, keydata, use_armor);
+
   if (!err && pattern && *pattern)
     err = add_arg (gpg, pattern);
 
@@ -1727,22 +1942,13 @@ gpg_export (void *engine, const char *pattern, unsigned int reserved,
 
 
 static gpgme_error_t
-gpg_export_ext (void *engine, const char *pattern[], unsigned int reserved,
+gpg_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode,
                gpgme_data_t keydata, int use_armor)
 {
   engine_gpg_t gpg = engine;
   gpgme_error_t err;
 
-  if (reserved)
-    return gpg_error (GPG_ERR_INV_VALUE);
-
-  err = add_arg (gpg, "--export");
-  if (!err && use_armor)
-    err = add_arg (gpg, "--armor");
-  if (!err)
-    err = add_data (gpg, keydata, 1, 1);
-  if (!err)
-    err = add_arg (gpg, "--");
+  err = export_common (gpg, mode, keydata, use_armor);
 
   if (pattern)
     {
@@ -1788,18 +1994,178 @@ gpg_genkey (void *engine, gpgme_data_t help_data, int use_armor,
   return err;
 }
 
+/* Return the next DELIM delimited string from DATA as a C-string.
+   The caller needs to provide the address of a pointer variable which
+   he has to set to NULL before the first call.  After the last call
+   to this function, this function needs to be called once more with
+   DATA set to NULL so that the function can release its internal
+   state.  After that the pointer variable is free for use again.
+   Note that we use a delimiter and thus a trailing delimiter is not
+   required.  DELIM may not be changed after the first call. */
+static const char *
+string_from_data (gpgme_data_t data, int delim,
+                  void **helpptr, gpgme_error_t *r_err)
+{
+#define MYBUFLEN 2000 /* Fixme: We don't support URLs longer than that.  */
+  struct {
+    int  eof_seen;
+    int  nbytes;      /* Length of the last returned string including
+                         the delimiter. */
+    int  buflen;      /* Valid length of BUF.  */
+    char buf[MYBUFLEN+1];  /* Buffer with one byte extra space.  */
+  } *self;
+  char *p;
+  int nread;
+
+  *r_err = 0;
+  if (!data)
+    {
+      if (*helpptr)
+        {
+          free (*helpptr);
+          *helpptr = NULL;
+        }
+      return NULL;
+    }
+
+  if (*helpptr)
+    self = *helpptr;
+  else
+    {
+      self = malloc (sizeof *self);
+      if (!self)
+        {
+          *r_err = gpg_error_from_syserror ();
+          return NULL;
+        }
+      *helpptr = self;
+      self->eof_seen = 0;
+      self->nbytes = 0;
+      self->buflen = 0;
+    }
+
+  if (self->eof_seen)
+    return NULL;
+
+  assert (self->nbytes <= self->buflen);
+  memmove (self->buf, self->buf + self->nbytes, self->buflen - self->nbytes);
+  self->buflen -= self->nbytes;
+  self->nbytes = 0;
+
+  do
+    {
+      /* Fixme: This is fairly infective scanning because we may scan
+         the buffer several times.  */
+      p = memchr (self->buf, delim, self->buflen);
+      if (p)
+        {
+          *p = 0;
+          self->nbytes = p - self->buf + 1;
+          return self->buf;
+        }
+
+      if ( !(MYBUFLEN - self->buflen) )
+        {
+          /* Not enough space - URL too long.  */
+          *r_err = gpg_error (GPG_ERR_TOO_LARGE);
+          return NULL;
+        }
+
+      nread = gpgme_data_read (data, self->buf + self->buflen,
+                               MYBUFLEN - self->buflen);
+      if (nread < 0)
+        {
+          *r_err = gpg_error_from_syserror ();
+          return NULL;
+        }
+      self->buflen += nread;
+    }
+  while (nread);
+
+  /* EOF reached.  If we have anything in the buffer, append a Nul and
+     return it. */
+  self->eof_seen = 1;
+  if (self->buflen)
+    {
+      self->buf[self->buflen] = 0;  /* (we allocated one extra byte)  */
+      return self->buf;
+    }
+  return NULL;
+#undef MYBUFLEN
+}
+
+
 
 static gpgme_error_t
-gpg_import (void *engine, gpgme_data_t keydata)
+gpg_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray)
 {
   engine_gpg_t gpg = engine;
   gpgme_error_t err;
+  int idx;
+  gpgme_data_encoding_t dataenc;
 
-  err = add_arg (gpg, "--import");
-  if (!err)
-    err = add_arg (gpg, "--");
-  if (!err)
-    err = add_data (gpg, keydata, -1, 0);
+  if (keydata && keyarray)
+    return gpg_error (GPG_ERR_INV_VALUE); /* Only one is allowed.  */
+
+  dataenc = gpgme_data_get_encoding (keydata);
+
+  if (keyarray)
+    {
+      err = add_arg (gpg, "--recv-keys");
+      if (!err)
+        err = add_arg (gpg, "--");
+      for (idx=0; !err && keyarray[idx]; idx++)
+        {
+          if (keyarray[idx]->protocol != GPGME_PROTOCOL_OpenPGP)
+            ;
+          else if (!keyarray[idx]->subkeys)
+            ;
+          else if (keyarray[idx]->subkeys->fpr && *keyarray[idx]->subkeys->fpr)
+            err = add_arg (gpg, keyarray[idx]->subkeys->fpr);
+          else if (*keyarray[idx]->subkeys->keyid)
+            err = add_arg (gpg, keyarray[idx]->subkeys->keyid);
+        }
+    }
+  else if (dataenc == GPGME_DATA_ENCODING_URL
+           || dataenc == GPGME_DATA_ENCODING_URL0)
+    {
+      void *helpptr;
+      const char *string;
+      gpgme_error_t xerr;
+      int delim = (dataenc == GPGME_DATA_ENCODING_URL)? '\n': 0;
+
+      /* FIXME: --fetch-keys is probably not correct because it can't
+         grok all kinds of URLs.  On Unix it should just work but on
+         Windows we will build the command line and that may fail for
+         some embedded control characters.  It is anyway limited to
+         the maximum size of the command line.  We need another
+         command which can take its input from a file.  Maybe we
+         should use an option to gpg to modify such commands (ala
+         --multifile).  */
+      err = add_arg (gpg, "--fetch-keys");
+      if (!err)
+        err = add_arg (gpg, "--");
+      helpptr = NULL;
+      while (!err
+             && (string = string_from_data (keydata, delim, &helpptr, &xerr)))
+        err = add_arg (gpg, string);
+      if (!err)
+        err = xerr;
+      string_from_data (NULL, delim, &helpptr, &xerr);
+    }
+  else if (dataenc == GPGME_DATA_ENCODING_URLESC)
+    {
+      /* Already escaped URLs are not yet supported.  */
+      err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+    }
+  else
+    {
+      err = add_arg (gpg, "--import");
+      if (!err)
+        err = add_arg (gpg, "--");
+      if (!err)
+        err = add_data (gpg, keydata, -1, 0);
+    }
 
   if (!err)
     err = start (gpg);
@@ -1822,6 +2188,7 @@ gpg_keylist_preprocess (char *line, char **r_line)
 #define NR_FIELDS 16
   char *field[NR_FIELDS];
   int fields = 0;
+  size_t n;
 
   *r_line = NULL;
 
@@ -1839,7 +2206,7 @@ gpg_keylist_preprocess (char *line, char **r_line)
     rectype = RT_PUB;
   else if (!strcmp (field[0], "uid"))
     rectype = RT_UID;
-  else 
+  else
     rectype = RT_NONE;
 
   switch (rectype)
@@ -1857,16 +2224,34 @@ gpg_keylist_preprocess (char *line, char **r_line)
         pub:<keyid>:<algo>:<keylen>:<creationdate>:<expirationdate>:<flags>
 
         as defined in 5.2. Machine Readable Indexes of the OpenPGP
-        HTTP Keyserver Protocol (draft). 
+        HTTP Keyserver Protocol (draft).  Modern versions of the SKS
+        keyserver return the fingerprint instead of the keyid.  We
+        detect this here and use the v4 fingerprint format to convert
+        it to a key id.
 
         We want:
         pub:o<flags>:<keylen>:<algo>:<keyid>:<creatdate>:<expdate>::::::::
       */
 
-      if (asprintf (r_line, "pub:o%s:%s:%s:%s:%s:%s::::::::",
-                   field[6], field[3], field[2], field[1],
-                   field[4], field[5]) < 0)
-       return gpg_error_from_errno (errno);
+      n = strlen (field[1]);
+      if (n > 16)
+        {
+          if (asprintf (r_line,
+                        "pub:o%s:%s:%s:%s:%s:%s::::::::\n"
+                        "fpr:::::::::%s:",
+                        field[6], field[3], field[2], field[1] + n - 16,
+                        field[4], field[5], field[1]) < 0)
+            return gpg_error_from_syserror ();
+        }
+      else
+        {
+          if (asprintf (r_line,
+                        "pub:o%s:%s:%s:%s:%s:%s::::::::",
+                        field[6], field[3], field[2], field[1],
+                        field[4], field[5]) < 0)
+            return gpg_error_from_syserror ();
+        }
+
       return 0;
 
     case RT_UID:
@@ -1875,7 +2260,7 @@ gpg_keylist_preprocess (char *line, char **r_line)
          uid:<escaped uid string>:<creationdate>:<expirationdate>:<flags>
 
         as defined in 5.2. Machine Readable Indexes of the OpenPGP
-        HTTP Keyserver Protocol (draft). 
+        HTTP Keyserver Protocol (draft).
 
         We want:
         uid:o<flags>::::<creatdate>:<expdate>:::<c-coded uid>:
@@ -1891,7 +2276,7 @@ gpg_keylist_preprocess (char *line, char **r_line)
        char *dst;
 
        if (! uid)
-         return gpg_error_from_errno (errno);
+         return gpg_error_from_syserror ();
        src = field[1];
        dst = uid;
        while (*src)
@@ -1911,6 +2296,7 @@ gpg_keylist_preprocess (char *line, char **r_line)
               {
                 *dst++ = '\\';
                 *dst++ = '\\';
+                src++;
               }
            else
              *(dst++) = *(src++);
@@ -1919,7 +2305,7 @@ gpg_keylist_preprocess (char *line, char **r_line)
 
        if (asprintf (r_line, "uid:o%s::::%s:%s:::%s:",
                      field[4], field[2], field[3], uid) < 0)
-         return gpg_error_from_errno (errno);
+         return gpg_error_from_syserror ();
       }
       return 0;
 
@@ -1939,12 +2325,26 @@ gpg_keylist_build_options (engine_gpg_t gpg, int secret_only,
   gpg_error_t err;
 
   err = add_arg (gpg, "--with-colons");
-  if (!err)
-    err = add_arg (gpg, "--fixed-list-mode");
-  if (!err)
-    err = add_arg (gpg, "--with-fingerprint");
-  if (!err)
-    err = add_arg (gpg, "--with-fingerprint");
+
+  /* Since gpg 2.1.15 fingerprints are always printed, thus there is
+   * no more need to explictly request them.  */
+  if (!have_gpg_version (gpg, "2.1.15"))
+    {
+      if (!err)
+        err = add_arg (gpg, "--fixed-list-mode");
+      if (!err)
+        err = add_arg (gpg, "--with-fingerprint");
+      if (!err)
+        err = add_arg (gpg, "--with-fingerprint");
+    }
+
+  if (!err && (mode & GPGME_KEYLIST_MODE_WITH_TOFU)
+      && have_gpg_version (gpg, "2.1.16"))
+    err = add_arg (gpg, "--with-tofu-info");
+
+  if (!err && (mode & GPGME_KEYLIST_MODE_WITH_SECRET))
+    err = add_arg (gpg, "--with-secret");
+
   if (!err
       && (mode & GPGME_KEYLIST_MODE_SIGS)
       && (mode & GPGME_KEYLIST_MODE_SIG_NOTATIONS))
@@ -1953,6 +2353,7 @@ gpg_keylist_build_options (engine_gpg_t gpg, int secret_only,
       if (!err)
        err = add_arg (gpg, "show-sig-subpackets=\"20,26\"");
     }
+
   if (!err)
     {
       if ( (mode & GPGME_KEYLIST_MODE_EXTERN) )
@@ -1965,8 +2366,8 @@ gpg_keylist_build_options (engine_gpg_t gpg, int secret_only,
                  gpg >= 2.0.10.  FIXME: We should check that we have
                  such a version to that we can return a proper error
                  code.  The problem is that we don't know the context
-                 here and thus can't accesses the cached version
-                 number for the engine info structure.  */
+                 here and thus can't access the cached version number
+                 for the engine info structure.  */
               err = add_arg (gpg, "--locate-keys");
               if ((mode & GPGME_KEYLIST_MODE_SIGS))
                 err = add_arg (gpg, "--with-sig-check");
@@ -1984,16 +2385,17 @@ gpg_keylist_build_options (engine_gpg_t gpg, int secret_only,
                             ? "--check-sigs" : "--list-keys"));
         }
     }
+
   if (!err)
     err = add_arg (gpg, "--");
-  
+
   return err;
 }
-                           
+
 
 static gpgme_error_t
 gpg_keylist (void *engine, const char *pattern, int secret_only,
-            gpgme_keylist_mode_t mode)
+            gpgme_keylist_mode_t mode, int engine_flags)
 {
   engine_gpg_t gpg = engine;
   gpgme_error_t err;
@@ -2012,7 +2414,7 @@ gpg_keylist (void *engine, const char *pattern, int secret_only,
 
 static gpgme_error_t
 gpg_keylist_ext (void *engine, const char *pattern[], int secret_only,
-                int reserved, gpgme_keylist_mode_t mode)
+                int reserved, gpgme_keylist_mode_t mode, int engine_flags)
 {
   engine_gpg_t gpg = engine;
   gpgme_error_t err;
@@ -2052,8 +2454,14 @@ gpg_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
        err = add_arg (gpg, "--detach");
       if (!err && use_armor)
        err = add_arg (gpg, "--armor");
-      if (!err && use_textmode)
-       err = add_arg (gpg, "--textmode");
+      if (!err)
+        {
+          if (gpgme_data_get_encoding (in) == GPGME_DATA_ENCODING_MIME
+              && have_gpg_version (gpg, "2.1.14"))
+            err = add_arg (gpg, "--mimemode");
+          else if (use_textmode)
+            err = add_arg (gpg, "--textmode");
+        }
     }
 
   if (!err)
@@ -2071,6 +2479,8 @@ gpg_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
 
   /* Tell the gpg object about the data.  */
   if (!err)
+    err = add_input_size_hint (gpg, in);
+  if (!err)
     err = add_arg (gpg, "--");
   if (!err)
     err = add_data (gpg, in, -1, 0);
@@ -2078,7 +2488,7 @@ gpg_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
     err = add_data (gpg, out, 1, 1);
 
   if (!err)
-    start (gpg);
+    err = start (gpg);
 
   return err;
 }
@@ -2092,7 +2502,7 @@ gpg_trustlist (void *engine, const char *pattern)
   err = add_arg (gpg, "--with-colons");
   if (!err)
     err = add_arg (gpg, "--list-trust-path");
-  
+
   /* Tell the gpg object about the data.  */
   if (!err)
     err = add_arg (gpg, "--");
@@ -2116,11 +2526,12 @@ gpg_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
   if (plaintext)
     {
       /* Normal or cleartext signature.  */
-
       err = add_arg (gpg, "--output");
       if (!err)
        err = add_arg (gpg, "-");
       if (!err)
+        err = add_input_size_hint (gpg, sig);
+      if (!err)
        err = add_arg (gpg, "--");
       if (!err)
        err = add_data (gpg, sig, -1, 0);
@@ -2131,6 +2542,8 @@ gpg_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
     {
       err = add_arg (gpg, "--verify");
       if (!err)
+        err = add_input_size_hint (gpg, signed_text);
+      if (!err)
        err = add_arg (gpg, "--");
       if (!err)
        err = add_data (gpg, sig, -1, 0);
@@ -2153,11 +2566,23 @@ gpg_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
   gpg->io_cbs = *io_cbs;
 }
 
+
+static gpgme_error_t
+gpg_set_pinentry_mode (void *engine, gpgme_pinentry_mode_t mode)
+{
+  engine_gpg_t gpg = engine;
+
+  gpg->pinentry_mode = mode;
+  return 0;
+}
+
+
 \f
 struct engine_ops _gpgme_engine_ops_gpg =
   {
     /* Static functions.  */
-    _gpgme_get_gpg_path,
+    _gpgme_get_default_gpg_name,
+    NULL,
     gpg_get_version,
     gpg_get_req_version,
     gpg_new,
@@ -2165,11 +2590,14 @@ struct engine_ops _gpgme_engine_ops_gpg =
     /* Member functions.  */
     gpg_release,
     NULL,                              /* reset */
+    gpg_set_status_cb,
     gpg_set_status_handler,
     gpg_set_command_handler,
     gpg_set_colon_line_handler,
     gpg_set_locale,
+    NULL,                              /* set_protocol */
     gpg_decrypt,
+    gpg_decrypt,                       /* decrypt_verify */
     gpg_delete,
     gpg_edit,
     gpg_encrypt,
@@ -2184,9 +2612,14 @@ struct engine_ops _gpgme_engine_ops_gpg =
     gpg_trustlist,
     gpg_verify,
     NULL,              /* getauditlog */
+    NULL,               /* opassuan_transact */
     NULL,              /* conf_load */
     NULL,              /* conf_save */
     gpg_set_io_cbs,
     gpg_io_event,
-    gpg_cancel
+    gpg_cancel,
+    NULL,              /* cancel_op */
+    gpg_passwd,
+    gpg_set_pinentry_mode,
+    NULL                /* opspawn */
   };