diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/ChangeLog | 63 | ||||
| -rw-r--r-- | src/Makefile.am | 5 | ||||
| -rw-r--r-- | src/context.h | 4 | ||||
| -rw-r--r-- | src/dirinfo.c | 189 | ||||
| -rw-r--r-- | src/engine-assuan.c | 744 | ||||
| -rw-r--r-- | src/engine-backend.h | 25 | ||||
| -rw-r--r-- | src/engine-gpg.c | 2 | ||||
| -rw-r--r-- | src/engine-gpgconf.c | 2 | ||||
| -rw-r--r-- | src/engine-gpgsm.c | 2 | ||||
| -rw-r--r-- | src/engine.c | 80 | ||||
| -rw-r--r-- | src/engine.h | 14 | ||||
| -rw-r--r-- | src/gpgme.c | 7 | ||||
| -rw-r--r-- | src/gpgme.def | 4 | ||||
| -rw-r--r-- | src/gpgme.h.in | 50 | ||||
| -rw-r--r-- | src/libgpgme.vers | 9 | ||||
| -rw-r--r-- | src/opassuan.c | 158 | ||||
| -rw-r--r-- | src/util.h | 11 | ||||
| -rw-r--r-- | src/version.c | 3 | 
18 files changed, 1335 insertions, 37 deletions
| diff --git a/src/ChangeLog b/src/ChangeLog index 95dc804c..bf272bca 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,36 @@ +2009-01-26  Werner Koch  <[email protected]> + +	* 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  <[email protected]>  	* rungpg.c: Rename to engine-gpg.c @@ -803,7 +836,7 @@  	* 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. @@ -868,7 +901,7 @@  2005-11-27  Marcus Brinkmann  <[email protected]>  	* 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  <[email protected]> @@ -1818,7 +1851,7 @@  	* 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  <[email protected]> @@ -3707,7 +3740,7 @@  2002-09-28  Marcus Brinkmann  <[email protected]>  	* 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. @@ -3820,7 +3853,7 @@  	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  <[email protected]> @@ -3993,7 +4026,7 @@  	* 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 @@ -4003,14 +4036,14 @@  2002-07-25  Marcus Brinkmann  <[email protected]> -	* 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. @@ -5060,7 +5093,7 @@  2001-12-19  Marcus Brinkmann  <[email protected]> -	* 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. @@ -5597,7 +5630,7 @@  	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  <[email protected]> @@ -5679,7 +5712,7 @@  	* 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 <[email protected]> +2001-05-01  José Carlos García Sogo <[email protected]>  	* encrypt.c (gpgme_op_encrypt_start): Deleted the assert ( !c->gpg )  	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.  	* 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 diff --git a/src/Makefile.am b/src/Makefile.am index 5a7ca59b..22254f0c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -78,7 +78,7 @@ system_components_not_extra =  endif  if HAVE_GPGSM -gpgsm_components = engine-gpgsm.c +gpgsm_components = engine-gpgsm.c engine-assuan.c  else  gpgsm_components =  endif @@ -105,9 +105,10 @@ main_sources =								\  	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)					\ diff --git a/src/context.h b/src/context.h index ed5d8502..76aeb337 100644 --- a/src/context.h +++ b/src/context.h @@ -36,7 +36,7 @@ typedef enum    {      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; @@ -51,7 +51,7 @@ struct ctx_op_data    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.  */ diff --git a/src/dirinfo.c b/src/dirinfo.c new file mode 100644 index 00000000..45f09c09 --- /dev/null +++ b/src/dirinfo.c @@ -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); +} + diff --git a/src/engine-assuan.c b/src/engine-assuan.c new file mode 100644 index 00000000..fb748689 --- /dev/null +++ b/src/engine-assuan.c @@ -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 +  }; diff --git a/src/engine-backend.h b/src/engine-backend.h index 2e2ef5ec..d656d9d6 100644 --- a/src/engine-backend.h +++ b/src/engine-backend.h @@ -1,5 +1,5 @@  /* 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. @@ -14,9 +14,8 @@     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 @@ -35,6 +34,11 @@ struct engine_ops    /* 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.  */ @@ -96,6 +100,16 @@ struct engine_ops  			   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); @@ -114,5 +128,8 @@ extern struct engine_ops _gpgme_engine_ops_gpgsm;	/* CMS.  */  #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 */ diff --git a/src/engine-gpg.c b/src/engine-gpg.c index 8be76000..e4334d14 100644 --- a/src/engine-gpg.c +++ b/src/engine-gpg.c @@ -2158,6 +2158,7 @@ struct engine_ops _gpgme_engine_ops_gpg =    {      /* Static functions.  */      _gpgme_get_gpg_path, +    NULL,                     gpg_get_version,      gpg_get_req_version,      gpg_new, @@ -2184,6 +2185,7 @@ struct engine_ops _gpgme_engine_ops_gpg =      gpg_trustlist,      gpg_verify,      NULL,		/* getauditlog */ +    NULL,               /* opassuan_transact */      NULL,		/* conf_load */      NULL,		/* conf_save */      gpg_set_io_cbs, diff --git a/src/engine-gpgconf.c b/src/engine-gpgconf.c index 3d107d45..d1f27c2b 100644 --- a/src/engine-gpgconf.c +++ b/src/engine-gpgconf.c @@ -885,6 +885,7 @@ struct engine_ops _gpgme_engine_ops_gpgconf =    {      /* Static functions.  */      _gpgme_get_gpgconf_path, +    NULL,      gpgconf_get_version,      gpgconf_get_req_version,      gpgconf_new, @@ -911,6 +912,7 @@ struct engine_ops _gpgme_engine_ops_gpgconf =      NULL,		/* trustlist */      NULL,		/* verify */      NULL,		/* getauditlog */ +    NULL,               /* opassuan_transact */      gpgconf_conf_load,      gpgconf_conf_save,      gpgconf_set_io_cbs, diff --git a/src/engine-gpgsm.c b/src/engine-gpgsm.c index 936ac2e2..7179b3c4 100644 --- a/src/engine-gpgsm.c +++ b/src/engine-gpgsm.c @@ -1922,6 +1922,7 @@ struct engine_ops _gpgme_engine_ops_gpgsm =    {      /* Static functions.  */      _gpgme_get_gpgsm_path, +    NULL,      gpgsm_get_version,      gpgsm_get_req_version,      gpgsm_new, @@ -1952,6 +1953,7 @@ struct engine_ops _gpgme_engine_ops_gpgsm =      NULL,		/* trustlist */      gpgsm_verify,      gpgsm_getauditlog, +    NULL,               /* opassuan_transact */      NULL,		/* conf_load */      NULL,		/* conf_save */      gpgsm_set_io_cbs, diff --git a/src/engine.c b/src/engine.c index cf3fe9fe..87d39392 100644 --- a/src/engine.c +++ b/src/engine.c @@ -1,6 +1,6 @@  /* 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. @@ -15,9 +15,8 @@     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> @@ -52,7 +51,12 @@ static struct engine_ops *engine_ops[] =      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 @@ -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     for PROTOCOL.  */  static char * @@ -175,18 +193,22 @@ gpgme_get_engine_info (gpgme_engine_info_t *info)        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) @@ -198,6 +220,8 @@ gpgme_get_engine_info (gpgme_engine_info_t *info)  	      if (file_name)  		free (file_name); +	      if (home_dir) +		free (home_dir);  	      UNLOCK (engine_info_lock);  	      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)->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; @@ -347,7 +371,20 @@ _gpgme_set_engine_info (gpgme_engine_info_t info, gpgme_protocol_t proto,  	}      }    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); @@ -731,6 +768,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_engine_op_conf_load (engine_t engine, gpgme_conf_comp_t *conf_p)  {    if (!engine) diff --git a/src/engine.h b/src/engine.h index e67399ec..a043b3e2 100644 --- a/src/engine.h +++ b/src/engine.h @@ -35,6 +35,9 @@ typedef gpgme_error_t (*engine_command_handler_t) (void *priv,  						   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); @@ -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_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); diff --git a/src/gpgme.c b/src/gpgme.c index 7fbe5c38..99d27ce6 100644 --- a/src/gpgme.c +++ b/src/gpgme.c @@ -192,7 +192,9 @@ gpgme_set_protocol (gpgme_ctx_t ctx, gpgme_protocol_t protocol)  	      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) @@ -233,6 +235,9 @@ gpgme_get_protocol_name (gpgme_protocol_t protocol)      case GPGME_PROTOCOL_CMS:        return "CMS"; +    case GPGME_PROTOCOL_ASSUAN: +      return "Assuan"; +      case GPGME_PROTOCOL_UNKNOWN:        return "unknown"; diff --git a/src/gpgme.def b/src/gpgme.def index c54549eb..835177ef 100644 --- a/src/gpgme.def +++ b/src/gpgme.def @@ -167,5 +167,9 @@ EXPORTS      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 diff --git a/src/gpgme.h.in b/src/gpgme.h.in index 9dc230cb..4b68d801 100644 --- a/src/gpgme.h.in +++ b/src/gpgme.h.in @@ -1,6 +1,6 @@  /* 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. @@ -301,6 +301,7 @@ typedef enum      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; @@ -746,6 +747,8 @@ typedef gpgme_error_t (*gpgme_edit_cb_t) (void *opaque,  					  gpgme_status_code_t status,  					  const char *args, int fd); + +  /* 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,                                       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).  */ diff --git a/src/libgpgme.vers b/src/libgpgme.vers index f0de90ef..1653a63c 100644 --- a/src/libgpgme.vers +++ b/src/libgpgme.vers @@ -1,5 +1,5 @@  # 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.  # @@ -14,8 +14,7 @@  # 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 @@ -48,6 +47,10 @@ GPGME_1.1 {      gpgme_op_conf_save;      gpgme_cancel_async; + +    gpgme_op_assuan_result;     +    gpgme_op_assuan_transact;     +    gpgme_op_assuan_transact_start;      }; diff --git a/src/opassuan.c b/src/opassuan.c new file mode 100644 index 00000000..f07dade6 --- /dev/null +++ b/src/opassuan.c @@ -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; +} + @@ -35,6 +35,11 @@ const char *_gpgme_get_gpgconf_path (void);  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); + +  /*-- replacement functions in <funcname>.c --*/  #ifdef HAVE_CONFIG_H @@ -111,4 +116,10 @@ int _gpgme_mkstemp (int *fd, char **name);  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 */ diff --git a/src/version.c b/src/version.c index 084f2a52..879e2f3e 100644 --- a/src/version.c +++ b/src/version.c @@ -179,7 +179,8 @@ gpgme_check_version (const char *req_version)       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;  } | 
