gpgme-tool: Fix segv for external key listing.
[gpgme.git] / src / engine-gpg.c
index 6d6ec47..4df0f3e 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"
@@ -39,7 +43,6 @@
 #include "sema.h"
 #include "debug.h"
 
-#include "status-table.h"
 #include "engine-backend.h"
 
 
@@ -84,7 +87,7 @@ struct engine_gpg
 
   struct
   {
-    int fd[2];  
+    int fd[2];
     int arg_loc;
     size_t bufsize;
     char *buffer;
@@ -98,7 +101,7 @@ struct engine_gpg
   /* 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 +113,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 +125,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 +134,7 @@ struct engine_gpg
   } cmd;
 
   struct gpgme_io_cbs io_cbs;
+  gpgme_pinentry_mode_t pinentry_mode;
 };
 
 typedef struct engine_gpg *engine_gpg_t;
@@ -207,7 +211,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;
@@ -266,7 +270,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;
@@ -292,14 +296,14 @@ 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";
 }
 
 
@@ -421,14 +425,14 @@ 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;
        }
     }
@@ -449,14 +453,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],
@@ -534,7 +538,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");
@@ -563,7 +567,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)
         {
@@ -577,6 +584,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)
     {
@@ -623,14 +631,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],
@@ -710,26 +718,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)
     {
@@ -762,29 +775,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 */
 
   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)
@@ -792,10 +807,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++;
     }
@@ -804,32 +819,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");
   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)
@@ -843,9 +884,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)
              {
@@ -860,6 +901,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.  */
@@ -901,10 +946,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];
@@ -926,10 +971,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++;
         }
@@ -957,16 +1002,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).
@@ -979,24 +1014,24 @@ 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;
 
   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)
     {
@@ -1024,32 +1059,29 @@ 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 (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.  */
@@ -1066,13 +1098,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)
                            {
@@ -1126,7 +1158,7 @@ read_status (engine_gpg_t gpg)
          else
            readpos++;
         }
-    } 
+    }
 
   /* Update the gpg object.  */
   gpg->status.bufsize = bufsize;
@@ -1158,23 +1190,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)
     {
@@ -1214,7 +1246,7 @@ read_colon_line (engine_gpg_t gpg)
                  if (line)
                    free (line);
                }
-            
+
              /* 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
@@ -1230,7 +1262,7 @@ read_colon_line (engine_gpg_t gpg)
          else
            readpos++;
         }
-    } 
+    }
 
   /* Update the gpg object.  */
   gpg->colon.bufsize = bufsize;
@@ -1265,17 +1297,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)
     {
@@ -1295,17 +1327,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;
@@ -1315,7 +1348,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++;
     }
@@ -1329,15 +1362,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,
-                            IOSPAWN_FLAG_ALLOW_SET_FG,
-                            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 );*/
 
@@ -1373,7 +1406,7 @@ 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;
@@ -1381,7 +1414,7 @@ start (engine_gpg_t gpg)
     }
 
   gpg_io_event (gpg, GPGME_EVENT_START, NULL);
-  
+
   /* fixme: check what data we can release here */
   return 0;
 }
@@ -1437,6 +1470,24 @@ gpg_delete (void *engine, gpgme_key_t key, int allow_secret)
 
 
 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)
+    start (gpg);
+  return err;
+}
+
+
+static gpgme_error_t
 append_args_from_signers (engine_gpg_t gpg, gpgme_ctx_t ctx /* FIXME */)
 {
   gpgme_error_t err = 0;
@@ -1483,7 +1534,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)
            {
@@ -1519,7 +1570,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] = '!';
@@ -1593,7 +1644,7 @@ append_args_from_recipients (engine_gpg_t gpg, gpgme_key_t recp[])
       if (err)
        break;
       i++;
-    }    
+    }
   return err;
 }
 
@@ -1611,6 +1662,9 @@ gpg_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
   if (!err && use_armor)
     err = add_arg (gpg, "--armor");
 
+  if (!err && (flags & GPGME_ENCRYPT_NO_COMPRESS))
+    err = add_arg (gpg, "--compress-algo=none");
+
   if (!symmetric)
     {
       /* If we know that all recipients are valid (full or ultimate trust)
@@ -1659,23 +1713,35 @@ gpg_encrypt_sign (void *engine, gpgme_key_t recp[],
 {
   engine_gpg_t gpg = engine;
   gpgme_error_t err;
+  int symmetric = !recp;
+
+  err = add_arg (gpg, symmetric ? "--symmetric" : "--encrypt");
 
-  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 (!symmetric)
+    {
+      /* 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);
 
@@ -1709,12 +1775,18 @@ static gpgme_error_t
 export_common (engine_gpg_t gpg, gpgme_export_mode_t mode,
                gpgme_data_t keydata, int use_armor)
 {
-  gpgme_error_t err;
+  gpgme_error_t err = 0;
 
-  if ((mode & ~GPGME_EXPORT_MODE_EXTERN))
+  if ((mode & ~(GPGME_EXPORT_MODE_EXTERN
+                |GPGME_EXPORT_MODE_MINIMAL)))
     return gpg_error (GPG_ERR_NOT_SUPPORTED);
 
-  if ((mode & GPGME_EXPORT_MODE_EXTERN))
+  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");
     }
@@ -1814,7 +1886,7 @@ gpg_genkey (void *engine, gpgme_data_t help_data, int use_armor,
    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, 
+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.  */
@@ -1882,7 +1954,7 @@ string_from_data (gpgme_data_t data, int delim,
           return NULL;
         }
 
-      nread = gpgme_data_read (data, self->buf + self->buflen, 
+      nread = gpgme_data_read (data, self->buf + self->buflen,
                                MYBUFLEN - self->buflen);
       if (nread < 0)
         {
@@ -2016,7 +2088,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)
@@ -2034,7 +2106,7 @@ 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).
 
         We want:
         pub:o<flags>:<keylen>:<algo>:<keyid>:<creatdate>:<expdate>::::::::
@@ -2043,7 +2115,7 @@ gpg_keylist_preprocess (char *line, char **r_line)
       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);
+       return gpg_error_from_syserror ();
       return 0;
 
     case RT_UID:
@@ -2052,7 +2124,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>:
@@ -2068,7 +2140,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)
@@ -2096,7 +2168,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;
 
@@ -2122,6 +2194,8 @@ gpg_keylist_build_options (engine_gpg_t gpg, int secret_only,
     err = add_arg (gpg, "--with-fingerprint");
   if (!err)
     err = add_arg (gpg, "--with-fingerprint");
+  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))
@@ -2163,10 +2237,10 @@ gpg_keylist_build_options (engine_gpg_t gpg, int secret_only,
     }
   if (!err)
     err = add_arg (gpg, "--");
-  
+
   return err;
 }
-                           
+
 
 static gpgme_error_t
 gpg_keylist (void *engine, const char *pattern, int secret_only,
@@ -2269,7 +2343,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, "--");
@@ -2330,12 +2404,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,
-    NULL,               
+    _gpgme_get_default_gpg_name,
+    NULL,
     gpg_get_version,
     gpg_get_req_version,
     gpg_new,
@@ -2347,7 +2432,9 @@ struct engine_ops _gpgme_engine_ops_gpg =
     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,
@@ -2368,5 +2455,8 @@ struct engine_ops _gpgme_engine_ops_gpg =
     gpg_set_io_cbs,
     gpg_io_event,
     gpg_cancel,
-    NULL               /* cancel_op */
+    NULL,              /* cancel_op */
+    gpg_passwd,
+    gpg_set_pinentry_mode,
+    NULL                /* opspawn */
   };