2008-01-04 Marcus Brinkmann <marcus@g10code.de>
authorMarcus Brinkmann <mb@g10code.com>
Fri, 4 Jan 2008 14:31:15 +0000 (14:31 +0000)
committerMarcus Brinkmann <mb@g10code.com>
Fri, 4 Jan 2008 14:31:15 +0000 (14:31 +0000)
* configure.ac: Support gpgconf.

gpgme/
2008-01-04  Marcus Brinkmann  <marcus@g10code.de>

* Makefile.am (gpgconf_components): New variable.
(main_sources): Add gpgconf.c.
* gpgme.h (gpgme_protocol_t): New protocol GPGME_PROTOCOL_GPGCONF.
(gpgme_conf_level_t, gpgme_conf_type_t, gpgme_conf_arg_t)
(gpgme_conf_opt_t, gpgme_conf_comp_t, gpgme_conf_arg_new)
(gpgme_conf_arg_release, gpgme_conf_opt_change)
(gpgme_conf_release, gpgme_op_conf_load, gpgme_op_conf_save): New
types.
* gpgconf.c, engine-gpgconf.c: New files.
* engine.h: (_gpgme_engine_op_conf_load,
(_gpgme_engine_op_conf_save): New prototypes.
* op-support.c (_gpgme_op_reset): Ignore not implemented locale
function.
* posix-util.c (_gpgme_get_gpgconf_path): New function.
* w32-util.c (_gpgme_get_gpgconf_path): New function.
* engine-gpgsm.c:
(_gpgme_engine_ops_gpgsm): Add stubs for conf_load and conf_save.
* rungpg.c:
(_gpgme_engine_ops_gpg): Add stubs for conf_load and conf_save.
* gpgme.def: Add new gpgconf related interfaces.
* libgpgme.vers: Likewise.
* util.h (_gpgme_get_gpgconf_path): New prototype.
* gpgme.h (gpgme_protocol_t): Add GPGME_PROTOCOL_GPGCONF.
* engine-backend.h (_gpgme_engine_ops_gpgconf): New prototype.
(struct engine_ops): Add members for conf_load and conf_save.
* engine.c (engine_ops): Add _gpgme_engine_ops_gpgconf.
(_gpgme_engine_op_conf_load,
(_gpgme_engine_op_conf_save): New functions.
(gpgme_get_engine_info): Allow protocol GPGME_PROTOCOL_GPGCONF.

tests/
2008-01-04  Marcus Brinkmann  <marcus@g10code.de>

* Makefile.am (TESTS_ENVIRONMENT): Use absolute path for
GNUPGHOME.
* gpg/Makefile.am (TESTS_ENVIRONMENT): Use absolute path for
GNUPGHOME.
* gpgsm/Makefile.am (TESTS_ENVIRONMENT): Use absolute path for
GNUPGHOME.
* gpg/Makefile.am (TESTS): Add t-gpgconf.
t-gpgconf.c: New file.

23 files changed:
ChangeLog
configure.ac
gpgme/ChangeLog
gpgme/Makefile.am
gpgme/engine-backend.h
gpgme/engine-gpgconf.c [new file with mode: 0644]
gpgme/engine-gpgsm.c
gpgme/engine.c
gpgme/engine.h
gpgme/gpgconf.c [new file with mode: 0644]
gpgme/gpgme.def
gpgme/gpgme.h
gpgme/libgpgme.vers
gpgme/op-support.c
gpgme/posix-util.c
gpgme/rungpg.c
gpgme/util.h
gpgme/w32-util.c
tests/ChangeLog
tests/Makefile.am
tests/gpg/Makefile.am
tests/gpg/t-gpgconf.c [new file with mode: 0644]
tests/gpgsm/Makefile.am

index 5cb11bc..4c77add 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2008-01-04  Marcus Brinkmann  <marcus@g10code.de>
+
+       * configure.ac: Support gpgconf.
+
 2007-09-27  Marcus Brinkmann  <marcus@g10code.de>
 
        * assuan-pipe-connect.c (pipe_connect_gpgme): Do not close process
index 5145d12..fcae5ab 100644 (file)
@@ -114,6 +114,7 @@ AM_CONDITIONAL(HAVE_LD_VERSION_SCRIPT, test "$have_ld_version_script" = "yes")
 
 GPG_DEFAULT=no
 GPGSM_DEFAULT=no
+GPGCONF_DEFAULT=no
 component_system=None
 have_dosish_system=no
 have_w32_system=no
@@ -126,6 +127,7 @@ case "${host}" in
         have_w32_system=yes
         GPG_DEFAULT='c:\\gnupg\\gpg.exe'
        GPGSM_DEFAULT='c:\\gnupg\\gpgsm.exe'
+        GPGCONF_DEFAULT='c:\\gnupg\\gpgconf.exe'
         #component_system='COM+'
 
        AM_PATH_GLIB_2_0
@@ -158,6 +160,7 @@ case "${host}" in
        # XXX: Probably use exec-prefix here?
 #      GPG_DEFAULT='/usr/bin/gpg'
 #      GPGSM_DEFAULT='/usr/bin/gpgsm'
+#      GPGCONF_DEFAULT='/usr/bin/gpgconf'
        ;;
 esac
 
@@ -261,8 +264,10 @@ AC_DEFINE(GPG_ERR_SOURCE_DEFAULT, GPG_ERR_SOURCE_GPGME,
 # Checks for system services
 NEED_GPG_VERSION_DEFAULT=1.3.0
 NEED_GPGSM_VERSION_DEFAULT=1.9.6
+NEED_GPGCONF_VERSION_DEFAULT=2.0.4
 NEED_GPG_VERSION="$NEED_GPG_VERSION_DEFAULT"
 NEED_GPGSM_VERSION="$NEED_GPGSM_VERSION_DEFAULT"
+NEED_GPGCONF_VERSION="$NEED_GPGCONF_VERSION_DEFAULT"
 AC_ARG_WITH(gpg-version,
            AC_HELP_STRING([--with-gpg-version=VER], [require GnuPG version VER]),
            NEED_GPG_VERSION=$withval)
@@ -281,11 +286,22 @@ fi
 if test "$NEED_GPGSM_VERSION" = "no"; then
   NEED_GPGSM_VERSION=0.0.0
 fi
+AC_ARG_WITH(gpgconf-version,
+           AC_HELP_STRING([--with-gpgconf-version=VER], [require GPGCONF version VER]),
+           NEED_GPGCONF_VERSION=$withval)
+if test "$NEED_GPGCONF_VERSION" = "yes"; then
+  NEED_GPGCONF_VERSION="$NEED_GPGCONF_VERSION_DEFAULT"
+fi
+if test "$NEED_GPGCONF_VERSION" = "no"; then
+  NEED_GPGCONF_VERSION=0.0.0
+fi
 
 AC_DEFINE_UNQUOTED(NEED_GPG_VERSION, "$NEED_GPG_VERSION",
                                     [Min. needed GnuPG version.])
 AC_DEFINE_UNQUOTED(NEED_GPGSM_VERSION, "$NEED_GPGSM_VERSION",
                                       [Min. needed GPGSM version.])
+AC_DEFINE_UNQUOTED(NEED_GPGCONF_VERSION, "$NEED_GPGCONF_VERSION",
+                                        [Min. needed GPGCONF version.])
 
 
 NO_OVERRIDE=no
@@ -477,6 +493,109 @@ AC_ARG_ENABLE(gpgsm-test,
 AM_CONDITIONAL(RUN_GPGSM_TESTS, test "$run_gpgsm_test" = "yes")
 
 
+NO_OVERRIDE=no
+AC_ARG_WITH(gpgconf,
+           AC_HELP_STRING([--with-gpgconf=PATH],
+                           [use gpgconf binary at PATH]),
+           GPGCONF=$withval, NO_OVERRIDE=yes)
+if test "$NO_OVERRIDE" = "yes" || test "$GPGCONF" = "yes"; then
+  GPGCONF=
+  NO_OVERRIDE=yes
+  if test "$cross_compiling" != "yes"; then
+    AC_PATH_PROG(GPGCONF, gpgconf)
+  fi
+  if test -z "$GPGCONF"; then
+    GPGCONF="$GPGCONF_DEFAULT"
+  fi
+fi
+if test "$GPGCONF" = no; then
+  if test "$NO_OVERRIDE" = "yes"; then
+    if test "$cross_compiling" != "yes"; then
+      AC_MSG_WARN([
+***
+*** Could not find gpgconf, install gpgconf or use --with-gpgconf=PATH to enable it
+***])
+    else
+      AC_MSG_ERROR([
+***
+*** Can not determine path to gpgconf when cross-compiling, use --with-gpgconf=PATH
+***])
+    fi
+  fi
+else
+  AC_DEFINE_UNQUOTED(GPGCONF_PATH, "$GPGCONF", [Path to the GPGCONF binary.])
+  AC_DEFINE(ENABLE_GPGCONF,1,[Whether GPGCONF support is enabled])
+fi
+AM_CONDITIONAL(HAVE_GPGCONF, test "$GPGCONF" != "no")
+
+dnl Check for GPGCONF version requirement.
+GPGCONF_VERSION=unknown
+ok=maybe
+if test -z "$GPGCONF" -o "x$GPGCONF" = "xno"; then
+  ok=no
+else
+  if test "$cross_compiling" = "yes"; then
+    AC_MSG_WARN([GPGCONF version can not be checked when cross compiling])
+    ok=no
+  else
+    if test ! -x "$GPGCONF"; then
+      AC_MSG_WARN([GPGCONF not executable, version check disabled])
+      ok=no
+    fi
+  fi
+fi
+if test "$ok" = "maybe"; then
+  AC_MSG_CHECKING(for GPGCONF >= $NEED_GPGCONF_VERSION)
+  req_major=`echo $NEED_GPGCONF_VERSION | \
+             sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\1/'`
+  req_minor=`echo $NEED_GPGCONF_VERSION | \
+            sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\2/'`
+  req_micro=`echo $NEED_GPGCONF_VERSION | \
+            sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\3/'`
+  GPGCONF_VERSION=`$GPGCONF --version | sed -n '1 s/[[^0-9]]*\(.*\)/\1/p'`
+  major=`echo $GPGCONF_VERSION | \
+        sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\1/'`
+  minor=`echo $GPGCONF_VERSION | \
+        sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\2/'`
+  micro=`echo $GPGCONF_VERSION | \
+        sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\3/'`
+
+  if test "$major" -gt "$req_major"; then
+    ok=yes
+  else
+    if test "$major" -eq "$req_major"; then
+      if test "$minor" -gt "$req_minor"; then
+        ok=yes
+      else
+        if test "$minor" -eq "$req_minor"; then
+          if test "$micro" -ge "$req_micro"; then
+            ok=yes
+         fi
+        fi
+      fi
+    fi
+  fi
+  if test "$ok" = "yes"; then
+    AC_MSG_RESULT(yes)
+  else
+    AC_MSG_RESULT(no)
+    AC_MSG_WARN([GPGCONF must be at least version $NEED_GPGCONF_VERSION])
+  fi
+fi
+run_gpgconf_test="$ok"
+AC_ARG_ENABLE(gpgconf-test,
+  AC_HELP_STRING([--disable-gpgconf-test], [disable GPGCONF run test]),
+         run_gpgconf_test=$enableval)
+AM_CONDITIONAL(RUN_GPGCONF_TESTS, test "$run_gpgconf_test" = "yes")
+
+# Only build if supported.
+AM_CONDITIONAL(BUILD_GPGCONF, test "$GPGCONF" != "no")
+if test "$GPGCONF" != "no"; then
+  AC_DEFINE(HAVE_GPGCONF, 1,
+            [Defined if we are building with gpgconf support.])
+fi
+
+
 # FIXME: Only build if supported.
 AM_CONDITIONAL(BUILD_ASSUAN, test "$GPGSM" != "no")
 if test "$GPGSM" != "no"; then
@@ -634,12 +753,15 @@ AC_OUTPUT
 echo "
        GPGME v${VERSION} has been configured as follows:
 
-       GnuPG path:    $GPG
-        GnuPG version: $GPG_VERSION, min. $NEED_GPG_VERSION
+       GnuPG path:      $GPG
+        GnuPG version:   $GPG_VERSION, min. $NEED_GPG_VERSION
+
+       GpgSM path:      $GPGSM
+        GpgSM version:   $GPGSM_VERSION, min. $NEED_GPGSM_VERSION
 
-       GpgSM path:    $GPGSM
-        GpgSM version: $GPGSM_VERSION, min. $NEED_GPGSM_VERSION
+       GpgConf path:    $GPGCONF
+        GpgConf version: $GPGCONF_VERSION, min. $NEED_GPGCONF_VERSION
 
-       GPGME Pthread: $have_pthread
-       GPGME Pth:     $have_pth
+       GPGME Pthread:   $have_pthread
+       GPGME Pth:       $have_pth
 "
index f942d83..ea6a9a0 100644 (file)
@@ -1,3 +1,35 @@
+2008-01-04  Marcus Brinkmann  <marcus@g10code.de>
+
+       * Makefile.am (gpgconf_components): New variable.
+       (main_sources): Add gpgconf.c.
+       * gpgme.h (gpgme_protocol_t): New protocol GPGME_PROTOCOL_GPGCONF.
+       (gpgme_conf_level_t, gpgme_conf_type_t, gpgme_conf_arg_t)
+       (gpgme_conf_opt_t, gpgme_conf_comp_t, gpgme_conf_arg_new)
+       (gpgme_conf_arg_release, gpgme_conf_opt_change)
+       (gpgme_conf_release, gpgme_op_conf_load, gpgme_op_conf_save): New
+       types.
+       * gpgconf.c, engine-gpgconf.c: New files.
+       * engine.h: (_gpgme_engine_op_conf_load,
+       (_gpgme_engine_op_conf_save): New prototypes.
+       * op-support.c (_gpgme_op_reset): Ignore not implemented locale
+       function.
+       * posix-util.c (_gpgme_get_gpgconf_path): New function.
+       * w32-util.c (_gpgme_get_gpgconf_path): New function.
+       * engine-gpgsm.c:
+       (_gpgme_engine_ops_gpgsm): Add stubs for conf_load and conf_save.
+       * rungpg.c:
+       (_gpgme_engine_ops_gpg): Add stubs for conf_load and conf_save.
+       * gpgme.def: Add new gpgconf related interfaces.
+       * libgpgme.vers: Likewise.
+       * util.h (_gpgme_get_gpgconf_path): New prototype.
+       * gpgme.h (gpgme_protocol_t): Add GPGME_PROTOCOL_GPGCONF.
+       * engine-backend.h (_gpgme_engine_ops_gpgconf): New prototype.
+       (struct engine_ops): Add members for conf_load and conf_save.
+       * engine.c (engine_ops): Add _gpgme_engine_ops_gpgconf.
+       (_gpgme_engine_op_conf_load,
+       (_gpgme_engine_op_conf_save): New functions.
+       (gpgme_get_engine_info): Allow protocol GPGME_PROTOCOL_GPGCONF.
+
 2007-11-28  Marcus Brinkmann  <marcus@g10code.de>
 
        * w32-util.c (_gpgme_get_gpg_path, _gpgme_get_gpgsm_path): Search
index f74dffe..bd0191a 100644 (file)
@@ -83,6 +83,12 @@ else
 gpgsm_components =
 endif
 
+if HAVE_GPGCONF
+gpgconf_components = engine-gpgconf.c
+else
+gpgconf_components =
+endif
+
 # These are the source files common to all library versions.  We used
 # to build a non-installed library for that, but that does not work
 # correctly on all platforms (in particular, one can not specify the
@@ -100,7 +106,8 @@ main_sources =                                                              \
        key.c keylist.c trust-item.c trustlist.c                        \
        import.c export.c genkey.c delete.c edit.c getauditlog.c        \
        engine.h engine-backend.h engine.c rungpg.c status-table.h      \
-       $(gpgsm_components) sema.h priv-io.h $(system_components)       \
+       $(gpgsm_components) $(gpgconf_components) gpgconf.c             \
+       sema.h priv-io.h $(system_components)                           \
        debug.c debug.h gpgme.c version.c error.c
 
 libgpgme_la_SOURCES = $(main_sources)                                  \
index f1b4dc0..2e2ef5e 100644 (file)
@@ -97,6 +97,9 @@ struct engine_ops
   gpgme_error_t  (*getauditlog) (void *engine, gpgme_data_t output,
                                  unsigned int flags);
 
+  gpgme_error_t  (*conf_load) (void *engine, gpgme_conf_comp_t *conf_p);
+  gpgme_error_t  (*conf_save) (void *engine, gpgme_conf_comp_t conf);
+
   void (*set_io_cbs) (void *engine, gpgme_io_cbs_t io_cbs);
   void (*io_event) (void *engine, gpgme_event_io_t type, void *type_data);
 
@@ -108,5 +111,8 @@ extern struct engine_ops _gpgme_engine_ops_gpg;             /* OpenPGP.  */
 #ifdef ENABLE_GPGSM
 extern struct engine_ops _gpgme_engine_ops_gpgsm;      /* CMS.  */
 #endif
+#ifdef ENABLE_GPGCONF
+extern struct engine_ops _gpgme_engine_ops_gpgconf;    /* gpg-conf.  */
+#endif
 
 #endif /* ENGINE_BACKEND_H */
diff --git a/gpgme/engine-gpgconf.c b/gpgme/engine-gpgconf.c
new file mode 100644 (file)
index 0000000..ee545f8
--- /dev/null
@@ -0,0 +1,877 @@
+// Check protocol.
+// IMPLEMENT NO_ARG_DESC!!!!
+
+/* engine-gpgconf.c - gpg-conf engine.
+   Copyright (C) 2000 Werner Koch (dd9jn)
+   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007 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, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA.  */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <assert.h>
+#include <unistd.h>
+#include <locale.h>
+#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"
+
+\f
+struct engine_gpgconf
+{
+  char *file_name;
+  char *home_dir;
+};
+
+typedef struct engine_gpgconf *engine_gpgconf_t;
+
+\f
+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;
+}
+
+\f
+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;
+}
+
+\f
+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 line[LINELENGTH] = "";
+  int linelen = 0;
+  char *argv[] = { NULL /* file_name */, arg1, arg2, 0 };
+  int rp[2];
+  struct spawn_fd_item_s pfd[] = { {0, -1}, {-1, -1} };
+  struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */}, {-1, -1} };
+  int status;
+  int nread;
+  char *mark = NULL;
+
+  /* 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 ();
+
+  pfd[0].fd = rp[1];
+  cfd[0].fd = rp[1];
+
+  status = _gpgme_io_spawn (gpgconf->file_name, argv, cfd, pfd);
+  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], &line[linelen], LINELENGTH - linelen - 1);
+      if (nread > 0)
+       {
+         line[linelen + nread] = '\0';
+         linelen += nread;
+
+         while ((mark = strchr (line, '\n')))
+           {
+             char *eol = mark;
+             
+             if (eol > &line[0] && *eol == '\r')
+               eol--;
+             *eol = '\0';
+             
+             /* Got a full line.  */
+             err = (*cb) (hook, line);
+             if (err)
+               break;
+             
+             linelen -= mark - line;
+             memmove (line, eol + 1, linelen);
+           }
+       }
+    }
+  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);
+
+  _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->description = strdup (field[2]);
+      if (!comp->description)
+       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;
+
+  mark = strchr (line, ',');
+  if (mark)
+    *mark = '\0';
+
+  while (line)
+    {
+      gpgme_conf_arg_t 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:
+           case GPGME_CONF_PATHNAME:
+           case GPGME_CONF_LDAP_SERVER:
+             /* 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 ();
+    }
+
+  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;
+}
+
+
+\f
+gpgme_error_t
+_gpgme_conf_arg_new (gpgme_conf_arg_t *arg_p,
+                    gpgme_conf_type_t type, 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
+    {
+      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_PATHNAME:
+       case GPGME_CONF_LDAP_SERVER:
+         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)
+{
+  switch (type)
+    {
+    case GPGME_CONF_NONE:
+    case GPGME_CONF_UINT32:
+    case GPGME_CONF_INT32:
+    case GPGME_CONF_STRING:
+    default:
+      break;
+       
+    case GPGME_CONF_PATHNAME:
+    case GPGME_CONF_LDAP_SERVER:
+      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 (opt->new_value)
+    release_arg (opt->new_value, opt->alt_type);
+
+  if (reset)
+    {
+      opt->new_value = NULL;
+      opt->change_value = 0;
+    }
+  else
+    {
+      opt->new_value = arg;
+      opt->change_value = 1;
+    }
+  return 0;
+}
+
+\f
+/* 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 pfd[] = { {1, -1}, {-1, -1} };
+  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;
+  argv[0] = "/home/marcus/g10/install/bin/gpgconf";
+
+  if (_gpgme_io_pipe (rp, 0) < 0)
+    return gpg_error_from_syserror ();
+
+  pfd[0].fd = rp[0];
+  cfd[0].fd = rp[0];
+
+  status = _gpgme_io_spawn (gpgconf->file_name, argv, cfd, pfd);
+  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:
+       case GPGME_CONF_PATHNAME:
+       case GPGME_CONF_LDAP_SERVER:
+         /* 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.  */
+}
+
+\f
+/* 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);
+}
+
+\f
+struct engine_ops _gpgme_engine_ops_gpgconf =
+  {
+    /* Static functions.  */
+    _gpgme_get_gpgconf_path,
+    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,              /* decrypt */
+    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 */
+    gpgconf_conf_load,
+    gpgconf_conf_save,
+    gpgconf_set_io_cbs,
+    NULL,              /* io_event */
+    NULL               /* cancel */
+  };
index 3815613..1e9ddd2 100644 (file)
@@ -1885,6 +1885,8 @@ struct engine_ops _gpgme_engine_ops_gpgsm =
     NULL,              /* trustlist */
     gpgsm_verify,
     gpgsm_getauditlog,
+    NULL,              /* conf_load */
+    NULL,              /* conf_save */
     gpgsm_set_io_cbs,
     gpgsm_io_event,
     gpgsm_cancel
index ef7147d..cf3fe9f 100644 (file)
@@ -47,7 +47,12 @@ static struct engine_ops *engine_ops[] =
   {
     &_gpgme_engine_ops_gpg,            /* OpenPGP.  */
 #ifdef ENABLE_GPGSM
-    &_gpgme_engine_ops_gpgsm           /* CMS.  */
+    &_gpgme_engine_ops_gpgsm,          /* CMS.  */
+#else
+    NULL,
+#endif
+#ifdef ENABLE_GPGCONF
+    &_gpgme_engine_ops_gpgconf         /* gpg-conf.  */
 #else
     NULL
 #endif
@@ -169,7 +174,8 @@ gpgme_get_engine_info (gpgme_engine_info_t *info)
     {
       gpgme_engine_info_t *lastp = &engine_info;
       gpgme_protocol_t proto_list[] = { GPGME_PROTOCOL_OpenPGP,
-                                       GPGME_PROTOCOL_CMS };
+                                       GPGME_PROTOCOL_CMS,
+                                       GPGME_PROTOCOL_GPGCONF };
       unsigned int proto;
 
       for (proto = 0; proto < DIM (proto_list); proto++)
@@ -724,6 +730,32 @@ _gpgme_engine_op_getauditlog (engine_t engine, gpgme_data_t output,
 }
 
 
+gpgme_error_t
+_gpgme_engine_op_conf_load (engine_t engine, gpgme_conf_comp_t *conf_p)
+{
+  if (!engine)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  if (!engine->ops->conf_load)
+    return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+  return (*engine->ops->conf_load) (engine->engine, conf_p);
+}
+
+
+gpgme_error_t
+_gpgme_engine_op_conf_save (engine_t engine, gpgme_conf_comp_t conf)
+{
+  if (!engine)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  if (!engine->ops->conf_save)
+    return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+  return (*engine->ops->conf_save) (engine->engine, conf);
+}
+
+
 void
 _gpgme_engine_set_io_cbs (engine_t engine, gpgme_io_cbs_t io_cbs)
 {
index 6c636e0..e67399e 100644 (file)
@@ -127,6 +127,11 @@ gpgme_error_t _gpgme_engine_op_getauditlog (engine_t engine,
                                             gpgme_data_t output,
                                             unsigned int flags);
 
+gpgme_error_t _gpgme_engine_op_conf_load (engine_t engine,
+                                         gpgme_conf_comp_t *conf_p);
+gpgme_error_t _gpgme_engine_op_conf_save (engine_t engine,
+                                         gpgme_conf_comp_t conf);
+
 void _gpgme_engine_set_io_cbs (engine_t engine,
                               gpgme_io_cbs_t io_cbs);
 void _gpgme_engine_io_event (engine_t engine,
diff --git a/gpgme/gpgconf.c b/gpgme/gpgconf.c
new file mode 100644 (file)
index 0000000..9fa2ce9
--- /dev/null
@@ -0,0 +1,134 @@
+/* gpgconf.c - GnuPG Made Easy.
+   Copyright (C) 2007 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, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA.  */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gpgme.h"
+
+#include "ops.h"
+#include "engine.h"
+
+#ifdef ENABLE_GPGCONF
+/* engine-gpgconf.c.  */
+gpgme_error_t _gpgme_conf_arg_new (gpgme_conf_arg_t *arg_p,
+                                 gpgme_conf_type_t type, void *value);
+void _gpgme_conf_arg_release (gpgme_conf_arg_t arg, gpgme_conf_type_t type);
+gpgme_error_t _gpgme_conf_opt_change (gpgme_conf_opt_t opt, int reset,
+                                     gpgme_conf_arg_t arg);
+void _gpgme_conf_release (gpgme_conf_comp_t conf);
+gpgme_error_t _gpgme_conf_load (void *engine, gpgme_conf_comp_t *conf_p);
+gpgme_error_t gpgme_op_conf_save (gpgme_ctx_t ctx, gpgme_conf_comp_t comp);
+
+#endif
+
+\f
+/* Allocate a new gpgme_conf_arg_t.  */
+gpgme_error_t
+gpgme_conf_arg_new (gpgme_conf_arg_t *arg_p,
+                   gpgme_conf_type_t type, void *value)
+{
+#ifdef ENABLE_GPGCONF
+  return _gpgme_conf_arg_new (arg_p, type, value);
+#else
+  return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+#endif
+}
+
+
+/* This also releases all chained argument structures!  */
+void
+gpgme_conf_arg_release (gpgme_conf_arg_t arg, gpgme_conf_type_t type)
+{
+#ifdef ENABLE_GPGCONF
+  return _gpgme_conf_arg_release (arg, type);
+#endif
+}
+
+
+/* Register a change for the value of OPT to ARG.  */
+gpgme_error_t
+gpgme_conf_opt_change (gpgme_conf_opt_t opt, int reset, gpgme_conf_arg_t arg)
+{
+#ifdef ENABLE_GPGCONF
+  return _gpgme_conf_opt_change (opt, reset, arg);
+#else
+  return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+#endif
+}
+
+
+
+/* Public function to release a gpgme_conf_comp list.  */
+void
+gpgme_conf_release (gpgme_conf_comp_t conf)
+{
+#ifdef ENABLE_GPGCONF
+  _gpgme_conf_release (conf);
+#endif
+}
+
+
+/* Public function to release load a configuration list.  No
+   asynchronous interface for now.  */
+gpgme_error_t
+gpgme_op_conf_load (gpgme_ctx_t ctx, gpgme_conf_comp_t *conf_p)
+{
+#ifdef ENABLE_GPGCONF
+  gpgme_error_t err;
+  gpgme_protocol_t proto = ctx->protocol;
+
+  ctx->protocol = GPGME_PROTOCOL_GPGCONF;
+  err = _gpgme_op_reset (ctx, 1);
+  if (err)
+    return err;
+
+  err = _gpgme_engine_op_conf_load (ctx->engine, conf_p);
+  ctx->protocol = proto;
+  return err;
+#else
+  return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+#endif
+}
+
+
+/* This function does not follow chained components!  */
+gpgme_error_t
+gpgme_op_conf_save (gpgme_ctx_t ctx, gpgme_conf_comp_t comp)
+{
+#ifdef ENABLE_GPGCONF
+  gpgme_error_t err;
+  gpgme_protocol_t proto = ctx->protocol;
+
+  ctx->protocol = GPGME_PROTOCOL_GPGCONF;
+  err = _gpgme_op_reset (ctx, 1);
+  if (err)
+    return err;
+
+  err = _gpgme_engine_op_conf_save (ctx->engine, comp);
+  ctx->protocol = proto;
+  return err;
+#else
+  return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+#endif
+}
+
+
index 57dbe40..3c5e335 100644 (file)
@@ -159,5 +159,12 @@ EXPORTS
     gpgme_op_getauditlog_start            @123
     gpgme_op_getauditlog                  @124
 
+    gpgme_conf_release                   @125
+    gpgme_conf_arg_new                   @126
+    gpgme_conf_arg_release               @127
+    gpgme_conf_opt_change                @128
+    gpgme_op_conf_load                   @129
+    gpgme_op_conf_save                   @130
+
 ; END
 
index b867419..bf42035 100644 (file)
@@ -72,7 +72,7 @@ extern "C" {
    AM_PATH_GPGME macro) check that this header matches the installed
    library.  Warning: Do not edit the next line.  configure will do
    that for you!  */
-#define GPGME_VERSION "1.1.6-svn1258"
+#define GPGME_VERSION "1.1.6-svn1282"
 
 
 \f
@@ -300,6 +300,7 @@ typedef enum
   {
     GPGME_PROTOCOL_OpenPGP = 0,  /* The default mode.  */
     GPGME_PROTOCOL_CMS     = 1,
+    GPGME_PROTOCOL_GPGCONF = 2,  /* Special code for gpgconf.  */
     GPGME_PROTOCOL_UNKNOWN = 255
   }
 gpgme_protocol_t;
@@ -1652,6 +1653,163 @@ gpgme_error_t gpgme_op_getauditlog_start (gpgme_ctx_t ctx, gpgme_data_t output,
 gpgme_error_t gpgme_op_getauditlog (gpgme_ctx_t ctx, gpgme_data_t output, 
                                     unsigned int flags);
 
+\f
+/* Interface to gpg-conf.  */
+
+/* The expert level at which a configuration option or group of
+   options should be displayed.  See the gpg-conf documentation for
+   more details.  */
+typedef enum
+  {
+    GPGME_CONF_BASIC = 0,
+    GPGME_CONF_ADVANCED = 1,
+    GPGME_CONF_EXPERT = 2,
+    GPGME_CONF_INVISIBLE = 3,
+    GPGME_CONF_INTERNAL = 4
+  }
+gpgme_conf_level_t;
+
+
+/* The data type of a configuration option argument.  See the gpg-conf
+   documentation for more details.  */
+typedef enum
+  {
+    /* Basic types.  */
+    GPGME_CONF_NONE = 0,
+    GPGME_CONF_STRING = 1,
+    GPGME_CONF_INT32 = 2,
+    GPGME_CONF_UINT32 = 3,
+
+    /* Complex types.  */
+    GPGME_CONF_PATHNAME = 32,
+    GPGME_CONF_LDAP_SERVER = 33
+  }
+gpgme_conf_type_t;
+
+
+/* This represents a single argument for a configuration option.
+   Which of the members of value is used depends on the ALT_TYPE.  */
+typedef struct gpgme_conf_arg
+{
+  struct gpgme_conf_arg *next;
+  /* True if the option appears without an (optional) argument.  */
+  unsigned int no_arg;
+  union
+  {
+    unsigned int count;
+    unsigned int uint32;
+    int int32;
+    char *string;
+  } value;
+} *gpgme_conf_arg_t;
+
+
+/* The flags of a configuration option.  See the gpg-conf
+   documentation for details.  */
+#define GPGME_CONF_GROUP       (1 << 0)
+#define GPGME_CONF_OPTIONAL    (1 << 1)
+#define GPGME_CONF_LIST                (1 << 2)
+#define GPGME_CONF_RUNTIME     (1 << 3)
+#define GPGME_CONF_DEFAULT     (1 << 4)
+#define GPGME_CONF_DEFAULT_DESC        (1 << 5)
+#define GPGME_CONF_NO_ARG_DESC (1 << 6)
+#define GPGME_CONF_NO_CHANGE   (1 << 7)
+
+
+/* The representation of a single configuration option.  See the
+   gpg-conf documentation for details.  */
+typedef struct gpgme_conf_opt
+{
+  struct gpgme_conf_opt *next;
+  
+  /* The option name.  */
+  char *name;
+
+  /* The flags for this option.  */
+  unsigned int flags;
+
+  /* The level of this option.  */
+  gpgme_conf_level_t level;
+
+  /* The localized description of this option.  */
+  char *description;
+
+  /* The type and alternate type of this option.  */
+  gpgme_conf_type_t type;
+  gpgme_conf_type_t alt_type;
+
+  /* The localized (short) name of the argument, if any.  */
+  char *argname;
+
+  /* The default value.  */
+  gpgme_conf_arg_t default_value;
+  char *default_description;
+  
+  /* The default value if the option is not set.  */
+  gpgme_conf_arg_t no_arg_value;
+  char *no_arg_description;
+
+  /* The current value if the option is set.  */
+  gpgme_conf_arg_t value;
+
+  /* The new value, if any.  NULL means reset to default.  */
+  int change_value;
+  gpgme_conf_arg_t new_value;
+
+  /* Free for application use.  */
+  void *user_data;
+} *gpgme_conf_opt_t;
+
+
+/* The representation of a component that can be configured.  See the
+   gpg-conf documentation for details.  */
+typedef struct gpgme_conf_comp
+{
+  struct gpgme_conf_comp *next;
+
+  /* Internal to GPGME, do not use!  */
+  gpgme_conf_opt_t *_last_opt_p;
+
+  /* The component name.  */
+  char *name;
+
+  /* A human-readable description for the component.  */
+  char *description;
+
+  /* The program name (an absolute path to the program).  */
+  char *program_name;  
+
+  /* A linked list of options for this component.  */
+  struct gpgme_conf_opt *options;
+} *gpgme_conf_comp_t;
+
+
+/* Allocate a new gpgme_conf_arg_t.  If VALUE is NULL, a "no arg
+   default" is prepared.  If type is a string type, VALUE should point
+   to the string.  Else, it should point to an unsigned or signed
+   integer respectively.  */
+gpgme_error_t gpgme_conf_arg_new (gpgme_conf_arg_t *arg_p,
+                                 gpgme_conf_type_t type, void *value);
+
+/* This also releases all chained argument structures!  */
+void gpgme_conf_arg_release (gpgme_conf_arg_t arg, gpgme_conf_type_t type);
+
+/* Register a change for the value of OPT to ARG.  If RESET is 1 (do
+   not use any values but 0 or 1), ARG is ignored and the option is
+   not changed (reverting a previous change).  Otherwise, if ARG is
+   NULL, the option is cleared or reset to its default.  */
+gpgme_error_t gpgme_conf_opt_change (gpgme_conf_opt_t opt, int reset,
+                                    gpgme_conf_arg_t arg);
+
+/* Release a set of configurations.  */
+void gpgme_conf_release (gpgme_conf_comp_t conf);
+/* Retrieve the current configurations.  */
+gpgme_error_t gpgme_op_conf_load (gpgme_ctx_t ctx, gpgme_conf_comp_t *conf_p);
+
+/* Save the configuration of component comp.  This function does not
+   follow chained components!  */
+gpgme_error_t gpgme_op_conf_save (gpgme_ctx_t ctx, gpgme_conf_comp_t comp);
 
 \f
 /* Various functions.  */
index 9f18daf..183a394 100644 (file)
@@ -39,6 +39,13 @@ GPGME_1.1 {
 
     gpgme_op_getauditlog_start;
     gpgme_op_getauditlog;
+
+    gpgme_conf_release;
+    gpgme_conf_arg_new;
+    gpgme_conf_arg_release;
+    gpgme_conf_opt_change;
+    gpgme_op_conf_load;
+    gpgme_op_conf_save;
 };
 
 
index 20c0738..1212c54 100644 (file)
@@ -115,6 +115,8 @@ _gpgme_op_reset (gpgme_ctx_t ctx, int type)
         err = _gpgme_engine_set_locale (ctx->engine,
                                         LC_MESSAGES, ctx->lc_messages);
 #endif
+      if (gpg_err_code (err) == GPG_ERR_NOT_IMPLEMENTED)
+       err = 0;
       if (err)
         {
           _gpgme_engine_release (ctx->engine);
index d5b4172..168dad6 100644 (file)
@@ -49,6 +49,16 @@ _gpgme_get_gpgsm_path (void)
 #endif
 }
 
+const char *
+_gpgme_get_gpgconf_path (void)
+{
+#ifdef GPGCONF_PATH
+  return GPGCONF_PATH;
+#else
+  return NULL;
+#endif
+}
+
 /* See w32-util.c */
 int
 _gpgme_get_conf_int (const char *key, int *value)
index c54da3a..0ffdc7c 100644 (file)
@@ -2120,7 +2120,9 @@ struct engine_ops _gpgme_engine_ops_gpg =
     gpg_sign,
     gpg_trustlist,
     gpg_verify,
-    NULL,
+    NULL,              /* getauditlog */
+    NULL,              /* conf_load */
+    NULL,              /* conf_save */
     gpg_set_io_cbs,
     gpg_io_event,
     gpg_cancel
index 28d5e19..0cd6ab5 100644 (file)
@@ -31,6 +31,7 @@
 /*-- {posix,w32}-util.c --*/
 const char *_gpgme_get_gpg_path (void);
 const char *_gpgme_get_gpgsm_path (void);
+const char *_gpgme_get_gpgconf_path (void);
 int _gpgme_get_conf_int (const char *key, int *value);
 
 \f
index 60c8396..b0f6df2 100644 (file)
@@ -274,6 +274,7 @@ find_program_in_inst_dir (const char *name)
   return result;
 }
 
+
 static char *
 find_program_at_standard_place (const char *name)
 {
@@ -313,6 +314,7 @@ _gpgme_get_gpg_path (void)
   return gpg_program;
 }
 
+
 const char *
 _gpgme_get_gpgsm_path (void)
 {
@@ -330,6 +332,24 @@ _gpgme_get_gpgsm_path (void)
 }
 
 
+const char *
+_gpgme_get_gpgconf_path (void)
+{
+  static char *gpgconf_program;
+
+  LOCK (get_path_lock);
+  if (!gpgconf_program)
+    gpgconf_program = find_program_in_registry ("gpgconfProgram");
+  if (!gpgconf_program)
+    gpgconf_program = find_program_in_inst_dir ("gpgconf.exe");
+  if (!gpgconf_program)
+    gpgconf_program
+      = find_program_at_standard_place ("GNU\\GnuPG\\gpgconf.exe");
+  UNLOCK (get_path_lock);
+  return gpgconf_program;
+}
+
+
 /* Return an integer value from gpgme specific configuration
    entries. VALUE receives that value; function returns true if a value
    has been configured and false if not. */
index 9e16ce2..dd7c396 100644 (file)
@@ -1,3 +1,14 @@
+2008-01-04  Marcus Brinkmann  <marcus@g10code.de>
+
+       * Makefile.am (TESTS_ENVIRONMENT): Use absolute path for
+       GNUPGHOME.
+       * gpg/Makefile.am (TESTS_ENVIRONMENT): Use absolute path for
+       GNUPGHOME.
+       * gpgsm/Makefile.am (TESTS_ENVIRONMENT): Use absolute path for
+       GNUPGHOME.
+       * gpg/Makefile.am (TESTS): Add t-gpgconf.
+       t-gpgconf.c: New file.
+
 2007-11-23  Marcus Brinkmann  <marcus@g10code.de>
 
        * gpgsm/t-verify.c (show_auditlog): Check for GPG_ERR_ASS_UNKNOWN_CMD.
index aee20ca..ceabc99 100644 (file)
@@ -20,7 +20,7 @@
 
 ## Process this file with automake to produce Makefile.in
 
-TESTS_ENVIRONMENT = GNUPGHOME=.
+TESTS_ENVIRONMENT = GNUPGHOME=$(abs_builddir)
 
 TESTS = t-version t-data t-engine-info
 
index e5d9de9..cac589f 100644 (file)
@@ -21,7 +21,7 @@
 
 GPG = @GPG@
 
-TESTS_ENVIRONMENT = GNUPGHOME=. GPG_AGENT_INFO=
+TESTS_ENVIRONMENT = GNUPGHOME=$(abs_builddir) GPG_AGENT_INFO=
 
 # The keylist tests must come after the import and the edit test.
 noinst_HEADERS = t-support.h
@@ -35,9 +35,9 @@ endif
 TESTS = t-encrypt t-encrypt-sym t-encrypt-sign t-sign t-signers                \
        t-decrypt t-verify t-decrypt-verify t-sig-notation t-export     \
        t-import t-trustlist t-edit t-keylist t-keylist-sig t-wait      \
-       t-encrypt-large t-file-name $(tests_unix)
+       t-encrypt-large t-file-name t-gpgconf $(tests_unix)
 
-CLEANFILES = secring.gpg pubring.gpg trustdb.gpg
+CLEANFILES = secring.gpg pubring.gpg trustdb.gpg dirmngr.conf
 DISTCLEANFILES = pubring.gpg~ random_seed gpg.conf
 
 EXTRA_DIST = mkdemodirs pubdemo.asc secdemo.asc cipher-1.asc cipher-2.asc \
diff --git a/tests/gpg/t-gpgconf.c b/tests/gpg/t-gpgconf.c
new file mode 100644 (file)
index 0000000..458bbe7
--- /dev/null
@@ -0,0 +1,305 @@
+/* t-gpgconf.c - Regression test.
+   Copyright (C) 2001, 2004, 2007 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, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA.  */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+
+#ifdef HAVE_W32_SYSTEM
+#include <windows.h>
+#endif
+
+#include <gpgme.h>
+
+
+#define fail_if_err(err)                                       \
+  do                                                           \
+    {                                                          \
+      if (err)                                                 \
+        {                                                      \
+          fprintf (stderr, "%s:%d: %s: %s\n",                  \
+                   __FILE__, __LINE__, gpgme_strsource (err),  \
+                  gpgme_strerror (err));                       \
+          exit (1);                                            \
+        }                                                      \
+    }                                                          \
+  while (0)
+
+
+void
+init_gpgme (gpgme_protocol_t proto)
+{
+  gpgme_error_t err;
+
+  gpgme_check_version (NULL);
+  setlocale (LC_ALL, "");
+  gpgme_set_locale (NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL));
+#ifndef HAVE_W32_SYSTEM
+  gpgme_set_locale (NULL, LC_MESSAGES, setlocale (LC_MESSAGES, NULL));
+#endif
+
+  err = gpgme_engine_check_version (proto);
+  fail_if_err (err);
+}
+
+\f
+static char *
+spaces (char *str, int extra)
+{
+  static char buf[80];
+  int len = str ? strlen (str) : 0;
+  int n;
+
+#define TABSTOP 30
+  n = TABSTOP - len - extra;
+
+  memset (buf, ' ', sizeof (buf));
+  if (n < 1 || n > (sizeof (buf) - 1))
+    {
+      buf[0] = '\n';
+      n = TABSTOP + 1;
+    }
+
+  buf[n] = '\0';
+  return buf;
+}
+
+
+void
+dump_arg (int type, gpgme_conf_arg_t arg)
+{
+  if (!arg)
+    {
+      printf ("(none)");
+      return;
+    }
+
+  while (arg)
+    {
+      switch (type)
+       {
+       case GPGME_CONF_STRING:
+       case GPGME_CONF_PATHNAME:
+       case GPGME_CONF_LDAP_SERVER:
+         printf ("%s", arg->value.string);
+         break;
+
+       case GPGME_CONF_UINT32:
+         printf ("%u", arg->value.uint32);
+         break;
+
+       case GPGME_CONF_INT32:
+         printf ("%i", arg->value.int32);
+         break;
+
+       case GPGME_CONF_NONE:
+         printf ("%i (times)", arg->value.count);
+         break;
+
+       default:
+         printf ("(unknown type)");
+       }
+
+      arg = arg->next;
+      if (arg)
+       printf (" ");
+    }
+}
+
+
+void
+dump_opt (gpgme_conf_opt_t opt)
+{
+  char level;
+  char runtime = (opt->flags & GPGME_CONF_RUNTIME) ? 'r' : ' ';
+
+  switch (opt->level)
+    {
+    case GPGME_CONF_BASIC:
+      level = 'b';
+      break;
+    case GPGME_CONF_ADVANCED:
+      level = 'a';
+      break;
+    case GPGME_CONF_EXPERT:
+      level = 'e';
+      break;
+    case GPGME_CONF_INVISIBLE:
+      level = 'i';
+      break;
+    case GPGME_CONF_INTERNAL:
+      level = '#';
+      break;
+    default:
+      level = '?';
+    }
+
+  if (opt->flags & GPGME_CONF_GROUP)
+    {
+      printf ("\n");
+      printf ("%c%c [%s]%s%s\n", level, runtime, opt->name, spaces (opt->name, 5),
+             opt->description
+             ? opt->description : "");
+    }
+  else
+    {
+      if (opt->argname)
+       {
+         char *more = (opt->flags & GPGME_CONF_LIST) ? "..." : "";
+      
+         if (opt->flags & GPGME_CONF_OPTIONAL)
+           {
+             printf ("%c%c --%s [%s%s] %s", level, runtime, opt->name, opt->argname, more,
+                     spaces (opt->name, 9 + strlen (opt->argname) + strlen (more)));
+           }
+         else
+           {
+             printf ("%c%c --%s %s%s %s", level, runtime, opt->name, opt->argname, more,
+                     spaces (opt->name, 7 + strlen (opt->argname) + strlen (more)));
+           }
+       }
+      else
+       printf ("%c%c --%s%s", level, runtime, opt->name, spaces (opt->name, 5));
+      
+      if (opt->description)
+       printf ("%s", opt->description);
+      printf ("\n");
+
+      if (opt->flags & GPGME_CONF_DEFAULT)
+       {
+         printf ("%s%s = ", spaces (NULL, 0), opt->argname ? opt->argname : "(default)");
+         dump_arg (opt->type, opt->default_value);
+         printf ("\n");
+       }
+      else if (opt->flags & GPGME_CONF_DEFAULT_DESC)
+       printf ("%s%s = %s\n", spaces (NULL, 0), opt->argname ? opt->argname : "(default)",
+               opt->default_description);
+
+      if (opt->no_arg_value)
+       {
+         printf ("%sNo Arg Def = ", spaces (NULL, 0));
+         dump_arg (opt->type, opt->no_arg_value);
+         printf ("\n");
+       }
+      if (opt->value)
+       {
+         printf ("%sCurrent = ", spaces (NULL, 0));
+         dump_arg (opt->type, opt->value);
+         printf ("\n");
+       }
+    }
+
+#if 0
+  arg = comp->options;
+  while (opt)
+    {
+      dump_opt (opt);
+      opt = opt->next;
+    }
+#endif
+}
+
+
+void
+dump_comp (gpgme_conf_comp_t comp)
+{
+  gpgme_conf_opt_t opt;
+
+  printf ("COMPONENT\n");
+  printf ("=========\n");
+  printf ("  Name: %s\n", comp->name);
+  if (comp->description)
+    printf ("  Desc: %s\n", comp->description);
+  if (comp->program_name)
+    printf ("  Path: %s\n", comp->program_name);
+  printf ("\n");
+
+  opt = comp->options;
+  while (opt)
+    {
+      dump_opt (opt);
+      opt = opt->next;
+    }
+}
+
+
+int 
+main (int argc, char **argv)
+{
+  gpgme_ctx_t ctx;
+  gpgme_error_t err;
+  gpgme_conf_comp_t conf;
+  gpgme_conf_comp_t comp;
+  int first;
+  init_gpgme (GPGME_PROTOCOL_GPGCONF);
+
+  err = gpgme_new (&ctx);
+  fail_if_err (err);
+
+  err = gpgme_op_conf_load (ctx, &conf);
+  fail_if_err (err);
+
+  comp = conf;
+  first = 1;
+  while (comp)
+    {
+      if (!first)
+       printf ("\n");
+      else
+       first = 0;
+      dump_comp (comp);
+      comp = comp->next;
+    }
+
+#if 1
+  /* Now change something.  */
+  {
+    unsigned int count = 1;
+    gpgme_conf_arg_t arg;
+    gpgme_conf_opt_t opt;
+
+    err = gpgme_conf_arg_new (&arg, GPGME_CONF_NONE, &count);
+    fail_if_err (err);
+
+    comp = conf;
+    while (comp && strcmp (comp->name, "dirmngr"))
+      comp = comp->next;
+    opt = comp->options;
+    while (opt && strcmp (opt->name, "verbose"))
+      opt = opt->next;
+
+    err = gpgme_conf_opt_change (opt, 0, arg);
+    fail_if_err (err);
+
+    err = gpgme_op_conf_save (ctx, comp);
+    fail_if_err (err);
+  }
+#endif
+
+  gpgme_conf_release (conf);
+
+  return 0;
+}
index 86c04b0..4d34b91 100644 (file)
@@ -21,7 +21,7 @@
 
 GPGSM = @GPGSM@
 
-TESTS_ENVIRONMENT = GNUPGHOME=. GPG_AGENT_INFO= 
+TESTS_ENVIRONMENT = GNUPGHOME=$(abs_builddir) GPG_AGENT_INFO= 
 
 noinst_HEADERS = t-support.h
 TESTS = t-import t-keylist t-encrypt t-verify t-decrypt t-sign t-export