2009-10-22 Marcus Brinkmann <marcus@g10code.de>
* 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.
This commit is contained in:
parent
9e2397571d
commit
a6f3857128
@ -1,3 +1,7 @@
|
||||
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.
|
||||
|
13
NEWS
13
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)
|
||||
------------------------------------------------
|
||||
|
125
configure.ac
125
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
|
||||
|
@ -1,3 +1,24 @@
|
||||
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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
796
src/engine-g13.c
Normal file
796
src/engine-g13.c
Normal file
@ -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
|
||||
};
|
12
src/engine.c
12
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++)
|
||||
|
229
src/g13.c
Normal file
229
src/g13.c
Normal file
@ -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;
|
||||
}
|
@ -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";
|
||||
|
||||
|
@ -185,5 +185,7 @@ EXPORTS
|
||||
gpgme_op_export_keys @142
|
||||
gpgme_op_export_keys_start @143
|
||||
|
||||
gpgme_op_g13_mount @144
|
||||
|
||||
; END
|
||||
|
||||
|
@ -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). */
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -59,6 +59,16 @@ _gpgme_get_gpgconf_path (void)
|
||||
#endif
|
||||
}
|
||||
|
||||
const char *
|
||||
_gpgme_get_g13_path (void)
|
||||
{
|
||||
#ifdef G13_PATH
|
||||
return G13_PATH;
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* See w32-util.c */
|
||||
int
|
||||
_gpgme_get_conf_int (const char *key, int *value)
|
||||
|
@ -32,6 +32,7 @@
|
||||
const char *_gpgme_get_gpg_path (void);
|
||||
const char *_gpgme_get_gpgsm_path (void);
|
||||
const char *_gpgme_get_gpgconf_path (void);
|
||||
const char *_gpgme_get_g13_path (void);
|
||||
int _gpgme_get_conf_int (const char *key, int *value);
|
||||
void _gpgme_allow_set_foreground_window (pid_t pid);
|
||||
|
||||
@ -93,6 +94,9 @@ gpgme_error_t _gpgme_decode_c_string (const char *src, char **destp,
|
||||
gpgme_error_t _gpgme_decode_percent_string (const char *src, char **destp,
|
||||
size_t len, int binary);
|
||||
|
||||
gpgme_error_t _gpgme_encode_percent_string (const char *src, char **destp,
|
||||
size_t len);
|
||||
|
||||
|
||||
/* Parse the string TIMESTAMP into a time_t. The string may either be
|
||||
seconds since Epoch or in the ISO 8601 format like
|
||||
|
@ -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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user