json: Remove the -noinstall flag used during development.
[gpgme.git] / src / engine-gpgsm.c
index 2dda733..da7e524 100644 (file)
@@ -2,19 +2,19 @@
    Copyright (C) 2000 Werner Koch (dd9jn)
    Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009,
                  2010 g10 Code GmbH
    Copyright (C) 2000 Werner Koch (dd9jn)
    Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009,
                  2010 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.
    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.
    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, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
    You should have received a copy of the GNU Lesser General Public
    License along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 
 #include <stdlib.h>
 #include <string.h>
 
 #include <stdlib.h>
 #include <string.h>
-#include <sys/types.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
 #include <assert.h>
 #include <assert.h>
-#include <unistd.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_LOCALE_H
 #include <locale.h>
 #include <locale.h>
+#endif
 #include <fcntl.h> /* FIXME */
 #include <errno.h>
 
 #include <fcntl.h> /* FIXME */
 #include <errno.h>
 
@@ -42,7 +48,6 @@
 #include "data.h"
 
 #include "assuan.h"
 #include "data.h"
 
 #include "assuan.h"
-#include "status-table.h"
 #include "debug.h"
 
 #include "engine-backend.h"
 #include "debug.h"
 
 #include "engine-backend.h"
@@ -83,6 +88,8 @@ struct engine_gpgsm
   {
     engine_status_handler_t fnc;
     void *fnc_value;
   {
     engine_status_handler_t fnc;
     void *fnc_value;
+    gpgme_status_cb_t mon_cb;
+    void *mon_cb_value;
   } status;
 
   struct
   } status;
 
   struct
@@ -96,17 +103,19 @@ struct engine_gpgsm
       int linelen;
     } attic;
     int any; /* any data line seen */
       int linelen;
     } attic;
     int any; /* any data line seen */
-  } colon; 
+  } colon;
 
   gpgme_data_t inline_data;  /* Used to collect D lines.  */
 
 
   gpgme_data_t inline_data;  /* Used to collect D lines.  */
 
+  char request_origin[10];
+
   struct gpgme_io_cbs io_cbs;
 };
 
 typedef struct engine_gpgsm *engine_gpgsm_t;
 
 
   struct gpgme_io_cbs io_cbs;
 };
 
 typedef struct engine_gpgsm *engine_gpgsm_t;
 
 
-static void gpgsm_io_event (void *engine, 
+static void gpgsm_io_event (void *engine,
                             gpgme_event_io_t type, void *type_data);
 
 
                             gpgme_event_io_t type, void *type_data);
 
 
@@ -115,14 +124,14 @@ static char *
 gpgsm_get_version (const char *file_name)
 {
   return _gpgme_get_program_version (file_name ? file_name
 gpgsm_get_version (const char *file_name)
 {
   return _gpgme_get_program_version (file_name ? file_name
-                                    : _gpgme_get_gpgsm_path ());
+                                    : _gpgme_get_default_gpgsm_name ());
 }
 
 
 static const char *
 gpgsm_get_req_version (void)
 {
 }
 
 
 static const char *
 gpgsm_get_req_version (void)
 {
-  return NEED_GPGSM_VERSION;
+  return "2.0.4";
 }
 
 \f
 }
 
 \f
@@ -178,6 +187,8 @@ close_notify_handler (int fd, void *opaque)
 static gpgme_error_t
 default_inq_cb (engine_gpgsm_t gpgsm, const char *line)
 {
 static gpgme_error_t
 default_inq_cb (engine_gpgsm_t gpgsm, const char *line)
 {
+  (void)gpgsm;
+
   if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17]))
     {
       _gpgme_allow_set_foreground_window ((pid_t)strtoul (line+17, NULL, 10));
   if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17]))
     {
       _gpgme_allow_set_foreground_window ((pid_t)strtoul (line+17, NULL, 10));
@@ -230,10 +241,12 @@ gpgsm_release (void *engine)
 
 
 static gpgme_error_t
 
 
 static gpgme_error_t
-gpgsm_new (void **engine, const char *file_name, const char *home_dir)
+gpgsm_new (void **engine, const char *file_name, const char *home_dir,
+           const char *version)
 {
   gpgme_error_t err = 0;
   engine_gpgsm_t gpgsm;
 {
   gpgme_error_t err = 0;
   engine_gpgsm_t gpgsm;
+  const char *pgmname;
   const char *argv[5];
   int argc;
 #if !USE_DESCRIPTOR_PASSING
   const char *argv[5];
   int argc;
 #if !USE_DESCRIPTOR_PASSING
@@ -242,12 +255,15 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
 #endif
   char *dft_display = NULL;
   char dft_ttyname[64];
 #endif
   char *dft_display = NULL;
   char dft_ttyname[64];
+  char *env_tty = NULL;
   char *dft_ttytype = NULL;
   char *optstr;
 
   char *dft_ttytype = NULL;
   char *optstr;
 
+  (void)version; /* Not yet used.  */
+
   gpgsm = calloc (1, sizeof *gpgsm);
   if (!gpgsm)
   gpgsm = calloc (1, sizeof *gpgsm);
   if (!gpgsm)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
 
   gpgsm->status_cb.fd = -1;
   gpgsm->status_cb.dir = 1;
 
   gpgsm->status_cb.fd = -1;
   gpgsm->status_cb.dir = 1;
@@ -288,7 +304,7 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
 #if !USE_DESCRIPTOR_PASSING
   if (_gpgme_io_pipe (fds, 0) < 0)
     {
 #if !USE_DESCRIPTOR_PASSING
   if (_gpgme_io_pipe (fds, 0) < 0)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto leave;
     }
   gpgsm->input_cb.fd = fds[1];
       goto leave;
     }
   gpgsm->input_cb.fd = fds[1];
@@ -296,7 +312,7 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
 
   if (_gpgme_io_pipe (fds, 1) < 0)
     {
 
   if (_gpgme_io_pipe (fds, 1) < 0)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto leave;
     }
   gpgsm->output_cb.fd = fds[0];
       goto leave;
     }
   gpgsm->output_cb.fd = fds[0];
@@ -304,7 +320,7 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
 
   if (_gpgme_io_pipe (fds, 0) < 0)
     {
 
   if (_gpgme_io_pipe (fds, 0) < 0)
     {
-      err = gpg_error_from_errno (errno);
+      err = gpg_error_from_syserror ();
       goto leave;
     }
   gpgsm->message_cb.fd = fds[1];
       goto leave;
     }
   gpgsm->message_cb.fd = fds[1];
@@ -316,8 +332,10 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
   child_fds[3] = -1;
 #endif
 
   child_fds[3] = -1;
 #endif
 
+  pgmname = file_name ? file_name : _gpgme_get_default_gpgsm_name ();
+
   argc = 0;
   argc = 0;
-  argv[argc++] = "gpgsm";
+  argv[argc++] = _gpgme_get_basename (pgmname);
   if (home_dir)
     {
       argv[argc++] = "--homedir";
   if (home_dir)
     {
       argv[argc++] = "--homedir";
@@ -334,13 +352,24 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
   assuan_ctx_set_system_hooks (gpgsm->assuan_ctx, &_gpgme_assuan_system_hooks);
 
 #if USE_DESCRIPTOR_PASSING
   assuan_ctx_set_system_hooks (gpgsm->assuan_ctx, &_gpgme_assuan_system_hooks);
 
 #if USE_DESCRIPTOR_PASSING
-  err = assuan_pipe_connect
-    (gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path (),
-     argv, NULL, NULL, NULL, ASSUAN_PIPE_CONNECT_FDPASSING);
+  err = assuan_pipe_connect (gpgsm->assuan_ctx, pgmname, argv,
+                             NULL, NULL, NULL, ASSUAN_PIPE_CONNECT_FDPASSING);
 #else
 #else
-  err = assuan_pipe_connect
-    (gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path (),
-     argv, child_fds, NULL, NULL, 0);
+  {
+    assuan_fd_t achild_fds[4];
+    int i;
+
+    /* For now... */
+    for (i = 0; i < 4; i++)
+      achild_fds[i] = (assuan_fd_t) child_fds[i];
+
+    err = assuan_pipe_connect (gpgsm->assuan_ctx, pgmname, argv,
+                               achild_fds, NULL, NULL, 0);
+
+    /* For now... */
+    for (i = 0; i < 4; i++)
+      child_fds[i] = (int) achild_fds[i];
+  }
 
   /* On Windows, handles are inserted in the spawned process with
      DuplicateHandle, and child_fds contains the server-local names
 
   /* On Windows, handles are inserted in the spawned process with
      DuplicateHandle, and child_fds contains the server-local names
@@ -369,41 +398,48 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
     goto leave;
   if (dft_display)
     {
     goto leave;
   if (dft_display)
     {
-      if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
+      if (gpgrt_asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
         {
          free (dft_display);
         {
          free (dft_display);
-         err = gpg_error_from_errno (errno);
+         err = gpg_error_from_syserror ();
          goto leave;
        }
       free (dft_display);
 
       err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
                             NULL, NULL, NULL);
          goto leave;
        }
       free (dft_display);
 
       err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
                             NULL, NULL, NULL);
-      free (optstr);
+      gpgrt_free (optstr);
       if (err)
        goto leave;
     }
 
       if (err)
        goto leave;
     }
 
-  if (isatty (1))
+  err = _gpgme_getenv ("GPG_TTY", &env_tty);
+  if (isatty (1) || env_tty || err)
     {
     {
-      int rc;
+      int rc = 0;
 
 
-      rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
-      if (rc)
-       {
-         err = gpg_error_from_errno (rc);
-         goto leave;
-       }
+      if (err)
+        goto leave;
+      else if (env_tty)
+        {
+          snprintf (dft_ttyname, sizeof (dft_ttyname), "%s", env_tty);
+          free (env_tty);
+        }
       else
       else
+        rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
+
+      /* Even though isatty() returns 1, ttyname_r() may fail in many
+        ways, e.g., when /dev/pts is not accessible under chroot.  */
+      if (!rc)
        {
        {
-         if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
+         if (gpgrt_asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
            {
            {
-             err = gpg_error_from_errno (errno);
+             err = gpg_error_from_syserror ();
              goto leave;
            }
          err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
                                 NULL, NULL, NULL);
              goto leave;
            }
          err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
                                 NULL, NULL, NULL);
-         free (optstr);
+         gpgrt_free (optstr);
          if (err)
            goto leave;
 
          if (err)
            goto leave;
 
@@ -412,17 +448,17 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
            goto leave;
          if (dft_ttytype)
            {
            goto leave;
          if (dft_ttytype)
            {
-             if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
+             if (gpgrt_asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype)< 0)
                {
                  free (dft_ttytype);
                {
                  free (dft_ttytype);
-                 err = gpg_error_from_errno (errno);
+                 err = gpg_error_from_syserror ();
                  goto leave;
                }
              free (dft_ttytype);
 
              err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL,
                                     NULL, NULL, NULL, NULL);
                  goto leave;
                }
              free (dft_ttytype);
 
              err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL,
                                     NULL, NULL, NULL, NULL);
-             free (optstr);
+             gpgrt_free (optstr);
              if (err)
                goto leave;
            }
              if (err)
                goto leave;
            }
@@ -487,18 +523,39 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
 }
 
 
 }
 
 
+/* Copy flags from CTX into the engine object.  */
+static void
+gpgsm_set_engine_flags (void *engine, const gpgme_ctx_t ctx)
+{
+  engine_gpgsm_t gpgsm = engine;
+
+  if (ctx->request_origin)
+    {
+      if (strlen (ctx->request_origin) + 1 > sizeof gpgsm->request_origin)
+        strcpy (gpgsm->request_origin, "xxx"); /* Too long  - force error */
+      else
+        strcpy (gpgsm->request_origin, ctx->request_origin);
+    }
+  else
+    *gpgsm->request_origin = 0;
+}
+
+
 static gpgme_error_t
 gpgsm_set_locale (void *engine, int category, const char *value)
 {
   engine_gpgsm_t gpgsm = engine;
   gpgme_error_t err;
   char *optstr;
 static gpgme_error_t
 gpgsm_set_locale (void *engine, int category, const char *value)
 {
   engine_gpgsm_t gpgsm = engine;
   gpgme_error_t err;
   char *optstr;
-  char *catstr;
+  const char *catstr;
 
   /* FIXME: If value is NULL, we need to reset the option to default.
      But we can't do this.  So we error out here.  GPGSM needs support
      for this.  */
 
   /* FIXME: If value is NULL, we need to reset the option to default.
      But we can't do this.  So we error out here.  GPGSM needs support
      for this.  */
-  if (category == LC_CTYPE)
+  if (0)
+    ;
+#ifdef LC_CTYPE
+  else if (category == LC_CTYPE)
     {
       catstr = "lc-ctype";
       if (!value && gpgsm->lc_ctype_set)
     {
       catstr = "lc-ctype";
       if (!value && gpgsm->lc_ctype_set)
@@ -506,6 +563,7 @@ gpgsm_set_locale (void *engine, int category, const char *value)
       if (value)
        gpgsm->lc_ctype_set = 1;
     }
       if (value)
        gpgsm->lc_ctype_set = 1;
     }
+#endif
 #ifdef LC_MESSAGES
   else if (category == LC_MESSAGES)
     {
 #ifdef LC_MESSAGES
   else if (category == LC_MESSAGES)
     {
@@ -520,31 +578,29 @@ gpgsm_set_locale (void *engine, int category, const char *value)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   /* FIXME: Reset value to default.  */
     return gpg_error (GPG_ERR_INV_VALUE);
 
   /* FIXME: Reset value to default.  */
-  if (!value) 
+  if (!value)
     return 0;
 
     return 0;
 
-  if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
-    err = gpg_error_from_errno (errno);
+  if (gpgrt_asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
+    err = gpg_error_from_syserror ();
   else
     {
       err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL,
                             NULL, NULL, NULL, NULL);
   else
     {
       err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL,
                             NULL, NULL, NULL, NULL);
-      free (optstr);
+      gpgrt_free (optstr);
     }
 
   return err;
 }
 
 
     }
 
   return err;
 }
 
 
-/* Forward declaration.  */
-static gpgme_status_code_t parse_status (const char *name);
-
 static gpgme_error_t
 static gpgme_error_t
-gpgsm_assuan_simple_command (assuan_context_t ctx, char *cmd,
+gpgsm_assuan_simple_command (engine_gpgsm_t gpgsm, const char *cmd,
                             engine_status_handler_t status_fnc,
                             void *status_fnc_value)
 {
                             engine_status_handler_t status_fnc,
                             void *status_fnc_value)
 {
-  gpg_error_t err;
+  assuan_context_t ctx = gpgsm->assuan_ctx;
+  gpg_error_t err, cb_err;
   char *line;
   size_t linelen;
 
   char *line;
   size_t linelen;
 
@@ -552,11 +608,12 @@ gpgsm_assuan_simple_command (assuan_context_t ctx, char *cmd,
   if (err)
     return err;
 
   if (err)
     return err;
 
+  cb_err = 0;
   do
     {
       err = assuan_read_line (ctx, &line, &linelen);
       if (err)
   do
     {
       err = assuan_read_line (ctx, &line, &linelen);
       if (err)
-       return err;
+       break;
 
       if (*line == '#' || !linelen)
        continue;
 
       if (*line == '#' || !linelen)
        continue;
@@ -564,35 +621,62 @@ gpgsm_assuan_simple_command (assuan_context_t ctx, char *cmd,
       if (linelen >= 2
          && line[0] == 'O' && line[1] == 'K'
          && (line[2] == '\0' || line[2] == ' '))
       if (linelen >= 2
          && line[0] == 'O' && line[1] == 'K'
          && (line[2] == '\0' || line[2] == ' '))
-       return 0;
+       break;
       else if (linelen >= 4
          && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
          && line[3] == ' ')
       else if (linelen >= 4
          && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
          && line[3] == ' ')
-       err = atoi (&line[4]);
+        {
+          /* We prefer a callback generated error because that one is
+             more related to gpgme and thus probably more important
+             than the error returned by the engine.  */
+          err = cb_err? cb_err : atoi (&line[4]);
+          cb_err = 0;
+        }
       else if (linelen >= 2
               && line[0] == 'S' && line[1] == ' ')
        {
       else if (linelen >= 2
               && line[0] == 'S' && line[1] == ' ')
        {
-         char *rest;
-         gpgme_status_code_t r;
+          /* After an error from a status callback we skip all further
+             status lines.  */
+          if (!cb_err)
+            {
+              char *rest;
+              gpgme_status_code_t r;
 
 
-         rest = strchr (line + 2, ' ');
-         if (!rest)
-           rest = line + linelen; /* set to an empty string */
-         else
-           *(rest++) = 0;
+              rest = strchr (line + 2, ' ');
+              if (!rest)
+                rest = line + linelen; /* set to an empty string */
+              else
+                *(rest++) = 0;
 
 
-         r = parse_status (line + 2);
+              r = _gpgme_parse_status (line + 2);
+              if (gpgsm->status.mon_cb && r != GPGME_STATUS_PROGRESS)
+                {
+                  /* Note that we call the monitor even if we do
+                   * not know the status code (r < 0).  */
+                  cb_err = gpgsm->status.mon_cb (gpgsm->status.mon_cb_value,
+                                                 line + 2, rest);
+                }
 
 
-         if (r >= 0 && status_fnc)
-           err = status_fnc (status_fnc_value, r, rest);
-         else
-           err = gpg_error (GPG_ERR_GENERAL);
+              if (r >= 0 && status_fnc && !cb_err)
+                cb_err = status_fnc (status_fnc_value, r, rest);
+            }
        }
       else
        }
       else
-       err = gpg_error (GPG_ERR_GENERAL);
+        {
+          /* Invalid line or INQUIRY.  We can't do anything else than
+             to stop.  As with ERR we prefer a status callback
+             generated error code, though.  */
+          err = cb_err ? cb_err : gpg_error (GPG_ERR_GENERAL);
+          cb_err = 0;
+        }
     }
   while (!err);
 
     }
   while (!err);
 
+  /* We only want the first error from the status handler, thus we
+   * take the one saved in CB_ERR. */
+  if (!err && cb_err)
+    err = cb_err;
+
   return err;
 }
 
   return err;
 }
 
@@ -615,6 +699,9 @@ gpgsm_clear_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type)
       _gpgme_io_close (gpgsm->message_cb.fd);
       break;
     }
       _gpgme_io_close (gpgsm->message_cb.fd);
       break;
     }
+#else
+  (void)gpgsm;
+  (void)fd_type;
 #endif
 }
 
 #endif
 }
 
@@ -624,9 +711,11 @@ gpgsm_set_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type, const char *opt)
 {
   gpg_error_t err = 0;
   char line[COMMANDLINELEN];
 {
   gpg_error_t err = 0;
   char line[COMMANDLINELEN];
-  char *which;
+  const char *which;
   iocb_data_t *iocb_data;
   iocb_data_t *iocb_data;
+#if USE_DESCRIPTOR_PASSING
   int dir;
   int dir;
+#endif
 
   switch (fd_type)
     {
 
   switch (fd_type)
     {
@@ -649,9 +738,8 @@ gpgsm_set_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type, const char *opt)
       return gpg_error (GPG_ERR_INV_VALUE);
     }
 
       return gpg_error (GPG_ERR_INV_VALUE);
     }
 
-  dir = iocb_data->dir;
-
 #if USE_DESCRIPTOR_PASSING
 #if USE_DESCRIPTOR_PASSING
+  dir = iocb_data->dir;
   /* We try to short-cut the communication by giving GPGSM direct
      access to the file descriptor, rather than using a pipe.  */
   iocb_data->server_fd = _gpgme_data_get_fd (iocb_data->data);
   /* We try to short-cut the communication by giving GPGSM direct
      access to the file descriptor, rather than using a pipe.  */
   iocb_data->server_fd = _gpgme_data_get_fd (iocb_data->data);
@@ -660,7 +748,7 @@ gpgsm_set_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type, const char *opt)
       int fds[2];
 
       if (_gpgme_io_pipe (fds, dir) < 0)
       int fds[2];
 
       if (_gpgme_io_pipe (fds, dir) < 0)
-       return gpg_error_from_errno (errno);
+       return gpg_error_from_syserror ();
 
       iocb_data->fd = dir ? fds[0] : fds[1];
       iocb_data->server_fd = dir ? fds[1] : fds[0];
 
       iocb_data->fd = dir ? fds[0] : fds[1];
       iocb_data->server_fd = dir ? fds[1] : fds[0];
@@ -686,14 +774,14 @@ gpgsm_set_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type, const char *opt)
     snprintf (line, COMMANDLINELEN, "%s FD", which);
 #else
   if (opt)
     snprintf (line, COMMANDLINELEN, "%s FD", which);
 #else
   if (opt)
-    snprintf (line, COMMANDLINELEN, "%s FD=%s %s", 
+    snprintf (line, COMMANDLINELEN, "%s FD=%s %s",
               which, iocb_data->server_fd_str, opt);
   else
               which, iocb_data->server_fd_str, opt);
   else
-    snprintf (line, COMMANDLINELEN, "%s FD=%s", 
+    snprintf (line, COMMANDLINELEN, "%s FD=%s",
               which, iocb_data->server_fd_str);
 #endif
 
               which, iocb_data->server_fd_str);
 #endif
 
-  err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
+  err = gpgsm_assuan_simple_command (gpgsm, line, NULL, NULL);
 
 #if USE_DESCRIPTOR_PASSING
  leave_set_fd:
 
 #if USE_DESCRIPTOR_PASSING
  leave_set_fd:
@@ -733,27 +821,6 @@ map_data_enc (gpgme_data_t d)
 }
 
 
 }
 
 
-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);
-}
-
-
-static gpgme_status_code_t
-parse_status (const char *name)
-{
-  struct status_table_s t, *r;
-  t.name = name;
-  r = bsearch (&t, status_table, DIM(status_table) - 1,
-              sizeof t, status_cmp);
-  return r ? r->code : -1;
-}
-
-
 static gpgme_error_t
 status_handler (void *opaque, int fd)
 {
 static gpgme_error_t
 status_handler (void *opaque, int fd)
 {
@@ -793,9 +860,14 @@ status_handler (void *opaque, int fd)
               && (line[2] == '\0' || line[2] == ' '))
        {
          if (gpgsm->status.fnc)
               && (line[2] == '\0' || line[2] == ' '))
        {
          if (gpgsm->status.fnc)
-           err = gpgsm->status.fnc (gpgsm->status.fnc_value,
-                                    GPGME_STATUS_EOF, "");
-         
+            {
+              char emptystring[1] = {0};
+              err = gpgsm->status.fnc (gpgsm->status.fnc_value,
+                                       GPGME_STATUS_EOF, emptystring);
+              if (gpg_err_code (err) == GPG_ERR_FALSE)
+                err = 0; /* Drop special error code.  */
+            }
+
          if (!err && gpgsm->colon.fnc && gpgsm->colon.any)
             {
               /* We must tell a colon function about the EOF. We do
          if (!err && gpgsm->colon.fnc && gpgsm->colon.any)
             {
               /* We must tell a colon function about the EOF. We do
@@ -832,11 +904,11 @@ status_handler (void *opaque, int fd)
            {
              char *newline = realloc (*aline, *alinelen + linelen + 1);
              if (!newline)
            {
              char *newline = realloc (*aline, *alinelen + linelen + 1);
              if (!newline)
-               err = gpg_error_from_errno (errno);
+               err = gpg_error_from_syserror ();
              else
                {
                  *aline = newline;
              else
                {
                  *aline = newline;
-                 gpgsm->colon.attic.linesize += linelen + 1;
+                 gpgsm->colon.attic.linesize = *alinelen + linelen + 1;
                }
            }
          if (!err)
                }
            }
          if (!err)
@@ -858,12 +930,12 @@ status_handler (void *opaque, int fd)
                      *dst = *src++;
                      (*alinelen)++;
                    }
                      *dst = *src++;
                      (*alinelen)++;
                    }
-                 
+
                  if (*dst == '\n')
                    {
                      /* Terminate the pending line, pass it to the colon
                         handler and reset it.  */
                  if (*dst == '\n')
                    {
                      /* Terminate the pending line, pass it to the colon
                         handler and reset it.  */
-                     
+
                      gpgsm->colon.any = 1;
                      if (*alinelen > 1 && *(dst - 1) == '\r')
                        dst--;
                      gpgsm->colon.any = 1;
                      if (*alinelen > 1 && *(dst - 1) == '\r')
                        dst--;
@@ -892,7 +964,7 @@ status_handler (void *opaque, int fd)
           char *src = line + 2;
          char *end = line + linelen;
          char *dst = src;
           char *src = line + 2;
          char *end = line + linelen;
          char *dst = src;
-          ssize_t nwritten;
+          gpgme_ssize_t nwritten;
 
           linelen = 0;
           while (src < end)
 
           linelen = 0;
           while (src < end)
@@ -906,10 +978,10 @@ status_handler (void *opaque, int fd)
                 }
               else
                 *dst++ = *src++;
                 }
               else
                 *dst++ = *src++;
-              
+
               linelen++;
             }
               linelen++;
             }
-          
+
           src = line + 2;
           while (linelen > 0)
             {
           src = line + 2;
           while (linelen > 0)
             {
@@ -917,7 +989,7 @@ status_handler (void *opaque, int fd)
               if (!nwritten || (nwritten < 0 && errno != EINTR)
                   || nwritten > linelen)
                 {
               if (!nwritten || (nwritten < 0 && errno != EINTR)
                   || nwritten > linelen)
                 {
-                  err = gpg_error_from_errno (errno);
+                  err = gpg_error_from_syserror ();
                   break;
                 }
               src += nwritten;
                   break;
                 }
               src += nwritten;
@@ -933,19 +1005,23 @@ status_handler (void *opaque, int fd)
        {
          char *rest;
          gpgme_status_code_t r;
        {
          char *rest;
          gpgme_status_code_t r;
-         
+
          rest = strchr (line + 2, ' ');
          if (!rest)
            rest = line + linelen; /* set to an empty string */
          else
            *(rest++) = 0;
 
          rest = strchr (line + 2, ' ');
          if (!rest)
            rest = line + linelen; /* set to an empty string */
          else
            *(rest++) = 0;
 
-         r = parse_status (line + 2);
+         r = _gpgme_parse_status (line + 2);
 
          if (r >= 0)
            {
              if (gpgsm->status.fnc)
 
          if (r >= 0)
            {
              if (gpgsm->status.fnc)
-               err = gpgsm->status.fnc (gpgsm->status.fnc_value, r, rest);
+                {
+                  err = gpgsm->status.fnc (gpgsm->status.fnc_value, r, rest);
+                  if (gpg_err_code (err) == GPG_ERR_FALSE)
+                    err = 0; /* Drop special error code.  */
+                }
            }
          else
            fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
            }
          else
            fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
@@ -956,7 +1032,7 @@ status_handler (void *opaque, int fd)
       else if (linelen >= 7
                && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
                && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
       else if (linelen >= 7
                && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
                && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
-               && line[6] == 'E' 
+               && line[6] == 'E'
                && (line[7] == '\0' || line[7] == ' '))
         {
           char *keyword = line+7;
                && (line[7] == '\0' || line[7] == ' '))
         {
           char *keyword = line+7;
@@ -969,7 +1045,7 @@ status_handler (void *opaque, int fd)
 
     }
   while (!err && assuan_pending_line (gpgsm->assuan_ctx));
 
     }
   while (!err && assuan_pending_line (gpgsm->assuan_ctx));
-         
+
   return err;
 }
 
   return err;
 }
 
@@ -997,16 +1073,35 @@ static gpgme_error_t
 start (engine_gpgsm_t gpgsm, const char *command)
 {
   gpgme_error_t err;
 start (engine_gpgsm_t gpgsm, const char *command)
 {
   gpgme_error_t err;
+  assuan_fd_t afdlist[5];
   int fdlist[5];
   int nfds;
   int fdlist[5];
   int nfds;
+  int i;
+
+  if (*gpgsm->request_origin)
+    {
+      char *cmd;
+
+      cmd = _gpgme_strconcat ("OPTION request-origin=",
+                              gpgsm->request_origin, NULL);
+      if (!cmd)
+        return gpg_error_from_syserror ();
+      err = gpgsm_assuan_simple_command (gpgsm, cmd, NULL, NULL);
+      free (cmd);
+      if (err && gpg_err_code (err) != GPG_ERR_UNKNOWN_OPTION)
+        return err;
+    }
 
   /* We need to know the fd used by assuan for reads.  We do this by
      using the assumption that the first returned fd from
      assuan_get_active_fds() is always this one.  */
   nfds = assuan_get_active_fds (gpgsm->assuan_ctx, 0 /* read fds */,
 
   /* We need to know the fd used by assuan for reads.  We do this by
      using the assumption that the first returned fd from
      assuan_get_active_fds() is always this one.  */
   nfds = assuan_get_active_fds (gpgsm->assuan_ctx, 0 /* read fds */,
-                                fdlist, DIM (fdlist));
+                                afdlist, DIM (afdlist));
   if (nfds < 1)
     return gpg_error (GPG_ERR_GENERAL);        /* FIXME */
   if (nfds < 1)
     return gpg_error (GPG_ERR_GENERAL);        /* FIXME */
+  /* For now... */
+  for (i = 0; i < nfds; i++)
+    fdlist[i] = (int) afdlist[i];
 
   /* We "duplicate" the file descriptor, so we can close it here (we
      can't close fdlist[0], as that is closed by libassuan, and
 
   /* We "duplicate" the file descriptor, so we can close it here (we
      can't close fdlist[0], as that is closed by libassuan, and
@@ -1058,8 +1153,7 @@ gpgsm_reset (void *engine)
      need to reset the list of signers.  Note that RESET does not
      reset OPTION commands. */
   return (gpgsm->assuan_ctx
      need to reset the list of signers.  Note that RESET does not
      reset OPTION commands. */
   return (gpgsm->assuan_ctx
-          ? gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "RESET",
-                                         NULL, NULL)
+          ? gpgsm_assuan_simple_command (gpgsm, "RESET", NULL, NULL)
           : 0);
 }
 #endif
           : 0);
 }
 #endif
@@ -1067,11 +1161,25 @@ gpgsm_reset (void *engine)
 
 
 static gpgme_error_t
 
 
 static gpgme_error_t
-gpgsm_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
+gpgsm_decrypt (void *engine,
+               gpgme_decrypt_flags_t flags,
+               gpgme_data_t ciph, gpgme_data_t plain,
+               int export_session_key, const char *override_session_key,
+               int auto_key_retrieve)
 {
   engine_gpgsm_t gpgsm = engine;
   gpgme_error_t err;
 
 {
   engine_gpgsm_t gpgsm = engine;
   gpgme_error_t err;
 
+  (void)flags;
+
+  /* gpgsm is not capable of exporting session keys right now, so we
+   * will ignore this if requested. */
+  (void)export_session_key;
+  (void)override_session_key;
+
+  /* --auto-key-retrieve is also not supported.  */
+  (void)auto_key_retrieve;
+
   if (!gpgsm)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!gpgsm)
     return gpg_error (GPG_ERR_INV_VALUE);
 
@@ -1092,7 +1200,7 @@ gpgsm_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
 
 
 static gpgme_error_t
 
 
 static gpgme_error_t
-gpgsm_delete (void *engine, gpgme_key_t key, int allow_secret)
+gpgsm_delete (void *engine, gpgme_key_t key, unsigned int flags)
 {
   engine_gpgsm_t gpgsm = engine;
   gpgme_error_t err;
 {
   engine_gpgsm_t gpgsm = engine;
   gpgme_error_t err;
@@ -1101,6 +1209,8 @@ gpgsm_delete (void *engine, gpgme_key_t key, int allow_secret)
   char *line;
   int length = 8;      /* "DELKEYS " */
 
   char *line;
   int length = 8;      /* "DELKEYS " */
 
+  (void)flags;
+
   if (!fpr)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!fpr)
     return gpg_error (GPG_ERR_INV_VALUE);
 
@@ -1115,7 +1225,7 @@ gpgsm_delete (void *engine, gpgme_key_t key, int allow_secret)
 
   line = malloc (length);
   if (!line)
 
   line = malloc (length);
   if (!line)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
 
   strcpy (line, "DELKEYS ");
   linep = &line[8];
 
   strcpy (line, "DELKEYS ");
   linep = &line[8];
@@ -1163,7 +1273,6 @@ static gpgme_error_t
 set_recipients (engine_gpgsm_t gpgsm, gpgme_key_t recp[])
 {
   gpgme_error_t err = 0;
 set_recipients (engine_gpgsm_t gpgsm, gpgme_key_t recp[])
 {
   gpgme_error_t err = 0;
-  assuan_context_t ctx = gpgsm->assuan_ctx;
   char *line;
   int linelen;
   int invalid_recipients = 0;
   char *line;
   int linelen;
   int invalid_recipients = 0;
@@ -1172,7 +1281,7 @@ set_recipients (engine_gpgsm_t gpgsm, gpgme_key_t recp[])
   linelen = 10 + 40 + 1;       /* "RECIPIENT " + guess + '\0'.  */
   line = malloc (10 + 40 + 1);
   if (!line)
   linelen = 10 + 40 + 1;       /* "RECIPIENT " + guess + '\0'.  */
   line = malloc (10 + 40 + 1);
   if (!line)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
   strcpy (line, "RECIPIENT ");
   for (i =0; !err && recp[i]; i++)
     {
   strcpy (line, "RECIPIENT ");
   for (i =0; !err && recp[i]; i++)
     {
@@ -1192,16 +1301,16 @@ set_recipients (engine_gpgsm_t gpgsm, gpgme_key_t recp[])
          char *newline = realloc (line, newlen);
          if (! newline)
            {
          char *newline = realloc (line, newlen);
          if (! newline)
            {
-             int saved_errno = errno;
+             int saved_err = gpg_error_from_syserror ();
              free (line);
              free (line);
-             return gpg_error_from_errno (saved_errno);
+             return saved_err;
            }
          line = newline;
          linelen = newlen;
        }
       strcpy (&line[10], fpr);
 
            }
          line = newline;
          linelen = newlen;
        }
       strcpy (&line[10], fpr);
 
-      err = gpgsm_assuan_simple_command (ctx, line, gpgsm->status.fnc,
+      err = gpgsm_assuan_simple_command (gpgsm, line, gpgsm->status.fnc,
                                         gpgsm->status.fnc_value);
       /* FIXME: This requires more work.  */
       if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
                                         gpgsm->status.fnc_value);
       /* FIXME: This requires more work.  */
       if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
@@ -1218,8 +1327,57 @@ set_recipients (engine_gpgsm_t gpgsm, gpgme_key_t recp[])
 }
 
 
 }
 
 
+/* Take recipients from the LF delimited STRING and send RECIPIENT
+ * commands to gpgsm.  */
+static gpgme_error_t
+set_recipients_from_string (engine_gpgsm_t gpgsm, const char *string)
+{
+  gpg_error_t err = 0;
+  char *line = NULL;
+  int no_pubkey = 0;
+  const char *s;
+  int n;
+
+  for (;;)
+    {
+      while (*string == ' ' || *string == '\t')
+        string++;
+      if (!*string)
+        break;
+
+      s = strchr (string, '\n');
+      if (s)
+        n = s - string;
+      else
+        n = strlen (string);
+      while (n && (string[n-1] == ' ' || string[n-1] == '\t'))
+        n--;
+
+      gpgrt_free (line);
+      if (gpgrt_asprintf (&line, "RECIPIENT %.*s", n, string) < 0)
+        {
+          err = gpg_error_from_syserror ();
+          break;
+        }
+      string += n + !!s;
+
+      err = gpgsm_assuan_simple_command (gpgsm, line, gpgsm->status.fnc,
+                                        gpgsm->status.fnc_value);
+
+      /* Fixme: Improve error reporting.  */
+      if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
+       no_pubkey++;
+      else if (err)
+        break;
+    }
+  gpgrt_free (line);
+  return err? err : no_pubkey? gpg_error (GPG_ERR_NO_PUBKEY) : 0;
+}
+
+
 static gpgme_error_t
 static gpgme_error_t
-gpgsm_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
+gpgsm_encrypt (void *engine, gpgme_key_t recp[], const char *recpstring,
+               gpgme_encrypt_flags_t flags,
               gpgme_data_t plain, gpgme_data_t ciph, int use_armor)
 {
   engine_gpgsm_t gpgsm = engine;
               gpgme_data_t plain, gpgme_data_t ciph, int use_armor)
 {
   engine_gpgsm_t gpgsm = engine;
@@ -1230,9 +1388,9 @@ gpgsm_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
   if (!recp)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
   if (!recp)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
-  if (flags & GPGME_ENCRYPT_NO_ENCRYPT_TO)
+  if ((flags & GPGME_ENCRYPT_NO_ENCRYPT_TO))
     {
     {
-      err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx,
+      err = gpgsm_assuan_simple_command (gpgsm,
                                         "OPTION no-encrypt-to", NULL, NULL);
       if (err)
        return err;
                                         "OPTION no-encrypt-to", NULL, NULL);
       if (err)
        return err;
@@ -1250,7 +1408,10 @@ gpgsm_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
   gpgsm->inline_data = NULL;
 
   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
   gpgsm->inline_data = NULL;
 
-  err = set_recipients (gpgsm, recp);
+  if (!recp && recpstring)
+    err = set_recipients_from_string (gpgsm, recpstring);
+  else
+    err = set_recipients (gpgsm, recp);
 
   if (!err)
     err = start (gpgsm, "ENCRYPT");
 
   if (!err)
     err = start (gpgsm, "ENCRYPT");
@@ -1269,18 +1430,24 @@ gpgsm_export (void *engine, const char *pattern, gpgme_export_mode_t mode,
 
   if (!gpgsm)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!gpgsm)
     return gpg_error (GPG_ERR_INV_VALUE);
-  
-  if (mode)
-    return gpg_error (GPG_ERR_NOT_SUPPORTED);
 
   if (!pattern)
     pattern = "";
 
 
   if (!pattern)
     pattern = "";
 
-  cmd = malloc (7 + strlen (pattern) + 1);
+  cmd = malloc (7 + 9 + 9 + strlen (pattern) + 1);
   if (!cmd)
   if (!cmd)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
+
   strcpy (cmd, "EXPORT ");
   strcpy (cmd, "EXPORT ");
-  strcpy (&cmd[7], pattern);
+  if ((mode & GPGME_EXPORT_MODE_SECRET))
+    {
+      strcat (cmd, "--secret ");
+      if ((mode & GPGME_EXPORT_MODE_RAW))
+        strcat (cmd, "--raw ");
+      else if ((mode & GPGME_EXPORT_MODE_PKCS12))
+        strcat (cmd, "--pkcs12 ");
+    }
+  strcat (cmd, pattern);
 
   gpgsm->output_cb.data = keydata;
   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
 
   gpgsm->output_cb.data = keydata;
   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
@@ -1304,16 +1471,13 @@ gpgsm_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode,
   engine_gpgsm_t gpgsm = engine;
   gpgme_error_t err = 0;
   char *line;
   engine_gpgsm_t gpgsm = engine;
   gpgme_error_t err = 0;
   char *line;
-  /* Length is "EXPORT " + p + '\0'.  */
-  int length = 7 + 1;
+  /* Length is "EXPORT " + "--secret " + "--pkcs12 " + p + '\0'.  */
+  int length = 7 + 9 + 9 + 1;
   char *linep;
 
   if (!gpgsm)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   char *linep;
 
   if (!gpgsm)
     return gpg_error (GPG_ERR_INV_VALUE);
 
-  if (mode)
-    return gpg_error (GPG_ERR_NOT_SUPPORTED);
-
   if (pattern && *pattern)
     {
       const char **pat = pattern;
   if (pattern && *pattern)
     {
       const char **pat = pattern;
@@ -1335,10 +1499,18 @@ gpgsm_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode,
     }
   line = malloc (length);
   if (!line)
     }
   line = malloc (length);
   if (!line)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
 
   strcpy (line, "EXPORT ");
 
   strcpy (line, "EXPORT ");
-  linep = &line[7];
+  if ((mode & GPGME_EXPORT_MODE_SECRET))
+    {
+      strcat (line, "--secret ");
+      if ((mode & GPGME_EXPORT_MODE_RAW))
+        strcat (line, "--raw ");
+      else if ((mode & GPGME_EXPORT_MODE_PKCS12))
+        strcat (line, "--pkcs12 ");
+    }
+  linep = &line[strlen (line)];
 
   if (pattern && *pattern)
     {
 
   if (pattern && *pattern)
     {
@@ -1394,29 +1566,51 @@ gpgsm_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode,
 
 
 static gpgme_error_t
 
 
 static gpgme_error_t
-gpgsm_genkey (void *engine, gpgme_data_t help_data, int use_armor,
+gpgsm_genkey (void *engine,
+              const char *userid, const char *algo,
+              unsigned long reserved, unsigned long expires,
+              gpgme_key_t key, unsigned int flags,
+              gpgme_data_t help_data, unsigned int extraflags,
              gpgme_data_t pubkey, gpgme_data_t seckey)
 {
   engine_gpgsm_t gpgsm = engine;
   gpgme_error_t err;
 
              gpgme_data_t pubkey, gpgme_data_t seckey)
 {
   engine_gpgsm_t gpgsm = engine;
   gpgme_error_t err;
 
-  if (!gpgsm || !pubkey || seckey)
+  (void)reserved;
+
+  if (!gpgsm)
     return gpg_error (GPG_ERR_INV_VALUE);
 
     return gpg_error (GPG_ERR_INV_VALUE);
 
-  gpgsm->input_cb.data = help_data;
-  err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
-  if (err)
-    return err;
-  gpgsm->output_cb.data = pubkey;
-  err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
-                     : map_data_enc (gpgsm->output_cb.data));
-  if (err)
-    return err;
-  gpgsm_clear_fd (gpgsm, MESSAGE_FD);
-  gpgsm->inline_data = NULL;
+  if (help_data)
+    {
+      if (!pubkey || seckey)
+        return gpg_error (GPG_ERR_INV_VALUE);
 
 
-  err = start (gpgsm, "GENKEY");
-  return err;
+      gpgsm->input_cb.data = help_data;
+      err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
+      if (err)
+        return err;
+      gpgsm->output_cb.data = pubkey;
+      err = gpgsm_set_fd (gpgsm, OUTPUT_FD,
+                          (extraflags & GENKEY_EXTRAFLAG_ARMOR)? "--armor"
+                          : map_data_enc (gpgsm->output_cb.data));
+      if (err)
+        return err;
+      gpgsm_clear_fd (gpgsm, MESSAGE_FD);
+      gpgsm->inline_data = NULL;
+
+      err = start (gpgsm, "GENKEY");
+      return err;
+    }
+
+  (void)userid;
+  (void)algo;
+  (void)expires;
+  (void)key;
+  (void)flags;
+
+  /* The new interface has not yet been implemented,  */
+  return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 }
 
 
 }
 
 
@@ -1443,9 +1637,8 @@ gpgsm_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray)
 
       /* Fist check whether the engine already features the
          --re-import option.  */
 
       /* Fist check whether the engine already features the
          --re-import option.  */
-      err = gpgsm_assuan_simple_command 
-        (gpgsm->assuan_ctx, 
-         "GETINFO cmd_has_option IMPORT re-import", NULL, NULL);
+      err = gpgsm_assuan_simple_command
+        (gpgsm, "GETINFO cmd_has_option IMPORT re-import", NULL, NULL);
       if (err)
        return gpg_error (GPG_ERR_NOT_SUPPORTED);
 
       if (err)
        return gpg_error (GPG_ERR_NOT_SUPPORTED);
 
@@ -1457,7 +1650,7 @@ gpgsm_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray)
         {
           if (keyarray[idx]->protocol == GPGME_PROTOCOL_CMS
               && keyarray[idx]->subkeys
         {
           if (keyarray[idx]->protocol == GPGME_PROTOCOL_CMS
               && keyarray[idx]->subkeys
-              && keyarray[idx]->subkeys->fpr 
+              && keyarray[idx]->subkeys->fpr
               && *keyarray[idx]->subkeys->fpr)
             buflen += strlen (keyarray[idx]->subkeys->fpr) + 1;
         }
               && *keyarray[idx]->subkeys->fpr)
             buflen += strlen (keyarray[idx]->subkeys->fpr) + 1;
         }
@@ -1470,11 +1663,11 @@ gpgsm_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray)
         {
           if (keyarray[idx]->protocol == GPGME_PROTOCOL_CMS
               && keyarray[idx]->subkeys
         {
           if (keyarray[idx]->protocol == GPGME_PROTOCOL_CMS
               && keyarray[idx]->subkeys
-              && keyarray[idx]->subkeys->fpr 
+              && keyarray[idx]->subkeys->fpr
               && *keyarray[idx]->subkeys->fpr)
             p = stpcpy (stpcpy (p, keyarray[idx]->subkeys->fpr), "\n");
         }
               && *keyarray[idx]->subkeys->fpr)
             p = stpcpy (stpcpy (p, keyarray[idx]->subkeys->fpr), "\n");
         }
-      
+
       err = gpgme_data_new_from_mem (&gpgsm->input_helper_data,
                                      buffer, buflen, 0);
       if (err)
       err = gpgme_data_new_from_mem (&gpgsm->input_helper_data,
                                      buffer, buflen, 0);
       if (err)
@@ -1523,7 +1716,7 @@ gpgsm_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray)
 
 static gpgme_error_t
 gpgsm_keylist (void *engine, const char *pattern, int secret_only,
 
 static gpgme_error_t
 gpgsm_keylist (void *engine, const char *pattern, int secret_only,
-              gpgme_keylist_mode_t mode)
+              gpgme_keylist_mode_t mode, int engine_flags)
 {
   engine_gpgsm_t gpgsm = engine;
   char *line;
 {
   engine_gpgsm_t gpgsm = engine;
   char *line;
@@ -1538,11 +1731,22 @@ gpgsm_keylist (void *engine, const char *pattern, int secret_only,
   if (!pattern)
     pattern = "";
 
   if (!pattern)
     pattern = "";
 
+  /* Hack to make sure that the agent is started.  Only if the agent
+     has been started an application may connect to the agent via
+     GPGME_PROTOCOL_ASSUAN - for example to look for smartcards.  We
+     do this only if a secret key listing has been requested.  In
+     general this is not needed because a secret key listing starts
+     the agent.  However on a fresh installation no public keys are
+     available and thus there is no need for gpgsm to ask the agent
+     whether a secret key exists for the public key.  */
+  if (secret_only || (mode & GPGME_KEYLIST_MODE_WITH_SECRET))
+    gpgsm_assuan_simple_command (gpgsm, "GETINFO agent-check", NULL, NULL);
+
   /* Always send list-mode option because RESET does not reset it.  */
   /* Always send list-mode option because RESET does not reset it.  */
-  if (asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0)
-    return gpg_error_from_errno (errno);
-  err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
-  free (line);
+  if (gpgrt_asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0)
+    return gpg_error_from_syserror ();
+  err = gpgsm_assuan_simple_command (gpgsm, line, NULL, NULL);
+  gpgrt_free (line);
   if (err)
     return err;
 
   if (err)
     return err;
 
@@ -1551,24 +1755,34 @@ gpgsm_keylist (void *engine, const char *pattern, int secret_only,
 
   /* Use the validation mode if requested.  We don't check for an error
      yet because this is a pretty fresh gpgsm features. */
 
   /* Use the validation mode if requested.  We don't check for an error
      yet because this is a pretty fresh gpgsm features. */
-  gpgsm_assuan_simple_command (gpgsm->assuan_ctx, 
+  gpgsm_assuan_simple_command (gpgsm,
                                (mode & GPGME_KEYLIST_MODE_VALIDATE)?
                                "OPTION with-validation=1":
                                "OPTION with-validation=0" ,
                                NULL, NULL);
   /* Include the ephemeral keys if requested.  We don't check for an error
      yet because this is a pretty fresh gpgsm features. */
                                (mode & GPGME_KEYLIST_MODE_VALIDATE)?
                                "OPTION with-validation=1":
                                "OPTION with-validation=0" ,
                                NULL, NULL);
   /* Include the ephemeral keys if requested.  We don't check for an error
      yet because this is a pretty fresh gpgsm features. */
-  gpgsm_assuan_simple_command (gpgsm->assuan_ctx, 
+  gpgsm_assuan_simple_command (gpgsm,
                                (mode & GPGME_KEYLIST_MODE_EPHEMERAL)?
                                "OPTION with-ephemeral-keys=1":
                                "OPTION with-ephemeral-keys=0" ,
                                NULL, NULL);
                                (mode & GPGME_KEYLIST_MODE_EPHEMERAL)?
                                "OPTION with-ephemeral-keys=1":
                                "OPTION with-ephemeral-keys=0" ,
                                NULL, NULL);
+  gpgsm_assuan_simple_command (gpgsm,
+                               (mode & GPGME_KEYLIST_MODE_WITH_SECRET)?
+                               "OPTION with-secret=1":
+                               "OPTION with-secret=0" ,
+                               NULL, NULL);
+  gpgsm_assuan_simple_command (gpgsm,
+                               (engine_flags & GPGME_ENGINE_FLAG_OFFLINE)?
+                               "OPTION offline=1":
+                               "OPTION offline=0" ,
+                               NULL, NULL);
 
 
   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
   line = malloc (15 + strlen (pattern) + 1);
   if (!line)
 
 
   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
   line = malloc (15 + strlen (pattern) + 1);
   if (!line)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
   if (secret_only)
     {
       strcpy (line, "LISTSECRETKEYS ");
   if (secret_only)
     {
       strcpy (line, "LISTSECRETKEYS ");
@@ -1593,7 +1807,7 @@ gpgsm_keylist (void *engine, const char *pattern, int secret_only,
 
 static gpgme_error_t
 gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only,
 
 static gpgme_error_t
 gpgsm_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_gpgsm_t gpgsm = engine;
   char *line;
 {
   engine_gpgsm_t gpgsm = engine;
   char *line;
@@ -1613,22 +1827,31 @@ gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only,
     list_mode |= 2;
 
   /* Always send list-mode option because RESET does not reset it.  */
     list_mode |= 2;
 
   /* Always send list-mode option because RESET does not reset it.  */
-  if (asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0)
-    return gpg_error_from_errno (errno);
-  err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
-  free (line);
+  if (gpgrt_asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0)
+    return gpg_error_from_syserror ();
+  err = gpgsm_assuan_simple_command (gpgsm, line, NULL, NULL);
+  gpgrt_free (line);
   if (err)
     return err;
 
   /* Always send key validation because RESET does not reset it.  */
   /* Use the validation mode if required.  We don't check for an error
      yet because this is a pretty fresh gpgsm features. */
   if (err)
     return err;
 
   /* Always send key validation because RESET does not reset it.  */
   /* Use the validation mode if required.  We don't check for an error
      yet because this is a pretty fresh gpgsm features. */
-  gpgsm_assuan_simple_command (gpgsm->assuan_ctx, 
+  gpgsm_assuan_simple_command (gpgsm,
                                (mode & GPGME_KEYLIST_MODE_VALIDATE)?
                                "OPTION with-validation=1":
                                "OPTION with-validation=0" ,
                                NULL, NULL);
                                (mode & GPGME_KEYLIST_MODE_VALIDATE)?
                                "OPTION with-validation=1":
                                "OPTION with-validation=0" ,
                                NULL, NULL);
-
+  gpgsm_assuan_simple_command (gpgsm,
+                               (mode & GPGME_KEYLIST_MODE_WITH_SECRET)?
+                               "OPTION with-secret=1":
+                               "OPTION with-secret=0" ,
+                               NULL, NULL);
+  gpgsm_assuan_simple_command (gpgsm,
+                               (engine_flags & GPGME_ENGINE_FLAG_OFFLINE)?
+                               "OPTION offline=1":
+                               "OPTION offline=0" ,
+                               NULL, NULL);
 
   if (pattern && *pattern)
     {
 
   if (pattern && *pattern)
     {
@@ -1651,7 +1874,7 @@ gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only,
     }
   line = malloc (length);
   if (!line)
     }
   line = malloc (length);
   if (!line)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
   if (secret_only)
     {
       strcpy (line, "LISTSECRETKEYS ");
   if (secret_only)
     {
       strcpy (line, "LISTSECRETKEYS ");
@@ -1725,6 +1948,8 @@ gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
   int i;
   gpgme_key_t key;
 
   int i;
   gpgme_key_t key;
 
+  (void)use_textmode;
+
   if (!gpgsm)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!gpgsm)
     return gpg_error (GPG_ERR_INV_VALUE);
 
@@ -1736,11 +1961,11 @@ gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
         can reset any previously set value in case the default is
         requested.  */
 
         can reset any previously set value in case the default is
         requested.  */
 
-      if (asprintf (&assuan_cmd, "OPTION include-certs %i", include_certs) < 0)
-       return gpg_error_from_errno (errno);
-      err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, assuan_cmd,
-                                         NULL, NULL);
-      free (assuan_cmd);
+      if (gpgrt_asprintf (&assuan_cmd,
+                          "OPTION include-certs %i", include_certs) < 0)
+       return gpg_error_from_syserror ();
+      err = gpgsm_assuan_simple_command (gpgsm, assuan_cmd, NULL, NULL);
+      gpgrt_free (assuan_cmd);
       if (err)
        return err;
     }
       if (err)
        return err;
     }
@@ -1753,14 +1978,14 @@ gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
           char buf[100];
 
           strcpy (stpcpy (buf, "SIGNER "), s);
           char buf[100];
 
           strcpy (stpcpy (buf, "SIGNER "), s);
-          err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, buf,
+          err = gpgsm_assuan_simple_command (gpgsm, buf,
                                              gpgsm->status.fnc,
                                              gpgsm->status.fnc_value);
        }
       else
         err = gpg_error (GPG_ERR_INV_VALUE);
       gpgme_key_unref (key);
                                              gpgsm->status.fnc,
                                              gpgsm->status.fnc_value);
        }
       else
         err = gpg_error (GPG_ERR_INV_VALUE);
       gpgme_key_unref (key);
-      if (err) 
+      if (err)
         return err;
     }
 
         return err;
     }
 
@@ -1784,11 +2009,13 @@ gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
 
 static gpgme_error_t
 gpgsm_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
 
 static gpgme_error_t
 gpgsm_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
-             gpgme_data_t plaintext)
+             gpgme_data_t plaintext, gpgme_ctx_t ctx)
 {
   engine_gpgsm_t gpgsm = engine;
   gpgme_error_t err;
 
 {
   engine_gpgsm_t gpgsm = engine;
   gpgme_error_t err;
 
+  (void)ctx;
+
   if (!gpgsm)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!gpgsm)
     return gpg_error (GPG_ERR_INV_VALUE);
 
@@ -1854,10 +2081,21 @@ gpgsm_getauditlog (void *engine, gpgme_data_t output, unsigned int flags)
 }
 
 
 }
 
 
+/* This sets a status callback for monitoring status lines before they
+ * are passed to a caller set handler.  */
+static void
+gpgsm_set_status_cb (void *engine, gpgme_status_cb_t cb, void *cb_value)
+{
+  engine_gpgsm_t gpgsm = engine;
+
+  gpgsm->status.mon_cb = cb;
+  gpgsm->status.mon_cb_value = cb_value;
+}
+
 
 static void
 gpgsm_set_status_handler (void *engine, engine_status_handler_t fnc,
 
 static void
 gpgsm_set_status_handler (void *engine, engine_status_handler_t fnc,
-                         void *fnc_value) 
+                         void *fnc_value)
 {
   engine_gpgsm_t gpgsm = engine;
 
 {
   engine_gpgsm_t gpgsm = engine;
 
@@ -1868,7 +2106,7 @@ gpgsm_set_status_handler (void *engine, engine_status_handler_t fnc,
 
 static gpgme_error_t
 gpgsm_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
 
 static gpgme_error_t
 gpgsm_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
-                             void *fnc_value) 
+                             void *fnc_value)
 {
   engine_gpgsm_t gpgsm = engine;
 
 {
   engine_gpgsm_t gpgsm = engine;
 
@@ -1907,19 +2145,21 @@ gpgsm_passwd (void *engine, gpgme_key_t key, unsigned int flags)
   gpgme_error_t err;
   char *line;
 
   gpgme_error_t err;
   char *line;
 
+  (void)flags;
+
   if (!key || !key->subkeys || !key->subkeys->fpr)
   if (!key || !key->subkeys || !key->subkeys->fpr)
-    return gpg_error (GPG_ERR_INV_VALUE);
+    return gpg_error (GPG_ERR_INV_CERT_OBJ);
 
 
-  if (asprintf (&line, "PASSWD -- %s", key->subkeys->fpr) < 0)
+  if (gpgrt_asprintf (&line, "PASSWD -- %s", key->subkeys->fpr) < 0)
     return gpg_error_from_syserror ();
     return gpg_error_from_syserror ();
-  
+
   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
   gpgsm_clear_fd (gpgsm, INPUT_FD);
   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
   gpgsm->inline_data = NULL;
 
   err = start (gpgsm, line);
   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
   gpgsm_clear_fd (gpgsm, INPUT_FD);
   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
   gpgsm->inline_data = NULL;
 
   err = start (gpgsm, line);
-  free (line);
+  gpgrt_free (line);
 
   return err;
 }
 
   return err;
 }
@@ -1929,7 +2169,7 @@ gpgsm_passwd (void *engine, gpgme_key_t key, unsigned int flags)
 struct engine_ops _gpgme_engine_ops_gpgsm =
   {
     /* Static functions.  */
 struct engine_ops _gpgme_engine_ops_gpgsm =
   {
     /* Static functions.  */
-    _gpgme_get_gpgsm_path,
+    _gpgme_get_default_gpgsm_name,
     NULL,
     gpgsm_get_version,
     gpgsm_get_req_version,
     NULL,
     gpgsm_get_version,
     gpgsm_get_req_version,
@@ -1942,12 +2182,13 @@ struct engine_ops _gpgme_engine_ops_gpgsm =
 #else
     NULL,                      /* reset */
 #endif
 #else
     NULL,                      /* reset */
 #endif
+    gpgsm_set_status_cb,
     gpgsm_set_status_handler,
     NULL,              /* set_command_handler */
     gpgsm_set_colon_line_handler,
     gpgsm_set_locale,
     NULL,              /* set_protocol */
     gpgsm_set_status_handler,
     NULL,              /* set_command_handler */
     gpgsm_set_colon_line_handler,
     gpgsm_set_locale,
     NULL,              /* set_protocol */
-    gpgsm_decrypt,
+    gpgsm_set_engine_flags,
     gpgsm_decrypt,
     gpgsm_delete,      /* decrypt_verify */
     NULL,              /* edit */
     gpgsm_decrypt,
     gpgsm_delete,      /* decrypt_verify */
     NULL,              /* edit */
@@ -1959,6 +2200,9 @@ struct engine_ops _gpgme_engine_ops_gpgsm =
     gpgsm_import,
     gpgsm_keylist,
     gpgsm_keylist_ext,
     gpgsm_import,
     gpgsm_keylist,
     gpgsm_keylist_ext,
+    NULL,               /* keylist_data */
+    NULL,               /* keysign */
+    NULL,               /* tofu_policy */
     gpgsm_sign,
     NULL,              /* trustlist */
     gpgsm_verify,
     gpgsm_sign,
     NULL,              /* trustlist */
     gpgsm_verify,
@@ -1966,9 +2210,13 @@ struct engine_ops _gpgme_engine_ops_gpgsm =
     NULL,               /* opassuan_transact */
     NULL,              /* conf_load */
     NULL,              /* conf_save */
     NULL,               /* opassuan_transact */
     NULL,              /* conf_load */
     NULL,              /* conf_save */
+    NULL,              /* conf_dir */
+    NULL,               /* query_swdb */
     gpgsm_set_io_cbs,
     gpgsm_io_event,
     gpgsm_cancel,
     NULL,              /* cancel_op */
     gpgsm_set_io_cbs,
     gpgsm_io_event,
     gpgsm_cancel,
     NULL,              /* cancel_op */
-    gpgsm_passwd
+    gpgsm_passwd,
+    NULL,               /* set_pinentry_mode */
+    NULL                /* opspawn */
   };
   };