[svn] gpgme - r1404 - in trunk: . src

svn author marcus cvs at cvs.gnupg.org
Thu Oct 22 18:44:08 CEST 2009


Author: marcus
Date: 2009-10-22 18:44:07 +0200 (Thu, 22 Oct 2009)
New Revision: 1404

Added:
   trunk/src/engine-g13.c
   trunk/src/g13.c
Modified:
   trunk/ChangeLog
   trunk/NEWS
   trunk/configure.ac
   trunk/src/ChangeLog
   trunk/src/Makefile.am
   trunk/src/conversion.c
   trunk/src/engine-assuan.c
   trunk/src/engine-backend.h
   trunk/src/engine.c
   trunk/src/gpgme.c
   trunk/src/gpgme.def
   trunk/src/gpgme.h.in
   trunk/src/libgpgme.vers
   trunk/src/posix-util.c
   trunk/src/util.h
   trunk/src/w32-util.c
Log:
2009-10-22  Marcus Brinkmann  <marcus at g10code.de>

	* configure.ac: Add support for G13.

src/
2009-10-22  Marcus Brinkmann  <marcus at g10code.de>

	* 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.


[The diff below has been truncated]

Modified: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog	2009-10-20 16:05:21 UTC (rev 1403)
+++ trunk/ChangeLog	2009-10-22 16:44:07 UTC (rev 1404)
@@ -1,3 +1,7 @@
+2009-10-22  Marcus Brinkmann  <marcus at g10code.de>
+
+	* configure.ac: Add support for G13.
+
 2009-10-20  Marcus Brinkmann  <marcus at g10code.de>
 
 	* configure.ac (AC_CONFIG_FILES): Remove assuan/Makefile.

Modified: trunk/src/ChangeLog
===================================================================
--- trunk/src/ChangeLog	2009-10-20 16:05:21 UTC (rev 1403)
+++ trunk/src/ChangeLog	2009-10-22 16:44:07 UTC (rev 1404)
@@ -1,3 +1,24 @@
+2009-10-22  Marcus Brinkmann  <marcus at g10code.de>
+
+	* 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  <marcus at g10code.de>
 
 	* gpgme-config.in (netlibs): Remove.

Modified: trunk/NEWS
===================================================================
--- trunk/NEWS	2009-10-20 16:05:21 UTC (rev 1403)
+++ trunk/NEWS	2009-10-22 16:44:07 UTC (rev 1404)
@@ -6,11 +6,16 @@
    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)
 ------------------------------------------------

Modified: trunk/configure.ac
===================================================================
--- trunk/configure.ac	2009-10-20 16:05:21 UTC (rev 1403)
+++ trunk/configure.ac	2009-10-22 16:44:07 UTC (rev 1404)
@@ -120,6 +120,7 @@
 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 @@
         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 @@
 #	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 @@
 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 @@
 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 @@
 				       [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 @@
 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 @@
 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 @@
 	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

Modified: trunk/src/Makefile.am
===================================================================
--- trunk/src/Makefile.am	2009-10-20 16:05:21 UTC (rev 1403)
+++ trunk/src/Makefile.am	2009-10-22 16:44:07 UTC (rev 1404)
@@ -86,6 +86,12 @@
 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 @@
 	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_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_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_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

Modified: trunk/src/conversion.c
===================================================================
--- trunk/src/conversion.c	2009-10-20 16:05:21 UTC (rev 1403)
+++ trunk/src/conversion.c	2009-10-22 16:44:07 UTC (rev 1404)
@@ -245,6 +245,75 @@
 }
 
 
+/* 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

Modified: trunk/src/engine-assuan.c
===================================================================
--- trunk/src/engine-assuan.c	2009-10-20 16:05:21 UTC (rev 1403)
+++ trunk/src/engine-assuan.c	2009-10-22 16:44:07 UTC (rev 1404)
@@ -554,6 +554,8 @@
           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

Modified: trunk/src/engine-backend.h
===================================================================
--- trunk/src/engine-backend.h	2009-10-20 16:05:21 UTC (rev 1403)
+++ trunk/src/engine-backend.h	2009-10-22 16:44:07 UTC (rev 1404)
@@ -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 @@
 #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 */

Added: trunk/src/engine-g13.c
===================================================================
--- trunk/src/engine-g13.c	                        (rev 0)
+++ trunk/src/engine-g13.c	2009-10-22 16:44:07 UTC (rev 1404)
@@ -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
+  };

Modified: trunk/src/engine.c
===================================================================
--- trunk/src/engine.c	2009-10-20 16:05:21 UTC (rev 1403)
+++ trunk/src/engine.c	2009-10-22 16:44:07 UTC (rev 1404)
@@ -55,11 +55,16 @@
 #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_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++)

Added: trunk/src/g13.c
===================================================================
--- trunk/src/g13.c	                        (rev 0)
+++ trunk/src/g13.c	2009-10-22 16:44:07 UTC (rev 1404)
@@ -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;
+}

Modified: trunk/src/gpgme.c
===================================================================
--- trunk/src/gpgme.c	2009-10-20 16:05:21 UTC (rev 1403)
+++ trunk/src/gpgme.c	2009-10-22 16:44:07 UTC (rev 1404)
@@ -248,7 +248,8 @@
 
   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 @@
     case GPGME_PROTOCOL_ASSUAN:
       return "Assuan";




More information about the Gnupg-commits mailing list