From 96cf17b15995c9b925f80a64dbd42c1305ca9895 Mon Sep 17 00:00:00 2001 From: Marcus Brinkmann Date: Tue, 10 Nov 2009 09:07:19 +0000 Subject: [PATCH] 2009-11-10 Marcus Brinkmann * configure.ac: Activate UIServer if FD passing is enabled and Assuan is available. m4/ 2009-11-10 Marcus Brinkmann * libassuan.m4: Fix LIBASSUAN_VERSION. src/ 2009-11-10 Marcus Brinkmann * 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. --- ChangeLog | 5 + NEWS | 2 + TODO | 5 + configure.ac | 30 +- m4/ChangeLog | 4 + m4/libassuan.m4 | 2 +- src/ChangeLog | 35 ++ src/Makefile.am | 8 + src/assuan-support.c | 28 +- src/decrypt-verify.c | 2 +- src/dirinfo.c | 2 +- src/engine-assuan.c | 2 + src/engine-backend.h | 13 +- src/engine-g13.c | 2 + src/engine-gpg.c | 4 +- src/engine-gpgconf.c | 6 +- src/engine-gpgsm.c | 7 +- src/engine-uiserver.c | 1361 +++++++++++++++++++++++++++++++++++++++++ src/engine.c | 39 +- src/engine.h | 9 +- src/gpgme-tool.c | 61 +- src/gpgme.c | 26 +- src/gpgme.def | 3 + src/gpgme.h.in | 21 +- src/key.c | 33 +- src/keylist.c | 2 +- src/libgpgme.vers | 2 + src/ops.h | 2 +- src/posix-io.c | 7 +- src/posix-util.c | 24 + src/priv-io.h | 4 +- src/util.h | 2 + src/version.c | 2 +- src/w32-glib-io.c | 4 +- src/w32-io.c | 4 +- src/w32-qt-io.cpp | 4 +- src/w32-util.c | 23 + 37 files changed, 1721 insertions(+), 69 deletions(-) create mode 100644 src/engine-uiserver.c diff --git a/ChangeLog b/ChangeLog index c9c67924..2ba2099b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2009-11-10 Marcus Brinkmann + + * configure.ac: Activate UIServer if FD passing is enabled and + Assuan is available. + 2009-10-30 Marcus Brinkmann * configure.ac: Check for argp.h and error_t. diff --git a/NEWS b/NEWS index 58e91f67..3fcc8dee 100644 --- a/NEWS +++ b/NEWS @@ -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 diff --git a/TODO b/TODO index 1d8ac90b..0458cb5f 100644 --- a/TODO +++ b/TODO @@ -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 + + * libassuan.m4: Fix LIBASSUAN_VERSION. + 2006-06-08 Marcus Brinkmann * 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 + + * 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 * 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 #include +#include #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 +#endif + +#include +#include +#include +#include +#include +#include +#include /* FIXME */ +#include + +#include "gpgme.h" +#include "util.h" +#include "ops.h" +#include "wait.h" +#include "priv-io.h" +#include "sema.h" +#include "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; } @@ -791,6 +798,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) { @@ -1248,6 +1262,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) { @@ -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; @@ -1981,6 +1993,13 @@ gpgme_error_t gpgme_op_conf_load (gpgme_ctx_t ctx, gpgme_conf_comp_t *conf_p); follow chained components! */ 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. */ diff --git a/src/key.c b/src/key.c index 468946fe..7dd38c5b 100644 --- a/src/key.c +++ b/src/key.c @@ -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) @@ -370,6 +373,32 @@ gpgme_key_unref (gpgme_key_t key) free (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. */ 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; }; diff --git a/src/ops.h b/src/ops.h index 5426bb89..46c28eef 100644 --- a/src/ops.h +++ b/src/ops.h @@ -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); diff --git a/src/util.h b/src/util.h index c24a592b..f4d23b6e 100644 --- a/src/util.h +++ b/src/util.h @@ -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 @@ -380,6 +380,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) {