From a6f3857128220cc413434d5fb56cdd7f9a890c4c Mon Sep 17 00:00:00 2001 From: Marcus Brinkmann Date: Thu, 22 Oct 2009 16:44:07 +0000 Subject: [PATCH] 2009-10-22 Marcus Brinkmann * configure.ac: Add support for G13. src/ 2009-10-22 Marcus Brinkmann * 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. --- ChangeLog | 4 + NEWS | 13 +- configure.ac | 125 +++++++ src/ChangeLog | 21 ++ src/Makefile.am | 18 +- src/conversion.c | 69 ++++ src/engine-assuan.c | 2 + src/engine-backend.h | 8 +- src/engine-g13.c | 796 +++++++++++++++++++++++++++++++++++++++++++ src/engine.c | 12 +- src/g13.c | 229 +++++++++++++ src/gpgme.c | 6 +- src/gpgme.def | 2 + src/gpgme.h.in | 26 +- src/libgpgme.vers | 2 + src/posix-util.c | 10 + src/util.h | 4 + src/w32-util.c | 19 ++ 18 files changed, 1346 insertions(+), 20 deletions(-) create mode 100644 src/engine-g13.c create mode 100644 src/g13.c diff --git a/ChangeLog b/ChangeLog index ff98830d..fc92fa10 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2009-10-22 Marcus Brinkmann + + * configure.ac: Add support for G13. + 2009-10-20 Marcus Brinkmann * configure.ac (AC_CONFIG_FILES): Remove assuan/Makefile. diff --git a/NEWS b/NEWS index f22e197d..58e91f67 100644 --- a/NEWS +++ b/NEWS @@ -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 + + * 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 * 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 +#endif + +#include +#include +#include +#include +#include +#include +#include /* FIXME */ +#include + +#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 +#endif + +#include + +#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; @@ -1767,6 +1771,26 @@ gpgme_error_t gpgme_op_assuan_transact (gpgme_ctx_t ctx, void *stat_cb_value); + +/* 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). */ 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) diff --git a/src/util.h b/src/util.h index 5c61e542..c24a592b 100644 --- a/src/util.h +++ b/src/util.h @@ -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 @@ -361,6 +361,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) {