json: Remove the -noinstall flag used during development.
[gpgme.git] / src / engine-gpgsm.c
index 391b632..da7e524 100644 (file)
@@ -88,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
@@ -105,6 +107,8 @@ struct engine_gpgsm
 
   gpgme_data_t inline_data;  /* Used to collect D lines.  */
 
+  char request_origin[10];
+
   struct gpgme_io_cbs io_cbs;
 };
 
@@ -120,14 +124,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
@@ -183,6 +187,8 @@ close_notify_handler (int fd, void *opaque)
 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));
@@ -235,10 +241,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
@@ -247,9 +255,12 @@ 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_syserror ();
@@ -321,8 +332,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";
@@ -339,9 +352,8 @@ 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
-  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
   {
     assuan_fd_t achild_fds[4];
@@ -351,9 +363,8 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
     for (i = 0; i < 4; i++)
       achild_fds[i] = (assuan_fd_t) child_fds[i];
 
-    err = assuan_pipe_connect
-      (gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path (),
-       argv, achild_fds, NULL, NULL, 0);
+    err = assuan_pipe_connect (gpgsm->assuan_ctx, pgmname, argv,
+                               achild_fds, NULL, NULL, 0);
 
     /* For now... */
     for (i = 0; i < 4; i++)
@@ -387,7 +398,7 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
     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);
          err = gpg_error_from_syserror ();
@@ -397,31 +408,38 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
 
       err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
                             NULL, NULL, NULL);
-      free (optstr);
+      gpgrt_free (optstr);
       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
+        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_syserror ();
              goto leave;
            }
          err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
                                 NULL, NULL, NULL);
-         free (optstr);
+         gpgrt_free (optstr);
          if (err)
            goto leave;
 
@@ -430,7 +448,7 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
            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);
                  err = gpg_error_from_syserror ();
@@ -440,7 +458,7 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
 
              err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL,
                                     NULL, NULL, NULL, NULL);
-             free (optstr);
+             gpgrt_free (optstr);
              if (err)
                goto leave;
            }
@@ -505,13 +523,31 @@ 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;
-  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
@@ -545,13 +581,13 @@ gpgsm_set_locale (void *engine, int category, const char *value)
   if (!value)
     return 0;
 
-  if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
+  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);
-      free (optstr);
+      gpgrt_free (optstr);
     }
 
   return err;
@@ -559,11 +595,12 @@ gpgsm_set_locale (void *engine, int category, const char *value)
 
 
 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;
 
@@ -571,11 +608,12 @@ gpgsm_assuan_simple_command (assuan_context_t ctx, char *cmd,
   if (err)
     return err;
 
+  cb_err = 0;
   do
     {
       err = assuan_read_line (ctx, &line, &linelen);
       if (err)
-       return err;
+       break;
 
       if (*line == '#' || !linelen)
        continue;
@@ -583,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] == ' '))
-       return 0;
+       break;
       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] == ' ')
        {
-         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 = _gpgme_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);
+          cb_err = 0;
+        }
     }
   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;
 }
 
@@ -634,6 +699,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
 }
 
@@ -643,7 +711,7 @@ 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;
@@ -713,7 +781,7 @@ gpgsm_set_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type, const char *opt)
               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:
@@ -792,8 +860,13 @@ 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, "");
+            {
+              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)
             {
@@ -835,7 +908,7 @@ status_handler (void *opaque, int fd)
              else
                {
                  *aline = newline;
-                 gpgsm->colon.attic.linesize += linelen + 1;
+                 gpgsm->colon.attic.linesize = *alinelen + linelen + 1;
                }
            }
          if (!err)
@@ -944,7 +1017,11 @@ status_handler (void *opaque, int fd)
          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);
@@ -1001,6 +1078,20 @@ start (engine_gpgsm_t gpgsm, const char *command)
   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.  */
@@ -1062,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
-          ? gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "RESET",
-                                         NULL, NULL)
+          ? gpgsm_assuan_simple_command (gpgsm, "RESET", NULL, NULL)
           : 0);
 }
 #endif
@@ -1071,11 +1161,25 @@ gpgsm_reset (void *engine)
 
 
 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;
 
+  (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);
 
@@ -1096,7 +1200,7 @@ gpgsm_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
 
 
 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;
@@ -1105,6 +1209,8 @@ gpgsm_delete (void *engine, gpgme_key_t key, int allow_secret)
   char *line;
   int length = 8;      /* "DELKEYS " */
 
+  (void)flags;
+
   if (!fpr)
     return gpg_error (GPG_ERR_INV_VALUE);
 
@@ -1167,7 +1273,6 @@ 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;
@@ -1205,7 +1310,7 @@ set_recipients (engine_gpgsm_t gpgsm, gpgme_key_t recp[])
        }
       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)
@@ -1222,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
-gpgsm_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
+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
+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;
@@ -1234,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 (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;
@@ -1254,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;
 
-  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");
@@ -1274,17 +1431,23 @@ gpgsm_export (void *engine, const char *pattern, gpgme_export_mode_t mode,
   if (!gpgsm)
     return gpg_error (GPG_ERR_INV_VALUE);
 
-  if (mode)
-    return gpg_error (GPG_ERR_NOT_SUPPORTED);
-
   if (!pattern)
     pattern = "";
 
-  cmd = malloc (7 + strlen (pattern) + 1);
+  cmd = malloc (7 + 9 + 9 + strlen (pattern) + 1);
   if (!cmd)
     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"
@@ -1308,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;
-  /* 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);
 
-  if (mode)
-    return gpg_error (GPG_ERR_NOT_SUPPORTED);
-
   if (pattern && *pattern)
     {
       const char **pat = pattern;
@@ -1342,7 +1502,15 @@ gpgsm_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode,
     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)
     {
@@ -1398,29 +1566,51 @@ gpgsm_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode,
 
 
 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);
 }
 
 
@@ -1448,8 +1638,7 @@ gpgsm_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray)
       /* 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);
+        (gpgsm, "GETINFO cmd_has_option IMPORT re-import", NULL, NULL);
       if (err)
        return gpg_error (GPG_ERR_NOT_SUPPORTED);
 
@@ -1527,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,
-              gpgme_keylist_mode_t mode)
+              gpgme_keylist_mode_t mode, int engine_flags)
 {
   engine_gpgsm_t gpgsm = engine;
   char *line;
@@ -1550,15 +1739,14 @@ gpgsm_keylist (void *engine, const char *pattern, int secret_only,
      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)
-    gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "GETINFO agent-check",
-                                 NULL, NULL);
+  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)
+  if (gpgrt_asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0)
     return gpg_error_from_syserror ();
-  err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
-  free (line);
+  err = gpgsm_assuan_simple_command (gpgsm, line, NULL, NULL);
+  gpgrt_free (line);
   if (err)
     return err;
 
@@ -1567,18 +1755,28 @@ 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'.  */
@@ -1609,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,
-                  int reserved, gpgme_keylist_mode_t mode)
+                  int reserved, gpgme_keylist_mode_t mode, int engine_flags)
 {
   engine_gpgsm_t gpgsm = engine;
   char *line;
@@ -1629,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.  */
-  if (asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0)
+  if (gpgrt_asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0)
     return gpg_error_from_syserror ();
-  err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
-  free (line);
+  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. */
-  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)
     {
@@ -1741,6 +1948,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);
 
@@ -1752,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.  */
 
-      if (asprintf (&assuan_cmd, "OPTION include-certs %i", include_certs) < 0)
+      if (gpgrt_asprintf (&assuan_cmd,
+                          "OPTION include-certs %i", include_certs) < 0)
        return gpg_error_from_syserror ();
-      err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, assuan_cmd,
-                                         NULL, NULL);
-      free (assuan_cmd);
+      err = gpgsm_assuan_simple_command (gpgsm, assuan_cmd, NULL, NULL);
+      gpgrt_free (assuan_cmd);
       if (err)
        return err;
     }
@@ -1769,7 +1978,7 @@ 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,
+          err = gpgsm_assuan_simple_command (gpgsm, buf,
                                              gpgsm->status.fnc,
                                              gpgsm->status.fnc_value);
        }
@@ -1800,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,
-             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);
 
@@ -1870,6 +2081,17 @@ 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,
@@ -1923,10 +2145,12 @@ gpgsm_passwd (void *engine, gpgme_key_t key, unsigned int flags)
   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)
+  if (gpgrt_asprintf (&line, "PASSWD -- %s", key->subkeys->fpr) < 0)
     return gpg_error_from_syserror ();
 
   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
@@ -1935,7 +2159,7 @@ gpgsm_passwd (void *engine, gpgme_key_t key, unsigned int flags)
   gpgsm->inline_data = NULL;
 
   err = start (gpgsm, line);
-  free (line);
+  gpgrt_free (line);
 
   return err;
 }
@@ -1945,7 +2169,7 @@ gpgsm_passwd (void *engine, gpgme_key_t key, unsigned int flags)
 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,
@@ -1958,12 +2182,13 @@ 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_set_engine_flags,
     gpgsm_decrypt,
     gpgsm_delete,      /* decrypt_verify */
     NULL,              /* edit */
@@ -1975,6 +2200,9 @@ struct engine_ops _gpgme_engine_ops_gpgsm =
     gpgsm_import,
     gpgsm_keylist,
     gpgsm_keylist_ext,
+    NULL,               /* keylist_data */
+    NULL,               /* keysign */
+    NULL,               /* tofu_policy */
     gpgsm_sign,
     NULL,              /* trustlist */
     gpgsm_verify,
@@ -1982,10 +2210,13 @@ struct engine_ops _gpgme_engine_ops_gpgsm =
     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_passwd,
-    NULL                /* set_pinentry_mode */
+    NULL,               /* set_pinentry_mode */
+    NULL                /* opspawn */
   };