core: Use gpgrt locking for thread safeness
[gpgme.git] / src / engine-gpgsm.c
index 49d36c1..a815cf0 100644 (file)
@@ -1,19 +1,20 @@
 /* engine-gpgsm.c - GpgSM engine.
    Copyright (C) 2000 Werner Koch (dd9jn)
-   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009 g10 Code GmbH
+   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.
-   
+
    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
 
 #include <stdlib.h>
 #include <string.h>
-#include <sys/types.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
 #include <assert.h>
-#include <unistd.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_LOCALE_H
 #include <locale.h>
+#endif
 #include <fcntl.h> /* FIXME */
 #include <errno.h>
 
@@ -38,9 +45,9 @@
 #include "wait.h"
 #include "priv-io.h"
 #include "sema.h"
+#include "data.h"
 
 #include "assuan.h"
-#include "status-table.h"
 #include "debug.h"
 
 #include "engine-backend.h"
@@ -70,6 +77,8 @@ struct engine_gpgsm
 
   /* Input, output etc are from the servers perspective.  */
   iocb_data_t input_cb;
+  gpgme_data_t input_helper_data;  /* Input helper data object.  */
+  void *input_helper_memory;       /* Input helper memory block.  */
 
   iocb_data_t output_cb;
 
@@ -79,6 +88,8 @@ struct engine_gpgsm
   {
     engine_status_handler_t fnc;
     void *fnc_value;
+    gpgme_status_cb_t mon_cb;
+    void *mon_cb_value;
   } status;
 
   struct
@@ -92,7 +103,7 @@ struct engine_gpgsm
       int linelen;
     } attic;
     int any; /* any data line seen */
-  } colon; 
+  } colon;
 
   gpgme_data_t inline_data;  /* Used to collect D lines.  */
 
@@ -102,7 +113,7 @@ struct engine_gpgsm
 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);
 
 
@@ -111,14 +122,14 @@ static char *
 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)
 {
-  return NEED_GPGSM_VERSION;
+  return "2.0.4";
 }
 
 \f
@@ -141,6 +152,16 @@ close_notify_handler (int fd, void *opaque)
        (*gpgsm->io_cbs.remove) (gpgsm->input_cb.tag);
       gpgsm->input_cb.fd = -1;
       gpgsm->input_cb.tag = NULL;
+      if (gpgsm->input_helper_data)
+        {
+          gpgme_data_release (gpgsm->input_helper_data);
+          gpgsm->input_helper_data = NULL;
+        }
+      if (gpgsm->input_helper_memory)
+        {
+          free (gpgsm->input_helper_memory);
+          gpgsm->input_helper_memory = NULL;
+        }
     }
   else if (gpgsm->output_cb.fd == fd)
     {
@@ -159,140 +180,16 @@ close_notify_handler (int fd, void *opaque)
 }
 
 
-static gpgme_error_t
-map_assuan_error (gpg_error_t err)
-{
-  if (!err)
-    return 0;
-
-  if (err == -1)
-    return gpg_error (GPG_ERR_INV_ENGINE);
-
-  /* New code will use gpg_error_t values.  */
-  if (gpg_err_source (err))
-    return (gpgme_error_t) err;
-
-  /* Legacy code will use old values.  */
-  switch (err)
-    {
-    case ASSUAN_No_Error:
-      return gpg_error (GPG_ERR_NO_ERROR);
-    case ASSUAN_General_Error:
-      return gpg_error (GPG_ERR_GENERAL);
-    case ASSUAN_Out_Of_Core:
-      return gpg_error (GPG_ERR_ENOMEM);
-    case ASSUAN_Invalid_Value:
-      return gpg_error (GPG_ERR_INV_VALUE);
-    case ASSUAN_Timeout:
-      return gpg_error (GPG_ERR_ETIMEDOUT);
-    case ASSUAN_Read_Error:
-      return gpg_error (GPG_ERR_GENERAL);
-    case ASSUAN_Write_Error:
-      return gpg_error (GPG_ERR_GENERAL);
-
-    case ASSUAN_Problem_Starting_Server:
-    case ASSUAN_Not_A_Server:
-    case ASSUAN_Not_A_Client:
-    case ASSUAN_Nested_Commands:
-    case ASSUAN_No_Data_Callback:
-    case ASSUAN_No_Inquire_Callback:
-    case ASSUAN_Connect_Failed:
-    case ASSUAN_Accept_Failed:
-    case ASSUAN_Invalid_Command:
-    case ASSUAN_Unknown_Command:
-    case ASSUAN_Syntax_Error:
-    case ASSUAN_Parameter_Error:
-    case ASSUAN_Parameter_Conflict:
-    case ASSUAN_No_Input:
-    case ASSUAN_No_Output:
-    case ASSUAN_No_Data_Available:
-    case ASSUAN_Too_Much_Data:
-    case ASSUAN_Inquire_Unknown:
-    case ASSUAN_Inquire_Error:
-    case ASSUAN_Invalid_Option:
-    case ASSUAN_Unexpected_Status:
-    case ASSUAN_Unexpected_Data:
-    case ASSUAN_Invalid_Status:
-      return gpg_error (GPG_ERR_ASSUAN);
-
-    case ASSUAN_Invalid_Response:
-      return gpg_error (GPG_ERR_INV_RESPONSE);
-
-    case ASSUAN_Not_Implemented:
-      return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
-    case ASSUAN_Line_Too_Long:
-      return gpg_error (GPG_ERR_LINE_TOO_LONG);
-    case ASSUAN_Line_Not_Terminated:
-      return gpg_error (GPG_ERR_INCOMPLETE_LINE);
-    case ASSUAN_Canceled:
-      return gpg_error (GPG_ERR_CANCELED);
-
-    case ASSUAN_Unsupported_Algorithm:
-      return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
-    case ASSUAN_Server_Resource_Problem:
-      return gpg_error (GPG_ERR_RESOURCE_LIMIT);
-    case ASSUAN_Server_IO_Error:
-      return gpg_error (GPG_ERR_GENERAL);
-    case ASSUAN_Server_Bug:
-      return gpg_error (GPG_ERR_BUG);
-    case ASSUAN_Invalid_Data:
-      return gpg_error (GPG_ERR_INV_DATA);
-    case ASSUAN_Invalid_Index:
-      return gpg_error (GPG_ERR_INV_INDEX);
-    case ASSUAN_Not_Confirmed:
-      return gpg_error (GPG_ERR_NOT_CONFIRMED);
-    case ASSUAN_Bad_Certificate:
-      return gpg_error (GPG_ERR_BAD_CERT);
-    case ASSUAN_Bad_Certificate_Chain:
-      return gpg_error (GPG_ERR_BAD_CERT_CHAIN);
-    case ASSUAN_Missing_Certificate:
-      return gpg_error (GPG_ERR_MISSING_CERT);
-    case ASSUAN_Bad_Signature:
-      return gpg_error (GPG_ERR_BAD_SIGNATURE);
-    case ASSUAN_No_Agent:
-      return gpg_error (GPG_ERR_NO_AGENT);
-    case ASSUAN_Agent_Error:
-      return gpg_error (GPG_ERR_AGENT);
-    case ASSUAN_No_Public_Key:
-      return gpg_error (GPG_ERR_NO_PUBKEY);
-    case ASSUAN_No_Secret_Key:
-      return gpg_error (GPG_ERR_NO_SECKEY);
-    case ASSUAN_Invalid_Name:
-      return gpg_error (GPG_ERR_INV_NAME);
-      
-    case ASSUAN_Cert_Revoked:
-      return gpg_error (GPG_ERR_CERT_REVOKED);
-    case ASSUAN_No_CRL_For_Cert:
-      return gpg_error (GPG_ERR_NO_CRL_KNOWN);
-    case ASSUAN_CRL_Too_Old:
-      return gpg_error (GPG_ERR_CRL_TOO_OLD);
-    case ASSUAN_Not_Trusted:
-      return gpg_error (GPG_ERR_NOT_TRUSTED);
-
-    case ASSUAN_Card_Error:
-      return gpg_error (GPG_ERR_CARD);
-    case ASSUAN_Invalid_Card:
-      return gpg_error (GPG_ERR_INV_CARD);
-    case ASSUAN_No_PKCS15_App:
-      return gpg_error (GPG_ERR_NO_PKCS15_APP);
-    case ASSUAN_Card_Not_Present:
-      return gpg_error (GPG_ERR_CARD_NOT_PRESENT);
-    case ASSUAN_Invalid_Id:
-      return gpg_error (GPG_ERR_INV_ID);
-    default:
-      return gpg_error (GPG_ERR_GENERAL);
-    }
-}
-
-
 /* This is the default inquiry callback.  We use it to handle the
    Pinentry notifications.  */
 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_foregound_window ((pid_t)strtoul (line+17, NULL, 10));
+      _gpgme_allow_set_foreground_window ((pid_t)strtoul (line+17, NULL, 10));
     }
 
   return 0;
@@ -318,7 +215,7 @@ gpgsm_cancel (void *engine)
 
   if (gpgsm->assuan_ctx)
     {
-      assuan_disconnect (gpgsm->assuan_ctx);
+      assuan_release (gpgsm->assuan_ctx);
       gpgsm->assuan_ctx = NULL;
     }
 
@@ -342,10 +239,12 @@ gpgsm_release (void *engine)
 
 
 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;
+  const char *pgmname;
   const char *argv[5];
   int argc;
 #if !USE_DESCRIPTOR_PASSING
@@ -354,12 +253,15 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
 #endif
   char *dft_display = NULL;
   char dft_ttyname[64];
+  char *env_tty = NULL;
   char *dft_ttytype = NULL;
   char *optstr;
 
+  (void)version; /* Not yet used.  */
+
   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;
@@ -400,7 +302,7 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
 #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];
@@ -408,7 +310,7 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
 
   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];
@@ -416,7 +318,7 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
 
   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];
@@ -428,8 +330,10 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
   child_fds[3] = -1;
 #endif
 
+  pgmname = file_name ? file_name : _gpgme_get_default_gpgsm_name ();
+
   argc = 0;
-  argv[argc++] = "gpgsm";
+  argv[argc++] = _gpgme_get_basename (pgmname);
   if (home_dir)
     {
       argv[argc++] = "--homedir";
@@ -438,14 +342,32 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
   argv[argc++] = "--server";
   argv[argc++] = NULL;
 
+  err = assuan_new_ext (&gpgsm->assuan_ctx, GPG_ERR_SOURCE_GPGME,
+                       &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
+                       NULL);
+  if (err)
+    goto leave;
+  assuan_ctx_set_system_hooks (gpgsm->assuan_ctx, &_gpgme_assuan_system_hooks);
+
 #if USE_DESCRIPTOR_PASSING
-  err = assuan_pipe_connect_ext
-    (&gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path (),
-     argv, NULL, NULL, NULL, 1);
+  err = assuan_pipe_connect (gpgsm->assuan_ctx, pgmname, argv,
+                             NULL, NULL, NULL, ASSUAN_PIPE_CONNECT_FDPASSING);
 #else
-  err = assuan_pipe_connect
-    (&gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path (),
-     argv, child_fds);
+  {
+    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
@@ -469,12 +391,6 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
   if (err)
     goto leave;
 
-  /* assuan_pipe_connect in this case uses _gpgme_io_spawn which
-     closes the child fds for us.  */
-  gpgsm->input_cb.server_fd = -1;
-  gpgsm->output_cb.server_fd = -1;
-  gpgsm->message_cb.server_fd = -1;
-
   err = _gpgme_getenv ("DISPLAY", &dft_display);
   if (err)
     goto leave;
@@ -483,7 +399,7 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
       if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
         {
          free (dft_display);
-         err = gpg_error_from_errno (errno);
+         err = gpg_error_from_syserror ();
          goto leave;
        }
       free (dft_display);
@@ -492,37 +408,38 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
                             NULL, NULL, NULL);
       free (optstr);
       if (err)
-       {
-         err = map_assuan_error (err);
-         goto leave;
-       }
+       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
+        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)
            {
-             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);
          free (optstr);
          if (err)
-           {
-             err = map_assuan_error (err);
-             goto leave;
-           }
+           goto leave;
 
          err = _gpgme_getenv ("TERM", &dft_ttytype);
          if (err)
@@ -532,7 +449,7 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
              if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
                {
                  free (dft_ttytype);
-                 err = gpg_error_from_errno (errno);
+                 err = gpg_error_from_syserror ();
                  goto leave;
                }
              free (dft_ttytype);
@@ -541,10 +458,7 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
                                     NULL, NULL, NULL, NULL);
              free (optstr);
              if (err)
-               {
-                 err = map_assuan_error (err);
-                 goto leave;
-               }
+               goto leave;
            }
        }
     }
@@ -613,12 +527,15 @@ 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.  */
-  if (category == LC_CTYPE)
+  if (0)
+    ;
+#ifdef LC_CTYPE
+  else if (category == LC_CTYPE)
     {
       catstr = "lc-ctype";
       if (!value && gpgsm->lc_ctype_set)
@@ -626,6 +543,7 @@ gpgsm_set_locale (void *engine, int category, const char *value)
       if (value)
        gpgsm->lc_ctype_set = 1;
     }
+#endif
 #ifdef LC_MESSAGES
   else if (category == LC_MESSAGES)
     {
@@ -640,45 +558,42 @@ gpgsm_set_locale (void *engine, int category, const char *value)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   /* FIXME: Reset value to default.  */
-  if (!value) 
+  if (!value)
     return 0;
 
   if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
-    err = gpg_error_from_errno (errno);
+    err = gpg_error_from_syserror ();
   else
     {
       err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL,
                             NULL, NULL, NULL, NULL);
       free (optstr);
-      if (err)
-       err = map_assuan_error (err);
     }
 
   return err;
 }
 
 
-/* Forward declaration.  */
-static gpgme_status_code_t parse_status (const char *name);
-
 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)
 {
-  gpg_error_t err;
+  assuan_context_t ctx = gpgsm->assuan_ctx;
+  gpg_error_t err, cb_err;
   char *line;
   size_t linelen;
 
   err = assuan_write_line (ctx, cmd);
   if (err)
-    return map_assuan_error (err);
+    return err;
 
+  cb_err = 0;
   do
     {
       err = assuan_read_line (ctx, &line, &linelen);
       if (err)
-       return map_assuan_error (err);
+       return err;
 
       if (*line == '#' || !linelen)
        continue;
@@ -686,32 +601,52 @@ gpgsm_assuan_simple_command (assuan_context_t ctx, char *cmd,
       if (linelen >= 2
          && line[0] == 'O' && line[1] == 'K'
          && (line[2] == '\0' || line[2] == ' '))
-       return 0;
+       return cb_err;
       else if (linelen >= 4
          && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
          && line[3] == ' ')
-       err = map_assuan_error (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]);
+        }
       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
-       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);
+        }
     }
   while (!err);
 
@@ -737,6 +672,9 @@ gpgsm_clear_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type)
       _gpgme_io_close (gpgsm->message_cb.fd);
       break;
     }
+#else
+  (void)gpgsm;
+  (void)fd_type;
 #endif
 }
 
@@ -746,9 +684,11 @@ gpgsm_set_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type, const char *opt)
 {
   gpg_error_t err = 0;
   char line[COMMANDLINELEN];
-  char *which;
+  const char *which;
   iocb_data_t *iocb_data;
+#if USE_DESCRIPTOR_PASSING
   int dir;
+#endif
 
   switch (fd_type)
     {
@@ -771,9 +711,8 @@ gpgsm_set_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type, const char *opt)
       return gpg_error (GPG_ERR_INV_VALUE);
     }
 
-  dir = iocb_data->dir;
-
 #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);
@@ -781,8 +720,8 @@ gpgsm_set_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type, const char *opt)
     {
       int fds[2];
 
-      if (_gpgme_io_pipe (fds, 0) < 0)
-       return gpg_error_from_errno (errno);
+      if (_gpgme_io_pipe (fds, dir) < 0)
+       return gpg_error_from_syserror ();
 
       iocb_data->fd = dir ? fds[0] : fds[1];
       iocb_data->server_fd = dir ? fds[1] : fds[0];
@@ -808,14 +747,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=%s %s", 
+    snprintf (line, COMMANDLINELEN, "%s FD=%s %s",
               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
 
-  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:
@@ -855,55 +794,33 @@ 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)
 {
-  gpg_error_t assuan_err;
+  struct io_cb_data *data = (struct io_cb_data *) opaque;
+  engine_gpgsm_t gpgsm = (engine_gpgsm_t) data->handler_value;
   gpgme_error_t err = 0;
-  engine_gpgsm_t gpgsm = opaque;
   char *line;
   size_t linelen;
 
   do
     {
-      assuan_err = assuan_read_line (gpgsm->assuan_ctx, &line, &linelen);
-      if (assuan_err)
+      err = assuan_read_line (gpgsm->assuan_ctx, &line, &linelen);
+      if (err)
        {
          /* Try our best to terminate the connection friendly.  */
          /*      assuan_write_line (gpgsm->assuan_ctx, "BYE"); */
-         err = map_assuan_error (assuan_err);
           TRACE3 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
                  "fd 0x%x: error from assuan (%d) getting status line : %s",
-                  fd, assuan_err, gpg_strerror (err));
+                  fd, err, gpg_strerror (err));
        }
       else if (linelen >= 3
               && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
               && (line[3] == '\0' || line[3] == ' '))
        {
          if (line[3] == ' ')
-           err = map_assuan_error (atoi (&line[4]));
-         else
+           err = atoi (&line[4]);
+         if (! err)
            err = gpg_error (GPG_ERR_GENERAL);
           TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
                  "fd 0x%x: ERR line - mapped to: %s",
@@ -916,10 +833,15 @@ status_handler (void *opaque, int fd)
               && (line[2] == '\0' || line[2] == ' '))
        {
          if (gpgsm->status.fnc)
-           err = gpgsm->status.fnc (gpgsm->status.fnc_value,
-                                    GPGME_STATUS_EOF, "");
-         
-         if (!err && gpgsm->colon.fnc && gpgsm->colon.any )
+            {
+              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
                  this only when we have seen any data lines.  Note
@@ -955,11 +877,11 @@ status_handler (void *opaque, int fd)
            {
              char *newline = realloc (*aline, *alinelen + linelen + 1);
              if (!newline)
-               err = gpg_error_from_errno (errno);
+               err = gpg_error_from_syserror ();
              else
                {
                  *aline = newline;
-                 gpgsm->colon.attic.linesize += linelen + 1;
+                 gpgsm->colon.attic.linesize = *alinelen + linelen + 1;
                }
            }
          if (!err)
@@ -981,12 +903,12 @@ status_handler (void *opaque, int fd)
                      *dst = *src++;
                      (*alinelen)++;
                    }
-                 
+
                  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--;
@@ -1015,7 +937,7 @@ status_handler (void *opaque, int fd)
           char *src = line + 2;
          char *end = line + linelen;
          char *dst = src;
-          ssize_t nwritten;
+          gpgme_ssize_t nwritten;
 
           linelen = 0;
           while (src < end)
@@ -1029,10 +951,10 @@ status_handler (void *opaque, int fd)
                 }
               else
                 *dst++ = *src++;
-              
+
               linelen++;
             }
-          
+
           src = line + 2;
           while (linelen > 0)
             {
@@ -1040,7 +962,7 @@ status_handler (void *opaque, int fd)
               if (!nwritten || (nwritten < 0 && errno != EINTR)
                   || nwritten > linelen)
                 {
-                  err = gpg_error_from_errno (errno);
+                  err = gpg_error_from_syserror ();
                   break;
                 }
               src += nwritten;
@@ -1056,19 +978,23 @@ status_handler (void *opaque, int fd)
        {
          char *rest;
          gpgme_status_code_t r;
-         
+
          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)
-               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);
@@ -1079,7 +1005,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'
-               && line[6] == 'E' 
+               && line[6] == 'E'
                && (line[7] == '\0' || line[7] == ' '))
         {
           char *keyword = line+7;
@@ -1092,7 +1018,7 @@ status_handler (void *opaque, int fd)
 
     }
   while (!err && assuan_pending_line (gpgsm->assuan_ctx));
-         
+
   return err;
 }
 
@@ -1120,16 +1046,21 @@ static gpgme_error_t
 start (engine_gpgsm_t gpgsm, const char *command)
 {
   gpgme_error_t err;
+  assuan_fd_t afdlist[5];
   int fdlist[5];
   int nfds;
+  int i;
 
   /* 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 */
+  /* 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
@@ -1162,7 +1093,7 @@ start (engine_gpgsm_t gpgsm, const char *command)
     err = add_io_cb (gpgsm, &gpgsm->message_cb, _gpgme_data_outbound_handler);
 
   if (!err)
-    err = map_assuan_error (assuan_write_line (gpgsm->assuan_ctx, command));
+    err = assuan_write_line (gpgsm->assuan_ctx, command);
 
   if (!err)
     gpgsm_io_event (gpgsm, GPGME_EVENT_START, NULL);
@@ -1177,13 +1108,17 @@ gpgsm_reset (void *engine)
 {
   engine_gpgsm_t gpgsm = engine;
 
-  /* We must send a reset because we need to reset the list of
-     signers.  Note that RESET does not reset OPTION commands. */
-  return gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "RESET", NULL, NULL);
+  /* IF we have an active connection we must send a reset because we
+     need to reset the list of signers.  Note that RESET does not
+     reset OPTION commands. */
+  return (gpgsm->assuan_ctx
+          ? gpgsm_assuan_simple_command (gpgsm, "RESET", NULL, NULL)
+          : 0);
 }
 #endif
 
 
+
 static gpgme_error_t
 gpgsm_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
 {
@@ -1219,6 +1154,8 @@ gpgsm_delete (void *engine, gpgme_key_t key, int allow_secret)
   char *line;
   int length = 8;      /* "DELKEYS " */
 
+  (void)allow_secret;
+
   if (!fpr)
     return gpg_error (GPG_ERR_INV_VALUE);
 
@@ -1233,7 +1170,7 @@ gpgsm_delete (void *engine, gpgme_key_t key, int allow_secret)
 
   line = malloc (length);
   if (!line)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
 
   strcpy (line, "DELKEYS ");
   linep = &line[8];
@@ -1281,18 +1218,17 @@ static gpgme_error_t
 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;
-  int i = 0;
+  int i;
 
   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 ");
-  while (!err && recp[i])
+  for (i =0; !err && recp[i]; i++)
     {
       char *fpr;
       int newlen;
@@ -1310,16 +1246,16 @@ set_recipients (engine_gpgsm_t gpgsm, gpgme_key_t recp[])
          char *newline = realloc (line, newlen);
          if (! newline)
            {
-             int saved_errno = errno;
+             int saved_err = gpg_error_from_syserror ();
              free (line);
-             return gpg_error_from_errno (saved_errno);
+             return saved_err;
            }
          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)
@@ -1329,7 +1265,6 @@ set_recipients (engine_gpgsm_t gpgsm, gpgme_key_t recp[])
          free (line);
          return err;
        }
-      i++;
     }
   free (line);
   return gpg_error (invalid_recipients
@@ -1351,7 +1286,7 @@ gpgsm_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
 
   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;
@@ -1379,24 +1314,33 @@ gpgsm_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
 
 
 static gpgme_error_t
-gpgsm_export (void *engine, const char *pattern, unsigned int reserved,
+gpgsm_export (void *engine, const char *pattern, gpgme_export_mode_t mode,
              gpgme_data_t keydata, int use_armor)
 {
   engine_gpgsm_t gpgsm = engine;
   gpgme_error_t err = 0;
   char *cmd;
 
-  if (!gpgsm || reserved)
+  if (!gpgsm)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!pattern)
     pattern = "";
 
-  cmd = malloc (7 + strlen (pattern) + 1);
+  cmd = malloc (7 + 9 + 9 + strlen (pattern) + 1);
   if (!cmd)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
+
   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"
@@ -1414,17 +1358,17 @@ gpgsm_export (void *engine, const char *pattern, unsigned int reserved,
 
 
 static gpgme_error_t
-gpgsm_export_ext (void *engine, const char *pattern[], unsigned int reserved,
+gpgsm_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode,
                  gpgme_data_t keydata, int use_armor)
 {
   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 || reserved)
+  if (!gpgsm)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (pattern && *pattern)
@@ -1448,10 +1392,18 @@ gpgsm_export_ext (void *engine, const char *pattern[], unsigned int reserved,
     }
   line = malloc (length);
   if (!line)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
 
   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)
     {
@@ -1507,57 +1459,157 @@ gpgsm_export_ext (void *engine, const char *pattern[], unsigned int reserved,
 
 
 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;
 
-  if (!gpgsm || !pubkey || seckey)
+  (void)reserved;
+
+  if (!gpgsm)
     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);
 }
 
 
 static gpgme_error_t
-gpgsm_import (void *engine, gpgme_data_t keydata)
+gpgsm_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray)
 {
   engine_gpgsm_t gpgsm = engine;
   gpgme_error_t err;
+  gpgme_data_encoding_t dataenc;
+  int idx;
 
   if (!gpgsm)
     return gpg_error (GPG_ERR_INV_VALUE);
 
-  gpgsm->input_cb.data = keydata;
-  err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
-  if (err)
-    return err;
-  gpgsm_clear_fd (gpgsm, OUTPUT_FD);
-  gpgsm_clear_fd (gpgsm, MESSAGE_FD);
-  gpgsm->inline_data = NULL;
+  if (keydata && keyarray)
+    return gpg_error (GPG_ERR_INV_VALUE); /* Only one is allowed.  */
 
-  err = start (gpgsm, "IMPORT");
-  return err;
+  dataenc = gpgme_data_get_encoding (keydata);
+
+  if (keyarray)
+    {
+      size_t buflen;
+      char *buffer, *p;
+
+      /* Fist check whether the engine already features the
+         --re-import option.  */
+      err = gpgsm_assuan_simple_command
+        (gpgsm, "GETINFO cmd_has_option IMPORT re-import", NULL, NULL);
+      if (err)
+       return gpg_error (GPG_ERR_NOT_SUPPORTED);
+
+      /* Create an internal data object with a list of all
+         fingerprints.  The data object and its memory (to avoid an
+         extra copy by gpgme_data_new_from_mem) are stored in two
+         variables which are released by the close_notify_handler.  */
+      for (idx=0, buflen=0; keyarray[idx]; idx++)
+        {
+          if (keyarray[idx]->protocol == GPGME_PROTOCOL_CMS
+              && keyarray[idx]->subkeys
+              && keyarray[idx]->subkeys->fpr
+              && *keyarray[idx]->subkeys->fpr)
+            buflen += strlen (keyarray[idx]->subkeys->fpr) + 1;
+        }
+      /* Allocate a bufer with extra space for the trailing Nul
+         introduced by the use of stpcpy.  */
+      buffer = malloc (buflen+1);
+      if (!buffer)
+        return gpg_error_from_syserror ();
+      for (idx=0, p = buffer; keyarray[idx]; idx++)
+        {
+          if (keyarray[idx]->protocol == GPGME_PROTOCOL_CMS
+              && keyarray[idx]->subkeys
+              && keyarray[idx]->subkeys->fpr
+              && *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)
+        {
+          free (buffer);
+          return err;
+        }
+      gpgsm->input_helper_memory = buffer;
+
+      gpgsm->input_cb.data = gpgsm->input_helper_data;
+      err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
+      if (err)
+        {
+          gpgme_data_release (gpgsm->input_helper_data);
+          gpgsm->input_helper_data = NULL;
+          free (gpgsm->input_helper_memory);
+          gpgsm->input_helper_memory = NULL;
+          return err;
+        }
+      gpgsm_clear_fd (gpgsm, OUTPUT_FD);
+      gpgsm_clear_fd (gpgsm, MESSAGE_FD);
+      gpgsm->inline_data = NULL;
+
+      return start (gpgsm, "IMPORT --re-import");
+    }
+  else if (dataenc == GPGME_DATA_ENCODING_URL
+           || dataenc == GPGME_DATA_ENCODING_URL0
+           || dataenc == GPGME_DATA_ENCODING_URLESC)
+    {
+      return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+    }
+  else
+    {
+      gpgsm->input_cb.data = keydata;
+      err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
+      if (err)
+        return err;
+      gpgsm_clear_fd (gpgsm, OUTPUT_FD);
+      gpgsm_clear_fd (gpgsm, MESSAGE_FD);
+      gpgsm->inline_data = NULL;
+
+      return start (gpgsm, "IMPORT");
+    }
 }
 
 
 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;
@@ -1572,10 +1624,21 @@ gpgsm_keylist (void *engine, const char *pattern, int secret_only,
   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.  */
   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);
+    return gpg_error_from_syserror ();
+  err = gpgsm_assuan_simple_command (gpgsm, line, NULL, NULL);
   free (line);
   if (err)
     return err;
@@ -1585,24 +1648,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. */
-  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. */
-  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);
+  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)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
   if (secret_only)
     {
       strcpy (line, "LISTSECRETKEYS ");
@@ -1627,7 +1700,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,
-                  int reserved, gpgme_keylist_mode_t mode)
+                  int reserved, gpgme_keylist_mode_t mode, int engine_flags)
 {
   engine_gpgsm_t gpgsm = engine;
   char *line;
@@ -1648,8 +1721,8 @@ gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only,
 
   /* 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);
+    return gpg_error_from_syserror ();
+  err = gpgsm_assuan_simple_command (gpgsm, line, NULL, NULL);
   free (line);
   if (err)
     return err;
@@ -1657,12 +1730,21 @@ gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only,
   /* 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);
-
+  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)
     {
@@ -1685,7 +1767,7 @@ gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only,
     }
   line = malloc (length);
   if (!line)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
   if (secret_only)
     {
       strcpy (line, "LISTSECRETKEYS ");
@@ -1759,6 +1841,8 @@ gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
   int i;
   gpgme_key_t key;
 
+  (void)use_textmode;
+
   if (!gpgsm)
     return gpg_error (GPG_ERR_INV_VALUE);
 
@@ -1771,9 +1855,8 @@ gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
         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);
+       return gpg_error_from_syserror ();
+      err = gpgsm_assuan_simple_command (gpgsm, assuan_cmd, NULL, NULL);
       free (assuan_cmd);
       if (err)
        return err;
@@ -1787,13 +1870,14 @@ gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
           char buf[100];
 
           strcpy (stpcpy (buf, "SIGNER "), s);
-          err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, buf,
-                                             NULL, NULL);
+          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);
-      if (err) 
+      if (err)
         return err;
     }
 
@@ -1817,11 +1901,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,
-             gpgme_data_t plaintext)
+             gpgme_data_t plaintext, gpgme_ctx_t ctx)
 {
   engine_gpgsm_t gpgsm = engine;
   gpgme_error_t err;
 
+  (void)ctx;
+
   if (!gpgsm)
     return gpg_error (GPG_ERR_INV_VALUE);
 
@@ -1887,10 +1973,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,
-                         void *fnc_value) 
+                         void *fnc_value)
 {
   engine_gpgsm_t gpgsm = engine;
 
@@ -1901,7 +1998,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,
-                             void *fnc_value) 
+                             void *fnc_value)
 {
   engine_gpgsm_t gpgsm = engine;
 
@@ -1933,10 +2030,38 @@ gpgsm_io_event (void *engine, gpgme_event_io_t type, void *type_data)
 }
 
 
+static gpgme_error_t
+gpgsm_passwd (void *engine, gpgme_key_t key, unsigned int flags)
+{
+  engine_gpgsm_t gpgsm = engine;
+  gpgme_error_t err;
+  char *line;
+
+  (void)flags;
+
+  if (!key || !key->subkeys || !key->subkeys->fpr)
+    return gpg_error (GPG_ERR_INV_CERT_OBJ);
+
+  if (asprintf (&line, "PASSWD -- %s", key->subkeys->fpr) < 0)
+    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);
+  free (line);
+
+  return err;
+}
+
+
+
 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,
@@ -1949,12 +2074,15 @@ struct engine_ops _gpgme_engine_ops_gpgsm =
 #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_decrypt,
     gpgsm_decrypt,
-    gpgsm_delete,
+    gpgsm_delete,      /* decrypt_verify */
     NULL,              /* edit */
     gpgsm_encrypt,
     NULL,              /* encrypt_sign */
@@ -1964,6 +2092,8 @@ struct engine_ops _gpgme_engine_ops_gpgsm =
     gpgsm_import,
     gpgsm_keylist,
     gpgsm_keylist_ext,
+    NULL,               /* keysign */
+    NULL,               /* tofu_policy */
     gpgsm_sign,
     NULL,              /* trustlist */
     gpgsm_verify,
@@ -1971,7 +2101,12 @@ struct engine_ops _gpgme_engine_ops_gpgsm =
     NULL,               /* opassuan_transact */
     NULL,              /* conf_load */
     NULL,              /* conf_save */
+    NULL,               /* query_swdb */
     gpgsm_set_io_cbs,
     gpgsm_io_event,
-    gpgsm_cancel
+    gpgsm_cancel,
+    NULL,              /* cancel_op */
+    gpgsm_passwd,
+    NULL,               /* set_pinentry_mode */
+    NULL                /* opspawn */
   };