summaryrefslogtreecommitdiff
path: root/src/engine-gpgconf.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine-gpgconf.c')
-rw-r--r--src/engine-gpgconf.c928
1 files changed, 928 insertions, 0 deletions
diff --git a/src/engine-gpgconf.c b/src/engine-gpgconf.c
new file mode 100644
index 0000000..6807dce
--- /dev/null
+++ b/src/engine-gpgconf.c
@@ -0,0 +1,928 @@
+/* engine-gpgconf.c - gpg-conf engine.
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2008 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <assert.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <fcntl.h> /* FIXME */
+#include <errno.h>
+
+#include "gpgme.h"
+#include "util.h"
+#include "ops.h"
+#include "wait.h"
+#include "priv-io.h"
+#include "sema.h"
+
+#include "assuan.h"
+#include "debug.h"
+
+#include "engine-backend.h"
+
+
+struct engine_gpgconf
+{
+ char *file_name;
+ char *home_dir;
+};
+
+typedef struct engine_gpgconf *engine_gpgconf_t;
+
+
+static char *
+gpgconf_get_version (const char *file_name)
+{
+ return _gpgme_get_program_version (file_name ? file_name
+ : _gpgme_get_gpgconf_path ());
+}
+
+
+static const char *
+gpgconf_get_req_version (void)
+{
+ return NEED_GPGCONF_VERSION;
+}
+
+
+static void
+gpgconf_release (void *engine)
+{
+ engine_gpgconf_t gpgconf = engine;
+
+ if (!gpgconf)
+ return;
+
+ if (gpgconf->file_name)
+ free (gpgconf->file_name);
+ if (gpgconf->home_dir)
+ free (gpgconf->home_dir);
+
+ free (gpgconf);
+}
+
+
+static gpgme_error_t
+gpgconf_new (void **engine, const char *file_name, const char *home_dir)
+{
+ gpgme_error_t err = 0;
+ engine_gpgconf_t gpgconf;
+
+ gpgconf = calloc (1, sizeof *gpgconf);
+ if (!gpgconf)
+ return gpg_error_from_errno (errno);
+
+ gpgconf->file_name = strdup (file_name ? file_name
+ : _gpgme_get_gpgconf_path ());
+ if (!gpgconf->file_name)
+ err = gpg_error_from_syserror ();
+
+ if (!err && home_dir)
+ {
+ gpgconf->home_dir = strdup (home_dir);
+ if (!gpgconf->home_dir)
+ err = gpg_error_from_syserror ();
+ }
+
+ if (err)
+ gpgconf_release (gpgconf);
+ else
+ *engine = gpgconf;
+
+ return err;
+}
+
+
+static void
+release_arg (gpgme_conf_arg_t arg, gpgme_conf_type_t alt_type)
+{
+ while (arg)
+ {
+ gpgme_conf_arg_t next = arg->next;
+
+ if (alt_type == GPGME_CONF_STRING)
+ free (arg->value.string);
+ free (arg);
+ arg = next;
+ }
+}
+
+
+static void
+release_opt (gpgme_conf_opt_t opt)
+{
+ if (opt->name)
+ free (opt->name);
+ if (opt->description)
+ free (opt->description);
+ if (opt->argname)
+ free (opt->argname);
+
+ release_arg (opt->default_value, opt->alt_type);
+ if (opt->default_description)
+ free (opt->default_description);
+
+ release_arg (opt->no_arg_value, opt->alt_type);
+ release_arg (opt->value, opt->alt_type);
+ release_arg (opt->new_value, opt->alt_type);
+
+ free (opt);
+}
+
+
+static void
+release_comp (gpgme_conf_comp_t comp)
+{
+ gpgme_conf_opt_t opt;
+
+ if (comp->name)
+ free (comp->name);
+ if (comp->description)
+ free (comp->description);
+ if (comp->program_name)
+ free (comp->program_name);
+
+ opt = comp->options;
+ while (opt)
+ {
+ gpgme_conf_opt_t next = opt->next;
+ release_opt (opt);
+ opt = next;
+ }
+
+ free (comp);
+}
+
+
+static void
+gpgconf_config_release (gpgme_conf_comp_t conf)
+{
+ while (conf)
+ {
+ gpgme_conf_comp_t next = conf->next;
+ release_comp (conf);
+ conf = next;
+ }
+}
+
+
+static gpgme_error_t
+gpgconf_read (void *engine, char *arg1, char *arg2,
+ gpgme_error_t (*cb) (void *hook, char *line),
+ void *hook)
+{
+ struct engine_gpgconf *gpgconf = engine;
+ gpgme_error_t err = 0;
+#define LINELENGTH 1024
+ char linebuf[LINELENGTH] = "";
+ int linelen = 0;
+ char *argv[4] = { NULL /* file_name */, NULL, NULL, NULL };
+ int rp[2];
+ struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0},
+ {-1, -1} };
+ int status;
+ int nread;
+ char *mark = NULL;
+
+ argv[1] = arg1;
+ argv[2] = arg2;
+
+
+ /* FIXME: Deal with engine->home_dir. */
+
+ /* _gpgme_engine_new guarantees that this is not NULL. */
+ argv[0] = gpgconf->file_name;
+
+ if (_gpgme_io_pipe (rp, 1) < 0)
+ return gpg_error_from_syserror ();
+
+ cfd[0].fd = rp[1];
+
+ status = _gpgme_io_spawn (gpgconf->file_name, argv, 0, cfd, NULL, NULL, NULL);
+ if (status < 0)
+ {
+ _gpgme_io_close (rp[0]);
+ _gpgme_io_close (rp[1]);
+ return gpg_error_from_syserror ();
+ }
+
+ do
+ {
+ nread = _gpgme_io_read (rp[0],
+ linebuf + linelen, LINELENGTH - linelen - 1);
+ if (nread > 0)
+ {
+ char *line;
+ const char *lastmark = NULL;
+ size_t nused;
+
+ linelen += nread;
+ linebuf[linelen] = '\0';
+
+ for (line=linebuf; (mark = strchr (line, '\n')); line = mark+1 )
+ {
+ lastmark = mark;
+ if (mark > line && mark[-1] == '\r')
+ mark[-1] = '\0';
+ else
+ mark[0] = '\0';
+
+ /* Got a full line. Due to the CR removal code (which
+ occurs only on Windows) we might be one-off and thus
+ would see empty lines. Don't pass them to the
+ callback. */
+ err = *line? (*cb) (hook, line) : 0;
+ if (err)
+ goto leave;
+ }
+
+ nused = lastmark? (lastmark + 1 - linebuf) : 0;
+ memmove (linebuf, linebuf + nused, linelen - nused);
+ linelen -= nused;
+ }
+ }
+ while (nread > 0 && linelen < LINELENGTH - 1);
+
+ if (!err && nread < 0)
+ err = gpg_error_from_syserror ();
+ if (!err && nread > 0)
+ err = gpg_error (GPG_ERR_LINE_TOO_LONG);
+
+ leave:
+ _gpgme_io_close (rp[0]);
+
+ return err;
+}
+
+
+static gpgme_error_t
+gpgconf_config_load_cb (void *hook, char *line)
+{
+ gpgme_conf_comp_t *comp_p = hook;
+ gpgme_conf_comp_t comp = *comp_p;
+#define NR_FIELDS 16
+ char *field[NR_FIELDS];
+ int fields = 0;
+
+ while (line && fields < NR_FIELDS)
+ {
+ field[fields++] = line;
+ line = strchr (line, ':');
+ if (line)
+ *(line++) = '\0';
+ }
+
+ /* We require at least the first 3 fields. */
+ if (fields < 2)
+ return gpg_error (GPG_ERR_INV_ENGINE);
+
+ /* Find the pointer to the new component in the list. */
+ while (comp && comp->next)
+ comp = comp->next;
+ if (comp)
+ comp_p = &comp->next;
+
+ comp = calloc (1, sizeof (*comp));
+ if (!comp)
+ return gpg_error_from_syserror ();
+ /* Prepare return value. */
+ comp->_last_opt_p = &comp->options;
+ *comp_p = comp;
+
+ comp->name = strdup (field[0]);
+ if (!comp->name)
+ return gpg_error_from_syserror ();
+
+ comp->description = strdup (field[1]);
+ if (!comp->description)
+ return gpg_error_from_syserror ();
+
+ if (fields >= 3)
+ {
+ comp->program_name = strdup (field[2]);
+ if (!comp->program_name)
+ return gpg_error_from_syserror ();
+ }
+
+ return 0;
+}
+
+
+static gpgme_error_t
+gpgconf_parse_option (gpgme_conf_opt_t opt,
+ gpgme_conf_arg_t *arg_p, char *line)
+{
+ gpgme_error_t err;
+ char *mark;
+
+ if (!line[0])
+ return 0;
+
+ while (line)
+ {
+ gpgme_conf_arg_t arg;
+
+ mark = strchr (line, ',');
+ if (mark)
+ *mark = '\0';
+
+ arg = calloc (1, sizeof (*arg));
+ if (!arg)
+ return gpg_error_from_syserror ();
+ *arg_p = arg;
+ arg_p = &arg->next;
+
+ if (*line == '\0')
+ arg->no_arg = 1;
+ else
+ {
+ switch (opt->alt_type)
+ {
+ /* arg->value.count is an alias for arg->value.uint32. */
+ case GPGME_CONF_NONE:
+ case GPGME_CONF_UINT32:
+ arg->value.uint32 = strtoul (line, NULL, 0);
+ break;
+
+ case GPGME_CONF_INT32:
+ arg->value.uint32 = strtol (line, NULL, 0);
+ break;
+
+ case GPGME_CONF_STRING:
+ /* The complex types below are only here to silent the
+ compiler warning. */
+ case GPGME_CONF_FILENAME:
+ case GPGME_CONF_LDAP_SERVER:
+ case GPGME_CONF_KEY_FPR:
+ case GPGME_CONF_PUB_KEY:
+ case GPGME_CONF_SEC_KEY:
+ case GPGME_CONF_ALIAS_LIST:
+ /* Skip quote character. */
+ line++;
+
+ err = _gpgme_decode_percent_string (line, &arg->value.string,
+ 0, 0);
+ if (err)
+ return err;
+ break;
+ }
+ }
+
+ /* Find beginning of next value. */
+ if (mark++ && *mark)
+ line = mark;
+ else
+ line = NULL;
+ }
+
+ return 0;
+}
+
+
+static gpgme_error_t
+gpgconf_config_load_cb2 (void *hook, char *line)
+{
+ gpgme_error_t err;
+ gpgme_conf_comp_t comp = hook;
+ gpgme_conf_opt_t *opt_p = comp->_last_opt_p;
+ gpgme_conf_opt_t opt;
+#define NR_FIELDS 16
+ char *field[NR_FIELDS];
+ int fields = 0;
+
+ while (line && fields < NR_FIELDS)
+ {
+ field[fields++] = line;
+ line = strchr (line, ':');
+ if (line)
+ *(line++) = '\0';
+ }
+
+ /* We require at least the first 10 fields. */
+ if (fields < 10)
+ return gpg_error (GPG_ERR_INV_ENGINE);
+
+ opt = calloc (1, sizeof (*opt));
+ if (!opt)
+ return gpg_error_from_syserror ();
+
+ comp->_last_opt_p = &opt->next;
+ *opt_p = opt;
+
+ if (field[0][0])
+ {
+ opt->name = strdup (field[0]);
+ if (!opt->name)
+ return gpg_error_from_syserror ();
+ }
+
+ opt->flags = strtoul (field[1], NULL, 0);
+
+ opt->level = strtoul (field[2], NULL, 0);
+
+ if (field[3][0])
+ {
+ opt->description = strdup (field[3]);
+ if (!opt->description)
+ return gpg_error_from_syserror ();
+ }
+
+ opt->type = strtoul (field[4], NULL, 0);
+
+ opt->alt_type = strtoul (field[5], NULL, 0);
+
+ if (field[6][0])
+ {
+ opt->argname = strdup (field[6]);
+ if (!opt->argname)
+ return gpg_error_from_syserror ();
+ }
+
+ if (opt->flags & GPGME_CONF_DEFAULT)
+ {
+ err = gpgconf_parse_option (opt, &opt->default_value, field[7]);
+ if (err)
+ return err;
+ }
+ else if ((opt->flags & GPGME_CONF_DEFAULT_DESC) && field[7][0])
+ {
+ opt->default_description = strdup (field[7]);
+ if (!opt->default_description)
+ return gpg_error_from_syserror ();
+ }
+
+ if (opt->flags & GPGME_CONF_NO_ARG_DESC)
+ {
+ opt->no_arg_description = strdup (field[8]);
+ if (!opt->no_arg_description)
+ return gpg_error_from_syserror ();
+ }
+ else
+ {
+ err = gpgconf_parse_option (opt, &opt->no_arg_value, field[8]);
+ if (err)
+ return err;
+ }
+
+ err = gpgconf_parse_option (opt, &opt->value, field[9]);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+
+static gpgme_error_t
+gpgconf_conf_load (void *engine, gpgme_conf_comp_t *comp_p)
+{
+ gpgme_error_t err;
+ gpgme_conf_comp_t comp = NULL;
+ gpgme_conf_comp_t cur_comp;
+
+ *comp_p = NULL;
+
+ err = gpgconf_read (engine, "--list-components", NULL,
+ gpgconf_config_load_cb, &comp);
+ if (err)
+ {
+ gpgconf_release (comp);
+ return err;
+ }
+
+ cur_comp = comp;
+ while (!err && cur_comp)
+ {
+ err = gpgconf_read (engine, "--list-options", cur_comp->name,
+ gpgconf_config_load_cb2, cur_comp);
+ cur_comp = cur_comp->next;
+ }
+
+ if (err)
+ {
+ gpgconf_release (comp);
+ return err;
+ }
+
+ *comp_p = comp;
+ return 0;
+}
+
+
+
+gpgme_error_t
+_gpgme_conf_arg_new (gpgme_conf_arg_t *arg_p,
+ gpgme_conf_type_t type, const void *value)
+{
+ gpgme_conf_arg_t arg;
+
+ arg = calloc (1, sizeof (*arg));
+ if (!arg)
+ return gpg_error_from_syserror ();
+
+ if (!value)
+ arg->no_arg = 1;
+ else
+ {
+ /* We need to switch on type here because the alt-type is not
+ yet known. */
+ switch (type)
+ {
+ case GPGME_CONF_NONE:
+ case GPGME_CONF_UINT32:
+ arg->value.uint32 = *((unsigned int *) value);
+ break;
+
+ case GPGME_CONF_INT32:
+ arg->value.int32 = *((int *) value);
+ break;
+
+ case GPGME_CONF_STRING:
+ case GPGME_CONF_FILENAME:
+ case GPGME_CONF_LDAP_SERVER:
+ case GPGME_CONF_KEY_FPR:
+ case GPGME_CONF_PUB_KEY:
+ case GPGME_CONF_SEC_KEY:
+ case GPGME_CONF_ALIAS_LIST:
+ arg->value.string = strdup (value);
+ if (!arg->value.string)
+ {
+ free (arg);
+ return gpg_error_from_syserror ();
+ }
+ break;
+
+ default:
+ free (arg);
+ return gpg_error (GPG_ERR_INV_VALUE);
+ }
+ }
+
+ *arg_p = arg;
+ return 0;
+}
+
+
+void
+_gpgme_conf_arg_release (gpgme_conf_arg_t arg, gpgme_conf_type_t type)
+{
+ /* Lacking the alt_type we need to switch on type here. */
+ switch (type)
+ {
+ case GPGME_CONF_NONE:
+ case GPGME_CONF_UINT32:
+ case GPGME_CONF_INT32:
+ case GPGME_CONF_STRING:
+ default:
+ break;
+
+ case GPGME_CONF_FILENAME:
+ case GPGME_CONF_LDAP_SERVER:
+ case GPGME_CONF_KEY_FPR:
+ case GPGME_CONF_PUB_KEY:
+ case GPGME_CONF_SEC_KEY:
+ case GPGME_CONF_ALIAS_LIST:
+ type = GPGME_CONF_STRING;
+ break;
+ }
+
+ release_arg (arg, type);
+}
+
+
+gpgme_error_t
+_gpgme_conf_opt_change (gpgme_conf_opt_t opt, int reset, gpgme_conf_arg_t arg)
+{
+ if (reset)
+ {
+ if (opt->new_value)
+ release_arg (opt->new_value, opt->alt_type);
+ opt->new_value = NULL;
+ opt->change_value = 0;
+ }
+ else
+ {
+ /* Support self-assignment, for example for adding an item to an
+ existing list. */
+ if (opt->new_value && arg != opt->new_value)
+ release_arg (opt->new_value, opt->alt_type);
+ opt->new_value = arg;
+ opt->change_value = 1;
+ }
+ return 0;
+}
+
+
+/* FIXME: Major problem: We don't get errors from gpgconf. */
+
+static gpgme_error_t
+gpgconf_write (void *engine, char *arg1, char *arg2, gpgme_data_t conf)
+{
+ struct engine_gpgconf *gpgconf = engine;
+ gpgme_error_t err = 0;
+#define BUFLEN 1024
+ char buf[BUFLEN];
+ int buflen = 0;
+ char *argv[] = { NULL /* file_name */, arg1, arg2, 0 };
+ int rp[2];
+ struct spawn_fd_item_s cfd[] = { {-1, 0 /* STDIN_FILENO */}, {-1, -1} };
+ int status;
+ int nwrite;
+
+ /* FIXME: Deal with engine->home_dir. */
+
+ /* _gpgme_engine_new guarantees that this is not NULL. */
+ argv[0] = gpgconf->file_name;
+
+ if (_gpgme_io_pipe (rp, 0) < 0)
+ return gpg_error_from_syserror ();
+
+ cfd[0].fd = rp[0];
+
+ status = _gpgme_io_spawn (gpgconf->file_name, argv, 0, cfd, NULL, NULL, NULL);
+ if (status < 0)
+ {
+ _gpgme_io_close (rp[0]);
+ _gpgme_io_close (rp[1]);
+ return gpg_error_from_syserror ();
+ }
+
+ for (;;)
+ {
+ if (buflen == 0)
+ {
+ do
+ {
+ buflen = gpgme_data_read (conf, buf, BUFLEN);
+ }
+ while (buflen < 0 && errno == EAGAIN);
+
+ if (buflen < 0)
+ {
+ err = gpg_error_from_syserror ();
+ _gpgme_io_close (rp[1]);
+ return err;
+ }
+ else if (buflen == 0)
+ {
+ /* All is written. */
+ _gpgme_io_close (rp[1]);
+ return 0;
+ }
+ }
+
+ do
+ {
+ nwrite = _gpgme_io_write (rp[1], buf, buflen);
+ }
+ while (nwrite < 0 && errno == EAGAIN);
+
+ if (nwrite > 0)
+ {
+ buflen -= nwrite;
+ if (buflen > 0)
+ memmove (&buf[0], &buf[nwrite], buflen);
+ }
+ else if (nwrite < 0)
+ {
+ _gpgme_io_close (rp[1]);
+ return gpg_error_from_syserror ();
+ }
+ }
+
+ return 0;
+}
+
+
+static gpgme_error_t
+arg_to_data (gpgme_data_t conf, gpgme_conf_opt_t option, gpgme_conf_arg_t arg)
+{
+ gpgme_error_t err = 0;
+ int amt = 0;
+ char buf[16];
+
+ while (amt >= 0 && arg)
+ {
+ switch (option->alt_type)
+ {
+ case GPGME_CONF_NONE:
+ case GPGME_CONF_UINT32:
+ default:
+ snprintf (buf, sizeof (buf), "%u", arg->value.uint32);
+ buf[sizeof (buf) - 1] = '\0';
+ amt = gpgme_data_write (conf, buf, strlen (buf));
+ break;
+
+ case GPGME_CONF_INT32:
+ snprintf (buf, sizeof (buf), "%i", arg->value.uint32);
+ buf[sizeof (buf) - 1] = '\0';
+ amt = gpgme_data_write (conf, buf, strlen (buf));
+ break;
+
+
+ case GPGME_CONF_STRING:
+ /* The complex types below are only here to silent the
+ compiler warning. */
+ case GPGME_CONF_FILENAME:
+ case GPGME_CONF_LDAP_SERVER:
+ case GPGME_CONF_KEY_FPR:
+ case GPGME_CONF_PUB_KEY:
+ case GPGME_CONF_SEC_KEY:
+ case GPGME_CONF_ALIAS_LIST:
+ /* One quote character, and three times to allow
+ for percent escaping. */
+ {
+ char *ptr = arg->value.string;
+ amt = gpgme_data_write (conf, "\"", 1);
+ if (amt < 0)
+ break;
+
+ while (!err && *ptr)
+ {
+ switch (*ptr)
+ {
+ case '%':
+ amt = gpgme_data_write (conf, "%25", 3);
+ break;
+
+ case ':':
+ amt = gpgme_data_write (conf, "%3a", 3);
+ break;
+
+ case ',':
+ amt = gpgme_data_write (conf, "%2c", 3);
+ break;
+
+ default:
+ amt = gpgme_data_write (conf, ptr, 1);
+ }
+ ptr++;
+ }
+ }
+ break;
+ }
+
+ if (amt < 0)
+ break;
+
+ arg = arg->next;
+ /* Comma separator. */
+ if (arg)
+ amt = gpgme_data_write (conf, ",", 1);
+ }
+
+ if (amt < 0)
+ return gpg_error_from_syserror ();
+
+ return 0;
+}
+
+
+static gpgme_error_t
+gpgconf_conf_save (void *engine, gpgme_conf_comp_t comp)
+{
+ gpgme_error_t err;
+ int amt = 0;
+ /* We use a data object to store the new configuration. */
+ gpgme_data_t conf;
+ gpgme_conf_opt_t option;
+ int something_changed = 0;
+
+ err = gpgme_data_new (&conf);
+ if (err)
+ return err;
+
+ option = comp->options;
+ while (!err && amt >= 0 && option)
+ {
+ if (option->change_value)
+ {
+ unsigned int flags = 0;
+ char buf[16];
+
+ something_changed = 1;
+
+ amt = gpgme_data_write (conf, option->name, strlen (option->name));
+ if (amt >= 0)
+ amt = gpgme_data_write (conf, ":", 1);
+ if (amt < 0)
+ break;
+
+ if (!option->new_value)
+ flags |= GPGME_CONF_DEFAULT;
+ snprintf (buf, sizeof (buf), "%u", flags);
+ buf[sizeof (buf) - 1] = '\0';
+
+ amt = gpgme_data_write (conf, buf, strlen (buf));
+ if (amt >= 0)
+ amt = gpgme_data_write (conf, ":", 1);
+ if (amt < 0)
+ break;
+
+ if (option->new_value)
+ {
+ err = arg_to_data (conf, option, option->new_value);
+ if (err)
+ break;
+ }
+ amt = gpgme_data_write (conf, "\n", 1);
+ }
+ option = option->next;
+ }
+ if (!err && amt < 0)
+ err = gpg_error_from_syserror ();
+ if (err || !something_changed)
+ goto bail;
+
+ err = gpgme_data_seek (conf, 0, SEEK_SET);
+ if (err)
+ goto bail;
+
+ err = gpgconf_write (engine, "--change-options", comp->name, conf);
+ bail:
+ gpgme_data_release (conf);
+ return err;
+}
+
+
+static void
+gpgconf_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
+{
+ /* Nothing to do. */
+}
+
+
+/* Currently, we do not use the engine interface for the various
+ operations. */
+void
+_gpgme_conf_release (gpgme_conf_comp_t conf)
+{
+ gpgconf_config_release (conf);
+}
+
+
+struct engine_ops _gpgme_engine_ops_gpgconf =
+ {
+ /* Static functions. */
+ _gpgme_get_gpgconf_path,
+ NULL,
+ gpgconf_get_version,
+ gpgconf_get_req_version,
+ gpgconf_new,
+
+ /* Member functions. */
+ gpgconf_release,
+ NULL, /* reset */
+ NULL, /* set_status_handler */
+ NULL, /* set_command_handler */
+ NULL, /* set_colon_line_handler */
+ NULL, /* set_locale */
+ NULL, /* set_protocol */
+ NULL, /* decrypt */
+ NULL, /* decrypt_verify */
+ NULL, /* delete */
+ NULL, /* edit */
+ NULL, /* encrypt */
+ NULL, /* encrypt_sign */
+ NULL, /* export */
+ NULL, /* export_ext */
+ NULL, /* genkey */
+ NULL, /* import */
+ NULL, /* keylist */
+ NULL, /* keylist_ext */
+ NULL, /* sign */
+ NULL, /* trustlist */
+ NULL, /* verify */
+ NULL, /* getauditlog */
+ NULL, /* opassuan_transact */
+ gpgconf_conf_load,
+ gpgconf_conf_save,
+ gpgconf_set_io_cbs,
+ NULL, /* io_event */
+ NULL /* cancel */
+ };