* configure.ac: Add support for G13.
src/
2009-10-22 Marcus Brinkmann <marcus@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-22 Marcus Brinkmann <marcus@g10code.de>
+
+ * configure.ac: Add support for G13.
+
2009-10-20 Marcus Brinkmann <marcus@g10code.de>
* configure.ac (AC_CONFIG_FILES): Remove assuan/Makefile.
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)
------------------------------------------------
GPG_DEFAULT=no
GPGSM_DEFAULT=no
GPGCONF_DEFAULT=no
+G13_DEFAULT=no
component_system=None
have_dosish_system=no
have_w32_system=no
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
# GPG_DEFAULT='/usr/bin/gpg'
# GPGSM_DEFAULT='/usr/bin/gpgsm'
# GPGCONF_DEFAULT='/usr/bin/gpgconf'
+# G13_DEFAULT='/usr/bin/g13'
;;
esac
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)
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.])
[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
if test "$GPGSM" != "no"; then
require_libassuan=yes
fi
+if test "$G13" != "no"; then
+ require_libassuan=yes
+fi
NO_OVERRIDE=no
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
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
+2009-10-22 Marcus Brinkmann <marcus@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@g10code.de>
* gpgme-config.in (netlibs): Remove.
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
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
$(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) \
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
@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
}
+/* 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
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
#include "engine.h"
-/* FIXME: Correct check? */
-#ifdef GPGSM_PATH
-#define ENABLE_GPGSM 1
-#endif
-
struct engine_ops
{
/* Static functions. */
#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 */
--- /dev/null
+/* 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"
+
+\f
+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);
+
+
+\f
+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;
+}
+
+\f
+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
+ };
#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
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++)
--- /dev/null
+/* 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;
+
+
+\f
+/* 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;
+}
+
+\f
+/* 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;
+}
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)
case GPGME_PROTOCOL_ASSUAN:
return "Assuan";
+ case GPGME_PROTOCOL_G13:
+ return "G13";
+
case GPGME_PROTOCOL_UNKNOWN:
return "unknown";
gpgme_op_export_keys @142
gpgme_op_export_keys_start @143
+ gpgme_op_g13_mount @144
+
; END
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;
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;
void *stat_cb_value);
+\f
+/* 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);
+
\f
/* Interface to gpgconf(1). */
gpgme_op_import_keys_start;
gpgme_op_export_keys;
gpgme_op_export_keys_start;
+
+ gpgme_op_g13_mount;
};
#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)
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);
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
}
+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)
{