+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>
Release GPGME 1.1.8.
* configure.ac (AC_INIT): Bump version to 0.3.3.
* jnlib/Makefile.am: Rever to older version that includes xmalloc
- but not dotlock and some other files. Reported by Stéphane
- Corthésy.
+ but not dotlock and some other files. Reported by Stéphane
+ Corthésy.
2002-02-10 Marcus Brinkmann <marcus@g10code.de>
# Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
-# License along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+# License along with this program; if not, see <http://www.gnu.org/licenses/>.
## Process this file with automake to produce Makefile.in
+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)
------------------------------------------------
echo "Running autoconf${FORCE} ..."
$AUTOCONF${FORCE}
-echo "You may now run \"./configure --enable-maintainer-mode && make\"."
+echo "You may now run:
+ ./configure --enable-maintainer-mode && make
+"
# 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
# version for the real release, set the my_issvn macro to no.
-m4_define(my_version, [1.1.8])
-m4_define(my_issvn, [no])
+m4_define(my_version, [1.1.9])
+m4_define(my_issvn, [yes])
m4_define([svn_revision], m4_esyscmd([echo -n $( (svn info 2>/dev/null \
|| echo 'Revision: 0')|sed -n '/^Revision:/ {s/[^0-9]//gp;q;}')]))
# Create config files
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
src/versioninfo.rc
src/gpgme.h)
+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>
* rungpg.c: Rename to engine-gpg.c
* engine.c (gpgme_engine_check_version): Reimplemented to allow
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
it is set.
2005-11-27 Marcus Brinkmann <marcus@g10code.de>
* 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>
* gpgme-config.in (gpg_error_libs): Quote GPG_ERROR_CFLAGS and
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>
2002-09-28 Marcus Brinkmann <marcus@g10code.de>
* 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
operation type in invocation of _gpgme_op_reset.
variables encrypt_info and encrypt_info_len.
* trustlist.c (gpgme_op_trustlist_start): Set colon line handler.
* 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>
* vasprintf.c: Update to more recent libiberty version.
* debug.h: Replace #elsif with #elif.
- Submitted by Stéphane Corthésy:
+ Submitted by Stéphane Corthésy:
* util.h (vasprintf): Correct prototype.
* encrypt-sign.c: Include <stddef.h>.
(encrypt_sign_status_handler): Change type of ENCRYPT_INFO_LEN to
2002-07-25 Marcus Brinkmann <marcus@g10code.de>
- * wait.c (fdt_global): Make static. Reported by Stéphane
- Corthésy.
+ * wait.c (fdt_global): Make static. Reported by Stéphane
+ Corthésy.
* 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
- by Stéphane Corthésy.
+ by Stéphane Corthésy.
(gpgme_key_get_string_attr): Add GPGME_ATTR_SIG_SUMMARY case to
silence gcc warning.
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.
callers to use this function without a check for tmp_key.
* 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>
* version.c (gpgme_check_engine): Stop version number parsing at
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 )
line, because it gave an error if another operation had been made
* rungpg.c (_gpgme_gpg_spawn): Use new function to get GPG's path.
* signers.c (gpgme_signers_add): Ooops, one should test code and
- not just write it; the newarr was not assigned. Thanks to José
- for pointing this out. Hmmm, still not tested, why shoudl a coder
+ not just write it; the newarr was not assigned. Thanks to José
+ for pointing this out. Hmmm, still not tested, why should a coder
test his fix :-)
* w32-io.c: Does now use reader threads, so that we can use
endif
if HAVE_GPGSM
-gpgsm_components = engine-gpgsm.c
+gpgsm_components = engine-gpgsm.c engine-assuan.c
else
gpgsm_components =
endif
sign.c passphrase.c progress.c \
key.c keylist.c trust-item.c trustlist.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 \
$(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
libgpgme_la_SOURCES = $(main_sources) \
{
OPDATA_DECRYPT, OPDATA_SIGN, OPDATA_ENCRYPT, OPDATA_PASSPHRASE,
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 type;
/* 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);
/* The hook that points to the operation data. */
--- /dev/null
+/* 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);
+}
+
--- /dev/null
+/* 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"
+
+\f
+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);
+
+
+
+
+\f
+/* 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";
+}
+
+\f
+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
+ };
/* 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.
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
- License along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- 02111-1307, USA. */
+ License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
#ifndef ENGINE_BACKEND_H
#define ENGINE_BACKEND_H
/* Return the default file name for the binary of this engine. */
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
with the given binary file name (or the default if FILE_NAME is
NULL. */
gpgme_data_t plaintext);
gpgme_error_t (*getauditlog) (void *engine, gpgme_data_t output,
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_save) (void *engine, gpgme_conf_comp_t conf);
#ifdef ENABLE_GPGCONF
extern struct engine_ops _gpgme_engine_ops_gpgconf; /* gpg-conf. */
#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 */
{
/* Static functions. */
_gpgme_get_gpg_path,
+ NULL,
gpg_get_version,
gpg_get_req_version,
gpg_new,
gpg_trustlist,
gpg_verify,
NULL, /* getauditlog */
+ NULL, /* opassuan_transact */
NULL, /* conf_load */
NULL, /* conf_save */
gpg_set_io_cbs,
{
/* Static functions. */
_gpgme_get_gpgconf_path,
+ NULL,
gpgconf_get_version,
gpgconf_get_req_version,
gpgconf_new,
NULL, /* trustlist */
NULL, /* verify */
NULL, /* getauditlog */
+ NULL, /* opassuan_transact */
gpgconf_conf_load,
gpgconf_conf_save,
gpgconf_set_io_cbs,
{
/* Static functions. */
_gpgme_get_gpgsm_path,
+ NULL,
gpgsm_get_version,
gpgsm_get_req_version,
gpgsm_new,
NULL, /* trustlist */
gpgsm_verify,
gpgsm_getauditlog,
+ NULL, /* opassuan_transact */
NULL, /* conf_load */
NULL, /* conf_save */
gpgsm_set_io_cbs,
/* engine.c - GPGME engine support.
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.
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
- License along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- 02111-1307, USA. */
+ License along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
#ifdef HAVE_CONFIG_H
#include <config.h>
NULL,
#endif
#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
NULL
#endif
}
+/* 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
for PROTOCOL. */
static char *
gpgme_engine_info_t *lastp = &engine_info;
gpgme_protocol_t proto_list[] = { GPGME_PROTOCOL_OpenPGP,
GPGME_PROTOCOL_CMS,
- GPGME_PROTOCOL_GPGCONF };
+ GPGME_PROTOCOL_GPGCONF,
+ GPGME_PROTOCOL_ASSUAN };
unsigned int proto;
for (proto = 0; proto < DIM (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 *home_dir;
if (!ofile_name)
continue;
file_name = strdup (ofile_name);
+ home_dir = ohome_dir? strdup (ohome_dir): NULL;
*lastp = malloc (sizeof (*engine_info));
if (!*lastp || !file_name)
if (file_name)
free (file_name);
+ if (home_dir)
+ free (home_dir);
UNLOCK (engine_info_lock);
return gpg_error_from_errno (saved_errno);
(*lastp)->protocol = proto_list[proto];
(*lastp)->file_name = file_name;
- (*lastp)->home_dir = NULL;
+ (*lastp)->home_dir = home_dir;
(*lastp)->version = engine_get_version (proto_list[proto], NULL);
(*lastp)->req_version = engine_get_req_version (proto_list[proto]);
(*lastp)->next = NULL;
}
}
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. */
assert (info->file_name);
}
+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_engine_op_conf_load (engine_t engine, gpgme_conf_comp_t *conf_p)
{
gpgme_status_code_t code,
const char *keyword,
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. */
gpgme_error_t _gpgme_engine_info_copy (gpgme_engine_info_t *r_info);
gpgme_error_t _gpgme_engine_op_getauditlog (engine_t engine,
gpgme_data_t output,
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_conf_comp_t *conf_p);
protocol, gpgme_get_protocol_name (protocol)
? 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));
if (ctx->protocol != protocol)
case GPGME_PROTOCOL_CMS:
return "CMS";
+ case GPGME_PROTOCOL_ASSUAN:
+ return "Assuan";
+
case GPGME_PROTOCOL_UNKNOWN:
return "unknown";
gpgme_op_conf_save @130
gpgme_cancel_async @131
+
+ gpgme_op_assuan_result @132
+ gpgme_op_assuan_transact_start @133
+ gpgme_op_assuan_transact @134
; END
/* gpgme.h - Public interface to GnuPG Made Easy. -*- c -*-
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.
GPGME_PROTOCOL_OpenPGP = 0, /* The default mode. */
GPGME_PROTOCOL_CMS = 1,
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_t;
gpgme_status_code_t status,
const char *args, int fd);
+
+
\f
/* Context management functions. */
gpgme_error_t gpgme_op_getauditlog (gpgme_ctx_t ctx, gpgme_data_t output,
unsigned int flags);
+
+\f
+/* 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);
+
+
\f
/* Interface to gpgconf(1). */
# 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.
#
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
-# License along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+# License along with this program; if not, see <http://www.gnu.org/licenses/>.
#-------------------------------------------------------
# Please remember to add new functions also to gpgme.def
gpgme_op_conf_save;
gpgme_cancel_async;
+
+ gpgme_op_assuan_result;
+ gpgme_op_assuan_transact;
+ gpgme_op_assuan_transact_start;
};
--- /dev/null
+/* 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;
+
+
+
+\f
+/* 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;
+}
+
int _gpgme_get_conf_int (const char *key, int *value);
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);
+
+
\f
/*-- replacement functions in <funcname>.c --*/
#ifdef HAVE_CONFIG_H
const char *_gpgme_get_w32spawn_path (void);
#endif
+/*-- Error codes not yet available in current gpg-error.h. --*/
+#ifndef GPG_ERR_UNFINISHED
+#define GPG_ERR_UNFINISHED 199
+#endif
+
+
#endif /* UTIL_H */
automagically initialize the debug system with out the locks
being initialized and missing the assuan log level setting. */
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;
}
+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>
* Makefile.am (INCLUDES): Fix path to include file.
# Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
-# License along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+# License along with this program; if not, see <http://www.gnu.org/licenses/>.
## Process this file with automake to produce Makefile.in
endif
if RUN_GPGSM_TESTS
-gpgsmtests = gpgsm
+gpgsmtests = gpgsm opassuan
else
gpgsmtests =
endif
--- /dev/null
+# 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 =
+
--- /dev/null
+/* 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;
+}
+