[svn] gpgme - r1352 - trunk/src

svn author wk cvs at cvs.gnupg.org
Mon Jan 19 15:44:14 CET 2009


Author: wk
Date: 2009-01-19 15:44:13 +0100 (Mon, 19 Jan 2009)
New Revision: 1352

Added:
   trunk/src/engine-gpg.c
Removed:
   trunk/src/rungpg.c
Modified:
   trunk/src/ChangeLog
   trunk/src/Makefile.am
Log:
Renamed rungpg.c to engine-gpg.c for conistency.


[The diff below has been truncated]

Modified: trunk/src/ChangeLog
===================================================================
--- trunk/src/ChangeLog	2008-12-08 19:28:36 UTC (rev 1351)
+++ trunk/src/ChangeLog	2009-01-19 14:44:13 UTC (rev 1352)
@@ -1,3 +1,8 @@
+2009-01-19  Werner Koch  <wk at g10code.com>
+
+	* rungpg.c: Rename to engine-gpg.c
+	* Makefile.am (main_sources): Ditto.
+
 2008-12-03  Marcus Brinkmann  <marcus at g10code.de>
 
 	* Makefile.am (status-table.h): Use $(builddir) to find gpgme.h.
@@ -5893,7 +5898,7 @@
 	* data.c (gpgme_data_rewind): Allow to rewind data_type_none.
 
 
- Copyright 2001,2002,2003,2004,2005,2006,2007 g10 Code GmbH
+ Copyright 2001,2002,2003,2004,2005,2006,2007,2008,2009 g10 Code GmbH
 
  This file is free software; as a special exception the author gives
  unlimited permission to copy and/or distribute it, with or without

Modified: trunk/src/Makefile.am
===================================================================
--- trunk/src/Makefile.am	2008-12-08 19:28:36 UTC (rev 1351)
+++ trunk/src/Makefile.am	2009-01-19 14:44:13 UTC (rev 1352)
@@ -105,7 +105,7 @@
 	sign.c passphrase.c progress.c					\
 	key.c keylist.c trust-item.c trustlist.c			\
 	import.c export.c genkey.c delete.c edit.c getauditlog.c        \
-	engine.h engine-backend.h engine.c rungpg.c status-table.h	\
+	engine.h engine-backend.h engine.c engine-gpg.c status-table.h	\
 	$(gpgsm_components) $(gpgconf_components) gpgconf.c		\
 	sema.h priv-io.h $(system_components)				\
 	debug.c debug.h gpgme.c version.c error.c

Copied: trunk/src/engine-gpg.c (from rev 1351, trunk/src/rungpg.c)
===================================================================
--- trunk/src/engine-gpg.c	                        (rev 0)
+++ trunk/src/engine-gpg.c	2009-01-19 14:44:13 UTC (rev 1352)
@@ -0,0 +1,2192 @@
+/* engine-gpg.c - Gpg Engine.
+   Copyright (C) 2000 Werner Koch (dd9jn)
+   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007,
+                 2009 g10 Code GmbH
+ 
+   This file is part of GPGME.
+ 
+   GPGME is free software; you can redistribute it and/or modify it
+   under the terms of the GNU Lesser General Public License as
+   published by the Free Software Foundation; either version 2.1 of
+   the License, or (at your option) any later version.
+   
+   GPGME is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+   
+   You should have received a copy of the GNU Lesser General Public
+   License along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <unistd.h>
+#include <locale.h>
+
+#include "gpgme.h"
+#include "util.h"
+#include "ops.h"
+#include "wait.h"
+#include "context.h"  /*temp hack until we have GpmeData methods to do I/O */
+#include "priv-io.h"
+#include "sema.h"
+#include "debug.h"
+
+#include "status-table.h"
+#include "engine-backend.h"
+
+
+/* This type is used to build a list of gpg arguments and data
+   sources/sinks.  */
+struct arg_and_data_s
+{
+  struct arg_and_data_s *next;
+  gpgme_data_t data;  /* If this is not NULL, use arg below.  */
+  int inbound;     /* True if this is used for reading from gpg.  */
+  int dup_to;
+  int print_fd;    /* Print the fd number and not the special form of it.  */
+  int *arg_locp;   /* Write back the argv idx of this argument when
+		      building command line to this location.  */
+  char arg[1];     /* Used if data above is not used.  */
+};
+
+
+struct fd_data_map_s
+{
+  gpgme_data_t data;
+  int inbound;  /* true if this is used for reading from gpg */
+  int dup_to;
+  int fd;       /* the fd to use */
+  int peer_fd;  /* the other side of the pipe */
+  int arg_loc;  /* The index into the argv for translation purposes.  */
+  void *tag;
+};
+
+
+typedef gpgme_error_t (*colon_preprocessor_t) (char *line, char **rline);
+
+struct engine_gpg
+{
+  char *file_name;
+
+  char *lc_messages;
+  char *lc_ctype;
+
+  struct arg_and_data_s *arglist;
+  struct arg_and_data_s **argtail;
+
+  struct
+  {
+    int fd[2];  
+    int arg_loc;
+    size_t bufsize;
+    char *buffer;
+    size_t readpos;
+    int eof;
+    engine_status_handler_t fnc;
+    void *fnc_value;
+    void *tag;
+  } status;
+
+  /* This is a kludge - see the comment at colon_line_handler.  */
+  struct
+  {
+    int fd[2];  
+    int arg_loc;
+    size_t bufsize;
+    char *buffer;
+    size_t readpos;
+    int eof;
+    engine_colon_line_handler_t fnc;  /* this indicate use of this structrue */
+    void *fnc_value;
+    void *tag;
+    colon_preprocessor_t preprocess_fnc;
+  } colon;
+
+  char **argv;  
+  struct fd_data_map_s *fd_data_map;
+
+  /* stuff needed for interactive (command) mode */
+  struct
+  {
+    int used;
+    int fd;
+    void *cb_data;
+    int idx;		/* Index in fd_data_map */
+    gpgme_status_code_t code;  /* last code */
+    char *keyword;       /* what has been requested (malloced) */
+    engine_command_handler_t fnc; 
+    void *fnc_value;
+    /* The kludges never end.  This is used to couple command handlers
+       with output data in edit key mode.  */
+    gpgme_data_t linked_data;
+    int linked_idx;
+  } cmd;
+
+  struct gpgme_io_cbs io_cbs;
+};
+
+typedef struct engine_gpg *engine_gpg_t;
+
+
+static void
+gpg_io_event (void *engine, gpgme_event_io_t type, void *type_data)
+{
+  engine_gpg_t gpg = engine;
+
+  TRACE3 (DEBUG_ENGINE, "gpgme:gpg_io_event", gpg,
+          "event %p, type %d, type_data %p",
+          gpg->io_cbs.event, type, type_data);
+  if (gpg->io_cbs.event)
+    (*gpg->io_cbs.event) (gpg->io_cbs.event_priv, type, type_data);
+}
+
+
+static void
+close_notify_handler (int fd, void *opaque)
+{
+  engine_gpg_t gpg = opaque;
+  assert (fd != -1);
+
+  if (gpg->status.fd[0] == fd)
+    {
+      if (gpg->status.tag)
+	(*gpg->io_cbs.remove) (gpg->status.tag);
+      gpg->status.fd[0] = -1;
+    }
+  else if (gpg->status.fd[1] == fd)
+    gpg->status.fd[1] = -1;
+  else if (gpg->colon.fd[0] == fd)
+    {
+      if (gpg->colon.tag)
+	(*gpg->io_cbs.remove) (gpg->colon.tag);
+      gpg->colon.fd[0] = -1;
+    }
+  else if (gpg->colon.fd[1] == fd)
+    gpg->colon.fd[1] = -1;
+  else if (gpg->fd_data_map)
+    {
+      int i;
+
+      for (i = 0; gpg->fd_data_map[i].data; i++)
+	{
+	  if (gpg->fd_data_map[i].fd == fd)
+	    {
+	      if (gpg->fd_data_map[i].tag)
+		(*gpg->io_cbs.remove) (gpg->fd_data_map[i].tag);
+	      gpg->fd_data_map[i].fd = -1;
+	      break;
+            }
+	  if (gpg->fd_data_map[i].peer_fd == fd)
+	    {
+	      gpg->fd_data_map[i].peer_fd = -1;
+	      break;
+            }
+        }
+    }
+}
+
+/* If FRONT is true, push at the front of the list.  Use this for
+   options added late in the process.  */
+static gpgme_error_t
+_add_arg (engine_gpg_t gpg, const char *arg, int front, int *arg_locp)
+{
+  struct arg_and_data_s *a;
+
+  assert (gpg);
+  assert (arg);
+
+  a = malloc (sizeof *a + strlen (arg));
+  if (!a)
+    return gpg_error_from_errno (errno);
+
+  a->data = NULL;
+  a->dup_to = -1;
+  a->arg_locp = arg_locp;
+
+  strcpy (a->arg, arg);
+  if (front)
+    {
+      a->next = gpg->arglist;
+      if (!gpg->arglist)
+	{
+	  /* If this is the first argument, we need to update the tail
+	     pointer.  */
+	  gpg->argtail = &a->next;
+	}
+      gpg->arglist = a;
+    }
+  else
+    {
+      a->next = NULL;
+      *gpg->argtail = a;
+      gpg->argtail = &a->next;
+    }
+
+  return 0;
+}
+
+static gpgme_error_t
+add_arg_ext (engine_gpg_t gpg, const char *arg, int front)
+{
+  return _add_arg (gpg, arg, front, NULL);
+}
+
+
+static gpgme_error_t
+add_arg_with_locp (engine_gpg_t gpg, const char *arg, int *locp)
+{
+  return _add_arg (gpg, arg, 0, locp);
+}
+
+
+static gpgme_error_t
+add_arg (engine_gpg_t gpg, const char *arg)
+{
+  return add_arg_ext (gpg, arg, 0);
+}
+
+
+static gpgme_error_t
+add_data (engine_gpg_t gpg, gpgme_data_t data, int dup_to, int inbound)
+{
+  struct arg_and_data_s *a;
+
+  assert (gpg);
+  assert (data);
+
+  a = malloc (sizeof *a - 1);
+  if (!a)
+    return gpg_error_from_errno (errno);
+  a->next = NULL;
+  a->data = data;
+  a->inbound = inbound;
+  a->arg_locp = NULL;
+
+  if (dup_to == -2)
+    {
+      a->print_fd = 1;
+      a->dup_to = -1;
+    }
+  else
+    {
+      a->print_fd = 0;
+      a->dup_to = dup_to;
+    }
+  *gpg->argtail = a;
+  gpg->argtail = &a->next;
+  return 0;
+}
+
+
+static char *
+gpg_get_version (const char *file_name)
+{
+  return _gpgme_get_program_version (file_name ? file_name
+				     : _gpgme_get_gpg_path ());
+}
+
+
+static const char *
+gpg_get_req_version (void)
+{
+  return NEED_GPG_VERSION;
+}
+
+
+static void
+free_argv (char **argv)
+{
+  int i;
+
+  for (i = 0; argv[i]; i++)
+    free (argv[i]);
+  free (argv);
+}
+
+
+static void
+free_fd_data_map (struct fd_data_map_s *fd_data_map)
+{
+  int i;
+
+  if (!fd_data_map)
+    return;
+
+  for (i = 0; fd_data_map[i].data; i++)
+    {
+      if (fd_data_map[i].fd != -1)
+	_gpgme_io_close (fd_data_map[i].fd);
+      if (fd_data_map[i].peer_fd != -1)
+	_gpgme_io_close (fd_data_map[i].peer_fd);
+      /* Don't release data because this is only a reference.  */
+    }
+  free (fd_data_map);
+}
+
+
+static gpgme_error_t
+gpg_cancel (void *engine)
+{
+  engine_gpg_t gpg = engine;
+
+  if (!gpg)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  /* If gpg may be waiting for a cmd, close the cmd fd first.  On
+     Windows, close operations block on the reader/writer thread.  */
+  if (gpg->cmd.used)
+    {
+      if (gpg->cmd.fd != -1)
+	_gpgme_io_close (gpg->cmd.fd);
+      else if (gpg->fd_data_map
+	       && gpg->fd_data_map[gpg->cmd.idx].fd != -1)
+	_gpgme_io_close (gpg->fd_data_map[gpg->cmd.idx].fd);
+    }
+
+  if (gpg->status.fd[0] != -1)
+    _gpgme_io_close (gpg->status.fd[0]);
+  if (gpg->status.fd[1] != -1)
+    _gpgme_io_close (gpg->status.fd[1]);
+  if (gpg->colon.fd[0] != -1)
+    _gpgme_io_close (gpg->colon.fd[0]);
+  if (gpg->colon.fd[1] != -1)
+    _gpgme_io_close (gpg->colon.fd[1]);
+  if (gpg->fd_data_map)
+    {
+      free_fd_data_map (gpg->fd_data_map);
+      gpg->fd_data_map = NULL;
+    }
+
+  return 0;
+}
+
+static void
+gpg_release (void *engine)
+{
+  engine_gpg_t gpg = engine;
+
+  if (!gpg)
+    return;
+
+  gpg_cancel (engine);
+
+  if (gpg->file_name)
+    free (gpg->file_name);
+
+  if (gpg->lc_messages)
+    free (gpg->lc_messages);
+  if (gpg->lc_ctype)
+    free (gpg->lc_ctype);
+
+  while (gpg->arglist)
+    {
+      struct arg_and_data_s *next = gpg->arglist->next;
+
+      if (gpg->arglist)
+	free (gpg->arglist);
+      gpg->arglist = next;
+    }
+
+  if (gpg->status.buffer)
+    free (gpg->status.buffer);
+  if (gpg->colon.buffer)
+    free (gpg->colon.buffer);
+  if (gpg->argv)
+    free_argv (gpg->argv);
+  if (gpg->cmd.keyword)
+    free (gpg->cmd.keyword);
+
+  free (gpg);
+}
+
+
+static gpgme_error_t
+gpg_new (void **engine, const char *file_name, const char *home_dir)
+{
+  engine_gpg_t gpg;
+  gpgme_error_t rc = 0;
+  char *dft_display = NULL;
+  char dft_ttyname[64];
+  char *dft_ttytype = NULL;
+
+  gpg = calloc (1, sizeof *gpg);
+  if (!gpg)
+    return gpg_error_from_errno (errno);
+
+  if (file_name)
+    {
+      gpg->file_name = strdup (file_name);
+      if (!gpg->file_name)
+	{
+	  rc = gpg_error_from_errno (errno);
+	  goto leave;
+	}
+    }
+
+  gpg->argtail = &gpg->arglist;
+  gpg->status.fd[0] = -1;
+  gpg->status.fd[1] = -1;
+  gpg->colon.fd[0] = -1;
+  gpg->colon.fd[1] = -1;
+  gpg->cmd.fd = -1;
+  gpg->cmd.idx = -1;
+  gpg->cmd.linked_data = NULL;
+  gpg->cmd.linked_idx = -1;
+
+  /* Allocate the read buffer for the status pipe.  */
+  gpg->status.bufsize = 1024;
+  gpg->status.readpos = 0;
+  gpg->status.buffer = malloc (gpg->status.bufsize);
+  if (!gpg->status.buffer)
+    {
+      rc = gpg_error_from_errno (errno);
+      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);
+      goto leave;
+    }
+  if (_gpgme_io_set_close_notify (gpg->status.fd[0],
+				  close_notify_handler, gpg)
+      || _gpgme_io_set_close_notify (gpg->status.fd[1],
+				     close_notify_handler, gpg))
+    {
+      rc = gpg_error (GPG_ERR_GENERAL);
+      goto leave;
+    }
+  gpg->status.eof = 0;
+
+  if (home_dir)
+    {
+      rc = add_arg (gpg, "--homedir");
+      if (!rc)
+	rc = add_arg (gpg, home_dir);
+      if (rc)
+	goto leave;
+    }
+
+  rc = add_arg (gpg, "--status-fd");
+  if (rc)
+    goto leave;
+
+  {
+    char buf[25];
+    _gpgme_io_fd2str (buf, sizeof (buf), gpg->status.fd[1]);
+    rc = add_arg_with_locp (gpg, buf, &gpg->status.arg_loc);
+    if (rc)
+      goto leave;
+  }
+
+  rc = add_arg (gpg, "--no-tty");
+  if (!rc)
+    rc = add_arg (gpg, "--charset");
+  if (!rc)
+    rc = add_arg (gpg, "utf8");
+  if (!rc)
+    rc = add_arg (gpg, "--enable-progress-filter");
+  if (rc)
+    goto leave;
+
+  rc = _gpgme_getenv ("DISPLAY", &dft_display);
+  if (rc)
+    goto leave;
+  if (dft_display)
+    {
+      rc = add_arg (gpg, "--display");
+      if (!rc)
+	rc = add_arg (gpg, dft_display);
+
+      free (dft_display);
+    }
+
+  if (isatty (1))
+    {
+      int err;
+
+      err = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
+      if (err)
+	rc = gpg_error_from_errno (err);
+      else
+	{
+          if (*dft_ttyname)
+            {
+              rc = add_arg (gpg, "--ttyname");
+              if (!rc)
+                rc = add_arg (gpg, dft_ttyname);
+            }
+          else
+            rc = 0;
+          if (!rc)
+	    {
+	      rc = _gpgme_getenv ("TERM", &dft_ttytype);
+	      if (rc)
+		goto leave;
+              
+              if (dft_ttytype)
+                {
+                  rc = add_arg (gpg, "--ttytype");
+                  if (!rc)
+                    rc = add_arg (gpg, dft_ttytype);
+                }
+
+	      free (dft_ttytype);
+	    }
+	}
+      if (rc)
+	goto leave;
+    }
+
+ leave:
+  if (rc)
+    gpg_release (gpg);
+  else
+    *engine = gpg;
+  return rc;
+}
+
+
+static gpgme_error_t
+gpg_set_locale (void *engine, int category, const char *value)
+{
+  engine_gpg_t gpg = engine;
+
+  if (category == LC_CTYPE)
+    {
+      if (gpg->lc_ctype)
+        {
+          free (gpg->lc_ctype);
+          gpg->lc_ctype = NULL;
+        }
+      if (value)
+	{
+	  gpg->lc_ctype = strdup (value);
+	  if (!gpg->lc_ctype)
+	    return gpg_error_from_syserror ();
+	}
+    }
+#ifdef LC_MESSAGES
+  else if (category == LC_MESSAGES)
+    {
+      if (gpg->lc_messages)
+        {
+          free (gpg->lc_messages);
+          gpg->lc_messages = NULL;
+        }
+      if (value)
+	{
+	  gpg->lc_messages = strdup (value);
+	  if (!gpg->lc_messages)
+	    return gpg_error_from_syserror ();
+	}
+    }
+#endif /* LC_MESSAGES */
+  else
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  return 0;
+}
+
+
+/* Note, that the status_handler is allowed to modifiy the args
+   value.  */
+static void
+gpg_set_status_handler (void *engine, engine_status_handler_t fnc,
+			void *fnc_value)
+{
+  engine_gpg_t gpg = engine;
+
+  gpg->status.fnc = fnc;
+  gpg->status.fnc_value = fnc_value;
+}
+
+/* Kludge to process --with-colon output.  */
+static gpgme_error_t
+gpg_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
+			    void *fnc_value)
+{
+  engine_gpg_t gpg = engine;
+
+  gpg->colon.bufsize = 1024;
+  gpg->colon.readpos = 0;
+  gpg->colon.buffer = malloc (gpg->colon.bufsize);
+  if (!gpg->colon.buffer)
+    return gpg_error_from_errno (errno);
+
+  if (_gpgme_io_pipe (gpg->colon.fd, 1) == -1) 
+    {
+      int saved_errno = errno;
+      free (gpg->colon.buffer);
+      gpg->colon.buffer = NULL;
+      return gpg_error_from_errno (saved_errno);
+    }
+  if (_gpgme_io_set_close_notify (gpg->colon.fd[0], close_notify_handler, gpg)
+      || _gpgme_io_set_close_notify (gpg->colon.fd[1],
+				     close_notify_handler, gpg))
+    return gpg_error (GPG_ERR_GENERAL);
+  gpg->colon.eof = 0;
+  gpg->colon.fnc = fnc;
+  gpg->colon.fnc_value = fnc_value;
+  return 0;
+}
+
+
+static gpgme_error_t
+command_handler (void *opaque, int fd)
+{
+  gpgme_error_t err;
+  engine_gpg_t gpg = (engine_gpg_t) opaque;
+  int processed = 0;
+
+  assert (gpg->cmd.used);
+  assert (gpg->cmd.code);
+  assert (gpg->cmd.fnc);
+
+  err = gpg->cmd.fnc (gpg->cmd.fnc_value, gpg->cmd.code, gpg->cmd.keyword, fd,
+		      &processed);
+
+  gpg->cmd.code = 0;
+  /* And sleep again until read_status will wake us up again.  */
+  /* XXX We must check if there are any more fds active after removing
+     this one.  */
+  (*gpg->io_cbs.remove) (gpg->fd_data_map[gpg->cmd.idx].tag);
+  gpg->cmd.fd = gpg->fd_data_map[gpg->cmd.idx].fd;
+  gpg->fd_data_map[gpg->cmd.idx].fd = -1;
+
+  if (err)
+    return err;
+
+  /* We always need to send at least a newline character.  */
+  if (!processed)
+    _gpgme_io_write (fd, "\n", 1);
+
+  return 0;
+}
+
+
+
+/* The Fnc will be called to get a value for one of the commands with
+   a key KEY.  If the Code pssed to FNC is 0, the function may release
+   resources associated with the returned value from another call.  To
+   match such a second call to a first call, the returned value from
+   the first call is passed as keyword.  */
+static gpgme_error_t
+gpg_set_command_handler (void *engine, engine_command_handler_t fnc,
+			 void *fnc_value, gpgme_data_t linked_data)
+{
+  engine_gpg_t gpg = engine;
+  gpgme_error_t rc;
+
+  rc = add_arg (gpg, "--command-fd");
+  if (rc)
+    return rc;
+
+  /* This is a hack.  We don't have a real data object.  The only
+     thing that matters is that we use something unique, so we use the
+     address of the cmd structure in the gpg object.  */
+  rc = add_data (gpg, (void *) &gpg->cmd, -2, 0);
+  if (rc)
+    return rc;
+
+  gpg->cmd.fnc = fnc;
+  gpg->cmd.cb_data = (void *) &gpg->cmd;
+  gpg->cmd.fnc_value = fnc_value;
+  gpg->cmd.linked_data = linked_data;
+  gpg->cmd.used = 1;
+  return 0;
+}
+
+
+static gpgme_error_t
+build_argv (engine_gpg_t gpg)
+{
+  gpgme_error_t err;
+  struct arg_and_data_s *a;
+  struct fd_data_map_s *fd_data_map;
+  size_t datac=0, argc=0;  
+  char **argv;
+  int need_special = 0;
+  int use_agent = 0;
+  char *p;
+
+  /* We don't want to use the agent with a malformed environment
+     variable.  This is only a very basic test but sufficient to make
+     our life in the regression tests easier. */
+  err = _gpgme_getenv ("GPG_AGENT_INFO", &p);
+  if (err)
+    return err;
+  use_agent = (p && strchr (p, ':'));
+  if (p)
+    free (p);
+
+  if (gpg->argv)
+    {
+      free_argv (gpg->argv);
+      gpg->argv = NULL;
+    }
+  if (gpg->fd_data_map)
+    {
+      free_fd_data_map (gpg->fd_data_map);
+      gpg->fd_data_map = NULL;
+    }
+
+  argc++;	/* For argv[0].  */
+  for (a = gpg->arglist; a; a = a->next)
+    {
+      argc++;
+      if (a->data)
+	{
+	  /*fprintf (stderr, "build_argv: data\n" );*/
+	  datac++;
+	  if (a->dup_to == -1 && !a->print_fd)
+	    need_special = 1;
+        }
+      else
+	{
+	  /*   fprintf (stderr, "build_argv: arg=`%s'\n", a->arg );*/
+        }
+    }
+  if (need_special)
+    argc++;
+  if (use_agent)
+    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);
+  fd_data_map = calloc (datac + 1, sizeof *fd_data_map);
+  if (!fd_data_map)
+    {
+      int saved_errno = errno;
+      free_argv (argv);
+      return gpg_error_from_errno (saved_errno);
+    }
+
+  argc = datac = 0;
+  argv[argc] = strdup ("gpg"); /* argv[0] */
+  if (!argv[argc])
+    {
+      int saved_errno = errno;
+      free (fd_data_map);
+      free_argv (argv);
+      return gpg_error_from_errno (saved_errno);
+    }
+  argc++;
+  if (need_special)
+    {
+      argv[argc] = strdup ("--enable-special-filenames");
+      if (!argv[argc])
+	{
+	  int saved_errno = errno;
+	  free (fd_data_map);
+	  free_argv (argv);
+	  return gpg_error_from_errno (saved_errno);
+        }
+      argc++;
+    }
+  if (use_agent)
+    {
+      argv[argc] = strdup ("--use-agent");
+      if (!argv[argc])
+	{
+	  int saved_errno = errno;
+	  free (fd_data_map);
+	  free_argv (argv);
+	  return gpg_error_from_errno (saved_errno);
+        }
+      argc++;
+    }
+  if (!gpg->cmd.used)
+    {
+      argv[argc] = strdup ("--batch");
+      if (!argv[argc])
+	{
+	  int saved_errno = errno;
+	  free (fd_data_map);
+	  free_argv (argv);
+	  return gpg_error_from_errno (saved_errno);
+        }
+      argc++;
+    }
+  argv[argc] = strdup ("--no-sk-comment");
+  if (!argv[argc])
+    {
+      int saved_errno = errno;
+      free (fd_data_map);
+      free_argv (argv);
+      return gpg_error_from_errno (saved_errno);
+    }
+  argc++;
+  for (a = gpg->arglist; a; a = a->next)
+    {
+      if (a->arg_locp)
+	*(a->arg_locp) = argc;
+
+      if (a->data)
+	{
+	  /* Create a pipe to pass it down to gpg.  */
+	  fd_data_map[datac].inbound = a->inbound;
+
+	  /* Create a pipe.  */
+	  {   
+	    int fds[2];
+	    
+	    if (_gpgme_io_pipe (fds, fd_data_map[datac].inbound ? 1 : 0)
+		== -1)
+	      {
+		int saved_errno = errno;
+		free (fd_data_map);
+		free_argv (argv);
+		return gpg_error (saved_errno);
+	      }
+	    if (_gpgme_io_set_close_notify (fds[0],
+					    close_notify_handler, gpg)
+		|| _gpgme_io_set_close_notify (fds[1],
+					       close_notify_handler,
+					       gpg))
+	      {
+		return gpg_error (GPG_ERR_GENERAL);
+	      }
+	    /* If the data_type is FD, we have to do a dup2 here.  */
+	    if (fd_data_map[datac].inbound)
+	      {
+		fd_data_map[datac].fd       = fds[0];
+		fd_data_map[datac].peer_fd  = fds[1];
+	      }
+	    else
+	      {
+		fd_data_map[datac].fd       = fds[1];
+		fd_data_map[datac].peer_fd  = fds[0];
+	      }
+	  }
+
+	  /* Hack to get hands on the fd later.  */
+	  if (gpg->cmd.used)
+	    {
+	      if (gpg->cmd.cb_data == a->data)
+		{
+		  assert (gpg->cmd.idx == -1);
+		  gpg->cmd.idx = datac;
+		}
+	      else if (gpg->cmd.linked_data == a->data)
+		{
+		  assert (gpg->cmd.linked_idx == -1);
+		  gpg->cmd.linked_idx = datac;
+		}
+	    }
+
+	  fd_data_map[datac].data = a->data;
+	  fd_data_map[datac].dup_to = a->dup_to;
+
+	  if (a->dup_to == -1)
+	    {
+	      char *ptr;
+	      int buflen = 25;
+
+	      argv[argc] = malloc (buflen);
+	      if (!argv[argc])
+		{
+		  int saved_errno = errno;
+		  free (fd_data_map);
+		  free_argv (argv);
+		  return gpg_error_from_errno (saved_errno);
+                }
+
+	      ptr = argv[argc];
+	      if (!a->print_fd)
+		{
+		  *(ptr++) = '-';
+		  *(ptr++) = '&';
+		  buflen -= 2;
+		}
+
+	      _gpgme_io_fd2str (ptr, buflen, fd_data_map[datac].peer_fd);
+	      fd_data_map[datac].arg_loc = argc;
+	      argc++;
+            }
+	  datac++;
+        }
+      else
+	{
+	  argv[argc] = strdup (a->arg);
+	  if (!argv[argc])
+	    {
+	      int saved_errno = errno;
+	      free (fd_data_map);
+	      free_argv (argv);
+	      return gpg_error_from_errno (saved_errno);
+            }
+            argc++;
+        }
+    }
+
+  gpg->argv = argv;
+  gpg->fd_data_map = fd_data_map;
+  return 0;
+}
+
+
+static gpgme_error_t
+add_io_cb (engine_gpg_t gpg, int fd, int dir, gpgme_io_cb_t handler, void *data,
+	   void **tag)
+{
+  gpgme_error_t err;
+
+  err = (*gpg->io_cbs.add) (gpg->io_cbs.add_priv, fd, dir, handler, data, tag);
+  if (err)
+    return err;
+  if (!dir)
+    /* FIXME Kludge around poll() problem.  */
+    err = _gpgme_io_set_nonblocking (fd);
+  return err;
+}
+
+
+static int
+status_cmp (const void *ap, const void *bp)
+{
+  const struct status_table_s *a = ap;
+  const struct status_table_s *b = bp;
+
+  return strcmp (a->name, b->name);
+}
+
+
+/* Handle the status output of GnuPG.  This function does read entire
+   lines and passes them as C strings to the callback function (we can
+   use C Strings because the status output is always UTF-8 encoded).
+   Of course we have to buffer the lines to cope with long lines
+   e.g. with a large user ID.  Note: We can optimize this to only cope
+   with status line code we know about and skip all other stuff
+   without buffering (i.e. without extending the buffer).  */
+static gpgme_error_t
+read_status (engine_gpg_t gpg)
+{
+  char *p;
+  int nread;
+  size_t bufsize = gpg->status.bufsize; 
+  char *buffer = gpg->status.buffer;
+  size_t readpos = gpg->status.readpos; 
+
+  assert (buffer);
+  if (bufsize - readpos < 256)
+    { 
+      /* Need more room for the read.  */
+      bufsize += 1024;
+      buffer = realloc (buffer, bufsize);
+      if (!buffer)
+	return gpg_error_from_errno (errno);
+    }
+
+  nread = _gpgme_io_read (gpg->status.fd[0],
+			  buffer + readpos, bufsize-readpos);
+  if (nread == -1)
+    return gpg_error_from_errno (errno);
+
+  if (!nread)
+    {
+      gpg->status.eof = 1;
+      if (gpg->status.fnc)
+	{
+	  gpgme_error_t err;
+	  err = gpg->status.fnc (gpg->status.fnc_value, GPGME_STATUS_EOF, "");
+	  if (err)
+	    return err;
+	}
+      return 0;
+    }
+
+  while (nread > 0)
+    {
+      for (p = buffer + readpos; nread; nread--, p++)
+	{
+	  if (*p == '\n')
+	    {
+	      /* (we require that the last line is terminated by a LF) */
+	      if (p > buffer && p[-1] == '\r')
+		p[-1] = 0;
+	      *p = 0;
+	      if (!strncmp (buffer, "[GNUPG:] ", 9)
+		  && buffer[9] >= 'A' && buffer[9] <= 'Z')
+		{
+		  struct status_table_s t, *r;
+		  char *rest;
+
+		  rest = strchr (buffer + 9, ' ');
+		  if (!rest)
+		    rest = p; /* Set to an empty string.  */
+		  else
+		    *rest++ = 0;
+                    
+		  t.name = buffer+9;
+		  /* (the status table has one extra element) */
+		  r = bsearch (&t, status_table, DIM(status_table) - 1,
+			       sizeof t, status_cmp);
+		  if (r)
+		    {
+		      if (gpg->cmd.used
+			  && (r->code == GPGME_STATUS_GET_BOOL
+			      || r->code == GPGME_STATUS_GET_LINE
+			      || r->code == GPGME_STATUS_GET_HIDDEN))
+			{
+			  gpg->cmd.code = r->code;
+			  if (gpg->cmd.keyword)
+			    free (gpg->cmd.keyword);
+			  gpg->cmd.keyword = strdup (rest);
+			  if (!gpg->cmd.keyword)
+			    return gpg_error_from_errno (errno);
+			  /* This should be the last thing we have
+			     received and the next thing will be that
+			     the command handler does its action.  */
+			  if (nread > 1)
+			    TRACE0 (DEBUG_CTX, "gpgme:read_status", 0,
+				    "error: unexpected data");
+
+			  add_io_cb (gpg, gpg->cmd.fd, 0,
+				     command_handler, gpg,
+				     &gpg->fd_data_map[gpg->cmd.idx].tag);
+			  gpg->fd_data_map[gpg->cmd.idx].fd = gpg->cmd.fd;
+			  gpg->cmd.fd = -1;
+                        }
+		      else if (gpg->status.fnc)
+			{
+			  gpgme_error_t err;
+			  err = gpg->status.fnc (gpg->status.fnc_value, 
+						 r->code, rest);
+			  if (err)
+			    return err;
+                        }
+                    
+		      if (r->code == GPGME_STATUS_END_STREAM)
+			{
+			  if (gpg->cmd.used)
+			    {
+			      /* Before we can actually add the
+				 command fd, we might have to flush
+				 the linked output data pipe.  */
+			      if (gpg->cmd.linked_idx != -1
+				  && gpg->fd_data_map[gpg->cmd.linked_idx].fd
+				  != -1)
+				{
+				  struct io_select_fd_s fds;
+				  fds.fd =
+				    gpg->fd_data_map[gpg->cmd.linked_idx].fd;
+				  fds.for_read = 1;
+				  fds.for_write = 0;
+				  fds.opaque = NULL;
+				  do
+				    {
+				      fds.signaled = 0;
+				      _gpgme_io_select (&fds, 1, 1);
+				      if (fds.signaled)
+					_gpgme_data_inbound_handler
+					  (gpg->cmd.linked_data, fds.fd);
+				    }
+				  while (fds.signaled);
+				}
+
+			      /* XXX We must check if there are any
+				 more fds active after removing this
+				 one.  */
+			      (*gpg->io_cbs.remove)
+				(gpg->fd_data_map[gpg->cmd.idx].tag);
+			      gpg->cmd.fd = gpg->fd_data_map[gpg->cmd.idx].fd;
+			      gpg->fd_data_map[gpg->cmd.idx].fd = -1;
+			    }
+                        }
+                    }
+                }
+	      /* To reuse the buffer for the next line we have to
+		 shift the remaining data to the buffer start and
+		 restart the loop Hmmm: We can optimize this function
+		 by looking forward in the buffer to see whether a
+		 second complete line is available and in this case
+		 avoid the memmove for this line.  */
+	      nread--; p++;
+	      if (nread)
+		memmove (buffer, p, nread);
+	      readpos = 0;
+	      break; /* the for loop */
+            }
+	  else
+	    readpos++;
+        }
+    } 
+
+  /* Update the gpg object.  */
+  gpg->status.bufsize = bufsize;
+  gpg->status.buffer = buffer;
+  gpg->status.readpos = readpos;
+  return 0;
+}
+
+
+static gpgme_error_t
+status_handler (void *opaque, int fd)
+{
+  engine_gpg_t gpg = opaque;
+  int err;
+
+  assert (fd == gpg->status.fd[0]);
+  err = read_status (gpg);
+  if (err)
+    return err;
+  if (gpg->status.eof)
+    _gpgme_io_close (fd);
+  return 0;
+}
+
+
+static gpgme_error_t
+read_colon_line (engine_gpg_t gpg)
+{
+  char *p;
+  int nread;
+  size_t bufsize = gpg->colon.bufsize; 
+  char *buffer = gpg->colon.buffer;
+  size_t readpos = gpg->colon.readpos; 
+
+  assert (buffer);
+  if (bufsize - readpos < 256)
+    { 
+      /* Need more room for the read.  */
+      bufsize += 1024;
+      buffer = realloc (buffer, bufsize);
+      if (!buffer) 
+	return gpg_error_from_errno (errno);
+    }
+
+  nread = _gpgme_io_read (gpg->colon.fd[0], buffer+readpos, bufsize-readpos);
+  if (nread == -1)
+    return gpg_error_from_errno (errno);
+
+  if (!nread)
+    {
+      gpg->colon.eof = 1;
+      assert (gpg->colon.fnc);
+      gpg->colon.fnc (gpg->colon.fnc_value, NULL);
+      return 0;
+    }
+
+  while (nread > 0)
+    {
+      for (p = buffer + readpos; nread; nread--, p++)
+	{
+	  if ( *p == '\n' )
+	    {
+	      /* (we require that the last line is terminated by a LF)
+		 and we skip empty lines.  Note: we use UTF8 encoding
+		 and escaping of special characters.  We require at
+		 least one colon to cope with some other printed
+		 information.  */
+	      *p = 0;
+	      if (*buffer && strchr (buffer, ':'))
+		{
+		  char *line = NULL;
+
+		  if (gpg->colon.preprocess_fnc)
+		    {
+		      gpgme_error_t err;
+
+		      err = gpg->colon.preprocess_fnc (buffer, &line);
+		      if (err)
+			return err;
+		    }
+
+		  assert (gpg->colon.fnc);
+		  gpg->colon.fnc (gpg->colon.fnc_value, line ? line : buffer);
+		  if (line)
+		    free (line);
+		}
+            
+	      /* To reuse the buffer for the next line we have to
+		 shift the remaining data to the buffer start and
+		 restart the loop Hmmm: We can optimize this function
+		 by looking forward in the buffer to see whether a
+		 second complete line is available and in this case
+		 avoid the memmove for this line.  */
+	      nread--; p++;
+	      if (nread)
+		memmove (buffer, p, nread);
+	      readpos = 0;
+	      break; /* The for loop.  */
+            }
+	  else
+	    readpos++;
+        }
+    } 
+
+  /* Update the gpg object.  */
+  gpg->colon.bufsize = bufsize;
+  gpg->colon.buffer  = buffer;
+  gpg->colon.readpos = readpos;
+  return 0;
+}
+
+
+/* This colonline handler thing is not the clean way to do it.  It
+   might be better to enhance the gpgme_data_t object to act as a wrapper
+   for a callback.  Same goes for the status thing.  For now we use
+   this thing here because it is easier to implement.  */
+static gpgme_error_t
+colon_line_handler (void *opaque, int fd)
+{
+  engine_gpg_t gpg = opaque;
+  gpgme_error_t rc = 0;
+
+  assert (fd == gpg->colon.fd[0]);
+  rc = read_colon_line (gpg);
+  if (rc)
+    return rc;
+  if (gpg->colon.eof)
+    _gpgme_io_close (fd);
+  return 0;
+}
+
+
+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;
+
+  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->lc_ctype)
+    {
+      rc = add_arg_ext (gpg, gpg->lc_ctype, 1);
+      if (!rc)
+	rc = add_arg_ext (gpg, "--lc-ctype", 1);
+      if (rc)
+	return rc;
+    }
+
+  if (gpg->lc_messages)
+    {
+      rc = add_arg_ext (gpg, gpg->lc_messages, 1);
+      if (!rc)
+	rc = add_arg_ext (gpg, "--lc-messages", 1);
+      if (rc)
+	return rc;
+    }
+
+  rc = build_argv (gpg);
+  if (rc)
+    return rc;
+
+  /* status_fd, colon_fd and end of list.  */
+  n = 3;
+  for (i = 0; gpg->fd_data_map[i].data; i++) 
+    n++;
+  fd_list = calloc (n, sizeof *fd_list);
+  if (! fd_list)
+    return gpg_error_from_errno (errno);
+
+  /* Build the fd list for the child.  */
+  n = 0;
+  fd_list[n].fd = gpg->status.fd[1];
+  fd_list[n].dup_to = -1;
+  fd_list[n].arg_loc = gpg->status.arg_loc;
+  n++;
+  if (gpg->colon.fnc)
+    {
+      fd_list[n].fd = gpg->colon.fd[1]; 
+      fd_list[n].dup_to = 1;
+      n++;
+    }
+  for (i = 0; gpg->fd_data_map[i].data; i++)
+    {
+      fd_list[n].fd = gpg->fd_data_map[i].peer_fd;
+      fd_list[n].dup_to = gpg->fd_data_map[i].dup_to;
+      fd_list[n].arg_loc = gpg->fd_data_map[i].arg_loc;
+      n++;
+    }
+  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, fd_list, &pid);
+  saved_errno = errno;
+
+  free (fd_list);
+  if (status == -1)
+    return gpg_error_from_errno (saved_errno);
+
+  /*_gpgme_register_term_handler ( closure, closure_value, pid );*/
+
+  rc = add_io_cb (gpg, gpg->status.fd[0], 1, status_handler, gpg,
+		  &gpg->status.tag);
+  if (rc)
+    /* FIXME: kill the child */
+    return rc;
+
+  if (gpg->colon.fnc)
+    {
+      assert (gpg->colon.fd[0] != -1);
+      rc = add_io_cb (gpg, gpg->colon.fd[0], 1, colon_line_handler, gpg,
+		      &gpg->colon.tag);
+      if (rc)
+	/* FIXME: kill the child */
+	return rc;
+    }
+
+  for (i = 0; gpg->fd_data_map[i].data; i++)
+    {
+      if (gpg->cmd.used && i == gpg->cmd.idx)
+	{
+	  /* Park the cmd fd.  */
+	  gpg->cmd.fd = gpg->fd_data_map[i].fd;
+	  gpg->fd_data_map[i].fd = -1;
+	}
+      else
+	{
+	  rc = add_io_cb (gpg, gpg->fd_data_map[i].fd,
+			  gpg->fd_data_map[i].inbound,
+			  gpg->fd_data_map[i].inbound
+			  ? _gpgme_data_inbound_handler
+			  : _gpgme_data_outbound_handler,
+			  gpg->fd_data_map[i].data, &gpg->fd_data_map[i].tag);
+	  
+	  if (rc)
+	    /* FIXME: kill the child */
+	    return rc;
+	}
+    }
+
+  _gpgme_allow_set_foregound_window (pid);
+
+  gpg_io_event (gpg, GPGME_EVENT_START, NULL);
+  
+  /* fixme: check what data we can release here */
+  return 0;
+}
+
+
+static gpgme_error_t
+gpg_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
+{
+  engine_gpg_t gpg = engine;
+  gpgme_error_t err;
+
+  err = add_arg (gpg, "--decrypt");
+
+  /* Tell the gpg object about the data.  */
+  if (!err)
+    err = add_arg (gpg, "--output");
+  if (!err)
+    err = add_arg (gpg, "-");
+  if (!err)
+    err = add_data (gpg, plain, 1, 1);
+  if (!err)
+    err = add_arg (gpg, "--");
+  if (!err)
+    err = add_data (gpg, ciph, -1, 0);
+
+  if (!err)
+    start (gpg);
+  return err;
+}
+
+static gpgme_error_t
+gpg_delete (void *engine, gpgme_key_t key, int allow_secret)
+{
+  engine_gpg_t gpg = engine;
+  gpgme_error_t err;
+
+  err = add_arg (gpg, allow_secret ? "--delete-secret-and-public-key"
+		 : "--delete-key");
+  if (!err)
+    err = add_arg (gpg, "--");
+  if (!err)
+    {
+      if (!key->subkeys || !key->subkeys->fpr)
+	return gpg_error (GPG_ERR_INV_VALUE);
+      else
+	err = add_arg (gpg, key->subkeys->fpr);
+    }
+
+  if (!err)
+    start (gpg);
+  return err;
+}
+
+
+static gpgme_error_t
+append_args_from_signers (engine_gpg_t gpg, gpgme_ctx_t ctx /* FIXME */)
+{
+  gpgme_error_t err = 0;
+  int i;
+  gpgme_key_t key;
+
+  for (i = 0; (key = gpgme_signers_enum (ctx, i)); i++)
+    {
+      const char *s = key->subkeys ? key->subkeys->keyid : NULL;
+      if (s)
+	{
+	  if (!err)
+	    err = add_arg (gpg, "-u");
+	  if (!err)
+	    err = add_arg (gpg, s);
+	}
+      gpgme_key_unref (key);
+      if (err) break;
+    }
+  return err;
+}
+
+
+static gpgme_error_t
+append_args_from_sig_notations (engine_gpg_t gpg, gpgme_ctx_t ctx /* FIXME */)
+{
+  gpgme_error_t err = 0;
+  gpgme_sig_notation_t notation;
+
+  notation = gpgme_sig_notation_get (ctx);
+
+  while (!err && notation)
+    {
+      if (notation->name
+	  && !(notation->flags & GPGME_SIG_NOTATION_HUMAN_READABLE))
+	err = gpg_error (GPG_ERR_INV_VALUE);
+      else if (notation->name)
+	{
+	  char *arg;
+
+	  /* Maximum space needed is one byte for the "critical" flag,
+	     the name, one byte for '=', the value, and a terminating
+	     '\0'.  */
+
+	  arg = malloc (1 + notation->name_len + 1 + notation->value_len + 1);
+	  if (!arg)




More information about the Gnupg-commits mailing list