diff options
| author | Marcus Brinkmann <[email protected]> | 2009-11-10 09:07:19 +0000 | 
|---|---|---|
| committer | Marcus Brinkmann <[email protected]> | 2009-11-10 09:07:19 +0000 | 
| commit | 96cf17b15995c9b925f80a64dbd42c1305ca9895 (patch) | |
| tree | 364f15711af329ff9b3b207245516e1320fb3ba0 | |
| parent | Fix last change. (diff) | |
| download | gpgme-96cf17b15995c9b925f80a64dbd42c1305ca9895.tar.gz gpgme-96cf17b15995c9b925f80a64dbd42c1305ca9895.zip | |
2009-11-10  Marcus Brinkmann  <[email protected]>
	* configure.ac: Activate UIServer if FD passing is enabled and
	Assuan is available.
m4/
2009-11-10  Marcus Brinkmann  <[email protected]>
	* libassuan.m4: Fix LIBASSUAN_VERSION.
src/
2009-11-10  Marcus Brinkmann  <[email protected]>
	* Makefile.am (uiserver_components): New variable.
	(main_sources): Add it.
	* ops.h, key.c (_gpgme_key_append_name): Take CONVERT argument,
	implement it.  Adjust callers.
	(gpgme_key_from_uid): New function.
	* gpgme.h.in (gpgme_protocol_t): Add GPGME_PROTOCOL_DEFAULT.
	(gpgme_encrypt_flags_t): Add GPGME_ENCRYPT_PREPARE,
	GPGME_ENCRYPT_EXPECT_SIGN.
	(gpgme_set_sub_protocol, gpgme_key_from_uid): New functions.
	* libgpgme.vers, gpgme.def: Add new functions.
	* gpgme.c (gpgme_set_protocol): Add UIServer protocol.
	(gpgme_set_sub_protocol): New function.
	(gpgme_get_protocol_name): Add UIServer and default protocol.
	* assuan-support.c: Return correct error values, implement
	socketpair for POSIX.
	* priv-io.h, posix-io.c, w32-io.c, w32-glib-io.c,
	w32-qt-io.cpp (_gpgme_io_spawn): Add ATFORK and ATFORKVALUE
	arguments.  Implement it for POSIX.  Adjust all callers.
	* engine.h, engine-backend.h (_gpgme_engine_set_protocol)
	(_gpgme_engine_op_decrypt_verify): New prototypes.  Adjust all
	users.
	* engine.c (engine_ops, gpgme_get_engine_info): Add UIServer
	engine.
	(_gpgme_engine_set_protocol, _gpgme_engine_op_decrypt_verify): New
	function.
	* decrypt-verify.c (decrypt_verify_start): Call
	_gpgme_engine_op_decrypt_verify.
	* util.h, posix-util.c,
	w32-util.c (_gpgme_get_uiserver_socket_path): New function.
	* engine-gpgsm.c (gpgsm_set_fd): Fix _gpgme_io_pipe invocation.
	* gpgme-tool.c: Some support for UIServer protocol.
	* engine-uiserver.c: New file.
Diffstat (limited to '')
| -rw-r--r-- | ChangeLog | 5 | ||||
| -rw-r--r-- | NEWS | 2 | ||||
| -rw-r--r-- | TODO | 5 | ||||
| -rw-r--r-- | configure.ac | 30 | ||||
| -rw-r--r-- | m4/ChangeLog | 4 | ||||
| -rw-r--r-- | m4/libassuan.m4 | 2 | ||||
| -rw-r--r-- | src/ChangeLog | 35 | ||||
| -rw-r--r-- | src/Makefile.am | 8 | ||||
| -rw-r--r-- | src/assuan-support.c | 28 | ||||
| -rw-r--r-- | src/decrypt-verify.c | 2 | ||||
| -rw-r--r-- | src/dirinfo.c | 2 | ||||
| -rw-r--r-- | src/engine-assuan.c | 2 | ||||
| -rw-r--r-- | src/engine-backend.h | 13 | ||||
| -rw-r--r-- | src/engine-g13.c | 2 | ||||
| -rw-r--r-- | src/engine-gpg.c | 4 | ||||
| -rw-r--r-- | src/engine-gpgconf.c | 6 | ||||
| -rw-r--r-- | src/engine-gpgsm.c | 7 | ||||
| -rw-r--r-- | src/engine-uiserver.c | 1361 | ||||
| -rw-r--r-- | src/engine.c | 39 | ||||
| -rw-r--r-- | src/engine.h | 9 | ||||
| -rw-r--r-- | src/gpgme-tool.c | 61 | ||||
| -rw-r--r-- | src/gpgme.c | 26 | ||||
| -rw-r--r-- | src/gpgme.def | 3 | ||||
| -rw-r--r-- | src/gpgme.h.in | 21 | ||||
| -rw-r--r-- | src/key.c | 33 | ||||
| -rw-r--r-- | src/keylist.c | 2 | ||||
| -rw-r--r-- | src/libgpgme.vers | 2 | ||||
| -rw-r--r-- | src/ops.h | 2 | ||||
| -rw-r--r-- | src/posix-io.c | 7 | ||||
| -rw-r--r-- | src/posix-util.c | 24 | ||||
| -rw-r--r-- | src/priv-io.h | 4 | ||||
| -rw-r--r-- | src/util.h | 2 | ||||
| -rw-r--r-- | src/version.c | 2 | ||||
| -rw-r--r-- | src/w32-glib-io.c | 4 | ||||
| -rw-r--r-- | src/w32-io.c | 4 | ||||
| -rw-r--r-- | src/w32-qt-io.cpp | 4 | ||||
| -rw-r--r-- | src/w32-util.c | 23 | 
37 files changed, 1721 insertions, 69 deletions
| @@ -1,3 +1,8 @@ +2009-11-10  Marcus Brinkmann  <[email protected]> + +	* configure.ac: Activate UIServer if FD passing is enabled and +	Assuan is available. +  2009-10-30  Marcus Brinkmann  <[email protected]>  	* configure.ac: Check for argp.h and error_t. @@ -8,6 +8,8 @@ Noteworthy changes in version 1.2.1 (unreleased)   * New engine GPGME_PROTOCOL_G13 to support the new g13 tool. + * New engine GPGME_PROTOCOL_UISERVER to support UI Servers. +   * Interface changes relative to the 1.2.0 release:  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  GPGME_STATUS_INV_SGNR    NEW @@ -1,5 +1,10 @@  Hey Emacs, this is -*- org -*- mode! +* Document all the new stuff. +* Fix the remaining UI Server problems: +** VERIFY --silent support. +** ENCRYPT/DECRYPT/VERIFY/SIGN reset the engine, shouldn't be done with UISERVER? +  * IMPORTANT  ** When using descriptor passing, we need to set the fd to blocking before     issueing simple commands, because we are mixing synchronous diff --git a/configure.ac b/configure.ac index 3cd7c0b7..dc5ebe26 100644 --- a/configure.ac +++ b/configure.ac @@ -338,7 +338,7 @@ AC_DEFINE_UNQUOTED(NEED_GPGSM_VERSION, "$NEED_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.]) +				     [Min. needed G13 version.])  NO_OVERRIDE=no @@ -634,13 +634,6 @@ AC_ARG_ENABLE(gpgconf-test,           run_gpgconf_test=$enableval)  AM_CONDITIONAL(RUN_GPGCONF_TESTS, test "$run_gpgconf_test" = "yes") -# Only build if supported. -AM_CONDITIONAL(BUILD_GPGCONF, test "$GPGCONF" != "no") -if test "$GPGCONF" != "no"; then -  AC_DEFINE(HAVE_GPGCONF, 1, -            [Defined if we are building with gpgconf support.]) -fi -  NO_OVERRIDE=no  AC_ARG_WITH(g13, @@ -737,13 +730,6 @@ AC_ARG_ENABLE(g13-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) @@ -803,6 +789,17 @@ fi  AM_CONDITIONAL(USE_DESCRIPTOR_PASSING, test "$use_descriptor_passing" = "yes") +uiserver=no +if test "$use_descriptor_passing" = "yes" && test "$have_libassuan" = "yes"; then +  uiserver=yes +fi +if test "$uiserver" != "no"; then +  AC_DEFINE(ENABLE_UISERVER, 1, +            [Defined if we are building with uiserver support.]) +fi +AM_CONDITIONAL(HAVE_UISERVER, test "$uiserver" != "no") + +  AM_CONDITIONAL(BUILD_COMPLUS, test "$component_system" = "COM+")  # Generate values for the DLL version info @@ -912,6 +909,9 @@ echo "          Assuan version:  $LIBASSUAN_VERSION +	UI Server:       $uiserver +        FD Passing:      $use_descriptor_passing +  	GPGME Pthread:   $have_pthread  	GPGME Pth:       $have_pth  " diff --git a/m4/ChangeLog b/m4/ChangeLog index fadd952f..8eba5f0b 100644 --- a/m4/ChangeLog +++ b/m4/ChangeLog @@ -1,3 +1,7 @@ +2009-11-10  Marcus Brinkmann  <[email protected]> + +	* libassuan.m4: Fix LIBASSUAN_VERSION. +  2006-06-08  Marcus Brinkmann  <[email protected]>  	* pth.m4: Add --all to pth-config invocation. diff --git a/m4/libassuan.m4 b/m4/libassuan.m4 index bd2275e1..b7fec3ac 100644 --- a/m4/libassuan.m4 +++ b/m4/libassuan.m4 @@ -63,7 +63,7 @@ AC_DEFUN([AM_PATH_LIBASSUAN],    if test $ok = yes; then      LIBASSUAN_CFLAGS=`$LIBASSUAN_CONFIG $libassuan_config_args --cflags`      LIBASSUAN_LIBS=`$LIBASSUAN_CONFIG $libassuan_config_args --libs` -    LIBASSUAN_VERSION="$LIBASSUAN_CONFIG_VERSION" +    LIBASSUAN_VERSION="$libassuan_config_version"      AC_MSG_RESULT(yes)      ifelse([$2], , :, [$2])    else diff --git a/src/ChangeLog b/src/ChangeLog index 5caddaeb..b758b7ba 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,38 @@ +2009-11-10  Marcus Brinkmann  <[email protected]> + +	* Makefile.am (uiserver_components): New variable. +	(main_sources): Add it. +	* ops.h, key.c (_gpgme_key_append_name): Take CONVERT argument, +	implement it.  Adjust callers. +	(gpgme_key_from_uid): New function. +	* gpgme.h.in (gpgme_protocol_t): Add GPGME_PROTOCOL_DEFAULT. +	(gpgme_encrypt_flags_t): Add GPGME_ENCRYPT_PREPARE, +	GPGME_ENCRYPT_EXPECT_SIGN. +	(gpgme_set_sub_protocol, gpgme_key_from_uid): New functions. +	* libgpgme.vers, gpgme.def: Add new functions. +	* gpgme.c (gpgme_set_protocol): Add UIServer protocol. +	(gpgme_set_sub_protocol): New function. +	(gpgme_get_protocol_name): Add UIServer and default protocol. +	* assuan-support.c: Return correct error values, implement +	socketpair for POSIX. +	* priv-io.h, posix-io.c, w32-io.c, w32-glib-io.c, +	w32-qt-io.cpp (_gpgme_io_spawn): Add ATFORK and ATFORKVALUE +	arguments.  Implement it for POSIX.  Adjust all callers. +	* engine.h, engine-backend.h (_gpgme_engine_set_protocol) +	(_gpgme_engine_op_decrypt_verify): New prototypes.  Adjust all +	users. +	* engine.c (engine_ops, gpgme_get_engine_info): Add UIServer +	engine. +	(_gpgme_engine_set_protocol, _gpgme_engine_op_decrypt_verify): New +	function. +	* decrypt-verify.c (decrypt_verify_start): Call +	_gpgme_engine_op_decrypt_verify. +	* util.h, posix-util.c, +	w32-util.c (_gpgme_get_uiserver_socket_path): New function. +	* engine-gpgsm.c (gpgsm_set_fd): Fix _gpgme_io_pipe invocation. +	* gpgme-tool.c: Some support for UIServer protocol. +	* engine-uiserver.c: New file. +  2009-11-09  Marcus Brinkmann  <[email protected]>  	* engine-gpgsm.c (gpgsm_new): Close server side FDs. diff --git a/src/Makefile.am b/src/Makefile.am index b1d93d36..0879869e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -92,6 +92,13 @@ else  g13_components =  endif +if HAVE_UISERVER +uiserver_components = engine-uiserver.c +else +uiserver_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 @@ -111,6 +118,7 @@ main_sources =								\  	opassuan.c                                                      \  	engine.h engine-backend.h engine.c engine-gpg.c status-table.h	\  	$(gpgsm_components) $(assuan_components) $(gpgconf_components)  \ +	$(uiserver_components)						\  	$(g13_components) vfs-mount.c vfs-create.c			\  	gpgconf.c							\  	sema.h priv-io.h $(system_components) dirinfo.c			\ diff --git a/src/assuan-support.c b/src/assuan-support.c index 4646416f..d5defa8a 100644 --- a/src/assuan-support.c +++ b/src/assuan-support.c @@ -4,6 +4,7 @@  #include <assert.h>  #include <stdlib.h> +#include <errno.h>  #include "assuan.h" @@ -76,7 +77,8 @@ my_recvmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg,  	    int flags)  {  #ifdef HAVE_W32_SYSTEM -  return gpg_error (GPG_ERR_NOT_IMPLEMENTED); +  errno = ENOSYS; +  return -1;  #else    return _gpgme_io_recvmsg (fd, msg, flags);  #endif @@ -89,7 +91,8 @@ my_sendmsg (assuan_context_t ctx, assuan_fd_t fd, const assuan_msghdr_t msg,  	    int flags)  {  #ifdef HAVE_W32_SYSTEM -  return gpg_error (GPG_ERR_NOT_IMPLEMENTED); +  errno = ENOSYS; +  return -1;  #else    return _gpgme_io_sendmsg (fd, msg, flags);  #endif @@ -107,14 +110,17 @@ my_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name,  	  void (*atfork) (void *opaque, int reserved),  	  void *atforkvalue, unsigned int flags)  { -  gpg_error_t err; +  int err;    struct spawn_fd_item_s *fd_items;    int i;    assert (name);    if (! name) -    return gpg_error (GPG_ERR_NOT_IMPLEMENTED); +    { +      errno = ENOSYS; +      return -1; +    }    i = 0;    if (fd_child_list) @@ -126,7 +132,7 @@ my_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name,    i += 3;    fd_items = malloc (sizeof (struct spawn_fd_item_s) * i);    if (! fd_items) -    return gpg_error_from_syserror (); +    return -1;    i = 0;    if (fd_child_list)      { @@ -152,7 +158,8 @@ my_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name,    fd_items[i].fd = -1;    fd_items[i].dup_to = -1; -  err = _gpgme_io_spawn (name, argv, IOSPAWN_FLAG_NOCLOSE, fd_items, r_pid); +  err = _gpgme_io_spawn (name, argv, IOSPAWN_FLAG_NOCLOSE, fd_items, +			 atfork, atforkvalue, r_pid);    if (! err)      {        i = 0; @@ -195,8 +202,13 @@ static int  my_socketpair (assuan_context_t ctx, int namespace, int style,  	       int protocol, assuan_fd_t filedes[2])  { -  assert ("Should never happen."); -  return gpg_error (GPG_ERR_NOT_IMPLEMENTED); +#ifdef HAVE_W32_SYSTEM +  errno = ENOSYS; +  return -1; +#else +  /* FIXME: Debug output missing.  */ +  return __assuan_socketpair (ctx, namespace, style, protocol, filedes); +#endif  } diff --git a/src/decrypt-verify.c b/src/decrypt-verify.c index f28dda05..87d5223b 100644 --- a/src/decrypt-verify.c +++ b/src/decrypt-verify.c @@ -77,7 +77,7 @@ decrypt_verify_start (gpgme_ctx_t ctx, int synchronous,    _gpgme_engine_set_status_handler (ctx->engine,  				    decrypt_verify_status_handler, ctx); -  return _gpgme_engine_op_decrypt (ctx->engine, cipher, plain); +  return _gpgme_engine_op_decrypt_verify (ctx->engine, cipher, plain);  } diff --git a/src/dirinfo.c b/src/dirinfo.c index ab15a595..b1e438e2 100644 --- a/src/dirinfo.c +++ b/src/dirinfo.c @@ -102,7 +102,7 @@ read_gpgconf_dirs (void)    cfd[0].fd = rp[1]; -  status = _gpgme_io_spawn (pgmname, argv, 0, cfd, NULL); +  status = _gpgme_io_spawn (pgmname, argv, 0, cfd, NULL, NULL, NULL);    if (status < 0)      {        _gpgme_io_close (rp[0]); diff --git a/src/engine-assuan.c b/src/engine-assuan.c index cd722cf0..df3328ce 100644 --- a/src/engine-assuan.c +++ b/src/engine-assuan.c @@ -743,7 +743,9 @@ struct engine_ops _gpgme_engine_ops_assuan =      NULL,		/* set_command_handler */      NULL,               /* set_colon_line_handler */      llass_set_locale, +    NULL,		/* set_protocol */      NULL,               /* decrypt */ +    NULL,               /* decrypt_verify */      NULL,               /* delete */      NULL,		/* edit */      NULL,               /* encrypt */ diff --git a/src/engine-backend.h b/src/engine-backend.h index bb938d0f..fa0b4392 100644 --- a/src/engine-backend.h +++ b/src/engine-backend.h @@ -58,8 +58,11 @@ struct engine_ops  					   engine_colon_line_handler_t fnc,  					   void *fnc_value);    gpgme_error_t (*set_locale) (void *engine, int category, const char *value); +  gpgme_error_t (*set_protocol) (void *engine, gpgme_protocol_t protocol);    gpgme_error_t (*decrypt) (void *engine, gpgme_data_t ciph,  			    gpgme_data_t plain); +  gpgme_error_t (*decrypt_verify) (void *engine, gpgme_data_t ciph, +				   gpgme_data_t plain);    gpgme_error_t (*delete) (void *engine, gpgme_key_t key, int allow_secret);    gpgme_error_t (*edit) (void *engine, int type, gpgme_key_t key,  			 gpgme_data_t out, gpgme_ctx_t ctx /* FIXME */); @@ -88,12 +91,11 @@ struct engine_ops  				gpgme_keylist_mode_t mode);    gpgme_error_t (*sign) (void *engine, gpgme_data_t in, gpgme_data_t out,  			 gpgme_sig_mode_t mode, int use_armor, -			 int use_textmode, -			 int include_certs, gpgme_ctx_t ctx /* FIXME */); +			 int use_textmode, int include_certs, +			 gpgme_ctx_t ctx /* FIXME */);    gpgme_error_t (*trustlist) (void *engine, const char *pattern);    gpgme_error_t (*verify) (void *engine, gpgme_data_t sig, -			   gpgme_data_t signed_text, -			   gpgme_data_t plaintext); +			   gpgme_data_t signed_text, gpgme_data_t plaintext);    gpgme_error_t  (*getauditlog) (void *engine, gpgme_data_t output,                                   unsigned int flags);    gpgme_error_t  (*opassuan_transact) (void *engine,  @@ -132,5 +134,8 @@ extern struct engine_ops _gpgme_engine_ops_assuan;	/* Low-level Assuan. */  #ifdef ENABLE_G13  extern struct engine_ops _gpgme_engine_ops_g13;         /* Crypto VFS. */  #endif +#ifdef ENABLE_UISERVER +extern struct engine_ops _gpgme_engine_ops_uiserver; +#endif  #endif /* ENGINE_BACKEND_H */ diff --git a/src/engine-g13.c b/src/engine-g13.c index 4bb1630a..7ccca92c 100644 --- a/src/engine-g13.c +++ b/src/engine-g13.c @@ -757,7 +757,9 @@ struct engine_ops _gpgme_engine_ops_g13 =      NULL,		/* set_command_handler */      NULL,               /* set_colon_line_handler */      g13_set_locale, +    NULL,		/* set_protocol */      NULL,               /* decrypt */ +    NULL,               /* decrypt_verify */      NULL,               /* delete */      NULL,		/* edit */      NULL,               /* encrypt */ diff --git a/src/engine-gpg.c b/src/engine-gpg.c index 6d6ec47c..d4683b6e 100644 --- a/src/engine-gpg.c +++ b/src/engine-gpg.c @@ -1332,7 +1332,7 @@ start (engine_gpg_t gpg)    status = _gpgme_io_spawn (gpg->file_name ? gpg->file_name :  			    _gpgme_get_gpg_path (), gpg->argv,                              IOSPAWN_FLAG_ALLOW_SET_FG, -                            fd_list, &pid); +                            fd_list, NULL, NULL, &pid);    saved_errno = errno;    free (fd_list); @@ -2347,7 +2347,9 @@ struct engine_ops _gpgme_engine_ops_gpg =      gpg_set_command_handler,      gpg_set_colon_line_handler,      gpg_set_locale, +    NULL,				/* set_protocol */      gpg_decrypt, +    gpg_decrypt,			/* decrypt_verify */      gpg_delete,      gpg_edit,      gpg_encrypt, diff --git a/src/engine-gpgconf.c b/src/engine-gpgconf.c index cfa04ce8..6448b066 100644 --- a/src/engine-gpgconf.c +++ b/src/engine-gpgconf.c @@ -221,7 +221,7 @@ gpgconf_read (void *engine, char *arg1, char *arg2,    cfd[0].fd = rp[1]; -  status = _gpgme_io_spawn (gpgconf->file_name, argv, 0, cfd, NULL); +  status = _gpgme_io_spawn (gpgconf->file_name, argv, 0, cfd, NULL, NULL, NULL);    if (status < 0)      {        _gpgme_io_close (rp[0]); @@ -659,7 +659,7 @@ gpgconf_write (void *engine, char *arg1, char *arg2, gpgme_data_t conf)    cfd[0].fd = rp[0]; -  status = _gpgme_io_spawn (gpgconf->file_name, argv, 0, cfd, NULL); +  status = _gpgme_io_spawn (gpgconf->file_name, argv, 0, cfd, NULL, NULL, NULL);    if (status < 0)      {        _gpgme_io_close (rp[0]); @@ -897,7 +897,9 @@ struct engine_ops _gpgme_engine_ops_gpgconf =      NULL,		/* set_command_handler */      NULL,		/* set_colon_line_handler */      NULL,		/* set_locale */ +    NULL,		/* set_protocol */      NULL,		/* decrypt */ +    NULL,		/* decrypt_verify */      NULL,		/* delete */      NULL,		/* edit */      NULL,		/* encrypt */ diff --git a/src/engine-gpgsm.c b/src/engine-gpgsm.c index 850654c5..d2670d6f 100644 --- a/src/engine-gpgsm.c +++ b/src/engine-gpgsm.c @@ -38,6 +38,7 @@  #include "wait.h"  #include "priv-io.h"  #include "sema.h" +#include "data.h"  #include "assuan.h"  #include "status-table.h" @@ -657,7 +658,7 @@ gpgsm_set_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type, const char *opt)      {        int fds[2]; -      if (_gpgme_io_pipe (fds, 0) < 0) +      if (_gpgme_io_pipe (fds, dir) < 0)  	return gpg_error_from_errno (errno);        iocb_data->fd = dir ? fds[0] : fds[1]; @@ -1914,8 +1915,10 @@ struct engine_ops _gpgme_engine_ops_gpgsm =      NULL,		/* set_command_handler */      gpgsm_set_colon_line_handler,      gpgsm_set_locale, +    NULL,		/* set_protocol */      gpgsm_decrypt, -    gpgsm_delete, +    gpgsm_decrypt, +    gpgsm_delete,	/* decrypt_verify */      NULL,		/* edit */      gpgsm_encrypt,      NULL,		/* encrypt_sign */ diff --git a/src/engine-uiserver.c b/src/engine-uiserver.c new file mode 100644 index 00000000..a867832a --- /dev/null +++ b/src/engine-uiserver.c @@ -0,0 +1,1361 @@ +/* engine-uiserver.c - Uiserver 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.  */ + +/* Peculiar: Use special keys from email address for recipient and +   signer (==sender).  Use no data objects with encryption for +   prep_encrypt.  */ + +#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 "data.h" + +#include "assuan.h" +#include "status-table.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_uiserver +{ +  assuan_context_t assuan_ctx; + +  int lc_ctype_set; +  int lc_messages_set; +  gpgme_protocol_t protocol; + +  iocb_data_t status_cb; + +  /* Input, output etc are from the servers perspective.  */ +  iocb_data_t input_cb; +  gpgme_data_t input_helper_data;  /* Input helper data object.  */ +  void *input_helper_memory;       /* Input helper memory block.  */ + +  iocb_data_t output_cb; + +  iocb_data_t message_cb; + +  struct +  { +    engine_status_handler_t fnc; +    void *fnc_value; +  } status; + +  struct +  { +    engine_colon_line_handler_t fnc; +    void *fnc_value; +    struct +    { +      char *line; +      int linesize; +      int linelen; +    } attic; +    int any; /* any data line seen */ +  } colon;  + +  gpgme_data_t inline_data;  /* Used to collect D lines.  */ + +  struct gpgme_io_cbs io_cbs; +}; + +typedef struct engine_uiserver *engine_uiserver_t; + + +static void uiserver_io_event (void *engine,  +                            gpgme_event_io_t type, void *type_data); + + + +static char * +uiserver_get_version (const char *file_name) +{ +  return strdup ("1.0"); +} + + +static const char * +uiserver_get_req_version (void) +{ +  return "1.0"; +} + + +static void +close_notify_handler (int fd, void *opaque) +{ +  engine_uiserver_t uiserver = opaque; + +  assert (fd != -1); +  if (uiserver->status_cb.fd == fd) +    { +      if (uiserver->status_cb.tag) +	(*uiserver->io_cbs.remove) (uiserver->status_cb.tag); +      uiserver->status_cb.fd = -1; +      uiserver->status_cb.tag = NULL; +    } +  else if (uiserver->input_cb.fd == fd) +    { +      if (uiserver->input_cb.tag) +	(*uiserver->io_cbs.remove) (uiserver->input_cb.tag); +      uiserver->input_cb.fd = -1; +      uiserver->input_cb.tag = NULL; +      if (uiserver->input_helper_data) +        { +          gpgme_data_release (uiserver->input_helper_data); +          uiserver->input_helper_data = NULL; +        } +      if (uiserver->input_helper_memory) +        { +          free (uiserver->input_helper_memory); +          uiserver->input_helper_memory = NULL; +        } +    } +  else if (uiserver->output_cb.fd == fd) +    { +      if (uiserver->output_cb.tag) +	(*uiserver->io_cbs.remove) (uiserver->output_cb.tag); +      uiserver->output_cb.fd = -1; +      uiserver->output_cb.tag = NULL; +    } +  else if (uiserver->message_cb.fd == fd) +    { +      if (uiserver->message_cb.tag) +	(*uiserver->io_cbs.remove) (uiserver->message_cb.tag); +      uiserver->message_cb.fd = -1; +      uiserver->message_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_uiserver_t uiserver, const char *line) +{ +  if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17])) +    { +      _gpgme_allow_set_foreground_window ((pid_t)strtoul (line+17, NULL, 10)); +    } + +  return 0; +} + + +static gpgme_error_t +uiserver_cancel (void *engine) +{ +  engine_uiserver_t uiserver = engine; + +  if (!uiserver) +    return gpg_error (GPG_ERR_INV_VALUE); + +  if (uiserver->status_cb.fd != -1) +    _gpgme_io_close (uiserver->status_cb.fd); +  if (uiserver->input_cb.fd != -1) +    _gpgme_io_close (uiserver->input_cb.fd); +  if (uiserver->output_cb.fd != -1) +    _gpgme_io_close (uiserver->output_cb.fd); +  if (uiserver->message_cb.fd != -1) +    _gpgme_io_close (uiserver->message_cb.fd); + +  if (uiserver->assuan_ctx) +    { +      assuan_release (uiserver->assuan_ctx); +      uiserver->assuan_ctx = NULL; +    } + +  return 0; +} + + +static void +uiserver_release (void *engine) +{ +  engine_uiserver_t uiserver = engine; + +  if (!uiserver) +    return; + +  uiserver_cancel (engine); + +  free (uiserver->colon.attic.line); +  free (uiserver); +} + + +static gpgme_error_t +uiserver_new (void **engine, const char *file_name, const char *home_dir) +{ +  gpgme_error_t err = 0; +  engine_uiserver_t uiserver; +  char *dft_display = NULL; +  char dft_ttyname[64]; +  char *dft_ttytype = NULL; +  char *optstr; + +  uiserver = calloc (1, sizeof *uiserver); +  if (!uiserver) +    return gpg_error_from_syserror (); + +  uiserver->protocol = GPGME_PROTOCOL_DEFAULT; +  uiserver->status_cb.fd = -1; +  uiserver->status_cb.dir = 1; +  uiserver->status_cb.tag = 0; +  uiserver->status_cb.data = uiserver; + +  uiserver->input_cb.fd = -1; +  uiserver->input_cb.dir = 0; +  uiserver->input_cb.tag = 0; +  uiserver->input_cb.server_fd = -1; +  *uiserver->input_cb.server_fd_str = 0; +  uiserver->output_cb.fd = -1; +  uiserver->output_cb.dir = 1; +  uiserver->output_cb.tag = 0; +  uiserver->output_cb.server_fd = -1; +  *uiserver->output_cb.server_fd_str = 0; +  uiserver->message_cb.fd = -1; +  uiserver->message_cb.dir = 0; +  uiserver->message_cb.tag = 0; +  uiserver->message_cb.server_fd = -1; +  *uiserver->message_cb.server_fd_str = 0; + +  uiserver->status.fnc = 0; +  uiserver->colon.fnc = 0; +  uiserver->colon.attic.line = 0; +  uiserver->colon.attic.linesize = 0; +  uiserver->colon.attic.linelen = 0; +  uiserver->colon.any = 0; + +  uiserver->inline_data = NULL; + +  uiserver->io_cbs.add = NULL; +  uiserver->io_cbs.add_priv = NULL; +  uiserver->io_cbs.remove = NULL; +  uiserver->io_cbs.event = NULL; +  uiserver->io_cbs.event_priv = NULL; + +  err = assuan_new_ext (&uiserver->assuan_ctx, GPG_ERR_SOURCE_GPGME, +			&_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb, +			NULL); +  if (err) +    goto leave; +  assuan_ctx_set_system_hooks (uiserver->assuan_ctx, +			       &_gpgme_assuan_system_hooks); + +  err = assuan_socket_connect (uiserver->assuan_ctx, +			       file_name ? +			       file_name : _gpgme_get_uiserver_socket_path (), +			       0, 0); +  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 (uiserver->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 (uiserver->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 (uiserver->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 +     uiserver to tell us when it needs it.  */ +  if (!err) +    { +      err = assuan_transact (uiserver->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 uiserver.  */ +    } +#endif /*HAVE_W32_SYSTEM*/ + + leave: +  if (err) +    uiserver_release (uiserver); +  else +    *engine = uiserver; + +  return err; +} + + +static gpgme_error_t +uiserver_set_locale (void *engine, int category, const char *value) +{ +  engine_uiserver_t uiserver = 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.  UISERVER needs support +     for this.  */ +  if (category == LC_CTYPE) +    { +      catstr = "lc-ctype"; +      if (!value && uiserver->lc_ctype_set) +	return gpg_error (GPG_ERR_INV_VALUE); +      if (value) +	uiserver->lc_ctype_set = 1; +    } +#ifdef LC_MESSAGES +  else if (category == LC_MESSAGES) +    { +      catstr = "lc-messages"; +      if (!value && uiserver->lc_messages_set) +	return gpg_error (GPG_ERR_INV_VALUE); +      if (value) +	uiserver->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 (uiserver->assuan_ctx, optstr, NULL, NULL, +			     NULL, NULL, NULL, NULL); +      free (optstr); +    } + +  return err; +} + + +static gpgme_error_t +uiserver_set_protocol (void *engine, gpgme_protocol_t protocol) +{ +  engine_uiserver_t uiserver = engine; + +  if (protocol != GPGME_PROTOCOL_OpenPGP +      && protocol != GPGME_PROTOCOL_CMS +      && protocol != GPGME_PROTOCOL_DEFAULT) +    return gpg_error (GPG_ERR_INV_VALUE); + +  uiserver->protocol = protocol; +  return 0; +} + + +/* Forward declaration.  */ +static gpgme_status_code_t parse_status (const char *name); + +static gpgme_error_t +uiserver_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; +} + + +typedef enum { INPUT_FD, OUTPUT_FD, MESSAGE_FD } fd_type_t; + +#define COMMANDLINELEN 40 +static gpgme_error_t +uiserver_set_fd (engine_uiserver_t uiserver, fd_type_t fd_type, const char *opt) +{ +  gpg_error_t err = 0; +  char line[COMMANDLINELEN]; +  char *which; +  iocb_data_t *iocb_data; +  int dir; + +  switch (fd_type) +    { +    case INPUT_FD: +      which = "INPUT"; +      iocb_data = &uiserver->input_cb; +      break; + +    case OUTPUT_FD: +      which = "OUTPUT"; +      iocb_data = &uiserver->output_cb; +      break; + +    case MESSAGE_FD: +      which = "MESSAGE"; +      iocb_data = &uiserver->message_cb; +      break; + +    default: +      return gpg_error (GPG_ERR_INV_VALUE); +    } + +  dir = iocb_data->dir; + +  /* We try to short-cut the communication by giving UISERVER direct +     access to the file descriptor, rather than using a pipe.  */ +  iocb_data->server_fd = _gpgme_data_get_fd (iocb_data->data); +  if (iocb_data->server_fd < 0) +    { +      int fds[2]; + +      if (_gpgme_io_pipe (fds, 0) < 0) +	return gpg_error_from_errno (errno); + +      iocb_data->fd = dir ? fds[0] : fds[1]; +      iocb_data->server_fd = dir ? fds[1] : fds[0]; + +      if (_gpgme_io_set_close_notify (iocb_data->fd, +				      close_notify_handler, uiserver)) +	{ +	  err = gpg_error (GPG_ERR_GENERAL); +	  goto leave_set_fd; +	} +    } + +  err = assuan_sendfd (uiserver->assuan_ctx, iocb_data->server_fd); +  if (err) +    goto leave_set_fd; + +  _gpgme_io_close (iocb_data->server_fd); +  iocb_data->server_fd = -1; + +  if (opt) +    snprintf (line, COMMANDLINELEN, "%s FD %s", which, opt); +  else +    snprintf (line, COMMANDLINELEN, "%s FD", which); + +  err = uiserver_assuan_simple_command (uiserver->assuan_ctx, line, NULL, NULL); + + leave_set_fd: +  if (err) +    { +      _gpgme_io_close (iocb_data->fd); +      iocb_data->fd = -1; +      if (iocb_data->server_fd != -1) +        { +          _gpgme_io_close (iocb_data->server_fd); +          iocb_data->server_fd = -1; +        } +    } + +  return err; +} + + +static const char * +map_data_enc (gpgme_data_t d) +{ +  switch (gpgme_data_get_encoding (d)) +    { +    case GPGME_DATA_ENCODING_NONE: +      break; +    case GPGME_DATA_ENCODING_BINARY: +      return "--binary"; +    case GPGME_DATA_ENCODING_BASE64: +      return "--base64"; +    case GPGME_DATA_ENCODING_ARMOR: +      return "--armor"; +    default: +      break; +    } +  return NULL; +} + + +static int +status_cmp (const void *ap, const void *bp) +{ +  const struct status_table_s *a = ap; +  const struct status_table_s *b = bp; + +  return strcmp (a->name, b->name); +} + + +static gpgme_status_code_t +parse_status (const char *name) +{ +  struct status_table_s t, *r; +  t.name = name; +  r = bsearch (&t, status_table, DIM(status_table) - 1, +	       sizeof t, status_cmp); +  return r ? r->code : -1; +} + + +static gpgme_error_t +status_handler (void *opaque, int fd) +{ +  struct io_cb_data *data = (struct io_cb_data *) opaque; +  engine_uiserver_t uiserver = (engine_uiserver_t) data->handler_value; +  gpgme_error_t err = 0; +  char *line; +  size_t linelen; + +  do +    { +      err = assuan_read_line (uiserver->assuan_ctx, &line, &linelen); +      if (err) +	{ +	  /* Try our best to terminate the connection friendly.  */ +	  /*	  assuan_write_line (uiserver->assuan_ctx, "BYE"); */ +          TRACE3 (DEBUG_CTX, "gpgme:status_handler", uiserver, +		  "fd 0x%x: error from assuan (%d) getting status line : %s", +                  fd, err, 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", uiserver, +		  "fd 0x%x: ERR line - mapped to: %s", +                  fd, err ? gpg_strerror (err) : "ok"); +	  /* Try our best to terminate the connection friendly.  */ +	  /*	  assuan_write_line (uiserver->assuan_ctx, "BYE"); */ +	} +      else if (linelen >= 2 +	       && line[0] == 'O' && line[1] == 'K' +	       && (line[2] == '\0' || line[2] == ' ')) +	{ +	  if (uiserver->status.fnc) +	    err = uiserver->status.fnc (uiserver->status.fnc_value, +				     GPGME_STATUS_EOF, ""); +	   +	  if (!err && uiserver->colon.fnc && uiserver->colon.any) +            { +              /* We must tell a colon function about the EOF. We do +                 this only when we have seen any data lines.  Note +                 that this inlined use of colon data lines will +                 eventually be changed into using a regular data +                 channel. */ +              uiserver->colon.any = 0; +              err = uiserver->colon.fnc (uiserver->colon.fnc_value, NULL); +            } +          TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver, +		  "fd 0x%x: OK line - final status: %s", +                  fd, err ? gpg_strerror (err) : "ok"); +	  _gpgme_io_close (uiserver->status_cb.fd); +	  return err; +	} +      else if (linelen > 2 +	       && line[0] == 'D' && line[1] == ' ' +	       && uiserver->colon.fnc) +        { +	  /* 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; +          char **aline = &uiserver->colon.attic.line; +	  int *alinelen = &uiserver->colon.attic.linelen; + +	  if (uiserver->colon.attic.linesize < *alinelen + linelen + 1) +	    { +	      char *newline = realloc (*aline, *alinelen + linelen + 1); +	      if (!newline) +		err = gpg_error_from_errno (errno); +	      else +		{ +		  *aline = newline; +		  uiserver->colon.attic.linesize += linelen + 1; +		} +	    } +	  if (!err) +	    { +	      dst = *aline + *alinelen; + +	      while (!err && src < end) +		{ +		  if (*src == '%' && src + 2 < end) +		    { +		      /* Handle escaped characters.  */ +		      ++src; +		      *dst = _gpgme_hextobyte (src); +		      (*alinelen)++; +		      src += 2; +		    } +		  else +		    { +		      *dst = *src++; +		      (*alinelen)++; +		    } +		   +		  if (*dst == '\n') +		    { +		      /* Terminate the pending line, pass it to the colon +			 handler and reset it.  */ +		       +		      uiserver->colon.any = 1; +		      if (*alinelen > 1 && *(dst - 1) == '\r') +			dst--; +		      *dst = '\0'; + +		      /* FIXME How should we handle the return code?  */ +		      err = uiserver->colon.fnc (uiserver->colon.fnc_value, *aline); +		      if (!err) +			{ +			  dst = *aline; +			  *alinelen = 0; +			} +		    } +		  else +		    dst++; +		} +	    } +          TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver, +		  "fd 0x%x: D line; final status: %s", +                  fd, err? gpg_strerror (err):"ok"); +        } +      else if (linelen > 2 +	       && line[0] == 'D' && line[1] == ' ' +	       && uiserver->inline_data) +        { +          char *src = line + 2; +	  char *end = line + linelen; +	  char *dst = src; +          ssize_t nwritten; + +          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; +          while (linelen > 0) +            { +              nwritten = gpgme_data_write (uiserver->inline_data, src, linelen); +              if (!nwritten || (nwritten < 0 && errno != EINTR) +                  || nwritten > linelen) +                { +                  err = gpg_error_from_errno (errno); +                  break; +                } +              src += nwritten; +              linelen -= nwritten; +            } + +          TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver, +		  "fd 0x%x: D inlinedata; final status: %s", +                  fd, err? gpg_strerror (err):"ok"); +        } +      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) +	    { +	      if (uiserver->status.fnc) +		err = uiserver->status.fnc (uiserver->status.fnc_value, r, rest); +	    } +	  else +	    fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest); +          TRACE3 (DEBUG_CTX, "gpgme:status_handler", uiserver, +		  "fd 0x%x: S line (%s) - final status: %s", +                  fd, line+2, err? gpg_strerror (err):"ok"); +	} +      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 *keyword = line+7; + +          while (*keyword == ' ') +            keyword++;; +          default_inq_cb (uiserver, keyword); +          assuan_write_line (uiserver->assuan_ctx, "END"); +        } + +    } +  while (!err && assuan_pending_line (uiserver->assuan_ctx)); +	   +  return err; +} + + +static gpgme_error_t +add_io_cb (engine_uiserver_t uiserver, iocb_data_t *iocbd, gpgme_io_cb_t handler) +{ +  gpgme_error_t err; + +  TRACE_BEG2 (DEBUG_ENGINE, "engine-uiserver:add_io_cb", uiserver, +              "fd %d, dir %d", iocbd->fd, iocbd->dir); +  err = (*uiserver->io_cbs.add) (uiserver->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_uiserver_t uiserver, 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 (uiserver->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.  */ + +  uiserver->status_cb.fd = _gpgme_io_dup (fdlist[0]); +  if (uiserver->status_cb.fd < 0) +    return gpg_error_from_syserror (); + +  if (_gpgme_io_set_close_notify (uiserver->status_cb.fd, +				  close_notify_handler, uiserver)) +    { +      _gpgme_io_close (uiserver->status_cb.fd); +      uiserver->status_cb.fd = -1; +      return gpg_error (GPG_ERR_GENERAL); +    } + +  err = add_io_cb (uiserver, &uiserver->status_cb, status_handler); +  if (!err && uiserver->input_cb.fd != -1) +    err = add_io_cb (uiserver, &uiserver->input_cb, _gpgme_data_outbound_handler); +  if (!err && uiserver->output_cb.fd != -1) +    err = add_io_cb (uiserver, &uiserver->output_cb, _gpgme_data_inbound_handler); +  if (!err && uiserver->message_cb.fd != -1) +    err = add_io_cb (uiserver, &uiserver->message_cb, _gpgme_data_outbound_handler); + +  if (!err) +    err = assuan_write_line (uiserver->assuan_ctx, command); + +  if (!err) +    uiserver_io_event (uiserver, GPGME_EVENT_START, NULL); + +  return err; +} + + +static gpgme_error_t +uiserver_reset (void *engine) +{ +  engine_uiserver_t uiserver = engine; + +  /* We must send a reset because we need to reset the list of +     signers.  Note that RESET does not reset OPTION commands. */ +  return uiserver_assuan_simple_command (uiserver->assuan_ctx, "RESET", NULL, NULL); +} + + +static gpgme_error_t +_uiserver_decrypt (void *engine, int verify, +		   gpgme_data_t ciph, gpgme_data_t plain) +{ +  engine_uiserver_t uiserver = engine; +  gpgme_error_t err; +  const char *protocol; +  char *cmd; + +  if (!uiserver) +    return gpg_error (GPG_ERR_INV_VALUE); +  if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT) +    protocol = ""; +  else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP) +    protocol = " --protocol=OpenPGP"; +  else if (uiserver->protocol == GPGME_PROTOCOL_CMS) +    protocol = " --protocol=CMS"; +  else +    return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL); + +  if (asprintf (&cmd, "DECRYPT%s%s", protocol, +		verify ? "" : " --no-verify") < 0) +    return gpg_error_from_errno (errno); + +  uiserver->input_cb.data = ciph; +  err = uiserver_set_fd (uiserver, INPUT_FD, +			 map_data_enc (uiserver->input_cb.data)); +  if (err) +    { +      free (cmd); +      return gpg_error (GPG_ERR_GENERAL);	/* FIXME */ +    } +  uiserver->output_cb.data = plain; +  err = uiserver_set_fd (uiserver, OUTPUT_FD, 0); +  if (err) +    { +      free (cmd); +      return gpg_error (GPG_ERR_GENERAL);	/* FIXME */ +    } +  uiserver->inline_data = NULL; + +  err = start (engine, cmd); +  free (cmd); +  return err; +} + + +static gpgme_error_t +uiserver_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain) +{ +  return _uiserver_decrypt (engine, 0, ciph, plain); +} + + +static gpgme_error_t +uiserver_decrypt_verify (void *engine, gpgme_data_t ciph, gpgme_data_t plain) +{ +  return _uiserver_decrypt (engine, 1, ciph, plain); +} + + +static gpgme_error_t +set_recipients (engine_uiserver_t uiserver, gpgme_key_t recp[]) +{ +  gpgme_error_t err = 0; +  assuan_context_t ctx = uiserver->assuan_ctx; +  char *line; +  int linelen; +  int invalid_recipients = 0; +  int i = 0; + +  linelen = 10 + 40 + 1;	/* "RECIPIENT " + guess + '\0'.  */ +  line = malloc (10 + 40 + 1); +  if (!line) +    return gpg_error_from_errno (errno); +  strcpy (line, "RECIPIENT "); +  while (!err && recp[i]) +    { +      char *fpr; +      int newlen; + +      if (!recp[i]->subkeys || !recp[i]->subkeys->fpr) +	{ +	  invalid_recipients++; +	  continue; +	} +      fpr = recp[i]->subkeys->fpr; + +      newlen = 11 + strlen (fpr); +      if (linelen < newlen) +	{ +	  char *newline = realloc (line, newlen); +	  if (! newline) +	    { +	      int saved_errno = errno; +	      free (line); +	      return gpg_error_from_errno (saved_errno); +	    } +	  line = newline; +	  linelen = newlen; +	} +      strcpy (&line[10], fpr); + +      err = uiserver_assuan_simple_command (ctx, line, uiserver->status.fnc, +					 uiserver->status.fnc_value); +      /* FIXME: This requires more work.  */ +      if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY) +	invalid_recipients++; +      else if (err) +	{ +	  free (line); +	  return err; +	} +      i++; +    } +  free (line); +  return gpg_error (invalid_recipients +		    ? GPG_ERR_UNUSABLE_PUBKEY : GPG_ERR_NO_ERROR); +} + + +static gpgme_error_t +uiserver_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, +		  gpgme_data_t plain, gpgme_data_t ciph, int use_armor) +{ +  engine_uiserver_t uiserver = engine; +  gpgme_error_t err; +  const char *protocol; +  char *cmd; + +  if (!uiserver) +    return gpg_error (GPG_ERR_INV_VALUE); +  if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT) +    protocol = ""; +  else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP) +    protocol = " --protocol=OpenPGP"; +  else if (uiserver->protocol == GPGME_PROTOCOL_CMS) +    protocol = " --protocol=CMS"; +  else +    return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL); + +  if (flags & GPGME_ENCRYPT_PREPARE) +    { +      if (!recp || plain || ciph) +	return gpg_error (GPG_ERR_INV_VALUE); + +      if (asprintf (&cmd, "PREP_ENCRYPT%s%s", protocol, +		    (flags & GPGME_ENCRYPT_EXPECT_SIGN) +		    ? " --expect-sign" : "") < 0) +	return gpg_error_from_errno (errno); +    } +  else +    { +      if (!plain || !ciph) +	return gpg_error (GPG_ERR_INV_VALUE); + +      if (asprintf (&cmd, "ENCRYPT%s", protocol) < 0) +	return gpg_error_from_errno (errno); +    } + +  if (plain) +    { +      uiserver->input_cb.data = plain; +      err = uiserver_set_fd (uiserver, INPUT_FD, +			     map_data_enc (uiserver->input_cb.data)); +      if (err) +	{ +	  free (cmd); +	  return err; +	} +    } +     +  if (ciph) +    { +      uiserver->output_cb.data = ciph; +      err = uiserver_set_fd (uiserver, OUTPUT_FD, use_armor ? "--armor" +			     : map_data_enc (uiserver->output_cb.data)); +      if (err) +	{ +	  free (cmd); +	  return err; +	} +    } + +  uiserver->inline_data = NULL; + +  if (recp) +    { +      err = set_recipients (uiserver, recp); +      if (err) +	{ +	  free (cmd); +	  return err; +	} +    } + +  err = start (uiserver, cmd); +  free (cmd); +  return err; +} + + +static gpgme_error_t +uiserver_sign (void *engine, gpgme_data_t in, gpgme_data_t out, +	       gpgme_sig_mode_t mode, int use_armor, int use_textmode, +	       int include_certs, gpgme_ctx_t ctx /* FIXME */) +{ +  engine_uiserver_t uiserver = engine; +  gpgme_error_t err = 0; +  const char *protocol; +  char *cmd; + +  if (!uiserver || !in || !out) +    return gpg_error (GPG_ERR_INV_VALUE); +  if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT) +    protocol = ""; +  else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP) +    protocol = " --protocol=OpenPGP"; +  else if (uiserver->protocol == GPGME_PROTOCOL_CMS) +    protocol = " --protocol=CMS"; +  else +    return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL); + +  if (asprintf (&cmd, "SIGN%s%s", protocol, +		(mode == GPGME_SIG_MODE_DETACH) ? " --detached" : "") < 0) +    return gpg_error_from_errno (errno); + +  { +    gpgme_key_t key = gpgme_signers_enum (ctx, 0); +    const char *s = NULL; + +    if (key && key->uids) +      s = key->uids->email; +     +    if (s && strlen (s) < 80) +      { +	char buf[100]; +	 +	strcpy (stpcpy (buf, "SENDER --info "), s); +	err = uiserver_assuan_simple_command (uiserver->assuan_ctx, buf, +					      uiserver->status.fnc, +					      uiserver->status.fnc_value); +      } +    else +      err = gpg_error (GPG_ERR_INV_VALUE); +    gpgme_key_unref (key); +    if (err)  +      { +	free (cmd); +	return err; +      } +  } + +  uiserver->input_cb.data = in; +  err = uiserver_set_fd (uiserver, INPUT_FD, +			 map_data_enc (uiserver->input_cb.data)); +  if (err) +    { +      free (cmd); +      return err; +    } +  uiserver->output_cb.data = out; +  err = uiserver_set_fd (uiserver, OUTPUT_FD, use_armor ? "--armor" +			 : map_data_enc (uiserver->output_cb.data)); +  if (err) +    { +      free (cmd); +      return err; +    } +  uiserver->inline_data = NULL; + +  err = start (uiserver, cmd); +  free (cmd); +  return err; +} + + +/* FIXME: Missing a way to specify --silent.  */ +static gpgme_error_t +uiserver_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text, +	      gpgme_data_t plaintext) +{ +  engine_uiserver_t uiserver = engine; +  gpgme_error_t err; +  const char *protocol; +  char *cmd; + +  if (!uiserver) +    return gpg_error (GPG_ERR_INV_VALUE); +  if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT) +    protocol = ""; +  else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP) +    protocol = " --protocol=OpenPGP"; +  else if (uiserver->protocol == GPGME_PROTOCOL_CMS) +    protocol = " --protocol=CMS"; +  else +    return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL); + +  if (asprintf (&cmd, "VERIFY%s", protocol) < 0) +    return gpg_error_from_errno (errno); + +  uiserver->input_cb.data = sig; +  err = uiserver_set_fd (uiserver, INPUT_FD, +			 map_data_enc (uiserver->input_cb.data)); +  if (err) +    { +      free (cmd); +      return err; +    } +  if (plaintext) +    { +      /* Normal or cleartext signature.  */ +      uiserver->output_cb.data = plaintext; +      err = uiserver_set_fd (uiserver, OUTPUT_FD, 0); +    } +  else +    { +      /* Detached signature.  */ +      uiserver->message_cb.data = signed_text; +      err = uiserver_set_fd (uiserver, MESSAGE_FD, 0); +    } +  uiserver->inline_data = NULL; + +  if (!err) +    err = start (uiserver, cmd); + +  free (cmd); +  return err; +} + + +static void +uiserver_set_status_handler (void *engine, engine_status_handler_t fnc, +			  void *fnc_value)  +{ +  engine_uiserver_t uiserver = engine; + +  uiserver->status.fnc = fnc; +  uiserver->status.fnc_value = fnc_value; +} + + +static gpgme_error_t +uiserver_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc, +			      void *fnc_value)  +{ +  engine_uiserver_t uiserver = engine; + +  uiserver->colon.fnc = fnc; +  uiserver->colon.fnc_value = fnc_value; +  uiserver->colon.any = 0; +  return 0; +} + + +static void +uiserver_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs) +{ +  engine_uiserver_t uiserver = engine; +  uiserver->io_cbs = *io_cbs; +} + + +static void +uiserver_io_event (void *engine, gpgme_event_io_t type, void *type_data) +{ +  engine_uiserver_t uiserver = engine; + +  TRACE3 (DEBUG_ENGINE, "gpgme:uiserver_io_event", uiserver, +          "event %p, type %d, type_data %p", +          uiserver->io_cbs.event, type, type_data); +  if (uiserver->io_cbs.event) +    (*uiserver->io_cbs.event) (uiserver->io_cbs.event_priv, type, type_data); +} + + +struct engine_ops _gpgme_engine_ops_uiserver = +  { +    /* Static functions.  */ +    _gpgme_get_uiserver_socket_path, +    NULL, +    uiserver_get_version, +    uiserver_get_req_version, +    uiserver_new, + +    /* Member functions.  */ +    uiserver_release, +    uiserver_reset, +    uiserver_set_status_handler, +    NULL,		/* set_command_handler */ +    uiserver_set_colon_line_handler, +    uiserver_set_locale, +    uiserver_set_protocol, +    uiserver_decrypt, +    uiserver_decrypt_verify, +    NULL,		/* delete */ +    NULL,		/* edit */ +    uiserver_encrypt, +    NULL,		/* encrypt_sign */ +    NULL,		/* export */ +    NULL,		/* export_ext */ +    NULL,		/* genkey */ +    NULL,		/* import */ +    NULL,		/* keylist */ +    NULL,		/* keylist_ext */ +    uiserver_sign, +    NULL,		/* trustlist */ +    uiserver_verify, +    NULL,		/* getauditlog */ +    NULL,               /* opassuan_transact */ +    NULL,		/* conf_load */ +    NULL,		/* conf_save */ +    uiserver_set_io_cbs, +    uiserver_io_event, +    uiserver_cancel, +    NULL		/* cancel_op */ +  }; diff --git a/src/engine.c b/src/engine.c index 69128592..ecf23047 100644 --- a/src/engine.c +++ b/src/engine.c @@ -61,7 +61,12 @@ static struct engine_ops *engine_ops[] =      NULL,  #endif  #ifdef ENABLE_G13 -    &_gpgme_engine_ops_g13		/* Crypto VFS.  */ +    &_gpgme_engine_ops_g13,		/* Crypto VFS.  */ +#else +    NULL, +#endif +#ifdef ENABLE_UISERVER +    &_gpgme_engine_ops_uiserver		/* Crypto VFS.  */  #else      NULL  #endif @@ -200,7 +205,8 @@ gpgme_get_engine_info (gpgme_engine_info_t *info)  					GPGME_PROTOCOL_CMS,  					GPGME_PROTOCOL_GPGCONF,  					GPGME_PROTOCOL_ASSUAN, -					GPGME_PROTOCOL_G13 }; +					GPGME_PROTOCOL_G13, +					GPGME_PROTOCOL_UISERVER };        unsigned int proto;        for (proto = 0; proto < DIM (proto_list); proto++) @@ -550,6 +556,20 @@ _gpgme_engine_set_locale (engine_t engine, int category,    return (*engine->ops->set_locale) (engine->engine, category, value);  } + +gpgme_error_t +_gpgme_engine_set_protocol (engine_t engine, gpgme_protocol_t protocol) +{ +  if (!engine) +    return gpg_error (GPG_ERR_INV_VALUE); + +  if (!engine->ops->set_protocol) +    return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + +  return (*engine->ops->set_protocol) (engine->engine, protocol); +} +   +  gpgme_error_t  _gpgme_engine_op_decrypt (engine_t engine, gpgme_data_t ciph,  			  gpgme_data_t plain) @@ -563,6 +583,21 @@ _gpgme_engine_op_decrypt (engine_t engine, gpgme_data_t ciph,    return (*engine->ops->decrypt) (engine->engine, ciph, plain);  } + +gpgme_error_t +_gpgme_engine_op_decrypt_verify (engine_t engine, gpgme_data_t ciph, +				 gpgme_data_t plain) +{ +  if (!engine) +    return gpg_error (GPG_ERR_INV_VALUE); + +  if (!engine->ops->decrypt_verify) +    return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + +  return (*engine->ops->decrypt_verify) (engine->engine, ciph, plain); +} + +  gpgme_error_t  _gpgme_engine_op_delete (engine_t engine, gpgme_key_t key,  			 int allow_secret) diff --git a/src/engine.h b/src/engine.h index 86a3d422..d9119391 100644 --- a/src/engine.h +++ b/src/engine.h @@ -59,7 +59,8 @@ gpgme_error_t _gpgme_engine_reset (engine_t engine);  gpgme_error_t _gpgme_engine_set_locale (engine_t engine, int category,  					const char *value); - +gpgme_error_t _gpgme_engine_set_protocol (engine_t engine, +					  gpgme_protocol_t protocol);  void _gpgme_engine_release (engine_t engine);  void _gpgme_engine_set_status_handler (engine_t engine,  				       engine_status_handler_t fnc, @@ -72,9 +73,11 @@ gpgme_error_t  _gpgme_engine_set_colon_line_handler (engine_t engine,  				      engine_colon_line_handler_t fnc,  				      void *fnc_value); -gpgme_error_t _gpgme_engine_op_decrypt (engine_t engine, -					gpgme_data_t ciph, +gpgme_error_t _gpgme_engine_op_decrypt (engine_t engine, gpgme_data_t ciph,  					gpgme_data_t plain); +gpgme_error_t _gpgme_engine_op_decrypt_verify (engine_t engine, +					       gpgme_data_t ciph, +					       gpgme_data_t plain);  gpgme_error_t _gpgme_engine_op_delete (engine_t engine, gpgme_key_t key,  				       int allow_secret);  gpgme_error_t _gpgme_engine_op_edit (engine_t engine, int type, diff --git a/src/gpgme-tool.c b/src/gpgme-tool.c index d2745301..0283ffd2 100644 --- a/src/gpgme-tool.c +++ b/src/gpgme-tool.c @@ -669,7 +669,10 @@ gt_recipients_add (gpgme_tool_t gt, const char *pattern)    if (gt->recipients_nr >= MAX_RECIPIENTS)      return gpg_error_from_errno (ENOMEM); -  err = gt_get_key (gt, pattern, &key); +  if (gpgme_get_protocol (gt->ctx) == GPGME_PROTOCOL_UISERVER) +    err = gpgme_key_from_uid (&key, pattern); +  else +    err = gt_get_key (gt, pattern, &key);    if (err)      return err; @@ -780,6 +783,10 @@ gt_protocol_from_name (const char *name)      return GPGME_PROTOCOL_ASSUAN;    if (! strcasecmp (name, gpgme_get_protocol_name (GPGME_PROTOCOL_G13)))      return GPGME_PROTOCOL_G13; +  if (! strcasecmp (name, gpgme_get_protocol_name (GPGME_PROTOCOL_UISERVER))) +    return GPGME_PROTOCOL_UISERVER; +  if (! strcasecmp (name, gpgme_get_protocol_name (GPGME_PROTOCOL_DEFAULT))) +    return GPGME_PROTOCOL_DEFAULT;    return GPGME_PROTOCOL_UNKNOWN;  } @@ -792,6 +799,13 @@ gt_set_protocol (gpgme_tool_t gt, gpgme_protocol_t proto)  gpg_error_t +gt_set_sub_protocol (gpgme_tool_t gt, gpgme_protocol_t proto) +{ +  return gpgme_set_sub_protocol (gt->ctx, proto); +} + + +gpg_error_t  gt_get_protocol (gpgme_tool_t gt)  {    gpgme_protocol_t proto = gpgme_get_protocol (gt->ctx); @@ -1249,6 +1263,17 @@ cmd_protocol (assuan_context_t ctx, char *line)  static gpg_error_t +cmd_sub_protocol (assuan_context_t ctx, char *line) +{ +  struct server *server = assuan_get_pointer (ctx); +  if (line && *line) +    return gt_set_sub_protocol (server->gt, gt_protocol_from_name (line)); +  /* FIXME.  */ +  return 0; +} + + +static gpg_error_t  cmd_armor (assuan_context_t ctx, char *line)  {    struct server *server = assuan_get_pointer (ctx); @@ -1456,30 +1481,35 @@ _cmd_sign_encrypt (assuan_context_t ctx, char *line, int sign)    gpg_error_t err;    assuan_fd_t inp_fd;    assuan_fd_t out_fd; -  gpgme_data_t inp_data; -  gpgme_data_t out_data; +  gpgme_data_t inp_data = NULL; +  gpgme_data_t out_data = NULL;    gpgme_encrypt_flags_t flags = 0;    if (strstr (line, "--always-trust"))      flags |= GPGME_ENCRYPT_ALWAYS_TRUST;    if (strstr (line, "--no-encrypt-to"))      flags |= GPGME_ENCRYPT_NO_ENCRYPT_TO; +  if (strstr (line, "--prepare")) +    flags |= GPGME_ENCRYPT_PREPARE; +  if (strstr (line, "--expect-sign")) +    flags |= GPGME_ENCRYPT_EXPECT_SIGN;    inp_fd = assuan_get_input_fd (ctx); -  if (inp_fd == ASSUAN_INVALID_FD) -    return GPG_ERR_ASS_NO_INPUT;    out_fd = assuan_get_output_fd (ctx); -  if (out_fd == ASSUAN_INVALID_FD) -    return GPG_ERR_ASS_NO_OUTPUT; -   -  err = server_data_obj (inp_fd, server->input_enc, &inp_data); -  if (err) -    return err; -  err = server_data_obj (out_fd, server->output_enc, &out_data); -  if (err) +  if (inp_fd != ASSUAN_INVALID_FD)      { -      gpgme_data_release (inp_data); -      return err; +      err = server_data_obj (inp_fd, server->input_enc, &inp_data); +      if (err) +	return err; +    } +  if (out_fd != ASSUAN_INVALID_FD) +    { +      err = server_data_obj (out_fd, server->output_enc, &out_data); +      if (err) +	{ +	  gpgme_data_release (inp_data); +	  return err; +	}      }    err = gt_sign_encrypt (server->gt, flags, inp_data, out_data, sign);  @@ -1957,6 +1987,7 @@ register_commands (assuan_context_t ctx)      // TODO: Set engine info.      { "ENGINE", cmd_engine },      { "PROTOCOL", cmd_protocol, hlp_protocol }, +    { "SUB_PROTOCOL", cmd_sub_protocol },      { "ARMOR", cmd_armor },      { "TEXTMODE", cmd_textmode },      { "INCLUDE_CERTS", cmd_include_certs }, diff --git a/src/gpgme.c b/src/gpgme.c index 70f93f1a..b6c79a0d 100644 --- a/src/gpgme.c +++ b/src/gpgme.c @@ -265,13 +265,14 @@ gpgme_set_protocol (gpgme_ctx_t ctx, gpgme_protocol_t protocol)  {    TRACE_BEG2 (DEBUG_CTX, "gpgme_set_protocol", ctx, "protocol=%i (%s)",  	      protocol, gpgme_get_protocol_name (protocol) -	      ? gpgme_get_protocol_name (protocol) : "unknown"); +	      ? gpgme_get_protocol_name (protocol) : "invalid");    if (protocol != GPGME_PROTOCOL_OpenPGP        && protocol != GPGME_PROTOCOL_CMS        && protocol != GPGME_PROTOCOL_GPGCONF        && protocol != GPGME_PROTOCOL_ASSUAN -      && protocol != GPGME_PROTOCOL_G13) +      && protocol != GPGME_PROTOCOL_G13 +      && protocol != GPGME_PROTOCOL_UISERVER)      return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));    if (ctx->protocol != protocol) @@ -296,11 +297,24 @@ gpgme_get_protocol (gpgme_ctx_t ctx)    TRACE2 (DEBUG_CTX, "gpgme_get_protocol", ctx,  	  "ctx->protocol=%i (%s)", ctx->protocol,  	  gpgme_get_protocol_name (ctx->protocol) -	  ? gpgme_get_protocol_name (ctx->protocol) : "unknown"); +	  ? gpgme_get_protocol_name (ctx->protocol) : "invalid");    return ctx->protocol;  } +gpgme_error_t +gpgme_set_sub_protocol (gpgme_ctx_t ctx, gpgme_protocol_t protocol) +{ +  gpgme_error_t err; +  TRACE_BEG2 (DEBUG_CTX, "gpgme_set_sub_protocol", ctx, "protocol=%i (%s)", +	      protocol, gpgme_get_protocol_name (protocol) +	      ? gpgme_get_protocol_name (protocol) : "invalid"); + +  err = _gpgme_engine_set_protocol (ctx->engine, protocol); +  return TRACE_ERR (err); +} + +  const char *  gpgme_get_protocol_name (gpgme_protocol_t protocol)  { @@ -321,6 +335,12 @@ gpgme_get_protocol_name (gpgme_protocol_t protocol)      case GPGME_PROTOCOL_G13:        return "G13"; +    case GPGME_PROTOCOL_UISERVER: +      return "UIServer"; + +    case GPGME_PROTOCOL_DEFAULT: +      return "default"; +      case GPGME_PROTOCOL_UNKNOWN:        return "unknown"; diff --git a/src/gpgme.def b/src/gpgme.def index 816da318..5a1b2c15 100644 --- a/src/gpgme.def +++ b/src/gpgme.def @@ -192,5 +192,8 @@ EXPORTS      gpgme_op_vfs_mount			  @147      gpgme_op_vfs_create			  @148 +    gpgme_key_from_uid                    @149 +    gpgme_set_sub_protocol                @150 +  ; END diff --git a/src/gpgme.h.in b/src/gpgme.h.in index ea405bf0..2a3acd47 100644 --- a/src/gpgme.h.in +++ b/src/gpgme.h.in @@ -326,6 +326,7 @@ typedef enum      GPGME_PROTOCOL_ASSUAN  = 3,  /* Low-level access to an Assuan server.  */      GPGME_PROTOCOL_G13     = 4,      GPGME_PROTOCOL_UISERVER= 5,   +    GPGME_PROTOCOL_DEFAULT = 254,      GPGME_PROTOCOL_UNKNOWN = 255    }  gpgme_protocol_t; @@ -803,6 +804,15 @@ gpgme_error_t gpgme_set_protocol (gpgme_ctx_t ctx, gpgme_protocol_t proto);  /* Get the protocol used with CTX */  gpgme_protocol_t gpgme_get_protocol (gpgme_ctx_t ctx); +/* Set the crypto protocol to be used by CTX to PROTO. +   gpgme_set_protocol actually sets the backend engine.  This sets the +   crypto protocol used in engines that support more than one crypto +   prococol (for example, an UISERVER can support OpenPGP and CMS). +   This is reset to the default with gpgme_set_protocol.  */ +gpgme_error_t gpgme_set_sub_protocol (gpgme_ctx_t ctx, +					 gpgme_protocol_t proto); + +  /* Get the string describing protocol PROTO, or NULL if invalid.  */  const char *gpgme_get_protocol_name (gpgme_protocol_t proto); @@ -1209,7 +1219,9 @@ gpgme_encrypt_result_t gpgme_op_encrypt_result (gpgme_ctx_t ctx);  typedef enum    {      GPGME_ENCRYPT_ALWAYS_TRUST = 1, -    GPGME_ENCRYPT_NO_ENCRYPT_TO = 2 +    GPGME_ENCRYPT_NO_ENCRYPT_TO = 2, +    GPGME_ENCRYPT_PREPARE = 4, +    GPGME_ENCRYPT_EXPECT_SIGN = 8    }  gpgme_encrypt_flags_t; @@ -1982,6 +1994,13 @@ gpgme_error_t gpgme_op_conf_load (gpgme_ctx_t ctx, gpgme_conf_comp_t *conf_p);  gpgme_error_t gpgme_op_conf_save (gpgme_ctx_t ctx, gpgme_conf_comp_t comp); +/* UIServer support.  */ + +/* Create a dummy key to specify an email address.  */ +gpgme_error_t gpgme_key_from_uid (gpgme_key_t *key, const char *name); + + +  /* Various functions.  */  /* Check that the library fulfills the version requirement.  Note: @@ -202,7 +202,7 @@ parse_x509_user_id (char *src, char **name, char **email,  /* Take a name from the --with-colon listing, remove certain escape     sequences sequences and put it into the list of UIDs.  */  gpgme_error_t -_gpgme_key_append_name (gpgme_key_t key, char *src) +_gpgme_key_append_name (gpgme_key_t key, char *src, int convert)  {    gpgme_user_id_t uid;    char *dst; @@ -219,7 +219,10 @@ _gpgme_key_append_name (gpgme_key_t key, char *src)    uid->uid = ((char *) uid) + sizeof (*uid);    dst = uid->uid; -  _gpgme_decode_c_string (src, &dst, src_len + 1); +  if (convert) +    _gpgme_decode_c_string (src, &dst, src_len + 1); +  else +    memcpy (dst, src, src_len + 1);    dst += strlen (dst) + 1;    if (key->protocol == GPGME_PROTOCOL_CMS) @@ -371,6 +374,32 @@ gpgme_key_unref (gpgme_key_t key)  } +/* Support functions.  */ + +/* Create a dummy key to specify an email address.  */ +gpgme_error_t +gpgme_key_from_uid (gpgme_key_t *r_key, const char *name) +{ +  gpgme_error_t err; +  gpgme_key_t key; + +  *r_key = NULL; +  err = _gpgme_key_new (&key); +  if (err) +    return err; + +  /* Note: protocol doesn't matter if only email is provided.  */ +  err = _gpgme_key_append_name (key, name, 0); +  if (err) +    gpgme_key_unref (key); +  else +    *r_key = key; + +  return err; +} + + +  /* Compatibility interfaces.  */  void diff --git a/src/keylist.c b/src/keylist.c index 424a8654..f76904b0 100644 --- a/src/keylist.c +++ b/src/keylist.c @@ -650,7 +650,7 @@ keylist_colon_handler (void *priv, char *line)        /* Field 2 has the trust info, and field 10 has the user ID.  */        if (fields >= 10)  	{ -	  if (_gpgme_key_append_name (key, field[9])) +	  if (_gpgme_key_append_name (key, field[9], 1))  	    return gpg_error_from_errno (GPG_ERR_ENOMEM);	/* FIXME */  	  else  	    { diff --git a/src/libgpgme.vers b/src/libgpgme.vers index 24107d48..449197cb 100644 --- a/src/libgpgme.vers +++ b/src/libgpgme.vers @@ -73,6 +73,8 @@ GPGME_1.1 {      gpgme_op_vfs_mount;      gpgme_op_vfs_create; +    gpgme_key_from_uid; +    gpgme_set_sub_protocol;  }; @@ -128,7 +128,7 @@ gpgme_error_t _gpgme_progress_status_handler (void *priv,  gpgme_error_t _gpgme_key_new (gpgme_key_t *r_key);  gpgme_error_t _gpgme_key_add_subkey (gpgme_key_t key,  				     gpgme_subkey_t *r_subkey); -gpgme_error_t _gpgme_key_append_name (gpgme_key_t key, char *src); +gpgme_error_t _gpgme_key_append_name (gpgme_key_t key, char *src, int convert);  gpgme_key_sig_t _gpgme_key_add_sig (gpgme_key_t key, char *src); diff --git a/src/posix-io.c b/src/posix-io.c index b173f58e..6c33d9fc 100644 --- a/src/posix-io.c +++ b/src/posix-io.c @@ -305,7 +305,9 @@ _gpgme_io_waitpid (int pid, int hang, int *r_status, int *r_signal)  /* Returns 0 on success, -1 on error.  */  int  _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags, -		 struct spawn_fd_item_s *fd_list, pid_t *r_pid) +		 struct spawn_fd_item_s *fd_list, +		 void (*atfork) (void *opaque, int reserved), +		 void *atforkvalue, pid_t *r_pid)  {    pid_t pid;    int i; @@ -344,6 +346,9 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,  	  int seen_stdin = 0;  	  int seen_stderr = 0; +	  if (atfork) +	    atfork (atforkvalue, 0); +  	  /* First close all fds which will not be inherited.  */  	  for (fd = 0; fd < max_fds; fd++)  	    { diff --git a/src/posix-util.c b/src/posix-util.c index 86ef5e74..3acc7e08 100644 --- a/src/posix-util.c +++ b/src/posix-util.c @@ -69,6 +69,30 @@ _gpgme_get_g13_path (void)  #endif  } + +const char * +_gpgme_get_uiserver_socket_path (void) +{ +  static char *socket_path; +  char *homedir; +  const char name[] = "S.uiserver"; + +  if (socket_path) +    return socket_path; + +  homedir = _gpgme_get_default_homedir (); +  if (! homedir) +    return NULL; + +  socket_path = malloc (strlen (homedir) + 1 + strlen (name) + 1); +  if (! socket_path) +    return NULL; + +  strcpy (stpcpy (stpcpy (socket_path, homedir), "/"), name); +  return socket_path; +} + +  /* See w32-util.c */  int  _gpgme_get_conf_int (const char *key, int *value) diff --git a/src/priv-io.h b/src/priv-io.h index a9fb02ae..3ed2260a 100644 --- a/src/priv-io.h +++ b/src/priv-io.h @@ -76,7 +76,9 @@ int _gpgme_io_set_nonblocking (int fd);     optionally dup() the child fds.  Finally, all fds in the list are     closed in the parent.  */  int _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags, -		     struct spawn_fd_item_s *fd_list, pid_t *r_pid); +		     struct spawn_fd_item_s *fd_list, +		     void (*atfork) (void *opaque, int reserved), +		     void *atforkvalue, pid_t *r_pid);  int _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock); @@ -33,6 +33,8 @@ 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); +const char *_gpgme_get_uiserver_socket_path (void); +  int _gpgme_get_conf_int (const char *key, int *value);  void _gpgme_allow_set_foreground_window (pid_t pid); diff --git a/src/version.c b/src/version.c index 415279cf..8ba71ac3 100644 --- a/src/version.c +++ b/src/version.c @@ -309,7 +309,7 @@ _gpgme_get_program_version (const char *const file_name)    cfd[0].fd = rp[1]; -  status = _gpgme_io_spawn (file_name, argv, 0, cfd, NULL); +  status = _gpgme_io_spawn (file_name, argv, 0, cfd, NULL, NULL, NULL);    if (status < 0)      {        _gpgme_io_close (rp[0]); diff --git a/src/w32-glib-io.c b/src/w32-glib-io.c index 17d60fc0..a69f73ab 100644 --- a/src/w32-glib-io.c +++ b/src/w32-glib-io.c @@ -585,7 +585,9 @@ build_commandline (char **argv)  int  _gpgme_io_spawn (const char *path, char * const argv[], unsigned int flags, -		 struct spawn_fd_item_s *fd_list, pid_t *r_pid) +		 struct spawn_fd_item_s *fd_list, +		 void (*atfork) (void *opaque, int reserved), +		 void *atforkvalue, pid_t *r_pid)  {    SECURITY_ATTRIBUTES sec_attr;    PROCESS_INFORMATION pi = diff --git a/src/w32-io.c b/src/w32-io.c index e83aa0b9..d1c48084 100644 --- a/src/w32-io.c +++ b/src/w32-io.c @@ -1042,7 +1042,9 @@ build_commandline (char **argv)  int  _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags, -		 struct spawn_fd_item_s *fd_list, pid_t *r_pid) +		 struct spawn_fd_item_s *fd_list, +		 void (*atfork) (void *opaque, int reserved), +		 void *atforkvalue, pid_t *r_pid)  {    SECURITY_ATTRIBUTES sec_attr;    PROCESS_INFORMATION pi = diff --git a/src/w32-qt-io.cpp b/src/w32-qt-io.cpp index 03e9ea83..4eb9beb1 100644 --- a/src/w32-qt-io.cpp +++ b/src/w32-qt-io.cpp @@ -398,7 +398,9 @@ build_commandline (char **argv)  int  _gpgme_io_spawn (const char *path, char * const argv[], unsigned int flags, -		 struct spawn_fd_item_s *fd_list, pid_t *r_pid) +		 struct spawn_fd_item_s *fd_list, +		 void (*atfork) (void *opaque, int reserved), +		 void *atforkvalue, pid_t *r_pid)  {    SECURITY_ATTRIBUTES sec_attr;    PROCESS_INFORMATION pi = diff --git a/src/w32-util.c b/src/w32-util.c index d7fc06f3..bc186022 100644 --- a/src/w32-util.c +++ b/src/w32-util.c @@ -381,6 +381,29 @@ _gpgme_get_g13_path (void)  const char * +_gpgme_get_uiserver_socket_path (void) +{ +  static char *socket_path; +  char *homedir; +  const char name[] = "S.uiserver"; + +  if (socket_path) +    return socket_path; + +  homedir = _gpgme_get_default_homedir (); +  if (! homedir) +    return NULL; + +  socket_path = malloc (strlen (homedir) + 1 + strlen (name) + 1); +  if (! socket_path) +    return NULL; + +  strcpy (stpcpy (stpcpy (socket_path, homedir), "\\"), name); +  return socket_path; +} + + +const char *  _gpgme_get_w32spawn_path (void)  {    static char *w32spawn_program; | 
