aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ChangeLog39
-rw-r--r--src/Makefile.am13
-rw-r--r--src/assuan-defs.h7
-rw-r--r--src/assuan-handler.c26
-rw-r--r--src/assuan-inquire.c28
-rw-r--r--src/assuan-pipe-connect.c2
-rw-r--r--src/assuan-socket-connect.c219
-rw-r--r--src/assuan.h11
-rw-r--r--src/gpgcedev.c720
-rw-r--r--src/gpgcedev.def33
-rw-r--r--src/gpgcemgr.c72
-rw-r--r--src/libassuan.def3
-rw-r--r--src/libassuan.vers1
-rw-r--r--src/system.c18
-rw-r--r--src/sysutils.c84
15 files changed, 1216 insertions, 60 deletions
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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * system.c (assuan_free): New.
+ * libassuan.vers (assuan_free): Add it.
+ * libassuan.def (assuan_free): Add it.
+
+2010-02-11 Werner Koch <[email protected]>
+
+ * assuan-inquire.c (assuan_inquire): Allow case insensitive
+ responses.
+ (_assuan_inquire_ext_cb): Ditto.
+
+2010-02-10 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
* libassuan.vers, libassuan.def: Add assuan_set_sock_nonce.
+2010-02-01 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
* 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*/
+