gpgme-tool: Fix segv for external key listing.
[gpgme.git] / src / engine-gpg.c
index 7cf8894..4df0f3e 100644 (file)
@@ -1,7 +1,7 @@
 /* engine-gpg.c - Gpg Engine.
    Copyright (C) 2000 Werner Koch (dd9jn)
    Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007,
-                 2009, 2010, 2012 g10 Code GmbH
+                 2009, 2010, 2012, 2013 g10 Code GmbH
 
    This file is part of GPGME.
 
@@ -134,6 +134,7 @@ struct engine_gpg
   } cmd;
 
   struct gpgme_io_cbs io_cbs;
+  gpgme_pinentry_mode_t pinentry_mode;
 };
 
 typedef struct engine_gpg *engine_gpg_t;
@@ -210,7 +211,7 @@ _add_arg (engine_gpg_t gpg, const char *arg, int front, int *arg_locp)
 
   a = malloc (sizeof *a + strlen (arg));
   if (!a)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
 
   a->data = NULL;
   a->dup_to = -1;
@@ -269,7 +270,7 @@ add_data (engine_gpg_t gpg, gpgme_data_t data, int dup_to, int inbound)
 
   a = malloc (sizeof *a - 1);
   if (!a)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
   a->next = NULL;
   a->data = data;
   a->inbound = inbound;
@@ -295,14 +296,14 @@ static char *
 gpg_get_version (const char *file_name)
 {
   return _gpgme_get_program_version (file_name ? file_name
-                                    : _gpgme_get_gpg_path ());
+                                    : _gpgme_get_default_gpg_name ());
 }
 
 
 static const char *
 gpg_get_req_version (void)
 {
-  return NEED_GPG_VERSION;
+  return "1.4.0";
 }
 
 
@@ -424,14 +425,14 @@ gpg_new (void **engine, const char *file_name, const char *home_dir)
 
   gpg = calloc (1, sizeof *gpg);
   if (!gpg)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
 
   if (file_name)
     {
       gpg->file_name = strdup (file_name);
       if (!gpg->file_name)
        {
-         rc = gpg_error_from_errno (errno);
+         rc = gpg_error_from_syserror ();
          goto leave;
        }
     }
@@ -452,14 +453,14 @@ gpg_new (void **engine, const char *file_name, const char *home_dir)
   gpg->status.buffer = malloc (gpg->status.bufsize);
   if (!gpg->status.buffer)
     {
-      rc = gpg_error_from_errno (errno);
+      rc = gpg_error_from_syserror ();
       goto leave;
     }
   /* In any case we need a status pipe - create it right here and
      don't handle it with our generic gpgme_data_t mechanism.  */
   if (_gpgme_io_pipe (gpg->status.fd, 1) == -1)
     {
-      rc = gpg_error_from_errno (errno);
+      rc = gpg_error_from_syserror ();
       goto leave;
     }
   if (_gpgme_io_set_close_notify (gpg->status.fd[0],
@@ -630,14 +631,14 @@ gpg_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
   gpg->colon.readpos = 0;
   gpg->colon.buffer = malloc (gpg->colon.bufsize);
   if (!gpg->colon.buffer)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
 
   if (_gpgme_io_pipe (gpg->colon.fd, 1) == -1)
     {
-      int saved_errno = errno;
+      int saved_err = gpg_error_from_syserror ();
       free (gpg->colon.buffer);
       gpg->colon.buffer = NULL;
-      return gpg_error_from_errno (saved_errno);
+      return saved_err;
     }
   if (_gpgme_io_set_close_notify (gpg->colon.fd[0], close_notify_handler, gpg)
       || _gpgme_io_set_close_notify (gpg->colon.fd[1],
@@ -717,7 +718,7 @@ gpg_set_command_handler (void *engine, engine_command_handler_t fnc,
 
 
 static gpgme_error_t
-build_argv (engine_gpg_t gpg)
+build_argv (engine_gpg_t gpg, const char *pgmname)
 {
   gpgme_error_t err;
   struct arg_and_data_s *a;
@@ -728,15 +729,20 @@ build_argv (engine_gpg_t gpg)
   int use_agent = 0;
   char *p;
 
-  /* We don't want to use the agent with a malformed environment
-     variable.  This is only a very basic test but sufficient to make
-     our life in the regression tests easier. */
-  err = _gpgme_getenv ("GPG_AGENT_INFO", &p);
-  if (err)
-    return err;
-  use_agent = (p && strchr (p, ':'));
-  if (p)
-    free (p);
+  if (_gpgme_in_gpg_one_mode ())
+    {
+      /* In GnuPG-1 mode we don't want to use the agent with a
+         malformed environment variable.  This is only a very basic
+         test but sufficient to make our life in the regression tests
+         easier.  With GnuPG-2 the agent is anyway required and on
+         modern installations GPG_AGENT_INFO is optional.  */
+      err = _gpgme_getenv ("GPG_AGENT_INFO", &p);
+      if (err)
+        return err;
+      use_agent = (p && strchr (p, ':'));
+      if (p)
+        free (p);
+    }
 
   if (gpg->argv)
     {
@@ -769,29 +775,31 @@ build_argv (engine_gpg_t gpg)
     argc++;
   if (use_agent)
     argc++;
+  if (gpg->pinentry_mode)
+    argc++;
   if (!gpg->cmd.used)
     argc++;    /* --batch */
   argc += 1;   /* --no-sk-comment */
 
   argv = calloc (argc + 1, sizeof *argv);
   if (!argv)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
   fd_data_map = calloc (datac + 1, sizeof *fd_data_map);
   if (!fd_data_map)
     {
-      int saved_errno = errno;
+      int saved_err = gpg_error_from_syserror ();
       free_argv (argv);
-      return gpg_error_from_errno (saved_errno);
+      return saved_err;
     }
 
   argc = datac = 0;
-  argv[argc] = strdup ("gpg"); /* argv[0] */
+  argv[argc] = strdup (_gpgme_get_basename (pgmname)); /* argv[0] */
   if (!argv[argc])
     {
-      int saved_errno = errno;
+      int saved_err = gpg_error_from_syserror ();
       free (fd_data_map);
       free_argv (argv);
-      return gpg_error_from_errno (saved_errno);
+      return saved_err;
     }
   argc++;
   if (need_special)
@@ -799,10 +807,10 @@ build_argv (engine_gpg_t gpg)
       argv[argc] = strdup ("--enable-special-filenames");
       if (!argv[argc])
        {
-         int saved_errno = errno;
+          int saved_err = gpg_error_from_syserror ();
          free (fd_data_map);
          free_argv (argv);
-         return gpg_error_from_errno (saved_errno);
+         return saved_err;
         }
       argc++;
     }
@@ -811,32 +819,58 @@ build_argv (engine_gpg_t gpg)
       argv[argc] = strdup ("--use-agent");
       if (!argv[argc])
        {
-         int saved_errno = errno;
+          int saved_err = gpg_error_from_syserror ();
          free (fd_data_map);
          free_argv (argv);
-         return gpg_error_from_errno (saved_errno);
+         return saved_err;
         }
       argc++;
     }
+
+  if (gpg->pinentry_mode)
+    {
+      const char *s = NULL;
+      switch (gpg->pinentry_mode)
+        {
+        case GPGME_PINENTRY_MODE_DEFAULT: break;
+        case GPGME_PINENTRY_MODE_ASK:     s = "--pinentry-mode=ask"; break;
+        case GPGME_PINENTRY_MODE_CANCEL:  s = "--pinentry-mode=cancel"; break;
+        case GPGME_PINENTRY_MODE_ERROR:   s = "--pinentry-mode=error"; break;
+        case GPGME_PINENTRY_MODE_LOOPBACK:s = "--pinentry-mode=loopback"; break;
+        }
+      if (s)
+        {
+          argv[argc] = strdup (s);
+          if (!argv[argc])
+            {
+              int saved_err = gpg_error_from_syserror ();
+              free (fd_data_map);
+              free_argv (argv);
+              return saved_err;
+            }
+          argc++;
+        }
+    }
+
   if (!gpg->cmd.used)
     {
       argv[argc] = strdup ("--batch");
       if (!argv[argc])
        {
-         int saved_errno = errno;
+          int saved_err = gpg_error_from_syserror ();
          free (fd_data_map);
          free_argv (argv);
-         return gpg_error_from_errno (saved_errno);
+         return saved_err;
         }
       argc++;
     }
   argv[argc] = strdup ("--no-sk-comment");
   if (!argv[argc])
     {
-      int saved_errno = errno;
+      int saved_err = gpg_error_from_syserror ();
       free (fd_data_map);
       free_argv (argv);
-      return gpg_error_from_errno (saved_errno);
+      return saved_err;
     }
   argc++;
   for (a = gpg->arglist; a; a = a->next)
@@ -867,6 +901,10 @@ build_argv (engine_gpg_t gpg)
                                               close_notify_handler,
                                               gpg))
              {
+                /* We leak fd_data_map and the fds.  This is not easy
+                   to avoid and given that we reach this here only
+                   after a malloc failure for a small object, it is
+                   probably better not to do anything.  */
                return gpg_error (GPG_ERR_GENERAL);
              }
            /* If the data_type is FD, we have to do a dup2 here.  */
@@ -908,10 +946,10 @@ build_argv (engine_gpg_t gpg)
              argv[argc] = malloc (buflen);
              if (!argv[argc])
                {
-                 int saved_errno = errno;
+                  int saved_err = gpg_error_from_syserror ();
                  free (fd_data_map);
                  free_argv (argv);
-                 return gpg_error_from_errno (saved_errno);
+                 return saved_err;
                 }
 
              ptr = argv[argc];
@@ -933,10 +971,10 @@ build_argv (engine_gpg_t gpg)
          argv[argc] = strdup (a->arg);
          if (!argv[argc])
            {
-             int saved_errno = errno;
+              int saved_err = gpg_error_from_syserror ();
              free (fd_data_map);
              free_argv (argv);
-             return gpg_error_from_errno (saved_errno);
+             return saved_err;
             }
             argc++;
         }
@@ -987,13 +1025,13 @@ read_status (engine_gpg_t gpg)
       bufsize += 1024;
       buffer = realloc (buffer, bufsize);
       if (!buffer)
-       return gpg_error_from_errno (errno);
+       return gpg_error_from_syserror ();
     }
 
   nread = _gpgme_io_read (gpg->status.fd[0],
                          buffer + readpos, bufsize-readpos);
   if (nread == -1)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
 
   if (!nread)
     {
@@ -1043,7 +1081,7 @@ read_status (engine_gpg_t gpg)
                            free (gpg->cmd.keyword);
                          gpg->cmd.keyword = strdup (rest);
                          if (!gpg->cmd.keyword)
-                           return gpg_error_from_errno (errno);
+                           return gpg_error_from_syserror ();
                          /* This should be the last thing we have
                             received and the next thing will be that
                             the command handler does its action.  */
@@ -1163,12 +1201,12 @@ read_colon_line (engine_gpg_t gpg)
       bufsize += 1024;
       buffer = realloc (buffer, bufsize);
       if (!buffer)
-       return gpg_error_from_errno (errno);
+       return gpg_error_from_syserror ();
     }
 
   nread = _gpgme_io_read (gpg->colon.fd[0], buffer+readpos, bufsize-readpos);
   if (nread == -1)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
 
   if (!nread)
     {
@@ -1259,17 +1297,17 @@ static gpgme_error_t
 start (engine_gpg_t gpg)
 {
   gpgme_error_t rc;
-  int saved_errno;
   int i, n;
   int status;
   struct spawn_fd_item_s *fd_list;
   pid_t pid;
+  const char *pgmname;
 
   if (!gpg)
     return gpg_error (GPG_ERR_INV_VALUE);
 
-  if (!gpg->file_name && !_gpgme_get_gpg_path ())
-    return gpg_error (GPG_ERR_INV_ENGINE);
+  if (!gpg->file_name && !_gpgme_get_default_gpg_name ())
+    return trace_gpg_error (GPG_ERR_INV_ENGINE);
 
   if (gpg->lc_ctype)
     {
@@ -1289,7 +1327,8 @@ start (engine_gpg_t gpg)
        return rc;
     }
 
-  rc = build_argv (gpg);
+  pgmname = gpg->file_name ? gpg->file_name : _gpgme_get_default_gpg_name ();
+  rc = build_argv (gpg, pgmname);
   if (rc)
     return rc;
 
@@ -1299,7 +1338,7 @@ start (engine_gpg_t gpg)
     n++;
   fd_list = calloc (n, sizeof *fd_list);
   if (! fd_list)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
 
   /* Build the fd list for the child.  */
   n = 0;
@@ -1323,15 +1362,15 @@ start (engine_gpg_t gpg)
   fd_list[n].fd = -1;
   fd_list[n].dup_to = -1;
 
-  status = _gpgme_io_spawn (gpg->file_name ? gpg->file_name :
-                           _gpgme_get_gpg_path (), gpg->argv,
-                            IOSPAWN_FLAG_ALLOW_SET_FG,
+  status = _gpgme_io_spawn (pgmname, gpg->argv,
+                            (IOSPAWN_FLAG_DETACHED |IOSPAWN_FLAG_ALLOW_SET_FG),
                             fd_list, NULL, NULL, &pid);
-  saved_errno = errno;
-
-  free (fd_list);
-  if (status == -1)
-    return gpg_error_from_errno (saved_errno);
+  {
+    int saved_err = gpg_error_from_syserror ();
+    free (fd_list);
+    if (status == -1)
+      return saved_err;
+  }
 
   /*_gpgme_register_term_handler ( closure, closure_value, pid );*/
 
@@ -1495,7 +1534,7 @@ append_args_from_sig_notations (engine_gpg_t gpg, gpgme_ctx_t ctx /* FIXME */)
 
          arg = malloc (1 + notation->name_len + 1 + notation->value_len + 1);
          if (!arg)
-           err = gpg_error_from_errno (errno);
+           err = gpg_error_from_syserror ();
 
          if (!err)
            {
@@ -1531,7 +1570,7 @@ append_args_from_sig_notations (engine_gpg_t gpg, gpgme_ctx_t ctx /* FIXME */)
            {
              value = malloc (1 + notation->value_len + 1);
              if (!value)
-               err = gpg_error_from_errno (errno);
+               err = gpg_error_from_syserror ();
              else
                {
                  value[0] = '!';
@@ -1623,6 +1662,9 @@ gpg_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
   if (!err && use_armor)
     err = add_arg (gpg, "--armor");
 
+  if (!err && (flags & GPGME_ENCRYPT_NO_COMPRESS))
+    err = add_arg (gpg, "--compress-algo=none");
+
   if (!symmetric)
     {
       /* If we know that all recipients are valid (full or ultimate trust)
@@ -1671,23 +1713,35 @@ gpg_encrypt_sign (void *engine, gpgme_key_t recp[],
 {
   engine_gpg_t gpg = engine;
   gpgme_error_t err;
+  int symmetric = !recp;
+
+  err = add_arg (gpg, symmetric ? "--symmetric" : "--encrypt");
 
-  err = add_arg (gpg, "--encrypt");
   if (!err)
     err = add_arg (gpg, "--sign");
   if (!err && use_armor)
     err = add_arg (gpg, "--armor");
 
-  /* If we know that all recipients are valid (full or ultimate trust)
-     we can suppress further checks.  */
-  if (!err && (flags & GPGME_ENCRYPT_ALWAYS_TRUST))
-    err = add_arg (gpg, "--always-trust");
+  if (!err && (flags & GPGME_ENCRYPT_NO_COMPRESS))
+    err = add_arg (gpg, "--compress-algo=none");
 
-  if (!err)
-    err = append_args_from_recipients (gpg, recp);
+  if (!symmetric)
+    {
+      /* If we know that all recipients are valid (full or ultimate trust)
+        we can suppress further checks.  */
+      if (!err && (flags & GPGME_ENCRYPT_ALWAYS_TRUST))
+       err = add_arg (gpg, "--always-trust");
+
+      if (!err && (flags & GPGME_ENCRYPT_NO_ENCRYPT_TO))
+       err = add_arg (gpg, "--no-encrypt-to");
+
+      if (!err)
+       err = append_args_from_recipients (gpg, recp);
+    }
 
   if (!err)
     err = append_args_from_signers (gpg, ctx);
+
   if (!err)
     err = append_args_from_sig_notations (gpg, ctx);
 
@@ -2061,7 +2115,7 @@ gpg_keylist_preprocess (char *line, char **r_line)
       if (asprintf (r_line, "pub:o%s:%s:%s:%s:%s:%s::::::::",
                    field[6], field[3], field[2], field[1],
                    field[4], field[5]) < 0)
-       return gpg_error_from_errno (errno);
+       return gpg_error_from_syserror ();
       return 0;
 
     case RT_UID:
@@ -2086,7 +2140,7 @@ gpg_keylist_preprocess (char *line, char **r_line)
        char *dst;
 
        if (! uid)
-         return gpg_error_from_errno (errno);
+         return gpg_error_from_syserror ();
        src = field[1];
        dst = uid;
        while (*src)
@@ -2114,7 +2168,7 @@ gpg_keylist_preprocess (char *line, char **r_line)
 
        if (asprintf (r_line, "uid:o%s::::%s:%s:::%s:",
                      field[4], field[2], field[3], uid) < 0)
-         return gpg_error_from_errno (errno);
+         return gpg_error_from_syserror ();
       }
       return 0;
 
@@ -2140,6 +2194,8 @@ gpg_keylist_build_options (engine_gpg_t gpg, int secret_only,
     err = add_arg (gpg, "--with-fingerprint");
   if (!err)
     err = add_arg (gpg, "--with-fingerprint");
+  if (!err && (mode & GPGME_KEYLIST_MODE_WITH_SECRET))
+    err = add_arg (gpg, "--with-secret");
   if (!err
       && (mode & GPGME_KEYLIST_MODE_SIGS)
       && (mode & GPGME_KEYLIST_MODE_SIG_NOTATIONS))
@@ -2348,11 +2404,22 @@ gpg_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
   gpg->io_cbs = *io_cbs;
 }
 
+
+static gpgme_error_t
+gpg_set_pinentry_mode (void *engine, gpgme_pinentry_mode_t mode)
+{
+  engine_gpg_t gpg = engine;
+
+  gpg->pinentry_mode = mode;
+  return 0;
+}
+
+
 \f
 struct engine_ops _gpgme_engine_ops_gpg =
   {
     /* Static functions.  */
-    _gpgme_get_gpg_path,
+    _gpgme_get_default_gpg_name,
     NULL,
     gpg_get_version,
     gpg_get_req_version,
@@ -2389,5 +2456,7 @@ struct engine_ops _gpgme_engine_ops_gpg =
     gpg_io_event,
     gpg_cancel,
     NULL,              /* cancel_op */
-    gpg_passwd
+    gpg_passwd,
+    gpg_set_pinentry_mode,
+    NULL                /* opspawn */
   };