gpgme/src/engine-uiserver.c

1366 lines
34 KiB
C
Raw Normal View History

2009-11-10 Marcus Brinkmann <marcus@g10code.de> * configure.ac: Activate UIServer if FD passing is enabled and Assuan is available. m4/ 2009-11-10 Marcus Brinkmann <marcus@g10code.de> * libassuan.m4: Fix LIBASSUAN_VERSION. src/ 2009-11-10 Marcus Brinkmann <marcus@g10code.de> * 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-10 09:07:19 +00:00
/* 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>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
2009-11-10 Marcus Brinkmann <marcus@g10code.de> * configure.ac: Activate UIServer if FD passing is enabled and Assuan is available. m4/ 2009-11-10 Marcus Brinkmann <marcus@g10code.de> * libassuan.m4: Fix LIBASSUAN_VERSION. src/ 2009-11-10 Marcus Brinkmann <marcus@g10code.de> * 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-10 09:07:19 +00:00
#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, ASSUAN_SOCKET_SERVER_FDPASSING);
2009-11-10 Marcus Brinkmann <marcus@g10code.de> * configure.ac: Activate UIServer if FD passing is enabled and Assuan is available. m4/ 2009-11-10 Marcus Brinkmann <marcus@g10code.de> * libassuan.m4: Fix LIBASSUAN_VERSION. src/ 2009-11-10 Marcus Brinkmann <marcus@g10code.de> * 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-10 09:07:19 +00:00
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;
2009-11-10 16:32:11 +00:00
int i;
2009-11-10 Marcus Brinkmann <marcus@g10code.de> * configure.ac: Activate UIServer if FD passing is enabled and Assuan is available. m4/ 2009-11-10 Marcus Brinkmann <marcus@g10code.de> * libassuan.m4: Fix LIBASSUAN_VERSION. src/ 2009-11-10 Marcus Brinkmann <marcus@g10code.de> * 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-10 09:07:19 +00:00
linelen = 10 + 40 + 1; /* "RECIPIENT " + guess + '\0'. */
line = malloc (10 + 40 + 1);
if (!line)
return gpg_error_from_errno (errno);
strcpy (line, "RECIPIENT ");
2009-11-10 16:32:11 +00:00
for (i=0; !err && recp[i]; i++)
2009-11-10 Marcus Brinkmann <marcus@g10code.de> * configure.ac: Activate UIServer if FD passing is enabled and Assuan is available. m4/ 2009-11-10 Marcus Brinkmann <marcus@g10code.de> * libassuan.m4: Fix LIBASSUAN_VERSION. src/ 2009-11-10 Marcus Brinkmann <marcus@g10code.de> * 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-10 09:07:19 +00:00
{
2009-11-10 16:32:11 +00:00
char *uid;
2009-11-10 Marcus Brinkmann <marcus@g10code.de> * configure.ac: Activate UIServer if FD passing is enabled and Assuan is available. m4/ 2009-11-10 Marcus Brinkmann <marcus@g10code.de> * libassuan.m4: Fix LIBASSUAN_VERSION. src/ 2009-11-10 Marcus Brinkmann <marcus@g10code.de> * 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-10 09:07:19 +00:00
int newlen;
2009-11-10 16:32:11 +00:00
/* We use only the first user ID of the key. */
if (!recp[i]->uids || !(uid=recp[i]->uids->uid) || !*uid)
2009-11-10 Marcus Brinkmann <marcus@g10code.de> * configure.ac: Activate UIServer if FD passing is enabled and Assuan is available. m4/ 2009-11-10 Marcus Brinkmann <marcus@g10code.de> * libassuan.m4: Fix LIBASSUAN_VERSION. src/ 2009-11-10 Marcus Brinkmann <marcus@g10code.de> * 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-10 09:07:19 +00:00
{
invalid_recipients++;
continue;
}
2009-11-10 16:32:11 +00:00
newlen = 11 + strlen (uid);
2009-11-10 Marcus Brinkmann <marcus@g10code.de> * configure.ac: Activate UIServer if FD passing is enabled and Assuan is available. m4/ 2009-11-10 Marcus Brinkmann <marcus@g10code.de> * libassuan.m4: Fix LIBASSUAN_VERSION. src/ 2009-11-10 Marcus Brinkmann <marcus@g10code.de> * 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-10 09:07:19 +00:00
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;
}
2009-11-10 16:32:11 +00:00
/* FIXME: need to do proper escaping */
strcpy (&line[10], uid);
2009-11-10 Marcus Brinkmann <marcus@g10code.de> * configure.ac: Activate UIServer if FD passing is enabled and Assuan is available. m4/ 2009-11-10 Marcus Brinkmann <marcus@g10code.de> * libassuan.m4: Fix LIBASSUAN_VERSION. src/ 2009-11-10 Marcus Brinkmann <marcus@g10code.de> * 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-10 09:07:19 +00:00
err = uiserver_assuan_simple_command (ctx, line, uiserver->status.fnc,
2009-11-10 16:32:11 +00:00
uiserver->status.fnc_value);
/* FIXME: This might requires more work. */
2009-11-10 Marcus Brinkmann <marcus@g10code.de> * configure.ac: Activate UIServer if FD passing is enabled and Assuan is available. m4/ 2009-11-10 Marcus Brinkmann <marcus@g10code.de> * libassuan.m4: Fix LIBASSUAN_VERSION. src/ 2009-11-10 Marcus Brinkmann <marcus@g10code.de> * 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-10 09:07:19 +00:00
if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
invalid_recipients++;
else if (err)
{
free (line);
return err;
}
}
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;
2009-11-17 13:06:05 +00:00
gpgme_key_t key;
2009-11-10 Marcus Brinkmann <marcus@g10code.de> * configure.ac: Activate UIServer if FD passing is enabled and Assuan is available. m4/ 2009-11-10 Marcus Brinkmann <marcus@g10code.de> * libassuan.m4: Fix LIBASSUAN_VERSION. src/ 2009-11-10 Marcus Brinkmann <marcus@g10code.de> * 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-10 09:07:19 +00:00
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);
2009-11-17 13:06:05 +00:00
key = gpgme_signers_enum (ctx, 0);
if (key)
{
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)
2009-11-10 Marcus Brinkmann <marcus@g10code.de> * configure.ac: Activate UIServer if FD passing is enabled and Assuan is available. m4/ 2009-11-10 Marcus Brinkmann <marcus@g10code.de> * libassuan.m4: Fix LIBASSUAN_VERSION. src/ 2009-11-10 Marcus Brinkmann <marcus@g10code.de> * 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-10 09:07:19 +00:00
{
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 */
};