First take on the low-level assuan interface.

This commit is contained in:
Werner Koch 2009-01-26 10:21:10 +00:00
parent 23022dd9d9
commit d951cb713f
27 changed files with 1531 additions and 48 deletions

View File

@ -1,3 +1,7 @@
2009-01-26 Werner Koch <wk@g10code.com>
* configure.ac (AC_CONFIG_FILES): Add tests/opassuan/Makefile.
2008-12-08 Marcus Brinkmann <marcus@g10code.de> 2008-12-08 Marcus Brinkmann <marcus@g10code.de>
Release GPGME 1.1.8. Release GPGME 1.1.8.
@ -720,8 +724,8 @@
* configure.ac (AC_INIT): Bump version to 0.3.3. * configure.ac (AC_INIT): Bump version to 0.3.3.
* jnlib/Makefile.am: Rever to older version that includes xmalloc * jnlib/Makefile.am: Rever to older version that includes xmalloc
but not dotlock and some other files. Reported by Stéphane but not dotlock and some other files. Reported by Stéphane
Corthésy. Corthésy.
2002-02-10 Marcus Brinkmann <marcus@g10code.de> 2002-02-10 Marcus Brinkmann <marcus@g10code.de>

View File

@ -15,8 +15,7 @@
# Public License for more details. # Public License for more details.
# #
# You should have received a copy of the GNU Lesser General Public # You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the Free Software # License along with this program; if not, see <http://www.gnu.org/licenses/>.
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
## Process this file with automake to produce Makefile.in ## Process this file with automake to produce Makefile.in

16
NEWS
View File

@ -1,3 +1,19 @@
Noteworthy changes in version 1.1.9
------------------------------------------------
* Interface changes relative to the 1.1.7 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GPGME_PROTOCOL_ASSUAN NEW.
gpgme_assuan_data_cb_t NEW.
gpgme_assuan_sendfnc_ctx_t NEW.
gpgme_assuan_inquire_cb_t NEW.
gpgme_assuan_status_cb_t NEW.
gpgme_op_assuan_transact_start NEW.
gpgme_op_assuan_transact NEW.
gpgme_op_assuan_result NEW.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Noteworthy changes in version 1.1.8 (2008-12-08) Noteworthy changes in version 1.1.8 (2008-12-08)
------------------------------------------------ ------------------------------------------------

View File

@ -159,4 +159,6 @@ $AUTOMAKE --gnu;
echo "Running autoconf${FORCE} ..." echo "Running autoconf${FORCE} ..."
$AUTOCONF${FORCE} $AUTOCONF${FORCE}
echo "You may now run \"./configure --enable-maintainer-mode && make\"." echo "You may now run:
./configure --enable-maintainer-mode && make
"

View File

@ -31,8 +31,8 @@ min_automake_version="1.10"
# specific feature can already be done under the assumption that the # specific feature can already be done under the assumption that the
# SVN version is the most recent one in a branch. To disable the SVN # SVN version is the most recent one in a branch. To disable the SVN
# version for the real release, set the my_issvn macro to no. # version for the real release, set the my_issvn macro to no.
m4_define(my_version, [1.1.8]) m4_define(my_version, [1.1.9])
m4_define(my_issvn, [no]) m4_define(my_issvn, [yes])
m4_define([svn_revision], m4_esyscmd([echo -n $( (svn info 2>/dev/null \ m4_define([svn_revision], m4_esyscmd([echo -n $( (svn info 2>/dev/null \
|| echo 'Revision: 0')|sed -n '/^Revision:/ {s/[^0-9]//gp;q;}')])) || echo 'Revision: 0')|sed -n '/^Revision:/ {s/[^0-9]//gp;q;}')]))
@ -744,7 +744,10 @@ AC_SUBST(LTLIBOBJS)
# Create config files # Create config files
AC_CONFIG_FILES(Makefile assuan/Makefile src/Makefile AC_CONFIG_FILES(Makefile assuan/Makefile src/Makefile
tests/Makefile tests/gpg/Makefile tests/gpgsm/Makefile tests/Makefile
tests/gpg/Makefile
tests/gpgsm/Makefile
tests/opassuan/Makefile
doc/Makefile complus/Makefile doc/Makefile complus/Makefile
src/versioninfo.rc src/versioninfo.rc
src/gpgme.h) src/gpgme.h)

View File

@ -1,3 +1,36 @@
2009-01-26 Werner Koch <wk@g10code.com>
* opassuan.c, dirinfo.c, engine-assuan.c: New.
* Makefile.am: Add them.
* engine-backend.h: Add _gpgme_engine_ops_assuan.
(struct engine_ops): Add field OPASSUAN_TRANSACT. Update all
engine intializers.
* Makefile.am (gpgsm_components): Add engine-assuan.c.
* gpgme.h.in (gpgme_protocol_t): Add GPGME_PROTOCOL_ASSUAN.
(gpgme_assuan_data_cb_t, gpgme_assuan_sendfnc_ctx_t)
(gpgme_assuan_inquire_cb_t, gpgme_assuan_status_cb_t): New.
(gpgme_op_assuan_transact_start, gpgme_op_assuan_transact): New.
* gpgme.c (gpgme_get_protocol_name): Ditto.
(gpgme_set_protocol): Support it.
* engine.c (gpgme_get_engine_info): Ditto.
(engine_ops): Register it.
(_gpgme_engine_op_assuan_transact): New.
* libgpgme.vers (gpgme_op_assuan_transact_start)
(gpgme_op_assuan_transact): New.
* gpgme.def (gpgme_op_assuan_transact_start)
(gpgme_op_assuan_transact): New.
* engine-backend.h (struct engine_ops): Add GET_HOME_DIR and
initialize to NULL for all engines.
* engine.c (engine_get_home_dir): New.
(gpgme_get_engine_info): Use it.
(_gpgme_set_engine_info): Use it.
* engine.h (engine_assuan_result_cb_t): New.
* context.h (ctx_op_data_id_t): Add OPDATA_ASSUAN.
* util.h (GPG_ERR_UNFINISHED): Define if not yet defined.
* version.c (gpgme_check_version): Protect trace arg against NULL.
2009-01-19 Werner Koch <wk@g10code.com> 2009-01-19 Werner Koch <wk@g10code.com>
* rungpg.c: Rename to engine-gpg.c * rungpg.c: Rename to engine-gpg.c
@ -803,7 +836,7 @@
* engine.c (gpgme_engine_check_version): Reimplemented to allow * engine.c (gpgme_engine_check_version): Reimplemented to allow
checking the version correctly even after changing the engine checking the version correctly even after changing the engine
information. Bug reported by Stéphane Corthésy. information. Bug reported by Stéphane Corthésy.
* rungpg.c (read_colon_line): Invoke colon preprocess handler if * rungpg.c (read_colon_line): Invoke colon preprocess handler if
it is set. it is set.
@ -868,7 +901,7 @@
2005-11-27 Marcus Brinkmann <marcus@g10code.de> 2005-11-27 Marcus Brinkmann <marcus@g10code.de>
* engine.c (_gpgme_set_engine_info): Use new_file_name in * engine.c (_gpgme_set_engine_info): Use new_file_name in
engine_get_version invocation. Reported by Stéphane Corthésy. engine_get_version invocation. Reported by Stéphane Corthésy.
2005-11-24 Marcus Brinkmann <marcus@g10code.de> 2005-11-24 Marcus Brinkmann <marcus@g10code.de>
@ -1818,7 +1851,7 @@
* gpgme-config.in (gpg_error_libs): Quote GPG_ERROR_CFLAGS and * gpgme-config.in (gpg_error_libs): Quote GPG_ERROR_CFLAGS and
GPG_ERROR_LIBS when setting the corresponding variables. GPG_ERROR_LIBS when setting the corresponding variables.
Reported by Stéphane Corthésy. Reported by Stéphane Corthésy.
2003-07-22 Marcus Brinkmann <marcus@g10code.de> 2003-07-22 Marcus Brinkmann <marcus@g10code.de>
@ -3707,7 +3740,7 @@
2002-09-28 Marcus Brinkmann <marcus@g10code.de> 2002-09-28 Marcus Brinkmann <marcus@g10code.de>
* conversion.c (_gpgme_hextobyte): Prevent superfluous * conversion.c (_gpgme_hextobyte): Prevent superfluous
multiplication with base. Reported by Stéphane Corthésy. multiplication with base. Reported by Stéphane Corthésy.
* keylist.c (gpgme_op_keylist_ext_start): Use private asynchronous * keylist.c (gpgme_op_keylist_ext_start): Use private asynchronous
operation type in invocation of _gpgme_op_reset. operation type in invocation of _gpgme_op_reset.
@ -3820,7 +3853,7 @@
variables encrypt_info and encrypt_info_len. variables encrypt_info and encrypt_info_len.
* trustlist.c (gpgme_op_trustlist_start): Set colon line handler. * trustlist.c (gpgme_op_trustlist_start): Set colon line handler.
* posix-sema.c (sema_fatal): Remove function. * posix-sema.c (sema_fatal): Remove function.
All these reported by Stéphane Corthésy. All these reported by Stéphane Corthésy.
2002-08-23 Werner Koch <wk@gnupg.org> 2002-08-23 Werner Koch <wk@gnupg.org>
@ -3993,7 +4026,7 @@
* vasprintf.c: Update to more recent libiberty version. * vasprintf.c: Update to more recent libiberty version.
* debug.h: Replace #elsif with #elif. * debug.h: Replace #elsif with #elif.
Submitted by Stéphane Corthésy: Submitted by Stéphane Corthésy:
* util.h (vasprintf): Correct prototype. * util.h (vasprintf): Correct prototype.
* encrypt-sign.c: Include <stddef.h>. * encrypt-sign.c: Include <stddef.h>.
(encrypt_sign_status_handler): Change type of ENCRYPT_INFO_LEN to (encrypt_sign_status_handler): Change type of ENCRYPT_INFO_LEN to
@ -4003,14 +4036,14 @@
2002-07-25 Marcus Brinkmann <marcus@g10code.de> 2002-07-25 Marcus Brinkmann <marcus@g10code.de>
* wait.c (fdt_global): Make static. Reported by Stéphane * wait.c (fdt_global): Make static. Reported by Stéphane
Corthésy. Corthésy.
* rungpg.c (_gpgme_gpg_op_keylist_ext): Skip empty string * rungpg.c (_gpgme_gpg_op_keylist_ext): Skip empty string
patterns. Reported by Stéphane Corthésy. patterns. Reported by Stéphane Corthésy.
* key.c (gpgme_key_get_as_xml): Add OTRUST attribute. Requested * key.c (gpgme_key_get_as_xml): Add OTRUST attribute. Requested
by Stéphane Corthésy. by Stéphane Corthésy.
(gpgme_key_get_string_attr): Add GPGME_ATTR_SIG_SUMMARY case to (gpgme_key_get_string_attr): Add GPGME_ATTR_SIG_SUMMARY case to
silence gcc warning. silence gcc warning.
@ -5060,7 +5093,7 @@
2001-12-19 Marcus Brinkmann <marcus@g10code.de> 2001-12-19 Marcus Brinkmann <marcus@g10code.de>
* engine.c: Include `string.h'. Reported by Stéphane Corthésy. * engine.c: Include `string.h'. Reported by Stéphane Corthésy.
* version.c (get_engine_info): Remove prototype. * version.c (get_engine_info): Remove prototype.
@ -5597,7 +5630,7 @@
callers to use this function without a check for tmp_key. callers to use this function without a check for tmp_key.
* keylist.c (gpgme_op_keylist_next): Reset the key_cond after * keylist.c (gpgme_op_keylist_next): Reset the key_cond after
emptying the queue. Bug reported by Stéphane Corthésy. emptying the queue. Bug reported by Stéphane Corthésy.
2001-09-12 Werner Koch <wk@gnupg.org> 2001-09-12 Werner Koch <wk@gnupg.org>
@ -5679,7 +5712,7 @@
* version.c (gpgme_check_engine): Stop version number parsing at * version.c (gpgme_check_engine): Stop version number parsing at
the opening angle and not the closing one. By Tommy Reynolds. the opening angle and not the closing one. By Tommy Reynolds.
2001-05-01 José Carlos García Sogo <jose@jaimedelamo.eu.org> 2001-05-01 José Carlos García Sogo <jose@jaimedelamo.eu.org>
* encrypt.c (gpgme_op_encrypt_start): Deleted the assert ( !c->gpg ) * encrypt.c (gpgme_op_encrypt_start): Deleted the assert ( !c->gpg )
line, because it gave an error if another operation had been made line, because it gave an error if another operation had been made
@ -5865,8 +5898,8 @@
* rungpg.c (_gpgme_gpg_spawn): Use new function to get GPG's path. * rungpg.c (_gpgme_gpg_spawn): Use new function to get GPG's path.
* signers.c (gpgme_signers_add): Ooops, one should test code and * signers.c (gpgme_signers_add): Ooops, one should test code and
not just write it; the newarr was not assigned. Thanks to José not just write it; the newarr was not assigned. Thanks to José
for pointing this out. Hmmm, still not tested, why shoudl a coder for pointing this out. Hmmm, still not tested, why should a coder
test his fix :-) test his fix :-)
* w32-io.c: Does now use reader threads, so that we can use * w32-io.c: Does now use reader threads, so that we can use

View File

@ -78,7 +78,7 @@ system_components_not_extra =
endif endif
if HAVE_GPGSM if HAVE_GPGSM
gpgsm_components = engine-gpgsm.c gpgsm_components = engine-gpgsm.c engine-assuan.c
else else
gpgsm_components = gpgsm_components =
endif endif
@ -105,9 +105,10 @@ main_sources = \
sign.c passphrase.c progress.c \ sign.c passphrase.c progress.c \
key.c keylist.c trust-item.c trustlist.c \ key.c keylist.c trust-item.c trustlist.c \
import.c export.c genkey.c delete.c edit.c getauditlog.c \ import.c export.c genkey.c delete.c edit.c getauditlog.c \
opassuan.c \
engine.h engine-backend.h engine.c engine-gpg.c status-table.h \ engine.h engine-backend.h engine.c engine-gpg.c status-table.h \
$(gpgsm_components) $(gpgconf_components) gpgconf.c \ $(gpgsm_components) $(gpgconf_components) gpgconf.c \
sema.h priv-io.h $(system_components) \ sema.h priv-io.h $(system_components) dirinfo.c \
debug.c debug.h gpgme.c version.c error.c debug.c debug.h gpgme.c version.c error.c
libgpgme_la_SOURCES = $(main_sources) \ libgpgme_la_SOURCES = $(main_sources) \

View File

@ -36,7 +36,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_VERIFY, OPDATA_TRUSTLIST, OPDATA_ASSUAN
} ctx_op_data_id_t; } ctx_op_data_id_t;
@ -51,7 +51,7 @@ struct ctx_op_data
ctx_op_data_id_t type; ctx_op_data_id_t type;
/* The function to release HOOK and all its associated resources. /* The function to release HOOK and all its associated resources.
Can be NULL if no special dealllocation routine is necessary. */ Can be NULL if no special deallocation routine is necessary. */
void (*cleanup) (void *hook); void (*cleanup) (void *hook);
/* The hook that points to the operation data. */ /* The hook that points to the operation data. */

189
src/dirinfo.c Normal file
View File

@ -0,0 +1,189 @@
/* dirinfo.c - Get directory information
* Copyright (C) 2009 g10 Code GmbH
*
* This file is part of GPGME.
*
* GPGME is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* GPGME is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <string.h>
#include "gpgme.h"
#include "util.h"
#include "priv-io.h"
#include "debug.h"
#include "sema.h"
DEFINE_STATIC_LOCK (dirinfo_lock);
/* Constants used internally to select the data. */
enum
{
WANT_HOMEDIR,
WANT_AGENT_SOCKET
};
/* Values retrieved via gpgconf and cached here. */
static struct {
int valid; /* Cached information is valid. */
char *homedir;
char *agent_socket;
} dirinfo;
/* Parse the output of "gpgconf --list-dirs". This function expects
that DIRINFO_LOCK is held by the caller. */
static void
parse_output (char *line)
{
char *value, *p;
value = strchr (line, ':');
if (!value)
return;
*value++ = 0;
p = strchr (value, ':');
if (p)
*p = 0;
if (_gpgme_decode_percent_string (value, &value, strlen (value)+1, 0))
return;
if (!*value)
return;
if (!strcmp (line, "homedir") && !dirinfo.homedir)
dirinfo.homedir = strdup (value);
else if (!strcmp (line, "agent-socket") && !dirinfo.agent_socket)
dirinfo.agent_socket = strdup (value);
}
/* Read the directory information from gpgconf. This function expects
that DIRINFO_LOCK is held by the caller. */
static void
read_gpgconf_dirs (void)
{
const char *pgmname;
char linebuf[1024] = {0};
int linelen = 0;
char * argv[3];
int rp[2];
struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0},
{-1, -1} };
int status;
int nread;
char *mark = NULL;
pgmname = _gpgme_get_gpgconf_path ();
if (!pgmname)
return; /* No way. */
argv[0] = (char *)pgmname;
argv[1] = "--list-dirs";
argv[2] = NULL;
if (_gpgme_io_pipe (rp, 1) < 0)
return;
cfd[0].fd = rp[1];
status = _gpgme_io_spawn (pgmname, argv, cfd, NULL);
if (status < 0)
{
_gpgme_io_close (rp[0]);
_gpgme_io_close (rp[1]);
return;
}
do
{
nread = _gpgme_io_read (rp[0],
linebuf + linelen,
sizeof linebuf - linelen - 1);
if (nread > 0)
{
char *line;
const char *lastmark = NULL;
size_t nused;
linelen += nread;
linebuf[linelen] = '\0';
for (line=linebuf; (mark = strchr (line, '\n')); line = mark+1 )
{
lastmark = mark;
if (mark > line && mark[-1] == '\r')
mark[-1] = '\0';
else
mark[0] = '\0';
parse_output (line);
}
nused = lastmark? (lastmark + 1 - linebuf) : 0;
memmove (linebuf, linebuf + nused, linelen - nused);
linelen -= nused;
}
}
while (nread > 0 && linelen < sizeof linebuf - 1);
_gpgme_io_close (rp[0]);
}
static const char *
get_gpgconf_dir (int what)
{
const char *result = NULL;
LOCK (dirinfo_lock);
if (!dirinfo.valid)
{
read_gpgconf_dirs ();
/* Even if the reading of the directories failed (e.g. due to an
too old version gpgconf or no gpgconf at all), we need to
mark the entries as valid so that we won't try over and over
to read them. Note further that we are not able to change
the read values later because they are practically statically
allocated. */
dirinfo.valid = 1;
}
switch (what)
{
case WANT_HOMEDIR: result = dirinfo.homedir; break;
case WANT_AGENT_SOCKET: result = dirinfo.agent_socket; break;
}
UNLOCK (dirinfo_lock);
return result;
}
/* Return the default home directory. Returns NULL if not known. */
const char *
_gpgme_get_default_homedir (void)
{
return get_gpgconf_dir (WANT_HOMEDIR);
}
/* Return the default gpg-agent socket name. Returns NULL if not known. */
const char *
_gpgme_get_default_agent_socket (void)
{
return get_gpgconf_dir (WANT_AGENT_SOCKET);
}

744
src/engine-assuan.c Normal file
View File

@ -0,0 +1,744 @@
/* engine-assuan.c - Low-level Assuan protocol engine
* Copyright (C) 2009 g10 Code GmbH
*
* This file is part of GPGME.
*
* GPGME is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* GPGME is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/*
Note: This engine requires a modern Assuan server which uses
gpg-error codes. In particular there is no backward compatible
mapping of old Assuan error codes implemented.
*/
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <assert.h>
#include <unistd.h>
#include <locale.h>
#include <errno.h>
#include "gpgme.h"
#include "util.h"
#include "ops.h"
#include "wait.h"
#include "priv-io.h"
#include "sema.h"
#include "assuan.h"
#include "debug.h"
#include "engine-backend.h"
typedef struct
{
int fd; /* FD we talk about. */
int server_fd;/* Server FD for this connection. */
int dir; /* Inbound/Outbound, maybe given implicit? */
void *data; /* Handler-specific data. */
void *tag; /* ID from the user for gpgme_remove_io_callback. */
} iocb_data_t;
/* Engine instance data. */
struct engine_llass
{
assuan_context_t assuan_ctx;
int lc_ctype_set;
int lc_messages_set;
iocb_data_t status_cb;
struct gpgme_io_cbs io_cbs;
/* Internal callbacks. */
engine_assuan_result_cb_t result_cb;
void *result_cb_value;
/* User provided callbacks. */
struct {
gpgme_assuan_data_cb_t data_cb;
void *data_cb_value;
gpgme_assuan_inquire_cb_t inq_cb;
void *inq_cb_value;
gpgme_assuan_status_cb_t status_cb;
void *status_cb_value;
} user;
/* Option flags. */
struct {
int gpg_agent:1; /* Assume this is a gpg-agent connection. */
} opt;
};
typedef struct engine_llass *engine_llass_t;
/* Helper to pass data to a callback. */
struct _gpgme_assuan_sendfnc_ctx
{
assuan_context_t assuan_ctx;
};
/* Prototypes. */
static void llass_io_event (void *engine,
gpgme_event_io_t type, void *type_data);
/* return the default home directory. */
static const char *
llass_get_home_dir (void)
{
/* For this engine the home directory is not a filename but a string
used to convey options. The exclamation mark is a marker to show
that this is not a directory name. Options are strings delimited
by a space. The only option defined for now is GPG_AGENT to
enable GPG_AGENT specific commands to send to the server at
connection startup. */
return "!GPG_AGENT";
}
static char *
llass_get_version (const char *file_name)
{
return strdup ("1.0");
}
static const char *
llass_get_req_version (void)
{
return "1.0";
}
static void
close_notify_handler (int fd, void *opaque)
{
engine_llass_t llass = opaque;
assert (fd != -1);
if (llass->status_cb.fd == fd)
{
if (llass->status_cb.tag)
llass->io_cbs.remove (llass->status_cb.tag);
llass->status_cb.fd = -1;
llass->status_cb.tag = NULL;
}
}
static gpgme_error_t
llass_cancel (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);
if (llass->assuan_ctx)
{
assuan_disconnect (llass->assuan_ctx);
llass->assuan_ctx = NULL;
}
return 0;
}
static void
llass_release (void *engine)
{
engine_llass_t llass = engine;
if (!llass)
return;
llass_cancel (engine);
free (llass);
}
/* Create a new instance. If HOME_DIR is NULL standard options for use
with gpg-agent are issued. */
static gpgme_error_t
llass_new (void **engine, const char *file_name, const char *home_dir)
{
gpgme_error_t err = 0;
engine_llass_t llass;
char *optstr;
llass = calloc (1, sizeof *llass);
if (!llass)
return gpg_error_from_syserror ();
llass->status_cb.fd = -1;
llass->status_cb.dir = 1;
llass->status_cb.tag = 0;
llass->status_cb.data = llass;
/* Parse_options. */
if (home_dir && *home_dir == '!')
{
home_dir++;
/* Very simple parser only working for the one option we support. */
if (!strncmp (home_dir, "GPG_AGENT", 9)
&& (!home_dir[9] || home_dir[9] == ' '))
llass->opt.gpg_agent = 1;
}
err = assuan_socket_connect (&llass->assuan_ctx, file_name, 0);
if (err)
goto leave;
if (llass->opt.gpg_agent)
{
char *dft_display = NULL;
err = _gpgme_getenv ("DISPLAY", &dft_display);
if (err)
goto leave;
if (dft_display)
{
if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
{
err = gpg_error_from_syserror ();
free (dft_display);
goto leave;
}
free (dft_display);
err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL, NULL,
NULL, NULL, NULL);
free (optstr);
if (err)
goto leave;
}
}
if (llass->opt.gpg_agent && isatty (1))
{
int rc;
char dft_ttyname[64];
char *dft_ttytype = NULL;
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_syserror ();
goto leave;
}
err = assuan_transact (llass->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)
{
err = gpg_error_from_syserror ();
free (dft_ttytype);
goto leave;
}
free (dft_ttytype);
err = assuan_transact (llass->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
llass to tell us when it needs it. */
if (!err && llass->opt.gpg_agent)
{
err = assuan_transact (llass->assuan_ctx, "OPTION allow-pinentry-notify",
NULL, NULL, NULL, NULL, NULL, NULL);
if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
err = 0; /* This work only with recent gpg-agents. */
}
#endif /*HAVE_W32_SYSTEM*/
leave:
/* Close the server ends of the pipes (because of this, we must use
the stored server_fd_str in the function start). Our ends are
closed in llass_release(). */
if (err)
llass_release (llass);
else
*engine = llass;
return err;
}
static gpgme_error_t
llass_set_locale (void *engine, int category, const char *value)
{
gpgme_error_t err;
engine_llass_t llass = engine;
char *optstr;
char *catstr;
if (!llass->opt.gpg_agent)
return 0;
/* FIXME: If value is NULL, we need to reset the option to default.
But we can't do this. So we error out here. gpg-agent needs
support for this. */
if (category == LC_CTYPE)
{
catstr = "lc-ctype";
if (!value && llass->lc_ctype_set)
return gpg_error (GPG_ERR_INV_VALUE);
if (value)
llass->lc_ctype_set = 1;
}
#ifdef LC_MESSAGES
else if (category == LC_MESSAGES)
{
catstr = "lc-messages";
if (!value && llass->lc_messages_set)
return gpg_error (GPG_ERR_INV_VALUE);
if (value)
llass->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 (llass->assuan_ctx, optstr, NULL, NULL,
NULL, NULL, NULL, NULL);
free (optstr);
}
return err;
}
static gpgme_error_t
inquire_cb_sendfnc (gpgme_assuan_sendfnc_ctx_t ctx,
const void *data, size_t datalen)
{
if (data && datalen)
return assuan_send_data (ctx->assuan_ctx, data, datalen);
else
return 0; /* Don't allow an inquire to send a flush. */
}
/* This is the inquiry callback. It handles stuff which ee need to
handle here and passes everything on to the user callback. */
static gpgme_error_t
inquire_cb (engine_llass_t llass, const char *keyword, const char *args)
{
gpg_error_t err;
if (llass->opt.gpg_agent && !strcmp (keyword, "PINENTRY_LAUNCHED"))
{
_gpgme_allow_set_foregound_window ((pid_t)strtoul (args, NULL, 10));
}
if (llass->user.inq_cb)
{
struct _gpgme_assuan_sendfnc_ctx sendfnc_ctx;
sendfnc_ctx.assuan_ctx = llass->assuan_ctx;
err = llass->user.inq_cb (llass->user.inq_cb_value,
keyword, args,
inquire_cb_sendfnc, &sendfnc_ctx);
}
else
err = 0;
return err;
}
static gpgme_error_t
llass_status_handler (void *opaque, int fd)
{
gpgme_error_t err = 0;
engine_llass_t llass = opaque;
char *line;
size_t linelen;
do
{
err = assuan_read_line (llass->assuan_ctx, &line, &linelen);
if (err)
{
TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
"fd 0x%x: error reading assuan line: %s",
fd, gpg_strerror (err));
}
else if (linelen >= 2 && line[0] == 'D' && line[1] == ' ')
{
char *src = line + 2;
char *end = line + linelen;
char *dst = src;
linelen = 0;
while (src < end)
{
if (*src == '%' && src + 2 < end)
{
/* Handle escaped characters. */
++src;
*dst++ = _gpgme_hextobyte (src);
src += 2;
}
else
*dst++ = *src++;
linelen++;
}
src = line + 2;
if (linelen && llass->user.data_cb)
err = llass->user.data_cb (llass->user.data_cb_value,
src, linelen);
else
err = 0;
TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
"fd 0x%x: D inlinedata; status from cb: %s",
fd, (llass->user.data_cb ?
(err? gpg_strerror (err):"ok"):"no callback"));
}
else if (linelen >= 3
&& line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
&& (line[3] == '\0' || line[3] == ' '))
{
/* END received. Tell the data callback. */
if (llass->user.data_cb)
err = llass->user.data_cb (llass->user.data_cb_value, NULL, 0);
else
err = 0;
TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
"fd 0x%x: END line; status from cb: %s",
fd, (llass->user.data_cb ?
(err? gpg_strerror (err):"ok"):"no callback"));
}
else if (linelen > 2 && line[0] == 'S' && line[1] == ' ')
{
char *args;
char *src;
for (src=line+2; *src == ' '; src++)
;
args = strchr (src, ' ');
if (!args)
args = line + linelen; /* Let it point to an empty string. */
else
*(args++) = 0;
while (*args == ' ')
args++;
if (llass->user.status_cb)
err = llass->user.status_cb (llass->user.status_cb_value,
src, args);
else
err = 0;
TRACE3 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
"fd 0x%x: S line (%s) - status from cb: %s",
fd, line+2, (llass->user.status_cb ?
(err? gpg_strerror (err):"ok"):"no callback"));
}
else if (linelen >= 7
&& line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
&& line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
&& line[6] == 'E'
&& (line[7] == '\0' || line[7] == ' '))
{
char *src;
char *args;
for (src=line+7; *src == ' '; src++)
;
args = strchr (src, ' ');
if (!args)
args = line + linelen; /* Let it point to an empty string. */
else
*(args++) = 0;
while (*args == ' ')
args++;
err = inquire_cb (llass, src, args);
if (!err) /* Flush and send END. */
err = assuan_send_data (llass->assuan_ctx, NULL, 0);
}
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);
else
err = gpg_error (GPG_ERR_GENERAL);
TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
"fd 0x%x: ERR line: %s",
fd, err ? gpg_strerror (err) : "ok");
if (llass->result_cb)
err = llass->result_cb (llass->result_cb_value, err);
else
err = 0;
if (!err)
{
_gpgme_io_close (llass->status_cb.fd);
return 0;
}
}
else if (linelen >= 2
&& line[0] == 'O' && line[1] == 'K'
&& (line[2] == '\0' || line[2] == ' '))
{
TRACE1 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
"fd 0x%x: OK line", fd);
if (llass->result_cb)
err = llass->result_cb (llass->result_cb_value, 0);
else
err = 0;
if (!err)
{
_gpgme_io_close (llass->status_cb.fd);
return 0;
}
}
else
{
/* Comment line or invalid line. */
}
}
while (!err && assuan_pending_line (llass->assuan_ctx));
return err;
}
static gpgme_error_t
add_io_cb (engine_llass_t llass, iocb_data_t *iocbd, gpgme_io_cb_t handler)
{
gpgme_error_t err;
TRACE_BEG2 (DEBUG_ENGINE, "engine-assuan:add_io_cb", llass,
"fd %d, dir %d", iocbd->fd, iocbd->dir);
err = (*llass->io_cbs.add) (llass->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_llass_t llass, 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 (llass->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. */
llass->status_cb.fd = _gpgme_io_dup (fdlist[0]);
if (llass->status_cb.fd < 0)
return gpg_error_from_syserror ();
if (_gpgme_io_set_close_notify (llass->status_cb.fd,
close_notify_handler, llass))
{
_gpgme_io_close (llass->status_cb.fd);
llass->status_cb.fd = -1;
return gpg_error (GPG_ERR_GENERAL);
}
err = add_io_cb (llass, &llass->status_cb, llass_status_handler);
if (!err)
err = assuan_write_line (llass->assuan_ctx, command);
/* FIXME: If *command == '#' no answer is expected. */
if (!err)
llass_io_event (llass, GPGME_EVENT_START, NULL);
return err;
}
static gpgme_error_t
llass_transact (void *engine,
const char *command,
engine_assuan_result_cb_t result_cb,
void *result_cb_value,
gpgme_assuan_data_cb_t data_cb,
void *data_cb_value,
gpgme_assuan_inquire_cb_t inq_cb,
void *inq_cb_value,
gpgme_assuan_status_cb_t status_cb,
void *status_cb_value)
{
engine_llass_t llass = engine;
gpgme_error_t err;
if (!llass || !command || !*command)
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_value = data_cb_value;
llass->user.inq_cb = inq_cb;
llass->user.inq_cb_value = inq_cb_value;
llass->user.status_cb = status_cb;
llass->user.status_cb_value = status_cb_value;
err = start (llass, command);
return err;
}
static void
llass_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
{
engine_llass_t llass = engine;
llass->io_cbs = *io_cbs;
}
static void
llass_io_event (void *engine, gpgme_event_io_t type, void *type_data)
{
engine_llass_t llass = engine;
TRACE3 (DEBUG_ENGINE, "gpgme:llass_io_event", llass,
"event %p, type %d, type_data %p",
llass->io_cbs.event, type, type_data);
if (llass->io_cbs.event)
(*llass->io_cbs.event) (llass->io_cbs.event_priv, type, type_data);
}
struct engine_ops _gpgme_engine_ops_assuan =
{
/* Static functions. */
_gpgme_get_default_agent_socket,
llass_get_home_dir,
llass_get_version,
llass_get_req_version,
llass_new,
/* Member functions. */
llass_release,
NULL, /* reset */
NULL, /* set_status_handler */
NULL, /* set_command_handler */
NULL, /* set_colon_line_handler */
llass_set_locale,
NULL, /* decrypt */
NULL, /* delete */
NULL, /* edit */
NULL, /* encrypt */
NULL, /* encrypt_sign */
NULL, /* export */
NULL, /* export_ext */
NULL, /* genkey */
NULL, /* import */
NULL, /* keylist */
NULL, /* keylist_ext */
NULL, /* sign */
NULL, /* trustlist */
NULL, /* verify */
NULL, /* getauditlog */
llass_transact, /* opassuan_transact */
NULL, /* conf_load */
NULL, /* conf_save */
llass_set_io_cbs,
llass_io_event,
llass_cancel
};

View File

@ -1,5 +1,5 @@
/* engine-backend.h - A crypto backend for the engine interface. /* engine-backend.h - A crypto backend for the engine interface.
Copyright (C) 2002, 2003, 2004 g10 Code GmbH Copyright (C) 2002, 2003, 2004, 2009 g10 Code GmbH
This file is part of GPGME. This file is part of GPGME.
@ -14,9 +14,8 @@
Lesser General Public License for more details. Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public You should have received a copy of the GNU Lesser General Public
License along with this program; if not, write to the Free Software License along with this program; if not, see <http://www.gnu.org/licenses/>.
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA */
02111-1307, USA. */
#ifndef ENGINE_BACKEND_H #ifndef ENGINE_BACKEND_H
#define ENGINE_BACKEND_H #define ENGINE_BACKEND_H
@ -35,6 +34,11 @@ struct engine_ops
/* Return the default file name for the binary of this engine. */ /* Return the default file name for the binary of this engine. */
const char *(*get_file_name) (void); const char *(*get_file_name) (void);
/* Return the default home dir for the binary of this engine. If
this function pointer is not set, the standard default home dir
of the engine is used. */
const char *(*get_home_dir) (void);
/* Returns a malloced string containing the version of the engine /* Returns a malloced string containing the version of the engine
with the given binary file name (or the default if FILE_NAME is with the given binary file name (or the default if FILE_NAME is
NULL. */ NULL. */
@ -96,6 +100,16 @@ struct engine_ops
gpgme_data_t plaintext); gpgme_data_t plaintext);
gpgme_error_t (*getauditlog) (void *engine, gpgme_data_t output, gpgme_error_t (*getauditlog) (void *engine, gpgme_data_t output,
unsigned int flags); unsigned int flags);
gpgme_error_t (*opassuan_transact) (void *engine,
const char *command,
engine_assuan_result_cb_t result_cb,
void *result_cb_value,
gpgme_assuan_data_cb_t data_cb,
void *data_cb_value,
gpgme_assuan_inquire_cb_t inq_cb,
void *inq_cb_value,
gpgme_assuan_status_cb_t status_cb,
void *status_cb_value);
gpgme_error_t (*conf_load) (void *engine, gpgme_conf_comp_t *conf_p); gpgme_error_t (*conf_load) (void *engine, gpgme_conf_comp_t *conf_p);
gpgme_error_t (*conf_save) (void *engine, gpgme_conf_comp_t conf); gpgme_error_t (*conf_save) (void *engine, gpgme_conf_comp_t conf);
@ -114,5 +128,8 @@ extern struct engine_ops _gpgme_engine_ops_gpgsm; /* CMS. */
#ifdef ENABLE_GPGCONF #ifdef ENABLE_GPGCONF
extern struct engine_ops _gpgme_engine_ops_gpgconf; /* gpg-conf. */ extern struct engine_ops _gpgme_engine_ops_gpgconf; /* gpg-conf. */
#endif #endif
#ifdef ENABLE_GPGSM /* If this is enabled we also have assuan support. */
extern struct engine_ops _gpgme_engine_ops_assuan; /* Low-level Assuan. */
#endif
#endif /* ENGINE_BACKEND_H */ #endif /* ENGINE_BACKEND_H */

View File

@ -2158,6 +2158,7 @@ struct engine_ops _gpgme_engine_ops_gpg =
{ {
/* Static functions. */ /* Static functions. */
_gpgme_get_gpg_path, _gpgme_get_gpg_path,
NULL,
gpg_get_version, gpg_get_version,
gpg_get_req_version, gpg_get_req_version,
gpg_new, gpg_new,
@ -2184,6 +2185,7 @@ struct engine_ops _gpgme_engine_ops_gpg =
gpg_trustlist, gpg_trustlist,
gpg_verify, gpg_verify,
NULL, /* getauditlog */ NULL, /* getauditlog */
NULL, /* opassuan_transact */
NULL, /* conf_load */ NULL, /* conf_load */
NULL, /* conf_save */ NULL, /* conf_save */
gpg_set_io_cbs, gpg_set_io_cbs,

View File

@ -885,6 +885,7 @@ struct engine_ops _gpgme_engine_ops_gpgconf =
{ {
/* Static functions. */ /* Static functions. */
_gpgme_get_gpgconf_path, _gpgme_get_gpgconf_path,
NULL,
gpgconf_get_version, gpgconf_get_version,
gpgconf_get_req_version, gpgconf_get_req_version,
gpgconf_new, gpgconf_new,
@ -911,6 +912,7 @@ struct engine_ops _gpgme_engine_ops_gpgconf =
NULL, /* trustlist */ NULL, /* trustlist */
NULL, /* verify */ NULL, /* verify */
NULL, /* getauditlog */ NULL, /* getauditlog */
NULL, /* opassuan_transact */
gpgconf_conf_load, gpgconf_conf_load,
gpgconf_conf_save, gpgconf_conf_save,
gpgconf_set_io_cbs, gpgconf_set_io_cbs,

View File

@ -1922,6 +1922,7 @@ struct engine_ops _gpgme_engine_ops_gpgsm =
{ {
/* Static functions. */ /* Static functions. */
_gpgme_get_gpgsm_path, _gpgme_get_gpgsm_path,
NULL,
gpgsm_get_version, gpgsm_get_version,
gpgsm_get_req_version, gpgsm_get_req_version,
gpgsm_new, gpgsm_new,
@ -1952,6 +1953,7 @@ struct engine_ops _gpgme_engine_ops_gpgsm =
NULL, /* trustlist */ NULL, /* trustlist */
gpgsm_verify, gpgsm_verify,
gpgsm_getauditlog, gpgsm_getauditlog,
NULL, /* opassuan_transact */
NULL, /* conf_load */ NULL, /* conf_load */
NULL, /* conf_save */ NULL, /* conf_save */
gpgsm_set_io_cbs, gpgsm_set_io_cbs,

View File

@ -1,6 +1,6 @@
/* engine.c - GPGME engine support. /* engine.c - GPGME engine support.
Copyright (C) 2000 Werner Koch (dd9jn) Copyright (C) 2000 Werner Koch (dd9jn)
Copyright (C) 2001, 2002, 2003, 2004, 2006 g10 Code GmbH Copyright (C) 2001, 2002, 2003, 2004, 2006, 2009 g10 Code GmbH
This file is part of GPGME. This file is part of GPGME.
@ -15,9 +15,8 @@
Lesser General Public License for more details. Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public You should have received a copy of the GNU Lesser General Public
License along with this program; if not, write to the Free Software License along with this program; if not, see <http://www.gnu.org/licenses/>.
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA */
02111-1307, USA. */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include <config.h> #include <config.h>
@ -52,7 +51,12 @@ static struct engine_ops *engine_ops[] =
NULL, NULL,
#endif #endif
#ifdef ENABLE_GPGCONF #ifdef ENABLE_GPGCONF
&_gpgme_engine_ops_gpgconf /* gpg-conf. */ &_gpgme_engine_ops_gpgconf, /* gpg-conf. */
#else
NULL,
#endif
#ifdef ENABLE_GPGSM /* This indicates that we have assuan support. */
&_gpgme_engine_ops_assuan /* Low-Level Assuan. */
#else #else
NULL NULL
#endif #endif
@ -78,6 +82,20 @@ engine_get_file_name (gpgme_protocol_t proto)
} }
/* Get the standard home dir of the engine for PROTOCOL. */
static const char *
engine_get_home_dir (gpgme_protocol_t proto)
{
if (proto > DIM (engine_ops))
return NULL;
if (engine_ops[proto] && engine_ops[proto]->get_home_dir)
return (*engine_ops[proto]->get_home_dir) ();
else
return NULL;
}
/* Get a malloced string containing the version number of the engine /* Get a malloced string containing the version number of the engine
for PROTOCOL. */ for PROTOCOL. */
static char * static char *
@ -175,18 +193,22 @@ gpgme_get_engine_info (gpgme_engine_info_t *info)
gpgme_engine_info_t *lastp = &engine_info; gpgme_engine_info_t *lastp = &engine_info;
gpgme_protocol_t proto_list[] = { GPGME_PROTOCOL_OpenPGP, gpgme_protocol_t proto_list[] = { GPGME_PROTOCOL_OpenPGP,
GPGME_PROTOCOL_CMS, GPGME_PROTOCOL_CMS,
GPGME_PROTOCOL_GPGCONF }; GPGME_PROTOCOL_GPGCONF,
GPGME_PROTOCOL_ASSUAN };
unsigned int proto; unsigned int proto;
for (proto = 0; proto < DIM (proto_list); proto++) for (proto = 0; proto < DIM (proto_list); proto++)
{ {
const char *ofile_name = engine_get_file_name (proto_list[proto]); const char *ofile_name = engine_get_file_name (proto_list[proto]);
const char *ohome_dir = engine_get_home_dir (proto_list[proto]);
char *file_name; char *file_name;
char *home_dir;
if (!ofile_name) if (!ofile_name)
continue; continue;
file_name = strdup (ofile_name); file_name = strdup (ofile_name);
home_dir = ohome_dir? strdup (ohome_dir): NULL;
*lastp = malloc (sizeof (*engine_info)); *lastp = malloc (sizeof (*engine_info));
if (!*lastp || !file_name) if (!*lastp || !file_name)
@ -198,6 +220,8 @@ gpgme_get_engine_info (gpgme_engine_info_t *info)
if (file_name) if (file_name)
free (file_name); free (file_name);
if (home_dir)
free (home_dir);
UNLOCK (engine_info_lock); UNLOCK (engine_info_lock);
return gpg_error_from_errno (saved_errno); return gpg_error_from_errno (saved_errno);
@ -205,7 +229,7 @@ gpgme_get_engine_info (gpgme_engine_info_t *info)
(*lastp)->protocol = proto_list[proto]; (*lastp)->protocol = proto_list[proto];
(*lastp)->file_name = file_name; (*lastp)->file_name = file_name;
(*lastp)->home_dir = NULL; (*lastp)->home_dir = home_dir;
(*lastp)->version = engine_get_version (proto_list[proto], NULL); (*lastp)->version = engine_get_version (proto_list[proto], NULL);
(*lastp)->req_version = engine_get_req_version (proto_list[proto]); (*lastp)->req_version = engine_get_req_version (proto_list[proto]);
(*lastp)->next = NULL; (*lastp)->next = NULL;
@ -347,7 +371,20 @@ _gpgme_set_engine_info (gpgme_engine_info_t info, gpgme_protocol_t proto,
} }
} }
else else
new_home_dir = NULL; {
const char *ohome_dir = engine_get_home_dir (proto);
if (ohome_dir)
{
new_home_dir = strdup (ohome_dir);
if (!new_home_dir)
{
free (new_file_name);
return gpg_error_from_errno (errno);
}
}
else
new_home_dir = NULL;
}
/* Remove the old members. */ /* Remove the old members. */
assert (info->file_name); assert (info->file_name);
@ -730,6 +767,33 @@ _gpgme_engine_op_getauditlog (engine_t engine, gpgme_data_t output,
} }
gpgme_error_t
_gpgme_engine_op_assuan_transact (engine_t engine,
const char *command,
engine_assuan_result_cb_t result_cb,
void *result_cb_value,
gpgme_assuan_data_cb_t data_cb,
void *data_cb_value,
gpgme_assuan_inquire_cb_t inq_cb,
void *inq_cb_value,
gpgme_assuan_status_cb_t status_cb,
void *status_cb_value)
{
if (!engine)
return gpg_error (GPG_ERR_INV_VALUE);
if (!engine->ops->opassuan_transact)
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
return (*engine->ops->opassuan_transact) (engine->engine,
command,
result_cb, result_cb_value,
data_cb, data_cb_value,
inq_cb, inq_cb_value,
status_cb, status_cb_value);
}
gpgme_error_t gpgme_error_t
_gpgme_engine_op_conf_load (engine_t engine, gpgme_conf_comp_t *conf_p) _gpgme_engine_op_conf_load (engine_t engine, gpgme_conf_comp_t *conf_p)
{ {

View File

@ -35,6 +35,9 @@ typedef gpgme_error_t (*engine_command_handler_t) (void *priv,
gpgme_status_code_t code, gpgme_status_code_t code,
const char *keyword, const char *keyword,
int fd, int *processed); int fd, int *processed);
typedef gpgme_error_t (*engine_assuan_result_cb_t) (void *priv,
gpgme_error_t result);
/* Get a deep copy of the engine info and return it in INFO. */ /* Get a deep copy of the engine info and return it in INFO. */
gpgme_error_t _gpgme_engine_info_copy (gpgme_engine_info_t *r_info); gpgme_error_t _gpgme_engine_info_copy (gpgme_engine_info_t *r_info);
@ -126,6 +129,17 @@ gpgme_error_t _gpgme_engine_op_verify (engine_t engine, gpgme_data_t sig,
gpgme_error_t _gpgme_engine_op_getauditlog (engine_t engine, gpgme_error_t _gpgme_engine_op_getauditlog (engine_t engine,
gpgme_data_t output, gpgme_data_t output,
unsigned int flags); unsigned int flags);
gpgme_error_t _gpgme_engine_op_assuan_transact
(engine_t engine,
const char *command,
engine_assuan_result_cb_t result_cb,
void *result_cb_value,
gpgme_assuan_data_cb_t data_cb,
void *data_cb_value,
gpgme_assuan_inquire_cb_t inq_cb,
void *inq_cb_value,
gpgme_assuan_status_cb_t status_cb,
void *status_cb_value);
gpgme_error_t _gpgme_engine_op_conf_load (engine_t engine, gpgme_error_t _gpgme_engine_op_conf_load (engine_t engine,
gpgme_conf_comp_t *conf_p); gpgme_conf_comp_t *conf_p);

View File

@ -192,7 +192,9 @@ gpgme_set_protocol (gpgme_ctx_t ctx, gpgme_protocol_t protocol)
protocol, gpgme_get_protocol_name (protocol) protocol, gpgme_get_protocol_name (protocol)
? gpgme_get_protocol_name (protocol) : "unknown"); ? gpgme_get_protocol_name (protocol) : "unknown");
if (protocol != GPGME_PROTOCOL_OpenPGP && protocol != GPGME_PROTOCOL_CMS) if (protocol != GPGME_PROTOCOL_OpenPGP
&& protocol != GPGME_PROTOCOL_CMS
&& protocol != GPGME_PROTOCOL_ASSUAN)
return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
if (ctx->protocol != protocol) if (ctx->protocol != protocol)
@ -233,6 +235,9 @@ gpgme_get_protocol_name (gpgme_protocol_t protocol)
case GPGME_PROTOCOL_CMS: case GPGME_PROTOCOL_CMS:
return "CMS"; return "CMS";
case GPGME_PROTOCOL_ASSUAN:
return "Assuan";
case GPGME_PROTOCOL_UNKNOWN: case GPGME_PROTOCOL_UNKNOWN:
return "unknown"; return "unknown";

View File

@ -167,5 +167,9 @@ EXPORTS
gpgme_op_conf_save @130 gpgme_op_conf_save @130
gpgme_cancel_async @131 gpgme_cancel_async @131
gpgme_op_assuan_result @132
gpgme_op_assuan_transact_start @133
gpgme_op_assuan_transact @134
; END ; END

View File

@ -1,6 +1,6 @@
/* gpgme.h - Public interface to GnuPG Made Easy. -*- c -*- /* gpgme.h - Public interface to GnuPG Made Easy. -*- c -*-
Copyright (C) 2000 Werner Koch (dd9jn) Copyright (C) 2000 Werner Koch (dd9jn)
Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007 g10 Code GmbH Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009 g10 Code GmbH
This file is part of GPGME. This file is part of GPGME.
@ -301,6 +301,7 @@ typedef enum
GPGME_PROTOCOL_OpenPGP = 0, /* The default mode. */ GPGME_PROTOCOL_OpenPGP = 0, /* The default mode. */
GPGME_PROTOCOL_CMS = 1, GPGME_PROTOCOL_CMS = 1,
GPGME_PROTOCOL_GPGCONF = 2, /* Special code for gpgconf. */ GPGME_PROTOCOL_GPGCONF = 2, /* Special code for gpgconf. */
GPGME_PROTOCOL_ASSUAN = 3, /* Low-level access to an Assuan server. */
GPGME_PROTOCOL_UNKNOWN = 255 GPGME_PROTOCOL_UNKNOWN = 255
} }
gpgme_protocol_t; gpgme_protocol_t;
@ -746,6 +747,8 @@ typedef gpgme_error_t (*gpgme_edit_cb_t) (void *opaque,
gpgme_status_code_t status, gpgme_status_code_t status,
const char *args, int fd); const char *args, int fd);
/* Context management functions. */ /* Context management functions. */
@ -1656,6 +1659,51 @@ gpgme_error_t gpgme_op_getauditlog_start (gpgme_ctx_t ctx, gpgme_data_t output,
gpgme_error_t gpgme_op_getauditlog (gpgme_ctx_t ctx, gpgme_data_t output, gpgme_error_t gpgme_op_getauditlog (gpgme_ctx_t ctx, gpgme_data_t output,
unsigned int flags); unsigned int flags);
/* Low-level Assuan protocol access. */
typedef gpgme_error_t (*gpgme_assuan_data_cb_t)
(void *opaque, const void *data, size_t datalen);
struct _gpgme_assuan_sendfnc_ctx;
typedef struct _gpgme_assuan_sendfnc_ctx *gpgme_assuan_sendfnc_ctx_t;
typedef gpgme_error_t (*gpgme_assuan_sendfnc_t)
(gpgme_assuan_sendfnc_ctx_t ctx, const void *data, size_t datalen);
typedef gpgme_error_t (*gpgme_assuan_inquire_cb_t)
(void *opaque, const char *name, const char *args,
gpgme_assuan_sendfnc_t sendfnc,
gpgme_assuan_sendfnc_ctx_t sendfnc_ctx);
typedef gpgme_error_t (*gpgme_assuan_status_cb_t)
(void *opaque, const char *status, const char *args);
/* Return the result of the last Assuan command. */
gpgme_error_t gpgme_op_assuan_result (gpgme_ctx_t ctx);
/* Send the Assuan COMMAND and return results via the callbacks.
Asynchronous variant. */
gpgme_error_t gpgme_op_assuan_transact_start (gpgme_ctx_t ctx,
const char *command,
gpgme_assuan_data_cb_t data_cb,
void *data_cb_value,
gpgme_assuan_inquire_cb_t inq_cb,
void *inq_cb_value,
gpgme_assuan_status_cb_t stat_cb,
void *stat_cb_value);
/* Send the Assuan COMMAND and return results via the callbacks.
Synchronous variant. */
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 stat_cb,
void *stat_cb_value);
/* Interface to gpgconf(1). */ /* Interface to gpgconf(1). */

View File

@ -1,5 +1,5 @@
# libgpgme.vers - List of symbols to export. # libgpgme.vers - List of symbols to export.
# Copyright (C) 2002, 2004, 2005 g10 Code GmbH # Copyright (C) 2002, 2004, 2005, 2009 g10 Code GmbH
# #
# This file is part of GPGME. # This file is part of GPGME.
# #
@ -14,8 +14,7 @@
# GNU Lesser General Public License for more details. # GNU Lesser General Public License for more details.
# #
# You should have received a copy of the GNU Lesser General Public # You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the Free Software # License along with this program; if not, see <http://www.gnu.org/licenses/>.
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
#------------------------------------------------------- #-------------------------------------------------------
# Please remember to add new functions also to gpgme.def # Please remember to add new functions also to gpgme.def
@ -48,6 +47,10 @@ GPGME_1.1 {
gpgme_op_conf_save; gpgme_op_conf_save;
gpgme_cancel_async; gpgme_cancel_async;
gpgme_op_assuan_result;
gpgme_op_assuan_transact;
gpgme_op_assuan_transact_start;
}; };

158
src/opassuan.c Normal file
View File

@ -0,0 +1,158 @@
/* opassuan.c - Low-level Assuan operations.
Copyright (C) 2009 g10 Code GmbH
This file is part of GPGME.
GPGME is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of
the License, or (at your option) any later version.
GPGME is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include "gpgme.h"
#include "context.h"
#include "ops.h"
#include "util.h"
typedef struct
{
/* The result of the assuan command with 0 for OK and an error value
for ERR. */
gpgme_error_t 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 = result;
return 0;
}
gpgme_error_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;
if (err)
return err;
if (!opd)
return gpg_error (GPG_ERR_INTERNAL);
return opd->result;
}
static gpgme_error_t
opassuan_start (gpgme_ctx_t ctx, int synchronous,
const char *command,
gpgme_assuan_data_cb_t data_cb,
void *data_cb_value,
gpgme_assuan_inquire_cb_t inq_cb,
void *inq_cb_value,
gpgme_assuan_status_cb_t status_cb,
void *status_cb_value)
{
gpgme_error_t err;
void *hook;
op_data_t opd;
if (!command || !*command)
return gpg_error (GPG_ERR_INV_VALUE);
/* The flag value 256 is used to suppress an engine reset. This is
required to keep the connection running. */
err = _gpgme_op_reset (ctx, ((synchronous&255) | 256));
if (err)
return err;
err = _gpgme_op_data_lookup (ctx, OPDATA_ASSUAN, &hook, sizeof (*opd), NULL);
opd = hook;
if (err)
return err;
opd->result = gpg_error (GPG_ERR_UNFINISHED);
return _gpgme_engine_op_assuan_transact (ctx->engine, command,
result_cb, ctx,
data_cb, data_cb_value,
inq_cb, inq_cb_value,
status_cb, status_cb_value);
}
/* XXXX. This is the asynchronous variant. */
gpgme_error_t
gpgme_op_assuan_transact_start (gpgme_ctx_t ctx,
const char *command,
gpgme_assuan_data_cb_t data_cb,
void *data_cb_value,
gpgme_assuan_inquire_cb_t inq_cb,
void *inq_cb_value,
gpgme_assuan_status_cb_t status_cb,
void *status_cb_value)
{
return opassuan_start (ctx, 0, command,
data_cb, data_cb_value,
inq_cb, inq_cb_value,
status_cb, status_cb_value);
}
/* XXXX. This is the synchronous variant. */
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_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 (ctx);
return err;
}

View File

@ -35,6 +35,11 @@ const char *_gpgme_get_gpgconf_path (void);
int _gpgme_get_conf_int (const char *key, int *value); int _gpgme_get_conf_int (const char *key, int *value);
void _gpgme_allow_set_foregound_window (pid_t pid); void _gpgme_allow_set_foregound_window (pid_t pid);
/*-- dirinfo.c --*/
const char *_gpgme_get_default_homedir (void);
const char *_gpgme_get_default_agent_socket (void);
/*-- replacement functions in <funcname>.c --*/ /*-- replacement functions in <funcname>.c --*/
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
@ -111,4 +116,10 @@ int _gpgme_mkstemp (int *fd, char **name);
const char *_gpgme_get_w32spawn_path (void); const char *_gpgme_get_w32spawn_path (void);
#endif #endif
/*-- Error codes not yet available in current gpg-error.h. --*/
#ifndef GPG_ERR_UNFINISHED
#define GPG_ERR_UNFINISHED 199
#endif
#endif /* UTIL_H */ #endif /* UTIL_H */

View File

@ -179,7 +179,8 @@ gpgme_check_version (const char *req_version)
automagically initialize the debug system with out the locks automagically initialize the debug system with out the locks
being initialized and missing the assuan log level setting. */ being initialized and missing the assuan log level setting. */
TRACE2 (DEBUG_INIT, "gpgme_check_version: ", 0, TRACE2 (DEBUG_INIT, "gpgme_check_version: ", 0,
"req_version=%s, VERSION=%s", req_version, VERSION); "req_version=%s, VERSION=%s",
req_version? req_version:"(null)", VERSION);
return _gpgme_compare_versions (VERSION, req_version) ? VERSION : NULL; return _gpgme_compare_versions (VERSION, req_version) ? VERSION : NULL;
} }

View File

@ -1,3 +1,9 @@
2009-01-26 Werner Koch <wk@g10code.com>
* opassuan/: New.
* opassuan/Makefile.am: New.
* opassuan/t-command.c: New.
2008-12-03 Marcus Brinkmann <marcus@g10code.de> 2008-12-03 Marcus Brinkmann <marcus@g10code.de>
* Makefile.am (INCLUDES): Fix path to include file. * Makefile.am (INCLUDES): Fix path to include file.

View File

@ -15,8 +15,7 @@
# Public License for more details. # Public License for more details.
# #
# You should have received a copy of the GNU Lesser General Public # You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the Free Software # License along with this program; if not, see <http://www.gnu.org/licenses/>.
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
## Process this file with automake to produce Makefile.in ## Process this file with automake to produce Makefile.in
@ -40,7 +39,7 @@ gpgtests =
endif endif
if RUN_GPGSM_TESTS if RUN_GPGSM_TESTS
gpgsmtests = gpgsm gpgsmtests = gpgsm opassuan
else else
gpgsmtests = gpgsmtests =
endif endif

View File

@ -0,0 +1,35 @@
# Copyright (C) 2009 g10 Code GmbH
#
# This file is part of GPGME.
#
# GPGME is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as
# published by the Free Software Foundation; either version 2.1 of the
# License, or (at your option) any later version.
#
# GPGME is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
# Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, see <http://www.gnu.org/licenses/>.
## Process this file with automake to produce Makefile.in
TESTS_ENVIRONMENT = GNUPGHOME=$(abs_builddir) GPG_AGENT_INFO=
noinst_HEADERS =
TESTS =
EXTRA_DIST =
INCLUDES = -I$(top_builddir)/src
AM_CPPFLAGS = @GPG_ERROR_CFLAGS@
LDADD = ../../src/libgpgme.la
noinst_PROGRAMS = $(TESTS) t-command
DISTCLEANFILES =

121
tests/opassuan/t-command.c Normal file
View File

@ -0,0 +1,121 @@
/* t-command.c - Regression test.
Copyright (C) 2009 g10 Code GmbH
This file is part of GPGME.
GPGME is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of
the License, or (at your option) any later version.
GPGME is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <locale.h>
#include <gpgme.h>
#define fail_if_err(err) \
do \
{ \
if (err) \
{ \
fprintf (stderr, "%s:%d: %s: %s (%d.%d)\n", \
__FILE__, __LINE__, gpg_strsource (err), \
gpg_strerror (err), \
gpg_err_source (err), gpg_err_code (err)); \
exit (1); \
} \
} \
while (0)
static gpg_error_t
data_cb (void *opaque, const void *data, size_t datalen)
{
printf ("DATA_CB: datalen=%d\n", (int)datalen);
return 0;
}
static gpg_error_t
inq_cb (void *opaque, const char *name, const char *args,
gpgme_assuan_sendfnc_t sendfnc,
gpgme_assuan_sendfnc_ctx_t sendfnc_value)
{
printf ("INQ_CB: name=`%s' args=`%s'\n", name, args);
return 0;
}
static gpg_error_t
status_cb (void *opaque, const char *status, const char *args)
{
printf ("STATUS_CB: status=`%s' args=`%s'\n", status, args);
return 0;
}
int
main (int argc, char **argv)
{
gpgme_error_t err;
gpgme_ctx_t ctx;
const char *command;
gpgme_check_version (NULL);
#ifndef HAVE_W32_SYSTEM
setlocale (LC_ALL, "");
gpgme_set_locale (NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL));
gpgme_set_locale (NULL, LC_MESSAGES, setlocale (LC_MESSAGES, NULL));
#endif
if (argc)
{
argc--;
argv++;
}
command = argc? *argv : "NOP";
err = gpgme_new (&ctx);
fail_if_err (err);
err = gpgme_set_protocol (ctx, GPGME_PROTOCOL_ASSUAN);
fail_if_err (err);
err = gpgme_op_assuan_transact (ctx, command,
data_cb, NULL,
inq_cb, NULL,
status_cb, NULL);
fail_if_err (err);
err = gpgme_op_assuan_result (ctx);
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);
return 0;
}