Blame src/engine-gpgconf.c

Packit d7e8d0
/* engine-gpgconf.c - gpg-conf engine.
Packit d7e8d0
   Copyright (C) 2000 Werner Koch (dd9jn)
Packit d7e8d0
   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2008,
Packit d7e8d0
                 2013 g10 Code GmbH
Packit d7e8d0
Packit d7e8d0
   This file is part of GPGME.
Packit d7e8d0
Packit d7e8d0
   GPGME is free software; you can redistribute it and/or modify it
Packit d7e8d0
   under the terms of the GNU Lesser General Public License as
Packit d7e8d0
   published by the Free Software Foundation; either version 2.1 of
Packit d7e8d0
   the License, or (at your option) any later version.
Packit d7e8d0
Packit d7e8d0
   GPGME is distributed in the hope that it will be useful, but
Packit d7e8d0
   WITHOUT ANY WARRANTY; without even the implied warranty of
Packit d7e8d0
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit d7e8d0
   Lesser General Public License for more details.
Packit d7e8d0
Packit d7e8d0
   You should have received a copy of the GNU Lesser General Public
Packit d7e8d0
   License along with this program; if not, see <https://www.gnu.org/licenses/>.
Packit d7e8d0
 */
Packit d7e8d0
Packit d7e8d0
#if HAVE_CONFIG_H
Packit d7e8d0
#include <config.h>
Packit d7e8d0
#endif
Packit d7e8d0
Packit d7e8d0
#include <stdlib.h>
Packit d7e8d0
#include <string.h>
Packit d7e8d0
#ifdef HAVE_SYS_TYPES_H
Packit d7e8d0
# include <sys/types.h>
Packit d7e8d0
#endif
Packit d7e8d0
#include <assert.h>
Packit d7e8d0
#ifdef HAVE_UNISTD_H
Packit d7e8d0
# include <unistd.h>
Packit d7e8d0
#endif
Packit d7e8d0
#include <fcntl.h> /* FIXME */
Packit d7e8d0
#include <errno.h>
Packit d7e8d0
Packit d7e8d0
#include "gpgme.h"
Packit d7e8d0
#include "util.h"
Packit d7e8d0
#include "ops.h"
Packit d7e8d0
#include "wait.h"
Packit d7e8d0
#include "priv-io.h"
Packit d7e8d0
#include "sema.h"
Packit d7e8d0
Packit d7e8d0
#include "assuan.h"
Packit d7e8d0
#include "debug.h"
Packit d7e8d0
Packit d7e8d0
#include "engine-backend.h"
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0

Packit d7e8d0
struct engine_gpgconf
Packit d7e8d0
{
Packit d7e8d0
  char *file_name;
Packit d7e8d0
  char *home_dir;
Packit d7e8d0
  char *version;
Packit d7e8d0
};
Packit d7e8d0
Packit d7e8d0
typedef struct engine_gpgconf *engine_gpgconf_t;
Packit d7e8d0
Packit d7e8d0

Packit d7e8d0
/* Return true if the engine's version is at least VERSION.  */
Packit d7e8d0
static int
Packit d7e8d0
have_gpgconf_version (engine_gpgconf_t gpgconf, const char *version)
Packit d7e8d0
{
Packit d7e8d0
  return _gpgme_compare_versions (gpgconf->version, version);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static char *
Packit d7e8d0
gpgconf_get_version (const char *file_name)
Packit d7e8d0
{
Packit d7e8d0
  return _gpgme_get_program_version (file_name ? file_name
Packit d7e8d0
				     : _gpgme_get_default_gpgconf_name ());
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static const char *
Packit d7e8d0
gpgconf_get_req_version (void)
Packit d7e8d0
{
Packit d7e8d0
  return "2.0.4";
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0

Packit d7e8d0
static void
Packit d7e8d0
gpgconf_release (void *engine)
Packit d7e8d0
{
Packit d7e8d0
  engine_gpgconf_t gpgconf = engine;
Packit d7e8d0
Packit d7e8d0
  if (!gpgconf)
Packit d7e8d0
    return;
Packit d7e8d0
Packit d7e8d0
  if (gpgconf->file_name)
Packit d7e8d0
    free (gpgconf->file_name);
Packit d7e8d0
  if (gpgconf->home_dir)
Packit d7e8d0
    free (gpgconf->home_dir);
Packit d7e8d0
  if (gpgconf->version)
Packit d7e8d0
    free (gpgconf->version);
Packit d7e8d0
Packit d7e8d0
  free (gpgconf);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
gpgconf_new (void **engine, const char *file_name, const char *home_dir,
Packit d7e8d0
             const char *version)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_error_t err = 0;
Packit d7e8d0
  engine_gpgconf_t gpgconf;
Packit d7e8d0
Packit d7e8d0
  gpgconf = calloc (1, sizeof *gpgconf);
Packit d7e8d0
  if (!gpgconf)
Packit d7e8d0
    return gpg_error_from_syserror ();
Packit d7e8d0
Packit d7e8d0
  gpgconf->file_name = strdup (file_name ? file_name
Packit d7e8d0
			       : _gpgme_get_default_gpgconf_name ());
Packit d7e8d0
  if (!gpgconf->file_name)
Packit d7e8d0
    err = gpg_error_from_syserror ();
Packit d7e8d0
Packit d7e8d0
  if (!err && home_dir)
Packit d7e8d0
    {
Packit d7e8d0
      gpgconf->home_dir = strdup (home_dir);
Packit d7e8d0
      if (!gpgconf->home_dir)
Packit d7e8d0
	err = gpg_error_from_syserror ();
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  if (!err && version)
Packit d7e8d0
    {
Packit d7e8d0
      gpgconf->version = strdup (version);
Packit d7e8d0
      if (!gpgconf->version)
Packit d7e8d0
        err = gpg_error_from_syserror ();
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  if (err)
Packit d7e8d0
    gpgconf_release (gpgconf);
Packit d7e8d0
  else
Packit d7e8d0
    *engine = gpgconf;
Packit d7e8d0
Packit d7e8d0
  return err;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0

Packit d7e8d0
static void
Packit d7e8d0
release_arg (gpgme_conf_arg_t arg, gpgme_conf_type_t alt_type)
Packit d7e8d0
{
Packit d7e8d0
  while (arg)
Packit d7e8d0
    {
Packit d7e8d0
      gpgme_conf_arg_t next = arg->next;
Packit d7e8d0
Packit d7e8d0
      if (alt_type == GPGME_CONF_STRING)
Packit d7e8d0
	free (arg->value.string);
Packit d7e8d0
      free (arg);
Packit d7e8d0
      arg = next;
Packit d7e8d0
    }
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static void
Packit d7e8d0
release_opt (gpgme_conf_opt_t opt)
Packit d7e8d0
{
Packit d7e8d0
  if (opt->name)
Packit d7e8d0
    free (opt->name);
Packit d7e8d0
  if (opt->description)
Packit d7e8d0
    free (opt->description);
Packit d7e8d0
  if (opt->argname)
Packit d7e8d0
    free (opt->argname);
Packit d7e8d0
Packit d7e8d0
  release_arg (opt->default_value, opt->alt_type);
Packit d7e8d0
  if (opt->default_description)
Packit d7e8d0
    free (opt->default_description);
Packit d7e8d0
Packit d7e8d0
  release_arg (opt->no_arg_value, opt->alt_type);
Packit d7e8d0
  release_arg (opt->value, opt->alt_type);
Packit d7e8d0
  release_arg (opt->new_value, opt->alt_type);
Packit d7e8d0
Packit d7e8d0
  free (opt);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static void
Packit d7e8d0
release_comp (gpgme_conf_comp_t comp)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_conf_opt_t opt;
Packit d7e8d0
Packit d7e8d0
  if (comp->name)
Packit d7e8d0
    free (comp->name);
Packit d7e8d0
  if (comp->description)
Packit d7e8d0
    free (comp->description);
Packit d7e8d0
  if (comp->program_name)
Packit d7e8d0
    free (comp->program_name);
Packit d7e8d0
Packit d7e8d0
  opt = comp->options;
Packit d7e8d0
  while (opt)
Packit d7e8d0
    {
Packit d7e8d0
      gpgme_conf_opt_t next = opt->next;
Packit d7e8d0
      release_opt (opt);
Packit d7e8d0
      opt = next;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  free (comp);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static void
Packit d7e8d0
gpgconf_config_release (gpgme_conf_comp_t conf)
Packit d7e8d0
{
Packit d7e8d0
  while (conf)
Packit d7e8d0
    {
Packit d7e8d0
      gpgme_conf_comp_t next = conf->next;
Packit d7e8d0
      release_comp (conf);
Packit d7e8d0
      conf = next;
Packit d7e8d0
    }
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
/* Read from gpgconf and pass line after line to the hook function.
Packit d7e8d0
   We put a limit of 64 k on the maximum size for a line.  This should
Packit d7e8d0
   allow for quite a long "group" line, which is usually the longest
Packit d7e8d0
   line (mine is currently ~3k).  */
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
gpgconf_read (void *engine, const char *arg1, char *arg2,
Packit d7e8d0
	      gpgme_error_t (*cb) (void *hook, char *line),
Packit d7e8d0
	      void *hook)
Packit d7e8d0
{
Packit d7e8d0
  struct engine_gpgconf *gpgconf = engine;
Packit d7e8d0
  gpgme_error_t err = 0;
Packit d7e8d0
  char *linebuf;
Packit d7e8d0
  size_t linebufsize;
Packit d7e8d0
  int linelen;
Packit d7e8d0
  char *argv[6];
Packit d7e8d0
  int argc = 0;
Packit d7e8d0
  int rp[2];
Packit d7e8d0
  struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0},
Packit d7e8d0
				   {-1, -1} };
Packit d7e8d0
  int status;
Packit d7e8d0
  int nread;
Packit d7e8d0
  char *mark = NULL;
Packit d7e8d0
Packit d7e8d0
  /* _gpgme_engine_new guarantees that this is not NULL.  */
Packit d7e8d0
  argv[argc++] = gpgconf->file_name;
Packit d7e8d0
Packit d7e8d0
  if (gpgconf->home_dir && have_gpgconf_version (gpgconf, "2.1.13"))
Packit d7e8d0
    {
Packit d7e8d0
      argv[argc++] = (char*)"--homedir";
Packit d7e8d0
      argv[argc++] = gpgconf->home_dir;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  argv[argc++] = (char*)arg1;
Packit d7e8d0
  argv[argc++] = arg2;
Packit d7e8d0
  argv[argc] = NULL;
Packit d7e8d0
  assert (argc < DIM (argv));
Packit d7e8d0
Packit d7e8d0
  if (_gpgme_io_pipe (rp, 1) < 0)
Packit d7e8d0
    return gpg_error_from_syserror ();
Packit d7e8d0
Packit d7e8d0
  cfd[0].fd = rp[1];
Packit d7e8d0
Packit d7e8d0
  status = _gpgme_io_spawn (gpgconf->file_name, argv,
Packit d7e8d0
                            IOSPAWN_FLAG_DETACHED, cfd, NULL, NULL, NULL);
Packit d7e8d0
  if (status < 0)
Packit d7e8d0
    {
Packit d7e8d0
      _gpgme_io_close (rp[0]);
Packit d7e8d0
      _gpgme_io_close (rp[1]);
Packit d7e8d0
      return gpg_error_from_syserror ();
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  linebufsize = 1024; /* Usually enough for conf lines.  */
Packit d7e8d0
  linebuf = malloc (linebufsize);
Packit d7e8d0
  if (!linebuf)
Packit d7e8d0
    {
Packit d7e8d0
      err = gpg_error_from_syserror ();
Packit d7e8d0
      goto leave;
Packit d7e8d0
    }
Packit d7e8d0
  linelen = 0;
Packit d7e8d0
Packit d7e8d0
  while ((nread = _gpgme_io_read (rp[0], linebuf + linelen,
Packit d7e8d0
                                  linebufsize - linelen - 1)))
Packit d7e8d0
    {
Packit d7e8d0
      char *line;
Packit d7e8d0
      const char *lastmark = NULL;
Packit d7e8d0
      size_t nused;
Packit d7e8d0
Packit d7e8d0
      if (nread < 0)
Packit d7e8d0
        {
Packit d7e8d0
          err = gpg_error_from_syserror ();
Packit d7e8d0
          goto leave;
Packit d7e8d0
        }
Packit d7e8d0
Packit d7e8d0
      linelen += nread;
Packit d7e8d0
      linebuf[linelen] = '\0';
Packit d7e8d0
Packit d7e8d0
      for (line=linebuf; (mark = strchr (line, '\n')); line = mark+1 )
Packit d7e8d0
        {
Packit d7e8d0
          lastmark = mark;
Packit d7e8d0
          if (mark > line && mark[-1] == '\r')
Packit d7e8d0
            mark[-1] = '\0';
Packit d7e8d0
          else
Packit d7e8d0
            mark[0] = '\0';
Packit d7e8d0
Packit d7e8d0
          /* Got a full line.  Due to the CR removal code (which
Packit d7e8d0
             occurs only on Windows) we might be one-off and thus
Packit d7e8d0
             would see empty lines.  Don't pass them to the
Packit d7e8d0
             callback. */
Packit d7e8d0
          err = *line? (*cb) (hook, line) : 0;
Packit d7e8d0
          if (err)
Packit d7e8d0
            goto leave;
Packit d7e8d0
        }
Packit d7e8d0
Packit d7e8d0
      nused = lastmark? (lastmark + 1 - linebuf) : 0;
Packit d7e8d0
      memmove (linebuf, linebuf + nused, linelen - nused);
Packit d7e8d0
      linelen -= nused;
Packit d7e8d0
Packit d7e8d0
      if (!(linelen < linebufsize - 1))
Packit d7e8d0
        {
Packit d7e8d0
          char *newlinebuf;
Packit d7e8d0
Packit d7e8d0
          if (linelen <  8 * 1024 - 1)
Packit d7e8d0
            linebufsize = 8 * 1024;
Packit d7e8d0
          else if (linelen < 64 * 1024 - 1)
Packit d7e8d0
            linebufsize = 64 * 1024;
Packit d7e8d0
          else
Packit d7e8d0
            {
Packit d7e8d0
              /* We reached our limit - give up.  */
Packit d7e8d0
              err = gpg_error (GPG_ERR_LINE_TOO_LONG);
Packit d7e8d0
              goto leave;
Packit d7e8d0
            }
Packit d7e8d0
Packit d7e8d0
          newlinebuf = realloc (linebuf, linebufsize);
Packit d7e8d0
          if (!newlinebuf)
Packit d7e8d0
            {
Packit d7e8d0
              err = gpg_error_from_syserror ();
Packit d7e8d0
              goto leave;
Packit d7e8d0
            }
Packit d7e8d0
          linebuf = newlinebuf;
Packit d7e8d0
        }
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
 leave:
Packit d7e8d0
  free (linebuf);
Packit d7e8d0
  _gpgme_io_close (rp[0]);
Packit d7e8d0
  return err;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
gpgconf_config_load_cb (void *hook, char *line)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_conf_comp_t *comp_p = hook;
Packit d7e8d0
  gpgme_conf_comp_t comp = *comp_p;
Packit d7e8d0
#define NR_FIELDS 16
Packit d7e8d0
  char *field[NR_FIELDS];
Packit d7e8d0
  int fields = 0;
Packit d7e8d0
Packit d7e8d0
  while (line && fields < NR_FIELDS)
Packit d7e8d0
    {
Packit d7e8d0
      field[fields++] = line;
Packit d7e8d0
      line = strchr (line, ':');
Packit d7e8d0
      if (line)
Packit d7e8d0
	*(line++) = '\0';
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  /* We require at least the first 3 fields.  */
Packit d7e8d0
  if (fields < 2)
Packit d7e8d0
    return trace_gpg_error (GPG_ERR_INV_ENGINE);
Packit d7e8d0
Packit d7e8d0
  /* Find the pointer to the new component in the list.  */
Packit d7e8d0
  while (comp && comp->next)
Packit d7e8d0
    comp = comp->next;
Packit d7e8d0
  if (comp)
Packit d7e8d0
    comp_p = &comp->next;
Packit d7e8d0
Packit d7e8d0
  comp = calloc (1, sizeof (*comp));
Packit d7e8d0
  if (!comp)
Packit d7e8d0
    return gpg_error_from_syserror ();
Packit d7e8d0
  /* Prepare return value.  */
Packit d7e8d0
  comp->_last_opt_p = &comp->options;
Packit d7e8d0
  *comp_p = comp;
Packit d7e8d0
Packit d7e8d0
  comp->name = strdup (field[0]);
Packit d7e8d0
  if (!comp->name)
Packit d7e8d0
    return gpg_error_from_syserror ();
Packit d7e8d0
Packit d7e8d0
  comp->description = strdup (field[1]);
Packit d7e8d0
  if (!comp->description)
Packit d7e8d0
    return gpg_error_from_syserror ();
Packit d7e8d0
Packit d7e8d0
  if (fields >= 3)
Packit d7e8d0
    {
Packit d7e8d0
      comp->program_name = strdup (field[2]);
Packit d7e8d0
      if (!comp->program_name)
Packit d7e8d0
	return gpg_error_from_syserror ();
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
gpgconf_parse_option (gpgme_conf_opt_t opt,
Packit d7e8d0
		      gpgme_conf_arg_t *arg_p, char *line)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_error_t err;
Packit d7e8d0
  char *mark = NULL;
Packit d7e8d0
Packit d7e8d0
  if (!line[0])
Packit d7e8d0
    return 0;
Packit d7e8d0
Packit d7e8d0
  while (line)
Packit d7e8d0
    {
Packit d7e8d0
      gpgme_conf_arg_t arg;
Packit d7e8d0
Packit d7e8d0
      if (opt->type != GPGME_CONF_STRING)
Packit d7e8d0
        mark = strchr (line, ',');
Packit d7e8d0
      if (mark)
Packit d7e8d0
	*mark = '\0';
Packit d7e8d0
Packit d7e8d0
      arg = calloc (1, sizeof (*arg));
Packit d7e8d0
      if (!arg)
Packit d7e8d0
	return gpg_error_from_syserror ();
Packit d7e8d0
      *arg_p = arg;
Packit d7e8d0
      arg_p = &arg->next;
Packit d7e8d0
Packit d7e8d0
      if (*line == '\0')
Packit d7e8d0
	arg->no_arg = 1;
Packit d7e8d0
      else
Packit d7e8d0
	{
Packit d7e8d0
	  switch (opt->alt_type)
Packit d7e8d0
	    {
Packit d7e8d0
	      /* arg->value.count is an alias for arg->value.uint32.  */
Packit d7e8d0
	    case GPGME_CONF_NONE:
Packit d7e8d0
	    case GPGME_CONF_UINT32:
Packit d7e8d0
	      arg->value.uint32 = strtoul (line, NULL, 0);
Packit d7e8d0
	      break;
Packit d7e8d0
Packit d7e8d0
	    case GPGME_CONF_INT32:
Packit d7e8d0
	      arg->value.uint32 = strtol (line, NULL, 0);
Packit d7e8d0
	      break;
Packit d7e8d0
Packit d7e8d0
	    case GPGME_CONF_STRING:
Packit d7e8d0
              /* The complex types below are only here to silent the
Packit d7e8d0
                 compiler warning. */
Packit d7e8d0
            case GPGME_CONF_FILENAME:
Packit d7e8d0
            case GPGME_CONF_LDAP_SERVER:
Packit d7e8d0
            case GPGME_CONF_KEY_FPR:
Packit d7e8d0
            case GPGME_CONF_PUB_KEY:
Packit d7e8d0
            case GPGME_CONF_SEC_KEY:
Packit d7e8d0
            case GPGME_CONF_ALIAS_LIST:
Packit d7e8d0
	      /* Skip quote character.  */
Packit d7e8d0
	      line++;
Packit d7e8d0
Packit d7e8d0
	      err = _gpgme_decode_percent_string (line, &arg->value.string,
Packit d7e8d0
						  0, 0);
Packit d7e8d0
	      if (err)
Packit d7e8d0
		return err;
Packit d7e8d0
	      break;
Packit d7e8d0
	    }
Packit d7e8d0
	}
Packit d7e8d0
Packit d7e8d0
      /* Find beginning of next value.  */
Packit d7e8d0
      if (mark++ && *mark)
Packit d7e8d0
	line = mark;
Packit d7e8d0
      else
Packit d7e8d0
	line = NULL;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
gpgconf_config_load_cb2 (void *hook, char *line)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_error_t err;
Packit d7e8d0
  gpgme_conf_comp_t comp = hook;
Packit d7e8d0
  gpgme_conf_opt_t *opt_p = comp->_last_opt_p;
Packit d7e8d0
  gpgme_conf_opt_t opt;
Packit d7e8d0
#define NR_FIELDS 16
Packit d7e8d0
  char *field[NR_FIELDS];
Packit d7e8d0
  int fields = 0;
Packit d7e8d0
Packit d7e8d0
  while (line && fields < NR_FIELDS)
Packit d7e8d0
    {
Packit d7e8d0
      field[fields++] = line;
Packit d7e8d0
      line = strchr (line, ':');
Packit d7e8d0
      if (line)
Packit d7e8d0
	*(line++) = '\0';
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  /* We require at least the first 10 fields.  */
Packit d7e8d0
  if (fields < 10)
Packit d7e8d0
    return trace_gpg_error (GPG_ERR_INV_ENGINE);
Packit d7e8d0
Packit d7e8d0
  opt = calloc (1, sizeof (*opt));
Packit d7e8d0
  if (!opt)
Packit d7e8d0
    return gpg_error_from_syserror ();
Packit d7e8d0
Packit d7e8d0
  comp->_last_opt_p = &opt->next;
Packit d7e8d0
  *opt_p = opt;
Packit d7e8d0
Packit d7e8d0
  if (field[0][0])
Packit d7e8d0
    {
Packit d7e8d0
      opt->name = strdup (field[0]);
Packit d7e8d0
      if (!opt->name)
Packit d7e8d0
	return gpg_error_from_syserror ();
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  opt->flags = strtoul (field[1], NULL, 0);
Packit d7e8d0
Packit d7e8d0
  opt->level = strtoul (field[2], NULL, 0);
Packit d7e8d0
Packit d7e8d0
  if (field[3][0])
Packit d7e8d0
    {
Packit d7e8d0
      opt->description = strdup (field[3]);
Packit d7e8d0
      if (!opt->description)
Packit d7e8d0
	return gpg_error_from_syserror ();
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  opt->type = strtoul (field[4], NULL, 0);
Packit d7e8d0
Packit d7e8d0
  opt->alt_type = strtoul (field[5], NULL, 0);
Packit d7e8d0
Packit d7e8d0
  if (field[6][0])
Packit d7e8d0
    {
Packit d7e8d0
      opt->argname = strdup (field[6]);
Packit d7e8d0
      if (!opt->argname)
Packit d7e8d0
	return gpg_error_from_syserror ();
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  if (opt->flags & GPGME_CONF_DEFAULT)
Packit d7e8d0
    {
Packit d7e8d0
      err = gpgconf_parse_option (opt, &opt->default_value, field[7]);
Packit d7e8d0
      if (err)
Packit d7e8d0
	return err;
Packit d7e8d0
    }
Packit d7e8d0
  else if ((opt->flags & GPGME_CONF_DEFAULT_DESC) && field[7][0])
Packit d7e8d0
    {
Packit d7e8d0
      opt->default_description = strdup (field[7]);
Packit d7e8d0
      if (!opt->default_description)
Packit d7e8d0
	return gpg_error_from_syserror ();
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  if (opt->flags & GPGME_CONF_NO_ARG_DESC)
Packit d7e8d0
    {
Packit d7e8d0
      opt->no_arg_description = strdup (field[8]);
Packit d7e8d0
      if (!opt->no_arg_description)
Packit d7e8d0
	return gpg_error_from_syserror ();
Packit d7e8d0
    }
Packit d7e8d0
  else
Packit d7e8d0
    {
Packit d7e8d0
      err = gpgconf_parse_option (opt, &opt->no_arg_value, field[8]);
Packit d7e8d0
      if (err)
Packit d7e8d0
	return err;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  err = gpgconf_parse_option (opt, &opt->value, field[9]);
Packit d7e8d0
  if (err)
Packit d7e8d0
    return err;
Packit d7e8d0
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
gpgconf_conf_load (void *engine, gpgme_conf_comp_t *comp_p)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_error_t err;
Packit d7e8d0
  gpgme_conf_comp_t comp = NULL;
Packit d7e8d0
  gpgme_conf_comp_t cur_comp;
Packit d7e8d0
Packit d7e8d0
  *comp_p = NULL;
Packit d7e8d0
Packit d7e8d0
  err = gpgconf_read (engine, "--list-components", NULL,
Packit d7e8d0
		      gpgconf_config_load_cb, &comp);
Packit d7e8d0
  if (err)
Packit d7e8d0
    {
Packit d7e8d0
      gpgconf_release (comp);
Packit d7e8d0
      return err;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  cur_comp = comp;
Packit d7e8d0
  while (!err && cur_comp)
Packit d7e8d0
    {
Packit d7e8d0
      err = gpgconf_read (engine, "--list-options", cur_comp->name,
Packit d7e8d0
			  gpgconf_config_load_cb2, cur_comp);
Packit d7e8d0
      cur_comp = cur_comp->next;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  if (err)
Packit d7e8d0
    {
Packit d7e8d0
      gpgconf_release (comp);
Packit d7e8d0
      return err;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  *comp_p = comp;
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0

Packit d7e8d0
gpgme_error_t
Packit d7e8d0
_gpgme_conf_arg_new (gpgme_conf_arg_t *arg_p,
Packit d7e8d0
		     gpgme_conf_type_t type, const void *value)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_conf_arg_t arg;
Packit d7e8d0
Packit d7e8d0
  arg = calloc (1, sizeof (*arg));
Packit d7e8d0
  if (!arg)
Packit d7e8d0
    return gpg_error_from_syserror ();
Packit d7e8d0
Packit d7e8d0
  if (!value)
Packit d7e8d0
    arg->no_arg = 1;
Packit d7e8d0
  else
Packit d7e8d0
    {
Packit d7e8d0
      /* We need to switch on type here because the alt-type is not
Packit d7e8d0
         yet known.  */
Packit d7e8d0
      switch (type)
Packit d7e8d0
	{
Packit d7e8d0
	case GPGME_CONF_NONE:
Packit d7e8d0
	case GPGME_CONF_UINT32:
Packit d7e8d0
	  arg->value.uint32 = *((unsigned int *) value);
Packit d7e8d0
	  break;
Packit d7e8d0
Packit d7e8d0
	case GPGME_CONF_INT32:
Packit d7e8d0
	  arg->value.int32 = *((int *) value);
Packit d7e8d0
	  break;
Packit d7e8d0
Packit d7e8d0
	case GPGME_CONF_STRING:
Packit d7e8d0
	case GPGME_CONF_FILENAME:
Packit d7e8d0
	case GPGME_CONF_LDAP_SERVER:
Packit d7e8d0
        case GPGME_CONF_KEY_FPR:
Packit d7e8d0
        case GPGME_CONF_PUB_KEY:
Packit d7e8d0
        case GPGME_CONF_SEC_KEY:
Packit d7e8d0
        case GPGME_CONF_ALIAS_LIST:
Packit d7e8d0
	  arg->value.string = strdup (value);
Packit d7e8d0
	  if (!arg->value.string)
Packit d7e8d0
	    {
Packit d7e8d0
	      free (arg);
Packit d7e8d0
	      return gpg_error_from_syserror ();
Packit d7e8d0
	    }
Packit d7e8d0
	  break;
Packit d7e8d0
Packit d7e8d0
	default:
Packit d7e8d0
	  free (arg);
Packit d7e8d0
	  return gpg_error (GPG_ERR_INV_VALUE);
Packit d7e8d0
	}
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  *arg_p = arg;
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
void
Packit d7e8d0
_gpgme_conf_arg_release (gpgme_conf_arg_t arg, gpgme_conf_type_t type)
Packit d7e8d0
{
Packit d7e8d0
  /* Lacking the alt_type we need to switch on type here.  */
Packit d7e8d0
  switch (type)
Packit d7e8d0
    {
Packit d7e8d0
    case GPGME_CONF_NONE:
Packit d7e8d0
    case GPGME_CONF_UINT32:
Packit d7e8d0
    case GPGME_CONF_INT32:
Packit d7e8d0
    case GPGME_CONF_STRING:
Packit d7e8d0
    default:
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    case GPGME_CONF_FILENAME:
Packit d7e8d0
    case GPGME_CONF_LDAP_SERVER:
Packit d7e8d0
    case GPGME_CONF_KEY_FPR:
Packit d7e8d0
    case GPGME_CONF_PUB_KEY:
Packit d7e8d0
    case GPGME_CONF_SEC_KEY:
Packit d7e8d0
    case GPGME_CONF_ALIAS_LIST:
Packit d7e8d0
      type = GPGME_CONF_STRING;
Packit d7e8d0
      break;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  release_arg (arg, type);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
gpgme_error_t
Packit d7e8d0
_gpgme_conf_opt_change (gpgme_conf_opt_t opt, int reset, gpgme_conf_arg_t arg)
Packit d7e8d0
{
Packit d7e8d0
  if (reset)
Packit d7e8d0
    {
Packit d7e8d0
      if (opt->new_value)
Packit d7e8d0
	release_arg (opt->new_value, opt->alt_type);
Packit d7e8d0
      opt->new_value = NULL;
Packit d7e8d0
      opt->change_value = 0;
Packit d7e8d0
    }
Packit d7e8d0
  else
Packit d7e8d0
    {
Packit d7e8d0
      /* Support self-assignment, for example for adding an item to an
Packit d7e8d0
	 existing list.  */
Packit d7e8d0
      if (opt->new_value && arg != opt->new_value)
Packit d7e8d0
	release_arg (opt->new_value, opt->alt_type);
Packit d7e8d0
      opt->new_value = arg;
Packit d7e8d0
      opt->change_value = 1;
Packit d7e8d0
    }
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0

Packit d7e8d0
/* FIXME: Major problem: We don't get errors from gpgconf.  */
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
gpgconf_write (void *engine, const char *arg1, char *arg2, gpgme_data_t conf)
Packit d7e8d0
{
Packit d7e8d0
  struct engine_gpgconf *gpgconf = engine;
Packit d7e8d0
  gpgme_error_t err = 0;
Packit d7e8d0
#define BUFLEN 1024
Packit d7e8d0
  char buf[BUFLEN];
Packit d7e8d0
  int buflen = 0;
Packit d7e8d0
  char *argv[7];
Packit d7e8d0
  int argc = 0;
Packit d7e8d0
  int rp[2] = { -1, -1 };
Packit d7e8d0
  int errp[2] = { -1, -1 };
Packit d7e8d0
  struct spawn_fd_item_s cfd[] = { {-1, 0 /* STDIN_FILENO */},
Packit d7e8d0
                                   {-1, 2 /* STDERR_FILENO */, -1},
Packit d7e8d0
                                   {-1, -1} };
Packit d7e8d0
  int status;
Packit d7e8d0
  int nwrite;
Packit d7e8d0
Packit d7e8d0
  /* _gpgme_engine_new guarantees that this is not NULL.  */
Packit d7e8d0
  argv[argc++] = gpgconf->file_name;
Packit d7e8d0
Packit d7e8d0
  if (gpgconf->home_dir && have_gpgconf_version (gpgconf, "2.1.13"))
Packit d7e8d0
    {
Packit d7e8d0
      argv[argc++] = (char*)"--homedir";
Packit d7e8d0
      argv[argc++] = gpgconf->home_dir;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  argv[argc++] = (char*)"--runtime";
Packit d7e8d0
  argv[argc++] = (char*)arg1;
Packit d7e8d0
  argv[argc++] = arg2;
Packit d7e8d0
  argv[argc] = NULL;
Packit d7e8d0
  assert (argc < DIM (argv));
Packit d7e8d0
Packit d7e8d0
  if (_gpgme_io_pipe (rp, 0) < 0)
Packit d7e8d0
    {
Packit d7e8d0
      err = gpg_error_from_syserror ();
Packit d7e8d0
      goto leave;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  if (_gpgme_io_pipe (errp, 1) < 0)
Packit d7e8d0
    {
Packit d7e8d0
      err = gpg_error_from_syserror ();
Packit d7e8d0
      goto leave;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  cfd[0].fd = rp[0];
Packit d7e8d0
  cfd[1].fd = errp[1];
Packit d7e8d0
Packit d7e8d0
  status = _gpgme_io_spawn (gpgconf->file_name, argv,
Packit d7e8d0
                            IOSPAWN_FLAG_DETACHED, cfd, NULL, NULL, NULL);
Packit d7e8d0
  if (status < 0)
Packit d7e8d0
    {
Packit d7e8d0
      err = gpg_error_from_syserror ();
Packit d7e8d0
      goto leave;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  rp[0] = -1;
Packit d7e8d0
  errp[1] = -1;
Packit d7e8d0
Packit d7e8d0
  for (;;)
Packit d7e8d0
    {
Packit d7e8d0
      if (buflen == 0)
Packit d7e8d0
	{
Packit d7e8d0
	  do
Packit d7e8d0
	    {
Packit d7e8d0
	      buflen = gpgme_data_read (conf, buf, BUFLEN);
Packit d7e8d0
	    }
Packit d7e8d0
	  while (buflen < 0 && errno == EAGAIN);
Packit d7e8d0
Packit d7e8d0
	  if (buflen < 0)
Packit d7e8d0
	    {
Packit d7e8d0
	      err = gpg_error_from_syserror ();
Packit d7e8d0
              goto leave;
Packit d7e8d0
	    }
Packit d7e8d0
	  else if (buflen == 0)
Packit d7e8d0
	    {
Packit d7e8d0
	      /* All is written.  */
Packit d7e8d0
	      _gpgme_io_close (rp[1]);
Packit d7e8d0
              rp[1] = -1;
Packit d7e8d0
Packit d7e8d0
              for (;;)
Packit d7e8d0
                {
Packit d7e8d0
                  do
Packit d7e8d0
                    {
Packit d7e8d0
                      buflen = _gpgme_io_read (errp[0], buf, BUFLEN);
Packit d7e8d0
                    }
Packit d7e8d0
                  while (buflen < 0 && errno == EAGAIN);
Packit d7e8d0
Packit d7e8d0
                  if (buflen == 0)
Packit d7e8d0
                    {
Packit d7e8d0
                      err = 0;
Packit d7e8d0
                      goto leave;
Packit d7e8d0
                    }
Packit d7e8d0
                  /* XXX: Do something useful with BUF.  */
Packit d7e8d0
                }
Packit d7e8d0
	    }
Packit d7e8d0
	}
Packit d7e8d0
Packit d7e8d0
      do
Packit d7e8d0
	{
Packit d7e8d0
	  nwrite = _gpgme_io_write (rp[1], buf, buflen);
Packit d7e8d0
	}
Packit d7e8d0
      while (nwrite < 0 && errno == EAGAIN);
Packit d7e8d0
Packit d7e8d0
      if (nwrite > 0)
Packit d7e8d0
	{
Packit d7e8d0
	  buflen -= nwrite;
Packit d7e8d0
	  if (buflen > 0)
Packit d7e8d0
	    memmove (&buf[0], &buf[nwrite], buflen);
Packit d7e8d0
	}
Packit d7e8d0
      else if (nwrite < 0)
Packit d7e8d0
	{
Packit d7e8d0
	  err = gpg_error_from_syserror ();
Packit d7e8d0
          goto leave;
Packit d7e8d0
	}
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  assert (! "reached");
Packit d7e8d0
Packit d7e8d0
 leave:
Packit d7e8d0
  if (rp[0] != -1)
Packit d7e8d0
    _gpgme_io_close (rp[0]);
Packit d7e8d0
  if (rp[1] != -1)
Packit d7e8d0
  _gpgme_io_close (rp[1]);
Packit d7e8d0
  if (errp[0] != -1)
Packit d7e8d0
    _gpgme_io_close (errp[0]);
Packit d7e8d0
  if (errp[1] != -1)
Packit d7e8d0
  _gpgme_io_close (errp[1]);
Packit d7e8d0
Packit d7e8d0
  return err;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
arg_to_data (gpgme_data_t conf, gpgme_conf_opt_t option, gpgme_conf_arg_t arg)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_error_t err = 0;
Packit d7e8d0
  int amt = 0;
Packit d7e8d0
  char buf[16];
Packit d7e8d0
Packit d7e8d0
  while (amt >= 0 && arg)
Packit d7e8d0
    {
Packit d7e8d0
      switch (option->alt_type)
Packit d7e8d0
	{
Packit d7e8d0
	case GPGME_CONF_NONE:
Packit d7e8d0
	case GPGME_CONF_UINT32:
Packit d7e8d0
	default:
Packit d7e8d0
	  snprintf (buf, sizeof (buf), "%u", arg->value.uint32);
Packit d7e8d0
	  buf[sizeof (buf) - 1] = '\0';
Packit d7e8d0
	  amt = gpgme_data_write (conf, buf, strlen (buf));
Packit d7e8d0
	  break;
Packit d7e8d0
Packit d7e8d0
	case GPGME_CONF_INT32:
Packit d7e8d0
	  snprintf (buf, sizeof (buf), "%i", arg->value.uint32);
Packit d7e8d0
	  buf[sizeof (buf) - 1] = '\0';
Packit d7e8d0
	  amt = gpgme_data_write (conf, buf, strlen (buf));
Packit d7e8d0
	  break;
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
	case GPGME_CONF_STRING:
Packit d7e8d0
          /* The complex types below are only here to silent the
Packit d7e8d0
             compiler warning. */
Packit d7e8d0
        case GPGME_CONF_FILENAME:
Packit d7e8d0
        case GPGME_CONF_LDAP_SERVER:
Packit d7e8d0
        case GPGME_CONF_KEY_FPR:
Packit d7e8d0
        case GPGME_CONF_PUB_KEY:
Packit d7e8d0
        case GPGME_CONF_SEC_KEY:
Packit d7e8d0
        case GPGME_CONF_ALIAS_LIST:
Packit d7e8d0
          if (arg->value.string)
Packit d7e8d0
            {
Packit d7e8d0
              /* One quote character, and three times to allow for
Packit d7e8d0
                 percent escaping.  */
Packit d7e8d0
              char *ptr = arg->value.string;
Packit d7e8d0
              amt = gpgme_data_write (conf, "\"", 1);
Packit d7e8d0
              if (amt < 0)
Packit d7e8d0
                break;
Packit d7e8d0
Packit d7e8d0
              while (!err && *ptr)
Packit d7e8d0
                {
Packit d7e8d0
                  switch (*ptr)
Packit d7e8d0
                    {
Packit d7e8d0
                    case '%':
Packit d7e8d0
                      amt = gpgme_data_write (conf, "%25", 3);
Packit d7e8d0
                      break;
Packit d7e8d0
Packit d7e8d0
                    case ':':
Packit d7e8d0
                      amt = gpgme_data_write (conf, "%3a", 3);
Packit d7e8d0
                      break;
Packit d7e8d0
Packit d7e8d0
                    case ',':
Packit d7e8d0
                      amt = gpgme_data_write (conf, "%2c", 3);
Packit d7e8d0
                      break;
Packit d7e8d0
Packit d7e8d0
                    default:
Packit d7e8d0
                      amt = gpgme_data_write (conf, ptr, 1);
Packit d7e8d0
                    }
Packit d7e8d0
                  ptr++;
Packit d7e8d0
                }
Packit d7e8d0
            }
Packit d7e8d0
	  break;
Packit d7e8d0
	}
Packit d7e8d0
Packit d7e8d0
      if (amt < 0)
Packit d7e8d0
	break;
Packit d7e8d0
Packit d7e8d0
      arg = arg->next;
Packit d7e8d0
      /* Comma separator.  */
Packit d7e8d0
      if (arg)
Packit d7e8d0
	amt = gpgme_data_write (conf, ",", 1);
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  if (amt < 0)
Packit d7e8d0
    return gpg_error_from_syserror ();
Packit d7e8d0
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
gpgconf_conf_save (void *engine, gpgme_conf_comp_t comp)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_error_t err;
Packit d7e8d0
  int amt = 0;
Packit d7e8d0
  /* We use a data object to store the new configuration.  */
Packit d7e8d0
  gpgme_data_t conf;
Packit d7e8d0
  gpgme_conf_opt_t option;
Packit d7e8d0
  int something_changed = 0;
Packit d7e8d0
Packit d7e8d0
  err = gpgme_data_new (&conf;;
Packit d7e8d0
  if (err)
Packit d7e8d0
    return err;
Packit d7e8d0
Packit d7e8d0
  option = comp->options;
Packit d7e8d0
  while (!err && amt >= 0 && option)
Packit d7e8d0
    {
Packit d7e8d0
      if (option->change_value)
Packit d7e8d0
	{
Packit d7e8d0
	  unsigned int flags = 0;
Packit d7e8d0
	  char buf[16];
Packit d7e8d0
Packit d7e8d0
	  something_changed = 1;
Packit d7e8d0
Packit d7e8d0
	  amt = gpgme_data_write (conf, option->name, strlen (option->name));
Packit d7e8d0
	  if (amt >= 0)
Packit d7e8d0
	    amt = gpgme_data_write (conf, ":", 1);
Packit d7e8d0
	  if (amt < 0)
Packit d7e8d0
	    break;
Packit d7e8d0
Packit d7e8d0
	  if (!option->new_value)
Packit d7e8d0
	    flags |= GPGME_CONF_DEFAULT;
Packit d7e8d0
	  snprintf (buf, sizeof (buf), "%u", flags);
Packit d7e8d0
	  buf[sizeof (buf) - 1] = '\0';
Packit d7e8d0
Packit d7e8d0
	  amt = gpgme_data_write (conf, buf, strlen (buf));
Packit d7e8d0
	  if (amt >= 0)
Packit d7e8d0
	    amt = gpgme_data_write (conf, ":", 1);
Packit d7e8d0
	  if (amt < 0)
Packit d7e8d0
	    break;
Packit d7e8d0
Packit d7e8d0
	  if (option->new_value)
Packit d7e8d0
	    {
Packit d7e8d0
	      err = arg_to_data (conf, option, option->new_value);
Packit d7e8d0
	      if (err)
Packit d7e8d0
		break;
Packit d7e8d0
	    }
Packit d7e8d0
	  amt = gpgme_data_write (conf, "\n", 1);
Packit d7e8d0
	}
Packit d7e8d0
      option = option->next;
Packit d7e8d0
    }
Packit d7e8d0
  if (!err && amt < 0)
Packit d7e8d0
    err = gpg_error_from_syserror ();
Packit d7e8d0
  if (err || !something_changed)
Packit d7e8d0
    goto bail;
Packit d7e8d0
Packit d7e8d0
  err = gpgme_data_seek (conf, 0, SEEK_SET);
Packit d7e8d0
  if (err)
Packit d7e8d0
    goto bail;
Packit d7e8d0
Packit d7e8d0
  err = gpgconf_write (engine, "--change-options", comp->name, conf);
Packit d7e8d0
 bail:
Packit d7e8d0
  gpgme_data_release (conf);
Packit d7e8d0
  return err;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
struct gpgconf_config_dir_s
Packit d7e8d0
{
Packit d7e8d0
  const char *what;
Packit d7e8d0
  char *result;
Packit d7e8d0
};
Packit d7e8d0
Packit d7e8d0
/* Called for each line in the gpgconf --list-dirs output.  Searches
Packit d7e8d0
   for the desired line and returns the result, indicating success by
Packit d7e8d0
   a special error value GPG_ERR_USER_1 (which terminates the
Packit d7e8d0
   operation immediately).  */
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
gpgconf_config_dir_cb (void *hook, char *line)
Packit d7e8d0
{
Packit d7e8d0
  /* This is an input- and output-parameter.  */
Packit d7e8d0
  struct gpgconf_config_dir_s *data = (struct gpgconf_config_dir_s *) hook;
Packit d7e8d0
  int len = strlen(data->what);
Packit d7e8d0
Packit d7e8d0
  if (!strncmp(line, data->what, len) && line[len] == ':')
Packit d7e8d0
    {
Packit d7e8d0
      char *result = strdup(&line[len + 1]);
Packit d7e8d0
      if (!result)
Packit d7e8d0
	return gpg_error_from_syserror ();
Packit d7e8d0
      data->result = result;
Packit d7e8d0
      return gpg_error(GPG_ERR_USER_1);
Packit d7e8d0
    }
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Like gpgme_get_dirinfo, but uses the home directory of ENGINE and
Packit d7e8d0
   does not cache the result.  */
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
gpgconf_conf_dir (void *engine, const char *what, char **result)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_error_t err;
Packit d7e8d0
  struct gpgconf_config_dir_s data;
Packit d7e8d0
Packit d7e8d0
  data.what = what;
Packit d7e8d0
  data.result = NULL;
Packit d7e8d0
  err = gpgconf_read (engine, "--list-dirs", NULL,
Packit d7e8d0
		      gpgconf_config_dir_cb, &data);
Packit d7e8d0
  if (gpg_err_code (err) == GPG_ERR_USER_1)
Packit d7e8d0
    {
Packit d7e8d0
      /* This signals to us that a result was found.  */
Packit d7e8d0
      *result = data.result;
Packit d7e8d0
      return 0;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  if (!err)
Packit d7e8d0
    err = gpg_error(GPG_ERR_NOT_FOUND);
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Parse a line received from gpgconf --query-swdb.  This function may
Packit d7e8d0
 * modify LINE.  The result is stored at RESUL.  */
Packit d7e8d0
static gpg_error_t
Packit d7e8d0
parse_swdb_line (char *line, gpgme_query_swdb_result_t result)
Packit d7e8d0
{
Packit d7e8d0
  char *field[9];
Packit d7e8d0
  int fields = 0;
Packit d7e8d0
  gpg_err_code_t ec;
Packit d7e8d0
Packit d7e8d0
  while (line && fields < DIM (field))
Packit d7e8d0
    {
Packit d7e8d0
      field[fields++] = line;
Packit d7e8d0
      line = strchr (line, ':');
Packit d7e8d0
      if (line)
Packit d7e8d0
	*line++ = 0;
Packit d7e8d0
    }
Packit d7e8d0
  /* We require that all fields exists - gpgme emits all these fields
Packit d7e8d0
   * even on error.  They might be empty, though. */
Packit d7e8d0
  if (fields < 9)
Packit d7e8d0
    return gpg_error (GPG_ERR_INV_ENGINE);
Packit d7e8d0
Packit d7e8d0
  free (result->name);
Packit d7e8d0
  result->name = strdup (field[0]);
Packit d7e8d0
  if (!result->name)
Packit d7e8d0
    return gpg_error_from_syserror ();
Packit d7e8d0
Packit d7e8d0
  free (result->iversion);
Packit d7e8d0
  result->iversion = strdup (field[1]);
Packit d7e8d0
  if (!result->iversion)
Packit d7e8d0
    return gpg_error_from_syserror ();
Packit d7e8d0
Packit d7e8d0
  result->urgent = (strtol (field[3], NULL, 10) > 0);
Packit d7e8d0
Packit d7e8d0
  ec = gpg_err_code (strtoul (field[4], NULL, 10));
Packit d7e8d0
Packit d7e8d0
  result->created  = _gpgme_parse_timestamp (field[5], NULL);
Packit d7e8d0
  result->retrieved= _gpgme_parse_timestamp (field[6], NULL);
Packit d7e8d0
Packit d7e8d0
  free (result->version);
Packit d7e8d0
  result->version  = strdup (field[7]);
Packit d7e8d0
  if (!result->version)
Packit d7e8d0
    return gpg_error_from_syserror ();
Packit d7e8d0
Packit d7e8d0
  result->reldate  = _gpgme_parse_timestamp (field[8], NULL);
Packit d7e8d0
Packit d7e8d0
  /* Set other flags.  */
Packit d7e8d0
  result->warning = !!ec;
Packit d7e8d0
  result->update = 0;
Packit d7e8d0
  result->noinfo = 0;
Packit d7e8d0
  result->unknown = 0;
Packit d7e8d0
  result->tooold = 0;
Packit d7e8d0
  result->error = 0;
Packit d7e8d0
Packit d7e8d0
  switch (*field[2])
Packit d7e8d0
    {
Packit d7e8d0
    case '-': result->warning = 1; break;
Packit d7e8d0
    case '?': result->unknown = result->warning = 1; break;
Packit d7e8d0
    case 'u': result->update = 1; break;
Packit d7e8d0
    case 'c': break;
Packit d7e8d0
    case 'n': break;
Packit d7e8d0
    default:
Packit d7e8d0
      result->warning = 1;
Packit d7e8d0
      if (!ec)
Packit d7e8d0
        ec = GPG_ERR_INV_ENGINE;
Packit d7e8d0
      break;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  if (ec == GPG_ERR_TOO_OLD)
Packit d7e8d0
    result->tooold = 1;
Packit d7e8d0
  else if (ec == GPG_ERR_ENOENT)
Packit d7e8d0
    result->noinfo = 1;
Packit d7e8d0
  else if (ec)
Packit d7e8d0
    result->error = 1;
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
gpgconf_query_swdb (void *engine,
Packit d7e8d0
                    const char *name, const char *iversion,
Packit d7e8d0
                    gpgme_query_swdb_result_t result)
Packit d7e8d0
{
Packit d7e8d0
  struct engine_gpgconf *gpgconf = engine;
Packit d7e8d0
  gpgme_error_t err = 0;
Packit d7e8d0
  char *linebuf;
Packit d7e8d0
  size_t linebufsize;
Packit d7e8d0
  int linelen;
Packit d7e8d0
  char *argv[7];
Packit d7e8d0
  int argc = 0;
Packit d7e8d0
  int rp[2];
Packit d7e8d0
  struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0},
Packit d7e8d0
				   {-1, -1} };
Packit d7e8d0
  int status;
Packit d7e8d0
  int nread;
Packit d7e8d0
  char *mark = NULL;
Packit d7e8d0
Packit d7e8d0
  if (!have_gpgconf_version (gpgconf, "2.1.16"))
Packit d7e8d0
    return gpg_error (GPG_ERR_ENGINE_TOO_OLD);
Packit d7e8d0
Packit d7e8d0
  /* _gpgme_engine_new guarantees that this is not NULL.  */
Packit d7e8d0
  argv[argc++] = gpgconf->file_name;
Packit d7e8d0
Packit d7e8d0
  if (gpgconf->home_dir)
Packit d7e8d0
    {
Packit d7e8d0
      argv[argc++] = (char*)"--homedir";
Packit d7e8d0
      argv[argc++] = gpgconf->home_dir;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  argv[argc++] = (char*)"--query-swdb";
Packit d7e8d0
  argv[argc++] = (char*)name;
Packit d7e8d0
  argv[argc++] = (char*)iversion;
Packit d7e8d0
  argv[argc] = NULL;
Packit d7e8d0
  assert (argc < DIM (argv));
Packit d7e8d0
Packit d7e8d0
  if (_gpgme_io_pipe (rp, 1) < 0)
Packit d7e8d0
    return gpg_error_from_syserror ();
Packit d7e8d0
Packit d7e8d0
  cfd[0].fd = rp[1];
Packit d7e8d0
Packit d7e8d0
  status = _gpgme_io_spawn (gpgconf->file_name, argv,
Packit d7e8d0
                            IOSPAWN_FLAG_DETACHED, cfd, NULL, NULL, NULL);
Packit d7e8d0
  if (status < 0)
Packit d7e8d0
    {
Packit d7e8d0
      _gpgme_io_close (rp[0]);
Packit d7e8d0
      _gpgme_io_close (rp[1]);
Packit d7e8d0
      return gpg_error_from_syserror ();
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  linebufsize = 2048; /* Same as used by gpgconf.  */
Packit d7e8d0
  linebuf = malloc (linebufsize);
Packit d7e8d0
  if (!linebuf)
Packit d7e8d0
    {
Packit d7e8d0
      err = gpg_error_from_syserror ();
Packit d7e8d0
      goto leave;
Packit d7e8d0
    }
Packit d7e8d0
  linelen = 0;
Packit d7e8d0
Packit d7e8d0
  while ((nread = _gpgme_io_read (rp[0], linebuf + linelen,
Packit d7e8d0
                                  linebufsize - linelen - 1)))
Packit d7e8d0
    {
Packit d7e8d0
      char *line;
Packit d7e8d0
      const char *lastmark = NULL;
Packit d7e8d0
      size_t nused;
Packit d7e8d0
Packit d7e8d0
      if (nread < 0)
Packit d7e8d0
        {
Packit d7e8d0
          err = gpg_error_from_syserror ();
Packit d7e8d0
          goto leave;
Packit d7e8d0
        }
Packit d7e8d0
Packit d7e8d0
      linelen += nread;
Packit d7e8d0
      linebuf[linelen] = '\0';
Packit d7e8d0
Packit d7e8d0
      for (line=linebuf; (mark = strchr (line, '\n')); line = mark+1 )
Packit d7e8d0
        {
Packit d7e8d0
          lastmark = mark;
Packit d7e8d0
          if (mark > line && mark[-1] == '\r')
Packit d7e8d0
            mark[-1] = '\0';
Packit d7e8d0
          else
Packit d7e8d0
            mark[0] = '\0';
Packit d7e8d0
Packit d7e8d0
          /* Got a full line.  Due to the CR removal code (which
Packit d7e8d0
             occurs only on Windows) we might be one-off and thus
Packit d7e8d0
             would see empty lines.  */
Packit d7e8d0
          if (*line)
Packit d7e8d0
            {
Packit d7e8d0
              err = parse_swdb_line (line, result);
Packit d7e8d0
              goto leave; /* Ready.  */
Packit d7e8d0
            }
Packit d7e8d0
          else /* empty line.  */
Packit d7e8d0
            err = 0;
Packit d7e8d0
        }
Packit d7e8d0
Packit d7e8d0
      nused = lastmark? (lastmark + 1 - linebuf) : 0;
Packit d7e8d0
      memmove (linebuf, linebuf + nused, linelen - nused);
Packit d7e8d0
      linelen -= nused;
Packit d7e8d0
Packit d7e8d0
      if (!(linelen < linebufsize - 1))
Packit d7e8d0
        {
Packit d7e8d0
          char *newlinebuf;
Packit d7e8d0
Packit d7e8d0
          if (linelen <  8 * 1024 - 1)
Packit d7e8d0
            linebufsize = 8 * 1024;
Packit d7e8d0
          else if (linelen < 64 * 1024 - 1)
Packit d7e8d0
            linebufsize = 64 * 1024;
Packit d7e8d0
          else
Packit d7e8d0
            {
Packit d7e8d0
              /* We reached our limit - give up.  */
Packit d7e8d0
              err = gpg_error (GPG_ERR_LINE_TOO_LONG);
Packit d7e8d0
              goto leave;
Packit d7e8d0
            }
Packit d7e8d0
Packit d7e8d0
          newlinebuf = realloc (linebuf, linebufsize);
Packit d7e8d0
          if (!newlinebuf)
Packit d7e8d0
            {
Packit d7e8d0
              err = gpg_error_from_syserror ();
Packit d7e8d0
              goto leave;
Packit d7e8d0
            }
Packit d7e8d0
          linebuf = newlinebuf;
Packit d7e8d0
        }
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
 leave:
Packit d7e8d0
  free (linebuf);
Packit d7e8d0
  _gpgme_io_close (rp[0]);
Packit d7e8d0
  return err;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static void
Packit d7e8d0
gpgconf_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
Packit d7e8d0
{
Packit d7e8d0
  (void)engine;
Packit d7e8d0
  (void)io_cbs;
Packit d7e8d0
  /* Nothing to do.  */
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0

Packit d7e8d0
/* Currently, we do not use the engine interface for the various
Packit d7e8d0
   operations.  */
Packit d7e8d0
void
Packit d7e8d0
_gpgme_conf_release (gpgme_conf_comp_t conf)
Packit d7e8d0
{
Packit d7e8d0
  gpgconf_config_release (conf);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0

Packit d7e8d0
struct engine_ops _gpgme_engine_ops_gpgconf =
Packit d7e8d0
  {
Packit d7e8d0
    /* Static functions.  */
Packit d7e8d0
    _gpgme_get_default_gpgconf_name,
Packit d7e8d0
    NULL,
Packit d7e8d0
    gpgconf_get_version,
Packit d7e8d0
    gpgconf_get_req_version,
Packit d7e8d0
    gpgconf_new,
Packit d7e8d0
Packit d7e8d0
    /* Member functions.  */
Packit d7e8d0
    gpgconf_release,
Packit d7e8d0
    NULL,		/* reset */
Packit d7e8d0
    NULL,               /* set_status_cb */
Packit d7e8d0
    NULL,		/* set_status_handler */
Packit d7e8d0
    NULL,		/* set_command_handler */
Packit d7e8d0
    NULL,		/* set_colon_line_handler */
Packit d7e8d0
    NULL,		/* set_locale */
Packit d7e8d0
    NULL,		/* set_protocol */
Packit d7e8d0
    NULL,		/* decrypt */
Packit d7e8d0
    NULL,		/* delete */
Packit d7e8d0
    NULL,		/* edit */
Packit d7e8d0
    NULL,		/* encrypt */
Packit d7e8d0
    NULL,		/* encrypt_sign */
Packit d7e8d0
    NULL,		/* export */
Packit d7e8d0
    NULL,		/* export_ext */
Packit d7e8d0
    NULL,		/* genkey */
Packit d7e8d0
    NULL,		/* import */
Packit d7e8d0
    NULL,		/* keylist */
Packit d7e8d0
    NULL,		/* keylist_ext */
Packit d7e8d0
    NULL,               /* keylist_data */
Packit d7e8d0
    NULL,               /* keysign */
Packit d7e8d0
    NULL,               /* tofu_policy */
Packit d7e8d0
    NULL,		/* sign */
Packit d7e8d0
    NULL,		/* trustlist */
Packit d7e8d0
    NULL,		/* verify */
Packit d7e8d0
    NULL,		/* getauditlog */
Packit d7e8d0
    NULL,               /* opassuan_transact */
Packit d7e8d0
    gpgconf_conf_load,
Packit d7e8d0
    gpgconf_conf_save,
Packit d7e8d0
    gpgconf_conf_dir,
Packit d7e8d0
    gpgconf_query_swdb,
Packit d7e8d0
    gpgconf_set_io_cbs,
Packit d7e8d0
    NULL,		/* io_event */
Packit d7e8d0
    NULL,		/* cancel */
Packit d7e8d0
    NULL,               /* cancel_op */
Packit d7e8d0
    NULL,               /* passwd */
Packit d7e8d0
    NULL,               /* set_pinentry_mode */
Packit d7e8d0
    NULL                /* opspawn */
Packit d7e8d0
  };