Blame src/engine-gpgconf.c

Packit d7e8d0
/* engine-gpgconf.c - gpg-conf engine.
Packit Service 30b792
 * Copyright (C) 2000 Werner Koch (dd9jn)
Packit Service 30b792
 * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2008,
Packit Service 30b792
 *               2013 g10 Code GmbH
Packit Service 30b792
 *
Packit Service 30b792
 * This file is part of GPGME.
Packit Service 30b792
 *
Packit Service 30b792
 * GPGME is free software; you can redistribute it and/or modify it
Packit Service 30b792
 * under the terms of the GNU Lesser General Public License as
Packit Service 30b792
 * published by the Free Software Foundation; either version 2.1 of
Packit Service 30b792
 * the License, or (at your option) any later version.
Packit Service 30b792
 *
Packit Service 30b792
 * GPGME is distributed in the hope that it will be useful, but
Packit Service 30b792
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 30b792
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 30b792
 * Lesser General Public License for more details.
Packit Service 30b792
 *
Packit Service 30b792
 * You should have received a copy of the GNU Lesser General Public
Packit Service 30b792
 * License along with this program; if not, see <https://gnu.org/licenses/>.
Packit Service 30b792
 * SPDX-License-Identifier: LGPL-2.1-or-later
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 Service 30b792
 * modify LINE.  The result is stored at RESULT.  */
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 Service 30b792
    NULL,               /* set_engine_flags */
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
  };