diff options
| author | Marcus Brinkmann <[email protected]> | 2008-01-04 14:31:15 +0000 | 
|---|---|---|
| committer | Marcus Brinkmann <[email protected]> | 2008-01-04 14:31:15 +0000 | 
| commit | 19025d791890e4c42931f0023a41fea45e866945 (patch) | |
| tree | 4d4e81f80669a804c30c4cc27e391dab5e8f1c1d | |
| parent | 2007-11-28 Marcus Brinkmann <[email protected]> (diff) | |
| download | gpgme-19025d791890e4c42931f0023a41fea45e866945.tar.gz gpgme-19025d791890e4c42931f0023a41fea45e866945.zip | |
2008-01-04  Marcus Brinkmann  <[email protected]>
	* configure.ac: Support gpgconf.
gpgme/
2008-01-04  Marcus Brinkmann  <[email protected]>
	* 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  <[email protected]>
	* 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.
| -rw-r--r-- | ChangeLog | 4 | ||||
| -rw-r--r-- | configure.ac | 134 | ||||
| -rw-r--r-- | gpgme/ChangeLog | 32 | ||||
| -rw-r--r-- | gpgme/Makefile.am | 9 | ||||
| -rw-r--r-- | gpgme/engine-backend.h | 6 | ||||
| -rw-r--r-- | gpgme/engine-gpgconf.c | 877 | ||||
| -rw-r--r-- | gpgme/engine-gpgsm.c | 2 | ||||
| -rw-r--r-- | gpgme/engine.c | 36 | ||||
| -rw-r--r-- | gpgme/engine.h | 5 | ||||
| -rw-r--r-- | gpgme/gpgconf.c | 134 | ||||
| -rw-r--r-- | gpgme/gpgme.def | 7 | ||||
| -rw-r--r-- | gpgme/gpgme.h | 160 | ||||
| -rw-r--r-- | gpgme/libgpgme.vers | 7 | ||||
| -rw-r--r-- | gpgme/op-support.c | 2 | ||||
| -rw-r--r-- | gpgme/posix-util.c | 10 | ||||
| -rw-r--r-- | gpgme/rungpg.c | 4 | ||||
| -rw-r--r-- | gpgme/util.h | 1 | ||||
| -rw-r--r-- | gpgme/w32-util.c | 20 | ||||
| -rw-r--r-- | tests/ChangeLog | 11 | ||||
| -rw-r--r-- | tests/Makefile.am | 2 | ||||
| -rw-r--r-- | tests/gpg/Makefile.am | 6 | ||||
| -rw-r--r-- | tests/gpg/t-gpgconf.c | 305 | ||||
| -rw-r--r-- | tests/gpgsm/Makefile.am | 2 | 
23 files changed, 1760 insertions, 16 deletions
| @@ -1,3 +1,7 @@ +2008-01-04  Marcus Brinkmann  <[email protected]> + +	* configure.ac: Support gpgconf. +  2007-09-27  Marcus Brinkmann  <[email protected]>  	* assuan-pipe-connect.c (pipe_connect_gpgme): Do not close process diff --git a/configure.ac b/configure.ac index 5145d128..fcae5ab5 100644 --- a/configure.ac +++ b/configure.ac @@ -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  " diff --git a/gpgme/ChangeLog b/gpgme/ChangeLog index f942d83c..ea6a9a01 100644 --- a/gpgme/ChangeLog +++ b/gpgme/ChangeLog @@ -1,3 +1,35 @@ +2008-01-04  Marcus Brinkmann  <[email protected]> + +	* 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  <[email protected]>  	* w32-util.c (_gpgme_get_gpg_path, _gpgme_get_gpgsm_path): Search diff --git a/gpgme/Makefile.am b/gpgme/Makefile.am index f74dffef..bd0191ae 100644 --- a/gpgme/Makefile.am +++ b/gpgme/Makefile.am @@ -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)					\ diff --git a/gpgme/engine-backend.h b/gpgme/engine-backend.h index f1b4dc00..2e2ef5ec 100644 --- a/gpgme/engine-backend.h +++ b/gpgme/engine-backend.h @@ -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 index 00000000..ee545f88 --- /dev/null +++ b/gpgme/engine-gpgconf.c @@ -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" + + +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 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; +} + + + +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; +} + + +/* 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.  */ +} + + +/* 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, +    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 */ +  }; diff --git a/gpgme/engine-gpgsm.c b/gpgme/engine-gpgsm.c index 3815613e..1e9ddd23 100644 --- a/gpgme/engine-gpgsm.c +++ b/gpgme/engine-gpgsm.c @@ -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 diff --git a/gpgme/engine.c b/gpgme/engine.c index ef7147d4..cf3fe9fe 100644 --- a/gpgme/engine.c +++ b/gpgme/engine.c @@ -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)  { diff --git a/gpgme/engine.h b/gpgme/engine.h index 6c636e04..e67399ec 100644 --- a/gpgme/engine.h +++ b/gpgme/engine.h @@ -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 index 00000000..9fa2ce9e --- /dev/null +++ b/gpgme/gpgconf.c @@ -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 + + +/* 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 +} + + diff --git a/gpgme/gpgme.def b/gpgme/gpgme.def index 57dbe40d..3c5e335b 100644 --- a/gpgme/gpgme.def +++ b/gpgme/gpgme.def @@ -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 diff --git a/gpgme/gpgme.h b/gpgme/gpgme.h index b867419c..bf42035e 100644 --- a/gpgme/gpgme.h +++ b/gpgme/gpgme.h @@ -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" @@ -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); + +/* 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);  /* Various functions.  */ diff --git a/gpgme/libgpgme.vers b/gpgme/libgpgme.vers index 9f18dafc..183a3943 100644 --- a/gpgme/libgpgme.vers +++ b/gpgme/libgpgme.vers @@ -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;  }; diff --git a/gpgme/op-support.c b/gpgme/op-support.c index 20c07385..1212c542 100644 --- a/gpgme/op-support.c +++ b/gpgme/op-support.c @@ -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); diff --git a/gpgme/posix-util.c b/gpgme/posix-util.c index d5b4172d..168dad6e 100644 --- a/gpgme/posix-util.c +++ b/gpgme/posix-util.c @@ -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) diff --git a/gpgme/rungpg.c b/gpgme/rungpg.c index c54da3a2..0ffdc7c7 100644 --- a/gpgme/rungpg.c +++ b/gpgme/rungpg.c @@ -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 diff --git a/gpgme/util.h b/gpgme/util.h index 28d5e192..0cd6ab5c 100644 --- a/gpgme/util.h +++ b/gpgme/util.h @@ -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); diff --git a/gpgme/w32-util.c b/gpgme/w32-util.c index 60c8396d..b0f6df2d 100644 --- a/gpgme/w32-util.c +++ b/gpgme/w32-util.c @@ -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. */ diff --git a/tests/ChangeLog b/tests/ChangeLog index 9e16ce2e..dd7c3968 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,14 @@ +2008-01-04  Marcus Brinkmann  <[email protected]> + +	* 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  <[email protected]>  	* gpgsm/t-verify.c (show_auditlog): Check for GPG_ERR_ASS_UNKNOWN_CMD. diff --git a/tests/Makefile.am b/tests/Makefile.am index aee20ca1..ceabc999 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -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 diff --git a/tests/gpg/Makefile.am b/tests/gpg/Makefile.am index e5d9de9b..cac589f2 100644 --- a/tests/gpg/Makefile.am +++ b/tests/gpg/Makefile.am @@ -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 index 00000000..458bbe75 --- /dev/null +++ b/tests/gpg/t-gpgconf.c @@ -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); +} + + +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; +} diff --git a/tests/gpgsm/Makefile.am b/tests/gpgsm/Makefile.am index 86c04b0d..4d34b91f 100644 --- a/tests/gpgsm/Makefile.am +++ b/tests/gpgsm/Makefile.am @@ -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 | 
