2009-10-26 Marcus Brinkmann <marcus@g10code.de>

* configure.ac (NEED_GPG_VERSION_DEFAULT): Bump to 1.4.0 as 1.3.0
	was development versions only.

tests/
2009-10-26  Marcus Brinkmann  <marcus@g10code.de>

	* opassuan/t-command.c: Update to new interface.

src/
2009-10-26  Marcus Brinkmann  <marcus@g10code.de>

	* gpgme.h.in (struct gpgme_io_event_done_data)
	(gpgme_io_event_done_data_t): New types.
	(struct _gpgme_op_assuan_result): Deprecate the err member.
	(gpgme_op_assuan_result): Deprecate (for now).
	(gpgme_op_assuan_transact_ext): New prototype.
	(gpgme_op_assuan_transact): Deprecate.
	(struct _gpgme_op_g13_result): Replace with ...
	(struct _gpgme_op_vfs_mount_result): ... this.
	(gpgme_op_g13_mount): Replace with ...
	(gpgme_op_vfs_mount): ... this.
	* gpgme.def (gpgme_op_assuan_transact_ext, gpgme_wait_ext)
	(gpgme_op_vfs_mount_result, gpgme_op_vfs_mount): New.
	(gpgme_op_g13_mount): Remove.
	* libgpgme.vers: Likewise.
	* engine-backend.h (struct engine_ops): Remove RESULT_CB and
	RESULT_CB_VALUE args in opassuan_transact member.  Add CANCEL_OP
	member.
	* ops.h (_gpgme_cancel_with_err, _gpgme_wait_on_condition): Add
	OP_ERR argument.
	(_gpgme_wait_one_ext): New prototype.
	* context.h (ctx_op_data_id_t): Add OPDATA_VFS_MOUNT.
	* engine-g13.c (g13_cancel_op): New function.
	(parse_status): Remove declaration.
	(g13_assuan_simple_command): Do nothing with status lines for now.
	(status_handler): Update opaque value access.
	(_gpgme_engine_ops_g13): Add new cancel_op member.
	* gpgme.c (_gpgme_cancel_with_err): Add new parameter OP_ERR.
	Handle operational errors.
	(gpgme_cancel, gpgme_io_read, gpgme_io_write): Add debug output.
	* data.c (_gpgme_data_inbound_handler)
	(_gpgme_data_outbound_handler): Adjust opaque value access.
	* engine-gpg.c (command_handler, status_handler)
	(colon_line_handler): Likewise.
	* engine-gpgsm.c (status_handler): Likewise.
	* engine-gpg.c (_gpgme_engine_ops_gpg): Add cancel_op member.
	* engine-gpgsm.c (_gpgme_engine_ops_gpgsm): Likewise.
	* g13.c: Rewritten (and will be rewritten again).
	* engine.h (_gpgme_engine_op_assuan_transact): Remove result_cb
	and result_cb_value parameters from prototype.
	(_gpgme_engine_cancel_op): New prototype.
	* engine.c (engine_ops) [! ENABLE_ASSUAN]: Add missing comma.
	(_gpgme_engine_op_assuan_transact): Remove result_cb and
	result_cb_value parameter.
	(_gpgme_engine_cancel_op): New function.
	* wait.h (_gpgme_run_io_cb): Add new argument OP_ERR.
	(struct io_cb_data): New struct to pass opaque data and get a
	op_err return value.  Needed because we can't modify I/O callback
	handler signature because it is exposed to the user.
	* wait.c (_gpgme_run_io_cb): Add OP_ERR parameter.  Handle
	operational errors.
	* wait-user.c (_gpgme_user_io_cb_handler): Handle operational
	errors.
	* wait-private.c (_gpgme_wait_on_condition): New argument to
	retrieve the operational result.  Handle operational errors in
	session based protocols.
	(_gpgme_wait_one_ext): New function.
	(_gpgme_wait_one): Pass argument in invocation of
	_gpgme_wait_on_condition.
	* wait-global.c (struct ctx_list_item): Add member OP_ERR.
	(ctx_done): New argument OP_ERR.
	(ctx_wait): New argument OP_ERR.
	(gpgme_wait_ext): New function based on gpgme_wait but handling
	operational errors.
	(gpgme_wait): Implement in term of gpgme_wait_ext.
	* keylist.c (gpgme_op_keylist_next): Pass argument in invocation
	of _gpgme_wait_on_condition.
	* trustlist.c (gpgme_op_trustlist_next): Pass argument in
	invocation of _gpgme_wait_on_condition.
	* engine-assuan.c (struct engine_llass): Replace members RESULT_CB
	and RESULT_CB_VALUE by LAST_OP_ERR.
	(_gpgme_engine_assuan_last_op_err): Add this hack function.
	(llass_cancel_op): New function.
	(_gpgme_engine_llass_ops): Add cancel_op member.
	(llass_status_handler): Update opaque value access.
	(llass_transact): Remove RESULT_CB and RESULT_CB_VALUE arguments.
	* opassuan.c: Move compat hacks to the end of file.
	(opassuan_start): Do not set OPD->result.err.
	Do not pass RESULT_Cb and CTX to _gpgme_engine_op_assuan_transact.
	(gpgme_op_assuan_transact_ext): New function.
This commit is contained in:
Marcus Brinkmann 2009-10-26 18:52:32 +00:00
parent ca67902311
commit c8e934b276
28 changed files with 558 additions and 323 deletions

View File

@ -1,3 +1,8 @@
2009-10-26 Marcus Brinkmann <marcus@g10code.de>
* configure.ac (NEED_GPG_VERSION_DEFAULT): Bump to 1.4.0 as 1.3.0
was development versions only.
2009-10-22 Marcus Brinkmann <marcus@g10code.de> 2009-10-22 Marcus Brinkmann <marcus@g10code.de>
* configure.ac: Add support for G13. * configure.ac: Add support for G13.

View File

@ -286,7 +286,7 @@ if test "$have_libassuan" = "yes"; then
fi fi
# Checks for system services # Checks for system services
NEED_GPG_VERSION_DEFAULT=1.3.0 NEED_GPG_VERSION_DEFAULT=1.4.0
NEED_GPGSM_VERSION_DEFAULT=1.9.6 NEED_GPGSM_VERSION_DEFAULT=1.9.6
NEED_GPGCONF_VERSION_DEFAULT=2.0.4 NEED_GPGCONF_VERSION_DEFAULT=2.0.4
NEED_G13_VERSION_DEFAULT=2.1.0 NEED_G13_VERSION_DEFAULT=2.1.0

View File

@ -1,5 +1,85 @@
2009-10-26 Marcus Brinkmann <marcus@g10code.de> 2009-10-26 Marcus Brinkmann <marcus@g10code.de>
* gpgme.h.in (struct gpgme_io_event_done_data)
(gpgme_io_event_done_data_t): New types.
(struct _gpgme_op_assuan_result): Deprecate the err member.
(gpgme_op_assuan_result): Deprecate (for now).
(gpgme_op_assuan_transact_ext): New prototype.
(gpgme_op_assuan_transact): Deprecate.
(struct _gpgme_op_g13_result): Replace with ...
(struct _gpgme_op_vfs_mount_result): ... this.
(gpgme_op_g13_mount): Replace with ...
(gpgme_op_vfs_mount): ... this.
* gpgme.def (gpgme_op_assuan_transact_ext, gpgme_wait_ext)
(gpgme_op_vfs_mount_result, gpgme_op_vfs_mount): New.
(gpgme_op_g13_mount): Remove.
* libgpgme.vers: Likewise.
* engine-backend.h (struct engine_ops): Remove RESULT_CB and
RESULT_CB_VALUE args in opassuan_transact member. Add CANCEL_OP
member.
* ops.h (_gpgme_cancel_with_err, _gpgme_wait_on_condition): Add
OP_ERR argument.
(_gpgme_wait_one_ext): New prototype.
* context.h (ctx_op_data_id_t): Add OPDATA_VFS_MOUNT.
* engine-g13.c (g13_cancel_op): New function.
(parse_status): Remove declaration.
(g13_assuan_simple_command): Do nothing with status lines for now.
(status_handler): Update opaque value access.
(_gpgme_engine_ops_g13): Add new cancel_op member.
* gpgme.c (_gpgme_cancel_with_err): Add new parameter OP_ERR.
Handle operational errors.
(gpgme_cancel, gpgme_io_read, gpgme_io_write): Add debug output.
* data.c (_gpgme_data_inbound_handler)
(_gpgme_data_outbound_handler): Adjust opaque value access.
* engine-gpg.c (command_handler, status_handler)
(colon_line_handler): Likewise.
* engine-gpgsm.c (status_handler): Likewise.
* engine-gpg.c (_gpgme_engine_ops_gpg): Add cancel_op member.
* engine-gpgsm.c (_gpgme_engine_ops_gpgsm): Likewise.
* g13.c: Rewritten (and will be rewritten again).
* engine.h (_gpgme_engine_op_assuan_transact): Remove result_cb
and result_cb_value parameters from prototype.
(_gpgme_engine_cancel_op): New prototype.
* engine.c (engine_ops) [! ENABLE_ASSUAN]: Add missing comma.
(_gpgme_engine_op_assuan_transact): Remove result_cb and
result_cb_value parameter.
(_gpgme_engine_cancel_op): New function.
* wait.h (_gpgme_run_io_cb): Add new argument OP_ERR.
(struct io_cb_data): New struct to pass opaque data and get a
op_err return value. Needed because we can't modify I/O callback
handler signature because it is exposed to the user.
* wait.c (_gpgme_run_io_cb): Add OP_ERR parameter. Handle
operational errors.
* wait-user.c (_gpgme_user_io_cb_handler): Handle operational
errors.
* wait-private.c (_gpgme_wait_on_condition): New argument to
retrieve the operational result. Handle operational errors in
session based protocols.
(_gpgme_wait_one_ext): New function.
(_gpgme_wait_one): Pass argument in invocation of
_gpgme_wait_on_condition.
* wait-global.c (struct ctx_list_item): Add member OP_ERR.
(ctx_done): New argument OP_ERR.
(ctx_wait): New argument OP_ERR.
(gpgme_wait_ext): New function based on gpgme_wait but handling
operational errors.
(gpgme_wait): Implement in term of gpgme_wait_ext.
* keylist.c (gpgme_op_keylist_next): Pass argument in invocation
of _gpgme_wait_on_condition.
* trustlist.c (gpgme_op_trustlist_next): Pass argument in
invocation of _gpgme_wait_on_condition.
* engine-assuan.c (struct engine_llass): Replace members RESULT_CB
and RESULT_CB_VALUE by LAST_OP_ERR.
(_gpgme_engine_assuan_last_op_err): Add this hack function.
(llass_cancel_op): New function.
(_gpgme_engine_llass_ops): Add cancel_op member.
(llass_status_handler): Update opaque value access.
(llass_transact): Remove RESULT_CB and RESULT_CB_VALUE arguments.
* opassuan.c: Move compat hacks to the end of file.
(opassuan_start): Do not set OPD->result.err.
Do not pass RESULT_Cb and CTX to _gpgme_engine_op_assuan_transact.
(gpgme_op_assuan_transact_ext): New function.
* debug.h (DEBUG_GLOBAL): New debug level. * debug.h (DEBUG_GLOBAL): New debug level.
* conversion.c (gnupg_errors, _gpgme_map_gnupg_error): Removed. * conversion.c (gnupg_errors, _gpgme_map_gnupg_error): Removed.
* data-user.c (gpgme_data_new_from_cbs): Add debug output. * data-user.c (gpgme_data_new_from_cbs): Add debug output.

View File

@ -38,7 +38,7 @@ typedef enum
{ {
OPDATA_DECRYPT, OPDATA_SIGN, OPDATA_ENCRYPT, OPDATA_PASSPHRASE, OPDATA_DECRYPT, OPDATA_SIGN, OPDATA_ENCRYPT, OPDATA_PASSPHRASE,
OPDATA_IMPORT, OPDATA_GENKEY, OPDATA_KEYLIST, OPDATA_EDIT, OPDATA_IMPORT, OPDATA_GENKEY, OPDATA_KEYLIST, OPDATA_EDIT,
OPDATA_VERIFY, OPDATA_TRUSTLIST, OPDATA_ASSUAN OPDATA_VERIFY, OPDATA_TRUSTLIST, OPDATA_ASSUAN, OPDATA_VFS_MOUNT
} ctx_op_data_id_t; } ctx_op_data_id_t;

View File

@ -247,7 +247,8 @@ gpgme_data_get_file_name (gpgme_data_t dh)
gpgme_error_t gpgme_error_t
_gpgme_data_inbound_handler (void *opaque, int fd) _gpgme_data_inbound_handler (void *opaque, int fd)
{ {
gpgme_data_t dh = (gpgme_data_t) opaque; struct io_cb_data *data = (struct io_cb_data *) opaque;
gpgme_data_t dh = (gpgme_data_t) data->handler_value;
char buffer[BUFFER_SIZE]; char buffer[BUFFER_SIZE];
char *bufp = buffer; char *bufp = buffer;
ssize_t buflen; ssize_t buflen;
@ -279,7 +280,8 @@ _gpgme_data_inbound_handler (void *opaque, int fd)
gpgme_error_t gpgme_error_t
_gpgme_data_outbound_handler (void *opaque, int fd) _gpgme_data_outbound_handler (void *opaque, int fd)
{ {
gpgme_data_t dh = (gpgme_data_t) opaque; struct io_cb_data *data = (struct io_cb_data *) opaque;
gpgme_data_t dh = (gpgme_data_t) data->handler_value;
ssize_t nwritten; ssize_t nwritten;
TRACE_BEG1 (DEBUG_CTX, "_gpgme_data_outbound_handler", dh, TRACE_BEG1 (DEBUG_CTX, "_gpgme_data_outbound_handler", dh,
"fd=0x%x", fd); "fd=0x%x", fd);

View File

@ -70,9 +70,8 @@ struct engine_llass
struct gpgme_io_cbs io_cbs; struct gpgme_io_cbs io_cbs;
/* Internal callbacks. */ /* Hack for old opassuan.c interface, see there the result struct. */
engine_assuan_result_cb_t result_cb; gpg_error_t last_op_err;
void *result_cb_value;
/* User provided callbacks. */ /* User provided callbacks. */
struct { struct {
@ -95,6 +94,12 @@ struct engine_llass
typedef struct engine_llass *engine_llass_t; typedef struct engine_llass *engine_llass_t;
gpg_error_t _gpgme_engine_assuan_last_op_err (void *engine)
{
engine_llass_t llass = engine;
return llass->last_op_err;
}
/* Prototypes. */ /* Prototypes. */
static void llass_io_event (void *engine, static void llass_io_event (void *engine,
@ -169,6 +174,21 @@ llass_cancel (void *engine)
} }
static gpgme_error_t
llass_cancel_op (void *engine)
{
engine_llass_t llass = engine;
if (!llass)
return gpg_error (GPG_ERR_INV_VALUE);
if (llass->status_cb.fd != -1)
_gpgme_io_close (llass->status_cb.fd);
return 0;
}
static void static void
llass_release (void *engine) llass_release (void *engine)
{ {
@ -408,8 +428,9 @@ inquire_cb (engine_llass_t llass, const char *keyword, const char *args)
static gpgme_error_t static gpgme_error_t
llass_status_handler (void *opaque, int fd) llass_status_handler (void *opaque, int fd)
{ {
struct io_cb_data *data = (struct io_cb_data *) opaque;
engine_llass_t llass = (engine_llass_t) data->handler_value;
gpgme_error_t err = 0; gpgme_error_t err = 0;
engine_llass_t llass = opaque;
char *line; char *line;
size_t linelen; size_t linelen;
@ -459,8 +480,6 @@ llass_status_handler (void *opaque, int fd)
if (linelen && llass->user.data_cb) if (linelen && llass->user.data_cb)
err = llass->user.data_cb (llass->user.data_cb_value, err = llass->user.data_cb (llass->user.data_cb_value,
src, linelen); src, linelen);
else
err = 0;
TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass, TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
"fd 0x%x: D inlinedata; status from cb: %s", "fd 0x%x: D inlinedata; status from cb: %s",
@ -474,8 +493,6 @@ llass_status_handler (void *opaque, int fd)
/* END received. Tell the data callback. */ /* END received. Tell the data callback. */
if (llass->user.data_cb) if (llass->user.data_cb)
err = llass->user.data_cb (llass->user.data_cb_value, NULL, 0); err = llass->user.data_cb (llass->user.data_cb_value, NULL, 0);
else
err = 0;
TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass, TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
"fd 0x%x: END line; status from cb: %s", "fd 0x%x: END line; status from cb: %s",
@ -502,8 +519,6 @@ llass_status_handler (void *opaque, int fd)
if (llass->user.status_cb) if (llass->user.status_cb)
err = llass->user.status_cb (llass->user.status_cb_value, err = llass->user.status_cb (llass->user.status_cb_value,
src, args); src, args);
else
err = 0;
TRACE3 (DEBUG_CTX, "gpgme:llass_status_handler", llass, TRACE3 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
"fd 0x%x: S line (%s) - status from cb: %s", "fd 0x%x: S line (%s) - status from cb: %s",
@ -554,34 +569,28 @@ llass_status_handler (void *opaque, int fd)
TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass, TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
"fd 0x%x: ERR line: %s", "fd 0x%x: ERR line: %s",
fd, err ? gpg_strerror (err) : "ok"); fd, err ? gpg_strerror (err) : "ok");
/* Command execution errors are not fatal, as we use /* Command execution errors are not fatal, as we use
a session based protocol. */ a session based protocol. */
if (llass->result_cb) data->op_err = err;
err = llass->result_cb (llass->result_cb_value, err); llass->last_op_err = err;
else
err = 0; /* The caller will do the rest (namely, call cancel_op,
if (!err) which closes status_fd). */
{
_gpgme_io_close (llass->status_cb.fd);
return 0; return 0;
} }
}
else if (linelen >= 2 else if (linelen >= 2
&& line[0] == 'O' && line[1] == 'K' && line[0] == 'O' && line[1] == 'K'
&& (line[2] == '\0' || line[2] == ' ')) && (line[2] == '\0' || line[2] == ' '))
{ {
TRACE1 (DEBUG_CTX, "gpgme:llass_status_handler", llass, TRACE1 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
"fd 0x%x: OK line", fd); "fd 0x%x: OK line", fd);
if (llass->result_cb)
err = llass->result_cb (llass->result_cb_value, 0); llass->last_op_err = 0;
else
err = 0;
if (!err)
{
_gpgme_io_close (llass->status_cb.fd); _gpgme_io_close (llass->status_cb.fd);
return 0; return 0;
} }
}
else else
{ {
/* Comment line or invalid line. */ /* Comment line or invalid line. */
@ -667,8 +676,6 @@ start (engine_llass_t llass, const char *command)
static gpgme_error_t static gpgme_error_t
llass_transact (void *engine, llass_transact (void *engine,
const char *command, const char *command,
engine_assuan_result_cb_t result_cb,
void *result_cb_value,
gpgme_assuan_data_cb_t data_cb, gpgme_assuan_data_cb_t data_cb,
void *data_cb_value, void *data_cb_value,
gpgme_assuan_inquire_cb_t inq_cb, gpgme_assuan_inquire_cb_t inq_cb,
@ -682,8 +689,6 @@ llass_transact (void *engine,
if (!llass || !command || !*command) if (!llass || !command || !*command)
return gpg_error (GPG_ERR_INV_VALUE); return gpg_error (GPG_ERR_INV_VALUE);
llass->result_cb = result_cb;
llass->result_cb_value = result_cb_value;
llass->user.data_cb = data_cb; llass->user.data_cb = data_cb;
llass->user.data_cb_value = data_cb_value; llass->user.data_cb_value = data_cb_value;
llass->user.inq_cb = inq_cb; llass->user.inq_cb = inq_cb;
@ -754,5 +759,6 @@ struct engine_ops _gpgme_engine_ops_assuan =
NULL, /* conf_save */ NULL, /* conf_save */
llass_set_io_cbs, llass_set_io_cbs,
llass_io_event, llass_io_event,
llass_cancel llass_cancel,
llass_cancel_op
}; };

View File

@ -98,8 +98,6 @@ struct engine_ops
unsigned int flags); unsigned int flags);
gpgme_error_t (*opassuan_transact) (void *engine, gpgme_error_t (*opassuan_transact) (void *engine,
const char *command, const char *command,
engine_assuan_result_cb_t result_cb,
void *result_cb_value,
gpgme_assuan_data_cb_t data_cb, gpgme_assuan_data_cb_t data_cb,
void *data_cb_value, void *data_cb_value,
gpgme_assuan_inquire_cb_t inq_cb, gpgme_assuan_inquire_cb_t inq_cb,
@ -113,7 +111,11 @@ struct engine_ops
void (*set_io_cbs) (void *engine, gpgme_io_cbs_t io_cbs); void (*set_io_cbs) (void *engine, gpgme_io_cbs_t io_cbs);
void (*io_event) (void *engine, gpgme_event_io_t type, void *type_data); void (*io_event) (void *engine, gpgme_event_io_t type, void *type_data);
/* Cancel the whole engine session. */
gpgme_error_t (*cancel) (void *engine); gpgme_error_t (*cancel) (void *engine);
/* Cancel only the current operation, not the whole session. */
gpgme_error_t (*cancel_op) (void *engine);
}; };

View File

@ -180,6 +180,21 @@ g13_cancel (void *engine)
} }
static gpgme_error_t
g13_cancel_op (void *engine)
{
engine_g13_t g13 = engine;
if (!g13)
return gpg_error (GPG_ERR_INV_VALUE);
if (g13->status_cb.fd != -1)
_gpgme_io_close (g13->status_cb.fd);
return 0;
}
static void static void
g13_release (void *engine) g13_release (void *engine)
{ {
@ -385,9 +400,6 @@ g13_set_locale (void *engine, int category, const char *value)
} }
/* Forward declaration. */
static gpgme_status_code_t parse_status (const char *name);
static gpgme_error_t static gpgme_error_t
g13_assuan_simple_command (assuan_context_t ctx, char *cmd, g13_assuan_simple_command (assuan_context_t ctx, char *cmd,
engine_status_handler_t status_fnc, engine_status_handler_t status_fnc,
@ -422,7 +434,6 @@ g13_assuan_simple_command (assuan_context_t ctx, char *cmd,
&& line[0] == 'S' && line[1] == ' ') && line[0] == 'S' && line[1] == ' ')
{ {
char *rest; char *rest;
gpgme_status_code_t r;
rest = strchr (line + 2, ' '); rest = strchr (line + 2, ' ');
if (!rest) if (!rest)
@ -430,12 +441,7 @@ g13_assuan_simple_command (assuan_context_t ctx, char *cmd,
else else
*(rest++) = 0; *(rest++) = 0;
r = parse_status (line + 2); /* Nothing to do with status lines. */
if (r >= 0 && status_fnc)
err = status_fnc (status_fnc_value, r, rest);
else
err = gpg_error (GPG_ERR_GENERAL);
} }
else else
err = gpg_error (GPG_ERR_GENERAL); err = gpg_error (GPG_ERR_GENERAL);
@ -449,8 +455,9 @@ g13_assuan_simple_command (assuan_context_t ctx, char *cmd,
static gpgme_error_t static gpgme_error_t
status_handler (void *opaque, int fd) status_handler (void *opaque, int fd)
{ {
struct io_cb_data *data = (struct io_cb_data *) opaque;
engine_g13_t g13 = (engine_g13_t) data->handler_value;
gpgme_error_t err = 0; gpgme_error_t err = 0;
engine_g13_t g13 = opaque;
char *line; char *line;
size_t linelen; size_t linelen;
@ -792,5 +799,6 @@ struct engine_ops _gpgme_engine_ops_g13 =
NULL, /* conf_save */ NULL, /* conf_save */
g13_set_io_cbs, g13_set_io_cbs,
g13_io_event, g13_io_event,
g13_cancel g13_cancel,
g13_cancel_op,
}; };

View File

@ -646,10 +646,10 @@ gpg_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
static gpgme_error_t static gpgme_error_t
command_handler (void *opaque, int fd) command_handler (void *opaque, int fd)
{ {
struct io_cb_data *data = (struct io_cb_data *) opaque;
engine_gpg_t gpg = (engine_gpg_t) data->handler_value;
gpgme_error_t err; gpgme_error_t err;
engine_gpg_t gpg = (engine_gpg_t) opaque;
int processed = 0; int processed = 0;
assert (gpg->cmd.used); assert (gpg->cmd.used);
assert (gpg->cmd.code); assert (gpg->cmd.code);
assert (gpg->cmd.fnc); assert (gpg->cmd.fnc);
@ -1139,7 +1139,8 @@ read_status (engine_gpg_t gpg)
static gpgme_error_t static gpgme_error_t
status_handler (void *opaque, int fd) status_handler (void *opaque, int fd)
{ {
engine_gpg_t gpg = opaque; struct io_cb_data *data = (struct io_cb_data *) opaque;
engine_gpg_t gpg = (engine_gpg_t) data->handler_value;
int err; int err;
assert (fd == gpg->status.fd[0]); assert (fd == gpg->status.fd[0]);
@ -1246,7 +1247,8 @@ read_colon_line (engine_gpg_t gpg)
static gpgme_error_t static gpgme_error_t
colon_line_handler (void *opaque, int fd) colon_line_handler (void *opaque, int fd)
{ {
engine_gpg_t gpg = opaque; struct io_cb_data *data = (struct io_cb_data *) opaque;
engine_gpg_t gpg = (engine_gpg_t) data->handler_value;
gpgme_error_t rc = 0; gpgme_error_t rc = 0;
assert (fd == gpg->colon.fd[0]); assert (fd == gpg->colon.fd[0]);
@ -2365,5 +2367,6 @@ struct engine_ops _gpgme_engine_ops_gpg =
NULL, /* conf_save */ NULL, /* conf_save */
gpg_set_io_cbs, gpg_set_io_cbs,
gpg_io_event, gpg_io_event,
gpg_cancel gpg_cancel,
NULL /* cancel_op */
}; };

View File

@ -761,8 +761,9 @@ parse_status (const char *name)
static gpgme_error_t static gpgme_error_t
status_handler (void *opaque, int fd) status_handler (void *opaque, int fd)
{ {
struct io_cb_data *data = (struct io_cb_data *) opaque;
engine_gpgsm_t gpgsm = (engine_gpgsm_t) data->handler_value;
gpgme_error_t err = 0; gpgme_error_t err = 0;
engine_gpgsm_t gpgsm = opaque;
char *line; char *line;
size_t linelen; size_t linelen;
@ -799,7 +800,7 @@ status_handler (void *opaque, int fd)
err = gpgsm->status.fnc (gpgsm->status.fnc_value, err = gpgsm->status.fnc (gpgsm->status.fnc_value,
GPGME_STATUS_EOF, ""); GPGME_STATUS_EOF, "");
if (!err && gpgsm->colon.fnc && gpgsm->colon.any ) if (!err && gpgsm->colon.fnc && gpgsm->colon.any)
{ {
/* We must tell a colon function about the EOF. We do /* We must tell a colon function about the EOF. We do
this only when we have seen any data lines. Note this only when we have seen any data lines. Note
@ -1939,5 +1940,6 @@ struct engine_ops _gpgme_engine_ops_gpgsm =
NULL, /* conf_save */ NULL, /* conf_save */
gpgsm_set_io_cbs, gpgsm_set_io_cbs,
gpgsm_io_event, gpgsm_io_event,
gpgsm_cancel gpgsm_cancel,
NULL /* cancel_op */
}; };

View File

@ -58,7 +58,7 @@ static struct engine_ops *engine_ops[] =
#ifdef ENABLE_ASSUAN #ifdef ENABLE_ASSUAN
&_gpgme_engine_ops_assuan, /* Low-Level Assuan. */ &_gpgme_engine_ops_assuan, /* Low-Level Assuan. */
#else #else
NULL NULL,
#endif #endif
#ifdef ENABLE_G13 #ifdef ENABLE_G13
&_gpgme_engine_ops_g13 /* Crypto VFS. */ &_gpgme_engine_ops_g13 /* Crypto VFS. */
@ -777,8 +777,6 @@ _gpgme_engine_op_getauditlog (engine_t engine, gpgme_data_t output,
gpgme_error_t gpgme_error_t
_gpgme_engine_op_assuan_transact (engine_t engine, _gpgme_engine_op_assuan_transact (engine_t engine,
const char *command, const char *command,
engine_assuan_result_cb_t result_cb,
void *result_cb_value,
gpgme_assuan_data_cb_t data_cb, gpgme_assuan_data_cb_t data_cb,
void *data_cb_value, void *data_cb_value,
gpgme_assuan_inquire_cb_t inq_cb, gpgme_assuan_inquire_cb_t inq_cb,
@ -794,7 +792,6 @@ _gpgme_engine_op_assuan_transact (engine_t engine,
return (*engine->ops->opassuan_transact) (engine->engine, return (*engine->ops->opassuan_transact) (engine->engine,
command, command,
result_cb, result_cb_value,
data_cb, data_cb_value, data_cb, data_cb_value,
inq_cb, inq_cb_value, inq_cb, inq_cb_value,
status_cb, status_cb_value); status_cb, status_cb_value);
@ -848,6 +845,7 @@ _gpgme_engine_io_event (engine_t engine,
} }
/* Cancel the session and the pending operation if any. */
gpgme_error_t gpgme_error_t
_gpgme_engine_cancel (engine_t engine) _gpgme_engine_cancel (engine_t engine)
{ {
@ -859,3 +857,17 @@ _gpgme_engine_cancel (engine_t engine)
return (*engine->ops->cancel) (engine->engine); return (*engine->ops->cancel) (engine->engine);
} }
/* Cancel the pending operation, but not the complete session. */
gpgme_error_t
_gpgme_engine_cancel_op (engine_t engine)
{
if (!engine)
return gpg_error (GPG_ERR_INV_VALUE);
if (!engine->ops->cancel_op)
return 0;
return (*engine->ops->cancel_op) (engine->engine);
}

View File

@ -133,8 +133,6 @@ gpgme_error_t _gpgme_engine_op_getauditlog (engine_t engine,
gpgme_error_t _gpgme_engine_op_assuan_transact gpgme_error_t _gpgme_engine_op_assuan_transact
(engine_t engine, (engine_t engine,
const char *command, const char *command,
engine_assuan_result_cb_t result_cb,
void *result_cb_value,
gpgme_assuan_data_cb_t data_cb, gpgme_assuan_data_cb_t data_cb,
void *data_cb_value, void *data_cb_value,
gpgme_assuan_inquire_cb_t inq_cb, gpgme_assuan_inquire_cb_t inq_cb,
@ -154,4 +152,6 @@ void _gpgme_engine_io_event (engine_t engine,
gpgme_error_t _gpgme_engine_cancel (engine_t engine); gpgme_error_t _gpgme_engine_cancel (engine_t engine);
gpgme_error_t _gpgme_engine_cancel_op (engine_t engine);
#endif /* ENGINE_H */ #endif /* ENGINE_H */

View File

@ -31,41 +31,19 @@
typedef struct typedef struct
{ {
struct _gpgme_op_g13_result result; struct _gpgme_op_vfs_mount_result result;
} *op_data_t; } *op_data_t;
/* This callback is used to return the status of the assuan command gpgme_vfs_mount_result_t
back rather than transmission errors. */ gpgme_op_vfs_mount_result (gpgme_ctx_t ctx)
static gpgme_error_t
result_cb (void *priv, gpgme_error_t result)
{
gpgme_ctx_t ctx = (gpgme_ctx_t)priv;
gpgme_error_t err;
void *hook;
op_data_t opd;
err = _gpgme_op_data_lookup (ctx, OPDATA_ASSUAN, &hook, -1, NULL);
opd = hook;
if (err)
return err;
if (!opd)
return gpg_error (GPG_ERR_INTERNAL);
opd->result.err = result;
return 0;
}
gpgme_g13_result_t
gpgme_op_g13_result (gpgme_ctx_t ctx)
{ {
gpgme_error_t err; gpgme_error_t err;
void *hook; void *hook;
op_data_t opd; op_data_t opd;
err = _gpgme_op_data_lookup (ctx, OPDATA_ASSUAN, &hook, -1, NULL); err = _gpgme_op_data_lookup (ctx, OPDATA_VFS_MOUNT, &hook, -1, NULL);
opd = hook; opd = hook;
/* Check in case this function is used without having run a command /* Check in case this function is used without having run a command
before. */ before. */
@ -77,7 +55,7 @@ gpgme_op_g13_result (gpgme_ctx_t ctx)
static gpgme_error_t static gpgme_error_t
opg13_start (gpgme_ctx_t ctx, int synchronous, vfs_start (gpgme_ctx_t ctx, int synchronous,
const char *command, const char *command,
gpgme_assuan_data_cb_t data_cb, gpgme_assuan_data_cb_t data_cb,
void *data_cb_value, void *data_cb_value,
@ -99,14 +77,13 @@ opg13_start (gpgme_ctx_t ctx, int synchronous,
if (err) if (err)
return err; return err;
err = _gpgme_op_data_lookup (ctx, OPDATA_ASSUAN, &hook, sizeof (*opd), NULL); err = _gpgme_op_data_lookup (ctx, OPDATA_VFS_MOUNT, &hook, sizeof (*opd),
NULL);
opd = hook; opd = hook;
if (err) if (err)
return err; return err;
opd->result.err = gpg_error (GPG_ERR_UNFINISHED);
return _gpgme_engine_op_assuan_transact (ctx->engine, command, return _gpgme_engine_op_assuan_transact (ctx->engine, command,
result_cb, ctx,
data_cb, data_cb_value, data_cb, data_cb_value,
inq_cb, inq_cb_value, inq_cb, inq_cb_value,
status_cb, status_cb_value); status_cb, status_cb_value);
@ -116,7 +93,7 @@ opg13_start (gpgme_ctx_t ctx, int synchronous,
/* XXXX. This is the asynchronous variant. */ /* XXXX. This is the asynchronous variant. */
static gpgme_error_t static gpgme_error_t
gpgme_op_g13_transact_start (gpgme_ctx_t ctx, gpgme_op_vfs_transact_start (gpgme_ctx_t ctx,
const char *command, const char *command,
gpgme_assuan_data_cb_t data_cb, gpgme_assuan_data_cb_t data_cb,
void *data_cb_value, void *data_cb_value,
@ -125,59 +102,52 @@ gpgme_op_g13_transact_start (gpgme_ctx_t ctx,
gpgme_assuan_status_cb_t status_cb, gpgme_assuan_status_cb_t status_cb,
void *status_cb_value) void *status_cb_value)
{ {
return opg13_start (ctx, 0, command, data_cb, data_cb_value, return vfs_start (ctx, 0, command, data_cb, data_cb_value,
inq_cb, inq_cb_value, status_cb, status_cb_value); inq_cb, inq_cb_value, status_cb, status_cb_value);
} }
/* XXXX. This is the synchronous variant. */ /* XXXX. This is the synchronous variant. */
static gpgme_error_t static gpgme_error_t
gpgme_op_g13_transact (gpgme_ctx_t ctx, gpgme_op_vfs_transact (gpgme_ctx_t ctx,
const char *command, const char *command,
gpgme_assuan_data_cb_t data_cb, gpgme_assuan_data_cb_t data_cb,
void *data_cb_value, void *data_cb_value,
gpgme_assuan_inquire_cb_t inq_cb, gpgme_assuan_inquire_cb_t inq_cb,
void *inq_cb_value, void *inq_cb_value,
gpgme_assuan_status_cb_t status_cb, gpgme_assuan_status_cb_t status_cb,
void *status_cb_value) void *status_cb_value,
gpgme_error_t *op_err)
{ {
gpgme_error_t err; gpgme_error_t err;
err = opg13_start (ctx, 1, command, data_cb, data_cb_value, err = vfs_start (ctx, 1, command, data_cb, data_cb_value,
inq_cb, inq_cb_value, status_cb, status_cb_value); inq_cb, inq_cb_value, status_cb, status_cb_value);
if (!err) if (!err)
err = _gpgme_wait_one (ctx); err = _gpgme_wait_one_ext (ctx, op_err);
return err; return err;
} }
/* The actual exported interface follows. */ /* The actual exported interface follows. */
static gpg_error_t
get_err (gpgme_ctx_t ctx)
{
gpgme_g13_result_t res;
res = gpgme_op_g13_result (ctx);
if (! res)
return gpg_error (GPG_ERR_GENERAL);
return res->err;
}
/* The container is automatically unmounted when the context is reset /* The container is automatically unmounted when the context is reset
or destroyed. This is a synchronous convenience interface, which or destroyed. This is a synchronous convenience interface, which
automatically returns an operation error if there is no automatically returns an operation error if there is no
transmission error. */ transmission error. */
gpgme_error_t gpgme_error_t
gpgme_op_g13_mount (gpgme_ctx_t ctx, const char *container_file, gpgme_op_vfs_mount (gpgme_ctx_t ctx, const char *container_file,
const char *mount_dir, int flags) const char *mount_dir, int flags, gpgme_error_t *op_err)
{ {
gpg_error_t err; gpg_error_t err;
char *cmd; char *cmd;
char *container_file_esc = NULL; char *container_file_esc = NULL;
/* We want to encourage people to check error values, so not getting
them is discouraged here. Also makes our code easier. */
if (! op_err)
return gpg_error (GPG_ERR_INV_VALUE);
err = _gpgme_encode_percent_string (container_file, &container_file_esc, 0); err = _gpgme_encode_percent_string (container_file, &container_file_esc, 0);
if (err) if (err)
return err; return err;
@ -190,11 +160,10 @@ gpgme_op_g13_mount (gpgme_ctx_t ctx, const char *container_file,
} }
free (container_file_esc); free (container_file_esc);
err = gpgme_op_g13_transact (ctx, cmd, NULL, NULL, NULL, NULL, err = gpgme_op_vfs_transact (ctx, cmd, NULL, NULL, NULL, NULL,
NULL, NULL); NULL, NULL, op_err);
free (cmd); free (cmd);
err = err || get_err (ctx); if (err || *op_err)
if (err)
return err; return err;
if (mount_dir) if (mount_dir)
@ -219,11 +188,9 @@ gpgme_op_g13_mount (gpgme_ctx_t ctx, const char *container_file,
return gpg_error_from_syserror (); return gpg_error_from_syserror ();
} }
err = gpgme_op_g13_transact (ctx, cmd, NULL, NULL, NULL, NULL, err = gpgme_op_vfs_transact (ctx, cmd, NULL, NULL, NULL, NULL,
NULL, NULL); NULL, NULL, op_err);
free (cmd); free (cmd);
/* Note: in symmetry with the asynchronous variant, we don't return
the error in the result structure here, if any. */
return err; return err;
} }

View File

@ -46,7 +46,7 @@ static char *def_lc_messages;
gpgme_error_t _gpgme_selftest = GPG_ERR_NOT_OPERATIONAL; gpgme_error_t _gpgme_selftest = GPG_ERR_NOT_OPERATIONAL;
/* Protects all reference counters in result structures. All other /* Protects all reference counters in result structures. All other
accesses to a key are read only. */ accesses to a result structure are read only. */
DEFINE_STATIC_LOCK (result_ref_lock); DEFINE_STATIC_LOCK (result_ref_lock);
@ -118,17 +118,32 @@ gpgme_new (gpgme_ctx_t *r_ctx)
gpgme_error_t gpgme_error_t
_gpgme_cancel_with_err (gpgme_ctx_t ctx, gpg_error_t ctx_err) _gpgme_cancel_with_err (gpgme_ctx_t ctx, gpg_error_t ctx_err,
gpg_error_t op_err)
{ {
gpgme_error_t err; gpgme_error_t err;
TRACE_BEG1 (DEBUG_CTX, "_gpgme_cancel_with_err", ctx, "ctx_err=%i", struct gpgme_io_event_done_data data;
ctx_err);
TRACE_BEG2 (DEBUG_CTX, "_gpgme_cancel_with_err", ctx, "ctx_err=%i, op_err=%i",
ctx_err, op_err);
if (ctx_err)
{
err = _gpgme_engine_cancel (ctx->engine); err = _gpgme_engine_cancel (ctx->engine);
if (err) if (err)
return TRACE_ERR (err); return TRACE_ERR (err);
}
else
{
err = _gpgme_engine_cancel_op (ctx->engine);
if (err)
return TRACE_ERR (err);
}
_gpgme_engine_io_event (ctx->engine, GPGME_EVENT_DONE, &ctx_err); data.err = ctx_err;
data.op_err = op_err;
_gpgme_engine_io_event (ctx->engine, GPGME_EVENT_DONE, &data);
return TRACE_ERR (0); return TRACE_ERR (0);
} }
@ -138,7 +153,13 @@ _gpgme_cancel_with_err (gpgme_ctx_t ctx, gpg_error_t ctx_err)
gpgme_error_t gpgme_error_t
gpgme_cancel (gpgme_ctx_t ctx) gpgme_cancel (gpgme_ctx_t ctx)
{ {
return _gpgme_cancel_with_err (ctx, gpg_error (GPG_ERR_CANCELED)); gpg_error_t err;
TRACE_BEG (DEBUG_CTX, "gpgme_cancel", ctx);
err = _gpgme_cancel_with_err (ctx, gpg_error (GPG_ERR_CANCELED), 0);
return TRACE_ERR (err);
} }
@ -486,10 +507,12 @@ ssize_t
gpgme_io_read (int fd, void *buffer, size_t count) gpgme_io_read (int fd, void *buffer, size_t count)
{ {
int ret; int ret;
TRACE_BEG2 (DEBUG_GLOBAL, "gpgme_io_read", fd,
"buffer=%p, count=%u", buffer, count);
ret = _gpgme_io_read (fd, buffer, count); ret = _gpgme_io_read (fd, buffer, count);
return ret; return TRACE_SYSRES (ret);
} }
@ -500,10 +523,12 @@ ssize_t
gpgme_io_write (int fd, const void *buffer, size_t count) gpgme_io_write (int fd, const void *buffer, size_t count)
{ {
int ret; int ret;
TRACE_BEG2 (DEBUG_GLOBAL, "gpgme_io_write", fd,
"buffer=%p, count=%u", buffer, count);
ret = _gpgme_io_write (fd, buffer, count); ret = _gpgme_io_write (fd, buffer, count);
return ret; return TRACE_SYSRES (ret);
} }

View File

@ -185,7 +185,11 @@ EXPORTS
gpgme_op_export_keys @142 gpgme_op_export_keys @142
gpgme_op_export_keys_start @143 gpgme_op_export_keys_start @143
gpgme_op_g13_mount @144 gpgme_op_assuan_transact_ext @144
gpgme_wait_ext @145
gpgme_op_vfs_mount_result @146
gpgme_op_vfs_mount @147
; END ; END

View File

@ -960,6 +960,17 @@ typedef enum
} }
gpgme_event_io_t; gpgme_event_io_t;
struct gpgme_io_event_done_data
{
/* A fatal IPC error or an operational error in state-less
protocols. */
gpgme_error_t err;
/* An operational errors in session-based protocols. */
gpgme_error_t op_err;
};
typedef struct gpgme_io_event_done_data *gpgme_io_event_done_data_t;
/* The type of a function that is called when a context finished an /* The type of a function that is called when a context finished an
operation. */ operation. */
typedef void (*gpgme_event_io_cb_t) (void *data, gpgme_event_io_t type, typedef void (*gpgme_event_io_cb_t) (void *data, gpgme_event_io_t type,
@ -990,6 +1001,9 @@ ssize_t gpgme_io_write (int fd, const void *buffer, size_t count);
the pending operation to finish. */ the pending operation to finish. */
gpgme_ctx_t gpgme_wait (gpgme_ctx_t ctx, gpgme_error_t *status, int hang); gpgme_ctx_t gpgme_wait (gpgme_ctx_t ctx, gpgme_error_t *status, int hang);
gpgme_ctx_t gpgme_wait_ext (gpgme_ctx_t ctx, gpgme_error_t *status,
gpgme_error_t *op_err, int hang);
/* Functions to handle data objects. */ /* Functions to handle data objects. */
@ -1733,21 +1747,6 @@ typedef gpgme_error_t (*gpgme_assuan_inquire_cb_t)
typedef gpgme_error_t (*gpgme_assuan_status_cb_t) typedef gpgme_error_t (*gpgme_assuan_status_cb_t)
(void *opaque, const char *status, const char *args); (void *opaque, const char *status, const char *args);
struct _gpgme_op_assuan_result
{
/* The result of the actual assuan command. An OK is indicated by a
value of 0 and an ERR by the respective error error value. This
is required because assuan operations use a session-based
interface. The error code of the GPGME function calls just
reflects transmission errors. */
gpgme_error_t err;
};
typedef struct _gpgme_op_assuan_result *gpgme_assuan_result_t;
/* Return the result of the last Assuan command. */
gpgme_assuan_result_t gpgme_op_assuan_result (gpgme_ctx_t ctx);
/* Send the Assuan COMMAND and return results via the callbacks. /* Send the Assuan COMMAND and return results via the callbacks.
Asynchronous variant. */ Asynchronous variant. */
gpgme_error_t gpgme_op_assuan_transact_start (gpgme_ctx_t ctx, gpgme_error_t gpgme_op_assuan_transact_start (gpgme_ctx_t ctx,
@ -1761,35 +1760,56 @@ gpgme_error_t gpgme_op_assuan_transact_start (gpgme_ctx_t ctx,
/* Send the Assuan COMMAND and return results via the callbacks. /* Send the Assuan COMMAND and return results via the callbacks.
Synchronous variant. */ Synchronous variant. */
gpgme_error_t gpgme_op_assuan_transact (gpgme_ctx_t ctx, gpgme_error_t gpgme_op_assuan_transact_ext (gpgme_ctx_t ctx,
const char *command, const char *command,
gpgme_assuan_data_cb_t data_cb, gpgme_assuan_data_cb_t data_cb,
void *data_cb_value, void *data_cb_value,
gpgme_assuan_inquire_cb_t inq_cb, gpgme_assuan_inquire_cb_t inq_cb,
void *inq_cb_value, void *inq_cb_value,
gpgme_assuan_status_cb_t stat_cb, gpgme_assuan_status_cb_t stat_cb,
void *stat_cb_value); void *stat_cb_value,
gpgme_error_t *op_err);
/* Compat. */
struct _gpgme_op_assuan_result
{
/* Deprecated. Use the second value in a DONE event or the
synchronous variant gpgme_op_assuan_transact_ext. */
gpgme_error_t err _GPGME_DEPRECATED_OUTSIDE_GPGME;
};
typedef struct _gpgme_op_assuan_result *gpgme_assuan_result_t;
/* Return the result of the last Assuan command. */
gpgme_assuan_result_t gpgme_op_assuan_result (gpgme_ctx_t ctx)
_GPGME_DEPRECATED;
gpgme_error_t
gpgme_op_assuan_transact (gpgme_ctx_t ctx,
const char *command,
gpgme_assuan_data_cb_t data_cb,
void *data_cb_value,
gpgme_assuan_inquire_cb_t inq_cb,
void *inq_cb_value,
gpgme_assuan_status_cb_t status_cb,
void *status_cb_value) _GPGME_DEPRECATED;
/* Crypto container support. */ /* Crypto container support. */
struct _gpgme_op_g13_result struct _gpgme_op_vfs_mount_result
{ {
/* The result of the actual assuan command. An OK is indicated by a char *mount_dir;
value of 0 and an ERR by the respective error error value. This
is required because assuan operations use a session-based
interface. The error code of the GPGME function calls just
reflects transmission errors. */
gpgme_error_t err;
}; };
typedef struct _gpgme_op_g13_result *gpgme_g13_result_t; typedef struct _gpgme_op_vfs_mount_result *gpgme_vfs_mount_result_t;
gpgme_vfs_mount_result_t gpgme_op_vfs_mount_result (gpgme_ctx_t ctx);
/* The container is automatically unmounted when the context is reset /* The container is automatically unmounted when the context is reset
or destroyed. This is a synchronous convenience interface, which or destroyed. Transmission errors are returned directly,
automatically returns an operation error if there is no operational errors are returned in OP_ERR. */
transmission error. */ gpgme_error_t gpgme_op_vfs_mount (gpgme_ctx_t ctx, const char *container_file,
gpgme_error_t gpgme_op_g13_mount (gpgme_ctx_t ctx, const char *container_file, const char *mount_dir, int flags,
const char *mount_dir, int flags); gpgme_error_t *op_err);
/* Interface to gpgconf(1). */ /* Interface to gpgconf(1). */

View File

@ -928,7 +928,7 @@ gpgme_op_keylist_next (gpgme_ctx_t ctx, gpgme_key_t *r_key)
if (!opd->key_queue) if (!opd->key_queue)
{ {
err = _gpgme_wait_on_condition (ctx, &opd->key_cond); err = _gpgme_wait_on_condition (ctx, &opd->key_cond, NULL);
if (err) if (err)
return err; return err;

View File

@ -65,7 +65,12 @@ GPGME_1.1 {
gpgme_op_export_keys; gpgme_op_export_keys;
gpgme_op_export_keys_start; gpgme_op_export_keys_start;
gpgme_op_g13_mount; gpgme_op_assuan_transact_ext;
gpgme_wait_ext;
gpgme_op_vfs_mount_result;
gpgme_op_vfs_mount;
}; };

View File

@ -26,58 +26,6 @@
#include "ops.h" #include "ops.h"
#include "util.h" #include "util.h"
typedef struct
{
struct _gpgme_op_assuan_result result;
} *op_data_t;
/* This callback is used to return the status of the assuan command
back. Note that this is different from the error code returned
from gpgme_op_assuan_transact because the later only reflects error
with the connection. */
static gpgme_error_t
result_cb (void *priv, gpgme_error_t result)
{
gpgme_ctx_t ctx = (gpgme_ctx_t)priv;
gpgme_error_t err;
void *hook;
op_data_t opd;
err = _gpgme_op_data_lookup (ctx, OPDATA_ASSUAN, &hook, -1, NULL);
opd = hook;
if (err)
return err;
if (!opd)
return gpg_error (GPG_ERR_INTERNAL);
opd->result.err = result;
return 0;
}
gpgme_assuan_result_t
gpgme_op_assuan_result (gpgme_ctx_t ctx)
{
gpgme_error_t err;
void *hook;
op_data_t opd;
err = _gpgme_op_data_lookup (ctx, OPDATA_ASSUAN, &hook, -1, NULL);
opd = hook;
/* Check in case this function is used without having run a command
before. */
if (err || !opd)
return NULL;
return &opd->result;
}
static gpgme_error_t static gpgme_error_t
opassuan_start (gpgme_ctx_t ctx, int synchronous, opassuan_start (gpgme_ctx_t ctx, int synchronous,
const char *command, const char *command,
@ -89,8 +37,6 @@ opassuan_start (gpgme_ctx_t ctx, int synchronous,
void *status_cb_value) void *status_cb_value)
{ {
gpgme_error_t err; gpgme_error_t err;
void *hook;
op_data_t opd;
if (!command || !*command) if (!command || !*command)
return gpg_error (GPG_ERR_INV_VALUE); return gpg_error (GPG_ERR_INV_VALUE);
@ -101,14 +47,7 @@ opassuan_start (gpgme_ctx_t ctx, int synchronous,
if (err) if (err)
return err; return err;
err = _gpgme_op_data_lookup (ctx, OPDATA_ASSUAN, &hook, sizeof (*opd), NULL);
opd = hook;
if (err)
return err;
opd->result.err = gpg_error (GPG_ERR_UNFINISHED);
return _gpgme_engine_op_assuan_transact (ctx->engine, command, return _gpgme_engine_op_assuan_transact (ctx->engine, command,
result_cb, ctx,
data_cb, data_cb_value, data_cb, data_cb_value,
inq_cb, inq_cb_value, inq_cb, inq_cb_value,
status_cb, status_cb_value); status_cb, status_cb_value);
@ -135,6 +74,70 @@ gpgme_op_assuan_transact_start (gpgme_ctx_t ctx,
/* XXXX. This is the synchronous variant. */ /* XXXX. This is the synchronous variant. */
gpgme_error_t
gpgme_op_assuan_transact_ext (gpgme_ctx_t ctx,
const char *command,
gpgme_assuan_data_cb_t data_cb,
void *data_cb_value,
gpgme_assuan_inquire_cb_t inq_cb,
void *inq_cb_value,
gpgme_assuan_status_cb_t status_cb,
void *status_cb_value,
gpgme_error_t *op_err)
{
gpgme_error_t err;
err = opassuan_start (ctx, 1, command,
data_cb, data_cb_value,
inq_cb, inq_cb_value,
status_cb, status_cb_value);
if (!err)
err = _gpgme_wait_one_ext (ctx, op_err);
return err;
}
/* Compatibility code for old interface. */
/* Evil hack breaking abstractions for the purpose of localizing our
other hack. This is copied from engine.c. */
struct engine
{
struct engine_ops *ops;
void *engine;
};
typedef struct
{
struct _gpgme_op_assuan_result result;
} *op_data_t;
gpg_error_t _gpgme_engine_assuan_last_op_err (void *engine);
gpgme_assuan_result_t
gpgme_op_assuan_result (gpgme_ctx_t ctx)
{
gpgme_error_t err;
void *hook;
op_data_t opd;
err = _gpgme_op_data_lookup (ctx, OPDATA_ASSUAN, &hook, -1, NULL);
opd = hook;
/* Check in case this function is used without having run a command
before. */
if (err || !opd)
return NULL;
/* All of this is a hack for the old style interface. The new style
interface returns op errors directly. */
opd->result.err = _gpgme_engine_assuan_last_op_err (ctx->engine->engine);
return &opd->result;
}
gpgme_error_t gpgme_error_t
gpgme_op_assuan_transact (gpgme_ctx_t ctx, gpgme_op_assuan_transact (gpgme_ctx_t ctx,
const char *command, const char *command,
@ -145,14 +148,14 @@ gpgme_op_assuan_transact (gpgme_ctx_t ctx,
gpgme_assuan_status_cb_t status_cb, gpgme_assuan_status_cb_t status_cb,
void *status_cb_value) void *status_cb_value)
{ {
gpgme_error_t op_err;
gpgme_error_t err; gpgme_error_t err;
err = opassuan_start (ctx, 1, command, /* Users of the old-style session based interfaces need to look at
data_cb, data_cb_value, the result structure. */
gpgme_op_assuan_transact_ext (ctx, command, data_cb, data_cb_value,
inq_cb, inq_cb_value, inq_cb, inq_cb_value,
status_cb, status_cb_value); status_cb, status_cb_value, &op_err);
if (!err)
err = _gpgme_wait_one (ctx);
return err; return err;
} }

View File

@ -27,14 +27,17 @@
/* From gpgme.c. */ /* From gpgme.c. */
gpgme_error_t _gpgme_cancel_with_err (gpgme_ctx_t ctx, gpg_error_t ctx_err); gpgme_error_t _gpgme_cancel_with_err (gpgme_ctx_t ctx, gpg_error_t ctx_err,
gpg_error_t op_err);
void _gpgme_release_result (gpgme_ctx_t ctx); void _gpgme_release_result (gpgme_ctx_t ctx);
/* From wait.c. */ /* From wait.c. */
gpgme_error_t _gpgme_wait_one (gpgme_ctx_t ctx); gpgme_error_t _gpgme_wait_one (gpgme_ctx_t ctx);
gpgme_error_t _gpgme_wait_on_condition (gpgme_ctx_t ctx, volatile int *cond); gpgme_error_t _gpgme_wait_one_ext (gpgme_ctx_t ctx, gpgme_error_t *op_err);
gpgme_error_t _gpgme_wait_on_condition (gpgme_ctx_t ctx, volatile int *cond,
gpgme_error_t *op_err);
/* From data.c. */ /* From data.c. */

View File

@ -220,7 +220,7 @@ gpgme_op_trustlist_next (gpgme_ctx_t ctx, gpgme_trust_item_t *r_item)
if (!opd->trust_queue) if (!opd->trust_queue)
{ {
err = _gpgme_wait_on_condition (ctx, &opd->trust_cond); err = _gpgme_wait_on_condition (ctx, &opd->trust_cond, NULL);
if (err) if (err)
return err; return err;
if (!opd->trust_cond) if (!opd->trust_cond)

View File

@ -74,6 +74,7 @@ struct ctx_list_item
gpgme_ctx_t ctx; gpgme_ctx_t ctx;
/* The status is set when the ctx is moved to the done list. */ /* The status is set when the ctx is moved to the done list. */
gpgme_error_t status; gpgme_error_t status;
gpgme_error_t op_err;
}; };
/* The active list contains all contexts that are in the global event /* The active list contains all contexts that are in the global event
@ -112,7 +113,7 @@ ctx_active (gpgme_ctx_t ctx)
/* Enter the context CTX into the done list with status STATUS. */ /* Enter the context CTX into the done list with status STATUS. */
static void static void
ctx_done (gpgme_ctx_t ctx, gpgme_error_t status) ctx_done (gpgme_ctx_t ctx, gpgme_error_t status, gpgme_error_t op_err)
{ {
struct ctx_list_item *li; struct ctx_list_item *li;
@ -131,6 +132,7 @@ ctx_done (gpgme_ctx_t ctx, gpgme_error_t status)
ctx_active_list = li->next; ctx_active_list = li->next;
li->status = status; li->status = status;
li->op_err = op_err;
/* Add LI to done list. */ /* Add LI to done list. */
li->next = ctx_done_list; li->next = ctx_done_list;
@ -147,7 +149,7 @@ ctx_done (gpgme_ctx_t ctx, gpgme_error_t status)
If a matching context could be found, return it. Return NULL if no If a matching context could be found, return it. Return NULL if no
context could be found. */ context could be found. */
static gpgme_ctx_t static gpgme_ctx_t
ctx_wait (gpgme_ctx_t ctx, gpgme_error_t *status) ctx_wait (gpgme_ctx_t ctx, gpgme_error_t *status, gpgme_error_t *op_err)
{ {
struct ctx_list_item *li; struct ctx_list_item *li;
@ -164,6 +166,8 @@ ctx_wait (gpgme_ctx_t ctx, gpgme_error_t *status)
ctx = li->ctx; ctx = li->ctx;
if (status) if (status)
*status = li->status; *status = li->status;
if (op_err)
*op_err = li->op_err;
/* Remove LI from done list. */ /* Remove LI from done list. */
if (li->next) if (li->next)
@ -203,15 +207,16 @@ _gpgme_wait_global_event_cb (void *data, gpgme_event_io_t type,
if (err) if (err)
/* An error occured. Close all fds in this context, and /* An error occured. Close all fds in this context, and
send the error in a done event. */ send the error in a done event. */
_gpgme_cancel_with_err (ctx, err); _gpgme_cancel_with_err (ctx, err, 0);
} }
break; break;
case GPGME_EVENT_DONE: case GPGME_EVENT_DONE:
{ {
gpgme_error_t *errp = (gpgme_error_t *) type_data; gpgme_io_event_done_data_t done_data =
assert (errp); (gpgme_io_event_done_data_t) type_data;
ctx_done (ctx, *errp);
ctx_done (ctx, done_data->err, done_data->op_err);
} }
break; break;
@ -246,7 +251,8 @@ _gpgme_wait_global_event_cb (void *data, gpgme_event_io_t type,
error occurs, NULL is returned and *STATUS is set to the error error occurs, NULL is returned and *STATUS is set to the error
value. */ value. */
gpgme_ctx_t gpgme_ctx_t
gpgme_wait (gpgme_ctx_t ctx, gpgme_error_t *status, int hang) gpgme_wait_ext (gpgme_ctx_t ctx, gpgme_error_t *status,
gpgme_error_t *op_err, int hang)
{ {
do do
{ {
@ -266,6 +272,8 @@ gpgme_wait (gpgme_ctx_t ctx, gpgme_error_t *status, int hang)
UNLOCK (ctx_list_lock); UNLOCK (ctx_list_lock);
if (status) if (status)
*status = gpg_error_from_errno (saved_errno); *status = gpg_error_from_errno (saved_errno);
if (op_err)
*op_err = 0;
return NULL; return NULL;
} }
fdt.size = i; fdt.size = i;
@ -285,6 +293,8 @@ gpgme_wait (gpgme_ctx_t ctx, gpgme_error_t *status, int hang)
free (fdt.fds); free (fdt.fds);
if (status) if (status)
*status = gpg_error_from_errno (saved_errno); *status = gpg_error_from_errno (saved_errno);
if (op_err)
*op_err = 0;
return NULL; return NULL;
} }
@ -294,6 +304,7 @@ gpgme_wait (gpgme_ctx_t ctx, gpgme_error_t *status, int hang)
{ {
gpgme_ctx_t ictx; gpgme_ctx_t ictx;
gpgme_error_t err = 0; gpgme_error_t err = 0;
gpgme_error_t local_op_err = 0;
struct wait_item_s *item; struct wait_item_s *item;
assert (nr); assert (nr);
@ -310,12 +321,12 @@ gpgme_wait (gpgme_ctx_t ctx, gpgme_error_t *status, int hang)
UNLOCK (ctx->lock); UNLOCK (ctx->lock);
if (!err) if (!err)
err = _gpgme_run_io_cb (&fdt.fds[i], 0); err = _gpgme_run_io_cb (&fdt.fds[i], 0, &local_op_err);
if (err) if (err || local_op_err)
{ {
/* An error occured. Close all fds in this context, /* An error occured. Close all fds in this context,
and signal it. */ and signal it. */
_gpgme_cancel_with_err (ictx, err); _gpgme_cancel_with_err (ictx, err, local_op_err);
/* Break out of the loop, and retry the select() /* Break out of the loop, and retry the select()
from scratch, because now all fds should be from scratch, because now all fds should be
@ -338,7 +349,9 @@ gpgme_wait (gpgme_ctx_t ctx, gpgme_error_t *status, int hang)
break; break;
if (i == actx->fdt.size) if (i == actx->fdt.size)
{ {
gpgme_error_t err = 0; struct gpgme_io_event_done_data data;
data.err = 0;
data.op_err = 0;
/* FIXME: This does not perform too well. We have to /* FIXME: This does not perform too well. We have to
release the lock because the I/O event handler release the lock because the I/O event handler
@ -349,7 +362,7 @@ gpgme_wait (gpgme_ctx_t ctx, gpgme_error_t *status, int hang)
contexts to be released and call the DONE events contexts to be released and call the DONE events
afterwards. */ afterwards. */
UNLOCK (ctx_list_lock); UNLOCK (ctx_list_lock);
_gpgme_engine_io_event (actx->engine, GPGME_EVENT_DONE, &err); _gpgme_engine_io_event (actx->engine, GPGME_EVENT_DONE, &data);
LOCK (ctx_list_lock); LOCK (ctx_list_lock);
goto retry; goto retry;
} }
@ -357,7 +370,7 @@ gpgme_wait (gpgme_ctx_t ctx, gpgme_error_t *status, int hang)
UNLOCK (ctx_list_lock); UNLOCK (ctx_list_lock);
{ {
gpgme_ctx_t dctx = ctx_wait (ctx, status); gpgme_ctx_t dctx = ctx_wait (ctx, status, op_err);
if (dctx) if (dctx)
{ {
@ -369,6 +382,8 @@ gpgme_wait (gpgme_ctx_t ctx, gpgme_error_t *status, int hang)
ctx = NULL; ctx = NULL;
if (status) if (status)
*status = 0; *status = 0;
if (op_err)
*op_err = 0;
} }
} }
} }
@ -376,3 +391,10 @@ gpgme_wait (gpgme_ctx_t ctx, gpgme_error_t *status, int hang)
return ctx; return ctx;
} }
gpgme_ctx_t
gpgme_wait (gpgme_ctx_t ctx, gpgme_error_t *status, int hang)
{
return gpgme_wait_ext (ctx, status, NULL, hang);
}

View File

@ -72,7 +72,8 @@ _gpgme_wait_private_event_cb (void *data, gpgme_event_io_t type,
finished and return its error value. Otherwise, wait until COND is finished and return its error value. Otherwise, wait until COND is
satisfied or the operation finished. */ satisfied or the operation finished. */
gpgme_error_t gpgme_error_t
_gpgme_wait_on_condition (gpgme_ctx_t ctx, volatile int *cond) _gpgme_wait_on_condition (gpgme_ctx_t ctx, volatile int *cond,
gpgme_error_t *op_err_p)
{ {
gpgme_error_t err = 0; gpgme_error_t err = 0;
int hang = 1; int hang = 1;
@ -87,8 +88,10 @@ _gpgme_wait_on_condition (gpgme_ctx_t ctx, volatile int *cond)
/* An error occured. Close all fds in this context, and /* An error occured. Close all fds in this context, and
signal it. */ signal it. */
err = gpg_error_from_errno (errno); err = gpg_error_from_errno (errno);
_gpgme_cancel_with_err (ctx, err); _gpgme_cancel_with_err (ctx, err, 0);
if (op_err_p)
*op_err_p = 0;
return err; return err;
} }
@ -96,6 +99,8 @@ _gpgme_wait_on_condition (gpgme_ctx_t ctx, volatile int *cond)
{ {
if (ctx->fdt.fds[i].fd != -1 && ctx->fdt.fds[i].signaled) if (ctx->fdt.fds[i].fd != -1 && ctx->fdt.fds[i].signaled)
{ {
gpgme_error_t op_err = 0;
ctx->fdt.fds[i].signaled = 0; ctx->fdt.fds[i].signaled = 0;
assert (nr); assert (nr);
nr--; nr--;
@ -106,15 +111,33 @@ _gpgme_wait_on_condition (gpgme_ctx_t ctx, volatile int *cond)
UNLOCK (ctx->lock); UNLOCK (ctx->lock);
if (!err) if (!err)
err = _gpgme_run_io_cb (&ctx->fdt.fds[i], 0); err = _gpgme_run_io_cb (&ctx->fdt.fds[i], 0, &op_err);
if (err) if (err)
{ {
/* An error occured. Close all fds in this context, /* An error occured. Close all fds in this context,
and signal it. */ and signal it. */
_gpgme_cancel_with_err (ctx, err); _gpgme_cancel_with_err (ctx, err, 0);
if (op_err_p)
*op_err_p = 0;
return err; return err;
} }
else if (op_err)
{
/* An operational error occured. Cancel the current
operation but not the session, and signal it. */
_gpgme_cancel_with_err (ctx, 0, op_err);
/* NOTE: This relies on the operational error being
generated after the operation really has
completed, for example after no further status
line output is generated. Otherwise the
following I/O will spill over into the next
operation. */
if (op_err_p)
*op_err_p = op_err;
return 0;
}
} }
} }
@ -123,7 +146,10 @@ _gpgme_wait_on_condition (gpgme_ctx_t ctx, volatile int *cond)
break; break;
if (i == ctx->fdt.size) if (i == ctx->fdt.size)
{ {
_gpgme_engine_io_event (ctx->engine, GPGME_EVENT_DONE, &err); struct gpgme_io_event_done_data data;
data.err = 0;
data.op_err = 0;
_gpgme_engine_io_event (ctx->engine, GPGME_EVENT_DONE, &data);
hang = 0; hang = 0;
} }
if (cond && *cond) if (cond && *cond)
@ -131,14 +157,26 @@ _gpgme_wait_on_condition (gpgme_ctx_t ctx, volatile int *cond)
} }
while (hang); while (hang);
if (op_err_p)
*op_err_p = 0;
return 0; return 0;
} }
/* Wait until the blocking operation in context CTX has finished and /* Wait until the blocking operation in context CTX has finished and
return the error value. */ return the error value. This variant can not be used for
session-based protocols. */
gpgme_error_t gpgme_error_t
_gpgme_wait_one (gpgme_ctx_t ctx) _gpgme_wait_one (gpgme_ctx_t ctx)
{ {
return _gpgme_wait_on_condition (ctx, NULL); return _gpgme_wait_on_condition (ctx, NULL, NULL);
}
/* Wait until the blocking operation in context CTX has finished and
return the error value. This is the right variant to use for
sesion-based protocols. */
gpgme_error_t
_gpgme_wait_one_ext (gpgme_ctx_t ctx, gpgme_error_t *op_err)
{
return _gpgme_wait_on_condition (ctx, NULL, op_err);
} }

View File

@ -41,6 +41,7 @@ gpgme_error_t
_gpgme_user_io_cb_handler (void *data, int fd) _gpgme_user_io_cb_handler (void *data, int fd)
{ {
gpgme_error_t err = 0; gpgme_error_t err = 0;
gpgme_error_t op_err = 0;
struct tag *tag = (struct tag *) data; struct tag *tag = (struct tag *) data;
gpgme_ctx_t ctx; gpgme_ctx_t ctx;
@ -54,9 +55,9 @@ _gpgme_user_io_cb_handler (void *data, int fd)
UNLOCK (ctx->lock); UNLOCK (ctx->lock);
if (! err) if (! err)
err = _gpgme_run_io_cb (&ctx->fdt.fds[tag->idx], 0); err = _gpgme_run_io_cb (&ctx->fdt.fds[tag->idx], 0, &op_err);
if (err) if (err || op_err)
_gpgme_cancel_with_err (ctx, err); _gpgme_cancel_with_err (ctx, err, op_err);
else else
{ {
unsigned int i; unsigned int i;
@ -64,8 +65,15 @@ _gpgme_user_io_cb_handler (void *data, int fd)
for (i = 0; i < ctx->fdt.size; i++) for (i = 0; i < ctx->fdt.size; i++)
if (ctx->fdt.fds[i].fd != -1) if (ctx->fdt.fds[i].fd != -1)
break; break;
if (i == ctx->fdt.size) if (i == ctx->fdt.size)
_gpgme_engine_io_event (ctx->engine, GPGME_EVENT_DONE, &err); {
struct gpgme_io_event_done_data done_data;
done_data.err = 0;
done_data.op_err = 0;
_gpgme_engine_io_event (ctx->engine, GPGME_EVENT_DONE, &done_data);
}
} }
return 0; return 0;
} }

View File

@ -180,9 +180,13 @@ _gpgme_remove_io_cb (void *data)
we are called from our own event loops. So if CHECKED is 1, the we are called from our own event loops. So if CHECKED is 1, the
check is skipped. */ check is skipped. */
gpgme_error_t gpgme_error_t
_gpgme_run_io_cb (struct io_select_fd_s *an_fds, int checked) _gpgme_run_io_cb (struct io_select_fd_s *an_fds, int checked,
gpgme_error_t *op_err)
{ {
struct wait_item_s *item; struct wait_item_s *item;
struct io_cb_data iocb_data;
gpgme_error_t err;
item = (struct wait_item_s *) an_fds->opaque; item = (struct wait_item_s *) an_fds->opaque;
assert (item); assert (item);
@ -207,5 +211,11 @@ _gpgme_run_io_cb (struct io_select_fd_s *an_fds, int checked)
TRACE2 (DEBUG_CTX, "_gpgme_run_io_cb", item, "handler (%p, %d)", TRACE2 (DEBUG_CTX, "_gpgme_run_io_cb", item, "handler (%p, %d)",
item->handler_value, an_fds->fd); item->handler_value, an_fds->fd);
return item->handler (item->handler_value, an_fds->fd);
iocb_data.handler_value = item->handler_value;
iocb_data.op_err = 0;
err = item->handler (&iocb_data, an_fds->fd);
*op_err = iocb_data.op_err;
return err;
} }

View File

@ -75,8 +75,22 @@ void _gpgme_wait_user_remove_io_cb (void *tag);
void _gpgme_wait_user_event_cb (void *data, gpgme_event_io_t type, void _gpgme_wait_user_event_cb (void *data, gpgme_event_io_t type,
void *type_data); void *type_data);
gpgme_error_t _gpgme_wait_one (gpgme_ctx_t ctx); gpgme_error_t _gpgme_run_io_cb (struct io_select_fd_s *an_fds, int checked,
gpgme_error_t *err);
gpgme_error_t _gpgme_run_io_cb (struct io_select_fd_s *an_fds, int checked);
/* Session based interfaces require to make a distinction between IPC
errors and operational errors. To glue this into the old
interface, I/O handlers (esp. the status handler) are called with a
struct as the opaque value that contains the handlers opaque value
but also a field for the operational error to be returned. */
struct io_cb_data
{
/* If this is the first field, the old internal code will still work. */
void *handler_value;
/* The I/O callback can pass an operational error here. */
gpgme_error_t op_err;
};
#endif /* WAIT_H */ #endif /* WAIT_H */

View File

@ -1,3 +1,7 @@
2009-10-26 Marcus Brinkmann <marcus@g10code.de>
* opassuan/t-command.c: Update to new interface.
2009-10-15 Werner Koch <wk@g10code.com> 2009-10-15 Werner Koch <wk@g10code.com>
* run-verify.c: New. * run-verify.c: New.

View File

@ -101,6 +101,7 @@ int
main (int argc, char **argv) main (int argc, char **argv)
{ {
gpgme_error_t err; gpgme_error_t err;
gpgme_error_t op_err;
gpgme_ctx_t ctx; gpgme_ctx_t ctx;
const char *command; const char *command;
@ -125,18 +126,9 @@ main (int argc, char **argv)
err = gpgme_set_protocol (ctx, GPGME_PROTOCOL_ASSUAN); err = gpgme_set_protocol (ctx, GPGME_PROTOCOL_ASSUAN);
fail_if_err (err); fail_if_err (err);
err = gpgme_op_assuan_transact (ctx, command, err = gpgme_op_assuan_transact_ext (ctx, command, data_cb, NULL,
data_cb, NULL, inq_cb, NULL, status_cb, NULL, &op_err);
inq_cb, NULL, fail_if_err (err || op_err);
status_cb, NULL);
fail_if_err (err);
err = gpgme_op_assuan_result (ctx)->err;
if (err)
fprintf (stderr, "assuan command `%s' failed: %s <%s> (%d)\n",
command, gpg_strerror (err), gpg_strsource (err), err);
else
fprintf (stderr, "assuan command `%s' succeeded\n", command);
gpgme_release (ctx); gpgme_release (ctx);