diff --git a/src/engine-assuan.c b/src/engine-assuan.c
index 681be62c..c4a84a39 100644
--- a/src/engine-assuan.c
+++ b/src/engine-assuan.c
@@ -1,791 +1,791 @@
 /* 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>
 #ifdef HAVE_SYS_TYPES_H
 # include <sys/types.h>
 #endif
 #include <assert.h>
 #ifdef HAVE_UNISTD_H
 # include <unistd.h>
 #endif
 #ifdef HAVE_LOCALE_H
 #include <locale.h>
 #endif
 #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;
 
   /* Hack for old opassuan.c interface, see there the result struct.  */
   gpg_error_t last_op_err;
 
   /* 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;
 
 
 gpg_error_t _gpgme_engine_assuan_last_op_err (void *engine)
 {
   engine_llass_t llass = engine;
   return llass->last_op_err;
 }
 
 
 /* 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.0");
+  return NULL;
 }
 
 
 static const char *
 llass_get_req_version (void)
 {
-  return "1.0.0";
+  return NULL;
 }
 
 
 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_release (llass->assuan_ctx);
       llass->assuan_ctx = NULL;
     }
 
   return 0;
 }
 
 
 static gpgme_error_t
 llass_cancel_op (void *engine)
 {
   engine_llass_t llass = engine;
 
   if (!llass)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (llass->status_cb.fd != -1)
     _gpgme_io_close (llass->status_cb.fd);
 
   return 0;
 }
 
 
 static void
 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,
            const char *version)
 {
   gpgme_error_t err = 0;
   engine_llass_t llass;
   char *optstr;
 
   (void)version; /* Not yet used.  */
 
   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.  */
       /* Note that wk promised to write a regression test if this
          parser will be extended.  */
       if (!strncmp (home_dir, "GPG_AGENT", 9)
           && (!home_dir[9] || home_dir[9] == ' '))
         llass->opt.gpg_agent = 1;
     }
 
   err = assuan_new_ext (&llass->assuan_ctx, GPG_ERR_SOURCE_GPGME,
 			&_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
 			NULL);
   if (err)
     goto leave;
   assuan_ctx_set_system_hooks (llass->assuan_ctx, &_gpgme_assuan_system_hooks);
   assuan_set_flag (llass->assuan_ctx, ASSUAN_CONVEY_COMMENTS, 1);
 
   err = assuan_socket_connect (llass->assuan_ctx, file_name, 0, 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));
 
       /* Even though isatty() returns 1, ttyname_r() may fail in many
 	 ways, e.g., when /dev/pts is not accessible under chroot.  */
       if (!rc)
 	{
 	  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 (0)
     ;
 #ifdef LC_CTYPE
   else 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;
     }
 #endif
 #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_syserror ();
   else
     {
       err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL,
 			     NULL, NULL, NULL, NULL);
       free (optstr);
     }
   return err;
 }
 
 
 /* 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_foreground_window ((pid_t)strtoul (args, NULL, 10));
     }
 
   if (llass->user.inq_cb)
     {
       gpgme_data_t data = NULL;
 
       err = llass->user.inq_cb (llass->user.inq_cb_value,
                                 keyword, args, &data);
       if (!err && data)
         {
           /* FIXME: Returning data is not yet implemented.  However we
              need to allow the caller to cleanup his data object.
              Thus we run the callback in finish mode immediately.  */
           err = llass->user.inq_cb (llass->user.inq_cb_value,
                                     NULL, NULL, &data);
         }
     }
   else
     err = 0;
 
   return err;
 }
 
 
 static gpgme_error_t
 llass_status_handler (void *opaque, int fd)
 {
   struct io_cb_data *data = (struct io_cb_data *) opaque;
   engine_llass_t llass = (engine_llass_t) data->handler_value;
   gpgme_error_t err = 0;
   char *line;
   size_t linelen;
 
   do
     {
       err = assuan_read_line (llass->assuan_ctx, &line, &linelen);
       if (err)
 	{
 	  /* Reading a full line may not be possible when
 	     communicating over a socket in nonblocking mode.  In this
 	     case, we are done for now.  */
 	  if (gpg_err_code (err) == GPG_ERR_EAGAIN)
 	    {
 	      TRACE1 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
 		      "fd 0x%x: EAGAIN reading assuan line (ignored)", fd);
 	      err = 0;
 	      continue;
 	    }
 
 	  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);
 
           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);
 
           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);
 
           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 (gpg_err_code (err) == GPG_ERR_ASS_CANCELED)
             {
               /* Flush and send CANcel.  */
               err = assuan_send_data (llass->assuan_ctx, NULL, 1);
             }
         }
       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");
 
 	  /* Command execution errors are not fatal, as we use
 	     a session based protocol.  */
 	  data->op_err = err;
 	  llass->last_op_err = err;
 
 	  /* The caller will do the rest (namely, call cancel_op,
 	     which closes status_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);
 
 	  llass->last_op_err = 0;
 
 	  _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;
   assuan_fd_t afdlist[5];
   int fdlist[5];
   int nfds;
   int i;
 
   /* 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 */,
                                 afdlist, DIM (afdlist));
   if (nfds < 1)
     return gpg_error (GPG_ERR_GENERAL);	/* FIXME */
   /* For now... */
   for (i = 0; i < nfds; i++)
     fdlist[i] = (int) afdlist[i];
 
   /* 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,
                 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->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_cb */
     NULL,               /* set_status_handler */
     NULL,		/* set_command_handler */
     NULL,               /* set_colon_line_handler */
     llass_set_locale,
     NULL,		/* set_protocol */
     NULL,               /* decrypt */
     NULL,               /* decrypt_verify */
     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,
     llass_cancel_op,
     NULL,               /* passwd */
     NULL,               /* set_pinentry_mode */
     NULL                /* opspawn */
   };
diff --git a/src/engine-spawn.c b/src/engine-spawn.c
index c01b50e2..e2ee8ba6 100644
--- a/src/engine-spawn.c
+++ b/src/engine-spawn.c
@@ -1,477 +1,477 @@
 /* engine-spawn.c - Run an arbitrary program
    Copyright (C) 2014 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 <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <assert.h>
 #include <errno.h>
 #ifdef HAVE_UNISTD_H
 # include <unistd.h>
 #endif
 #ifdef HAVE_LOCALE_H
 #include <locale.h>
 #endif
 
 #include "gpgme.h"
 #include "util.h"
 #include "ops.h"
 #include "wait.h"
 #include "context.h"  /*temp hack until we have GpmeData methods to do I/O */
 #include "priv-io.h"
 #include "sema.h"
 #include "debug.h"
 
 #include "engine-backend.h"
 
 
 /* This type is used to build a list of data sources/sinks.  */
 struct datalist_s
 {
   struct datalist_s *next;
   gpgme_data_t data;  /* The data object. */
   int inbound;        /* True if this is used for reading from the peer.  */
   int dup_to;         /* The fd used by the peer.  */
 };
 
 
 struct fd_data_map_s
 {
   gpgme_data_t data;
   int inbound;  /* True if this is used for reading from the peer. */
   int dup_to;   /* Dup the fd to that one.  */
   int fd;       /* The fd to use.  */
   int peer_fd;  /* The other side of the pipe. */
   void *tag;    /* Tag used by the I/O callback.  */
 };
 
 
 struct engine_spawn
 {
   struct datalist_s *arglist;
   struct datalist_s **argtail;
 
   struct fd_data_map_s *fd_data_map;
 
   struct gpgme_io_cbs io_cbs;
 };
 typedef struct engine_spawn *engine_spawn_t;
 
 
 static void engspawn_io_event (void *engine,
                                gpgme_event_io_t type, void *type_data);
 static gpgme_error_t engspawn_cancel (void *engine);
 
 
 
 static void
 close_notify_handler (int fd, void *opaque)
 {
   engine_spawn_t esp = opaque;
   int i;
 
   assert (fd != -1);
 
   if (esp->fd_data_map)
     {
       for (i = 0; esp->fd_data_map[i].data; i++)
 	{
 	  if (esp->fd_data_map[i].fd == fd)
 	    {
 	      if (esp->fd_data_map[i].tag)
 		(*esp->io_cbs.remove) (esp->fd_data_map[i].tag);
 	      esp->fd_data_map[i].fd = -1;
 	      break;
             }
 	  if (esp->fd_data_map[i].peer_fd == fd)
 	    {
 	      esp->fd_data_map[i].peer_fd = -1;
 	      break;
             }
         }
     }
 }
 
 
 static gpgme_error_t
 add_data (engine_spawn_t esp, gpgme_data_t data, int dup_to, int inbound)
 {
   struct datalist_s *a;
 
   assert (esp);
   assert (data);
 
   a = malloc (sizeof *a);
   if (!a)
     return gpg_error_from_syserror ();
   a->next = NULL;
   a->data = data;
   a->inbound = inbound;
   a->dup_to = dup_to;
   *esp->argtail = a;
   esp->argtail = &a->next;
   return 0;
 }
 
 
 static void
 free_fd_data_map (struct fd_data_map_s *fd_data_map)
 {
   int i;
 
   if (!fd_data_map)
     return;
 
   for (i = 0; fd_data_map[i].data; i++)
     {
       if (fd_data_map[i].fd != -1)
 	_gpgme_io_close (fd_data_map[i].fd);
       if (fd_data_map[i].peer_fd != -1)
 	_gpgme_io_close (fd_data_map[i].peer_fd);
       /* Don't release data because this is only a reference.  */
     }
   free (fd_data_map);
 }
 
 
 static gpgme_error_t
 build_fd_data_map (engine_spawn_t esp)
 {
   struct datalist_s *a;
   size_t datac;
   int fds[2];
 
   for (datac = 0, a = esp->arglist; a; a = a->next)
     if (a->data)
       datac++;
 
   free_fd_data_map (esp->fd_data_map);
   esp->fd_data_map = calloc (datac + 1, sizeof *esp->fd_data_map);
   if (!esp->fd_data_map)
     return gpg_error_from_syserror ();
 
   for (datac = 0, a = esp->arglist; a; a = a->next)
     {
       assert (a->data);
 
       if (_gpgme_io_pipe (fds, a->inbound ? 1 : 0) == -1)
         {
           free (esp->fd_data_map);
           esp->fd_data_map = NULL;
           return gpg_error_from_syserror ();
         }
       if (_gpgme_io_set_close_notify (fds[0], close_notify_handler, esp)
           || _gpgme_io_set_close_notify (fds[1], close_notify_handler, esp))
         {
           /* FIXME: Need error cleanup.  */
           return gpg_error (GPG_ERR_GENERAL);
         }
 
       esp->fd_data_map[datac].inbound = a->inbound;
       if (a->inbound)
         {
           esp->fd_data_map[datac].fd       = fds[0];
           esp->fd_data_map[datac].peer_fd  = fds[1];
         }
       else
         {
           esp->fd_data_map[datac].fd       = fds[1];
           esp->fd_data_map[datac].peer_fd  = fds[0];
         }
       esp->fd_data_map[datac].data    = a->data;
       esp->fd_data_map[datac].dup_to  = a->dup_to;
       datac++;
     }
 
   return 0;
 }
 
 
 static gpgme_error_t
 add_io_cb (engine_spawn_t esp, int fd, int dir, gpgme_io_cb_t handler,
            void *data, void **tag)
 {
   gpgme_error_t err;
 
   err = (*esp->io_cbs.add) (esp->io_cbs.add_priv, fd, dir, handler, data, tag);
   if (err)
     return err;
   if (!dir) /* Fixme: Kludge around poll() problem.  */
     err = _gpgme_io_set_nonblocking (fd);
   return err;
 }
 
 
 static gpgme_error_t
 engspawn_start (engine_spawn_t esp, const char *file, const char *argv[],
                 unsigned int flags)
 {
   gpgme_error_t err;
   int i, n;
   int status;
   struct spawn_fd_item_s *fd_list;
   pid_t pid;
   unsigned int spflags;
   const char *save_argv0 = NULL;
 
   if (!esp || !file || !argv || !argv[0])
     return gpg_error (GPG_ERR_INV_VALUE);
 
   spflags = 0;
   if ((flags & GPGME_SPAWN_DETACHED))
     spflags |= IOSPAWN_FLAG_DETACHED;
   if ((flags & GPGME_SPAWN_ALLOW_SET_FG))
     spflags |= IOSPAWN_FLAG_ALLOW_SET_FG;
 
 
   err = build_fd_data_map (esp);
   if (err)
     return err;
 
   n = 0;
   for (i = 0; esp->fd_data_map[i].data; i++)
     n++;
   fd_list = calloc (n+1, sizeof *fd_list);
   if (!fd_list)
     return gpg_error_from_syserror ();
 
   /* Build the fd list for the child.  */
   n = 0;
   for (i = 0; esp->fd_data_map[i].data; i++)
     {
       fd_list[n].fd = esp->fd_data_map[i].peer_fd;
       fd_list[n].dup_to = esp->fd_data_map[i].dup_to;
       n++;
     }
   fd_list[n].fd = -1;
   fd_list[n].dup_to = -1;
 
   if (argv[0] && !*argv[0])
     {
       save_argv0 = argv[0];
       argv[0] = _gpgme_get_basename (file);
     }
   status = _gpgme_io_spawn (file, (char * const *)argv, spflags,
                             fd_list, NULL, NULL, &pid);
   if (save_argv0)
     argv[0] = save_argv0;
   free (fd_list);
   if (status == -1)
     return gpg_error_from_syserror ();
 
   for (i = 0; esp->fd_data_map[i].data; i++)
     {
       err = add_io_cb (esp, esp->fd_data_map[i].fd,
                        esp->fd_data_map[i].inbound,
                        esp->fd_data_map[i].inbound
                        ? _gpgme_data_inbound_handler
                        : _gpgme_data_outbound_handler,
                        esp->fd_data_map[i].data, &esp->fd_data_map[i].tag);
       if (err)
         return err;  /* FIXME: kill the child */
     }
 
   engspawn_io_event (esp, GPGME_EVENT_START, NULL);
 
   return 0;
 }
 
 
 
 /*
     Public functions
  */
 
 static const char *
 engspawn_get_file_name (void)
 {
   return "/nonexistent";
 }
 
 
 static char *
 engspawn_get_version (const char *file_name)
 {
   (void)file_name;
-  return strdup ("1.0.0");
+  return NULL;
 }
 
 
 static const char *
 engspawn_get_req_version (void)
 {
-  return "1.0.0";
+  return NULL;
 }
 
 
 static gpgme_error_t
 engspawn_new (void **engine, const char *file_name, const char *home_dir,
               const char *version)
 {
   engine_spawn_t esp;
 
   (void)file_name;
   (void)home_dir;
   (void)version;
 
   esp = calloc (1, sizeof *esp);
   if (!esp)
     return gpg_error_from_syserror ();
 
   esp->argtail = &esp->arglist;
   *engine = esp;
   return 0;
 }
 
 
 static void
 engspawn_release (void *engine)
 {
   engine_spawn_t esp = engine;
 
   if (!esp)
     return;
 
   engspawn_cancel (engine);
 
   while (esp->arglist)
     {
       struct datalist_s *next = esp->arglist->next;
 
       free (esp->arglist);
       esp->arglist = next;
     }
 
   free (esp);
 }
 
 
 static void
 engspawn_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
 {
   engine_spawn_t esp = engine;
 
   esp->io_cbs = *io_cbs;
 }
 
 
 static void
 engspawn_io_event (void *engine, gpgme_event_io_t type, void *type_data)
 {
   engine_spawn_t esp = engine;
 
   TRACE3 (DEBUG_ENGINE, "gpgme:engspawn_io_event", esp,
           "event %p, type %d, type_data %p",
           esp->io_cbs.event, type, type_data);
   if (esp->io_cbs.event)
     (*esp->io_cbs.event) (esp->io_cbs.event_priv, type, type_data);
 }
 
 
 static gpgme_error_t
 engspawn_cancel (void *engine)
 {
   engine_spawn_t esp = engine;
 
   if (!esp)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (esp->fd_data_map)
     {
       free_fd_data_map (esp->fd_data_map);
       esp->fd_data_map = NULL;
     }
 
   return 0;
 }
 
 
 static gpgme_error_t
 engspawn_op_spawn (void *engine,
                    const char *file, const char *argv[],
                    gpgme_data_t datain,
                    gpgme_data_t dataout, gpgme_data_t dataerr,
                    unsigned int flags)
 {
   engine_spawn_t esp = engine;
   gpgme_error_t err = 0;
 
   if (datain)
     err = add_data (esp, datain, 0, 0);
   if (!err && dataout)
     err = add_data (esp, dataout, 1, 1);
   if (!err && dataerr)
     err = add_data (esp, dataerr, 2, 1);
 
   if (!err)
     err = engspawn_start (esp, file, argv, flags);
 
   return err;
 }
 
 
 
 struct engine_ops _gpgme_engine_ops_spawn =
   {
     /* Static functions.  */
     engspawn_get_file_name,
     NULL,               /* get_home_dir */
     engspawn_get_version,
     engspawn_get_req_version,
     engspawn_new,
 
     /* Member functions.  */
     engspawn_release,
     NULL,		/* reset */
     NULL,               /* set_status_cb */
     NULL,		/* set_status_handler */
     NULL,		/* set_command_handler */
     NULL,		/* set_colon_line_handler */
     NULL,		/* set_locale */
     NULL,		/* set_protocol */
     NULL,		/* decrypt */
     NULL,		/* decrypt_verify */
     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 */
     NULL,               /* opassuan_transact */
     NULL,		/* conf_load */
     NULL,		/* conf_save */
     engspawn_set_io_cbs,
     engspawn_io_event,	/* io_event */
     engspawn_cancel,	/* cancel */
     NULL,               /* cancel_op */
     NULL,               /* passwd */
     NULL,               /* set_pinentry_mode */
     engspawn_op_spawn   /* opspawn */
   };
diff --git a/src/engine-uiserver.c b/src/engine-uiserver.c
index 1869ff3f..de12f2b1 100644
--- a/src/engine-uiserver.c
+++ b/src/engine-uiserver.c
@@ -1,1371 +1,1372 @@
 /* engine-uiserver.c - Uiserver engine.
    Copyright (C) 2000 Werner Koch (dd9jn)
    Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009 g10 Code GmbH
 
    This file is part of GPGME.
 
    GPGME is free software; you can redistribute it and/or modify it
    under the terms of the GNU Lesser General Public License as
    published by the Free Software Foundation; either version 2.1 of
    the License, or (at your option) any later version.
 
    GPGME is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.
 
    You should have received a copy of the GNU Lesser General Public
    License along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
    02111-1307, USA.  */
 
 /* Peculiar: Use special keys from email address for recipient and
    signer (==sender).  Use no data objects with encryption for
    prep_encrypt.  */
 
 #if HAVE_CONFIG_H
 #include <config.h>
 #endif
 
 #include <stdlib.h>
 #include <string.h>
 #ifdef HAVE_SYS_TYPES_H
 # include <sys/types.h>
 #endif
 #include <assert.h>
 #ifdef HAVE_UNISTD_H
 # include <unistd.h>
 #endif
 #include <locale.h>
 #include <fcntl.h> /* FIXME */
 #include <errno.h>
 
 #include "gpgme.h"
 #include "util.h"
 #include "ops.h"
 #include "wait.h"
 #include "priv-io.h"
 #include "sema.h"
 #include "data.h"
 
 #include "assuan.h"
 #include "debug.h"
 
 #include "engine-backend.h"
 
 
 typedef struct
 {
   int fd;	/* FD we talk about.  */
   int server_fd;/* Server FD for this connection.  */
   int dir;	/* Inbound/Outbound, maybe given implicit?  */
   void *data;	/* Handler-specific data.  */
   void *tag;	/* ID from the user for gpgme_remove_io_callback.  */
   char server_fd_str[15]; /* Same as SERVER_FD but as a string.  We
                              need this because _gpgme_io_fd2str can't
                              be used on a closed descriptor.  */
 } iocb_data_t;
 
 
 struct engine_uiserver
 {
   assuan_context_t assuan_ctx;
 
   int lc_ctype_set;
   int lc_messages_set;
   gpgme_protocol_t protocol;
 
   iocb_data_t status_cb;
 
   /* Input, output etc are from the servers perspective.  */
   iocb_data_t input_cb;
   gpgme_data_t input_helper_data;  /* Input helper data object.  */
   void *input_helper_memory;       /* Input helper memory block.  */
 
   iocb_data_t output_cb;
 
   iocb_data_t message_cb;
 
   struct
   {
     engine_status_handler_t fnc;
     void *fnc_value;
     gpgme_status_cb_t mon_cb;
     void *mon_cb_value;
   } status;
 
   struct
   {
     engine_colon_line_handler_t fnc;
     void *fnc_value;
     struct
     {
       char *line;
       int linesize;
       int linelen;
     } attic;
     int any; /* any data line seen */
   } colon;
 
   gpgme_data_t inline_data;  /* Used to collect D lines.  */
 
   struct gpgme_io_cbs io_cbs;
 };
 
 typedef struct engine_uiserver *engine_uiserver_t;
 
 
 static void uiserver_io_event (void *engine,
                             gpgme_event_io_t type, void *type_data);
 
 
 
 static char *
 uiserver_get_version (const char *file_name)
 {
-  return strdup ("1.0.0");
+  (void)file_name;
+  return NULL;
 }
 
 
 static const char *
 uiserver_get_req_version (void)
 {
-  return "1.0.0";
+  return NULL;
 }
 
 
 static void
 close_notify_handler (int fd, void *opaque)
 {
   engine_uiserver_t uiserver = opaque;
 
   assert (fd != -1);
   if (uiserver->status_cb.fd == fd)
     {
       if (uiserver->status_cb.tag)
 	(*uiserver->io_cbs.remove) (uiserver->status_cb.tag);
       uiserver->status_cb.fd = -1;
       uiserver->status_cb.tag = NULL;
     }
   else if (uiserver->input_cb.fd == fd)
     {
       if (uiserver->input_cb.tag)
 	(*uiserver->io_cbs.remove) (uiserver->input_cb.tag);
       uiserver->input_cb.fd = -1;
       uiserver->input_cb.tag = NULL;
       if (uiserver->input_helper_data)
         {
           gpgme_data_release (uiserver->input_helper_data);
           uiserver->input_helper_data = NULL;
         }
       if (uiserver->input_helper_memory)
         {
           free (uiserver->input_helper_memory);
           uiserver->input_helper_memory = NULL;
         }
     }
   else if (uiserver->output_cb.fd == fd)
     {
       if (uiserver->output_cb.tag)
 	(*uiserver->io_cbs.remove) (uiserver->output_cb.tag);
       uiserver->output_cb.fd = -1;
       uiserver->output_cb.tag = NULL;
     }
   else if (uiserver->message_cb.fd == fd)
     {
       if (uiserver->message_cb.tag)
 	(*uiserver->io_cbs.remove) (uiserver->message_cb.tag);
       uiserver->message_cb.fd = -1;
       uiserver->message_cb.tag = NULL;
     }
 }
 
 
 /* This is the default inquiry callback.  We use it to handle the
    Pinentry notifications.  */
 static gpgme_error_t
 default_inq_cb (engine_uiserver_t uiserver, const char *line)
 {
   if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17]))
     {
       _gpgme_allow_set_foreground_window ((pid_t)strtoul (line+17, NULL, 10));
     }
 
   return 0;
 }
 
 
 static gpgme_error_t
 uiserver_cancel (void *engine)
 {
   engine_uiserver_t uiserver = engine;
 
   if (!uiserver)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (uiserver->status_cb.fd != -1)
     _gpgme_io_close (uiserver->status_cb.fd);
   if (uiserver->input_cb.fd != -1)
     _gpgme_io_close (uiserver->input_cb.fd);
   if (uiserver->output_cb.fd != -1)
     _gpgme_io_close (uiserver->output_cb.fd);
   if (uiserver->message_cb.fd != -1)
     _gpgme_io_close (uiserver->message_cb.fd);
 
   if (uiserver->assuan_ctx)
     {
       assuan_release (uiserver->assuan_ctx);
       uiserver->assuan_ctx = NULL;
     }
 
   return 0;
 }
 
 
 static void
 uiserver_release (void *engine)
 {
   engine_uiserver_t uiserver = engine;
 
   if (!uiserver)
     return;
 
   uiserver_cancel (engine);
 
   free (uiserver->colon.attic.line);
   free (uiserver);
 }
 
 
 static gpgme_error_t
 uiserver_new (void **engine, const char *file_name, const char *home_dir,
               const char *version)
 {
   gpgme_error_t err = 0;
   engine_uiserver_t uiserver;
   char *dft_display = NULL;
   char dft_ttyname[64];
   char *dft_ttytype = NULL;
   char *optstr;
 
   (void)version; /* Not yet used.  */
 
   uiserver = calloc (1, sizeof *uiserver);
   if (!uiserver)
     return gpg_error_from_syserror ();
 
   uiserver->protocol = GPGME_PROTOCOL_DEFAULT;
   uiserver->status_cb.fd = -1;
   uiserver->status_cb.dir = 1;
   uiserver->status_cb.tag = 0;
   uiserver->status_cb.data = uiserver;
 
   uiserver->input_cb.fd = -1;
   uiserver->input_cb.dir = 0;
   uiserver->input_cb.tag = 0;
   uiserver->input_cb.server_fd = -1;
   *uiserver->input_cb.server_fd_str = 0;
   uiserver->output_cb.fd = -1;
   uiserver->output_cb.dir = 1;
   uiserver->output_cb.tag = 0;
   uiserver->output_cb.server_fd = -1;
   *uiserver->output_cb.server_fd_str = 0;
   uiserver->message_cb.fd = -1;
   uiserver->message_cb.dir = 0;
   uiserver->message_cb.tag = 0;
   uiserver->message_cb.server_fd = -1;
   *uiserver->message_cb.server_fd_str = 0;
 
   uiserver->status.fnc = 0;
   uiserver->colon.fnc = 0;
   uiserver->colon.attic.line = 0;
   uiserver->colon.attic.linesize = 0;
   uiserver->colon.attic.linelen = 0;
   uiserver->colon.any = 0;
 
   uiserver->inline_data = NULL;
 
   uiserver->io_cbs.add = NULL;
   uiserver->io_cbs.add_priv = NULL;
   uiserver->io_cbs.remove = NULL;
   uiserver->io_cbs.event = NULL;
   uiserver->io_cbs.event_priv = NULL;
 
   err = assuan_new_ext (&uiserver->assuan_ctx, GPG_ERR_SOURCE_GPGME,
 			&_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
 			NULL);
   if (err)
     goto leave;
   assuan_ctx_set_system_hooks (uiserver->assuan_ctx,
 			       &_gpgme_assuan_system_hooks);
 
   err = assuan_socket_connect (uiserver->assuan_ctx,
 			       file_name ?
 			       file_name : _gpgme_get_default_uisrv_socket (),
 			       0, ASSUAN_SOCKET_SERVER_FDPASSING);
   if (err)
     goto leave;
 
   err = _gpgme_getenv ("DISPLAY", &dft_display);
   if (err)
     goto leave;
   if (dft_display)
     {
       if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
         {
 	  err = gpg_error_from_syserror ();
 	  free (dft_display);
 	  goto leave;
 	}
       free (dft_display);
 
       err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL, NULL,
 			     NULL, NULL, NULL);
       free (optstr);
       if (err)
 	goto leave;
     }
 
   if (isatty (1))
     {
       int rc;
 
       rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
 
       /* Even though isatty() returns 1, ttyname_r() may fail in many
 	 ways, e.g., when /dev/pts is not accessible under chroot.  */
       if (!rc)
 	{
 	  if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
 	    {
 	      err = gpg_error_from_syserror ();
 	      goto leave;
 	    }
 	  err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL, NULL,
 				 NULL, NULL, NULL);
 	  free (optstr);
 	  if (err)
 	    goto leave;
 
 	  err = _gpgme_getenv ("TERM", &dft_ttytype);
 	  if (err)
 	    goto leave;
 	  if (dft_ttytype)
 	    {
 	      if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
 		{
 		  err = gpg_error_from_syserror ();
 		  free (dft_ttytype);
 		  goto leave;
 		}
 	      free (dft_ttytype);
 
 	      err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL,
 				     NULL, NULL, NULL, NULL);
 	      free (optstr);
 	      if (err)
 		goto leave;
 	    }
 	}
     }
 
 #ifdef HAVE_W32_SYSTEM
   /* Under Windows we need to use AllowSetForegroundWindow.  Tell
      uiserver to tell us when it needs it.  */
   if (!err)
     {
       err = assuan_transact (uiserver->assuan_ctx, "OPTION allow-pinentry-notify",
                              NULL, NULL, NULL, NULL, NULL, NULL);
       if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
         err = 0; /* This is a new feature of uiserver.  */
     }
 #endif /*HAVE_W32_SYSTEM*/
 
  leave:
   if (err)
     uiserver_release (uiserver);
   else
     *engine = uiserver;
 
   return err;
 }
 
 
 static gpgme_error_t
 uiserver_set_locale (void *engine, int category, const char *value)
 {
   engine_uiserver_t uiserver = engine;
   gpgme_error_t err;
   char *optstr;
   char *catstr;
 
   /* FIXME: If value is NULL, we need to reset the option to default.
      But we can't do this.  So we error out here.  UISERVER needs support
      for this.  */
   if (category == LC_CTYPE)
     {
       catstr = "lc-ctype";
       if (!value && uiserver->lc_ctype_set)
 	return gpg_error (GPG_ERR_INV_VALUE);
       if (value)
 	uiserver->lc_ctype_set = 1;
     }
 #ifdef LC_MESSAGES
   else if (category == LC_MESSAGES)
     {
       catstr = "lc-messages";
       if (!value && uiserver->lc_messages_set)
 	return gpg_error (GPG_ERR_INV_VALUE);
       if (value)
 	uiserver->lc_messages_set = 1;
     }
 #endif /* LC_MESSAGES */
   else
     return gpg_error (GPG_ERR_INV_VALUE);
 
   /* FIXME: Reset value to default.  */
   if (!value)
     return 0;
 
   if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
     err = gpg_error_from_syserror ();
   else
     {
       err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL,
 			     NULL, NULL, NULL, NULL);
       free (optstr);
     }
 
   return err;
 }
 
 
 static gpgme_error_t
 uiserver_set_protocol (void *engine, gpgme_protocol_t protocol)
 {
   engine_uiserver_t uiserver = engine;
 
   if (protocol != GPGME_PROTOCOL_OpenPGP
       && protocol != GPGME_PROTOCOL_CMS
       && protocol != GPGME_PROTOCOL_DEFAULT)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   uiserver->protocol = protocol;
   return 0;
 }
 
 
 static gpgme_error_t
 uiserver_assuan_simple_command (engine_uiserver_t uiserver, char *cmd,
                                 engine_status_handler_t status_fnc,
                                 void *status_fnc_value)
 {
   assuan_context_t ctx = uiserver->assuan_ctx;
   gpg_error_t err;
   char *line;
   size_t linelen;
 
   err = assuan_write_line (ctx, cmd);
   if (err)
     return err;
 
   do
     {
       err = assuan_read_line (ctx, &line, &linelen);
       if (err)
 	return err;
 
       if (*line == '#' || !linelen)
 	continue;
 
       if (linelen >= 2
 	  && line[0] == 'O' && line[1] == 'K'
 	  && (line[2] == '\0' || line[2] == ' '))
 	return 0;
       else if (linelen >= 4
 	  && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
 	  && line[3] == ' ')
 	err = atoi (&line[4]);
       else if (linelen >= 2
 	       && line[0] == 'S' && line[1] == ' ')
 	{
 	  char *rest;
 	  gpgme_status_code_t r;
 
 	  rest = strchr (line + 2, ' ');
 	  if (!rest)
 	    rest = line + linelen; /* set to an empty string */
 	  else
 	    *(rest++) = 0;
 
 	  r = _gpgme_parse_status (line + 2);
           if (uiserver->status.mon_cb && r != GPGME_STATUS_PROGRESS)
             {
               /* Note that we call the monitor even if we do
                * not know the status code (r < 0).  */
               err = uiserver->status.mon_cb (uiserver->status.mon_cb_value,
                                              line + 2, rest);
             }
 
           if (err)
             ;
 	  else if (r >= 0 && status_fnc)
 	    err = status_fnc (status_fnc_value, r, rest);
 	  else
 	    err = gpg_error (GPG_ERR_GENERAL);
 	}
       else
 	err = gpg_error (GPG_ERR_GENERAL);
     }
   while (!err);
 
   return err;
 }
 
 
 typedef enum { INPUT_FD, OUTPUT_FD, MESSAGE_FD } fd_type_t;
 
 #define COMMANDLINELEN 40
 static gpgme_error_t
 uiserver_set_fd (engine_uiserver_t uiserver, fd_type_t fd_type, const char *opt)
 {
   gpg_error_t err = 0;
   char line[COMMANDLINELEN];
   char *which;
   iocb_data_t *iocb_data;
   int dir;
 
   switch (fd_type)
     {
     case INPUT_FD:
       which = "INPUT";
       iocb_data = &uiserver->input_cb;
       break;
 
     case OUTPUT_FD:
       which = "OUTPUT";
       iocb_data = &uiserver->output_cb;
       break;
 
     case MESSAGE_FD:
       which = "MESSAGE";
       iocb_data = &uiserver->message_cb;
       break;
 
     default:
       return gpg_error (GPG_ERR_INV_VALUE);
     }
 
   dir = iocb_data->dir;
 
   /* We try to short-cut the communication by giving UISERVER direct
      access to the file descriptor, rather than using a pipe.  */
   iocb_data->server_fd = _gpgme_data_get_fd (iocb_data->data);
   if (iocb_data->server_fd < 0)
     {
       int fds[2];
 
       if (_gpgme_io_pipe (fds, 0) < 0)
 	return gpg_error_from_syserror ();
 
       iocb_data->fd = dir ? fds[0] : fds[1];
       iocb_data->server_fd = dir ? fds[1] : fds[0];
 
       if (_gpgme_io_set_close_notify (iocb_data->fd,
 				      close_notify_handler, uiserver))
 	{
 	  err = gpg_error (GPG_ERR_GENERAL);
 	  goto leave_set_fd;
 	}
     }
 
   err = assuan_sendfd (uiserver->assuan_ctx, iocb_data->server_fd);
   if (err)
     goto leave_set_fd;
 
   _gpgme_io_close (iocb_data->server_fd);
   iocb_data->server_fd = -1;
 
   if (opt)
     snprintf (line, COMMANDLINELEN, "%s FD %s", which, opt);
   else
     snprintf (line, COMMANDLINELEN, "%s FD", which);
 
   err = uiserver_assuan_simple_command (uiserver, line, NULL, NULL);
 
  leave_set_fd:
   if (err)
     {
       _gpgme_io_close (iocb_data->fd);
       iocb_data->fd = -1;
       if (iocb_data->server_fd != -1)
         {
           _gpgme_io_close (iocb_data->server_fd);
           iocb_data->server_fd = -1;
         }
     }
 
   return err;
 }
 
 
 static const char *
 map_data_enc (gpgme_data_t d)
 {
   switch (gpgme_data_get_encoding (d))
     {
     case GPGME_DATA_ENCODING_NONE:
       break;
     case GPGME_DATA_ENCODING_BINARY:
       return "--binary";
     case GPGME_DATA_ENCODING_BASE64:
       return "--base64";
     case GPGME_DATA_ENCODING_ARMOR:
       return "--armor";
     default:
       break;
     }
   return NULL;
 }
 
 
 static gpgme_error_t
 status_handler (void *opaque, int fd)
 {
   struct io_cb_data *data = (struct io_cb_data *) opaque;
   engine_uiserver_t uiserver = (engine_uiserver_t) data->handler_value;
   gpgme_error_t err = 0;
   char *line;
   size_t linelen;
 
   do
     {
       err = assuan_read_line (uiserver->assuan_ctx, &line, &linelen);
       if (err)
 	{
 	  /* Try our best to terminate the connection friendly.  */
 	  /*	  assuan_write_line (uiserver->assuan_ctx, "BYE"); */
           TRACE3 (DEBUG_CTX, "gpgme:status_handler", uiserver,
 		  "fd 0x%x: error from assuan (%d) getting status line : %s",
                   fd, err, gpg_strerror (err));
 	}
       else if (linelen >= 3
 	       && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
 	       && (line[3] == '\0' || line[3] == ' '))
 	{
 	  if (line[3] == ' ')
 	    err = atoi (&line[4]);
 	  if (! err)
 	    err = gpg_error (GPG_ERR_GENERAL);
           TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
 		  "fd 0x%x: ERR line - mapped to: %s",
                   fd, err ? gpg_strerror (err) : "ok");
 	  /* Try our best to terminate the connection friendly.  */
 	  /*	  assuan_write_line (uiserver->assuan_ctx, "BYE"); */
 	}
       else if (linelen >= 2
 	       && line[0] == 'O' && line[1] == 'K'
 	       && (line[2] == '\0' || line[2] == ' '))
 	{
 	  if (uiserver->status.fnc)
 	    err = uiserver->status.fnc (uiserver->status.fnc_value,
 				     GPGME_STATUS_EOF, "");
 
 	  if (!err && uiserver->colon.fnc && uiserver->colon.any)
             {
               /* We must tell a colon function about the EOF. We do
                  this only when we have seen any data lines.  Note
                  that this inlined use of colon data lines will
                  eventually be changed into using a regular data
                  channel. */
               uiserver->colon.any = 0;
               err = uiserver->colon.fnc (uiserver->colon.fnc_value, NULL);
             }
           TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
 		  "fd 0x%x: OK line - final status: %s",
                   fd, err ? gpg_strerror (err) : "ok");
 	  _gpgme_io_close (uiserver->status_cb.fd);
 	  return err;
 	}
       else if (linelen > 2
 	       && line[0] == 'D' && line[1] == ' '
 	       && uiserver->colon.fnc)
         {
 	  /* We are using the colon handler even for plain inline data
              - strange name for that function but for historic reasons
              we keep it.  */
           /* FIXME We can't use this for binary data because we
              assume this is a string.  For the current usage of colon
              output it is correct.  */
           char *src = line + 2;
 	  char *end = line + linelen;
 	  char *dst;
           char **aline = &uiserver->colon.attic.line;
 	  int *alinelen = &uiserver->colon.attic.linelen;
 
 	  if (uiserver->colon.attic.linesize < *alinelen + linelen + 1)
 	    {
 	      char *newline = realloc (*aline, *alinelen + linelen + 1);
 	      if (!newline)
 		err = gpg_error_from_syserror ();
 	      else
 		{
 		  *aline = newline;
 		  uiserver->colon.attic.linesize = *alinelen + linelen + 1;
 		}
 	    }
 	  if (!err)
 	    {
 	      dst = *aline + *alinelen;
 
 	      while (!err && src < end)
 		{
 		  if (*src == '%' && src + 2 < end)
 		    {
 		      /* Handle escaped characters.  */
 		      ++src;
 		      *dst = _gpgme_hextobyte (src);
 		      (*alinelen)++;
 		      src += 2;
 		    }
 		  else
 		    {
 		      *dst = *src++;
 		      (*alinelen)++;
 		    }
 
 		  if (*dst == '\n')
 		    {
 		      /* Terminate the pending line, pass it to the colon
 			 handler and reset it.  */
 
 		      uiserver->colon.any = 1;
 		      if (*alinelen > 1 && *(dst - 1) == '\r')
 			dst--;
 		      *dst = '\0';
 
 		      /* FIXME How should we handle the return code?  */
 		      err = uiserver->colon.fnc (uiserver->colon.fnc_value, *aline);
 		      if (!err)
 			{
 			  dst = *aline;
 			  *alinelen = 0;
 			}
 		    }
 		  else
 		    dst++;
 		}
 	    }
           TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
 		  "fd 0x%x: D line; final status: %s",
                   fd, err? gpg_strerror (err):"ok");
         }
       else if (linelen > 2
 	       && line[0] == 'D' && line[1] == ' '
 	       && uiserver->inline_data)
         {
           char *src = line + 2;
 	  char *end = line + linelen;
 	  char *dst = src;
           gpgme_ssize_t nwritten;
 
           linelen = 0;
           while (src < end)
             {
               if (*src == '%' && src + 2 < end)
                 {
                   /* Handle escaped characters.  */
                   ++src;
                   *dst++ = _gpgme_hextobyte (src);
                   src += 2;
                 }
               else
                 *dst++ = *src++;
 
               linelen++;
             }
 
           src = line + 2;
           while (linelen > 0)
             {
               nwritten = gpgme_data_write (uiserver->inline_data, src, linelen);
               if (!nwritten || (nwritten < 0 && errno != EINTR)
                   || nwritten > linelen)
                 {
                   err = gpg_error_from_syserror ();
                   break;
                 }
               src += nwritten;
               linelen -= nwritten;
             }
 
           TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
 		  "fd 0x%x: D inlinedata; final status: %s",
                   fd, err? gpg_strerror (err):"ok");
         }
       else if (linelen > 2
 	       && line[0] == 'S' && line[1] == ' ')
 	{
 	  char *rest;
 	  gpgme_status_code_t r;
 
 	  rest = strchr (line + 2, ' ');
 	  if (!rest)
 	    rest = line + linelen; /* set to an empty string */
 	  else
 	    *(rest++) = 0;
 
 	  r = _gpgme_parse_status (line + 2);
 
 	  if (r >= 0)
 	    {
 	      if (uiserver->status.fnc)
 		err = uiserver->status.fnc (uiserver->status.fnc_value, r, rest);
 	    }
 	  else
 	    fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
           TRACE3 (DEBUG_CTX, "gpgme:status_handler", uiserver,
 		  "fd 0x%x: S line (%s) - final status: %s",
                   fd, line+2, err? gpg_strerror (err):"ok");
 	}
       else if (linelen >= 7
                && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
                && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
                && line[6] == 'E'
                && (line[7] == '\0' || line[7] == ' '))
         {
           char *keyword = line+7;
 
           while (*keyword == ' ')
             keyword++;;
           default_inq_cb (uiserver, keyword);
           assuan_write_line (uiserver->assuan_ctx, "END");
         }
 
     }
   while (!err && assuan_pending_line (uiserver->assuan_ctx));
 
   return err;
 }
 
 
 static gpgme_error_t
 add_io_cb (engine_uiserver_t uiserver, iocb_data_t *iocbd, gpgme_io_cb_t handler)
 {
   gpgme_error_t err;
 
   TRACE_BEG2 (DEBUG_ENGINE, "engine-uiserver:add_io_cb", uiserver,
               "fd %d, dir %d", iocbd->fd, iocbd->dir);
   err = (*uiserver->io_cbs.add) (uiserver->io_cbs.add_priv,
 			      iocbd->fd, iocbd->dir,
 			      handler, iocbd->data, &iocbd->tag);
   if (err)
     return TRACE_ERR (err);
   if (!iocbd->dir)
     /* FIXME Kludge around poll() problem.  */
     err = _gpgme_io_set_nonblocking (iocbd->fd);
   return TRACE_ERR (err);
 }
 
 
 static gpgme_error_t
 start (engine_uiserver_t uiserver, const char *command)
 {
   gpgme_error_t err;
   int fdlist[5];
   int nfds;
 
   /* We need to know the fd used by assuan for reads.  We do this by
      using the assumption that the first returned fd from
      assuan_get_active_fds() is always this one.  */
   nfds = assuan_get_active_fds (uiserver->assuan_ctx, 0 /* read fds */,
                                 fdlist, DIM (fdlist));
   if (nfds < 1)
     return gpg_error (GPG_ERR_GENERAL);	/* FIXME */
 
   /* We "duplicate" the file descriptor, so we can close it here (we
      can't close fdlist[0], as that is closed by libassuan, and
      closing it here might cause libassuan to close some unrelated FD
      later).  Alternatively, we could special case status_fd and
      register/unregister it manually as needed, but this increases
      code duplication and is more complicated as we can not use the
      close notifications etc.  A third alternative would be to let
      Assuan know that we closed the FD, but that complicates the
      Assuan interface.  */
 
   uiserver->status_cb.fd = _gpgme_io_dup (fdlist[0]);
   if (uiserver->status_cb.fd < 0)
     return gpg_error_from_syserror ();
 
   if (_gpgme_io_set_close_notify (uiserver->status_cb.fd,
 				  close_notify_handler, uiserver))
     {
       _gpgme_io_close (uiserver->status_cb.fd);
       uiserver->status_cb.fd = -1;
       return gpg_error (GPG_ERR_GENERAL);
     }
 
   err = add_io_cb (uiserver, &uiserver->status_cb, status_handler);
   if (!err && uiserver->input_cb.fd != -1)
     err = add_io_cb (uiserver, &uiserver->input_cb, _gpgme_data_outbound_handler);
   if (!err && uiserver->output_cb.fd != -1)
     err = add_io_cb (uiserver, &uiserver->output_cb, _gpgme_data_inbound_handler);
   if (!err && uiserver->message_cb.fd != -1)
     err = add_io_cb (uiserver, &uiserver->message_cb, _gpgme_data_outbound_handler);
 
   if (!err)
     err = assuan_write_line (uiserver->assuan_ctx, command);
 
   if (!err)
     uiserver_io_event (uiserver, GPGME_EVENT_START, NULL);
 
   return err;
 }
 
 
 static gpgme_error_t
 uiserver_reset (void *engine)
 {
   engine_uiserver_t uiserver = engine;
 
   /* We must send a reset because we need to reset the list of
      signers.  Note that RESET does not reset OPTION commands. */
   return uiserver_assuan_simple_command (uiserver, "RESET", NULL, NULL);
 }
 
 
 static gpgme_error_t
 _uiserver_decrypt (void *engine, int verify,
 		   gpgme_data_t ciph, gpgme_data_t plain)
 {
   engine_uiserver_t uiserver = engine;
   gpgme_error_t err;
   const char *protocol;
   char *cmd;
 
   if (!uiserver)
     return gpg_error (GPG_ERR_INV_VALUE);
   if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
     protocol = "";
   else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
     protocol = " --protocol=OpenPGP";
   else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
     protocol = " --protocol=CMS";
   else
     return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
 
   if (asprintf (&cmd, "DECRYPT%s%s", protocol,
 		verify ? "" : " --no-verify") < 0)
     return gpg_error_from_syserror ();
 
   uiserver->input_cb.data = ciph;
   err = uiserver_set_fd (uiserver, INPUT_FD,
 			 map_data_enc (uiserver->input_cb.data));
   if (err)
     {
       free (cmd);
       return gpg_error (GPG_ERR_GENERAL);	/* FIXME */
     }
   uiserver->output_cb.data = plain;
   err = uiserver_set_fd (uiserver, OUTPUT_FD, 0);
   if (err)
     {
       free (cmd);
       return gpg_error (GPG_ERR_GENERAL);	/* FIXME */
     }
   uiserver->inline_data = NULL;
 
   err = start (engine, cmd);
   free (cmd);
   return err;
 }
 
 
 static gpgme_error_t
 uiserver_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
 {
   return _uiserver_decrypt (engine, 0, ciph, plain);
 }
 
 
 static gpgme_error_t
 uiserver_decrypt_verify (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
 {
   return _uiserver_decrypt (engine, 1, ciph, plain);
 }
 
 
 static gpgme_error_t
 set_recipients (engine_uiserver_t uiserver, gpgme_key_t recp[])
 {
   gpgme_error_t err = 0;
   char *line;
   int linelen;
   int invalid_recipients = 0;
   int i;
 
   linelen = 10 + 40 + 1;	/* "RECIPIENT " + guess + '\0'.  */
   line = malloc (10 + 40 + 1);
   if (!line)
     return gpg_error_from_syserror ();
   strcpy (line, "RECIPIENT ");
   for (i=0; !err && recp[i]; i++)
     {
       char *uid;
       int newlen;
 
       /* We use only the first user ID of the key.  */
       if (!recp[i]->uids || !(uid=recp[i]->uids->uid) || !*uid)
 	{
 	  invalid_recipients++;
 	  continue;
 	}
 
       newlen = 11 + strlen (uid);
       if (linelen < newlen)
 	{
 	  char *newline = realloc (line, newlen);
 	  if (! newline)
 	    {
 	      int saved_err = gpg_error_from_syserror ();
 	      free (line);
 	      return saved_err;
 	    }
 	  line = newline;
 	  linelen = newlen;
 	}
       /* FIXME: need to do proper escaping  */
       strcpy (&line[10], uid);
 
       err = uiserver_assuan_simple_command (uiserver, line,
                                             uiserver->status.fnc,
                                             uiserver->status.fnc_value);
       /* FIXME: This might requires more work.  */
       if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
 	invalid_recipients++;
       else if (err)
 	{
 	  free (line);
 	  return err;
 	}
     }
   free (line);
   return gpg_error (invalid_recipients
 		    ? GPG_ERR_UNUSABLE_PUBKEY : GPG_ERR_NO_ERROR);
 }
 
 
 static gpgme_error_t
 uiserver_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
 		  gpgme_data_t plain, gpgme_data_t ciph, int use_armor)
 {
   engine_uiserver_t uiserver = engine;
   gpgme_error_t err;
   const char *protocol;
   char *cmd;
 
   if (!uiserver)
     return gpg_error (GPG_ERR_INV_VALUE);
   if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
     protocol = "";
   else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
     protocol = " --protocol=OpenPGP";
   else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
     protocol = " --protocol=CMS";
   else
     return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
 
   if (flags & GPGME_ENCRYPT_PREPARE)
     {
       if (!recp || plain || ciph)
 	return gpg_error (GPG_ERR_INV_VALUE);
 
       if (asprintf (&cmd, "PREP_ENCRYPT%s%s", protocol,
 		    (flags & GPGME_ENCRYPT_EXPECT_SIGN)
 		    ? " --expect-sign" : "") < 0)
 	return gpg_error_from_syserror ();
     }
   else
     {
       if (!plain || !ciph)
 	return gpg_error (GPG_ERR_INV_VALUE);
 
       if (asprintf (&cmd, "ENCRYPT%s", protocol) < 0)
 	return gpg_error_from_syserror ();
     }
 
   if (plain)
     {
       uiserver->input_cb.data = plain;
       err = uiserver_set_fd (uiserver, INPUT_FD,
 			     map_data_enc (uiserver->input_cb.data));
       if (err)
 	{
 	  free (cmd);
 	  return err;
 	}
     }
 
   if (ciph)
     {
       uiserver->output_cb.data = ciph;
       err = uiserver_set_fd (uiserver, OUTPUT_FD, use_armor ? "--armor"
 			     : map_data_enc (uiserver->output_cb.data));
       if (err)
 	{
 	  free (cmd);
 	  return err;
 	}
     }
 
   uiserver->inline_data = NULL;
 
   if (recp)
     {
       err = set_recipients (uiserver, recp);
       if (err)
 	{
 	  free (cmd);
 	  return err;
 	}
     }
 
   err = start (uiserver, cmd);
   free (cmd);
   return err;
 }
 
 
 static gpgme_error_t
 uiserver_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
 	       gpgme_sig_mode_t mode, int use_armor, int use_textmode,
 	       int include_certs, gpgme_ctx_t ctx /* FIXME */)
 {
   engine_uiserver_t uiserver = engine;
   gpgme_error_t err = 0;
   const char *protocol;
   char *cmd;
   gpgme_key_t key;
 
   if (!uiserver || !in || !out)
     return gpg_error (GPG_ERR_INV_VALUE);
   if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
     protocol = "";
   else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
     protocol = " --protocol=OpenPGP";
   else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
     protocol = " --protocol=CMS";
   else
     return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
 
   if (asprintf (&cmd, "SIGN%s%s", protocol,
 		(mode == GPGME_SIG_MODE_DETACH) ? " --detached" : "") < 0)
     return gpg_error_from_syserror ();
 
   key = gpgme_signers_enum (ctx, 0);
   if (key)
     {
       const char *s = NULL;
 
       if (key && key->uids)
         s = key->uids->email;
 
       if (s && strlen (s) < 80)
         {
           char buf[100];
 
           strcpy (stpcpy (buf, "SENDER --info "), s);
           err = uiserver_assuan_simple_command (uiserver, buf,
                                                 uiserver->status.fnc,
                                                 uiserver->status.fnc_value);
         }
       else
         err = gpg_error (GPG_ERR_INV_VALUE);
       gpgme_key_unref (key);
       if (err)
       {
 	free (cmd);
 	return err;
       }
   }
 
   uiserver->input_cb.data = in;
   err = uiserver_set_fd (uiserver, INPUT_FD,
 			 map_data_enc (uiserver->input_cb.data));
   if (err)
     {
       free (cmd);
       return err;
     }
   uiserver->output_cb.data = out;
   err = uiserver_set_fd (uiserver, OUTPUT_FD, use_armor ? "--armor"
 			 : map_data_enc (uiserver->output_cb.data));
   if (err)
     {
       free (cmd);
       return err;
     }
   uiserver->inline_data = NULL;
 
   err = start (uiserver, cmd);
   free (cmd);
   return err;
 }
 
 
 /* FIXME: Missing a way to specify --silent.  */
 static gpgme_error_t
 uiserver_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
 	      gpgme_data_t plaintext)
 {
   engine_uiserver_t uiserver = engine;
   gpgme_error_t err;
   const char *protocol;
   char *cmd;
 
   if (!uiserver)
     return gpg_error (GPG_ERR_INV_VALUE);
   if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
     protocol = "";
   else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
     protocol = " --protocol=OpenPGP";
   else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
     protocol = " --protocol=CMS";
   else
     return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
 
   if (asprintf (&cmd, "VERIFY%s", protocol) < 0)
     return gpg_error_from_syserror ();
 
   uiserver->input_cb.data = sig;
   err = uiserver_set_fd (uiserver, INPUT_FD,
 			 map_data_enc (uiserver->input_cb.data));
   if (err)
     {
       free (cmd);
       return err;
     }
   if (plaintext)
     {
       /* Normal or cleartext signature.  */
       uiserver->output_cb.data = plaintext;
       err = uiserver_set_fd (uiserver, OUTPUT_FD, 0);
     }
   else
     {
       /* Detached signature.  */
       uiserver->message_cb.data = signed_text;
       err = uiserver_set_fd (uiserver, MESSAGE_FD, 0);
     }
   uiserver->inline_data = NULL;
 
   if (!err)
     err = start (uiserver, cmd);
 
   free (cmd);
   return err;
 }
 
 
 /* This sets a status callback for monitoring status lines before they
  * are passed to a caller set handler.  */
 static void
 uiserver_set_status_cb (void *engine, gpgme_status_cb_t cb, void *cb_value)
 {
   engine_uiserver_t uiserver = engine;
 
   uiserver->status.mon_cb = cb;
   uiserver->status.mon_cb_value = cb_value;
 }
 
 
 static void
 uiserver_set_status_handler (void *engine, engine_status_handler_t fnc,
 			  void *fnc_value)
 {
   engine_uiserver_t uiserver = engine;
 
   uiserver->status.fnc = fnc;
   uiserver->status.fnc_value = fnc_value;
 }
 
 
 static gpgme_error_t
 uiserver_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
 			      void *fnc_value)
 {
   engine_uiserver_t uiserver = engine;
 
   uiserver->colon.fnc = fnc;
   uiserver->colon.fnc_value = fnc_value;
   uiserver->colon.any = 0;
   return 0;
 }
 
 
 static void
 uiserver_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
 {
   engine_uiserver_t uiserver = engine;
   uiserver->io_cbs = *io_cbs;
 }
 
 
 static void
 uiserver_io_event (void *engine, gpgme_event_io_t type, void *type_data)
 {
   engine_uiserver_t uiserver = engine;
 
   TRACE3 (DEBUG_ENGINE, "gpgme:uiserver_io_event", uiserver,
           "event %p, type %d, type_data %p",
           uiserver->io_cbs.event, type, type_data);
   if (uiserver->io_cbs.event)
     (*uiserver->io_cbs.event) (uiserver->io_cbs.event_priv, type, type_data);
 }
 
 
 struct engine_ops _gpgme_engine_ops_uiserver =
   {
     /* Static functions.  */
     _gpgme_get_default_uisrv_socket,
     NULL,
     uiserver_get_version,
     uiserver_get_req_version,
     uiserver_new,
 
     /* Member functions.  */
     uiserver_release,
     uiserver_reset,
     uiserver_set_status_cb,
     uiserver_set_status_handler,
     NULL,		/* set_command_handler */
     uiserver_set_colon_line_handler,
     uiserver_set_locale,
     uiserver_set_protocol,
     uiserver_decrypt,
     uiserver_decrypt_verify,
     NULL,		/* delete */
     NULL,		/* edit */
     uiserver_encrypt,
     NULL,		/* encrypt_sign */
     NULL,		/* export */
     NULL,		/* export_ext */
     NULL,		/* genkey */
     NULL,		/* import */
     NULL,		/* keylist */
     NULL,		/* keylist_ext */
     uiserver_sign,
     NULL,		/* trustlist */
     uiserver_verify,
     NULL,		/* getauditlog */
     NULL,               /* opassuan_transact */
     NULL,		/* conf_load */
     NULL,		/* conf_save */
     uiserver_set_io_cbs,
     uiserver_io_event,
     uiserver_cancel,
     NULL,		/* cancel_op */
     NULL,               /* passwd */
     NULL,                /* set_pinentry_mode */
     NULL                /* opspawn */
   };
diff --git a/src/engine.c b/src/engine.c
index 4e59adad..a7c016f0 100644
--- a/src/engine.c
+++ b/src/engine.c
@@ -1,976 +1,1003 @@
 /* engine.c - GPGME engine support.
    Copyright (C) 2000 Werner Koch (dd9jn)
    Copyright (C) 2001, 2002, 2003, 2004, 2006, 2009, 2010 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 <errno.h>
 #include <assert.h>
 
 #include "gpgme.h"
 #include "util.h"
 #include "sema.h"
 #include "ops.h"
 #include "debug.h"
 
 #include "engine.h"
 #include "engine-backend.h"
 
 
 struct engine
 {
   struct engine_ops *ops;
   void *engine;
 };
 
 
 static struct engine_ops *engine_ops[] =
   {
     &_gpgme_engine_ops_gpg,		/* OpenPGP.  */
     &_gpgme_engine_ops_gpgsm,		/* CMS.  */
     &_gpgme_engine_ops_gpgconf,		/* gpg-conf.  */
     &_gpgme_engine_ops_assuan,		/* Low-Level Assuan.  */
     &_gpgme_engine_ops_g13,		/* Crypto VFS.  */
 #ifdef ENABLE_UISERVER
     &_gpgme_engine_ops_uiserver,	/* UI-Server.  */
 #else
     NULL,
 #endif
     &_gpgme_engine_ops_spawn
   };
 
 
 /* The engine info.  */
 static gpgme_engine_info_t engine_info;
 DEFINE_STATIC_LOCK (engine_info_lock);
 
 
 /* Get the file name of the engine for PROTOCOL.  */
 static const char *
 engine_get_file_name (gpgme_protocol_t proto)
 {
   if (proto > DIM (engine_ops))
     return NULL;
 
   if (engine_ops[proto] && engine_ops[proto]->get_file_name)
     return (*engine_ops[proto]->get_file_name) ();
   else
     return NULL;
 }
 
 
 /* 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.  */
+ * for PROTOCOL.  If this function returns NULL for a valid protocol,
+ * it should be assumed that the engine is a pseudo engine. */
 static char *
 engine_get_version (gpgme_protocol_t proto, const char *file_name)
 {
   if (proto > DIM (engine_ops))
     return NULL;
 
   if (engine_ops[proto] && engine_ops[proto]->get_version)
     return (*engine_ops[proto]->get_version) (file_name);
   else
     return NULL;
 }
 
 
-/* Get the required version number of the engine for PROTOCOL.  */
+/* Get the required version number of the engine for PROTOCOL.  This
+ * may be NULL. */
 static const char *
 engine_get_req_version (gpgme_protocol_t proto)
 {
   if (proto > DIM (engine_ops))
     return NULL;
 
   if (engine_ops[proto] && engine_ops[proto]->get_req_version)
     return (*engine_ops[proto]->get_req_version) ();
   else
     return NULL;
 }
 
 
 /* Verify the version requirement for the engine for PROTOCOL.  */
 gpgme_error_t
 gpgme_engine_check_version (gpgme_protocol_t proto)
 {
   gpgme_error_t err;
   gpgme_engine_info_t info;
   int result;
 
   LOCK (engine_info_lock);
   info = engine_info;
   if (!info)
     {
       /* Make sure it is initialized.  */
       UNLOCK (engine_info_lock);
       err = gpgme_get_engine_info (&info);
       if (err)
 	return err;
 
       LOCK (engine_info_lock);
     }
 
   while (info && info->protocol != proto)
     info = info->next;
 
   if (!info)
     result = 0;
   else
     result = _gpgme_compare_versions (info->version,
 				      info->req_version);
 
   UNLOCK (engine_info_lock);
   return result ? 0 : trace_gpg_error (GPG_ERR_INV_ENGINE);
 }
 
 
 /* Release the engine info INFO.  */
 void
 _gpgme_engine_info_release (gpgme_engine_info_t info)
 {
   while (info)
     {
       gpgme_engine_info_t next_info = info->next;
 
-      assert (info->file_name);
-      free (info->file_name);
+      if (info->file_name)
+        free (info->file_name);
       if (info->home_dir)
 	free (info->home_dir);
       if (info->version)
 	free (info->version);
       free (info);
       info = next_info;
     }
 }
 
 
 /* Get the information about the configured and installed engines.  A
    pointer to the first engine in the statically allocated linked list
    is returned in *INFO.  If an error occurs, it is returned.  The
    returned data is valid until the next gpgme_set_engine_info.  */
 gpgme_error_t
 gpgme_get_engine_info (gpgme_engine_info_t *info)
 {
   gpgme_error_t err;
 
   LOCK (engine_info_lock);
   if (!engine_info)
     {
       gpgme_engine_info_t *lastp = &engine_info;
       gpgme_protocol_t proto_list[] = { GPGME_PROTOCOL_OpenPGP,
 					GPGME_PROTOCOL_CMS,
 					GPGME_PROTOCOL_GPGCONF,
 					GPGME_PROTOCOL_ASSUAN,
 					GPGME_PROTOCOL_G13,
 					GPGME_PROTOCOL_UISERVER,
                                         GPGME_PROTOCOL_SPAWN    };
       unsigned int proto;
 
       err = 0;
       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 *version = engine_get_version (proto_list[proto], NULL);
 	  char *file_name;
 	  char *home_dir;
 
 	  if (!ofile_name)
 	    continue;
 
 	  file_name = strdup (ofile_name);
           if (!file_name)
             err = gpg_error_from_syserror ();
 
           if (ohome_dir)
             {
               home_dir = strdup (ohome_dir);
               if (!home_dir && !err)
                 err = gpg_error_from_syserror ();
             }
           else
             home_dir = NULL;
 
-	  *lastp = malloc (sizeof (*engine_info));
+	  *lastp = calloc (1, sizeof (*engine_info));
           if (!*lastp && !err)
             err = gpg_error_from_syserror ();
 
+          /* Now set the dummy version for pseudo engines.  */
+          if (!err && !version)
+            {
+              version = strdup ("1.0.0");
+              if (!version)
+                err = gpg_error_from_syserror ();
+            }
+
 	  if (err)
 	    {
 	      _gpgme_engine_info_release (engine_info);
 	      engine_info = NULL;
 
 	      if (file_name)
 		free (file_name);
 	      if (home_dir)
 		free (home_dir);
+	      if (version)
+		free (version);
 
 	      UNLOCK (engine_info_lock);
 	      return err;
 	    }
 
 	  (*lastp)->protocol = proto_list[proto];
 	  (*lastp)->file_name = file_name;
 	  (*lastp)->home_dir = home_dir;
-	  (*lastp)->version = engine_get_version (proto_list[proto], NULL);
+	  (*lastp)->version = version;
 	  (*lastp)->req_version = engine_get_req_version (proto_list[proto]);
+	  if (!(*lastp)->req_version)
+            (*lastp)->req_version = "1.0.0"; /* Dummy for pseudo engines. */
 	  (*lastp)->next = NULL;
 	  lastp = &(*lastp)->next;
 	}
     }
 
   *info = engine_info;
   UNLOCK (engine_info_lock);
   return 0;
 }
 
 
 /* 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 err = 0;
   gpgme_engine_info_t info;
   gpgme_engine_info_t new_info;
   gpgme_engine_info_t *lastp;
 
   LOCK (engine_info_lock);
   info = engine_info;
   if (!info)
     {
       /* Make sure it is initialized.  */
       UNLOCK (engine_info_lock);
       err = gpgme_get_engine_info (&info);
       if (err)
 	return err;
 
       LOCK (engine_info_lock);
     }
 
   new_info = NULL;
   lastp = &new_info;
 
   while (info)
     {
       char *file_name;
       char *home_dir;
       char *version;
 
       assert (info->file_name);
       file_name = strdup (info->file_name);
       if (!file_name)
         err = gpg_error_from_syserror ();
 
       if (info->home_dir)
 	{
 	  home_dir = strdup (info->home_dir);
 	  if (!home_dir && !err)
 	    err = gpg_error_from_syserror ();
 	}
       else
 	home_dir = NULL;
 
       if (info->version)
 	{
 	  version = strdup (info->version);
 	  if (!version && !err)
 	    err = gpg_error_from_syserror ();
 	}
       else
 	version = NULL;
 
       *lastp = malloc (sizeof (*engine_info));
       if (!*lastp && !err)
         err = gpg_error_from_syserror ();
 
       if (err)
 	{
 	  _gpgme_engine_info_release (new_info);
 	  if (file_name)
 	    free (file_name);
 	  if (home_dir)
 	    free (home_dir);
 	  if (version)
 	    free (version);
 
 	  UNLOCK (engine_info_lock);
 	  return err;
 	}
 
       (*lastp)->protocol = info->protocol;
       (*lastp)->file_name = file_name;
       (*lastp)->home_dir = home_dir;
       (*lastp)->version = version;
       (*lastp)->req_version = info->req_version;
       (*lastp)->next = NULL;
       lastp = &(*lastp)->next;
 
       info = info->next;
     }
 
   *r_info = new_info;
   UNLOCK (engine_info_lock);
   return 0;
 }
 
 
 /* Set the engine info for the info list INFO, protocol PROTO, to the
    file name FILE_NAME and the home directory HOME_DIR.  */
 gpgme_error_t
 _gpgme_set_engine_info (gpgme_engine_info_t info, gpgme_protocol_t proto,
 			const char *file_name, const char *home_dir)
 {
   char *new_file_name;
   char *new_home_dir;
+  char *new_version;
 
   /* FIXME: Use some PROTO_MAX definition.  */
   if (proto > DIM (engine_ops))
     return gpg_error (GPG_ERR_INV_VALUE);
 
   while (info && info->protocol != proto)
     info = info->next;
 
   if (!info)
     return trace_gpg_error (GPG_ERR_INV_ENGINE);
 
   /* Prepare new members.  */
   if (file_name)
     new_file_name = strdup (file_name);
   else
     {
       const char *ofile_name = engine_get_file_name (proto);
       assert (ofile_name);
       new_file_name = strdup (ofile_name);
     }
   if (!new_file_name)
     return gpg_error_from_syserror ();
 
   if (home_dir)
     {
       new_home_dir = strdup (home_dir);
       if (!new_home_dir)
 	{
 	  free (new_file_name);
 	  return gpg_error_from_syserror ();
 	}
     }
   else
     {
       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_syserror ();
             }
         }
       else
         new_home_dir = NULL;
     }
 
+  new_version = engine_get_version (proto, new_file_name);
+  if (!new_version)
+    {
+      new_version = strdup ("1.0.0"); /* Fake one for dummy entries.  */
+      if (!new_version)
+        {
+          free (new_file_name);
+          free (new_home_dir);
+        }
+    }
+
   /* Remove the old members.  */
   assert (info->file_name);
   free (info->file_name);
   if (info->home_dir)
     free (info->home_dir);
   if (info->version)
     free (info->version);
 
   /* Install the new members.  */
   info->file_name = new_file_name;
   info->home_dir = new_home_dir;
-  info->version = engine_get_version (proto, new_file_name);
+  info->version = new_version;
 
   return 0;
 }
 
 
 /* Set the default engine info for the protocol PROTO to the file name
    FILE_NAME and the home directory HOME_DIR.  */
 gpgme_error_t
 gpgme_set_engine_info (gpgme_protocol_t proto,
 		       const char *file_name, const char *home_dir)
 {
   gpgme_error_t err;
   gpgme_engine_info_t info;
 
   LOCK (engine_info_lock);
   info = engine_info;
   if (!info)
     {
       /* Make sure it is initialized.  */
       UNLOCK (engine_info_lock);
       err = gpgme_get_engine_info (&info);
       if (err)
 	return err;
 
       LOCK (engine_info_lock);
     }
 
   err = _gpgme_set_engine_info (info, proto, file_name, home_dir);
   UNLOCK (engine_info_lock);
   return err;
 }
 
 
 gpgme_error_t
 _gpgme_engine_new (gpgme_engine_info_t info, engine_t *r_engine)
 {
   engine_t engine;
 
   if (!info->file_name || !info->version)
     return trace_gpg_error (GPG_ERR_INV_ENGINE);
 
   engine = calloc (1, sizeof *engine);
   if (!engine)
     return gpg_error_from_syserror ();
 
   engine->ops = engine_ops[info->protocol];
   if (engine->ops->new)
     {
       gpgme_error_t err;
       err = (*engine->ops->new) (&engine->engine,
 				 info->file_name, info->home_dir,
                                  info->version);
       if (err)
 	{
 	  free (engine);
 	  return err;
 	}
     }
   else
     engine->engine = NULL;
 
   *r_engine = engine;
   return 0;
 }
 
 
 gpgme_error_t
 _gpgme_engine_reset (engine_t engine)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!engine->ops->reset)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
   return (*engine->ops->reset) (engine->engine);
 }
 
 
 void
 _gpgme_engine_release (engine_t engine)
 {
   if (!engine)
     return;
 
   if (engine->ops->release)
     (*engine->ops->release) (engine->engine);
   free (engine);
 }
 
 
 /* Set a status callback which is used to monitor the status values
  * before they are passed to a handler set with
  * _gpgme_engine_set_status_handler.  */
 void
 _gpgme_engine_set_status_cb (engine_t engine,
                              gpgme_status_cb_t cb, void *cb_value)
 {
   if (!engine)
     return;
 
   if (engine->ops->set_status_cb)
     (*engine->ops->set_status_cb) (engine->engine, cb, cb_value);
 }
 
 
 void
 _gpgme_engine_set_status_handler (engine_t engine,
 				  engine_status_handler_t fnc, void *fnc_value)
 {
   if (!engine)
     return;
 
   if (engine->ops->set_status_handler)
     (*engine->ops->set_status_handler) (engine->engine, fnc, fnc_value);
 }
 
 
 gpgme_error_t
 _gpgme_engine_set_command_handler (engine_t engine,
 				   engine_command_handler_t fnc,
 				   void *fnc_value,
 				   gpgme_data_t linked_data)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!engine->ops->set_command_handler)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
   return (*engine->ops->set_command_handler) (engine->engine,
 					      fnc, fnc_value, linked_data);
 }
 
 gpgme_error_t
 _gpgme_engine_set_colon_line_handler (engine_t engine,
 				      engine_colon_line_handler_t fnc,
 				      void *fnc_value)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!engine->ops->set_colon_line_handler)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
   return (*engine->ops->set_colon_line_handler) (engine->engine,
 						 fnc, fnc_value);
 }
 
 gpgme_error_t
 _gpgme_engine_set_locale (engine_t engine, int category,
 			  const char *value)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!engine->ops->set_locale)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
   return (*engine->ops->set_locale) (engine->engine, category, value);
 }
 
 
 gpgme_error_t
 _gpgme_engine_set_protocol (engine_t engine, gpgme_protocol_t protocol)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!engine->ops->set_protocol)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
   return (*engine->ops->set_protocol) (engine->engine, protocol);
 }
 
 
 gpgme_error_t
 _gpgme_engine_op_decrypt (engine_t engine, gpgme_data_t ciph,
 			  gpgme_data_t plain)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!engine->ops->decrypt)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
   return (*engine->ops->decrypt) (engine->engine, ciph, plain);
 }
 
 
 gpgme_error_t
 _gpgme_engine_op_decrypt_verify (engine_t engine, gpgme_data_t ciph,
 				 gpgme_data_t plain)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!engine->ops->decrypt_verify)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
   return (*engine->ops->decrypt_verify) (engine->engine, ciph, plain);
 }
 
 
 gpgme_error_t
 _gpgme_engine_op_delete (engine_t engine, gpgme_key_t key,
 			 int allow_secret)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!engine->ops->delete)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
   return (*engine->ops->delete) (engine->engine, key, allow_secret);
 }
 
 
 gpgme_error_t
 _gpgme_engine_op_edit (engine_t engine, int type, gpgme_key_t key,
 		       gpgme_data_t out, gpgme_ctx_t ctx /* FIXME */)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!engine->ops->edit)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
   return (*engine->ops->edit) (engine->engine, type, key, out, ctx);
 }
 
 
 gpgme_error_t
 _gpgme_engine_op_encrypt (engine_t engine, gpgme_key_t recp[],
 			  gpgme_encrypt_flags_t flags,
 			  gpgme_data_t plain, gpgme_data_t ciph, int use_armor)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!engine->ops->encrypt)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
   return (*engine->ops->encrypt) (engine->engine, recp, flags, plain, ciph,
 				  use_armor);
 }
 
 
 gpgme_error_t
 _gpgme_engine_op_encrypt_sign (engine_t engine, gpgme_key_t recp[],
 			       gpgme_encrypt_flags_t flags,
 			       gpgme_data_t plain, gpgme_data_t ciph,
 			       int use_armor, gpgme_ctx_t ctx /* FIXME */)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!engine->ops->encrypt_sign)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
   return (*engine->ops->encrypt_sign) (engine->engine, recp, flags,
 				       plain, ciph, use_armor, ctx);
 }
 
 
 gpgme_error_t
 _gpgme_engine_op_export (engine_t engine, const char *pattern,
 			 gpgme_export_mode_t mode, gpgme_data_t keydata,
 			 int use_armor)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!engine->ops->export)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
   return (*engine->ops->export) (engine->engine, pattern, mode,
 				 keydata, use_armor);
 }
 
 
 gpgme_error_t
 _gpgme_engine_op_export_ext (engine_t engine, const char *pattern[],
 			     unsigned int reserved, gpgme_data_t keydata,
 			     int use_armor)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!engine->ops->export_ext)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
   return (*engine->ops->export_ext) (engine->engine, pattern, reserved,
 				     keydata, use_armor);
 }
 
 
 gpgme_error_t
 _gpgme_engine_op_genkey (engine_t engine, gpgme_data_t help_data,
 			 int use_armor, gpgme_data_t pubkey,
 			 gpgme_data_t seckey)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!engine->ops->genkey)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
   return (*engine->ops->genkey) (engine->engine, help_data, use_armor,
 				 pubkey, seckey);
 }
 
 
 gpgme_error_t
 _gpgme_engine_op_import (engine_t engine, gpgme_data_t keydata,
                          gpgme_key_t *keyarray)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!engine->ops->import)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
   return (*engine->ops->import) (engine->engine, keydata, keyarray);
 }
 
 
 gpgme_error_t
 _gpgme_engine_op_keylist (engine_t engine, const char *pattern,
 			  int secret_only, gpgme_keylist_mode_t mode,
 			  int engine_flags)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!engine->ops->keylist)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
   return (*engine->ops->keylist) (engine->engine, pattern, secret_only, mode,
                                   engine_flags);
 }
 
 
 gpgme_error_t
 _gpgme_engine_op_keylist_ext (engine_t engine, const char *pattern[],
 			      int secret_only, int reserved,
 			      gpgme_keylist_mode_t mode, int engine_flags)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!engine->ops->keylist_ext)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
   return (*engine->ops->keylist_ext) (engine->engine, pattern, secret_only,
 				      reserved, mode, engine_flags);
 }
 
 
 gpgme_error_t
 _gpgme_engine_op_sign (engine_t engine, gpgme_data_t in, gpgme_data_t out,
 		       gpgme_sig_mode_t mode, int use_armor,
 		       int use_textmode, int include_certs,
 		       gpgme_ctx_t ctx /* FIXME */)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!engine->ops->sign)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
   return (*engine->ops->sign) (engine->engine, in, out, mode, use_armor,
 			       use_textmode, include_certs, ctx);
 }
 
 
 gpgme_error_t
 _gpgme_engine_op_trustlist (engine_t engine, const char *pattern)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!engine->ops->trustlist)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
   return (*engine->ops->trustlist) (engine->engine, pattern);
 }
 
 
 gpgme_error_t
 _gpgme_engine_op_verify (engine_t engine, gpgme_data_t sig,
 			 gpgme_data_t signed_text, gpgme_data_t plaintext)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!engine->ops->verify)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
   return (*engine->ops->verify) (engine->engine, sig, signed_text, plaintext);
 }
 
 
 gpgme_error_t
 _gpgme_engine_op_getauditlog (engine_t engine, gpgme_data_t output,
                               unsigned int flags)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!engine->ops->getauditlog)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
   return (*engine->ops->getauditlog) (engine->engine, output, flags);
 }
 
 
 gpgme_error_t
 _gpgme_engine_op_assuan_transact (engine_t engine,
                                   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)
 {
   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,
                                             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)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!engine->ops->conf_load)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
   return (*engine->ops->conf_load) (engine->engine, conf_p);
 }
 
 
 gpgme_error_t
 _gpgme_engine_op_conf_save (engine_t engine, gpgme_conf_comp_t conf)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!engine->ops->conf_save)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
   return (*engine->ops->conf_save) (engine->engine, conf);
 }
 
 
 void
 _gpgme_engine_set_io_cbs (engine_t engine, gpgme_io_cbs_t io_cbs)
 {
   if (!engine)
     return;
 
   (*engine->ops->set_io_cbs) (engine->engine, io_cbs);
 }
 
 
 void
 _gpgme_engine_io_event (engine_t engine,
 			gpgme_event_io_t type, void *type_data)
 {
   if (!engine)
     return;
 
   (*engine->ops->io_event) (engine->engine, type, type_data);
 }
 
 
 /* Cancel the session and the pending operation if any.  */
 gpgme_error_t
 _gpgme_engine_cancel (engine_t engine)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!engine->ops->cancel)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
   return (*engine->ops->cancel) (engine->engine);
 }
 
 
 /* Cancel the pending operation, but not the complete session.  */
 gpgme_error_t
 _gpgme_engine_cancel_op (engine_t engine)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!engine->ops->cancel_op)
     return 0;
 
   return (*engine->ops->cancel_op) (engine->engine);
 }
 
 
 /* Change the passphrase for KEY.  */
 gpgme_error_t
 _gpgme_engine_op_passwd (engine_t engine, gpgme_key_t key,
                          unsigned int flags)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!engine->ops->passwd)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
   return (*engine->ops->passwd) (engine->engine, key, flags);
 }
 
 
 /* Set the pinentry mode for ENGINE to MODE.  */
 gpgme_error_t
 _gpgme_engine_set_pinentry_mode (engine_t engine, gpgme_pinentry_mode_t mode)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!engine->ops->set_pinentry_mode)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
   return (*engine->ops->set_pinentry_mode) (engine->engine, mode);
 }
 
 
 gpgme_error_t
 _gpgme_engine_op_spawn (engine_t engine,
                         const char *file, const char *argv[],
                         gpgme_data_t datain,
                         gpgme_data_t dataout, gpgme_data_t dataerr,
                         unsigned int flags)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!engine->ops->opspawn)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
   return (*engine->ops->opspawn) (engine->engine, file, argv,
                                   datain, dataout, dataerr, flags);
 }
diff --git a/src/version.c b/src/version.c
index 15e5aeec..e2f1c353 100644
--- a/src/version.c
+++ b/src/version.c
@@ -1,371 +1,371 @@
 /* version.c - Version check routines.
    Copyright (C) 2000 Werner Koch (dd9jn)
    Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2008 g10 Code GmbH
 
    This file is part of GPGME.
 
    GPGME is free software; you can redistribute it and/or modify it
    under the terms of the GNU Lesser General Public License as
    published by the Free Software Foundation; either version 2.1 of
    the License, or (at your option) any later version.
 
    GPGME is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.
 
    You should have received a copy of the GNU Lesser General Public
    License along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
    02111-1307, USA.  */
 
 #if HAVE_CONFIG_H
 #include <config.h>
 #endif
 #include <stdlib.h>
 #include <string.h>
 #include <limits.h>
 #include <ctype.h>
 #ifdef HAVE_W32_SYSTEM
 #include <winsock2.h>
 #endif
 
 #include "gpgme.h"
 #include "priv-io.h"
 #include "debug.h"
 #include "context.h"
 
 /* For _gpgme_sema_subsystem_init and _gpgme_status_init.  */
 #include "sema.h"
 #include "util.h"
 
 #ifdef HAVE_ASSUAN_H
 #include "assuan.h"
 #endif
 
 #ifdef HAVE_W32_SYSTEM
 #include "windows.h"
 #endif
 
 /* We implement this function, so we have to disable the overriding
    macro.  */
 #undef gpgme_check_version
 
 
 /* Bootstrap the subsystems needed for concurrent operation.  This
    must be done once at startup.  We can not guarantee this using a
    lock, though, because the semaphore subsystem needs to be
    initialized itself before it can be used.  So we expect that the
    user performs the necessary synchronization.  */
 static void
 do_subsystem_inits (void)
 {
   static int done = 0;
 
   if (done)
     return;
 
 #ifdef HAVE_W32_SYSTEM
   /* We need to make sure that the sockets are initialized.  */
   {
     WSADATA wsadat;
 
     WSAStartup (0x202, &wsadat);
   }
 #endif
 
   _gpgme_sema_subsystem_init ();
   _gpgme_debug_subsystem_init ();
   _gpgme_io_subsystem_init ();
   _gpgme_status_init ();
 
   done = 1;
 }
 
 
 /* Put vesion information into the binary.  */
 static const char *
 cright_blurb (void)
 {
   static const char blurb[] =
     "\n\n"
     "This is GPGME " PACKAGE_VERSION " - The GnuPG Made Easy library\n"
     CRIGHTBLURB
     "\n"
     "(" BUILD_REVISION " " BUILD_TIMESTAMP ")\n"
     "\n\n";
   return blurb;
 }
 
 
 /* Read the next number in the version string STR and return it in
    *NUMBER.  Return a pointer to the tail of STR after parsing, or
    *NULL if the version string was invalid.  */
 static const char *
 parse_version_number (const char *str, int *number)
 {
 #define MAXVAL ((INT_MAX - 10) / 10)
   int val = 0;
 
   /* Leading zeros are not allowed.  */
   if (*str == '0' && isdigit(str[1]))
     return NULL;
 
   while (isdigit (*str) && val <= MAXVAL)
     {
       val *= 10;
       val += *(str++) - '0';
     }
   *number = val;
   return val > MAXVAL ? NULL : str;
 }
 
 
 /* Parse the version string STR in the format MAJOR.MINOR.MICRO (for
    example, 9.3.2) and return the components in MAJOR, MINOR and MICRO
    as integers.  The function returns the tail of the string that
-   follows the version number.  This might be te empty string if there
+   follows the version number.  This might be the empty string if there
    is nothing following the version number, or a patchlevel.  The
    function returns NULL if the version string is not valid.  */
 static const char *
 parse_version_string (const char *str, int *major, int *minor, int *micro)
 {
   str = parse_version_number (str, major);
   if (!str || *str != '.')
     return NULL;
   str++;
 
   str = parse_version_number (str, minor);
   if (!str || *str != '.')
     return NULL;
   str++;
 
   str = parse_version_number (str, micro);
   if (!str)
     return NULL;
 
   /* A patchlevel might follow.  */
   return str;
 }
 
 
 /* Return true if MY_VERSION is at least REQ_VERSION, and false
    otherwise.  */
 int
 _gpgme_compare_versions (const char *my_version,
 			 const char *rq_version)
 {
   int my_major, my_minor, my_micro;
   int rq_major, rq_minor, rq_micro;
   const char *my_plvl, *rq_plvl;
 
   if (!rq_version)
     return 1;
   if (!my_version)
     return 0;
 
   my_plvl = parse_version_string (my_version, &my_major, &my_minor, &my_micro);
   if (!my_plvl)
     return 0;
 
   rq_plvl = parse_version_string (rq_version, &rq_major, &rq_minor, &rq_micro);
   if (!rq_plvl)
     return 0;
 
   if (my_major > rq_major
       || (my_major == rq_major && my_minor > rq_minor)
       || (my_major == rq_major && my_minor == rq_minor
 	  && my_micro > rq_micro)
       || (my_major == rq_major && my_minor == rq_minor
 	  && my_micro == rq_micro && strcmp (my_plvl, rq_plvl) >= 0))
     return 1;
 
   return 0;
 }
 
 
 /* Check that the the version of the library is at minimum the
    requested one and return the version string; return NULL if the
    condition is not met.  If a NULL is passed to this function, no
    check is done and the version string is simply returned.
 
    This function must be run once at startup, as it also initializes
    some subsystems.  Its invocation must be synchronized against
    calling any of the other functions in a multi-threaded
    environments.  */
 const char *
 gpgme_check_version (const char *req_version)
 {
   char *result;
   do_subsystem_inits ();
 
   /* Catch-22: We need to get at least the debug subsystem ready
      before using the trace facility.  If we won't the trace would
      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? req_version:"(null)", VERSION);
 
   result = _gpgme_compare_versions (VERSION, req_version) ? VERSION : NULL;
   if (result != NULL)
     _gpgme_selftest = 0;
 
   return result;
 }
 
 /* Check the version and also at runtime if the struct layout of the
    library matches the one of the user.  This is particular useful for
    Windows targets (-mms-bitfields).  */
 const char *
 gpgme_check_version_internal (const char *req_version,
 			      size_t offset_sig_validity)
 {
   const char *result;
 
   if (req_version && req_version[0] == 1 && req_version[1] == 1)
     return cright_blurb ();
   result = gpgme_check_version (req_version);
   if (result == NULL)
     return result;
 
   /* Catch-22, see above.  */
   TRACE2 (DEBUG_INIT, "gpgme_check_version_internal", 0,
 	  "req_version=%s, offset_sig_validity=%i",
 	  req_version ? req_version : "(null)", offset_sig_validity);
 
   if (offset_sig_validity != offsetof (struct _gpgme_signature, validity))
     {
       TRACE1 (DEBUG_INIT, "gpgme_check_version_internal", 0,
 	      "offset_sig_validity mismatch: expected %i",
 	      offsetof (struct _gpgme_signature, validity));
       _gpgme_selftest = GPG_ERR_SELFTEST_FAILED;
     }
 
   return result;
 }
 
 
 #define LINELENGTH 80
 
 /* Extract the version string of a program from STRING.  The version
    number is expected to be in GNU style format:
 
      foo 1.2.3
      foo (bar system) 1.2.3
      foo 1.2.3 cruft
      foo (bar system) 1.2.3 cruft.
 
   Spaces and tabs are skipped and used as delimiters, a term in
   (nested) parenthesis before the version string is skipped, the
   version string may consist of any non-space and non-tab characters
   but needs to bstart with a digit.
 */
 static const char *
 extract_version_string (const char *string, size_t *r_len)
 {
   const char *s;
   int count, len;
 
   for (s=string; *s; s++)
     if (*s == ' ' || *s == '\t')
         break;
   while (*s == ' ' || *s == '\t')
     s++;
   if (*s == '(')
     {
       for (count=1, s++; count && *s; s++)
         if (*s == '(')
           count++;
         else if (*s == ')')
           count--;
     }
   /* For robustness we look for a digit.  */
   while ( *s && !(*s >= '0' && *s <= '9') )
     s++;
   if (*s >= '0' && *s <= '9')
     {
       for (len=0; s[len]; len++)
         if (s[len] == ' ' || s[len] == '\t')
           break;
     }
   else
     len = 0;
 
   *r_len = len;
   return s;
 }
 
 
 /* Retrieve the version number from the --version output of the
    program FILE_NAME.  */
 char *
 _gpgme_get_program_version (const char *const file_name)
 {
   char line[LINELENGTH] = "";
   int linelen = 0;
   char *mark = NULL;
   int rp[2];
   int nread;
   char *argv[] = {NULL /* file_name */, "--version", 0};
   struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0},
 				   {-1, -1} };
   int status;
 
   if (!file_name)
     return NULL;
   argv[0] = (char *) file_name;
 
   if (_gpgme_io_pipe (rp, 1) < 0)
     return NULL;
 
   cfd[0].fd = rp[1];
 
   status = _gpgme_io_spawn (file_name, argv,
                             IOSPAWN_FLAG_DETACHED, cfd, NULL, NULL, NULL);
   if (status < 0)
     {
       _gpgme_io_close (rp[0]);
       _gpgme_io_close (rp[1]);
       return NULL;
     }
 
   do
     {
       nread = _gpgme_io_read (rp[0], &line[linelen], LINELENGTH - linelen - 1);
       if (nread > 0)
 	{
 	  line[linelen + nread] = '\0';
 	  mark = strchr (&line[linelen], '\n');
 	  if (mark)
 	    {
 	      if (mark > &line[0] && *mark == '\r')
 		mark--;
 	      *mark = '\0';
 	      break;
 	    }
 	  linelen += nread;
 	}
     }
   while (nread > 0 && linelen < LINELENGTH - 1);
 
   _gpgme_io_close (rp[0]);
 
   if (mark)
     {
       size_t len;
       const char *s;
 
       s = extract_version_string (line, &len);
       if (!len)
         return NULL;
       mark = malloc (len + 1);
       if (!mark)
 	return NULL;
       memcpy (mark, s, len);
       mark[len] = 0;
       return mark;
     }
 
   return NULL;
 }