diff options
Diffstat (limited to 'gpgme/rungpg.c')
| -rw-r--r-- | gpgme/rungpg.c | 981 | 
1 files changed, 510 insertions, 471 deletions
| diff --git a/gpgme/rungpg.c b/gpgme/rungpg.c index b6bc45d2..19ca74d3 100644 --- a/gpgme/rungpg.c +++ b/gpgme/rungpg.c @@ -1,25 +1,26 @@ -/* rungpg.c  - *	Copyright (C) 2000 Werner Koch (dd9jn) - *      Copyright (C) 2001, 2002 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 General Public License as published by - * the Free Software Foundation; either version 2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - */ - +/* rungpg.c - Gpg Engine. +   Copyright (C) 2000 Werner Koch (dd9jn) +   Copyright (C) 2001, 2002 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 General Public License as published by +   the Free Software Foundation; either version 2 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 +   General Public License for more details. +  +   You should have received a copy of the GNU General Public License +   along with GPGME; if not, write to the Free Software Foundation, +   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */ + +#if HAVE_CONFIG_H  #include <config.h> +#endif  #include <stdio.h>  #include <stdlib.h>  #include <string.h> @@ -36,12 +37,12 @@  #include "util.h"  #include "ops.h"  #include "wait.h" -#include "rungpg.h"  #include "context.h"  /*temp hack until we have GpmeData methods to do I/O */  #include "io.h"  #include "sema.h"  #include "status-table.h" +#include "engine-backend.h"  /* This type is used to build a list of gpg arguments and data @@ -56,6 +57,7 @@ struct arg_and_data_s    char arg[1];     /* Used if data above is not used.  */  }; +  struct fd_data_map_s  {    GpgmeData data; @@ -85,7 +87,7 @@ struct gpg_object_s      void *tag;    } status; -  /* This is a kludge - see the comment at gpg_colon_line_handler.  */ +  /* This is a kludge - see the comment at colon_line_handler.  */    struct    {      int fd[2];   @@ -121,17 +123,16 @@ struct gpg_object_s    struct GpgmeIOCbs io_cbs;  }; -static void free_argv (char **argv); -static void free_fd_data_map (struct fd_data_map_s *fd_data_map); -static void gpg_status_handler (void *opaque, int fd); -static GpgmeError read_status (GpgObject gpg); +static void +gpg_io_event (void *engine, GpgmeEventIO type, void *type_data) +{ +  GpgObject gpg = engine; -static void gpg_colon_line_handler (void *opaque, int fd); -static GpgmeError read_colon_line (GpgObject gpg); +  if (gpg->io_cbs.event) +    (*gpg->io_cbs.event) (gpg->io_cbs.event_priv, type, type_data); +} -static int command_cb (void *opaque, char *buffer, size_t length, -		       size_t *nread);  static void  close_notify_handler (int fd, void *opaque) @@ -204,7 +205,7 @@ close_notify_handler (int fd, void *opaque)  	  }      }    if (!not_done) -    _gpgme_gpg_io_event (gpg, GPGME_EVENT_DONE, NULL); +    gpg_io_event (gpg, GPGME_EVENT_DONE, NULL);  }  static GpgmeError @@ -263,8 +264,8 @@ add_data (GpgObject gpg, GpgmeData data, int dup_to, int inbound)  } -const char * -_gpgme_gpg_get_version (void) +static const char * +gpg_get_version (void)  {    static const char *gpg_version;    DEFINE_STATIC_LOCK (gpg_version_lock); @@ -276,16 +277,86 @@ _gpgme_gpg_get_version (void)    return gpg_version;  } -GpgmeError -_gpgme_gpg_check_version (void) + +static GpgmeError +gpg_check_version (void)  { -  return _gpgme_compare_versions (_gpgme_gpg_get_version (), -                                  NEED_GPG_VERSION) +  return _gpgme_compare_versions (gpg_get_version (), NEED_GPG_VERSION)      ? 0 : mk_error (Invalid_Engine);  } -GpgmeError -_gpgme_gpg_new (GpgObject *r_gpg) + +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 void +gpg_release (void *engine) +{ +  GpgObject gpg = engine; + +  if (!gpg) +    return; + +  while (gpg->arglist) +    { +      struct arg_and_data_s *next = gpg->arglist->next; + +      free (gpg->arglist); +      gpg->arglist = next; +    } + +  free (gpg->status.buffer); +  free (gpg->colon.buffer); +  if (gpg->argv) +    free_argv (gpg->argv); +  gpgme_data_release (gpg->cmd.cb_data); +  free (gpg->cmd.keyword); + +  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]); +  free_fd_data_map (gpg->fd_data_map); +  if (gpg->cmd.fd != -1) +    _gpgme_io_close (gpg->cmd.fd); +  free (gpg); +} + + +static GpgmeError +gpg_new (void **engine)  {    GpgObject gpg;    int rc = 0; @@ -344,55 +415,18 @@ _gpgme_gpg_new (GpgObject *r_gpg)   leave:    if (rc) -    { -      _gpgme_gpg_release (gpg); -      *r_gpg = NULL; -    } +    gpg_release (gpg);    else -    *r_gpg = gpg; +    *engine = gpg;    return rc;  } -void -_gpgme_gpg_release (GpgObject gpg) +static GpgmeError +gpg_set_verbosity (void *engine, int verbosity)  { -  if (!gpg) -    return; - -  while (gpg->arglist) -    { -      struct arg_and_data_s *next = gpg->arglist->next; - -      free (gpg->arglist); -      gpg->arglist = next; -    } - -  free (gpg->status.buffer); -  free (gpg->colon.buffer); -  if (gpg->argv) -    free_argv (gpg->argv); -  gpgme_data_release (gpg->cmd.cb_data); -  free (gpg->cmd.keyword); - -  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]); -  free_fd_data_map (gpg->fd_data_map); -  if (gpg->cmd.fd != -1) -    _gpgme_io_close (gpg->cmd.fd); -  free (gpg); -} - +  GpgObject gpg = engine; -GpgmeError -_gpgme_gpg_set_verbosity (GpgObject gpg, int verbosity) -{    GpgmeError err = 0;    while (!err && verbosity-- > 0)      err = add_arg (gpg, "--verbose"); @@ -401,22 +435,21 @@ _gpgme_gpg_set_verbosity (GpgObject gpg, int verbosity)  /* Note, that the status_handler is allowed to modifiy the args     value.  */ -void -_gpgme_gpg_set_status_handler (GpgObject gpg, -			       GpgmeStatusHandler fnc, void *fnc_value) +static void +gpg_set_status_handler (void *engine, GpgmeStatusHandler fnc, void *fnc_value)  { -  assert (gpg); +  GpgObject gpg = engine;    gpg->status.fnc = fnc;    gpg->status.fnc_value = fnc_value;  }  /* Kludge to process --with-colon output.  */ -GpgmeError -_gpgme_gpg_set_colon_line_handler (GpgObject gpg, -				   GpgmeColonLineHandler fnc, void *fnc_value) +static GpgmeError +gpg_set_colon_line_handler (void *engine, GpgmeColonLineHandler fnc, +			    void *fnc_value)  { -  assert (gpg); +  GpgObject gpg = engine;    gpg->colon.bufsize = 1024;    gpg->colon.readpos = 0; @@ -441,21 +474,80 @@ _gpgme_gpg_set_colon_line_handler (GpgObject gpg,  } +/* Here we handle --command-fd.  This works closely together with the +   status handler.  */ +static int +command_cb (void *opaque, char *buffer, size_t length, size_t *nread) +{ +  GpgObject gpg = opaque; +  const char *value; +  int value_len; + +  DEBUG0 ("command_cb: enter\n"); +  assert (gpg->cmd.used); +  if (!buffer || !length || !nread) +    return 0; /* These values are reserved for extensions.  */ +  *nread = 0; +  if (!gpg->cmd.code) +    { +      DEBUG0 ("command_cb: no code\n"); +      return -1; +    } +     +  if (!gpg->cmd.fnc) +    { +      DEBUG0 ("command_cb: no user cb\n"); +      return -1; +    } + +  value = gpg->cmd.fnc (gpg->cmd.fnc_value,  +			gpg->cmd.code, gpg->cmd.keyword); +  if (!value) +    { +      DEBUG0 ("command_cb: no data from user cb\n"); +      gpg->cmd.fnc (gpg->cmd.fnc_value, 0, value); +      return -1; +    } + +  value_len = strlen (value); +  if (value_len + 1 > length) +    { +      DEBUG0 ("command_cb: too much data from user cb\n"); +      gpg->cmd.fnc (gpg->cmd.fnc_value, 0, value); +      return -1; +    } + +  memcpy (buffer, value, value_len); +  if (!value_len || (value_len && value[value_len-1] != '\n'))  +    buffer[value_len++] = '\n'; +  *nread = value_len; +     +  gpg->cmd.fnc (gpg->cmd.fnc_value, 0, value); +  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; + +  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.  */ -GpgmeError -_gpgme_gpg_set_command_handler (GpgObject gpg, -				GpgmeCommandHandler fnc, void *fnc_value, -				GpgmeData linked_data) +static GpgmeError +gpg_set_command_handler (void *engine, GpgmeCommandHandler fnc, +			 void *fnc_value, GpgmeData linked_data)  { +  GpgObject gpg = engine;    GpgmeData tmp;    GpgmeError err; -  assert (gpg); -    err = gpgme_data_new_with_read_cb (&tmp, command_cb, gpg);    if (err)      return err; @@ -471,37 +563,6 @@ _gpgme_gpg_set_command_handler (GpgObject gpg,  } -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 GpgmeError  build_argv (GpgObject gpg)  { @@ -712,9 +773,10 @@ build_argv (GpgObject gpg)    return 0;  } +  static GpgmeError -_gpgme_gpg_add_io_cb (GpgObject gpg, int fd, int dir, -		      GpgmeIOCb handler, void *data, void **tag) +add_io_cb (GpgObject gpg, int fd, int dir, GpgmeIOCb handler, void *data, +	   void **tag)  {    GpgmeError err; @@ -727,179 +789,24 @@ _gpgme_gpg_add_io_cb (GpgObject gpg, int fd, int dir,    return err;  } -GpgmeError -_gpgme_gpg_spawn (GpgObject gpg, void *opaque) -{ -  GpgmeError rc; -  int i, n; -  int status; -  struct spawn_fd_item_s *fd_child_list, *fd_parent_list; - -  if (!gpg) -    return mk_error (Invalid_Value); - -  if (! _gpgme_get_gpg_path ()) -    return mk_error (Invalid_Engine); - -  /* Kludge, so that we don't need to check the return code of all the -     add_arg ().  We bail out here instead.  */ -  if (gpg->arg_error) -    return mk_error (Out_Of_Core); - -  rc = build_argv (gpg); -  if (rc) -    return rc; - -  n = 3; /* status_fd, colon_fd and end of list */ -  for (i = 0; gpg->fd_data_map[i].data; i++)  -    n++; -  fd_child_list = calloc (n + n, sizeof *fd_child_list); -  if (!fd_child_list) -    return mk_error (Out_Of_Core); -  fd_parent_list = fd_child_list + n; - -  /* build the fd list for the child */ -  n = 0; -  if (gpg->colon.fnc) -    { -      fd_child_list[n].fd = gpg->colon.fd[1];  -      fd_child_list[n].dup_to = 1; /* dup to stdout */ -      n++; -    } -  for (i = 0; gpg->fd_data_map[i].data; i++) -    { -      if (gpg->fd_data_map[i].dup_to != -1) -	{ -	  fd_child_list[n].fd = gpg->fd_data_map[i].peer_fd; -	  fd_child_list[n].dup_to = gpg->fd_data_map[i].dup_to; -	  n++; -        } -    } -  fd_child_list[n].fd = -1; -  fd_child_list[n].dup_to = -1; - -  /* Build the fd list for the parent.  */ -  n = 0; -  if (gpg->status.fd[1] != -1) -    { -      fd_parent_list[n].fd = gpg->status.fd[1]; -      fd_parent_list[n].dup_to = -1; -      n++; -      gpg->status.fd[1] = -1; -    } -  if (gpg->colon.fd[1] != -1) -    { -      fd_parent_list[n].fd = gpg->colon.fd[1]; -      fd_parent_list[n].dup_to = -1; -      n++; -      gpg->colon.fd[1] = -1; -    } -  for (i = 0; gpg->fd_data_map[i].data; i++) -    { -      fd_parent_list[n].fd = gpg->fd_data_map[i].peer_fd; -      fd_parent_list[n].dup_to = -1; -      n++; -      gpg->fd_data_map[i].peer_fd = -1; -    }         -  fd_parent_list[n].fd = -1; -  fd_parent_list[n].dup_to = -1; - -  status = _gpgme_io_spawn (_gpgme_get_gpg_path (), -			    gpg->argv, fd_child_list, fd_parent_list); -  free (fd_child_list); -  if (status == -1) -    return mk_error (Exec_Error); - -  /*_gpgme_register_term_handler ( closure, closure_value, pid );*/ - -  rc = _gpgme_gpg_add_io_cb (gpg, gpg->status.fd[0], 1, -			     gpg_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 = _gpgme_gpg_add_io_cb (gpg, gpg->colon.fd[0], 1, -				 gpg_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 = _gpgme_gpg_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; -	} -    } -   -  /* fixme: check what data we can release here */ -  return 0; -} - - -static void -gpg_status_handler (void *opaque, int fd) -{ -  GpgObject gpg = opaque; -  int err; - -  assert (fd == gpg->status.fd[0]); -  err = read_status (gpg); -  if (err) -    { -      /* XXX Horrible kludge.  We really must not make use of -	 fnc_value.  */ -      GpgmeCtx ctx = (GpgmeCtx) gpg->status.fnc_value; -      ctx->error = err; -      DEBUG1 ("gpg_handler: read_status problem %d\n - stop", err); -      _gpgme_io_close (fd); -      return; -    } -  if (gpg->status.eof) -    _gpgme_io_close (fd); -} -  static int  status_cmp (const void *ap, const void *bp)  { -    const struct status_table_s *a = ap; -    const struct status_table_s *b = bp; +  const struct status_table_s *a = ap; +  const struct status_table_s *b = bp; -    return strcmp (a->name, b->name); +  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).  */ +/* 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 GpgmeError  read_status (GpgObject gpg)  { @@ -997,11 +904,10 @@ read_status (GpgObject gpg)  			      while (fds.signaled);  			    } -			  _gpgme_gpg_add_io_cb -			    (gpg, gpg->cmd.fd, -			     0, _gpgme_data_outbound_handler, -			     gpg->fd_data_map[gpg->cmd.idx].data, -			     &gpg->fd_data_map[gpg->cmd.idx].tag); +			  add_io_cb (gpg, gpg->cmd.fd, 0, +				     _gpgme_data_outbound_handler, +				     gpg->fd_data_map[gpg->cmd.idx].data, +				     &gpg->fd_data_map[gpg->cmd.idx].tag);  			  gpg->fd_data_map[gpg->cmd.idx].fd = gpg->cmd.fd;  			  gpg->cmd.fd = -1;                          } @@ -1051,12 +957,109 @@ read_status (GpgObject gpg)  } +static void +status_handler (void *opaque, int fd) +{ +  GpgObject gpg = opaque; +  int err; + +  assert (fd == gpg->status.fd[0]); +  err = read_status (gpg); +  if (err) +    { +      /* XXX Horrible kludge.  We really must not make use of +	 fnc_value.  */ +      GpgmeCtx ctx = (GpgmeCtx) gpg->status.fnc_value; +      ctx->error = err; +      DEBUG1 ("gpg_handler: read_status problem %d\n - stop", err); +      _gpgme_io_close (fd); +      return; +    } +  if (gpg->status.eof) +    _gpgme_io_close (fd); +} + + +static GpgmeError +read_colon_line (GpgObject 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 mk_error (Out_Of_Core); +    } + +  nread = _gpgme_io_read (gpg->colon.fd[0], buffer+readpos, bufsize-readpos); +  if (nread == -1) +    return mk_error (Read_Error); + +  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, ':')) +		{ +		  assert (gpg->colon.fnc); +		  gpg->colon.fnc (gpg->colon.fnc_value, buffer); +		} +             +	      /* 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 GpgmeData 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 void -gpg_colon_line_handler (void *opaque, int fd) +colon_line_handler (void *opaque, int fd)  {    GpgObject gpg = opaque;    GpgmeError rc = 0; @@ -1074,143 +1077,141 @@ gpg_colon_line_handler (void *opaque, int fd)      _gpgme_io_close (fd);  } +  static GpgmeError -read_colon_line ( GpgObject gpg ) +gpg_start (void *engine, void *opaque)  { -    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 mk_error (Out_Of_Core); -    } -     +  GpgObject gpg = engine; +  GpgmeError rc; +  int i, n; +  int status; +  struct spawn_fd_item_s *fd_child_list, *fd_parent_list; -    nread = _gpgme_io_read ( gpg->colon.fd[0], -                             buffer+readpos, bufsize-readpos ); -    if (nread == -1) -        return mk_error(Read_Error); +  if (!gpg) +    return mk_error (Invalid_Value); -    if (!nread) { -        gpg->colon.eof = 1; -        assert (gpg->colon.fnc); -        gpg->colon.fnc ( gpg->colon.fnc_value, NULL ); -        return 0; -    } +  if (! _gpgme_get_gpg_path ()) +    return mk_error (Invalid_Engine); -    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, ':')) -		  { -                    assert (gpg->colon.fnc); -                    gpg->colon.fnc (gpg->colon.fnc_value, buffer); -		  } -             -                /* 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; -} +  /* Kludge, so that we don't need to check the return code of all the +     add_arg ().  We bail out here instead.  */ +  if (gpg->arg_error) +    return mk_error (Out_Of_Core); -/*  - * Here we handle --command-fd.  This works closely together with - * the status handler.   - */ +  rc = build_argv (gpg); +  if (rc) +    return rc; -static int -command_cb (void *opaque, char *buffer, size_t length, size_t *nread) -{ -  GpgObject gpg = opaque; -  const char *value; -  int value_len; +  n = 3; /* status_fd, colon_fd and end of list */ +  for (i = 0; gpg->fd_data_map[i].data; i++)  +    n++; +  fd_child_list = calloc (n + n, sizeof *fd_child_list); +  if (!fd_child_list) +    return mk_error (Out_Of_Core); +  fd_parent_list = fd_child_list + n; -  DEBUG0 ("command_cb: enter\n"); -  assert (gpg->cmd.used); -  if (!buffer || !length || !nread) -    return 0; /* These values are reserved for extensions.  */ -  *nread = 0; -  if (!gpg->cmd.code) +  /* build the fd list for the child */ +  n = 0; +  if (gpg->colon.fnc)      { -      DEBUG0 ("command_cb: no code\n"); -      return -1; +      fd_child_list[n].fd = gpg->colon.fd[1];  +      fd_child_list[n].dup_to = 1; /* dup to stdout */ +      n++;      } -     -  if (!gpg->cmd.fnc) +  for (i = 0; gpg->fd_data_map[i].data; i++)      { -      DEBUG0 ("command_cb: no user cb\n"); -      return -1; +      if (gpg->fd_data_map[i].dup_to != -1) +	{ +	  fd_child_list[n].fd = gpg->fd_data_map[i].peer_fd; +	  fd_child_list[n].dup_to = gpg->fd_data_map[i].dup_to; +	  n++; +        }      } +  fd_child_list[n].fd = -1; +  fd_child_list[n].dup_to = -1; -  value = gpg->cmd.fnc (gpg->cmd.fnc_value,  -			gpg->cmd.code, gpg->cmd.keyword); -  if (!value) +  /* Build the fd list for the parent.  */ +  n = 0; +  if (gpg->status.fd[1] != -1)      { -      DEBUG0 ("command_cb: no data from user cb\n"); -      gpg->cmd.fnc (gpg->cmd.fnc_value, 0, value); -      return -1; +      fd_parent_list[n].fd = gpg->status.fd[1]; +      fd_parent_list[n].dup_to = -1; +      n++; +      gpg->status.fd[1] = -1;      } - -  value_len = strlen (value); -  if (value_len + 1 > length) +  if (gpg->colon.fd[1] != -1)      { -      DEBUG0 ("command_cb: too much data from user cb\n"); -      gpg->cmd.fnc (gpg->cmd.fnc_value, 0, value); -      return -1; +      fd_parent_list[n].fd = gpg->colon.fd[1]; +      fd_parent_list[n].dup_to = -1; +      n++; +      gpg->colon.fd[1] = -1;      } +  for (i = 0; gpg->fd_data_map[i].data; i++) +    { +      fd_parent_list[n].fd = gpg->fd_data_map[i].peer_fd; +      fd_parent_list[n].dup_to = -1; +      n++; +      gpg->fd_data_map[i].peer_fd = -1; +    }         +  fd_parent_list[n].fd = -1; +  fd_parent_list[n].dup_to = -1; -  memcpy (buffer, value, value_len); -  if (!value_len || (value_len && value[value_len-1] != '\n'))  -    buffer[value_len++] = '\n'; -  *nread = value_len; -     -  gpg->cmd.fnc (gpg->cmd.fnc_value, 0, value); -  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; +  status = _gpgme_io_spawn (_gpgme_get_gpg_path (), +			    gpg->argv, fd_child_list, fd_parent_list); +  free (fd_child_list); +  if (status == -1) +    return mk_error (Exec_Error); + +  /*_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; +	} +    } +   +  /* fixme: check what data we can release here */    return 0;  } -GpgmeError -_gpgme_gpg_op_decrypt (GpgObject gpg, GpgmeData ciph, GpgmeData plain) + +static GpgmeError +gpg_decrypt (void *engine, GpgmeData ciph, GpgmeData plain)  { +  GpgObject gpg = engine;    GpgmeError err;    err = add_arg (gpg, "--decrypt"); @@ -1228,9 +1229,10 @@ _gpgme_gpg_op_decrypt (GpgObject gpg, GpgmeData ciph, GpgmeData plain)    return err;  } -GpgmeError -_gpgme_gpg_op_delete (GpgObject gpg, GpgmeKey key, int allow_secret) +static GpgmeError +gpg_delete (void *engine, GpgmeKey key, int allow_secret)  { +  GpgObject gpg = engine;    GpgmeError err;    err = add_arg (gpg, allow_secret ? "--delete-secret-and-public-key" @@ -1251,8 +1253,7 @@ _gpgme_gpg_op_delete (GpgObject gpg, GpgmeKey key, int allow_secret)  static GpgmeError -_gpgme_append_gpg_args_from_signers (GpgObject gpg, -				     GpgmeCtx ctx /* FIXME */) +append_args_from_signers (GpgObject gpg, GpgmeCtx ctx /* FIXME */)  {    GpgmeError err = 0;    int i; @@ -1276,15 +1277,15 @@ _gpgme_append_gpg_args_from_signers (GpgObject gpg,  } -GpgmeError -_gpgme_gpg_op_edit (GpgObject gpg, GpgmeKey key, GpgmeData out, -		    GpgmeCtx ctx /* FIXME */) +static GpgmeError +gpg_edit (void *engine, GpgmeKey key, GpgmeData out, GpgmeCtx ctx /* FIXME */)  { +  GpgObject gpg = engine;    GpgmeError err;    err = add_arg (gpg, "--with-colons");    if (!err) -    err = _gpgme_append_gpg_args_from_signers (gpg, ctx); +    err = append_args_from_signers (gpg, ctx);    if (!err)    err = add_arg (gpg, "--edit-key");    if (!err) @@ -1305,8 +1306,7 @@ _gpgme_gpg_op_edit (GpgObject gpg, GpgmeKey key, GpgmeData out,  static GpgmeError -_gpgme_append_gpg_args_from_recipients (GpgObject gpg, -					const GpgmeRecipients rset) +append_args_from_recipients (GpgObject gpg, const GpgmeRecipients rset)  {    GpgmeError err = 0;    struct user_id_s *r; @@ -1324,10 +1324,11 @@ _gpgme_append_gpg_args_from_recipients (GpgObject gpg,  } -GpgmeError -_gpgme_gpg_op_encrypt (GpgObject gpg, GpgmeRecipients recp, -		       GpgmeData plain, GpgmeData ciph, int use_armor) +static GpgmeError +gpg_encrypt (void *engine, GpgmeRecipients recp, GpgmeData plain, +	     GpgmeData ciph, int use_armor)  { +  GpgObject gpg = engine;    GpgmeError err;    int symmetric = !recp; @@ -1344,7 +1345,7 @@ _gpgme_gpg_op_encrypt (GpgObject gpg, GpgmeRecipients recp,  	err = add_arg (gpg, "--always-trust");        if (!err) -	err = _gpgme_append_gpg_args_from_recipients (gpg, recp); +	err = append_args_from_recipients (gpg, recp);      }    /* Tell the gpg object about the data.  */ @@ -1362,11 +1363,11 @@ _gpgme_gpg_op_encrypt (GpgObject gpg, GpgmeRecipients recp,    return err;  } -GpgmeError -_gpgme_gpg_op_encrypt_sign (GpgObject gpg, GpgmeRecipients recp, -			    GpgmeData plain, GpgmeData ciph, int use_armor, -			    GpgmeCtx ctx /* FIXME */) +static GpgmeError +gpg_encrypt_sign (void *engine, GpgmeRecipients recp, GpgmeData plain, +		  GpgmeData ciph, int use_armor, GpgmeCtx ctx /* FIXME */)  { +  GpgObject gpg = engine;    GpgmeError err;    err = add_arg (gpg, "--encrypt"); @@ -1381,10 +1382,10 @@ _gpgme_gpg_op_encrypt_sign (GpgObject gpg, GpgmeRecipients recp,      err = add_arg (gpg, "--always-trust");    if (!err) -    err = _gpgme_append_gpg_args_from_recipients (gpg, recp); +    err = append_args_from_recipients (gpg, recp);    if (!err) -    err = _gpgme_append_gpg_args_from_signers (gpg, ctx); +    err = append_args_from_signers (gpg, ctx);    /* Tell the gpg object about the data.  */    if (!err) @@ -1401,10 +1402,11 @@ _gpgme_gpg_op_encrypt_sign (GpgObject gpg, GpgmeRecipients recp,    return err;  } -GpgmeError -_gpgme_gpg_op_export (GpgObject gpg, GpgmeRecipients recp, -		      GpgmeData keydata, int use_armor) +static GpgmeError +gpg_export (void *engine, GpgmeRecipients recp, GpgmeData keydata, +	    int use_armor)  { +  GpgObject gpg = engine;    GpgmeError err;    err = add_arg (gpg, "--export"); @@ -1430,19 +1432,21 @@ _gpgme_gpg_op_export (GpgObject gpg, GpgmeRecipients recp,    return err;  } -GpgmeError -_gpgme_gpg_op_genkey (GpgObject gpg, GpgmeData help_data, int use_armor, -		      GpgmeData pubkey, GpgmeData seckey) + +static GpgmeError +gpg_genkey (void *engine, GpgmeData help_data, int use_armor, +	    GpgmeData pubkey, GpgmeData seckey)  { +  GpgObject gpg = engine;    GpgmeError err;    if (!gpg)      return mk_error (Invalid_Value); -  /* We need a special mechanism to get the fd of a pipe here, so -   * that we can use this for the %pubring and %secring parameters. -   * We don't have this yet, so we implement only the adding to the -   * standard keyrings */ +  /* We need a special mechanism to get the fd of a pipe here, so that +     we can use this for the %pubring and %secring parameters.  We +     don't have this yet, so we implement only the adding to the +     standard keyrings.  */    if (pubkey || seckey)      return err = mk_error (Not_Implemented); @@ -1455,9 +1459,11 @@ _gpgme_gpg_op_genkey (GpgObject gpg, GpgmeData help_data, int use_armor,    return err;  } -GpgmeError -_gpgme_gpg_op_import (GpgObject gpg, GpgmeData keydata) + +static GpgmeError +gpg_import (void *engine, GpgmeData keydata)  { +  GpgObject gpg = engine;    GpgmeError err;    err = add_arg (gpg, "--import"); @@ -1468,10 +1474,11 @@ _gpgme_gpg_op_import (GpgObject gpg, GpgmeData keydata)  } -GpgmeError -_gpgme_gpg_op_keylist (GpgObject gpg, const char *pattern, int secret_only, -		       int keylist_mode) +static GpgmeError +gpg_keylist (void *engine, const char *pattern, int secret_only, +	     int keylist_mode)  { +  GpgObject gpg = engine;    GpgmeError err;    err = add_arg (gpg, "--with-colons"); @@ -1495,10 +1502,11 @@ _gpgme_gpg_op_keylist (GpgObject gpg, const char *pattern, int secret_only,  } -GpgmeError -_gpgme_gpg_op_keylist_ext (GpgObject gpg, const char *pattern[], -			   int secret_only, int reserved, int keylist_mode) +static GpgmeError +gpg_keylist_ext (void *engine, const char *pattern[], int secret_only, +		 int reserved, int keylist_mode)  { +  GpgObject gpg = engine;    GpgmeError err;    if (reserved) @@ -1525,11 +1533,12 @@ _gpgme_gpg_op_keylist_ext (GpgObject gpg, const char *pattern[],  } -GpgmeError -_gpgme_gpg_op_sign (GpgObject gpg, GpgmeData in, GpgmeData out, -		    GpgmeSigMode mode, int use_armor, -		    int use_textmode, GpgmeCtx ctx /* FIXME */) +static GpgmeError +gpg_sign (void *engine, GpgmeData in, GpgmeData out, GpgmeSigMode mode, +	  int use_armor, int use_textmode, int include_certs, +	  GpgmeCtx ctx /* FIXME */)  { +  GpgObject gpg = engine;    GpgmeError err;    if (mode == GPGME_SIG_MODE_CLEAR) @@ -1546,7 +1555,7 @@ _gpgme_gpg_op_sign (GpgObject gpg, GpgmeData in, GpgmeData out,      }    if (!err) -    err = _gpgme_append_gpg_args_from_signers (gpg, ctx); +    err = append_args_from_signers (gpg, ctx);    /* Tell the gpg object about the data.  */    if (!err) @@ -1557,9 +1566,10 @@ _gpgme_gpg_op_sign (GpgObject gpg, GpgmeData in, GpgmeData out,    return err;  } -GpgmeError -_gpgme_gpg_op_trustlist (GpgObject gpg, const char *pattern) +static GpgmeError +gpg_trustlist (void *engine, const char *pattern)  { +  GpgObject gpg = engine;    GpgmeError err;    err = add_arg (gpg, "--with-colons"); @@ -1575,10 +1585,12 @@ _gpgme_gpg_op_trustlist (GpgObject gpg, const char *pattern)    return err;  } -GpgmeError -_gpgme_gpg_op_verify (GpgObject gpg, GpgmeData sig, GpgmeData signed_text, -		      GpgmeData plaintext) + +static GpgmeError +gpg_verify (void *engine, GpgmeData sig, GpgmeData signed_text, +	    GpgmeData plaintext)  { +  GpgObject gpg = engine;    GpgmeError err = 0;    if (plaintext) @@ -1614,16 +1626,43 @@ _gpgme_gpg_op_verify (GpgObject gpg, GpgmeData sig, GpgmeData signed_text,  } -void -_gpgme_gpg_set_io_cbs (GpgObject gpg, struct GpgmeIOCbs *io_cbs) +static void +gpg_set_io_cbs (void *engine, struct GpgmeIOCbs *io_cbs)  { +  GpgObject gpg = engine; +    gpg->io_cbs = *io_cbs;  } - -void -_gpgme_gpg_io_event (GpgObject gpg, GpgmeEventIO type, void *type_data) -{ -  if (gpg->io_cbs.event) -    (*gpg->io_cbs.event) (gpg->io_cbs.event_priv, type, type_data); -} + +struct engine_ops _gpgme_engine_ops_gpg = +  { +    /* Static functions.  */ +    _gpgme_get_gpg_path, +    gpg_get_version, +    gpg_check_version, +    gpg_new, + +    /* Member functions.  */ +    gpg_release, +    gpg_set_status_handler, +    gpg_set_command_handler, +    gpg_set_colon_line_handler, +    gpg_set_verbosity, +    gpg_decrypt, +    gpg_delete, +    gpg_edit, +    gpg_encrypt, +    gpg_encrypt_sign, +    gpg_export, +    gpg_genkey, +    gpg_import, +    gpg_keylist, +    gpg_keylist_ext, +    gpg_sign, +    gpg_trustlist, +    gpg_verify, +    gpg_start, +    gpg_set_io_cbs, +    gpg_io_event +  }; | 
