[Pkg-gnupg-commit] [libassuan] 300/437: A couple of changes to eventually fully support W32ce.

Eric Dorland eric at moszumanska.debian.org
Fri May 22 05:33:56 UTC 2015


This is an automated email from the git hooks/post-receive script.

eric pushed a commit to branch master
in repository libassuan.

commit d20a4d4c79d4f12021383a7aeb5c34a861d858a4
Author: Werner Koch <wk at gnupg.org>
Date:   Wed Feb 24 15:50:55 2010 +0000

    A couple of changes to eventually fully support W32ce.
---
 ChangeLog                   |  14 +
 NEWS                        |   9 +
 configure.ac                |   3 +-
 doc/assuan.texi             |   8 +-
 src/ChangeLog               |  39 +++
 src/Makefile.am             |  13 +
 src/assuan-defs.h           |   7 +-
 src/assuan-handler.c        |  26 +-
 src/assuan-inquire.c        |  28 +-
 src/assuan-pipe-connect.c   |   2 +-
 src/assuan-socket-connect.c | 219 ++++++++++--
 src/assuan.h                |  11 +
 src/gpgcedev.c              | 720 +++++++++++++++++++++++++++++++++++++++
 src/gpgcedev.def            |  33 ++
 src/gpgcemgr.c              |  72 ++++
 src/libassuan.def           |   3 +
 src/libassuan.vers          |   1 +
 src/system.c                |  18 +-
 src/sysutils.c              |  84 +++++
 tests/Makefile.am           |  11 +-
 tests/ce-createpipe.c       | 179 ++++++++++
 tests/ce-server.c           | 811 ++++++++++++++++++++++++++++++++++++++++++--
 tests/common.h              |  20 ++
 23 files changed, 2234 insertions(+), 97 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 0c22f98..50f499f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2010-02-24  Werner Koch  <wk at g10code.com>
+
+	* tests/ce-server.c: New.
+
+	* tests/ce-createpipe.c [W32CE]: New.
+
+2010-02-11  Werner Koch  <wk at g10code.com>
+
+	* configure.ac (inet_pton): Check for it.
+
+2010-02-04  Werner Koch  <wk at g10code.com>
+
+	* configure.ac (AC_TYPE_UINT16_T): New.
+
 2010-01-27  Werner Koch  <wk at g10code.com>
 
 	* tests/common.h (SOCKET2HANDLE, HANDLE2SOCKET): New.
diff --git a/NEWS b/NEWS
index d80c67a..739e043 100644
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,15 @@ Noteworthy changes in version 2.0.1 (unreleased)
 
  * Support for WindowsCE.
 
+ * Input and output notification handler can now really access the
+   parsed fd as stated in the manual.
+
+ * Interface changes relative to the 2.0.0 release:
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ assuan_free               NEW.
+ _assuan_w32ce_create_pipe NEW.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
 
 Noteworthy changes in version 2.0.0 (2010-01-08)
 ------------------------------------------------
diff --git a/configure.ac b/configure.ac
index 64e0cbb..8c9f3ab 100644
--- a/configure.ac
+++ b/configure.ac
@@ -224,6 +224,7 @@ AC_SUBST(LIBASSUAN_CONFIG_EXTRA_LIBS)
 AC_HEADER_STDC
 AC_CHECK_HEADERS([string.h locale.h sys/uio.h stdint.h inttypes.h])
 AC_TYPE_UINTPTR_T
+AC_TYPE_UINT16_T
 
 # Checks for typedefs, structures, and compiler characteristics.
 AC_C_CONST
@@ -267,7 +268,7 @@ AM_PATH_GPG_ERROR(1.8,, AC_MSG_ERROR([libgpg-error was not found]))
 #
 # Checks for library functions.
 #
-AC_CHECK_FUNCS([flockfile funlockfile])
+AC_CHECK_FUNCS([flockfile funlockfile inet_pton])
 
 # On some systems (e.g. Solaris) nanosleep requires linking to librl.
 # Given that we use nanosleep only as an optimization over a select
diff --git a/doc/assuan.texi b/doc/assuan.texi
index 28b68d1..1b86768 100644
--- a/doc/assuan.texi
+++ b/doc/assuan.texi
@@ -216,7 +216,7 @@ octets.  The default IPC mechanism is Unix Domain Sockets.
 
 On a connect request the server responds either with an okay or an
 error status.  For authentication-check the server may send an Inquiry
-Response prior to the first Okay, and it may also issue Status
+response prior to the first Okay, and it may also issue Status
 messages.  The server must check that the client is allowed to
 connect, this is done by requesting the credentials for the peer and
 comparing them to those of the server.  This avoids attacks based on
@@ -1379,7 +1379,8 @@ an @code{INPUT} command has been seen and successfully parsed.  The second
 argument passed to that function is the entire line.  Because that line
 has already been parsed when the function gets called, a file descriptor
 set with the @code{INPUT} command may already be used.  That file
-descriptor is available by calling @code{assuan_get_input_fd}.
+descriptor is available by calling @code{assuan_get_input_fd}.  If the
+notification function returns an error, the input fd does not change.
 @end deftypefun
 
 @deftypefun gpg_error_t assuan_register_output_notify (@w{assuan_context_t @var{ctx}}, @w{assuan_handler_t @var{handler}})
@@ -1390,7 +1391,8 @@ an @code{OUTPUT} command has been seen and successfully parsed.  The second
 argument passed to that function is the entire line.  Because that line
 has already been parsed when the function gets called, a file descriptor
 set with the @code{OUTPUT} command may already be used.  That file
-descriptor is available by calling @code{assuan_get_output_fd}.
+descriptor is available by calling @code{assuan_get_output_fd}. If the
+notification function returns an error, the output fd does not change.
 @end deftypefun
 
 @deftypefun gpg_error_t assuan_set_hello_line (@w{assuan_context_t @var{ctx}}, @w{const char *@var{line}})
diff --git a/src/ChangeLog b/src/ChangeLog
index e54e196..0fc7019 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,7 +1,46 @@
+2010-02-24  Werner Koch  <wk at g10code.com>
+
+	* gpgcemgr.c: New.
+	* gpgcedev.c: New.
+	* sysutils.c (_assuan_w32ce_create_pipe): Rewrote to make use of
+	the new driver.
+
+2010-02-16  Werner Koch  <wk at g10code.com>
+
+	* system.c (assuan_free): New.
+	* libassuan.vers (assuan_free): Add it.
+	* libassuan.def (assuan_free): Add it.
+
+2010-02-11  Werner Koch  <wk at g10code.com>
+
+	* assuan-inquire.c (assuan_inquire): Allow case insensitive
+	responses.
+	(_assuan_inquire_ext_cb): Ditto.
+
+2010-02-10  Werner Koch  <wk at g10code.com>
+
+	* assuan-handler.c (std_handler_input, std_handler_output): Make
+	the parsed FD available to the notification functions.  This is
+	the documented behaviour.
+
+2010-02-04  Werner Koch  <wk at g10code.com>
+
+	* assuan-socket-connect.c: Include stdint.h and arpa/inet.h.
+	(parse_portno): New.
+	(assuan_socket_connect): Return a correct error code on failure.
+	Support assuan:// and file:// schemes.
+
 2010-02-03  Marcus Brinkmann  <marcus at g10code.de>
 
 	* libassuan.vers, libassuan.def: Add assuan_set_sock_nonce.
 
+2010-02-01  Werner Koch  <wk at g10code.com>
+
+	* sysutils.c (_assuan_w32ce_create_pipe): New.
+	* libassuan.def (_assuan_w32ce_create_pipe): New.
+	* assuan-defs.h (CreateFile) [W32CE]: New macro
+	* system.c (__assuan_pipe): Make it work for W32CE.
+
 2010-01-28  Werner Koch  <wk at g10code.com>
 
 	* assuan.h: Remove ranges in list of copyright years.
diff --git a/src/Makefile.am b/src/Makefile.am
index 5895bc5..6403e79 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -25,6 +25,10 @@ bin_SCRIPTS = libassuan-config
 m4datadir = $(datadir)/aclocal
 m4data_DATA = libassuan.m4
 lib_LTLIBRARIES = libassuan.la
+if HAVE_W32CE_SYSTEM
+lib_LTLIBRARIES += libgpgcedev.la
+bin_PROGRAMS = gpgcemgr
+endif
 include_HEADERS = assuan.h
 
 if HAVE_LD_VERSION_SCRIPT
@@ -97,3 +101,12 @@ libassuan_la_LDFLAGS = $(libassuan_res_ldflag) $(no_undefined) \
 libassuan_la_DEPENDENCIES = @LTLIBOBJS@ \
 	$(srcdir)/libassuan.vers $(libassuan_deps)
 libassuan_la_LIBADD = @LTLIBOBJS@ @NETLIBS@ @GPG_ERROR_LIBS@
+
+if HAVE_W32CE_SYSTEM
+libgpgcedev_la_SOURCES = gpgcedev.c
+libgpgcedev_la_CPPFLAGS = $(AM_CPPFLAGS)
+libgpgcedev_la_LDFLAGS = $(no_undefined) -export-symbols $(srcdir)/gpgcedev.def
+libgpgcedev_la_DEPENDENCIES = gpgcedev.def
+gpgcemgr_SOURCES = gpgcemgr.c
+gpgcemgr_CPPFLAGS = $(AM_CPPFLAGS)
+endif
diff --git a/src/assuan-defs.h b/src/assuan-defs.h
index 1f288f4..6478a71 100644
--- a/src/assuan-defs.h
+++ b/src/assuan-defs.h
@@ -1,5 +1,6 @@
 /* assuan-defs.h - Internal definitions to Assuan
-   Copyright (C) 2001, 2002, 2004, 2005, 2007-2009 Free Software Foundation, Inc.
+   Copyright (C) 2001, 2002, 2004, 2005, 2007, 2008,
+                 2009, 2010 Free Software Foundation, Inc.
 
    This file is part of Assuan.
 
@@ -339,10 +340,12 @@ FILE *_assuan_funopen(void *cookie,
 const char *_assuan_sysutils_blurb (void);
 
 #ifdef HAVE_W32CE_SYSTEM
+
 #define getpid() GetCurrentProcessId ()
 char *_assuan_getenv (const char *name);
 #define getenv(a) _assuan_getenv ((a))
-#endif
+
+#endif /*HAVE_W32CE_SYSTEM*/
 
 
 /* Prototypes for replacement functions.  */
diff --git a/src/assuan-handler.c b/src/assuan-handler.c
index eacbeb3..9bb3010 100644
--- a/src/assuan-handler.c
+++ b/src/assuan-handler.c
@@ -234,7 +234,7 @@ assuan_command_parse_fd (assuan_context_t ctx, char *line, assuan_fd_t *rfd)
 	return set_error (ctx, GPG_ERR_ASS_SYNTAX, "number required");
 #ifdef HAVE_W32_SYSTEM
       /* Fixme: For a W32/64bit system we will need to change the cast
-         and the conversion fucntion.  */
+         and the conversion function.  */
       *rfd = (void*)strtoul (line, &endp, 10);
 #else
       *rfd = strtoul (line, &endp, 10);
@@ -259,14 +259,20 @@ static gpg_error_t
 std_handler_input (assuan_context_t ctx, char *line)
 {
   gpg_error_t rc;
-  assuan_fd_t fd;
+  assuan_fd_t fd, oldfd;
 
   rc = assuan_command_parse_fd (ctx, line, &fd);
   if (rc)
     return PROCESS_DONE (ctx, rc);
   if (ctx->input_notify_fnc)
-    rc = ctx->input_notify_fnc (ctx, line);
-  if (! rc)
+    {
+      oldfd = ctx->input_fd;
+      ctx->input_fd = fd;
+      rc = ctx->input_notify_fnc (ctx, line);
+      if (rc)
+        ctx->input_fd = oldfd;
+    }
+  else if (!rc)
     ctx->input_fd = fd;
   return PROCESS_DONE (ctx, rc);
 }
@@ -277,14 +283,20 @@ static gpg_error_t
 std_handler_output (assuan_context_t ctx, char *line)
 {
   gpg_error_t rc;
-  assuan_fd_t fd;
+  assuan_fd_t fd, oldfd;
 
   rc = assuan_command_parse_fd (ctx, line, &fd);
   if (rc)
     return PROCESS_DONE (ctx, rc);
   if (ctx->output_notify_fnc)
-    rc = ctx->output_notify_fnc (ctx, line);
-  if (!rc)
+    {
+      oldfd = ctx->output_fd;
+      ctx->output_fd = fd;
+      rc = ctx->output_notify_fnc (ctx, line);
+      if (rc)
+        ctx->output_fd = oldfd;
+    }
+  else if (!rc)
     ctx->output_fd = fd;
   return PROCESS_DONE (ctx, rc);
 }
diff --git a/src/assuan-inquire.c b/src/assuan-inquire.c
index 8772a15..265d37c 100644
--- a/src/assuan-inquire.c
+++ b/src/assuan-inquire.c
@@ -183,15 +183,23 @@ assuan_inquire (assuan_context_t ctx, const char *keyword,
           linelen = ctx->inbound.linelen;
         }    
       while (*line == '#' || !linelen);
-      if (line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
+
+      /* Note: As a convenience for manual testing we allow case
+         insensitive keywords.  */
+      if ((line[0] == 'E'||line[0] == 'e')
+          && (line[1] == 'N' || line[1] == 'n')
+          && (line[2] == 'D' || line[2] == 'd')
           && (!line[3] || line[3] == ' '))
         break; /* END command received*/
-      if (line[0] == 'C' && line[1] == 'A' && line[2] == 'N')
+      if ((line[0] == 'C' || line[0] == 'c')
+          && (line[1] == 'A' || line[1] == 'a')
+          && (line[2] == 'N' || line[2] == 'n'))
         {
           rc = _assuan_error (ctx, GPG_ERR_ASS_CANCELED);
           goto leave;
         }
-      if (line[0] != 'D' || line[1] != ' ' || nodataexpected)
+      if ((line[0] != 'D' && line[0] != 'd') 
+          || line[1] != ' ' || nodataexpected)
         {
           rc = _assuan_error (ctx, GPG_ERR_ASS_UNEXPECTED_CMD);
           goto leave;
@@ -268,19 +276,23 @@ _assuan_inquire_ext_cb (assuan_context_t ctx)
   linelen = ctx->inbound.linelen;
   mb = ctx->inquire_membuf;
 
-  if (line[0] == 'C' && line[1] == 'A' && line[2] == 'N')
+  if ((line[0] == 'C' || line[0] == 'c')
+      && (line[1] == 'A' || line[1] == 'a')
+      && (line[2] == 'N' || line[2] == 'n'))
     {
       rc = _assuan_error (ctx, GPG_ERR_ASS_CANCELED);
       goto leave;
     }
-  if (line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
+  if ((line[0] == 'E'||line[0] == 'e')
+      && (line[1] == 'N' || line[1] == 'n')
+      && (line[2] == 'D' || line[2] == 'd')
       && (!line[3] || line[3] == ' '))
     {
       rc = 0;
       goto leave;
     }
 
-  if (line[0] != 'D' || line[1] != ' ' || mb == NULL)
+  if ((line[0] != 'D' && line[0] != 'd') || line[1] != ' ' || mb == NULL)
     {
       rc = _assuan_error (ctx, GPG_ERR_ASS_UNEXPECTED_CMD);
       goto leave;
@@ -344,8 +356,8 @@ _assuan_inquire_ext_cb (assuan_context_t ctx)
  * @cb: A callback handler which is invoked after the operation completed.
  * @cb_data: A user-provided value passed to the callback handler.
  * 
- * A Server may use this to Send an inquire.  r_buffer, r_length and
- * maxlen may all be NULL/0 to indicate that no real data is expected.
+ * A server may use this to send an inquire.  R_BUFFER, R_LENGTH and
+ * MAXLEN may all be NULL/0 to indicate that no real data is expected.
  * When this function returns, 
  *
  * Return value: 0 on success or an ASSUAN error code
diff --git a/src/assuan-pipe-connect.c b/src/assuan-pipe-connect.c
index 0a04777..dec8ccc 100644
--- a/src/assuan-pipe-connect.c
+++ b/src/assuan-pipe-connect.c
@@ -382,7 +382,7 @@ socketpair_connect (assuan_context_t ctx,
           pipes are usually created using the `socketpair' function.
           It also enables features only available with such servers.
 
-   Bit 7: If set and there is a need to start ther server it will be
+   Bit 7: If set and there is a need to start the server it will be
           started as a background process.  This flag is useful under
           W32 systems, so that no new console is created and pops up a
           console window when starting the server
diff --git a/src/assuan-socket-connect.c b/src/assuan-socket-connect.c
index 3b20f19..13b8aa5 100644
--- a/src/assuan-socket-connect.c
+++ b/src/assuan-socket-connect.c
@@ -23,13 +23,18 @@
 #include <stdio.h>
 #include <string.h>
 #include <errno.h>
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
 #include <unistd.h>
 #include <sys/types.h>
-#ifndef HAVE_W32_SYSTEM
-#include <sys/socket.h>
-#include <sys/un.h>
+#ifdef HAVE_W32_SYSTEM
+# include <windows.h>
 #else
-#include <windows.h>
+# include <sys/socket.h>
+# include <sys/un.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
 #endif
 
 #include "assuan-defs.h"
@@ -53,19 +58,74 @@
 #endif
 
 
+#undef WITH_IPV6
+#if defined (AF_INET6) && defined(PF_INET) \
+    && defined (INET6_ADDRSTRLEN) && defined(HAVE_INET_PTON)
+# define WITH_IPV6 1
+#endif
+
+
+
+/* Returns true if STR represents a valid port number in decimal
+   notation and no garbage is following.  */
+static int 
+parse_portno (const char *str, uint16_t *r_port)
+{
+  unsigned int value;
+
+  for (value=0; *str && (*str >= '0' && *str <= '9'); str++)
+    {
+      value = value * 10 + (*str - '0');
+      if (value > 65535)
+        return 0;
+    }
+  if (*str || !value)
+    return 0;
+
+  *r_port = value;
+  return 1;
+}
+
+
+
 /* Make a connection to the Unix domain socket NAME and return a new
    Assuan context in CTX.  SERVER_PID is currently not used but may
-   become handy in the future.  With flags set to 1 sendmsg and
-   recvmsg are used. */
+   become handy in the future.  Defined flag bits are:
+
+     ASSUAN_SOCKET_CONNECT_FDPASSING
+        sendmsg and recvmsg are used.
+
+   NAME must either start with a slash and optional with a drive
+   prefix ("c:") or use one of these URL schemata:
+
+      file://<fname>
+
+        This is the same as the defualt just with an explicit schemata.
+
+      assuan://<ipaddr>:<port>
+      assuan://[<ip6addr>]:<port>
+         
+        Connect using TCP to PORT of the server with the numerical
+        IPADDR.  Not that the '[' and ']' are literal characters.
+
+  */
 gpg_error_t
 assuan_socket_connect (assuan_context_t ctx, const char *name,
 		       pid_t server_pid, unsigned int flags)
 {
-  gpg_error_t err;
+  gpg_error_t err = 0;
   assuan_fd_t fd;
-  struct sockaddr_un srvr_addr;
-  size_t len;
+#ifdef WITH_IPV6
+  struct sockaddr_in6 srvr_addr_in6;
+#endif
+  struct sockaddr_un srvr_addr_un;
+  struct sockaddr_in srvr_addr_in;
+  struct sockaddr *srvr_addr = NULL;
+  uint16_t port = 0;
+  size_t len = 0;
   const char *s;
+  int af = AF_LOCAL;
+  int pf = PF_LOCAL;
 
   TRACE2 (ctx, ASSUAN_LOG_CTX, "assuan_socket_connect", ctx,
 	  "name=%s, flags=0x%x", name ? name : "(null)", flags);
@@ -73,38 +133,127 @@ assuan_socket_connect (assuan_context_t ctx, const char *name,
   if (!ctx || !name)
     return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
 
-  /* We require that the name starts with a slash, so that we
-     eventually can reuse this function for other socket types.  To
-     make things easier we allow an optional driver prefix.  */
-  s = name;
-  if (*s && s[1] == ':')
-    s += 2;
-  if (*s != DIRSEP_C && *s != '/')
-    return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
+  if (!strncmp (name, "file://", 7) && name[7])
+    name += 7;
+  else if (!strncmp (name, "assuan://", 9) && name[9])
+    {
+      name += 9;
+      af = AF_INET;
+      pf = PF_INET;
+    }
+  else /* Default.  */
+    {
+      /* We require that the name starts with a slash if no URL
+         schemata is used.  To make things easier we allow an optional
+         driver prefix.  */
+      s = name;
+      if (*s && s[1] == ':')
+        s += 2;
+      if (*s != DIRSEP_C && *s != '/')
+        return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
+    }
 
-  if (strlen (name)+1 >= sizeof srvr_addr.sun_path)
-    return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
+  if (af == AF_LOCAL)
+    {
+      if (strlen (name)+1 >= sizeof srvr_addr_un.sun_path)
+        return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
 
-  fd = _assuan_sock_new (ctx, PF_LOCAL, SOCK_STREAM, 0);
+      memset (&srvr_addr_un, 0, sizeof srvr_addr_un);
+      srvr_addr_un.sun_family = AF_LOCAL;
+      strncpy (srvr_addr_un.sun_path, name, sizeof (srvr_addr_un.sun_path) - 1);
+      srvr_addr_un.sun_path[sizeof (srvr_addr_un.sun_path) - 1] = 0;
+      len = SUN_LEN (&srvr_addr_un);
+
+      srvr_addr = (struct sockaddr *)&srvr_addr_un;
+    }
+  else
+    {
+      char *addrstr, *p;
+      void *addrbuf = NULL;
+
+      addrstr = _assuan_malloc (ctx, strlen (name) + 1);
+      if (!addrstr)
+        return _assuan_error (ctx, gpg_err_code_from_syserror ());
+
+      if (*addrstr == '[')
+        {
+          strcpy (addrstr, name+1);
+          p = strchr (addrstr, ']');
+          if (!p || p[1] != ':' || !parse_portno (p+2, &port))
+            err = _assuan_error (ctx, GPG_ERR_BAD_URI);
+          else 
+            {
+              *p = 0;
+#ifdef WITH_IPV6
+              af = AF_INET6;
+              pf = PF_INET6;
+              memset (&srvr_addr_in6, 0, sizeof srvr_addr_in6);
+              srvr_addr_in6.sin6_family = af;
+              srvr_addr_in6.sin6_port = htons (port);
+              addrbuf = &srvr_addr_in6.sin6_addr;
+              srvr_addr = (struct sockaddr *)&srvr_addr_in6;
+              len = sizeof srvr_addr_in6;
+#else
+              err =  _assuan_error (ctx, GPG_ERR_EAFNOSUPPORT);
+#endif
+            }
+        }
+      else
+        {
+          strcpy (addrstr, name);
+          p = strchr (addrstr, ':');
+          if (!p || !parse_portno (p+1, &port))
+            err = _assuan_error (ctx, GPG_ERR_BAD_URI);
+          else
+            {
+              *p = 0;
+              memset (&srvr_addr_in, 0, sizeof srvr_addr_in);
+              srvr_addr_in.sin_family = af;
+              srvr_addr_in.sin_port = htons (port);
+              addrbuf = &srvr_addr_in.sin_addr;
+              srvr_addr = (struct sockaddr *)&srvr_addr_in;
+              len = sizeof srvr_addr_in;
+            }
+        }
+
+      if (!err)
+        {
+#ifdef HAVE_INET_PTON
+          switch (inet_pton (af, addrstr, addrbuf))
+            {
+            case 1:  break;
+            case 0:  err = _assuan_error (ctx, GPG_ERR_BAD_URI); break;
+            default: err = _assuan_error (ctx, gpg_err_code_from_syserror ());
+            }
+#else /*!HAVE_INET_PTON*/
+          /* We need to use the old function.  If we are here v6
+             support isn't enabled anyway and thus we can do fine
+             without.  Note that Windows as a compatible inet_pton
+             function named inetPton, but only since Vista.  */
+          srvr_addr_in.sin_addr.s_addr = inet_addr (addrstr);
+          if (srvr_addr_in.sin_addr.s_addr == INADDR_NONE)
+            err = _assuan_error (ctx, GPG_ERR_BAD_URI);
+#endif /*!HAVE_INET_PTON*/
+        }
+      
+      _assuan_free (ctx, addrstr);
+      if (err)
+        return err;
+    }
+  
+  fd = _assuan_sock_new (ctx, pf, SOCK_STREAM, 0);
   if (fd == ASSUAN_INVALID_FD)
     {
-      TRACE1 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect_ext", ctx,
-	      "can't create socket: %s", strerror (errno));
-      /* FIXME: Cleanup  */
-      return _assuan_error (ctx, GPG_ERR_ASS_GENERAL);
+      err = _assuan_error (ctx, gpg_err_code_from_syserror ());
+      TRACE1 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect", ctx,
+              "can't create socket: %s", strerror (errno));
+      return err;
     }
 
-  memset (&srvr_addr, 0, sizeof srvr_addr);
-  srvr_addr.sun_family = AF_LOCAL;
-  strncpy (srvr_addr.sun_path, name, sizeof (srvr_addr.sun_path) - 1);
-  srvr_addr.sun_path[sizeof (srvr_addr.sun_path) - 1] = 0;
-  len = SUN_LEN (&srvr_addr);
-
-  if (_assuan_sock_connect (ctx, fd, (struct sockaddr *) &srvr_addr, len) == -1)
+  if (_assuan_sock_connect (ctx, fd, srvr_addr, len) == -1)
     {
-      TRACE2 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect_ext", ctx,
+      TRACE2 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect", ctx,
 	      "can't connect to `%s': %s\n", name, strerror (errno));
-      /* FIXME: Cleanup */
       _assuan_close (ctx, fd);
       return _assuan_error (ctx, GPG_ERR_ASS_CONNECT_FAILED);
     }
@@ -129,14 +278,14 @@ assuan_socket_connect (assuan_context_t ctx, const char *name,
 
     err = _assuan_read_from_server (ctx, &response, &off);
     if (err)
-      TRACE1 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect_ext", ctx,
+      TRACE1 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect", ctx,
 	      "can't connect to server: %s\n", gpg_strerror (err));
     else if (response != ASSUAN_RESPONSE_OK)
       {
 	char *sname = _assuan_encode_c_string (ctx, ctx->inbound.line);
 	if (sname)
 	  {
-	    TRACE1 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect_ext", ctx,
+	    TRACE1 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect", ctx,
 		    "can't connect to server: %s", sname);
 	    _assuan_free (ctx, sname);
 	  }
diff --git a/src/assuan.h b/src/assuan.h
index ebc4bae..6509310 100644
--- a/src/assuan.h
+++ b/src/assuan.h
@@ -199,6 +199,10 @@ gpg_error_t assuan_new (assuan_context_t *ctx);
 /* Release all resources associated with the given context.  */
 void assuan_release (assuan_context_t ctx);
 
+/* Release the memory at PTR using the allocation handler of the
+   context CTX.  This is a convenience function.  */
+void assuan_free (assuan_context_t ctx, void *ptr);
+
 

 /* Set user-data in a context.  */
 void assuan_set_pointer (assuan_context_t ctx, void *pointer);
@@ -634,6 +638,13 @@ int __assuan_socketpair (assuan_context_t ctx, int _namespace, int style,
 extern struct assuan_system_hooks _assuan_system_pth;
 #define ASSUAN_SYSTEM_PTH &_assuan_system_pth
 
+#ifdef __MINGW32CE__
+/* FIXME: Include this code only if build for this platform.  */
+DWORD _assuan_w32ce_create_pipe (HANDLE *read_hd, HANDLE *write_hd,
+                                 LPSECURITY_ATTRIBUTES sec_attr, DWORD size);
+#define CreatePipe(a,b,c,d) _assuan_w32ce_create_pipe ((a),(b),(c),(d))
+
+#endif /*__MINGW32CE__*/
 
 #ifdef __cplusplus
 }
diff --git a/src/gpgcedev.c b/src/gpgcedev.c
new file mode 100644
index 0000000..763ab29
--- /dev/null
+++ b/src/gpgcedev.c
@@ -0,0 +1,720 @@
+/* gpgcedrv.c - WindowsCE device driver to implement a pipe.
+   Copyright (C) 2010 Free Software Foundation, Inc.
+
+   This file is part of Assuan.
+
+   Assuan 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 3 of
+   the License, or (at your option) any later version.
+
+   Assuan 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/>.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <windows.h>
+#include <devload.h>
+#include <winioctl.h>
+
+#define ENABLE_DEBUG
+#warning Cancel and caller process termination not handled.
+
+
+/* Missing IOCTLs in the current mingw32ce.  */
+#ifndef IOCTL_PSL_NOTIFY
+# define FILE_DEVICE_PSL 259
+# define IOCTL_PSL_NOTIFY                               \
+  CTL_CODE (259, 255, METHOD_NEITHER, FILE_ANY_ACCESS)
+#endif /*IOCTL_PSL_NOTIFY*/
+
+
+/* The IOCTL used to tell the device about the handle.
+
+   The required inbuf parameter is the address of a variable holding
+   the handle.  */
+#define GPGCEDEV_IOCTL_SET_HANDLE \
+  CTL_CODE (FILE_DEVICE_STREAMS, 2048, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+/* The IOCTL used to create the pipe. 
+
+   The caller sends this IOCTL to the read handle.  The required inbuf
+   parameter is the address of variable holding the write handle.
+   Note that the SET_HANDLE IOCTLs must have been used prior to this
+   one.  */
+#define GPGCEDEV_IOCTL_MAKE_PIPE \
+  CTL_CODE (FILE_DEVICE_STREAMS, 2049, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+
+/* An object to store information pertaining to an open-context.  */
+struct opnctx_s;
+typedef struct opnctx_s *opnctx_t;
+struct opnctx_s
+{
+  int inuse;        /* True if this object has valid data.  */
+  opnctx_t assoc;   /* This context has been associated with this
+                       other context; i.e. a pipe has been
+                       established.  */
+  int is_write;     /* True if this is the write end of the pipe.  */
+  HANDLE hd;        /* The system's handle object or INVALID_HANDLE_VALUE.  */
+  DWORD access_code;/* Value from OpenFile.  */
+  DWORD share_mode; /* Value from OpenFile.  */
+  CRITICAL_SECTION critsect;  /* Lock for all operations.  */
+  int locked;       /* True if we are in a critical section.  */
+
+  /* The malloced buffer and its size.  We use a buffer for each
+     handle which allows us eventually implement a system to
+     distribute data to several handles.  Not sure whether this is
+     really needed but as a side effect it makes the code easier. */
+  char *buffer;       
+  size_t buffer_size;
+  size_t buffer_len;  /* The valid length of the bufer.  */
+  size_t buffer_pos;  /* The actual read or write position.  */
+
+  HANDLE space_available; /* Set if space is available.  */
+  HANDLE data_available;  /* Set if data is available.  */
+};
+
+/* A malloced table of open-context and the number of allocated slots.  */
+static opnctx_t opnctx_table;
+static size_t   opnctx_table_size;
+
+/* A criticial section object used to protect the OPNCTX_TABLE.  */
+static CRITICAL_SECTION opnctx_table_cs;
+
+/* We don't need a device context thus we use the adress of the
+   critical section object for it.  */
+#define DEVCTX_VALUE ((DWORD)(&opnctx_table_cs))
+
+/* Constants used for our lock functions.  */
+#define LOCK_TRY  0
+#define LOCK_WAIT 1
+
+
+

+static void
+log_debug (const char *fmt, ...)
+{
+#ifndef ENABLE_DEBUG
+  (void)fmt;
+#else
+  va_list arg_ptr;
+  FILE *fp;
+
+  fp = fopen ("\\gpgcedev.log", "a+");
+  if (!fp)
+    return;
+  va_start (arg_ptr, fmt);
+  vfprintf (fp, fmt, arg_ptr);
+  va_end (arg_ptr);
+  fclose (fp);
+#endif
+}
+
+
+
+

+/* Return a new opnctx handle and mark it as used.  Returns NULL and
+   sets LastError on memory failure etc.  On success the context is
+   locked.  */
+static opnctx_t
+get_new_opnctx (void)
+{
+  opnctx_t opnctx = NULL;
+  int idx;
+
+  EnterCriticalSection (&opnctx_table_cs);
+  for (idx=0; idx < opnctx_table_size; idx++)
+    if (!opnctx_table[idx].inuse)
+      break;
+  if (idx == opnctx_table_size)
+    {
+      /* We need to increase the size of the table.  The approach we
+         take is straightforward to minimize the risk of bugs.  */
+      opnctx_t newtbl;
+      size_t newsize = opnctx_table_size + 64;
+
+      newtbl = calloc (newsize, sizeof *newtbl);
+      if (!newtbl)
+        goto leave;
+      for (idx=0; idx < opnctx_table_size; idx++)
+        newtbl[idx] = opnctx_table[idx];
+      free (opnctx_table);
+      opnctx_table = newtbl;
+      idx = opnctx_table_size;
+      opnctx_table_size = newsize;
+    }
+  opnctx = opnctx_table + idx;
+  opnctx->assoc = NULL;
+  opnctx->hd = INVALID_HANDLE_VALUE;
+  opnctx->assoc = 0;
+  opnctx->buffer_size = 512;
+  opnctx->buffer = malloc (opnctx->buffer_size);
+  if (!opnctx->buffer)
+    {
+      opnctx = NULL;
+      goto leave;
+    }
+  opnctx->buffer_len = 0;
+  opnctx->buffer_pos = 0;
+  opnctx->data_available = INVALID_HANDLE_VALUE;
+  opnctx->space_available = INVALID_HANDLE_VALUE;
+
+  opnctx->inuse = 1;
+  InitializeCriticalSection (&opnctx->critsect);
+  EnterCriticalSection (&opnctx->critsect);
+  opnctx->locked = 1;
+  
+ leave:
+  LeaveCriticalSection (&opnctx_table_cs);
+  log_debug ("get_new_opnctx -> %p\n", opnctx);
+  return opnctx;
+}
+
+
+/* Find the OPNCTX for handle HD.  */
+static opnctx_t
+find_and_lock_opnctx (HANDLE hd)
+{
+  opnctx_t result = NULL;
+  int idx;
+
+  EnterCriticalSection (&opnctx_table_cs);
+  for (idx=0; idx < opnctx_table_size; idx++)
+    if (opnctx_table[idx].inuse && opnctx_table[idx].hd == hd)
+      {
+        result = opnctx_table + idx;
+        break;
+      }
+  LeaveCriticalSection (&opnctx_table_cs);
+  if (!result)
+    SetLastError (ERROR_INVALID_HANDLE);
+  else if (TryEnterCriticalSection (&result->critsect))
+    result->locked++;
+  else
+    {
+      SetLastError (ERROR_BUSY);
+      result = NULL;
+    }
+  log_debug ("find_opnctx -> %p\n", result);
+  return result;
+}
+
+
+/* Check that OPNCTX is valid.  Returns TRUE if it is valid or FALSE
+   if it is a bad or closed contect.  In the latter case SetLastError
+   is called.  In the former case a lock is taken and unlock_opnctx
+   needs to be called.  If WAIT is false the fucntion only tries to
+   acquire a lock. */
+static BOOL
+validate_and_lock_opnctx (opnctx_t opnctx, int wait)
+{
+  BOOL result = FALSE;
+  int idx;
+
+  EnterCriticalSection (&opnctx_table_cs);
+  for (idx=0; idx < opnctx_table_size; idx++)
+    if (opnctx_table[idx].inuse && (opnctx_table + idx) == opnctx)
+      {
+        result = TRUE;
+        break;
+      }
+  LeaveCriticalSection (&opnctx_table_cs);
+
+  if (!result)
+    SetLastError (ERROR_INVALID_HANDLE);
+  else if (wait)
+    {
+      EnterCriticalSection (&opnctx->critsect);
+      opnctx->locked++;
+    }
+  else if (TryEnterCriticalSection (&opnctx->critsect))
+    opnctx->locked++;
+  else
+    {
+      SetLastError (ERROR_BUSY);
+      result = FALSE;
+    }
+  return result;
+}
+
+
+static void
+unlock_opnctx (opnctx_t opnctx)
+{
+  opnctx->locked--;
+  LeaveCriticalSection (&opnctx->critsect);
+}
+
+
+
+

+static char *
+wchar_to_utf8 (const wchar_t *string)
+{
+  int n;
+  size_t length = wcslen (string);
+  char *result;
+
+  n = WideCharToMultiByte (CP_UTF8, 0, string, length, NULL, 0, NULL, NULL);
+  if (n < 0 || (n+1) <= 0)
+    abort ();
+
+  result = malloc (n+1);
+  if (!result)
+    abort ();
+  n = WideCharToMultiByte (CP_ACP, 0, string, length, result, n, NULL, NULL);
+  if (n < 0)
+    abort ();
+  
+  result[n] = 0;
+  return result;
+}
+
+
+/* Initialize the device and return a device specific context.  */
+DWORD
+GPG_Init (LPCTSTR active_key, DWORD bus_context)
+{
+  char *tmpbuf;
+  (void)bus_context;
+  
+  tmpbuf = wchar_to_utf8 (active_key);
+  log_debug ("GPG_Init (%s)\n", tmpbuf);
+  free (tmpbuf);
+
+  /* We don't need any global data.  However, we need to return
+     something.  */
+  return DEVCTX_VALUE;
+}
+
+
+

+/* Deinitialize this device driver.  */
+BOOL
+GPG_Deinit (DWORD devctx)
+{
+  log_debug ("GPG_Deinit (%p)\n", (void*)devctx);
+  if (devctx != DEVCTX_VALUE)
+    {
+      SetLastError (ERROR_INVALID_PARAMETER);
+      return FALSE; /* Error.  */
+    }
+  
+  /* FIXME: Release resources.  */
+
+  return TRUE; /* Success.  */
+}
+
+
+

+/* Create a new open context.  This fucntion is called due to a
+   CreateFile from the application.  */
+DWORD
+GPG_Open (DWORD devctx, DWORD access_code, DWORD share_mode)
+{
+  opnctx_t opnctx;
+
+  log_debug ("GPG_Open(devctx=%p)\n", (void*)devctx);
+  if (devctx != DEVCTX_VALUE)
+    {
+      SetLastError (ERROR_INVALID_PARAMETER);
+      return 0; /* Error.  */
+    }
+
+  opnctx = get_new_opnctx ();
+  if (!opnctx)
+    return 0;
+  opnctx->access_code = access_code;
+  opnctx->share_mode = share_mode;
+
+  unlock_opnctx (opnctx);
+  return (DWORD)opnctx;
+}
+
+
+

+BOOL
+GPG_Close (DWORD opnctx_arg)
+{
+  opnctx_t opnctx = (opnctx_t)opnctx_arg;
+  BOOL result = FALSE;
+  int idx;
+
+  log_debug ("GPG_Close(%p)\n", (void*)opnctx);
+
+  EnterCriticalSection (&opnctx_table_cs);
+  for (idx=0; idx < opnctx_table_size; idx++)
+    if (opnctx_table[idx].inuse && (opnctx_table + idx) == opnctx)
+      {
+        if (opnctx->hd != INVALID_HANDLE_VALUE)
+          {
+            if (opnctx->assoc)
+              {
+                opnctx->assoc->assoc = NULL;
+                opnctx->assoc = NULL;
+              }
+            opnctx->hd = INVALID_HANDLE_VALUE;
+          }
+        if (opnctx->locked)
+          {
+            /* FIXME: Check earlier or use close only in locked state
+               or use PReClose.  */
+            log_debug ("GPG_Close while still locked\n");
+          }
+        DeleteCriticalSection (&opnctx->critsect);
+        if (opnctx->buffer)
+          {
+            free (opnctx->buffer);
+            opnctx->buffer = NULL;
+            opnctx->buffer_size = 0;
+          }
+        if (opnctx->space_available != INVALID_HANDLE_VALUE)
+          {
+            CloseHandle (opnctx->space_available);
+            opnctx->space_available = INVALID_HANDLE_VALUE;
+          }
+        if (opnctx->data_available != INVALID_HANDLE_VALUE)
+          {
+            CloseHandle (opnctx->data_available);
+            opnctx->data_available = INVALID_HANDLE_VALUE;
+          }
+        opnctx->inuse = 0;
+        result = TRUE;
+        break;
+      }
+  LeaveCriticalSection (&opnctx_table_cs);
+
+  if (!result)
+    SetLastError (ERROR_INVALID_HANDLE);
+  return result;
+}
+
+
+

+DWORD
+GPG_Read (DWORD opnctx_arg, void *buffer, DWORD count)
+{
+  opnctx_t rctx = (opnctx_t)opnctx_arg;
+  opnctx_t wctx;
+  int result = -1;
+  const char *src;
+  char *dst;
+
+  log_debug ("GPG_Read(%p, count=%d)\n", (void*)rctx, count);
+
+  /* We use the write end's buffer, thus there is no need to wait for
+     our (read end) lock.  */
+  if (!validate_and_lock_opnctx (rctx, LOCK_TRY))
+    return -1; /* Error.  */
+
+  if (rctx->is_write)
+    {
+      SetLastError (ERROR_INVALID_ACCESS);
+      goto leave;
+    }
+  if (rctx->hd == INVALID_HANDLE_VALUE || !rctx->assoc)
+    {
+      SetLastError (ERROR_BROKEN_PIPE);
+      goto leave;
+    }
+
+  /* Read from the corresponding write buffer.  */
+ retry:
+  wctx = rctx->assoc;
+  if (!validate_and_lock_opnctx (wctx, LOCK_WAIT))
+    goto leave;
+
+  if (wctx->buffer_pos == wctx->buffer_len)
+    {
+      unlock_opnctx (wctx);
+      log_debug ("%s:%d: WFSO(data_available)\n",  __func__, __LINE__);
+      WaitForSingleObject (wctx->data_available, INFINITE);
+      log_debug ("%s:%d: WFSO ... woke up\n",  __func__, __LINE__);
+      goto retry;
+    }
+  
+  dst = buffer;
+  src = wctx->buffer + wctx->buffer_pos;
+  while (count > 0 && wctx->buffer_pos < wctx->buffer_len)
+    {
+      *dst++ = *src++;
+      count--;
+      wctx->buffer_pos++;
+    }
+  result = (dst - (char*)buffer);
+  if (wctx->buffer_pos == wctx->buffer_len)
+    wctx->buffer_pos = wctx->buffer_len = 0;
+  
+  /* Now there should be some space available.  Signal the write end.
+     Even if COUNT was passed as NULL and no space is available,
+     signaling must be done.  */
+  if (!SetEvent (wctx->space_available))
+    {
+      log_debug ("%s:%d: SetEvent(space_available) failed: rc=%d\n",
+                 __func__, __LINE__, (int)GetLastError ());
+      unlock_opnctx (wctx);
+      goto leave;
+    }
+  unlock_opnctx (wctx);
+
+ leave:
+  unlock_opnctx (rctx);
+  return result;
+}
+
+
+

+DWORD
+GPG_Write (DWORD opnctx_arg, const void *buffer, DWORD count)
+{
+  opnctx_t wctx = (opnctx_t)opnctx_arg;
+  int result = -1;
+  const char *src;
+  char *dst;
+  size_t nwritten = 0;
+
+  log_debug ("GPG_Write(%p, count=%d)\n", (void*)wctx, count);
+ retry:
+  if (!validate_and_lock_opnctx (wctx, LOCK_WAIT))
+    return -1; /* Error.  */
+
+  if (!wctx->is_write)
+    {
+      SetLastError (ERROR_INVALID_ACCESS);
+      goto leave;
+    }
+  if (wctx->hd == INVALID_HANDLE_VALUE || !wctx->assoc)
+    {
+      SetLastError (ERROR_BROKEN_PIPE);
+      goto leave;
+    }
+  if (!count)
+    {
+      result = 0;
+      goto leave;
+    }
+
+  /* Write to our buffer.  */
+  if (wctx->buffer_len == wctx->buffer_size)
+    {
+      /* Buffer is full.  */
+      unlock_opnctx (wctx);
+      log_debug ("%s:%d: WFSO(space_available)\n",  __func__, __LINE__);
+      WaitForSingleObject (wctx->space_available, INFINITE);
+      log_debug ("%s:%d: WFSO ... woke up\n",  __func__, __LINE__);
+      goto retry;
+    }
+
+  src = buffer;
+  dst = wctx->buffer + wctx->buffer_len;
+  while (count > 0 && wctx->buffer_len < wctx->buffer_size)
+    {
+      *dst++ = *src++;
+      count--;
+      wctx->buffer_len++;
+      nwritten++;
+    }
+  if (!SetEvent (wctx->data_available))
+    {
+      log_debug ("%s:%d: SetEvent(data_available) failed: rc=%d\n",
+                 __func__, __LINE__, (int)GetLastError ());
+      goto leave;
+    }
+  result = nwritten;
+
+ leave:
+  unlock_opnctx (wctx);
+  return result;
+}
+
+
+

+DWORD
+GPG_Seek (DWORD opnctx, long amount, WORD type)
+{
+  SetLastError (ERROR_SEEK_ON_DEVICE);
+  return -1; /* Error.  */
+}
+
+
+

+static BOOL
+set_handle (opnctx_t opnctx, HANDLE hd)
+{
+  log_debug ("  set_handle(%p, hd=%p)\n", opnctx, hd);
+  if (opnctx->hd != INVALID_HANDLE_VALUE)
+    {
+      SetLastError (ERROR_ALREADY_ASSIGNED);
+      return FALSE;
+    }
+  opnctx->hd = hd;
+  return TRUE;
+}
+
+static BOOL
+make_pipe (opnctx_t rctx, HANDLE hd)
+{
+  BOOL result = FALSE;
+  opnctx_t wctx = NULL;
+
+  log_debug ("  make_pipe(%p, hd=%p)\n", rctx, hd);
+  if (rctx->hd == INVALID_HANDLE_VALUE)
+    {
+      SetLastError (ERROR_NOT_READY);
+      goto leave;
+    }
+  if (rctx->assoc)
+    {
+      SetLastError (ERROR_ALREADY_ASSIGNED);
+      goto leave;
+    }
+  if (!(rctx->access_code & GENERIC_READ))
+    {
+      SetLastError (ERROR_INVALID_ACCESS);
+      goto leave;
+    }
+
+  wctx = find_and_lock_opnctx (hd);
+  if (!wctx)
+    {
+      SetLastError (ERROR_NOT_FOUND);
+      goto leave;
+    }
+  if (wctx == rctx)
+    {
+      SetLastError (ERROR_INVALID_TARGET_HANDLE);
+      goto leave;
+    }
+  if (wctx->hd == INVALID_HANDLE_VALUE)
+    {
+      SetLastError (ERROR_NOT_READY);
+      goto leave;
+    }
+  if (wctx->assoc)
+    {
+      SetLastError (ERROR_ALREADY_ASSIGNED);
+      goto leave;
+    }
+  if (!(wctx->access_code & GENERIC_WRITE))
+    {
+      SetLastError (ERROR_INVALID_ACCESS);
+      goto leave;
+    }
+  wctx->space_available = CreateEvent (NULL, FALSE, FALSE, NULL);
+  wctx->data_available = CreateEvent (NULL, FALSE, FALSE, NULL);
+  
+  rctx->assoc = wctx;
+  wctx->assoc = rctx;
+  rctx->is_write = 0;
+  wctx->is_write = 1;
+  result = TRUE;
+
+ leave:
+  if (wctx)
+    unlock_opnctx (wctx);
+  return result;
+}
+
+
+BOOL
+GPG_IOControl (DWORD opnctx_arg, DWORD code, void *inbuf, DWORD inbuflen,
+               void *outbuf, DWORD outbuflen, DWORD *actualoutlen)
+{
+  opnctx_t opnctx = (opnctx_t)opnctx_arg;
+  BOOL result = FALSE;
+
+  log_debug ("GPG_IOControl(%p, %d)\n", (void*)opnctx, code);
+  if (!validate_and_lock_opnctx (opnctx, LOCK_TRY))
+    return FALSE;
+
+  switch (code)
+    {
+    case GPGCEDEV_IOCTL_SET_HANDLE:
+      if (!opnctx || !inbuf || inbuflen < sizeof (HANDLE) 
+          || outbuf || outbuflen || actualoutlen )
+        {
+          SetLastError (ERROR_INVALID_PARAMETER);
+          goto leave;
+        }
+      if (set_handle (opnctx, *(HANDLE*)inbuf))
+        result = TRUE;
+      break;
+
+    case GPGCEDEV_IOCTL_MAKE_PIPE:
+      if (!opnctx || !inbuf || inbuflen < sizeof (HANDLE) 
+          || outbuf || outbuflen || actualoutlen )
+        {
+          SetLastError (ERROR_INVALID_PARAMETER);
+          goto leave;
+        }
+      if (make_pipe (opnctx, *(HANDLE*)inbuf))
+        result = TRUE;
+      break;
+
+    case IOCTL_PSL_NOTIFY:
+      /* Unexpected process termination.  */
+      break;
+
+    default:
+      SetLastError (ERROR_INVALID_PARAMETER);
+      break;
+    }
+
+ leave:
+  unlock_opnctx (opnctx);
+  return result;
+}
+
+
+

+void
+GPG_PowerUp (DWORD devctx)
+{
+}
+
+
+

+void
+GPG_PowerDown (DWORD devctx)
+{
+}
+
+
+
+

+/* Entry point called by the DLL loader.  */
+int WINAPI
+DllMain (HINSTANCE hinst, DWORD reason, LPVOID reserved)
+{
+  (void)reserved;
+
+  switch (reason)
+    {
+    case DLL_PROCESS_ATTACH:
+      InitializeCriticalSection (&opnctx_table_cs);
+      break;
+
+    case DLL_THREAD_ATTACH:
+      break;
+
+    case DLL_THREAD_DETACH:
+      break;
+
+    case DLL_PROCESS_DETACH:
+      DeleteCriticalSection (&opnctx_table_cs);
+      break;
+
+    default:
+      break;
+    }
+  
+  return TRUE;
+}
+
diff --git a/src/gpgcedev.def b/src/gpgcedev.def
new file mode 100644
index 0000000..387e3a6
--- /dev/null
+++ b/src/gpgcedev.def
@@ -0,0 +1,33 @@
+; gpgcedev.def - List of symbols to export for gpgcedev.
+; Copyright (C) 2010 Free Software Foundation, Inc.
+;
+; This file is part of Assuan.
+;
+; Assuan 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 3 of
+; the License, or (at your option) any later version.
+;
+; Assuan 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/>.
+
+
+EXPORTS
+    GPG_Init
+    GPG_Deinit
+    GPG_Open
+    GPG_Close
+    GPG_Read
+    GPG_Write
+    GPG_Seek
+    GPG_IOControl
+    GPG_PowerUp
+    GPG_PowerDown
+
+; END
+
diff --git a/src/gpgcemgr.c b/src/gpgcemgr.c
new file mode 100644
index 0000000..da51602
--- /dev/null
+++ b/src/gpgcemgr.c
@@ -0,0 +1,72 @@
+/* gpgcempg.c - Manager fopr GPG CE devices
+   Copyright (C) 2010 Free Software Foundation, Inc.
+
+   This file is part of Assuan.
+
+   Assuan 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 3 of
+   the License, or (at your option) any later version.
+
+   Assuan 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/>.
+ */
+
+#define _WIN32_WCE 0x0500
+
+#include <stdio.h>
+#include <windows.h>
+
+#define PGM "gpgcemgr"
+
+#warning Fixme: Add support to create the device.
+
+int
+main (int argc, char **argv)
+{
+  int result = 0;
+  HANDLE shd;
+  DEVMGR_DEVICE_INFORMATION dinfo;
+
+  memset (&dinfo, 0, sizeof dinfo);
+  dinfo.dwSize = sizeof dinfo;
+  shd = FindFirstDevice (DeviceSearchByLegacyName, L"GPG1:", &dinfo);
+  if (shd == INVALID_HANDLE_VALUE)
+    {
+      if (GetLastError () == 18)
+        fprintf (stderr, PGM": device not found\n");
+      else
+        {
+          fprintf (stderr, PGM": FindFirstDevice failed: rc=%d\n", 
+                   (int)GetLastError ());
+          result = 1;
+        }
+    }
+  else
+    {
+      fprintf (stderr, PGM": ActivateDevice handle is %p\n", dinfo.hDevice);
+      if (dinfo.hDevice && dinfo.hDevice != INVALID_HANDLE_VALUE)
+        {
+          if (!DeactivateDevice (dinfo.hDevice))
+            {
+              fprintf (stderr, PGM": DeactivateDevice failed: rc=%d\n",
+                       (int)GetLastError ());
+              result = 1;
+            }
+          else
+            fprintf (stderr, PGM": DeactivateDevice succeeded\n");
+        }
+      FindClose (shd);
+    }
+  fflush (stdout);
+  fflush (stderr);
+  Sleep (1000);
+  return result;
+}
+
+
diff --git a/src/libassuan.def b/src/libassuan.def
index 0dda563..ba8ed8d 100644
--- a/src/libassuan.def
+++ b/src/libassuan.def
@@ -97,5 +97,8 @@ EXPORTS
     assuan_client_read_response		@76
     assuan_client_parse_response	@77
     assuan_set_sock_nonce               @78
+    _assuan_w32ce_create_pipe           @79
+    assuan_free                         @80
+
 ; END
 
diff --git a/src/libassuan.vers b/src/libassuan.vers
index 3a2b01a..b91d8e4 100644
--- a/src/libassuan.vers
+++ b/src/libassuan.vers
@@ -97,6 +97,7 @@ LIBASSUAN_1.0 {
     assuan_transact;
     assuan_write_line;
     assuan_write_status;
+    assuan_free;
 
     __assuan_close;
     __assuan_pipe;
diff --git a/src/system.c b/src/system.c
index 09c2b8c..fa942f7 100644
--- a/src/system.c
+++ b/src/system.c
@@ -113,6 +113,16 @@ _assuan_free (assuan_context_t ctx, void *ptr)
     ctx->malloc_hooks.free (ptr);
 }
 
+
+/* Release the memory at PTR using the allocation handler of the
+   context CTX.  This is a convenience function.  */
+void
+assuan_free (assuan_context_t ctx, void *ptr)
+{
+  _assuan_free (ctx, ptr);
+}
+
+
 

 /* Copy the system hooks struct, paying attention to version
    differences.  SRC is usually from the user, DST MUST be from the
@@ -203,19 +213,13 @@ __assuan_pipe (assuan_context_t ctx, assuan_fd_t fd[2], int inherit_idx)
   sec_attr.nLength = sizeof (sec_attr);
   sec_attr.bInheritHandle = FALSE;
 
-#ifdef HAVE_W32CE_SYSTEM
-# warning Implement a CreatePipe Replacement.  
-      gpg_err_set_errno (EIO);
-      return -1;
-#else
-  if (! CreatePipe (&rh, &wh, &sec_attr, 0))
+  if (!CreatePipe (&rh, &wh, &sec_attr, 0))
     {
       TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_pipe", ctx,
 	      "CreatePipe failed: %s", _assuan_w32_strerror (ctx, -1));
       gpg_err_set_errno (EIO);
       return -1;
     }
-#endif
 
   if (! DuplicateHandle (GetCurrentProcess(), (inherit_idx == 0) ? rh : wh,
 			 GetCurrentProcess(), &th, 0,
diff --git a/src/sysutils.c b/src/sysutils.c
index 448f803..c7e20a8 100644
--- a/src/sysutils.c
+++ b/src/sysutils.c
@@ -26,10 +26,22 @@
 #include <stdarg.h>
 #ifdef HAVE_W32_SYSTEM
 #include <windows.h>
+# ifdef HAVE_W32CE_SYSTEM
+# include <winioctl.h>
+# include <devload.h>
+# endif /*HAVE_W32CE_SYSTEM*/
 #endif /*HAVE_W32_SYSTEM*/
 
 #include "assuan-defs.h"
 
+#ifdef HAVE_W32CE_SYSTEM
+#define GPGCEDEV_IOCTL_SET_HANDLE                                      \
+  CTL_CODE (FILE_DEVICE_STREAMS, 2048, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define GPGCEDEV_IOCTL_MAKE_PIPE                                        \
+  CTL_CODE (FILE_DEVICE_STREAMS, 2049, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#endif /*HAVE_W32CE_SYSTEM*/
+
+
 
 /* This is actually a dummy function to make sure that is module is
    not empty.  Sokme compilers barf on that.  */
@@ -128,3 +140,75 @@ _assuan_getenv (const char *name)
     return NULL;
 }
 #endif /*HAVE_W32CE_SYSTEM*/
+
+
+#ifdef HAVE_W32_SYSTEM
+/* WindowsCE does not provide a pipe feature.  However we need
+   something like a pipe to convey data between processes and in some
+   cases within a process.  This replacement is not only used by
+   libassuan but exported and thus usable by gnupg and gpgme as well.  */
+DWORD
+_assuan_w32ce_create_pipe (HANDLE *read_hd, HANDLE *write_hd,
+                           LPSECURITY_ATTRIBUTES sec_attr, DWORD size)
+{
+#ifdef HAVE_W32CE_SYSTEM
+  HANDLE hd[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
+
+  *read_hd = *write_hd = INVALID_HANDLE_VALUE;
+
+  ActivateDevice (L"Drivers\\GnuPG_Device", 0);
+
+  /* Note: Using "\\$device\\GPG1" should be identical to "GPG1:".
+     However this returns an invalid parameter error without having
+     called GPG_Init in the driver.  The docs mention something about
+     RegisterAFXEx but that API is not documented.  */
+  hd[0] = CreateFile (L"GPG1:", GENERIC_READ,
+                      FILE_SHARE_READ | FILE_SHARE_WRITE,
+                      NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+  if (hd[0] == INVALID_HANDLE_VALUE)
+    return 0;
+
+  if (!DeviceIoControl (hd[0], GPGCEDEV_IOCTL_SET_HANDLE,
+                        &hd[0], sizeof hd[0], NULL, 0, NULL, NULL))
+    fprintf (stderr, "GPGCEDEV_IOCTL_SET_HANDLE(0) failed: %d\n", 
+             (int)GetLastError ());
+  
+  hd[1] = CreateFile (L"GPG1:", GENERIC_WRITE,
+                      FILE_SHARE_READ | FILE_SHARE_WRITE,
+                      NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,NULL);
+  if (hd[1] == INVALID_HANDLE_VALUE)
+    {
+      DWORD lasterr = GetLastError ();
+      CloseHandle (hd[0]);
+      SetLastError (lasterr);
+      return 0;
+    }
+  if (!DeviceIoControl (hd[1], GPGCEDEV_IOCTL_SET_HANDLE,
+                        &hd[1], sizeof hd[1], NULL, 0, NULL, NULL))
+    fprintf (stderr, "GPGCEDEV_IOCTL_SET_HANDLE(1) failed: %d\n", 
+             (int)GetLastError ());
+
+  if (!DeviceIoControl (hd[0], GPGCEDEV_IOCTL_MAKE_PIPE,
+                        &hd[1], sizeof hd[1], NULL, 0, NULL, NULL))
+    {
+      fprintf (stderr, "GPGCEDEV_IOCTL_MAKE_PIPE failed: %d\n", 
+               (int)GetLastError ());
+      if (hd[0] != INVALID_HANDLE_VALUE)
+        CloseHandle (hd[0]);
+      if (hd[1] != INVALID_HANDLE_VALUE)
+        CloseHandle (hd[1]);
+      return 0;
+    }
+  else
+    {
+      *read_hd = hd[0];
+      *write_hd = hd[1];
+      return 1;
+    }
+#else /*!HAVE_W32CE_SYSTEM*/
+  return CreatePipe (read_hd, write_hd, sec_attr, size);
+#endif /*!HAVE_W32CE_SYSTEM*/
+}
+
+#endif /*!HAVE_W32_SYSTEM*/
+
diff --git a/tests/Makefile.am b/tests/Makefile.am
index c243c27..692748c 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -20,20 +20,25 @@
 
 TESTS_ENVIRONMENT = 
 
-EXTRA_DIST = motd
+EXTRA_DIST = motd ce-createpipe.c
 
 BUILT_SOURCES = 
 CLEANFILES = 
 
-TESTS = ce-server
+TESTS = 
+
+if HAVE_W32CE_SYSTEM
+w32cetools = ce-createpipe
+endif
 
 if USE_DESCRIPTOR_PASSING
 TESTS += fdpassing
 endif
 
+
 AM_CFLAGS = $(GPG_ERROR_CFLAGS)
 
 noinst_HEADERS = common.h
-noinst_PROGRAMS = $(TESTS)
+noinst_PROGRAMS = $(TESTS) ce-server $(w32cetools)
 LDADD = ../src/libassuan.la  $(NETLIBS) $(GPG_ERROR_LIBS)
 
diff --git a/tests/ce-createpipe.c b/tests/ce-createpipe.c
new file mode 100644
index 0000000..810edbc
--- /dev/null
+++ b/tests/ce-createpipe.c
@@ -0,0 +1,179 @@
+/* ce-createpipe.c - Test the W32CE CreatePipe implementation.
+   Copyright (C) 2010 Free Software Foundation, Inc.
+
+   This file is part of Assuan.
+
+   Assuan 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 3 of
+   the License, or (at your option) any later version.
+
+   Assuan 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include "common.h"
+
+#include "../src/assuan.h" /* We need the CreatePipe prototype.  */
+
+
+static DWORD
+reader_thread (void *arg)
+{
+  HANDLE hd = arg;
+  DWORD nread;
+  char buffer[16];
+
+  for (;;)
+    {
+      if (!ReadFile (hd, buffer, sizeof buffer, &nread, FALSE))
+	{
+	  log_error ("reader: ReadFile failed: rc=%d\n", (int)GetLastError ());
+	  break;
+	}
+      log_info ("reader: red %d bytes\n", (int)nread);
+      log_printhex ("got: ", buffer, nread);
+    }
+
+  log_info ("reader: finished\n");
+  CloseHandle (hd);
+  return 0;
+}
+
+
+static DWORD
+writer_thread (void *arg)
+{
+  HANDLE hd = arg;
+  DWORD nwritten;
+  int i = 0;
+  int j;
+  char buffer[20];
+  int count;
+
+  for (count=0; count < 30; count++)
+    {
+      for (j=0; j < sizeof buffer; j++)
+        buffer[j] = i++;
+
+      if (!WriteFile (hd, buffer, sizeof buffer, &nwritten, NULL))
+	{
+	  log_error ("writer: WriteFile failed: rc=%d\n", (int)GetLastError ());
+	  break;
+	}
+      if (nwritten != sizeof buffer)
+        log_info ("writer: wrote only %d bytes\n", (int)nwritten);
+    }
+
+  log_info ("writer: finished\n");
+  CloseHandle (hd);
+  return 0;
+}
+
+
+
+static void
+run_test (void)
+{
+  HANDLE hd[2];
+  HANDLE threads[2];
+
+  if (!CreatePipe (&hd[0], &hd[1], NULL, 0))
+    {
+      log_error ("CreatePipe failed: rc=%d\n", (int)GetLastError ());
+      return;
+    }
+  log_info ("pipe created read=%p write=%p\n", hd[0], hd[1]);
+  
+  threads[0] = CreateThread (NULL, 0, reader_thread, hd[0], 0, NULL);
+  if (!threads[0])
+    log_fatal ("error creating reader thread: rc=%d\n", (int)GetLastError ());
+  else
+    log_info ("reader thread created\n");
+  threads[1] = CreateThread (NULL, 0, writer_thread, hd[1], 0, NULL);
+  if (!threads[0])
+    log_fatal ("error creating writer thread: rc=%d\n", (int)GetLastError ());
+  else
+    log_info ("writer thread created\n");
+
+  switch (WaitForMultipleObjects (2, threads, FALSE, INFINITE))
+    {
+    case WAIT_OBJECT_0:
+      log_info ("reader thread finished firstt\n");
+      break;
+    case WAIT_OBJECT_0 + 1:
+      log_info ("writer thread finished first\n");
+      break;
+    default:
+      log_error ("WFMO failed: rc=%d\n", (int)GetLastError ());
+      break;
+    }
+
+  
+
+  CloseHandle (threads[0]);
+  CloseHandle (threads[1]);
+}
+
+
+
+/* 
+     M A I N
+ */
+int 
+main (int argc, char **argv)
+{
+  int last_argc = -1;
+
+  if (argc)
+    {
+      log_set_prefix (*argv);
+      argc--; argv++;
+    }
+  while (argc && last_argc != argc )
+    {
+      last_argc = argc;
+      if (!strcmp (*argv, "--help"))
+        {
+          printf ("usage: %s [options]\n"
+                  "\n"
+                  "Options:\n"
+                  "  --verbose      Show what is going on\n",
+                  log_get_prefix ());
+          exit (0);
+        }
+      if (!strcmp (*argv, "--verbose"))
+        {
+          verbose = 1;
+          argc--; argv++;
+        }
+      else if (!strcmp (*argv, "--debug"))
+        {
+          verbose = debug = 1;
+          argc--; argv++;
+        }
+    }
+
+  run_test ();
+
+  return errorcount ? 1 : 0;
+}
+
diff --git a/tests/ce-server.c b/tests/ce-server.c
index 1b242aa..6ff3cae 100644
--- a/tests/ce-server.c
+++ b/tests/ce-server.c
@@ -39,10 +39,12 @@
 #include <errno.h>
 
 #ifdef HAVE_W32CE_SYSTEM
-#ifndef FILE_ATTRIBUTE_ROMSTATICREF
-#define FILE_ATTRIBUTE_ROMSTATICREF FILE_ATTRIBUTE_OFFLINE
-#endif
-#endif
+# ifndef FILE_ATTRIBUTE_ROMSTATICREF
+# define FILE_ATTRIBUTE_ROMSTATICREF FILE_ATTRIBUTE_OFFLINE
+# endif
+extern BOOL GetStdioPathW (int, wchar_t *, DWORD *);
+extern BOOL SetStdioPathW (int, const wchar_t *);
+#endif /*!HAVE_W32CE_SYSTEM*/
 
 #include "../src/assuan.h"
 
@@ -54,26 +56,201 @@ static short server_port = 15898;
 /* Flag set to indicate a shutdown.  */
 static int shutdown_pending;
 
+/* An object to keep track of file descriptors.  */
+struct fdinfo_s
+{
+  struct fdinfo_s *next;
+  assuan_fd_t fd;  /* The descriptor.  */
+};
+typedef struct fdinfo_s *fdinfo_t;
+
+
 /* The local state of a connection.  */
 struct state_s
 {
-  char *cwd;  /* The current working directory - access using get_cwd().  */
+  /* The current working directory - access using get_cwd().  */
+  char *cwd;  
+  
+  /* If valid, a socket in listening state created by the dataport
+     command.  */
+  assuan_fd_t dataport_listen_fd;
+
+  /* If valid the socket accepted for the dataport.  */
+  assuan_fd_t dataport_accepted_fd;
+
+  /* The error code from a dataport accept operation.  */
+  gpg_error_t dataport_accept_err;
+
+  /* A list of all unused descriptors created by dataport commands.  */
+  fdinfo_t dataport_fds;
+
+  /* The descriptor set by the DATAPORT command.  */
+  assuan_fd_t dataport_fd;
 };
 typedef struct state_s *state_t;
 
 
 

+/* Local prototypes.  */
+static gpg_error_t cmd_newdataport_cont (void *opaque, gpg_error_t err,
+                                         unsigned char *data, size_t datalen);
+
+
+

+/* A wrapper around read to make it work under Windows with HANDLES
+   and socket descriptors.   Takes care of EINTR on POSIX. */
+static int
+my_read (assuan_fd_t fd, void *buffer, size_t size)
+{
+  int res;
+
+#ifdef HAVE_W32_SYSTEM
+  res = recv (HANDLE2SOCKET (fd), buffer, size, 0);
+  if (res == -1)
+    {
+      switch (WSAGetLastError ())
+        {
+        case WSAENOTSOCK:
+          {
+            DWORD nread = 0;
+            
+            res = ReadFile (fd, buffer, size, &nread, NULL);
+            if (!res)
+              {
+                switch (GetLastError ())
+                  {
+                  case ERROR_BROKEN_PIPE:
+		    gpg_err_set_errno (EPIPE);
+		    break;
+
+                  default:
+		    gpg_err_set_errno (EIO); 
+                  }
+                res = -1;
+              }
+            else
+              res = (int) nread;
+          }
+          break;
+          
+        case WSAEWOULDBLOCK:
+	  gpg_err_set_errno (EAGAIN);
+	  break;
+
+        case ERROR_BROKEN_PIPE:
+	  gpg_err_set_errno (EPIPE);
+	  break;
+
+        default:
+	  gpg_err_set_errno (EIO);
+	  break;
+        }
+    }
+  return res;
+#else	/*!HAVE_W32_SYSTEM*/
+  do
+    res = read (fd, buffer, size);
+  while (res == -1 && errno == EINTR);
+  return res;
+#endif	/*!HAVE_W32_SYSTEM*/
+}
+
+
+/* Extended version of write(2) to guarantee that all bytes are
+   written.  Returns 0 on success or -1 and ERRNO on failure.  NOTE:
+   This function does not return the number of bytes written, so any
+   error must be treated as fatal for this connection as the state of
+   the receiver is unknown.  This works best if blocking is allowed
+   (so EAGAIN cannot occur).  Under Windows this function handles
+   socket descriptors and system handles.  */
+static int
+my_writen (assuan_fd_t fd, const char *buffer, size_t length)
+{
+  while (length)
+    {
+      int nwritten;
+#ifdef HAVE_W32_SYSTEM
+      nwritten = send (HANDLE2SOCKET (fd), buffer, length, 0);
+      if (nwritten == -1 && WSAGetLastError () == WSAENOTSOCK)
+        {
+          DWORD nwrite;
+          
+          nwritten = WriteFile (fd, buffer, length, &nwrite, NULL);
+          if (!nwritten)
+            {
+              switch (GetLastError ())
+                {
+                case ERROR_BROKEN_PIPE: 
+                case ERROR_NO_DATA:
+                  gpg_err_set_errno (EPIPE);
+                  break;
+                  
+                default:
+                  gpg_err_set_errno (EIO);
+                  break;
+                }
+              nwritten= -1;
+            }
+          else
+            nwritten = (int)nwrite;
+        }
+#else	/*!HAVE_W32_SYSTEM*/
+      nwritten = write (fd, buffer, length);
+#endif	/*!HAVE_W32_SYSTEM*/
+      if (nwritten < 0)
+        {
+          if (errno == EINTR)
+            continue;
+          return -1; /* write error */
+        }
+      length -= nwritten;
+      buffer += nwritten;
+    }
+  return 0;  /* okay */
+}
+
+
+

+static state_t 
+new_state (void)
+{
+  state_t state = xcalloc (1, sizeof *state);
+  state->dataport_listen_fd = ASSUAN_INVALID_FD;
+  state->dataport_accepted_fd = ASSUAN_INVALID_FD;
+  state->dataport_fd = ASSUAN_INVALID_FD;
+  return state;
+}
+  
 static void 
 release_state (state_t state)
 {
+  fdinfo_t fi, fi2;
+
   if (!state)
     return;
+
   xfree (state->cwd);
+
+  if (state->dataport_fd != ASSUAN_INVALID_FD)
+    assuan_sock_close (state->dataport_fd);
+  if (state->dataport_listen_fd != ASSUAN_INVALID_FD)
+    assuan_sock_close (state->dataport_listen_fd);
+  if (state->dataport_accepted_fd != ASSUAN_INVALID_FD)
+    assuan_sock_close (state->dataport_accepted_fd);
+
+  for (fi=state->dataport_fds; fi; fi = fi2)
+    {
+      fi2 = fi->next;
+      if (fi->fd != ASSUAN_INVALID_FD)
+        assuan_sock_close (fi->fd);
+    }
+
   xfree (state);
 }
 
 
-/* Helper to print a message while leaving a command.  */
+/* Helper to print a message while leaving a command and to
+   acknowledge the command.  */
 static gpg_error_t
 leave_cmd (assuan_context_t ctx, gpg_error_t err)
 {
@@ -88,7 +265,7 @@ leave_cmd (assuan_context_t ctx, gpg_error_t err)
         log_error ("command '%s' failed: %s <%s>\n", name,
                    gpg_strerror (err), gpg_strsource (err));
     }
-  return err;
+  return assuan_process_done (ctx, err);
 }
 
 
@@ -167,7 +344,9 @@ get_cwd (state_t state)
     {
       /* No working directory yet.  On WindowsCE make it the module
          directory of this process.  */
+#ifdef HAVE_W32_SYSTEM
       char *p;
+#endif
 #ifdef HAVE_W32CE_SYSTEM
       wchar_t buf[MAX_PATH+1];
       size_t n;
@@ -197,10 +376,63 @@ get_cwd (state_t state)
 }
 
 
+

+static gpg_error_t
+reset_notify (assuan_context_t ctx, char *line)
+{
+  state_t state = assuan_get_pointer (ctx);
+  fdinfo_t fi, fi2;
 
+  /* Close all lingering dataport connections.  */
+  for (fi=state->dataport_fds; fi; fi = fi2)
+    {
+      fi2 = fi->next;
+      if (fi->fd != ASSUAN_INVALID_FD)
+        assuan_sock_close (fi->fd);
+    }
+  state->dataport_fds = NULL;
 
+  return 0;
+}
 
 
+static gpg_error_t
+input_notify (assuan_context_t ctx, char *line)
+{
+  state_t state = assuan_get_pointer (ctx);
+  assuan_fd_t fd = assuan_get_input_fd (ctx);
+  fdinfo_t fi;
+
+  if (fd != ASSUAN_INVALID_FD)
+    {
+      /* The fd is now in use use - remove it from the unused list.  */
+      for (fi=state->dataport_fds; fi; fi = fi->next)
+        if (fi->fd == fd)
+          fi->fd = ASSUAN_INVALID_FD;
+    }
+
+  return 0;
+}
+
+
+static gpg_error_t
+output_notify (assuan_context_t ctx, char *line)
+{
+  state_t state = assuan_get_pointer (ctx);
+  assuan_fd_t fd = assuan_get_output_fd (ctx);
+  fdinfo_t fi;
+
+  if (fd != ASSUAN_INVALID_FD)
+    {
+      /* The fd is now in use - remove it from the unused list.  */
+      for (fi=state->dataport_fds; fi; fi = fi->next)
+        if (fi->fd == fd)
+          fi->fd = ASSUAN_INVALID_FD;
+    }
+
+  return 0;
+}
+
 
 

 static const char hlp_echo[] = 
@@ -218,6 +450,116 @@ cmd_echo (assuan_context_t ctx, char *line)
 }
 
 
+

+static const char hlp_cat[] = 
+  "CAT [<filename>]\n"
+  "\n"
+  "Copy the content of FILENAME to the descriptor set by the OUTPUT\n"
+  "command.  If no OUTPUT command has been given, send the content\n"
+  "using data lines.  Without FILENAME take the content from the\n"
+  "descriptor set by the INPUT command; if a DATAPORT has been set\n"
+  "this descriptor is used for I/O and the INOPUT/OUTPUT descriptors\n"
+  "are not touched.";
+static gpg_error_t
+cmd_cat (assuan_context_t ctx, char *line)
+{
+  state_t state = assuan_get_pointer (ctx);
+  gpg_error_t err = 0;
+  assuan_fd_t fd_in = ASSUAN_INVALID_FD;
+  assuan_fd_t fd_out = ASSUAN_INVALID_FD;
+  FILE *fp_in = NULL;
+  char buf[256];
+  size_t nread;
+  int use_dataport = 0;
+
+  if (*line)
+    {
+      fp_in = fopen (line, "rb");
+      if (!fp_in)
+        err = gpg_error_from_syserror ();
+      else
+        fd_out = assuan_get_output_fd (ctx);
+    }
+  else if (state->dataport_fd != ASSUAN_INVALID_FD)
+    {
+      use_dataport = 1;
+      fd_in = state->dataport_fd;
+      fd_out = state->dataport_fd;
+    }
+  else if ((fd_in = assuan_get_input_fd (ctx)) != ASSUAN_INVALID_FD)
+    {
+      /* This FD is actually a socket descriptor.  We can't fdopen it
+         because under Windows we ust use recv(2) instead of read(2).
+         Note that on POSIX systems there is no difference between
+         libc file descriptors and socket descriptors.  */
+
+      fd_out = assuan_get_output_fd (ctx);
+    }
+  else
+    err = gpg_error (GPG_ERR_ASS_NO_INPUT);
+  if (err)
+    goto leave;
+
+  do
+    {
+      if (fp_in)
+        {
+          nread = fread (buf, 1, sizeof buf, fp_in);
+          if (nread < sizeof buf)
+            {
+              if (ferror (fp_in))
+                err = gpg_error_from_syserror ();
+              else if (feof (fp_in))
+                err = gpg_error (GPG_ERR_EOF);
+            }
+        }
+      else
+        {
+          int n;
+
+          nread = 0;
+          n = my_read (fd_in, buf, sizeof buf);
+          if (n < 0)
+            err = gpg_error_from_syserror ();
+          else if (!n)
+            err = gpg_error (GPG_ERR_EOF);
+          else
+            nread = n;
+        }
+      
+
+      if (fd_out != ASSUAN_INVALID_FD)
+        {
+          if (nread && my_writen (fd_out, buf, nread))
+            err = gpg_error_from_syserror ();
+        }
+      else if (nread)
+        err = assuan_send_data (ctx, buf, nread);
+    }
+  while (!err);
+  if (gpg_err_code (err) == GPG_ERR_EOF)
+    err = 0;
+
+leave:
+  if (fp_in)
+    fclose (fp_in);
+  if (use_dataport)
+    {
+      if (state->dataport_fd != ASSUAN_INVALID_FD)
+        {
+          assuan_sock_close (state->dataport_fd);
+          state->dataport_fd = ASSUAN_INVALID_FD;
+        }
+    }
+  else
+    {
+      assuan_close_input_fd (ctx);
+      assuan_close_output_fd (ctx);
+    }
+  return leave_cmd (ctx, err);
+}
+
+
 static const char hlp_pwd[] = 
   "PWD\n"
   "\n"
@@ -374,12 +716,25 @@ cmd_run (assuan_context_t ctx, char *line)
 {
   /*  state_t state = assuan_get_pointer (ctx); */
   gpg_error_t err;
+  BOOL w32ret;
   PROCESS_INFORMATION pi = { NULL, 0, 0, 0 };
   char *p;
   wchar_t *pgmname = NULL;
   wchar_t *cmdline = NULL;
   int code;
   DWORD exc;
+  int idx;
+  struct {
+    HANDLE hd[2];
+    int oldname_valid;
+    wchar_t oldname[MAX_PATH];
+  } pipes[3];
+
+  for (idx=0; idx < 3; idx++)
+    {
+      pipes[idx].hd[0] = pipes[idx].hd[1] = INVALID_HANDLE_VALUE;
+      pipes[idx].oldname_valid = 0;
+    }
 
   p = strchr (line, ' ');
   if (p)
@@ -399,19 +754,77 @@ cmd_run (assuan_context_t ctx, char *line)
     xfree (tmp2);
     xfree (tmp1);
   }
-  if (!CreateProcess (pgmname,     /* Program to start.  */
-                      cmdline,     /* Command line arguments.  */
-                      NULL,        /* Process security attributes. notsup. */
-                      NULL,        /* Thread security attributes.  notsup. */
-                      FALSE,       /* Inherit handles.  notsup.  */
-                      CREATE_SUSPENDED, /* Creation flags.  */
-                      NULL,        /* Environment.  notsup.  */
-                      NULL,        /* Use current drive/directory.  notsup. */
-                      NULL,        /* Startup information.  notsup. */
-                      &pi          /* Returns process information.  */
-                      ))
-    {
-      log_error ("CreateProcess failed: %d", GetLastError ());
+
+  /* Redirect the standard handles.  */
+  /* Create pipes.  */
+  for (idx=0; idx < 3; idx++)
+    {
+      if (!_assuan_w32ce_create_pipe (&pipes[idx].hd[0], &pipes[idx].hd[1],
+                                      NULL, 0))
+        {
+          err = gpg_error_from_syserror ();
+          log_error ("CreatePipe failed: %d\n", GetLastError ());
+          pipes[idx].hd[0] = pipes[idx].hd[1] = INVALID_HANDLE_VALUE;
+          goto leave;
+        }
+    }
+
+  /* Save currently assigned devices.  */
+  for (idx=0; idx < 3; idx++)
+    {
+      DWORD dwlen = MAX_PATH;
+      if (!GetStdioPathW (idx, pipes[idx].oldname, &dwlen))
+        {
+          err = gpg_error_from_syserror ();
+          log_error ("GetStdioPath failed: %d\n", GetLastError ());
+          goto leave;
+        }
+      pipes[idx].oldname_valid = 1;
+    }
+
+  /* Connect the pipes.  */
+    {
+      if (!SetStdioPathW (1, L"\\mystdout.log"))
+        {
+          err = gpg_error_from_syserror ();
+          log_error ("SetStdioPathW(%d) failed: %d\n", idx, GetLastError ());
+          goto leave;
+        }
+      if (!SetStdioPathW (2, L"\\mystderr.log"))
+        {
+          err = gpg_error_from_syserror ();
+          log_error ("SetStdioPathW(%d) failed: %d\n", idx, GetLastError ());
+          goto leave;
+        }
+    }
+
+  /* Create the process, restore the devices and check the error.  */
+  w32ret = CreateProcess (pgmname,     /* Program to start.  */
+                          cmdline,     /* Command line arguments.  */
+                          NULL,        /* Process security.  Not used. */
+                          NULL,        /* Thread security.  Not used. */
+                          FALSE,       /* Inherit handles.  Not used.  */
+                          CREATE_SUSPENDED, /* Creation flags.  */
+                          NULL,        /* Environment.  Not used.  */
+                          NULL,        /* Use current dir.  Not used. */
+                          NULL,        /* Startup information.  Not used. */
+                          &pi          /* Returns process information.  */
+                          );
+  for (idx=0; idx < 3; idx++)
+    {
+      if (pipes[idx].oldname_valid)
+        {
+          if (!SetStdioPathW (idx, pipes[idx].oldname))
+            log_error ("SetStdioPath(%d) failed during restore: %d\n",
+                       idx, GetLastError ());
+          else
+            pipes[idx].oldname_valid = 0;
+        }
+    }
+  if (!w32ret)
+    {
+      /* Error checking after restore so that the messages are visible.  */
+      log_error ("CreateProcess failed: %d\n", GetLastError ());
       err = gpg_error_from_syserror ();
       goto leave;
     }
@@ -460,6 +873,24 @@ cmd_run (assuan_context_t ctx, char *line)
   CloseHandle (pi.hProcess);
   
  leave:
+  for (idx=0; idx < 3; idx++)
+    {
+      if (pipes[idx].oldname_valid)
+        {
+          if (!SetStdioPathW (idx, pipes[idx].oldname))
+            log_error ("SetStdioPath(%d) failed during restore: %d\n",
+                       idx, GetLastError ());
+          else
+            pipes[idx].oldname_valid = 0;
+        }
+    }
+  for (idx=0; idx < 3; idx++)
+    {
+      if (pipes[idx].hd[0] != INVALID_HANDLE_VALUE)
+        CloseHandle (pipes[idx].hd[0]);
+      if (pipes[idx].hd[1] != INVALID_HANDLE_VALUE)
+        CloseHandle (pipes[idx].hd[1]);
+    }
   xfree (cmdline);
   xfree (pgmname);
   return leave_cmd (ctx, err);
@@ -467,17 +898,254 @@ cmd_run (assuan_context_t ctx, char *line)
 #endif /*HAVE_W32CE_SYSTEM*/
 
 
+

+static const char hlp_newdataport[] = 
+  "NEWDATAPORT\n"
+  "\n"
+  "Create a new dataport.  The server creates a listening socket and\n"
+  "issues the inquiry:\n"
+  "  INQUIRE CONNECT-TO <port>\n"
+  "The client is expected to connect to PORT of the server and confirm\n"
+  "this by sending just an \"END\".  In turn the server sends:\n"
+  "  S FDINFO <n>\n"
+  "With N being the local descriptor for the accepted connection.  This\n"
+  "descriptor may now be used with INPUT or OUTPUT commands.";
+struct cmd_dataport_locals
+{
+  assuan_context_t ctx;
+  int passthru;
+  int port;
+};
+static gpg_error_t
+cmd_newdataport (assuan_context_t ctx, char *line)
+{
+  state_t state = assuan_get_pointer (ctx);
+  gpg_error_t err = 0;
+  struct sockaddr_in addr;
+  socklen_t addrlen;
+  struct cmd_dataport_locals *cont;
+  char inqline[100];
+
+  cont = xmalloc (sizeof *cont);
+  cont->ctx = ctx;
+  cont->passthru = 0;
+  cont->port = 0;
+
+  if (state->dataport_listen_fd != ASSUAN_INVALID_FD)
+    {
+      log_error ("Oops, still listening on a dataport socket\n");
+      state->dataport_listen_fd = ASSUAN_INVALID_FD;
+    }
+  if (state->dataport_accepted_fd != ASSUAN_INVALID_FD)
+    {
+      log_error ("Oops, still holding an accepted dataport socket\n");
+      state->dataport_accepted_fd = ASSUAN_INVALID_FD;
+    }
+  state->dataport_accept_err = 0;
+
+  state->dataport_listen_fd = assuan_sock_new (PF_INET, SOCK_STREAM, 0);
+  if (state->dataport_listen_fd == ASSUAN_INVALID_FD)
+    {
+      err = gpg_error_from_syserror ();
+      log_error ("socket() failed: %s\n", strerror (errno));
+      goto leave;
+    }
+
+  addr.sin_family = AF_INET;
+  addr.sin_port = 0;
+  addr.sin_addr.s_addr = htonl (INADDR_ANY);
+  if (assuan_sock_bind (state->dataport_listen_fd,
+                        (struct sockaddr *)&addr, sizeof addr))
+    {
+      err = gpg_error_from_syserror ();
+      log_error ("listen() failed: %s\n", strerror (errno));
+      goto leave;
+    }
+  
+  if (listen (HANDLE2SOCKET (state->dataport_listen_fd), 1))
+    {
+      err = gpg_error_from_syserror ();
+      log_error ("listen() failed: %s\n", strerror (errno));
+      goto leave;
+    }
+
+  addrlen = sizeof addr;
+  if (getsockname (HANDLE2SOCKET (state->dataport_listen_fd),
+                   (struct sockaddr *)&addr, &addrlen))
+    {
+      err = gpg_error_from_syserror ();
+      log_error ("getsockname() failed: %s\n", strerror (errno));
+      goto leave;
+    }
+  cont->port = ntohs (addr.sin_port);
+
+  if (verbose)
+    log_info ("server now also listening on port %d\n", cont->port);
+  snprintf (inqline, sizeof inqline, "CONNECT-TO %d", cont->port);
+  err = assuan_inquire_ext (ctx, inqline, 0, cmd_newdataport_cont, cont);
+  if (!err)
+    return 0; /* Transfer to continuation.  */
+
+ leave:
+  cont->passthru = 1;
+  return cmd_newdataport_cont (cont, err, NULL, 0);
+}
+
+/* Continuation used by cmd_newdataport.  */
+static gpg_error_t
+cmd_newdataport_cont (void *opaque, gpg_error_t err,
+                      unsigned char *data, size_t datalen)
+{
+  struct cmd_dataport_locals *cont = opaque;
+  assuan_context_t ctx  = cont->ctx;
+  state_t state = assuan_get_pointer (ctx);
+  char numbuf[35];
+  fdinfo_t fi;
+
+  if (cont->passthru || err)
+    goto leave;
+
+  err = state->dataport_accept_err;
+  if (err)
+    goto leave;
+  if (state->dataport_listen_fd != ASSUAN_INVALID_FD
+      || state->dataport_accepted_fd == ASSUAN_INVALID_FD)
+    {
+      err = gpg_error (GPG_ERR_MISSING_ACTION);
+      goto leave;
+    }
+
+  for (fi = state->dataport_fds; fi; fi = fi->next)
+    if (fi->fd == ASSUAN_INVALID_FD)
+      break;
+  if (!fi)
+    {
+      fi = xcalloc (1, sizeof *fi);
+      fi->next = state->dataport_fds;
+      state->dataport_fds = fi;
+    }
+  fi->fd = state->dataport_accepted_fd;
+  state->dataport_accepted_fd = ASSUAN_INVALID_FD;
+
+  /* Note that under Windows the FD is the socket descriptor.  Socket
+     descriptors are neither handles nor libc file descriptors.  */
+  snprintf (numbuf, sizeof numbuf, "%d", HANDLE2SOCKET (fi->fd));
+  err = assuan_write_status (ctx, "FDINFO", numbuf);
+
+ leave:
+  if (state->dataport_listen_fd != ASSUAN_INVALID_FD)
+    {
+      assuan_sock_close (state->dataport_listen_fd);
+      state->dataport_listen_fd = ASSUAN_INVALID_FD;
+    }
+  if (state->dataport_accepted_fd != ASSUAN_INVALID_FD)
+    {
+      assuan_sock_close (state->dataport_accepted_fd);
+      state->dataport_accepted_fd = ASSUAN_INVALID_FD;
+    }
+  xfree (cont);
+  return leave_cmd (ctx, err);
+}
+
+
+

+static const char hlp_dataport[] =
+  "DATAPORT FD[=<n>]\n"
+  "\n"
+  "Set the file descriptor to read and write data via port.\n"
+  "This is similar to the \"INPUT\" and \"OUTPUT\" commands\n"
+  "but useful for socketpairs.";
+static gpg_error_t
+cmd_dataport (assuan_context_t ctx, char *line)
+{
+  state_t state = assuan_get_pointer (ctx);
+  gpg_error_t err;
+  assuan_fd_t fd;
+
+  if (state->dataport_fd != ASSUAN_INVALID_FD)
+    {
+      assuan_sock_close (state->dataport_fd);
+      state->dataport_fd = ASSUAN_INVALID_FD;
+    }
+
+  err = assuan_command_parse_fd (ctx, line, &fd);
+  if (!err && fd != ASSUAN_INVALID_FD)
+    {
+      fdinfo_t fi;
+
+      state->dataport_fd = fd;
+
+      /* The fd is now in use use - remove it from the unused list.  */
+      for (fi=state->dataport_fds; fi; fi = fi->next)
+        if (fi->fd == fd)
+          fi->fd = ASSUAN_INVALID_FD;
+    }
+
+  return leave_cmd (ctx, err);
+}
+
+
+

+static const char hlp_getinfo[] = 
+  "GETINFO <what>\n"
+  "\n"
+  "Multipurpose function to return a variety of information.\n"
+  "Supported values for WHAT are:\n"
+  "\n"
+  "  version     - Return the version of the program.\n"
+  "  pid         - Return the process id of the server.\n"
+  "  dataports   - Return a list of usused dataports.";
+static gpg_error_t
+cmd_getinfo (assuan_context_t ctx, char *line)
+{
+  state_t state = assuan_get_pointer (ctx);
+  gpg_error_t err = 0;
+  char numbuf[50];
+
+  if (!strcmp (line, "version"))
+    {
+      const char *s = VERSION;
+      err = assuan_send_data (ctx, s, strlen (s));
+    }
+  else if (!strcmp (line, "pid"))
+    {
+      snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ());
+      err = assuan_send_data (ctx, numbuf, strlen (numbuf));
+    }
+  else if (!strcmp (line, "dataports"))
+    {
+      fdinfo_t fi;
+      int any = 0;
+
+      for (fi=state->dataport_fds; !err && fi; fi = fi->next)
+        {
+          if (fi->fd != ASSUAN_INVALID_FD)
+            {
+              snprintf (numbuf, sizeof numbuf, "%s%d",
+                        any? " ":"", HANDLE2SOCKET (fi->fd));
+              any = 1;
+              err = assuan_send_data (ctx, numbuf, strlen (numbuf));
+            }
+        }
+    }
+  else
+    err = gpg_error (GPG_ERR_ASS_PARAMETER);
+
+  return leave_cmd (ctx, err);
+}
+
+
+

 static const char hlp_shutdown[] = 
   "SHUTDOWN\n"
   "\n"
-  "Shutdown the server process after ending this connection\n";
+  "Shutdown the server process\n";
 static gpg_error_t
 cmd_shutdown (assuan_context_t ctx, char *line)
 {
-  (void)ctx;
   (void)line;
   shutdown_pending = 1;
-  return 0;
+  return leave_cmd (ctx, 0);;
 }
 
 
@@ -498,8 +1166,12 @@ register_commands (assuan_context_t ctx)
         { "PWD",  cmd_pwd, hlp_pwd },
         { "CD",   cmd_cd,  hlp_cd },
 	{ "ECHO", cmd_echo, hlp_echo },
+        { "CAT",  cmd_cat, hlp_cat },
+        { "NEWDATAPORT", cmd_newdataport, hlp_newdataport },
+        { "DATAPORT",    cmd_dataport,    hlp_dataport },
 	{ "INPUT", NULL },
 	{ "OUTPUT", NULL },
+        { "GETINFO",  cmd_getinfo, hlp_getinfo },
 	{ "SHUTDOWN", cmd_shutdown, hlp_shutdown },
 	{ NULL, NULL }
       };
@@ -517,6 +1189,20 @@ register_commands (assuan_context_t ctx)
 }
 
 
+
+static assuan_fd_t
+get_connection_fd (assuan_context_t ctx)
+{
+  assuan_fd_t fds[5];
+
+  if (assuan_get_active_fds (ctx, 0, fds, DIM (fds)) < 1)
+    log_fatal ("assuan_get_active_fds failed\n");
+  if (fds[0] == ASSUAN_INVALID_FD)
+    log_fatal ("assuan_get_active_fds returned invalid conenction fd\n");
+  return fds[0];
+}
+
+
 /* Startup the server.  */
 static void
 server (void)
@@ -535,20 +1221,20 @@ server (void)
 
   server_fd = assuan_sock_new (PF_INET, SOCK_STREAM, 0);
   if (server_fd == ASSUAN_INVALID_FD)
-    log_fatal ("socket() failed: %s", strerror (errno));
+    log_fatal ("socket() failed: %s\n", strerror (errno));
 
   if (setsockopt (HANDLE2SOCKET (server_fd), 
                   SOL_SOCKET, SO_REUSEADDR, (void*)&one, sizeof one))
-    log_error ("setsockopt(SO_REUSEADDR) failed: %s", strerror (errno));
+    log_error ("setsockopt(SO_REUSEADDR) failed: %s\n", strerror (errno));
 
   name.sin_family = AF_INET;
   name.sin_port = htons (server_port);
   name.sin_addr.s_addr = htonl (INADDR_ANY);
   if (assuan_sock_bind (server_fd, (struct sockaddr *) &name, sizeof name))
-    log_fatal ("bind() failed: %s", strerror (errno));
+    log_fatal ("bind() failed: %s\n", strerror (errno));
   if (assuan_sock_get_nonce ((struct sockaddr*)&name, sizeof name, 
                              &server_nonce))
-    log_fatal ("assuan_sock_get_nonce failed: %s", strerror (errno));
+    log_fatal ("assuan_sock_get_nonce failed: %s\n", strerror (errno));
 
   /* Register the nonce with the context so that assuan_accept knows
      about it.  We can't do that directly in assuan_sock_bind because
@@ -572,12 +1258,19 @@ server (void)
   if (debug)
     assuan_set_log_stream (ctx, stderr);
 
+  assuan_register_reset_notify (ctx, reset_notify);
+  assuan_register_input_notify (ctx, input_notify);
+  assuan_register_output_notify (ctx, output_notify);
+
+  
+  state = new_state ();
   
-  state = xcalloc (1, sizeof state);
   assuan_set_pointer (ctx, state);
 
   while (!shutdown_pending)
     {
+      int done;
+
       err = assuan_accept (ctx);
       if (err)
         {
@@ -588,8 +1281,66 @@ server (void)
       
       log_info ("client connected.  Client's pid is %ld\n",
                 (long)assuan_get_pid (ctx));
-
-      err = assuan_process (ctx);
+      do
+        {
+          /* We need to use select here so that we can accept
+             supplemental connections from the client as requested by
+             the DATAPORT command.  */
+          fd_set rfds;
+          int connfd, datafd, max_fd;
+
+          connfd = HANDLE2SOCKET (get_connection_fd (ctx));
+          FD_ZERO (&rfds);
+          FD_SET (connfd, &rfds);
+          max_fd = connfd;
+
+          if (state->dataport_listen_fd != ASSUAN_INVALID_FD)
+            {
+              datafd = HANDLE2SOCKET (state->dataport_listen_fd);
+              FD_SET (datafd, &rfds);
+              if (datafd > max_fd)
+                max_fd = datafd;
+            }
+          else
+            datafd = -1;
+
+          if (select (max_fd + 1, &rfds, NULL, NULL, NULL) > 0)
+            {
+              if (datafd != -1 && FD_ISSET (datafd, &rfds))
+                {
+                  struct sockaddr_in clnt_addr;
+                  socklen_t len = sizeof clnt_addr;
+                  int fd;
+
+                  fd = accept (datafd, (struct sockaddr*)&clnt_addr, &len);
+                  if (fd == -1)
+                    {
+                      err = gpg_err_code_from_syserror ();
+                      assuan_sock_close (state->dataport_listen_fd);
+                      state->dataport_listen_fd = ASSUAN_INVALID_FD;
+                      log_error ("accepting on dataport failed: %s\n",
+                                 gpg_strerror (err));
+                      state->dataport_accept_err = err;
+                      err = 0;
+                    }
+                  else
+                    {
+                      /* No more need for the listening socket.  */
+                      assuan_sock_close (state->dataport_listen_fd);
+                      state->dataport_listen_fd = ASSUAN_INVALID_FD;
+                      /* Record the accepted fd.  */
+                      state->dataport_accept_err = 0;
+                      state->dataport_accepted_fd = SOCKET2HANDLE (fd);
+                    }
+                }
+
+              if (FD_ISSET (connfd, &rfds))
+                {
+                  err = assuan_process_next (ctx, &done);
+                }
+            }
+        }
+      while (!err && !done && !shutdown_pending);
       if (err)
         log_error ("assuan_process failed: %s\n", gpg_strerror (err));
     }
diff --git a/tests/common.h b/tests/common.h
index 4aca63a..c5ce811 100644
--- a/tests/common.h
+++ b/tests/common.h
@@ -34,9 +34,14 @@
 #if HAVE_W32_SYSTEM
 #define SOCKET2HANDLE(s) ((void *)(s))
 #define HANDLE2SOCKET(h) ((unsigned int)(h))
+CRITICAL_SECTION _log_critsect;
+#define _log_enter()  do { EnterCriticalSection (&_log_critsect); } while (0)
+#define _log_leave()  do { LeaveCriticalSection (&_log_critsect); } while (0)
 #else
 #define SOCKET2HANDLE(s) (s)
 #define HANDLE2SOCKET(h) (h)
+#define _log_enter()  do { } while (0)
+#define _log_leave()  do { } while (0)
 #endif
 
 #define DIM(v)		     (sizeof(v)/sizeof((v)[0]))
@@ -71,9 +76,11 @@ xcalloc (size_t n, size_t m)
   char *p = calloc (n, m);
   if (!p)
     {
+      _log_enter ();
       if (log_prefix)
         fprintf (stderr, "%s[%u]: ", log_prefix, (unsigned int)getpid ());
       fprintf (stderr, "out of core\n");
+      _log_leave ();
       exit (1);
     }
   return p;
@@ -98,7 +105,12 @@ xstrdup (const char *string)
 void
 log_set_prefix (const char *s)
 {
+#ifdef HAVE_W32_SYSTEM
+  InitializeCriticalSection (&_log_critsect);
+  log_prefix = strrchr (s, '\\');
+#else  
   log_prefix = strrchr (s, '/');
+#endif  
   if (log_prefix)
     log_prefix++;
   else
@@ -122,9 +134,11 @@ log_info (const char *format, ...)
     return;
 
   va_start (arg_ptr, format) ;
+  _log_enter ();
   if (log_prefix)
     fprintf (stderr, "%s[%u]: ", log_prefix, (unsigned int)getpid ());
   vfprintf (stderr, format, arg_ptr );
+  _log_leave ();
   va_end (arg_ptr);
 }
 
@@ -135,9 +149,11 @@ log_error (const char *format, ...)
   va_list arg_ptr ;
 
   va_start (arg_ptr, format) ;
+  _log_enter ();
   if (log_prefix)
     fprintf (stderr, "%s[%u]: ", log_prefix, (unsigned int)getpid ());
   vfprintf (stderr, format, arg_ptr );
+  _log_leave ();
   va_end (arg_ptr);
   errorcount++;
 }
@@ -149,9 +165,11 @@ log_fatal (const char *format, ...)
   va_list arg_ptr ;
 
   va_start (arg_ptr, format) ;
+  _log_enter ();
   if (log_prefix)
     fprintf (stderr, "%s[%u]: ", log_prefix, (unsigned int)getpid ());
   vfprintf (stderr, format, arg_ptr );
+  _log_leave ();
   va_end (arg_ptr);
   exit (2);
 }
@@ -162,12 +180,14 @@ log_printhex (const char *text, const void *buffer, size_t length)
 {
   const unsigned char *s;
 
+  _log_enter ();
   if (log_prefix)
     fprintf (stderr, "%s[%u]: ", log_prefix, (unsigned int)getpid ());
   fputs (text, stderr);
   for (s=buffer; length; s++, length--)
     fprintf (stderr, "%02X", *s);
   putc ('\n', stderr);
+  _log_leave ();
 }
 
 

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-gnupg/libassuan.git



More information about the Pkg-gnupg-commit mailing list