diff options
| -rw-r--r-- | ChangeLog | 4 | ||||
| -rw-r--r-- | NEWS | 13 | ||||
| -rw-r--r-- | configure.ac | 125 | ||||
| -rw-r--r-- | src/ChangeLog | 21 | ||||
| -rw-r--r-- | src/Makefile.am | 18 | ||||
| -rw-r--r-- | src/conversion.c | 69 | ||||
| -rw-r--r-- | src/engine-assuan.c | 2 | ||||
| -rw-r--r-- | src/engine-backend.h | 8 | ||||
| -rw-r--r-- | src/engine-g13.c | 796 | ||||
| -rw-r--r-- | src/engine.c | 12 | ||||
| -rw-r--r-- | src/g13.c | 229 | ||||
| -rw-r--r-- | src/gpgme.c | 6 | ||||
| -rw-r--r-- | src/gpgme.def | 2 | ||||
| -rw-r--r-- | src/gpgme.h.in | 26 | ||||
| -rw-r--r-- | src/libgpgme.vers | 2 | ||||
| -rw-r--r-- | src/posix-util.c | 10 | ||||
| -rw-r--r-- | src/util.h | 4 | ||||
| -rw-r--r-- | src/w32-util.c | 19 | 
18 files changed, 1346 insertions, 20 deletions
@@ -1,3 +1,7 @@ +2009-10-22  Marcus Brinkmann  <[email protected]> + +	* configure.ac: Add support for G13. +  2009-10-20  Marcus Brinkmann  <[email protected]>  	* configure.ac (AC_CONFIG_FILES): Remove assuan/Makefile. @@ -6,11 +6,16 @@ Noteworthy changes in version 1.2.1 (unreleased)     application programmers on systems that can resolve inter-library     dependencies at runtime, this is a transparent change. + * New engine GPGME_PROTOCOL_G13 to support the new g13 tool. +   * Interface changes relative to the 1.2.0 release: - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - GPGME_STATUS_INV_SGNR    NEW. - GPGME_STATUS_NO_SGNR     NEW. - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +GPGME_STATUS_INV_SGNR    NEW +GPGME_STATUS_NO_SGNR     NEW +GPGME_PROTOCOL_G13	 NEW +gpgme_op_g13_mount	 NEW +gpgme_g13_result_t	 NEW +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  Noteworthy changes in version 1.2.0 (2009-06-18)  ------------------------------------------------ diff --git a/configure.ac b/configure.ac index e685628d..44eb5ed5 100644 --- a/configure.ac +++ b/configure.ac @@ -120,6 +120,7 @@ AM_CONDITIONAL(HAVE_LD_VERSION_SCRIPT, test "$have_ld_version_script" = "yes")  GPG_DEFAULT=no  GPGSM_DEFAULT=no  GPGCONF_DEFAULT=no +G13_DEFAULT=no  component_system=None  have_dosish_system=no  have_w32_system=no @@ -133,6 +134,7 @@ case "${host}" in          GPG_DEFAULT='c:\\gnupg\\gpg.exe'  	GPGSM_DEFAULT='c:\\gnupg\\gpgsm.exe'          GPGCONF_DEFAULT='c:\\gnupg\\gpgconf.exe' +        G13_DEFAULT='c:\\gnupg\\g13.exe'          #component_system='COM+'  	AM_PATH_GLIB_2_0 @@ -166,6 +168,7 @@ case "${host}" in  #	GPG_DEFAULT='/usr/bin/gpg'  #	GPGSM_DEFAULT='/usr/bin/gpgsm'  #	GPGCONF_DEFAULT='/usr/bin/gpgconf' +#	G13_DEFAULT='/usr/bin/g13'  	;;  esac @@ -286,9 +289,11 @@ fi  NEED_GPG_VERSION_DEFAULT=1.3.0  NEED_GPGSM_VERSION_DEFAULT=1.9.6  NEED_GPGCONF_VERSION_DEFAULT=2.0.4 +NEED_G13_VERSION_DEFAULT=2.1.0  NEED_GPG_VERSION="$NEED_GPG_VERSION_DEFAULT"  NEED_GPGSM_VERSION="$NEED_GPGSM_VERSION_DEFAULT"  NEED_GPGCONF_VERSION="$NEED_GPGCONF_VERSION_DEFAULT" +NEED_G13_VERSION="$NEED_G13_VERSION_DEFAULT"  AC_ARG_WITH(gpg-version,  	    AC_HELP_STRING([--with-gpg-version=VER], [require GnuPG version VER]),  	    NEED_GPG_VERSION=$withval) @@ -316,6 +321,15 @@ fi  if test "$NEED_GPGCONF_VERSION" = "no"; then    NEED_GPGCONF_VERSION=0.0.0  fi +AC_ARG_WITH(g13-version, +	    AC_HELP_STRING([--with-g13-version=VER], [require G13 version VER]), +	    NEED_G13_VERSION=$withval) +if test "$NEED_G13_VERSION" = "yes"; then +  NEED_G13_VERSION="$NEED_G13_VERSION_DEFAULT" +fi +if test "$NEED_G13_VERSION" = "no"; then +  NEED_G13_VERSION=0.0.0 +fi  AC_DEFINE_UNQUOTED(NEED_GPG_VERSION, "$NEED_GPG_VERSION",  				     [Min. needed GnuPG version.]) @@ -323,6 +337,8 @@ 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.]) +AC_DEFINE_UNQUOTED(NEED_G13_VERSION, "$NEED_G13_VERSION", +				         [Min. needed G13 version.])  NO_OVERRIDE=no @@ -518,6 +534,9 @@ require_libassuan=no  if test "$GPGSM" != "no"; then    require_libassuan=yes  fi +if test "$G13" != "no"; then +  require_libassuan=yes +fi  NO_OVERRIDE=no @@ -623,6 +642,109 @@ if test "$GPGCONF" != "no"; then  fi +NO_OVERRIDE=no +AC_ARG_WITH(g13, +	    AC_HELP_STRING([--with-g13=PATH], +                           [use g13 binary at PATH]), +	    G13=$withval, NO_OVERRIDE=yes) +if test "$NO_OVERRIDE" = "yes" || test "$G13" = "yes"; then +  G13= +  NO_OVERRIDE=yes +  if test "$cross_compiling" != "yes"; then +    AC_PATH_PROG(G13, g13) +  fi +  if test -z "$G13"; then +    G13="$G13_DEFAULT" +  fi +fi +if test "$G13" = no; then +  if test "$NO_OVERRIDE" = "yes"; then +    if test "$cross_compiling" != "yes"; then +      AC_MSG_WARN([ +*** +*** Could not find g13, install g13 or use --with-g13=PATH to enable it +***]) +    else +      AC_MSG_ERROR([ +*** +*** Can not determine path to g13 when cross-compiling, use --with-g13=PATH +***]) +    fi +  fi +else +  AC_DEFINE_UNQUOTED(G13_PATH, "$G13", [Path to the G13 binary.]) +  AC_DEFINE(ENABLE_G13,1,[Whether G13 support is enabled]) +fi +AM_CONDITIONAL(HAVE_G13, test "$G13" != "no") + +dnl Check for G13 version requirement. +G13_VERSION=unknown +ok=maybe +if test -z "$G13" -o "x$G13" = "xno"; then +  ok=no +else +  if test "$cross_compiling" = "yes"; then +    AC_MSG_WARN([G13 version can not be checked when cross compiling]) +    ok=no +  else +    if test ! -x "$G13"; then +      AC_MSG_WARN([G13 not executable, version check disabled]) +      ok=no +    fi +  fi +fi +if test "$ok" = "maybe"; then +  AC_MSG_CHECKING(for G13 >= $NEED_G13_VERSION) +  req_major=`echo $NEED_G13_VERSION | \ +             sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\1/'` +  req_minor=`echo $NEED_G13_VERSION | \ +	     sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\2/'` +  req_micro=`echo $NEED_G13_VERSION | \ +	     sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\3/'` +  G13_VERSION=`$G13 --version | sed -n '1 s/.*\ \([[0-9]].*\)/\1/p'` +  major=`echo $G13_VERSION | \ +	 sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\1/'` +  minor=`echo $G13_VERSION | \ +	 sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\2/'` +  micro=`echo $G13_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([G13 must be at least version $NEED_G13_VERSION]) +  fi +fi +run_g13_test="$ok" +AC_ARG_ENABLE(g13-test, +  AC_HELP_STRING([--disable-g13-test], [disable G13 run test]), +         run_g13_test=$enableval) +AM_CONDITIONAL(RUN_G13_TESTS, test "$run_g13_test" = "yes") + +# Only build if supported. +AM_CONDITIONAL(BUILD_G13, test "$G13" != "no") +if test "$G13" != "no"; then +  AC_DEFINE(HAVE_G13, 1, +            [Defined if we are building with g13 support.]) +fi + +  # Check for funopen  AC_CHECK_FUNCS(funopen)  if test $ac_cv_func_funopen != yes; then @@ -777,6 +899,9 @@ echo "  	GpgConf path:    $GPGCONF          GpgConf version: $GPGCONF_VERSION, min. $NEED_GPGCONF_VERSION +	G13 path:        $G13 +        G13 version:     $G13_VERSION, min. $NEED_G13_VERSION +          Assuan version:  $LIBASSUAN_VERSION  	GPGME Pthread:   $have_pthread diff --git a/src/ChangeLog b/src/ChangeLog index ee32a25e..4214023f 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,24 @@ +2009-10-22  Marcus Brinkmann  <[email protected]> + +	* Makefile.am: Remove @NETLIBS@ from LIBADDs. +	(g13_components): New variable. +	(main_sources): Add $(g13_components). +	* g13.c, engine-g13.c: New files. +	* engine.c (engine_ops): Check for assuan for assuan engine, add +	g13 engine. +	* util.h (_gpgme_get_g13_path, _gpgme_encode_percent_string): New +	prototypes. +	* conversion.c (_gpgme_encode_percent_string): New function. +	* gpgme.h.in (gpgme_protocol_t): Add GPGME_PROTOCOL_G13. +	(struct _gpgme_op_g13_result, gpgme_g13_result_t): New types. +	(gpgme_op_g13_mount): New function. +	* gpgme.def, libgpgme.vers: Add gpgme_op_g13_mount. +	* gpgme.c (gpgme_set_protocol): Allow GPGME_PROTOCOL_G13. +	(gpgme_get_protocol_name): Add GPGME_PROTOCOL_G13. +	* posix-util.c (_gpgme_get_g13_path): New function. +	* w32-util.c (_gpgme_get_g13_path): New function. +	* engine-backend.h (_gpgme_engine_ops_g13): New declaration. +  2009-10-20  Marcus Brinkmann  <[email protected]>  	* gpgme-config.in (netlibs): Remove. diff --git a/src/Makefile.am b/src/Makefile.am index 73eeef20..cf88ab1e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -86,6 +86,12 @@ else  gpgconf_components =  endif +if HAVE_G13 +g13_components = engine-g13.c +else +g13_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 @@ -105,6 +111,7 @@ main_sources =								\  	opassuan.c                                                      \  	engine.h engine-backend.h engine.c engine-gpg.c status-table.h	\  	$(gpgsm_components) $(assuan_components) $(gpgconf_components)  \ +	$(g13_components) g13.c						\  	gpgconf.c							\  	sema.h priv-io.h $(system_components) dirinfo.c			\  	debug.c debug.h gpgme.c version.c error.c @@ -185,21 +192,20 @@ libgpgme_la_LDFLAGS = $(gpgme_res_ldflag) $(no_undefined) $(export_symbols) \  	$(libgpgme_version_script_cmd) -version-info \  	@LIBGPGME_LT_CURRENT@:@LIBGPGME_LT_REVISION@:@LIBGPGME_LT_AGE@  libgpgme_la_DEPENDENCIES = @LTLIBOBJS@ $(srcdir)/libgpgme.vers $(gpgme_deps) -libgpgme_la_LIBADD = @LIBASSUAN_LIBS@ @LTLIBOBJS@ \ -	@GPG_ERROR_LIBS@ @NETLIBS@ +libgpgme_la_LIBADD = @LIBASSUAN_LIBS@ @LTLIBOBJS@ @GPG_ERROR_LIBS@  libgpgme_pthread_la_LDFLAGS = $(libgpgme_version_script_cmd) -version-info \  	@LIBGPGME_LT_CURRENT@:@LIBGPGME_LT_REVISION@:@LIBGPGME_LT_AGE@  libgpgme_pthread_la_DEPENDENCIES = @LTLIBOBJS@ $(srcdir)/libgpgme.vers  libgpgme_pthread_la_LIBADD = @LIBASSUAN_LIBS@ @LTLIBOBJS@ \ -	-lpthread @GPG_ERROR_LIBS@ @NETLIBS@ +	-lpthread @GPG_ERROR_LIBS@  libgpgme_pth_la_LDFLAGS = @PTH_LDFLAGS@ \  	$(libgpgme_version_script_cmd) -version-info \  	@LIBGPGME_LT_CURRENT@:@LIBGPGME_LT_REVISION@:@LIBGPGME_LT_AGE@  libgpgme_pth_la_DEPENDENCIES = @LTLIBOBJS@ $(srcdir)/libgpgme.vers  libgpgme_pth_la_LIBADD = @LIBASSUAN_LIBS@ @LTLIBOBJS@ \ -	@PTH_LIBS@ @GPG_ERROR_LIBS@ @NETLIBS@ +	@PTH_LIBS@ @GPG_ERROR_LIBS@  if BUILD_W32_GLIB  libgpgme_glib_la_LDFLAGS = $(gpgme_res_ldflag) $(no_undefined) \ @@ -208,7 +214,7 @@ libgpgme_glib_la_LDFLAGS = $(gpgme_res_ldflag) $(no_undefined) \  libgpgme_glib_la_DEPENDENCIES =	@LTLIBOBJS@ \  	$(srcdir)/libgpgme.vers $(gpgme_deps)  libgpgme_glib_la_LIBADD = @LIBASSUAN_LIBS@ @LTLIBOBJS@ \ -	@GPG_ERROR_LIBS@ @GLIB_LIBS@ @NETLIBS@ +	@GPG_ERROR_LIBS@ @GLIB_LIBS@  endif  if BUILD_W32_QT @@ -217,7 +223,7 @@ libgpgme_qt_la_LDFLAGS = $(gpgme_res_ldflag) $(no_undefined) \  	@LIBGPGME_LT_CURRENT@:@LIBGPGME_LT_REVISION@:@LIBGPGME_LT_AGE@  libgpgme_qt_la_DEPENDENCIES = @LTLIBOBJS@ $(srcdir)/libgpgme.vers $(gpgme_deps)  libgpgme_qt_la_LIBADD = @LIBASSUAN_LIBS@ @LTLIBOBJS@ \ -	@GPG_ERROR_LIBS@ @QT4_CORE_LIBS@ @NETLIBS@ +	@GPG_ERROR_LIBS@ @QT4_CORE_LIBS@  endif  status-table.h : gpgme.h diff --git a/src/conversion.c b/src/conversion.c index f431238c..8b3b715c 100644 --- a/src/conversion.c +++ b/src/conversion.c @@ -245,6 +245,75 @@ _gpgme_decode_percent_string (const char *src, char **destp, size_t len,  } +/* Encode the string SRC with percent escaping and store the result in +   the buffer *DESTP which is LEN bytes long.  If LEN is zero, then a +   large enough buffer is allocated with malloc and *DESTP is set to +   the result.  Currently, LEN is only used to specify if allocation +   is desired or not, the caller is expected to make sure that *DESTP +   is large enough if LEN is not zero.  If BINARY is 1, then '\0' +   characters are allowed in the output.  */ +gpgme_error_t +_gpgme_encode_percent_string (const char *src, char **destp, size_t len) +{ +  size_t destlen; +  char *dest; +  const char *str; + +  destlen = 0; +  str = src; +  /* We percent-escape the + character because the user might need a +     "percent plus" escaped string (special gpg format).  But we +     percent-escape the space character, which works with and without +     the special plus format.  */ +  while (*str) +    { +      if (*str == '+' || *str == '\"' || *str == '%'  +          || *(const unsigned char *)str <= 0x20) +        destlen += 3; +      else +        destlen++; +    } +  /* Terminating nul byte.  */ +  destlen++; + +  /* Set up the destination buffer.  */ +  if (len) +    { +      if (len < destlen); +	return gpg_error (GPG_ERR_INTERNAL); + +      dest = *destp; +    } +  else +    { +      /* The converted string will never be larger than the original +	 string.  */ +      dest = malloc (destlen); +      if (!dest) +	return gpg_error_from_errno (errno); + +      *destp = dest; +    } + +  /* Convert the string.  */ +  while (*src) +    { +      if (*src == '+' || *src == '\"' || *src == '%'  +          || *(const unsigned char *)src <= 0x20) +        { +          snprintf (dest, 4, "%%%02X", *(unsigned char *)src); +	  dest += 3; +	} +      else +	*(dest++) = *src; +      src++; +    } +  *(dest++) = 0; + +  return 0; +} + +  /* Parse the string TIMESTAMP into a time_t.  The string may either be     seconds since Epoch or in the ISO 8601 format like     "20390815T143012".  Returns 0 for an empty string or seconds since diff --git a/src/engine-assuan.c b/src/engine-assuan.c index 74cde99c..07a21fa0 100644 --- a/src/engine-assuan.c +++ b/src/engine-assuan.c @@ -554,6 +554,8 @@ llass_status_handler (void *opaque, int fd)            TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,  		  "fd 0x%x: ERR line: %s",                    fd, err ? gpg_strerror (err) : "ok"); +	  /* Command execution errors are not fatal, as we use +	     a session based protocol.  */            if (llass->result_cb)              err = llass->result_cb (llass->result_cb_value, err);            else diff --git a/src/engine-backend.h b/src/engine-backend.h index 0a30f348..fa539a3f 100644 --- a/src/engine-backend.h +++ b/src/engine-backend.h @@ -22,11 +22,6 @@  #include "engine.h" -/* FIXME: Correct check?  */ -#ifdef GPGSM_PATH -#define ENABLE_GPGSM 1 -#endif -  struct engine_ops  {    /* Static functions.  */ @@ -132,5 +127,8 @@ extern struct engine_ops _gpgme_engine_ops_gpgconf;	/* gpg-conf.  */  #ifdef ENABLE_ASSUAN  extern struct engine_ops _gpgme_engine_ops_assuan;	/* Low-level Assuan. */  #endif +#ifdef ENABLE_G13 +extern struct engine_ops _gpgme_engine_ops_g13;         /* Crypto VFS. */ +#endif  #endif /* ENGINE_BACKEND_H */ diff --git a/src/engine-g13.c b/src/engine-g13.c new file mode 100644 index 00000000..f957691a --- /dev/null +++ b/src/engine-g13.c @@ -0,0 +1,796 @@ +/* engine-g13.c - G13 engine. +   Copyright (C) 2000 Werner Koch (dd9jn) +   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009 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" + + +typedef struct +{ +  int fd;	/* FD we talk about.  */ +  int server_fd;/* Server FD for this connection.  */ +  int dir;	/* Inbound/Outbound, maybe given implicit?  */ +  void *data;	/* Handler-specific data.  */ +  void *tag;	/* ID from the user for gpgme_remove_io_callback.  */ +  char server_fd_str[15]; /* Same as SERVER_FD but as a string.  We +                             need this because _gpgme_io_fd2str can't +                             be used on a closed descriptor.  */ +} iocb_data_t; + + +struct engine_g13 +{ +  assuan_context_t assuan_ctx; + +  int lc_ctype_set; +  int lc_messages_set; + +  iocb_data_t status_cb; + +  struct gpgme_io_cbs io_cbs; + +  /* Internal callbacks.  */ +  engine_assuan_result_cb_t result_cb; +  void *result_cb_value;  + +  /* User provided callbacks.  */ +  struct { +    gpgme_assuan_data_cb_t data_cb; +    void *data_cb_value; + +    gpgme_assuan_inquire_cb_t inq_cb; +    void *inq_cb_value; + +    gpgme_assuan_status_cb_t status_cb; +    void *status_cb_value; +  } user; +}; + +typedef struct engine_g13 *engine_g13_t; + + +static void g13_io_event (void *engine,  +                            gpgme_event_io_t type, void *type_data); + + + +static char * +g13_get_version (const char *file_name) +{ +  return _gpgme_get_program_version (file_name ? file_name +				     : _gpgme_get_g13_path ()); +} + + +static const char * +g13_get_req_version (void) +{ +  return NEED_G13_VERSION; +} + + +static void +close_notify_handler (int fd, void *opaque) +{ +  engine_g13_t g13 = opaque; + +  assert (fd != -1); +  if (g13->status_cb.fd == fd) +    { +      if (g13->status_cb.tag) +	(*g13->io_cbs.remove) (g13->status_cb.tag); +      g13->status_cb.fd = -1; +      g13->status_cb.tag = NULL; +    } +} + + +/* This is the default inquiry callback.  We use it to handle the +   Pinentry notifications.  */ +static gpgme_error_t +default_inq_cb (engine_g13_t g13, const char *keyword, const char *args) +{ +  gpg_error_t err; + +  if (!strcmp (keyword, "PINENTRY_LAUNCHED")) +    { +      _gpgme_allow_set_foreground_window ((pid_t)strtoul (args, NULL, 10)); +    } + +  if (g13->user.inq_cb) +    { +      gpgme_data_t data = NULL; + +      err = g13->user.inq_cb (g13->user.inq_cb_value, +                                keyword, args, &data); +      if (!err && data) +        { +          /* FIXME: Returning data is not yet implemented.  However we +             need to allow the caller to cleanup his data object. +             Thus we run the callback in finish mode immediately.  */ +          err = g13->user.inq_cb (g13->user.inq_cb_value, +				  NULL, NULL, &data); +        } +    } +  else +    err = 0; + +  return err; +} + + +static gpgme_error_t +g13_cancel (void *engine) +{ +  engine_g13_t g13 = engine; + +  if (!g13) +    return gpg_error (GPG_ERR_INV_VALUE); + +  if (g13->status_cb.fd != -1) +    _gpgme_io_close (g13->status_cb.fd); + +  if (g13->assuan_ctx) +    { +      assuan_release (g13->assuan_ctx); +      g13->assuan_ctx = NULL; +    } + +  return 0; +} + + +static void +g13_release (void *engine) +{ +  engine_g13_t g13 = engine; + +  if (!g13) +    return; + +  g13_cancel (engine); + +  free (g13); +} + + +static gpgme_error_t +g13_new (void **engine, const char *file_name, const char *home_dir) +{ +  gpgme_error_t err = 0; +  engine_g13_t g13; +  int argc; +  char *argv[5]; +  char *dft_display = NULL; +  char dft_ttyname[64]; +  char *dft_ttytype = NULL; +  char *optstr; + +  g13 = calloc (1, sizeof *g13); +  if (!g13) +    return gpg_error_from_errno (errno); + +  g13->status_cb.fd = -1; +  g13->status_cb.dir = 1; +  g13->status_cb.tag = 0; +  g13->status_cb.data = g13; + +  err = assuan_new (&g13->assuan_ctx); +  if (err) +    goto leave; + +  argc = 0; +  argv[argc++] = "g13"; +  if (home_dir) +    { +      argv[argc++] = "--homedir"; +      argv[argc++] = home_dir; +    } +  argv[argc++] = "--server"; +  argv[argc++] = NULL; + +  err = assuan_new_ext (&g13->assuan_ctx, GPG_ERR_SOURCE_GPGME, +			&_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb, +			NULL); +  if (err) +    goto leave; +  assuan_ctx_set_system_hooks (g13->assuan_ctx, &_gpgme_assuan_system_hooks); + +#if USE_DESCRIPTOR_PASSING +  err = assuan_pipe_connect_ext +    (g13->assuan_ctx, file_name ? file_name : _gpgme_get_g13_path (), +     argv, NULL, NULL, NULL, 1); +#else +  err = assuan_pipe_connect +    (g13->assuan_ctx, file_name ? file_name : _gpgme_get_g13_path (), +     argv, NULL); +#endif +  if (err) +    goto leave; + +  err = _gpgme_getenv ("DISPLAY", &dft_display); +  if (err) +    goto leave; +  if (dft_display) +    { +      if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0) +        { +	  free (dft_display); +	  err = gpg_error_from_errno (errno); +	  goto leave; +	} +      free (dft_display); + +      err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL, NULL, +			     NULL, NULL, NULL); +      free (optstr); +      if (err) +	goto leave; +    } + +  if (isatty (1)) +    { +      int rc; + +      rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname)); +      if (rc) +	{ +	  err = gpg_error_from_errno (rc); +	  goto leave; +	} +      else +	{ +	  if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0) +	    { +	      err = gpg_error_from_errno (errno); +	      goto leave; +	    } +	  err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL, NULL, +				 NULL, NULL, NULL); +	  free (optstr); +	  if (err) +	    goto leave; + +	  err = _gpgme_getenv ("TERM", &dft_ttytype); +	  if (err) +	    goto leave; +	  if (dft_ttytype) +	    { +	      if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0) +		{ +		  free (dft_ttytype); +		  err = gpg_error_from_errno (errno); +		  goto leave; +		} +	      free (dft_ttytype); + +	      err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL, +				     NULL, NULL, NULL, NULL); +	      free (optstr); +	      if (err) +		goto leave; +	    } +	} +    } + +#ifdef HAVE_W32_SYSTEM +  /* Under Windows we need to use AllowSetForegroundWindow.  Tell +     g13 to tell us when it needs it.  */ +  if (!err) +    { +      err = assuan_transact (g13->assuan_ctx, "OPTION allow-pinentry-notify", +                             NULL, NULL, NULL, NULL, NULL, NULL); +      if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION) +        err = 0; /* This is a new feature of g13.  */ +    } +#endif /*HAVE_W32_SYSTEM*/ + + leave: + +  if (err) +    g13_release (g13); +  else +    *engine = g13; + +  return err; +} + + +static gpgme_error_t +g13_set_locale (void *engine, int category, const char *value) +{ +  engine_g13_t g13 = engine; +  gpgme_error_t err; +  char *optstr; +  char *catstr; + +  /* FIXME: If value is NULL, we need to reset the option to default. +     But we can't do this.  So we error out here.  G13 needs support +     for this.  */ +  if (category == LC_CTYPE) +    { +      catstr = "lc-ctype"; +      if (!value && g13->lc_ctype_set) +	return gpg_error (GPG_ERR_INV_VALUE); +      if (value) +	g13->lc_ctype_set = 1; +    } +#ifdef LC_MESSAGES +  else if (category == LC_MESSAGES) +    { +      catstr = "lc-messages"; +      if (!value && g13->lc_messages_set) +	return gpg_error (GPG_ERR_INV_VALUE); +      if (value) +	g13->lc_messages_set = 1; +    } +#endif /* LC_MESSAGES */ +  else +    return gpg_error (GPG_ERR_INV_VALUE); + +  /* FIXME: Reset value to default.  */ +  if (!value)  +    return 0; + +  if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0) +    err = gpg_error_from_errno (errno); +  else +    { +      err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL, +			     NULL, NULL, NULL, NULL); +      free (optstr); +    } + +  return err; +} + + +/* Forward declaration.  */ +static gpgme_status_code_t parse_status (const char *name); + +static gpgme_error_t +g13_assuan_simple_command (assuan_context_t ctx, char *cmd, +			     engine_status_handler_t status_fnc, +			     void *status_fnc_value) +{ +  gpg_error_t err; +  char *line; +  size_t linelen; + +  err = assuan_write_line (ctx, cmd); +  if (err) +    return err; + +  do +    { +      err = assuan_read_line (ctx, &line, &linelen); +      if (err) +	return err; + +      if (*line == '#' || !linelen) +	continue; + +      if (linelen >= 2 +	  && line[0] == 'O' && line[1] == 'K' +	  && (line[2] == '\0' || line[2] == ' ')) +	return 0; +      else if (linelen >= 4 +	  && line[0] == 'E' && line[1] == 'R' && line[2] == 'R' +	  && line[3] == ' ') +	err = atoi (&line[4]); +      else if (linelen >= 2 +	       && line[0] == 'S' && line[1] == ' ') +	{ +	  char *rest; +	  gpgme_status_code_t r; + +	  rest = strchr (line + 2, ' '); +	  if (!rest) +	    rest = line + linelen; /* set to an empty string */ +	  else +	    *(rest++) = 0; + +	  r = parse_status (line + 2); + +	  if (r >= 0 && status_fnc) +	    err = status_fnc (status_fnc_value, r, rest); +	  else +	    err = gpg_error (GPG_ERR_GENERAL); +	} +      else +	err = gpg_error (GPG_ERR_GENERAL); +    } +  while (!err); + +  return err; +} + + +static gpgme_error_t +status_handler (void *opaque, int fd) +{ +  gpgme_error_t err = 0; +  engine_g13_t g13 = opaque; +  char *line; +  size_t linelen; + +  do +    { +      err = assuan_read_line (g13->assuan_ctx, &line, &linelen); +      if (err) +	{ +	  /* Try our best to terminate the connection friendly.  */ +	  /*	  assuan_write_line (g13->assuan_ctx, "BYE"); */ +          TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13, +		  "fd 0x%x: error reading assuan line: %s", +                  fd, gpg_strerror (err)); +	} +      else if (linelen >= 3 +	       && line[0] == 'E' && line[1] == 'R' && line[2] == 'R' +	       && (line[3] == '\0' || line[3] == ' ')) +	{ +	  if (line[3] == ' ') +	    err = atoi (&line[4]); +	  if (! err) +	    err = gpg_error (GPG_ERR_GENERAL); +          TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13, +		  "fd 0x%x: ERR line: %s", +                  fd, err ? gpg_strerror (err) : "ok"); +	   +	  /* In g13, command execution errors are not fatal, as we use +	     a session based protocol.  */ +          if (g13->result_cb) +            err = g13->result_cb (g13->result_cb_value, err); +          else +            err = 0; +          if (!err) +            { +              _gpgme_io_close (g13->status_cb.fd); +              return 0; +	    } +	} +      else if (linelen >= 2 +	       && line[0] == 'O' && line[1] == 'K' +	       && (line[2] == '\0' || line[2] == ' ')) +	{ +          TRACE1 (DEBUG_CTX, "gpgme:status_handler", g13, +		  "fd 0x%x: OK line", fd); +          if (g13->result_cb) +            err = g13->result_cb (g13->result_cb_value, 0); +          else +            err = 0; +	  if (!err) +            { +              _gpgme_io_close (g13->status_cb.fd); +              return 0; +            } +	} +      else if (linelen > 2 +	       && line[0] == 'D' && line[1] == ' ') +        { +	  /* We are using the colon handler even for plain inline data +             - strange name for that function but for historic reasons +             we keep it.  */ +          /* FIXME We can't use this for binary data because we +             assume this is a string.  For the current usage of colon +             output it is correct.  */ +          char *src = line + 2; +	  char *end = line + linelen; +	  char *dst = src; + +          linelen = 0; +          while (src < end) +            { +              if (*src == '%' && src + 2 < end) +                { +                  /* Handle escaped characters.  */ +                  ++src; +                  *dst++ = _gpgme_hextobyte (src); +                  src += 2; +                } +              else +                *dst++ = *src++; + +              linelen++; +            } + +          src = line + 2; +          if (linelen && g13->user.data_cb) +            err = g13->user.data_cb (g13->user.data_cb_value, +                                       src, linelen); +          else +            err = 0; + +          TRACE2 (DEBUG_CTX, "gpgme:g13_status_handler", g13, +		  "fd 0x%x: D inlinedata; status from cb: %s", +                  fd, (g13->user.data_cb ? +                       (err? gpg_strerror (err):"ok"):"no callback")); + +        } +      else if (linelen > 2 +	       && line[0] == 'S' && line[1] == ' ') +	{ +	  char *src; +	  char *args; + +	  src = line + 2; +          while (*src == ' ') +            src++; +	   +	  args = strchr (line + 2, ' '); +	  if (!args) +	    args = line + linelen; /* set to an empty string */ +	  else +	    *(args++) = 0; + +          while (*args == ' ') +            args++; + +          if (g13->user.status_cb) +            err = g13->user.status_cb (g13->user.status_cb_value, +				       src, args); +          else +            err = 0; + +          TRACE3 (DEBUG_CTX, "gpgme:g13_status_handler", g13, +		  "fd 0x%x: S line (%s) - status from cb: %s", +                  fd, line+2, (g13->user.status_cb ? +                               (err? gpg_strerror (err):"ok"):"no callback")); +	} +      else if (linelen >= 7 +               && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q' +               && line[3] == 'U' && line[4] == 'I' && line[5] == 'R' +               && line[6] == 'E'  +               && (line[7] == '\0' || line[7] == ' ')) +        { +          char *src; +	  char *args; +	   +          for (src=line+7; *src == ' '; src++) +            ; + +	  args = strchr (src, ' '); +	  if (!args) +	    args = line + linelen; /* Let it point to an empty string.  */ +	  else +	    *(args++) = 0; + +          while (*args == ' ') +            args++; + +          err = default_inq_cb (g13, src, args); +          if (!err)  +            { +              /* Flush and send END.  */ +              err = assuan_send_data (g13->assuan_ctx, NULL, 0); +            } +          else if (gpg_err_code (err) == GPG_ERR_ASS_CANCELED) +            { +              /* Flush and send CANcel.  */ +              err = assuan_send_data (g13->assuan_ctx, NULL, 1); +            } +          assuan_write_line (g13->assuan_ctx, "END"); +        } +    } +  while (!err && assuan_pending_line (g13->assuan_ctx)); +   +  return err; +} +   + +static gpgme_error_t +add_io_cb (engine_g13_t g13, iocb_data_t *iocbd, gpgme_io_cb_t handler) +{ +  gpgme_error_t err; + +  TRACE_BEG2 (DEBUG_ENGINE, "engine-g13:add_io_cb", g13, +              "fd %d, dir %d", iocbd->fd, iocbd->dir); +  err = (*g13->io_cbs.add) (g13->io_cbs.add_priv, +			      iocbd->fd, iocbd->dir, +			      handler, iocbd->data, &iocbd->tag); +  if (err) +    return TRACE_ERR (err); +  if (!iocbd->dir) +    /* FIXME Kludge around poll() problem.  */ +    err = _gpgme_io_set_nonblocking (iocbd->fd); +  return TRACE_ERR (err); +} + + +static gpgme_error_t +start (engine_g13_t g13, const char *command) +{ +  gpgme_error_t err; +  int fdlist[5]; +  int nfds; + +  /* We need to know the fd used by assuan for reads.  We do this by +     using the assumption that the first returned fd from +     assuan_get_active_fds() is always this one.  */ +  nfds = assuan_get_active_fds (g13->assuan_ctx, 0 /* read fds */, +                                fdlist, DIM (fdlist)); +  if (nfds < 1) +    return gpg_error (GPG_ERR_GENERAL);	/* FIXME */ + +  /* We "duplicate" the file descriptor, so we can close it here (we +     can't close fdlist[0], as that is closed by libassuan, and +     closing it here might cause libassuan to close some unrelated FD +     later).  Alternatively, we could special case status_fd and +     register/unregister it manually as needed, but this increases +     code duplication and is more complicated as we can not use the +     close notifications etc.  A third alternative would be to let +     Assuan know that we closed the FD, but that complicates the +     Assuan interface.  */ + +  g13->status_cb.fd = _gpgme_io_dup (fdlist[0]); +  if (g13->status_cb.fd < 0) +    return gpg_error_from_syserror (); + +  if (_gpgme_io_set_close_notify (g13->status_cb.fd, +				  close_notify_handler, g13)) +    { +      _gpgme_io_close (g13->status_cb.fd); +      g13->status_cb.fd = -1; +      return gpg_error (GPG_ERR_GENERAL); +    } + +  err = add_io_cb (g13, &g13->status_cb, status_handler); +  if (!err) +    err = assuan_write_line (g13->assuan_ctx, command); + +  if (!err) +    g13_io_event (g13, GPGME_EVENT_START, NULL); + +  return err; +} + + +#if USE_DESCRIPTOR_PASSING +static gpgme_error_t +g13_reset (void *engine) +{ +  engine_g13_t g13 = engine; + +  /* We must send a reset because we need to reset the list of +     signers.  Note that RESET does not reset OPTION commands. */ +  return g13_assuan_simple_command (g13->assuan_ctx, "RESET", NULL, NULL); +} +#endif + + +static gpgme_error_t +g13_transact (void *engine, +                const char *command, +                engine_assuan_result_cb_t result_cb, +                void *result_cb_value, +                gpgme_assuan_data_cb_t data_cb, +                void *data_cb_value, +                gpgme_assuan_inquire_cb_t inq_cb, +                void *inq_cb_value, +                gpgme_assuan_status_cb_t status_cb, +                void *status_cb_value) +{ +  engine_g13_t g13 = engine; +  gpgme_error_t err; + +  if (!g13 || !command || !*command) +    return gpg_error (GPG_ERR_INV_VALUE); + +  g13->result_cb = result_cb; +  g13->result_cb_value = result_cb_value; +  g13->user.data_cb = data_cb; +  g13->user.data_cb_value = data_cb_value; +  g13->user.inq_cb = inq_cb; +  g13->user.inq_cb_value = inq_cb_value; +  g13->user.status_cb = status_cb; +  g13->user.status_cb_value = status_cb_value; + +  err = start (g13, command); +  return err; +} + + + +static void +g13_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs) +{ +  engine_g13_t g13 = engine; +  g13->io_cbs = *io_cbs; +} + + +static void +g13_io_event (void *engine, gpgme_event_io_t type, void *type_data) +{ +  engine_g13_t g13 = engine; + +  TRACE3 (DEBUG_ENGINE, "gpgme:g13_io_event", g13, +          "event %p, type %d, type_data %p", +          g13->io_cbs.event, type, type_data); +  if (g13->io_cbs.event) +    (*g13->io_cbs.event) (g13->io_cbs.event_priv, type, type_data); +} + + +struct engine_ops _gpgme_engine_ops_g13 = +  { +    /* Static functions.  */ +    _gpgme_get_g13_path, +    NULL, +    g13_get_version, +    g13_get_req_version, +    g13_new, + +    /* Member functions.  */ +    g13_release, +#if USE_DESCRIPTOR_PASSING +    g13_reset, +#else +    NULL,			/* reset */ +#endif +    NULL,               /* set_status_handler */ +    NULL,		/* set_command_handler */ +    NULL,               /* set_colon_line_handler */ +    g13_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 */ +    g13_transact, +    NULL,		/* conf_load */ +    NULL,		/* conf_save */ +    g13_set_io_cbs, +    g13_io_event, +    g13_cancel +  }; diff --git a/src/engine.c b/src/engine.c index c8ff012b..b1d815ac 100644 --- a/src/engine.c +++ b/src/engine.c @@ -55,8 +55,13 @@ static struct engine_ops *engine_ops[] =  #else      NULL,  #endif -#ifdef ENABLE_GPGSM  /* This indicates that we have assuan support.  */ -    &_gpgme_engine_ops_assuan		/* Low-Level Assuan.  */ +#ifdef ENABLE_ASSUAN +    &_gpgme_engine_ops_assuan,		/* Low-Level Assuan.  */ +#else +    NULL +#endif +#ifdef ENABLE_G13 +    &_gpgme_engine_ops_g13		/* Crypto VFS.  */  #else      NULL  #endif @@ -194,7 +199,8 @@ gpgme_get_engine_info (gpgme_engine_info_t *info)        gpgme_protocol_t proto_list[] = { GPGME_PROTOCOL_OpenPGP,  					GPGME_PROTOCOL_CMS,  					GPGME_PROTOCOL_GPGCONF, -					GPGME_PROTOCOL_ASSUAN }; +					GPGME_PROTOCOL_ASSUAN, +					GPGME_PROTOCOL_G13 };        unsigned int proto;        for (proto = 0; proto < DIM (proto_list); proto++) diff --git a/src/g13.c b/src/g13.c new file mode 100644 index 00000000..b3f5fee8 --- /dev/null +++ b/src/g13.c @@ -0,0 +1,229 @@ +/* g13.c - g13 support in GPGME +   Copyright (C) 2009 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 "gpgme.h" +#include "context.h" +#include "ops.h" +#include "util.h" + +typedef struct +{ +  struct _gpgme_op_g13_result result; +} *op_data_t; + + + +/* This callback is used to return the status of the assuan command +   back rather than transmission errors.  */ +static gpgme_error_t +result_cb (void *priv, gpgme_error_t result) +{ +  gpgme_ctx_t ctx = (gpgme_ctx_t)priv; +  gpgme_error_t err; +  void *hook; +  op_data_t opd; + +  err = _gpgme_op_data_lookup (ctx, OPDATA_ASSUAN, &hook, -1, NULL); +  opd = hook; +  if (err) +    return err; +  if (!opd) +    return gpg_error (GPG_ERR_INTERNAL); + +  opd->result.err = result; +  return 0; +} + + +gpgme_g13_result_t +gpgme_op_g13_result (gpgme_ctx_t ctx) +{ +  gpgme_error_t err; +  void *hook; +  op_data_t opd; + +  err = _gpgme_op_data_lookup (ctx, OPDATA_ASSUAN, &hook, -1, NULL); +  opd = hook; +  /* Check in case this function is used without having run a command +     before.  */ +  if (err || !opd) +    return NULL; + +  return &opd->result; +} + + +static gpgme_error_t +opg13_start (gpgme_ctx_t ctx, int synchronous, +                const char *command, +                gpgme_assuan_data_cb_t data_cb, +                void *data_cb_value, +                gpgme_assuan_inquire_cb_t inq_cb, +                void *inq_cb_value, +                gpgme_assuan_status_cb_t status_cb, +                void *status_cb_value) +{ +  gpgme_error_t err; +  void *hook; +  op_data_t opd; + +  if (!command || !*command) +    return gpg_error (GPG_ERR_INV_VALUE); + +  /* The flag value 256 is used to suppress an engine reset.  This is +     required to keep the connection running.  */ +  err = _gpgme_op_reset (ctx, ((synchronous & 255) | 256)); +  if (err) +    return err; + +  err = _gpgme_op_data_lookup (ctx, OPDATA_ASSUAN, &hook, sizeof (*opd), NULL); +  opd = hook; +  if (err) +    return err; +  opd->result.err = gpg_error (GPG_ERR_UNFINISHED); + +  return _gpgme_engine_op_assuan_transact (ctx->engine, command, +					   result_cb, ctx, +					   data_cb, data_cb_value, +					   inq_cb, inq_cb_value, +					   status_cb, status_cb_value); +} + + + +/* XXXX.  This is the asynchronous variant. */ +static gpgme_error_t +gpgme_op_g13_transact_start (gpgme_ctx_t ctx,  +			     const char *command, +			     gpgme_assuan_data_cb_t data_cb, +			     void *data_cb_value, +			     gpgme_assuan_inquire_cb_t inq_cb, +			     void *inq_cb_value, +			     gpgme_assuan_status_cb_t status_cb, +			     void *status_cb_value) +{ +  return opg13_start (ctx, 0, command, data_cb, data_cb_value, +		      inq_cb, inq_cb_value, status_cb, status_cb_value); +} + + +/* XXXX.  This is the synchronous variant. */ +static gpgme_error_t +gpgme_op_g13_transact (gpgme_ctx_t ctx, +		       const char *command, +		       gpgme_assuan_data_cb_t data_cb, +		       void *data_cb_value, +		       gpgme_assuan_inquire_cb_t inq_cb, +		       void *inq_cb_value, +		       gpgme_assuan_status_cb_t status_cb, +		       void *status_cb_value) +{ +  gpgme_error_t err; +   +  err = opg13_start (ctx, 1, command, data_cb, data_cb_value, +		     inq_cb, inq_cb_value, status_cb, status_cb_value); +  if (!err) +    err = _gpgme_wait_one (ctx); +  return err; +} + + +/* The actual exported interface follows.  */ + +static gpg_error_t +get_err (gpgme_ctx_t ctx) +{ +  gpgme_g13_result_t res; + +  res = gpgme_op_g13_result (ctx); +  if (! res) +    return gpg_error (GPG_ERR_GENERAL); + +  return res->err; +} + + +/* The container is automatically unmounted when the context is reset +   or destroyed.  This is a synchronous convenience interface, which +   automatically returns an operation error if there is no +   transmission error.  */ +gpgme_error_t +gpgme_op_g13_mount (gpgme_ctx_t ctx, const char *container_file, +		    const char *mount_dir, int flags) +{ +  gpg_error_t err; +  char *cmd; +  char *container_file_esc = NULL; +   +  err = _gpgme_encode_percent_string (container_file, &container_file_esc, 0); +  if (err) +    return err; + +  if (asprintf (&cmd, "OPEN -- %s", container_file_esc) < 0) +    { +      err = gpg_error_from_syserror (); +      free (container_file_esc); +      return err; +    } +  free (container_file_esc); + +  err = gpgme_op_g13_transact (ctx, cmd, NULL, NULL, NULL, NULL, +			       NULL, NULL); +  free (cmd); +  err = err || get_err (ctx); +  if (err) +    return err; + +  if (mount_dir) +    { +      char *mount_dir_esc = NULL; + +      err = _gpgme_encode_percent_string (mount_dir, &mount_dir_esc, 0); +      if (err) +	return err; + +      if (asprintf (&cmd, "MOUNT -- %s", mount_dir_esc) < 0) +	{ +	  err = gpg_error_from_syserror (); +	  free (mount_dir_esc); +	  return err; +	} +      free (mount_dir_esc); +    } +  else +    { +      if (asprintf (&cmd, "MOUNT") < 0) +	return gpg_error_from_syserror (); +    } +     +  err = gpgme_op_g13_transact (ctx, cmd, NULL, NULL, NULL, NULL, +			       NULL, NULL); +  free (cmd); + +  /* Note: in symmetry with the asynchronous variant, we don't return  +     the error in the result structure here, if any.  */ +  return err; +} diff --git a/src/gpgme.c b/src/gpgme.c index 56caf59c..071b394c 100644 --- a/src/gpgme.c +++ b/src/gpgme.c @@ -248,7 +248,8 @@ gpgme_set_protocol (gpgme_ctx_t ctx, gpgme_protocol_t protocol)    if (protocol != GPGME_PROTOCOL_OpenPGP        && protocol != GPGME_PROTOCOL_CMS -      && protocol != GPGME_PROTOCOL_ASSUAN) +      && protocol != GPGME_PROTOCOL_ASSUAN +      && protocol != GPGME_PROTOCOL_G13)      return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));    if (ctx->protocol != protocol) @@ -292,6 +293,9 @@ gpgme_get_protocol_name (gpgme_protocol_t protocol)      case GPGME_PROTOCOL_ASSUAN:        return "Assuan"; +    case GPGME_PROTOCOL_G13: +      return "G13"; +      case GPGME_PROTOCOL_UNKNOWN:        return "unknown"; diff --git a/src/gpgme.def b/src/gpgme.def index d5195ebb..5089a275 100644 --- a/src/gpgme.def +++ b/src/gpgme.def @@ -185,5 +185,7 @@ EXPORTS      gpgme_op_export_keys                  @142      gpgme_op_export_keys_start            @143 +    gpgme_op_g13_mount			  @144 +  ; END diff --git a/src/gpgme.h.in b/src/gpgme.h.in index 0b42798c..45f5c128 100644 --- a/src/gpgme.h.in +++ b/src/gpgme.h.in @@ -324,6 +324,7 @@ typedef enum      GPGME_PROTOCOL_CMS     = 1,      GPGME_PROTOCOL_GPGCONF = 2,  /* Special code for gpgconf.  */      GPGME_PROTOCOL_ASSUAN  = 3,  /* Low-level access to an Assuan server.  */ +    GPGME_PROTOCOL_G13     = 4,      GPGME_PROTOCOL_UNKNOWN = 255    }  gpgme_protocol_t; @@ -1735,7 +1736,10 @@ typedef gpgme_error_t (*gpgme_assuan_status_cb_t)  struct _gpgme_op_assuan_result  {    /* The result of the actual assuan command.  An OK is indicated by a -     value of 0 and an ERR by the respective error error value.  */ +     value of 0 and an ERR by the respective error error value.  This +     is required because assuan operations use a session-based +     interface.  The error code of the GPGME function calls just +     reflects transmission errors.  */    gpgme_error_t err;  };  typedef struct _gpgme_op_assuan_result *gpgme_assuan_result_t; @@ -1768,6 +1772,26 @@ gpgme_error_t gpgme_op_assuan_transact (gpgme_ctx_t ctx, +/* Crypto container support.  */ +struct _gpgme_op_g13_result +{ +  /* The result of the actual assuan command.  An OK is indicated by a +     value of 0 and an ERR by the respective error error value.  This +     is required because assuan operations use a session-based +     interface.  The error code of the GPGME function calls just +     reflects transmission errors.  */ +  gpgme_error_t err; +}; +typedef struct _gpgme_op_g13_result *gpgme_g13_result_t; + +/* The container is automatically unmounted when the context is reset +   or destroyed.  This is a synchronous convenience interface, which +   automatically returns an operation error if there is no +   transmission error.  */ +gpgme_error_t gpgme_op_g13_mount (gpgme_ctx_t ctx, const char *container_file, +				  const char *mount_dir, int flags); + +  /* Interface to gpgconf(1).  */  /* The expert level at which a configuration option or group of diff --git a/src/libgpgme.vers b/src/libgpgme.vers index 41fa1be9..7f65d6e6 100644 --- a/src/libgpgme.vers +++ b/src/libgpgme.vers @@ -64,6 +64,8 @@ GPGME_1.1 {      gpgme_op_import_keys_start;      gpgme_op_export_keys;      gpgme_op_export_keys_start; + +    gpgme_op_g13_mount;  }; diff --git a/src/posix-util.c b/src/posix-util.c index b0d17b0e..86ef5e74 100644 --- a/src/posix-util.c +++ b/src/posix-util.c @@ -59,6 +59,16 @@ _gpgme_get_gpgconf_path (void)  #endif  } +const char * +_gpgme_get_g13_path (void) +{ +#ifdef G13_PATH +  return G13_PATH; +#else +  return NULL; +#endif +} +  /* See w32-util.c */  int  _gpgme_get_conf_int (const char *key, int *value) @@ -32,6 +32,7 @@  const char *_gpgme_get_gpg_path (void);  const char *_gpgme_get_gpgsm_path (void);  const char *_gpgme_get_gpgconf_path (void); +const char *_gpgme_get_g13_path (void);  int _gpgme_get_conf_int (const char *key, int *value);  void _gpgme_allow_set_foreground_window (pid_t pid); @@ -93,6 +94,9 @@ gpgme_error_t _gpgme_decode_c_string (const char *src, char **destp,  gpgme_error_t _gpgme_decode_percent_string (const char *src, char **destp,  					    size_t len, int binary); +gpgme_error_t _gpgme_encode_percent_string (const char *src, char **destp, +					    size_t len); +  /* Parse the string TIMESTAMP into a time_t.  The string may either be     seconds since Epoch or in the ISO 8601 format like diff --git a/src/w32-util.c b/src/w32-util.c index a4b713ed..d7fc06f3 100644 --- a/src/w32-util.c +++ b/src/w32-util.c @@ -362,6 +362,25 @@ _gpgme_get_gpgconf_path (void)  const char * +_gpgme_get_g13_path (void) +{ +  static char *g13_program; + +  LOCK (get_path_lock); +#if 0 +  if (!g13_program) +    g13_program = find_program_in_registry ("g13Program"); +#endif +  if (!g13_program) +    g13_program = find_program_in_inst_dir ("g13.exe"); +  if (!g13_program) +    g13_program = find_program_at_standard_place ("GNU\\GnuPG\\g13.exe"); +  UNLOCK (get_path_lock); +  return g13_program; +} + + +const char *  _gpgme_get_w32spawn_path (void)  {    static char *w32spawn_program;  | 
