[svn] GpgOL - r188 - in trunk: . doc m4 po src

svn author wk cvs at cvs.gnupg.org
Wed Oct 10 15:23:52 CEST 2007


Author: wk
Date: 2007-10-10 15:23:41 +0200 (Wed, 10 Oct 2007)
New Revision: 188

Added:
   trunk/m4/libassuan.m4
Modified:
   trunk/ChangeLog
   trunk/autogen.sh
   trunk/doc/gpgol.texi
   trunk/po/de.po
   trunk/po/sv.po
   trunk/src/ChangeLog
   trunk/src/Makefile.am
   trunk/src/common.c
   trunk/src/common.h
   trunk/src/engine-assuan.c
   trunk/src/engine-assuan.h
   trunk/src/engine-gpgme.c
   trunk/src/engine.c
   trunk/src/ext-commands.cpp
   trunk/src/main.c
   trunk/src/util.h
Log:
First encryption using the new Assuan based engine and GPA succeeded.
The asyncronous I/O framework is basically working now.


Modified: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog	2007-10-08 10:19:07 UTC (rev 187)
+++ trunk/ChangeLog	2007-10-10 13:23:41 UTC (rev 188)
@@ -1,3 +1,14 @@
+2007-10-05  Werner Koch  <wk at g10code.com>
+
+	* doc/ New. 
+	* doc/Makefile.am: New.
+	* doc/gpgol.texi: New.
+	* doc/gpl.texi: New.
+
+2007-09-25  Werner Koch  <wk at g10code.com>
+
+	* configure.ac: Check for libassuan.
+
 2007-09-17  Werner Koch  <wk at g10code.com>
 
 	* autogen.sh (FORCE): Add --force option.

Modified: trunk/autogen.sh
===================================================================
--- trunk/autogen.sh	2007-10-08 10:19:07 UTC (rev 187)
+++ trunk/autogen.sh	2007-10-10 13:23:41 UTC (rev 188)
@@ -91,7 +91,8 @@
     ./configure --enable-maintainer-mode --prefix=${w32root}  \
              --host=i586-mingw32msvc --build=${build} \
              --with-gpg-error-prefix=${w32root} \
-	     --with-gpgme-prefix=${w32root} 
+	     --with-gpgme-prefix=${w32root} \
+	     --with-libassuan-prefix=${w32root} 
     rc=$?
 
     exit $rc

Modified: trunk/doc/gpgol.texi
===================================================================
--- trunk/doc/gpgol.texi	2007-10-08 10:19:07 UTC (rev 187)
+++ trunk/doc/gpgol.texi	2007-10-10 13:23:41 UTC (rev 188)
@@ -122,7 +122,7 @@
 Assuan manual for details.
 
 @menu
-* ENCRYPT::                  Encrypting a message.
+* ENCRYPT::                  Encrypt a message.
 * SIGN::                     Sign a message.
 * DECRYPT::                  Decrypt a message.
 * VERIFY::                   Verify a message.
@@ -132,7 +132,7 @@
 
 
 @node ENCRYPT
- at section Encrypting a Message
+ at section Encrypt a Message
 
 Before encryption can be done the recipients must be set using the
 command:
@@ -248,11 +248,13 @@
 @noindent
 The signing operation is then initiated by:
 
- at deffn Command SIGN [- at w{}-detached] 
+ at deffn Command SIGN - at w{}-protocol=@var{name} [- at w{}-detached] 
 Sign the data set with the @code{INPUT} command and write it to the sink
-set by OUTPUT.  With option @code{--detached} given, a detached
+set by OUTPUT.  @var{name} is the signing protocol used for the
+message. For a description of the allowed protocols see the
+ at code{ENCRYPT} command.  With option @code{--detached} given, a detached
 signature is created; this is actually the usual way the command is
-used.  
+used.
 @end deffn
 
 @noindent
@@ -443,7 +445,44 @@
 
 @bye
 
+ at c xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+ at c
+ at c E D I T O R ' S   A T T I C
+ at c 
+ at c xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
 
+What about the message class mangling?
+
+* On receiving a new message GpgOL checks the MAPI message class
+  property of the message and if this is an S/MIME message class
+  ("IPM.Note.SMIME"), it is changed to a GpgOL specific one
+  ("IPM.Note.GpgOL").  This change is required so that OL does not not
+  apply its own S/MIME handler to the message but leaves it unchanged in
+  the message store.
+
+* For ease of implementarion the same thing applies to PGP messgaes,
+  although OL would not touch these messages.
+
+* When reading a message GpgOL quickly checks the message class and if
+  it is "IPM.Note.GpgOL" it will hook itself into the code path and
+  decrypt/verify the message.
+
+* Messages already in the message store before GpgOL was installed are
+  handled diffwerently: Here an Outlook specific event is used to change
+  the message class when browsing the messages folder.  This code path
+  is not fully ready as it requires the installation of an ECF(ile)
+  which has to be done manually as of now.
+
+* If GpgOL is deinstalled, the existing S/MIME messages can't be
+  decrypted or verified by Outlook's internal S/MIME support.
+  Multipart/signed messages are still readable, though.  We plan to add
+  a little tool for changing the GpgOL message classes back to
+  "IPM.Note.SMIME" which in turn allows using internal S/MIME support
+  again.
+
+
+
+
 @c Local Variables:
 @c coding: latin-1
 @c End:

Added: trunk/m4/libassuan.m4
===================================================================
--- trunk/m4/libassuan.m4	2007-10-08 10:19:07 UTC (rev 187)
+++ trunk/m4/libassuan.m4	2007-10-10 13:23:41 UTC (rev 188)
@@ -0,0 +1,175 @@
+dnl Autoconf macros for libassuan
+dnl       Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+dnl
+dnl This file is free software; as a special exception the author gives
+dnl unlimited permission to copy and/or distribute it, with or without
+dnl modifications, as long as this notice is preserved.
+dnl
+dnl This file is distributed in the hope that it will be useful, but
+dnl WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+dnl implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+dnl
+dnl Common code used for libassuan detection [internal]
+dnl Returns ok set to yes or no.
+dnl
+AC_DEFUN([_AM_PATH_LIBASSUAN_COMMON],
+[ AC_ARG_WITH(libassuan-prefix,
+              AC_HELP_STRING([--with-libassuan-prefix=PFX],
+                             [prefix where LIBASSUAN is installed (optional)]),
+     libassuan_config_prefix="$withval", libassuan_config_prefix="")
+  if test x$libassuan_config_prefix != x ; then
+    libassuan_config_args="$libassuan_config_args --prefix=$libassuan_config_prefix"
+    if test x${LIBASSUAN_CONFIG+set} != xset ; then
+      LIBASSUAN_CONFIG=$libassuan_config_prefix/bin/libassuan-config
+    fi
+  fi
+  AC_PATH_PROG(LIBASSUAN_CONFIG, libassuan-config, no)
+
+  tmp=ifelse([$1], ,1:0.9.2,$1)
+  if echo "$tmp" | grep ':' >/dev/null 2>/dev/null ; then
+    req_libassuan_api=`echo "$tmp"     | sed 's/\(.*\):\(.*\)/\1/'`
+    min_libassuan_version=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\2/'`
+  else
+    req_libassuan_api=0
+    min_libassuan_version="$tmp"
+  fi
+
+  if test "$LIBASSUAN_CONFIG" != "no" ; then
+    libassuan_version=`$LIBASSUAN_CONFIG --version`
+  fi
+  libassuan_version_major=`echo $libassuan_version | \
+               sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\1/'`
+  libassuan_version_minor=`echo $libassuan_version | \
+               sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\2/'`
+  libassuan_version_micro=`echo $libassuan_version | \
+               sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\3/'`
+
+  AC_MSG_CHECKING(for LIBASSUAN ifelse([$2], ,,[$2 ])- version >= $min_libassuan_version)
+  ok=no
+  if test "$LIBASSUAN_CONFIG" != "no" ; then
+    ifelse([$2], ,,[if `$LIBASSUAN_CONFIG --thread=$2 2> /dev/null` ; then])
+    req_major=`echo $min_libassuan_version | \
+               sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\1/'`
+    req_minor=`echo $min_libassuan_version | \
+               sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\2/'`
+    req_micro=`echo $min_libassuan_version | \
+               sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\3/'`
+    if test "$libassuan_version_major" -gt "$req_major"; then
+        ok=yes
+    else 
+        if test "$libassuan_version_major" -eq "$req_major"; then
+            if test "$libassuan_version_minor" -gt "$req_minor"; then
+               ok=yes
+            else
+               if test "$libassuan_version_minor" -eq "$req_minor"; then
+                   if test "$libassuan_version_micro" -ge "$req_micro"; then
+                     ok=yes
+                   fi
+               fi
+            fi
+        fi
+    fi
+    ifelse([$2], ,,[fi])
+  fi
+
+  if test $ok = yes; then
+    AC_MSG_RESULT([yes ($libassuan_version)])
+  else
+    AC_MSG_RESULT(no)
+  fi
+
+  if test $ok = yes; then
+    if test "$req_libassuan_api" -gt 0 ; then
+      tmp=`$LIBASSUAN_CONFIG --api-version 2>/dev/null || echo 0`
+      if test "$tmp" -gt 0 ; then
+        AC_MSG_CHECKING([LIBASSUAN ifelse([$2], ,,[$2 ])API version])
+        if test "$req_libassuan_api" -eq "$tmp" ; then
+          AC_MSG_RESULT(okay)
+        else
+          ok=no
+          AC_MSG_RESULT([does not match.  want=$req_libassuan_api got=$tmp.])
+        fi
+      fi
+    fi
+  fi
+
+])
+
+dnl AM_CHECK_LIBASSUAN([MINIMUM-VERSION,
+dnl                    [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]])
+dnl Test whether libassuan has at least MINIMUM-VERSION. This is
+dnl used to test for features only available in newer versions.
+dnl
+AC_DEFUN([AM_CHECK_LIBASSUAN],
+[ _AM_PATH_LIBASSUAN_COMMON($1)
+  if test $ok = yes; then
+    ifelse([$2], , :, [$2])
+  else
+    ifelse([$3], , :, [$3])
+  fi
+])
+
+
+
+
+dnl AM_PATH_LIBASSUAN([MINIMUM-VERSION,
+dnl                   [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]])
+dnl Test for libassuan and define LIBASSUAN_CFLAGS and LIBASSUAN_LIBS
+dnl
+AC_DEFUN([AM_PATH_LIBASSUAN],
+[ _AM_PATH_LIBASSUAN_COMMON($1)
+  if test $ok = yes; then
+    LIBASSUAN_CFLAGS=`$LIBASSUAN_CONFIG $libassuan_config_args --cflags`
+    LIBASSUAN_LIBS=`$LIBASSUAN_CONFIG $libassuan_config_args --libs`
+    ifelse([$2], , :, [$2])
+  else
+    LIBASSUAN_CFLAGS=""
+    LIBASSUAN_LIBS=""
+    ifelse([$3], , :, [$3])
+  fi
+  AC_SUBST(LIBASSUAN_CFLAGS)
+  AC_SUBST(LIBASSUAN_LIBS)
+])
+
+
+dnl AM_PATH_LIBASSUAN_PTH([MINIMUM-VERSION,
+dnl                      [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]])
+dnl Test for libassuan and define LIBASSUAN_PTH_CFLAGS and LIBASSUAN_PTH_LIBS
+dnl
+AC_DEFUN([AM_PATH_LIBASSUAN_PTH],
+[ _AM_PATH_LIBASSUAN_COMMON($1,pth)
+  if test $ok = yes; then
+    LIBASSUAN_PTH_CFLAGS=`$LIBASSUAN_CONFIG $libassuan_config_args --thread=pth --cflags`
+    LIBASSUAN_PTH_LIBS=`$LIBASSUAN_CONFIG $libassuan_config_args --thread=pth --libs`
+    ifelse([$2], , :, [$2])
+  else
+    LIBASSUAN_PTH_CFLAGS=""
+    LIBASSUAN_PTH_LIBS=""
+    ifelse([$3], , :, [$3])
+  fi
+  AC_SUBST(LIBASSUAN_PTH_CFLAGS)
+  AC_SUBST(LIBASSUAN_PTH_LIBS)
+])
+
+
+dnl AM_PATH_LIBASSUAN_PTHREAD([MINIMUM-VERSION,
+dnl                           [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]])
+dnl Test for libassuan and define LIBASSUAN_PTHREAD_CFLAGS 
+dnl                           and LIBASSUAN_PTHREAD_LIBS
+dnl
+AC_DEFUN([AM_PATH_LIBASSUAN_PTHREAD],
+[ _AM_PATH_LIBASSUAN_COMMON($1,pthread)
+  if test $ok = yes; then
+    LIBASSUAN_PTHREAD_CFLAGS=`$LIBASSUAN_CONFIG $libassuan_config_args --thread=pthread --cflags`
+    LIBASSUAN_PTHREAD_LIBS=`$LIBASSUAN_CONFIG $libassuan_config_args --thread=pthread --libs`
+    ifelse([$2], , :, [$2])
+  else
+    LIBASSUAN_PTHREAD_CFLAGS=""
+    LIBASSUAN_PTHREAD_LIBS=""
+    ifelse([$3], , :, [$3])
+  fi
+  AC_SUBST(LIBASSUAN_PTHREAD_CFLAGS)
+  AC_SUBST(LIBASSUAN_PTHREAD_LIBS)
+])
+

Modified: trunk/po/de.po
===================================================================
--- trunk/po/de.po	2007-10-08 10:19:07 UTC (rev 187)
+++ trunk/po/de.po	2007-10-10 13:23:41 UTC (rev 188)
@@ -7,7 +7,7 @@
 msgstr ""
 "Project-Id-Version: GPGol 0.9.4\n"
 "Report-Msgid-Bugs-To: bug-gpgol at g10code.com\n"
-"POT-Creation-Date: 2007-09-11 20:30+0200\n"
+"POT-Creation-Date: 2007-09-25 12:20+0200\n"
 "PO-Revision-Date: 2007-04-13 12:55+0200\n"
 "Last-Translator: Werner Koch <wk at gnupg.org>\n"
 "Language-Team: de\n"
@@ -23,62 +23,79 @@
 msgid "Select GPG Key Manager"
 msgstr "Das Schlüsselverwaltungsprogramm festlegen"
 
-#: src/engine-gpgme.c:1099
+#: src/engine-gpgme.c:356 src/gpgmsg.cpp:1812
+msgid ""
+"The configured default encryption key is not available or does not "
+"unambigiously specify a key. Please fix this in the option dialog.\n"
+"\n"
+"This message won't be be encrypted to this key!"
+msgstr ""
+"Der voreingestellte zusätzliche Schlüssel zum Verschlüsseln ist nicht\n"
+"vorhanden oder nicht eindeutig.  Bitte beheben Sie dies in den\n"
+"Optionseinstellungen.\n"
+"\n"
+"Die Nachricht wird deswegen nicht für diesen Schlüssel verschlüsselt!"
+
+#: src/engine-gpgme.c:360 src/gpgmsg.cpp:1816
+msgid "Encryption"
+msgstr "Verschlüsselung"
+
+#: src/engine-gpgme.c:766
 msgid "Fingerprint: "
 msgstr "Fingerabdruck: "
 
-#: src/engine-gpgme.c:1156
+#: src/engine-gpgme.c:823
 msgid "This signature is valid\n"
 msgstr "Diese Unterschrift ist korrekt\n"
 
-#: src/engine-gpgme.c:1158
+#: src/engine-gpgme.c:825
 msgid "signature state is \"green\"\n"
 msgstr "Status der Unterschrift ist \"grün\"\n"
 
-#: src/engine-gpgme.c:1160
+#: src/engine-gpgme.c:827
 msgid "signature state is \"red\"\n"
 msgstr "Status der Unterschrift ist \"rot\"\n"
 
-#: src/engine-gpgme.c:1164
+#: src/engine-gpgme.c:831
 msgid "Warning: One of the keys has been revoked\n"
 msgstr "Warnung: Einer der Schlüssel wurde widerrufen\n"
 
-#: src/engine-gpgme.c:1174
+#: src/engine-gpgme.c:841
 msgid "Warning: The key used to create the signature expired at: "
 msgstr ""
 "Warnung: Der Schlüssel mit der diese Unterschrift erzeugt wurde verfiel am: "
 
-#: src/engine-gpgme.c:1180
+#: src/engine-gpgme.c:847
 msgid "Warning: At least one certification key has expired\n"
 msgstr ""
 "Warnung: Mindestens einer der Zertifizierungsschlüssel ist abgelaufen\n"
 
-#: src/engine-gpgme.c:1186
+#: src/engine-gpgme.c:853
 msgid "Warning: The signature expired at: "
 msgstr "Die Unterschrift verfiel am: "
 
-#: src/engine-gpgme.c:1192
+#: src/engine-gpgme.c:859
 msgid "Can't verify due to a missing key or certificate\n"
 msgstr ""
 "Aufrund eines fehlenden Schlüssels ist eine Überprüfung nicht möglich\n"
 
-#: src/engine-gpgme.c:1196
+#: src/engine-gpgme.c:863
 msgid "The CRL is not available\n"
 msgstr "Die CRL ist nicht verfügbar\n"
 
-#: src/engine-gpgme.c:1202
+#: src/engine-gpgme.c:869
 msgid "Available CRL is too old\n"
 msgstr "Die vorhandene CRL ist zu alt\n"
 
-#: src/engine-gpgme.c:1207
+#: src/engine-gpgme.c:874
 msgid "A policy requirement was not met\n"
 msgstr "Eine Richtlinie wurde nicht erfüllt\n"
 
-#: src/engine-gpgme.c:1213
+#: src/engine-gpgme.c:880
 msgid "A system error occured"
 msgstr "Ein Systemfehler ist aufgetreten"
 
-#: src/engine-gpgme.c:1250
+#: src/engine-gpgme.c:917
 msgid ""
 "WARNING: We have NO indication whether the key belongs to the person named "
 "as shown above\n"
@@ -86,12 +103,12 @@
 "WARNUNG: Es gibt keinen Hinweis darauf, ob der Schlüssel wirklich der Person "
 "gehört, die oben angezeigt ist\n"
 
-#: src/engine-gpgme.c:1257
+#: src/engine-gpgme.c:924
 msgid "WARNING: The key does NOT BELONG to the person named as shown above\n"
 msgstr ""
 "WARNUNG: Der Schlüssel gehört NICHT der Person die oben angezeigt ist\n"
 
-#: src/engine-gpgme.c:1261
+#: src/engine-gpgme.c:928
 msgid ""
 "WARNING: It is NOT certain that the key belongs to the person named as shown "
 "above\n"
@@ -99,43 +116,43 @@
 "WARNING: Es ist nicht sicher, daß der Schlüssel der Person gehört, die oben "
 "angezeigt ist\n"
 
-#: src/engine-gpgme.c:1294
+#: src/engine-gpgme.c:961
 msgid "Verification started at: "
 msgstr "Überprüfung begann am: "
 
-#: src/engine-gpgme.c:1299
+#: src/engine-gpgme.c:966
 msgid "Verification result for: "
 msgstr "Prüfungsresultat für: "
 
-#: src/engine-gpgme.c:1300
+#: src/engine-gpgme.c:967
 msgid "[unnamed part]"
 msgstr "[Unbenannter Teil]"
 
-#: src/engine-gpgme.c:1318 src/engine-gpgme.c:1348
+#: src/engine-gpgme.c:985 src/engine-gpgme.c:1015
 msgid "Good signature from: "
 msgstr "Korrekte Unterschrift von: "
 
-#: src/engine-gpgme.c:1325
+#: src/engine-gpgme.c:992
 msgid "                aka: "
 msgstr "                    alias: "
 
-#: src/engine-gpgme.c:1329 src/engine-gpgme.c:1351
+#: src/engine-gpgme.c:996 src/engine-gpgme.c:1018
 msgid "            created: "
 msgstr "                  erzeugt: "
 
-#: src/engine-gpgme.c:1338
+#: src/engine-gpgme.c:1005
 msgid "*BAD* signature claimed to be from: "
 msgstr "*FALSCHE* Unterschrift, vorgeblich von: "
 
-#: src/engine-gpgme.c:1361
+#: src/engine-gpgme.c:1028
 msgid "Error checking signature"
 msgstr "Fehler beim Prüfen der Unterschrift"
 
-#: src/engine-gpgme.c:1377
+#: src/engine-gpgme.c:1044
 msgid "*** Begin Notation (signature by: "
 msgstr "*** Anfang Notation (Unterschrift von: "
 
-#: src/engine-gpgme.c:1397
+#: src/engine-gpgme.c:1064
 msgid "*** End Notation ***\n"
 msgstr "*** Ende Notation ***\n"
 
@@ -246,23 +263,6 @@
 msgid "Signing Failure"
 msgstr "Unterschrifterstellungsfehler"
 
-#: src/gpgmsg.cpp:1812
-msgid ""
-"The configured default encryption key is not available or does not "
-"unambigiously specify a key. Please fix this in the option dialog.\n"
-"\n"
-"This message won't be be encrypted to this key!"
-msgstr ""
-"Der voreingestellte zusätzliche Schlüssel zum Verschlüsseln ist nicht\n"
-"vorhanden oder nicht eindeutig.  Bitte beheben Sie dies in den\n"
-"Optionseinstellungen.\n"
-"\n"
-"Die Nachricht wird deswegen nicht für diesen Schlüssel verschlüsselt!"
-
-#: src/gpgmsg.cpp:1816
-msgid "Encryption"
-msgstr "Verschlüsselung"
-
 #: src/gpgmsg.cpp:1859
 msgid "Encryption Failure"
 msgstr "Verschlüsselungsfehler"
@@ -287,7 +287,7 @@
 msgid "The default key may not contain any spaces."
 msgstr "Der Standardschlüssel darf keine Leerzeichen enthalten."
 
-#: src/olflange.cpp:475
+#: src/olflange.cpp:485
 msgid ""
 "This version of Outlook is too old!\n"
 "\n"

Modified: trunk/po/sv.po
===================================================================
--- trunk/po/sv.po	2007-10-08 10:19:07 UTC (rev 187)
+++ trunk/po/sv.po	2007-10-10 13:23:41 UTC (rev 188)
@@ -7,7 +7,7 @@
 msgstr ""
 "Project-Id-Version: GPGol\n"
 "Report-Msgid-Bugs-To: bug-gpgol at g10code.com\n"
-"POT-Creation-Date: 2007-09-11 20:30+0200\n"
+"POT-Creation-Date: 2007-09-25 12:20+0200\n"
 "PO-Revision-Date: 2006-12-12 23:52+0100\n"
 "Last-Translator: Daniel Nylander <po at danielnylander.se>\n"
 "Language-Team: Swedish <tp-sv at listor.tp-sv.se>\n"
@@ -23,59 +23,75 @@
 msgid "Select GPG Key Manager"
 msgstr "Välj GPG-nyckelhanterare"
 
-#: src/engine-gpgme.c:1099
+#: src/engine-gpgme.c:356 src/gpgmsg.cpp:1812
+msgid ""
+"The configured default encryption key is not available or does not "
+"unambigiously specify a key. Please fix this in the option dialog.\n"
+"\n"
+"This message won't be be encrypted to this key!"
+msgstr ""
+"Den konfigurerade standardkrypteringsnyckeln är inte tillgänglig eller anger "
+"inte uttryckligen en nyckel. Rätta till det här i inställningarna.\n"
+"\n"
+"Det här meddelandet kommer inte att krypteras med den här nyckeln!"
+
+#: src/engine-gpgme.c:360 src/gpgmsg.cpp:1816
+msgid "Encryption"
+msgstr "Kryptering"
+
+#: src/engine-gpgme.c:766
 msgid "Fingerprint: "
 msgstr "Fingeravtryck: "
 
-#: src/engine-gpgme.c:1156
+#: src/engine-gpgme.c:823
 msgid "This signature is valid\n"
 msgstr "Den här signaturen är giltig\n"
 
-#: src/engine-gpgme.c:1158
+#: src/engine-gpgme.c:825
 msgid "signature state is \"green\"\n"
 msgstr "signaturens tillstånd är \"grön\"\n"
 
-#: src/engine-gpgme.c:1160
+#: src/engine-gpgme.c:827
 msgid "signature state is \"red\"\n"
 msgstr "signaturens tillstånd är \"röd\"\n"
 
-#: src/engine-gpgme.c:1164
+#: src/engine-gpgme.c:831
 msgid "Warning: One of the keys has been revoked\n"
 msgstr "Varning: En av nycklarna har spärrats\n"
 
-#: src/engine-gpgme.c:1174
+#: src/engine-gpgme.c:841
 msgid "Warning: The key used to create the signature expired at: "
 msgstr "Varning: Nyckeln som användes för att skapa signaturen gick ut den: "
 
-#: src/engine-gpgme.c:1180
+#: src/engine-gpgme.c:847
 msgid "Warning: At least one certification key has expired\n"
 msgstr "Varning: Åtminstone en certifieringsnyckel har gått ut\n"
 
-#: src/engine-gpgme.c:1186
+#: src/engine-gpgme.c:853
 msgid "Warning: The signature expired at: "
 msgstr "Varning: Signaturen gick ut den: "
 
-#: src/engine-gpgme.c:1192
+#: src/engine-gpgme.c:859
 msgid "Can't verify due to a missing key or certificate\n"
 msgstr "Kan inte validera på grund av en saknad nyckel eller certifikat\n"
 
-#: src/engine-gpgme.c:1196
+#: src/engine-gpgme.c:863
 msgid "The CRL is not available\n"
 msgstr "Spärrlistan är inte tillgänglig\n"
 
-#: src/engine-gpgme.c:1202
+#: src/engine-gpgme.c:869
 msgid "Available CRL is too old\n"
 msgstr "Tillgänglig spärrlista är för gammal\n"
 
-#: src/engine-gpgme.c:1207
+#: src/engine-gpgme.c:874
 msgid "A policy requirement was not met\n"
 msgstr "Ett policykrav matchades inte\n"
 
-#: src/engine-gpgme.c:1213
+#: src/engine-gpgme.c:880
 msgid "A system error occured"
 msgstr "Ett systemfel inträffade"
 
-#: src/engine-gpgme.c:1250
+#: src/engine-gpgme.c:917
 msgid ""
 "WARNING: We have NO indication whether the key belongs to the person named "
 "as shown above\n"
@@ -83,11 +99,11 @@
 "VARNING: Vi har INGA indikationer på huruvida nyckeln tillhör personen vars "
 "namn visas ovanför\n"
 
-#: src/engine-gpgme.c:1257
+#: src/engine-gpgme.c:924
 msgid "WARNING: The key does NOT BELONG to the person named as shown above\n"
 msgstr "VARNING: Nyckeln TILLHÖR INTE personen vars namn visas ovanför\n"
 
-#: src/engine-gpgme.c:1261
+#: src/engine-gpgme.c:928
 msgid ""
 "WARNING: It is NOT certain that the key belongs to the person named as shown "
 "above\n"
@@ -95,43 +111,43 @@
 "VARNING: Det är INTE säkert att nyckeln tillhör den person vars namn visas "
 "ovanför\n"
 
-#: src/engine-gpgme.c:1294
+#: src/engine-gpgme.c:961
 msgid "Verification started at: "
 msgstr "Validering startad: "
 
-#: src/engine-gpgme.c:1299
+#: src/engine-gpgme.c:966
 msgid "Verification result for: "
 msgstr "Valideringsresultat för: "
 
-#: src/engine-gpgme.c:1300
+#: src/engine-gpgme.c:967
 msgid "[unnamed part]"
 msgstr "[ej namngiven del]"
 
-#: src/engine-gpgme.c:1318 src/engine-gpgme.c:1348
+#: src/engine-gpgme.c:985 src/engine-gpgme.c:1015
 msgid "Good signature from: "
 msgstr "Korrekt signatur från: "
 
-#: src/engine-gpgme.c:1325
+#: src/engine-gpgme.c:992
 msgid "                aka: "
 msgstr "även känd som:"
 
-#: src/engine-gpgme.c:1329 src/engine-gpgme.c:1351
+#: src/engine-gpgme.c:996 src/engine-gpgme.c:1018
 msgid "            created: "
 msgstr "             skapad: "
 
-#: src/engine-gpgme.c:1338
+#: src/engine-gpgme.c:1005
 msgid "*BAD* signature claimed to be from: "
 msgstr "*FELAKTIG* signatur hävdades komma från: "
 
-#: src/engine-gpgme.c:1361
+#: src/engine-gpgme.c:1028
 msgid "Error checking signature"
 msgstr "Fel vid kontroll av signatur"
 
-#: src/engine-gpgme.c:1377
+#: src/engine-gpgme.c:1044
 msgid "*** Begin Notation (signature by: "
 msgstr "*** Notation start (signatur av: "
 
-#: src/engine-gpgme.c:1397
+#: src/engine-gpgme.c:1064
 msgid "*** End Notation ***\n"
 msgstr "*** Notation slut ***\n"
 
@@ -241,22 +257,6 @@
 msgid "Signing Failure"
 msgstr "Signering misslyckades"
 
-#: src/gpgmsg.cpp:1812
-msgid ""
-"The configured default encryption key is not available or does not "
-"unambigiously specify a key. Please fix this in the option dialog.\n"
-"\n"
-"This message won't be be encrypted to this key!"
-msgstr ""
-"Den konfigurerade standardkrypteringsnyckeln är inte tillgänglig eller anger "
-"inte uttryckligen en nyckel. Rätta till det här i inställningarna.\n"
-"\n"
-"Det här meddelandet kommer inte att krypteras med den här nyckeln!"
-
-#: src/gpgmsg.cpp:1816
-msgid "Encryption"
-msgstr "Kryptering"
-
 #: src/gpgmsg.cpp:1859
 msgid "Encryption Failure"
 msgstr "Kryptering misslyckades"
@@ -281,7 +281,7 @@
 msgid "The default key may not contain any spaces."
 msgstr "Standardnyckeln får inte innehålla några blanksteg."
 
-#: src/olflange.cpp:475
+#: src/olflange.cpp:485
 msgid ""
 "This version of Outlook is too old!\n"
 "\n"

Modified: trunk/src/ChangeLog
===================================================================
--- trunk/src/ChangeLog	2007-10-08 10:19:07 UTC (rev 187)
+++ trunk/src/ChangeLog	2007-10-10 13:23:41 UTC (rev 188)
@@ -1,3 +1,22 @@
+2007-10-08  Werner Koch  <wk at g10code.com>
+
+	* main.c (do_log): Remove trailing LF from w32 error message and
+	also print the numeric error code.
+
+2007-09-25  Werner Koch  <wk at g10code.com>
+
+	* Makefile.am (gpgol_LDADD): Link against libassuan.
+
+	* util.h (DIM, DIMof): New.
+
+	* engine.c (filter_gpgme_read_cb): Implement nonblock feature.
+	(filter_gpgme_write_cb): Ditto.
+
+2007-09-24  Werner Koch  <wk at g10code.com>
+
+	* common.c (standard_homedir, default_homedir): New. 
+	(w32_shgetfolderpath): Make static.
+
 2007-09-21  Werner Koch  <wk at g10code.com>
 
 	* mimeparser.c (build_mimeinfo): New.

Modified: trunk/src/Makefile.am
===================================================================
--- trunk/src/Makefile.am	2007-10-08 10:19:07 UTC (rev 187)
+++ trunk/src/Makefile.am	2007-10-10 13:23:41 UTC (rev 188)
@@ -66,22 +66,26 @@
 # versions of GPGME and gpg-error, because we want to link to them
 # statically, and not dynamically (otherwise Outlook would not find
 # them).
-gpgol_DEPENDENCIES = libmapi32.a libgpgme.a libgpg-error.a
+gpgol_DEPENDENCIES = libmapi32.a libgpg-error.a libgpgme.a libassuan.a
 
 libmapi32.a: mapi32.def
 	$(DLLTOOL) --output-lib $@ --def $<
 
+libgpg-error.a:
+	ln -s $(shell $(GPG_ERROR_CONFIG) --prefix)/lib/libgpg-error.a
+
 libgpgme.a:
 	ln -s $(shell $(GPGME_CONFIG) --prefix)/lib/libgpgme.a
 
-libgpg-error.a:
-	ln -s $(shell $(GPG_ERROR_CONFIG) --prefix)/lib/libgpg-error.a
+libassuan.a:
+	ln -s $(shell $(LIBASSUAN_CONFIG) --prefix)/lib/libassuan.a
 
 clean-local:
-	rm -f libmapi32.a libgpgme.a libgpg-error.a
+	rm -f libmapi32.a libgpg-error.a libgpgme.a libassuan.a
 
 gpgol_LDADD = $(srcdir)/gpgol.def  \
-	-L . -lgpgme -lgpg-error -lmapi32 -lshell32 -lgdi32 -lcomdlg32 \
+	-L . -lgpgme -lassuan -lgpg-error \
+	-lmapi32 -lshell32 -lgdi32 -lcomdlg32 \
         -lole32 -loleaut32 -lws2_32 -ladvapi32
 
 resource.o: resource.rc versioninfo.rc gpgol-rsrcs.rc olflange-rsrcs.rc

Modified: trunk/src/common.c
===================================================================
--- trunk/src/common.c	2007-10-08 10:19:07 UTC (rev 187)
+++ trunk/src/common.c	2007-10-10 13:23:41 UTC (rev 188)
@@ -1,4 +1,4 @@
-/* common.c - Common routines used bu GpgOL
+/* common.c - Common routines used by GpgOL
  *	Copyright (C) 2005, 2007 g10 Code GmbH
  *
  * This file is part of GpgOL.
@@ -21,7 +21,18 @@
 
 #include <config.h>
 #include <windows.h>
+#include <shlobj.h>
+#ifndef CSIDL_APPDATA
+#define CSIDL_APPDATA 0x001a
+#endif
+#ifndef CSIDL_LOCAL_APPDATA
+#define CSIDL_LOCAL_APPDATA 0x001c
+#endif
+#ifndef CSIDL_FLAG_CREATE
+#define CSIDL_FLAG_CREATE 0x8000
+#endif
 #include <time.h>
+#include <fcntl.h>
 
 #include "common.h"
 
@@ -194,7 +205,7 @@
 
 /* This is a helper function to load a Windows function from either of
    one DLLs. */
-HRESULT
+static HRESULT
 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
 {
   static int initialized;
@@ -447,8 +458,79 @@
 }
 
 
+/* Get the standard home directory.  In general this function should
+   not be used as it does not consider a registry value or the
+   GNUPGHOME environment variable.  Please use default_homedir(). */
+static const char *
+standard_homedir (void)
+{
+  static char *dir;
 
+  if (!dir)
+    {
+      char path[MAX_PATH];
+      
+      /* It might be better to use LOCAL_APPDATA because this is
+         defined as "non roaming" and thus more likely to be kept
+         locally.  For private keys this is desired.  However, given
+         that many users copy private keys anyway forth and back,
+         using a system roaming services might be better than to let
+         them do it manually.  A security conscious user will anyway
+         use the registry entry to have better control.  */
+      if (w32_shgetfolderpath (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE, 
+                               NULL, 0, path) >= 0) 
+        {
+          char *tmp = malloc (strlen (path) + 6 + 1);
 
+	  strcpy (tmp, path);
+	  strcat (tmp, "\\gnupg");
+          
+          dir = tmp;
+          
+          /* Try to create the directory if it does not yet exists.  */
+          if (access (dir, F_OK))
+            CreateDirectory (dir, NULL);
+        }
+      else
+        dir = xstrdup ("C:\\gnupg");
+    }
+  return dir;
+}
+
+
+/* Retrieve the default home directory.  */
+const char *
+default_homedir (void)
+{
+  static char *dir;
+
+  if (!dir)
+    {
+      dir = getenv ("GNUPGHOME");
+      if (!dir || !*dir)
+        {
+          char *tmp;
+          
+          tmp = read_w32_registry_string (NULL, "Software\\GNU\\GnuPG",
+                                          "HomeDir");
+          if (tmp && !*tmp)
+            {
+              free (tmp);
+              tmp = NULL;
+            }
+          if (tmp)
+            dir = tmp;
+          else
+            dir = xstrdup (standard_homedir ());
+        }
+      else
+        dir = xstrdup (dir);
+    }
+  
+  return dir;
+}
+
+
 /* Do in-place decoding of quoted-printable data of LENGTH in BUFFER.
    Returns the new length of the buffer. */
 size_t

Modified: trunk/src/common.h
===================================================================
--- trunk/src/common.h	2007-10-08 10:19:07 UTC (rev 187)
+++ trunk/src/common.h	2007-10-10 13:23:41 UTC (rev 188)
@@ -145,7 +145,7 @@
 char *get_save_filename (HWND root, const char *srcname);
 char *utf8_to_wincp (const char *string);
 
-HRESULT w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e);
+const char *default_homedir (void);
 
 size_t qp_decode (char *buffer, size_t length);
 void b64_init (b64_state_t *state);

Modified: trunk/src/engine-assuan.c
===================================================================
--- trunk/src/engine-assuan.c	2007-10-08 10:19:07 UTC (rev 187)
+++ trunk/src/engine-assuan.c	2007-10-10 13:23:41 UTC (rev 188)
@@ -30,6 +30,7 @@
 #define WIN32_LEAN_AND_MEAN 
 #include <windows.h>
 
+#include <assuan.h>
 #include "common.h"
 #include "engine.h"
 #include "engine-assuan.h"
@@ -40,32 +41,347 @@
                         } while (0)
 
 
-/* Because we are using asynchronous gpgme commands, we need to have a
-   closure to cleanup allocated resources and run the code required
-   adfter gpgme finished the command (e.g. getting the signature
-   verification result.  Thus all functions need to implement a
-   closure function and pass it using a closure_data_t object via the
-   gpgme_progress_cb hack.  */
+/* This is the buffer object used for the asynchronous reading of the
+   status channel.  */
+struct status_buffer_s
+{
+  int eof;
+  int linelen;  /* Used length of LINE. */
+  char line[ASSUAN_LINELENGTH];
+};
+typedef struct status_buffer_s *status_buffer_t;
+
+
+/* We operate in an asynchronous mode and thus need to run code for
+   final cleanup.  Thus all functions need to implement a closure
+   function and setup an closure_data_t object.  */ 
 struct closure_data_s;
 typedef struct closure_data_s *closure_data_t;
 struct closure_data_s
 {
-  void (*closure)(closure_data_t, gpgme_ctx_t, gpg_error_t);
+  void (*closure)(closure_data_t);
+  gpg_error_t final_err;         /* Final error code.  */
   engine_filter_t filter;
-  struct passphrase_cb_s pw_cb;  /* Passphrase callback info.  */
+  assuan_context_t assctx;
+  ULONG cmdid;
+  assuan_fd_t status_read_fd;
+  struct gpgme_data_cbs status_cbs;
+  gpgme_data_t status_data;
+  status_buffer_t status_buffer; /* Allocated on demand.  */
+  int status_ready;
+  gpg_error_t last_err;
 };
 
 
-static int init_done = 0;
+/* The object used by our I/O worker thread.  */
+struct work_item_s;
+typedef struct work_item_s *work_item_t;
+struct work_item_s
+{
+  work_item_t next;
+  int used;          /* If not set this object may be reused.  */
+  int waiting;       /* Helper for async_worker_thread.  */
 
+  const char *name;  /* Description used for debugging.  */
+  ULONG cmdid;       /* Used to group work items of one command.  */
+  closure_data_t cld;/* NULL or the closure.  */
+  int wait_on_success; /* This work item needs to be ready before
+                          invoking a closure for this command.  */
+  gpgme_data_t data; /* The data object we write to or read from.  */
+  int writing;       /* If true we are going to write to HD.  */
+  HANDLE hd;         /* The handle we read from or write to.  */
+  int io_pending;    /* I/O is still pending.  The value is the number
+                        of bytes to be written or the size of the
+                        buffer given to ReadFile. */
+  int got_ready;     /* Operation finished.  */
+  int delayed_ready; /* Ready but delayed to to a missing prerequesite.  */
+  int got_error;     /* An error as been encountered.  */
+  int aborting;      /* Set to true after a CancelIO has been issued.  */
+  void (*finalize)(work_item_t); /* Function called immediately before
+                                    the item is removed from the
+                                    queue.  */
+  OVERLAPPED ov;     /* The overlapped info structure.  */
+  char buffer[128];  /* The buffer used by ReadFile or WriteFile.  */
+};
 
-static DWORD WINAPI pipe_worker_thread (void *dummy);
 
+/* The queue of all outstandig I/O operations.  Protected by the
+   work_queue_lock.  */
+static work_item_t work_queue;
 
+/* The big lock used to protect the work queue.  */
+static CRITICAL_SECTION work_queue_lock;
+
+/* An auto-reset event which will be signaled to get the
+   async_worker_thread out of its WFMO and to inspect the work
+   queue.  */
+static HANDLE work_queue_event;
+
+
+/*-- prototypes --*/
+static DWORD WINAPI async_worker_thread (void *dummy);
+
+
+
+
+/* Return the next command id.  Command Ids are used to group
+   resources of one command. */
+static ULONG
+create_command_id (void)
+{
+  static ULONG command_id;
+  ULONG cmdid;
+
+  while (!(cmdid = InterlockedIncrement (&command_id)))
+    ;
+  return cmdid;
+}
+
+
+/* Duplicate HANDLE into the server's process and close HANDLE.  Note
+   that HANDLE is closed even if the function fails.  Returns the
+   duplicated handle on success or INVALID_HANDLE_VALUE on error.  */
+static HANDLE
+dup_to_server (HANDLE handle, pid_t serverpid)
+{
+  HANDLE prochandle, newhandle;
+
+  prochandle = OpenProcess (PROCESS_DUP_HANDLE, FALSE, serverpid);
+  if (!prochandle)
+    {
+      log_error_w32 (-1, "%s:%s: OpenProcess(%lu) failed", 
+                     SRCNAME, __func__, (unsigned long)serverpid);
+      CloseHandle (handle);
+      return INVALID_HANDLE_VALUE;
+    }
+
+  if (!DuplicateHandle (GetCurrentProcess(), handle,
+                        prochandle, &newhandle, 0,
+                        TRUE, DUPLICATE_SAME_ACCESS ))
+    {
+      log_error_w32 (-1, "%s:%s: DuplicateHandle to pid %lu failed", 
+                     SRCNAME, __func__, (unsigned long)serverpid);
+      CloseHandle (prochandle);
+      CloseHandle (handle);
+      return INVALID_HANDLE_VALUE;
+    }
+  CloseHandle (prochandle);
+  CloseHandle (handle);
+  return newhandle;
+}
+
+
+/* Create pipe with one end being inheritable and prepared for
+   overlapped I/O.
+
+     FILEDES[0] := read handle. 
+     FILEDES[1] := write handle. 
+
+   SERVERPID is the PID of the server.  FOR_WRITE is seen out of our
+   perspective; if it is set, the read handle is created in the server
+   process and the write handle is overlapped.  If it is not set the
+   write handle is created in the server process and the read handle
+   is overlapped.
+*/
+static gpg_error_t
+create_io_pipe (HANDLE filedes[2], pid_t serverpid, int for_write)
+{
+  static ULONG pipenumber;
+  ULONG pipeno;
+  char pipename[100];
+  HANDLE r, w;
+  SECURITY_ATTRIBUTES sec_attr;
+
+  memset (&sec_attr, 0, sizeof sec_attr );
+  sec_attr.nLength = sizeof sec_attr;
+
+  /* CreatePipe is in reality implemented using a Named Pipe.  We do
+     it the same but use a name which is in our name space.  We allow
+     only one instance, use the standard timeout of 120 seconds and
+     buffers of 4k. */
+  pipeno = InterlockedIncrement (&pipenumber);
+  snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\GpgOL_anon.%08lx.%08lx",
+            (unsigned long)GetCurrentProcessId(), pipeno);
+  sec_attr.bInheritHandle = /*for_write? TRUE :*/FALSE;
+  r = CreateNamedPipe (pipename, (PIPE_ACCESS_INBOUND
+                                  | (for_write? 0:FILE_FLAG_OVERLAPPED)),
+                       PIPE_TYPE_BYTE | PIPE_WAIT,
+                       1, 4096, 4096, 120000, &sec_attr);
+  if (r == INVALID_HANDLE_VALUE)
+    {
+      log_error_w32 (-1, "%s:%s: CreateNamedPipe failed for `%s'",
+                     SRCNAME, __func__, pipename);
+      return gpg_error (GPG_ERR_GENERAL);
+    }
+  if (for_write)
+    {
+      r = dup_to_server (r, serverpid);
+      if (r == INVALID_HANDLE_VALUE)
+        {
+          log_error_w32 (-1, "%s:%s: dup_for_server(r) failed for `%s'",
+                         SRCNAME, __func__, pipename);
+          return gpg_error (GPG_ERR_GENERAL);
+        }
+    }
+
+  /* Now open the other side of the named pipe.  Because we have not
+     called ConnectNamedPipe another process should not be able to
+     open the pipe in the meantime.  This is an educated guess by
+     looking at REACTOS and WINE - they implement an anonymous pipe
+     this way.  */
+  sec_attr.bInheritHandle = /*for_write?*/ FALSE /*: TRUE*/;
+  w = CreateFile (pipename, GENERIC_WRITE, 0, &sec_attr,
+                  OPEN_EXISTING, (FILE_ATTRIBUTE_NORMAL
+                                  | (for_write? FILE_FLAG_OVERLAPPED:0)),
+                  NULL);
+  if (w == INVALID_HANDLE_VALUE)
+    {
+      log_error_w32 (-1, "%s:%s: CreateFile failed for `%s'",
+                     SRCNAME, __func__, pipename);
+      CloseHandle (r);
+      return gpg_error (GPG_ERR_GENERAL);
+    }
+  if (!for_write)
+    {
+      w = dup_to_server (w, serverpid);
+      if (w == INVALID_HANDLE_VALUE)
+        {
+          log_error_w32 (-1, "%s:%s: dup_for_server(w) failed for `%s'",
+                         SRCNAME, __func__, pipename);
+          CloseHandle (r);
+          return gpg_error (GPG_ERR_GENERAL);
+        }
+    }
+
+  filedes[0] = r;
+  filedes[1] = w;
+  log_debug ("%s:%s: new pipe created: r=%p%s w=%p%s",  SRCNAME, __func__,
+             r, for_write? " (server)":"",
+             w, !for_write?" (server)":"");
+  return 0;
+}
+
+
+/* Return the socket name of the UI Server.  */
+static const char *
+get_socket_name (void)
+{
+  static char *name;
+
+  if (!name)
+    {
+      const char *dir = default_homedir ();
+      name = xmalloc (strlen (dir) + 11 + 1);
+      strcpy (stpcpy (name, dir), "\\S.uiserver");
+    }
+
+  return name;
+}
+
+
+
+static gpg_error_t
+send_one_option (assuan_context_t ctx, const char *name, const char *value)
+{
+  gpg_error_t err;
+  char buffer[1024];
+
+  if (!value || !*value)
+    err = 0;  /* Avoid sending empty strings.  */
+  else 
+    {
+      snprintf (buffer, sizeof buffer, "OPTION %s=%s", name, value);
+      err = assuan_transact (ctx, buffer, NULL, NULL, NULL, NULL, NULL, NULL);
+    }
+
+  return err;
+}
+
+
+static int
+getinfo_pid_cb (void *opaque, const void *buffer, size_t length)
+{
+  pid_t *pid = opaque;
+  char pidbuf[50];
+
+  /* There is only the pid in the server's response.  */
+  if (length >= sizeof pidbuf)
+    length = sizeof pidbuf -1;
+  if (length)
+    {
+      strncpy (pidbuf, buffer, length);
+      pidbuf[length] = 0;
+      *pid = (pid_t)strtoul (pidbuf, NULL, 10);
+    }
+  return 0;
+}
+
+
+/* Send options to the UI server and return the server's PID.  */
+static gpg_error_t
+send_options (assuan_context_t ctx, void *hwnd, pid_t *r_pid)
+{
+  gpg_error_t err = 0;
+  char numbuf[50];
+
+  *r_pid = (pid_t)(-1);
+  if (hwnd)
+    {
+      snprintf (numbuf, sizeof numbuf, "%lx", (unsigned long)hwnd);
+      err = send_one_option (ctx, "window-id", numbuf);
+    }
+  if (!err)
+    {
+      err = assuan_transact (ctx, "GETINFO pid", getinfo_pid_cb, r_pid,
+                             NULL, NULL, NULL, NULL);
+      if (!err && *r_pid == (pid_t)(-1))
+        {
+          log_debug ("%s:%s: server did not return a PID", SRCNAME, __func__);
+          err = gpg_error (GPG_ERR_ASSUAN_SERVER_FAULT);
+        }
+    }
+
+  return err;
+}
+
+
+/* Connect to the UI server and setup the connection.  */
+static gpg_error_t
+connect_uiserver (assuan_context_t *r_ctx, pid_t *r_pid, ULONG *r_cmdid,
+                  void *hwnd)
+{
+  gpg_error_t err;
+  assuan_context_t ctx;
+
+  *r_ctx = NULL;
+  *r_pid = (pid_t)(-1);
+  *r_cmdid = 0;
+  err = assuan_socket_connect (&ctx, get_socket_name (), -1);
+  if (err)
+    {
+      log_error ("%s:%s: error connecting `%s': %s\n", SRCNAME, __func__,
+                 get_socket_name (), gpg_strerror (err));
+    }
+  else if ((err = send_options (ctx, hwnd, r_pid)))
+    {
+      assuan_disconnect (ctx);
+    }
+  else
+    {
+      *r_cmdid = create_command_id ();
+      *r_ctx = ctx;
+    }
+  return err;
+}
+
+
+
+
+
 static void
 cleanup (void)
 {
-  /* Fixme: We should stop the thread.  */
+  /* Fixme: We should stop the worker thread.  */
 }
 
 
@@ -81,23 +397,40 @@
 int
 op_assuan_init (void)
 {
+  static int init_done;
   gpgme_error_t err;
+  assuan_context_t ctx;
+  pid_t pid;
+  ULONG cmdid;
 
   if (init_done)
     return 0;
-
-  /* FIXME: Connect to the server and return failure if it is not
-     possible.  */
-  return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
-
+  
+  /* Run a test connection to see whether the UI server is available.  */
+  err = connect_uiserver (&ctx, &pid, &cmdid, NULL);
+  if (!err)
+    {
+      err = assuan_transact (ctx, "NOP", NULL, NULL, NULL, NULL, NULL, NULL);
+      assuan_disconnect (ctx);
+    }
+  if (err)
+    return err;
+  
   /* Fire up the pipe worker thread. */
   {
     HANDLE th;
     DWORD tid;
 
-    th = CreateThread (NULL, 128*1024, pipe_worker_thread, NULL, 0, &tid);
+    InitializeCriticalSection (&work_queue_lock);
+    work_queue_event = CreateEvent (NULL, FALSE, FALSE, NULL);
+    if (!work_queue_event)
+      {
+        log_error_w32 (-1, "%s:%s: CreateEvent failed", SRCNAME, __func__);
+        return gpg_error (GPG_ERR_GENERAL);
+      }
+    th = CreateThread (NULL, 256*1024, async_worker_thread, NULL, 0, &tid);
     if (th == INVALID_HANDLE_VALUE)
-      log_error ("failed to start the piper worker thread\n");
+      log_error ("failed to launch the async_worker_thread");
     else
       CloseHandle (th);
   }
@@ -107,87 +440,809 @@
 }
 
 
+
+/* Helper for async_worker_thread.  Returns true if the item's handle
+   needs to be put on the wait list.  This is called with the worker
+   mutex hold. */
+static int
+worker_start_read (work_item_t item)
+{
+  int nwritten;
+  DWORD nbytes;
+  int retval = 0;
+
+  /* Read from the handle and write to the callback.  The gpgme
+     callback is expected to never block.  */
+  if (ReadFile (item->hd, item->buffer, sizeof item->buffer,
+                &nbytes, &item->ov) )
+    {
+      /* (With overlapped, EOF is not indicated by NBYTES==0.)  */
+      if (!nbytes)
+        log_error ("%s:%s: [%s:%p] short read (0 bytes)",
+                   SRCNAME, __func__, item->name, item->hd);
+      else
+        {
+          nwritten = gpgme_data_write (item->data, item->buffer, nbytes);
+          if (nwritten < 0)
+            {
+              log_error ("%s:%s: [%s:%p] writing to callback failed: %s",
+                         SRCNAME, __func__, item->name, item->hd,
+                         strerror (errno));
+              item->got_error = 1;
+            }
+          else if (nwritten < nbytes)
+            {
+              log_error ("%s:%s: [%s:%p] short write to callback (%d of %lu)",
+                         SRCNAME, __func__, item->name, item->hd,
+                         nwritten, nbytes);
+              item->got_error = 1;
+            }
+          else
+            log_debug ("%s:%s: [%s:%p] wrote %d bytes to callback", 
+                       SRCNAME, __func__, item->name, item->hd, nwritten);
+        }
+      retval = 1;
+    }
+  else 
+    {
+      int syserr = GetLastError ();
+
+      if (syserr == ERROR_IO_PENDING)
+        {
+          log_debug ("%s:%s: [%s:%p] io(read) pending",
+                     SRCNAME, __func__, item->name, item->hd);
+          item->io_pending = sizeof item->buffer;
+          retval = 1;
+        }
+      else if (syserr == ERROR_HANDLE_EOF || syserr == ERROR_BROKEN_PIPE)
+        {
+          log_debug ("%s:%s: [%s:%p] EOF%s seen",
+                     SRCNAME, __func__, item->name, item->hd,
+                     syserr == ERROR_BROKEN_PIPE? " (broken pipe)":"");
+          item->got_ready = 1;
+        }
+      else
+        {
+          log_error_w32 (syserr, "%s:%s: [%s:%p] read error",
+                         SRCNAME, __func__, item->name, item->hd);
+          item->got_error = 1;
+        }
+    }
+
+  return retval;
+}
+
+/* Result checking helper for async_worker_thread.  This is called with
+   the worker mutex hold.  */
+static void
+worker_check_read (work_item_t item, DWORD nbytes)
+{
+  int nwritten;
+
+  if (!nbytes)
+    log_error ("%s:%s: [%s:%p] short read (0 bytes)",
+               SRCNAME, __func__, item->name, item->hd);
+  else
+    {
+      assert (nbytes > 0);
+      nwritten = gpgme_data_write (item->data, item->buffer, nbytes);
+      if (nwritten < 0)
+        {
+          log_error ("%s:%s: [%s:%p] error writing to callback: %s",
+                     SRCNAME, __func__, item->name, item->hd,strerror (errno));
+          item->got_error = 1;
+        }
+      else if (nwritten < nbytes)
+        {
+          log_error ("%s:%s: [%s:%p] short write to callback (%d of %lu)",
+                     SRCNAME, __func__, item->name, item->hd, nwritten,nbytes);
+          item->got_error = 1;
+        }
+      else
+        log_debug ("%s:%s: [%s:%p] wrote %d bytes to callback",
+                   SRCNAME, __func__, item->name, item->hd, nwritten);
+    }
+}
+
+
+
+/* Helper for async_worker_thread.  Returns true if the item's handle
+   needs to be put on the wait list.  This is called with the worker
+   mutex hold.  */
+static int
+worker_start_write (work_item_t item)
+{
+  int nread;
+  DWORD nbytes;
+  int retval = 0;
+
+  /* Read from the callback and the write to the handle.  The gpgme
+     callback is expected to never block.  */
+  nread = gpgme_data_read (item->data, item->buffer, sizeof item->buffer);
+  if (nread < 0)
+    {
+      if (errno == EAGAIN)
+        {
+          log_debug ("%s:%s: [%s:%p] ignoring EAGAIN from callback",
+                     SRCNAME, __func__, item->name, item->hd);
+          Sleep (0);
+          retval = 1;
+        }
+      else
+        {
+          log_error ("%s:%s: [%s:%p] error reading from callback: %s",
+                     SRCNAME, __func__, item->name, item->hd,strerror (errno));
+          item->got_error = 1;
+        }
+    }
+  else if (!nread)
+    {
+      log_debug ("%s:%s: [%s:%p] EOF received from callback",
+                 SRCNAME, __func__, item->name, item->hd);
+      item->got_ready = 1;
+      retval = 1;
+    }
+  else 
+    {                  
+      if (WriteFile (item->hd, item->buffer, nread, &nbytes, &item->ov))
+        {
+          if (nbytes < nread)
+            {
+              log_error ("%s:%s: [%s:%p] short write (%lu of %d)", 
+                         SRCNAME, __func__, item->name,item->hd,nbytes, nread);
+              item->got_error = 1;
+            }
+          else
+            log_debug ("%s:%s: [%s:%p] wrote %lu bytes", 
+                       SRCNAME, __func__, item->name, item->hd, nbytes);
+          retval = 1;
+        }
+      else 
+        {
+          int syserr = GetLastError ();
+
+          if (syserr == ERROR_IO_PENDING)
+            {
+              log_debug ("%s:%s: [%s:%p] io(write) pending (%d bytes)",
+                         SRCNAME, __func__, item->name, item->hd, nread);
+              item->io_pending = nread;
+              retval = 1;
+            }
+          else
+            {
+              log_error_w32 (syserr, "%s:%s: [%s:%p] write error",
+                             SRCNAME, __func__, item->name, item->hd);
+              item->got_error = 1;
+            }
+        }
+    }
+
+  return retval;
+}
+
+
+/* Result checking helper for async_worker_thread.  This is called with
+   the worker mutex hold.  */
+static void
+worker_check_write (work_item_t item, DWORD nbytes)
+{
+  if (nbytes < item->io_pending)
+    {
+      log_error ("%s:%s: [%s:%p] short write (%lu of %d)",
+                 SRCNAME,__func__, item->name, item->hd, nbytes,
+                 item->io_pending);
+      item->got_error = 1;
+    }
+  else
+    log_debug ("%s:%s: [%s:%p] write finished (%lu bytes)", 
+               SRCNAME, __func__, item->name, item->hd, nbytes);
+}
+
+
+
 /* The worker thread which feeds the pipes.  */
 static DWORD WINAPI
-pipe_worker_thread (void *dummy)
+async_worker_thread (void *dummy)
 {
-  gpgme_ctx_t ctx;
-  gpg_error_t err;
-  void *a_voidptr;
-  closure_data_t closure_data;
+  work_item_t item;
+  int n;
+  DWORD nbytes;
+  HANDLE hdarray[MAXIMUM_WAIT_OBJECTS];
+  int count, addit, any_ready, hdarraylen;
 
   (void)dummy;
 
   for (;;)
     {
-      Sleep (1000);
+      /* Process our queue and fire up async I/O requests.  */
+      log_debug ("%s:%s: processing work queue", SRCNAME, __func__);
+      EnterCriticalSection (&work_queue_lock);
+      hdarraylen = 0;
+      hdarray[hdarraylen++] = work_queue_event;
+      count = 0;
+      any_ready = 0;
+      for (item = work_queue; item; item = item->next)
+        {
+          item->waiting = 0;
+          if (!item->used)
+            continue;
+          assert (item->hd != INVALID_HANDLE_VALUE);
+          count++;
+          if (item->got_error)
+            {
+              if (!item->delayed_ready)
+                any_ready = 1;
+              continue; 
+            }
+          assert (item->data);
+          if (hdarraylen == DIM (hdarray))
+            {
+              log_debug ("%s:%s: [%s:%p] wait array full - ignored for now",
+                         SRCNAME, __func__, item->name, item->hd);
+              continue;
+            }
+          
+          if (item->io_pending)
+            addit = 1;
+          else if (item->writing)
+            addit = worker_start_write (item);
+          else 
+            addit = worker_start_read (item);
+
+          if (addit)
+            {
+              hdarray[hdarraylen++] = item->hd;
+              item->waiting = 1; /* Just for the tarce output.  */
+            }
+          if (!item->delayed_ready && (item->got_error || item->got_ready))
+            any_ready = 1;
+        }
+      LeaveCriticalSection (&work_queue_lock);
+
+      if (any_ready)
+        log_debug ("%s:%s: %d items in queue; skipping wait", 
+                   SRCNAME, __func__, count);
+      else
+        {
+          log_debug ("%s:%s: %d items in queue; waiting for %d items:", 
+                     SRCNAME, __func__, count, hdarraylen-1);
+          for (item = work_queue; item; item = item->next)
+            {
+              if (item->waiting)
+                log_debug ("%s:%s: [%s:%p]",
+                           SRCNAME, __func__, item->name, item->hd);
+            }
+          n = WaitForMultipleObjects (hdarraylen, hdarray, FALSE, INFINITE);
+          if (n == WAIT_FAILED)
+            {
+              log_error_w32 (-1, "%s:%s: WFMO failed", SRCNAME, __func__);
+              Sleep (1000);
+            }
+          else if (n >= 0 && n < hdarraylen)
+            {
+              log_debug ("%s:%s: WFMO succeeded (res=%d)",SRCNAME,__func__, n);
+            }
+          else
+            {
+              log_error ("%s:%s: WFMO returned: %d", SRCNAME, __func__, n);
+              Sleep (1000);
+            }
+        }
+
+      /* Handle completion status.  */
+      EnterCriticalSection (&work_queue_lock);
+      log_debug ("%s:%s: checking completion states", SRCNAME, __func__);
+      for (item = work_queue; item; item = item->next)
+        {
+          if (!item->io_pending)
+            ;
+          else if (GetOverlappedResult (item->hd, &item->ov, &nbytes, FALSE))
+            {
+              if (item->writing)
+                worker_check_write (item, nbytes);
+              else
+                worker_check_read (item, nbytes);
+              item->io_pending = 0;
+            }
+          else 
+            {
+              int syserr = GetLastError ();
+              if (syserr == ERROR_IO_INCOMPLETE)
+                ;
+              else if (!item->writing && syserr == ERROR_HANDLE_EOF)
+                {
+                  /* Got EOF.  */
+                  log_debug ("%s:%s: [%s:%p] EOF received",
+                             SRCNAME, __func__, item->name, item->hd);
+                  item->io_pending = 0;
+                  item->got_ready = 1;
+                }
+              else
+                {
+                  log_error_w32 (syserr,
+                                 "%s:%s: [%s:%p] GetOverlappedResult failed",
+                                 SRCNAME, __func__, item->name, item->hd);
+                  item->got_error = 1;
+                  if (!item->aborting)
+                    {
+                      item->aborting = 1;
+                      if (!CancelIo (item->hd))
+                        log_error_w32 (-1, "%s:%s: [%s:%p] CancelIo failed",
+                                       SRCNAME,__func__, item->name, item->hd);
+                    }
+                  else 
+                    item->got_ready = 1;
+                }
+            }
+        }
+      LeaveCriticalSection (&work_queue_lock);
+
+      Sleep (0);
+
+      EnterCriticalSection (&work_queue_lock);
+      log_debug ("%s:%s: cleaning up work queue", SRCNAME, __func__);
+      for (item = work_queue; item; item = item->next)
+        {
+          if (item->used && (item->got_ready || item->got_error))
+            {
+              if (item->cld)
+                {
+                  if (!item->cld->final_err && item->got_error)
+                    item->cld->final_err = gpg_error (GPG_ERR_EIO);
+
+                  if (!item->cld->final_err)
+                    {
+                      /* Check whether there are other work items in
+                         this group we need to wait for before
+                         invoking the closure. */
+                      work_item_t itm2;
+                      
+                      for (itm2=work_queue; itm2; itm2 = itm2->next)
+                        if (itm2->used && itm2 != item 
+                            && itm2->cmdid == item->cmdid
+                            && itm2->wait_on_success
+                            && !(itm2->got_ready || itm2->got_error))
+                          break;
+                      if (itm2)
+                        {
+                          log_debug ("%s:%s: [%s:%p] delaying closure due to "
+                                     "[%s/%p]", SRCNAME, __func__,
+                                     item->name, item->hd, 
+                                     itm2->name, itm2->hd);
+                          item->delayed_ready = 1;
+                          break; 
+                        }
+                    }
+                  item->delayed_ready = 0;
+                  log_debug ("%s:%s: [%s:%p] invoking closure",
+                             SRCNAME,__func__, item->name, item->hd);
+                  
+                  item->cld->closure (item->cld);
+                  xfree (item->cld);
+                  item->cld = NULL;
+                }
+
+              item->got_ready = 0;
+              item->finalize (item);
+              item->used = 0;
+            }
+        }
+
+      LeaveCriticalSection (&work_queue_lock);
     }
 }
 
 
+void
+engine_assuan_cancel (void *cancel_data)
+{
+  /* FIXME */
+}
 
 
 
 
+/* Standard finalize handler.  Called right before the item is removed
+   from the queue.  Called while the work_queue_lock is hold.  */
+static void
+finalize_handler (work_item_t item)
+{
+  log_debug ("%s:%s: [%s:%p] closing handle", 
+             SRCNAME, __func__, item->name, item->hd);
+  CloseHandle (item->hd);
+  item->hd = INVALID_HANDLE_VALUE;
+}
 
+/* A finalize handler which does not close the handle.  */
+static void
+noclose_finalize_handler (work_item_t item)
+{
+  log_debug ("%s:%s: [%s:%p] called", SRCNAME, __func__, item->name, item->hd);
+  item->hd = INVALID_HANDLE_VALUE;
+}
 
-/* Not that this closure is called in the context of the
+
+/* Add a data callback and a handle to the work queue.  This should
+   only be called once per handle.  Caller gives up ownership of
+   CLD. */
+static void
+enqueue_callback (const char *name, assuan_context_t ctx, 
+                  gpgme_data_t data, HANDLE hd,
+                  int for_write, void (*fin_handler)(work_item_t),
+                  ULONG cmdid, closure_data_t cld, int wait_on_success)
+{
+  work_item_t item;
+  int created = 0;
+
+  EnterCriticalSection (&work_queue_lock);
+  for (item = work_queue; item; item = item->next)
+    if (!item->used)
+      break;
+  if (!item)
+    {
+      item = xmalloc (sizeof *item);
+      item->next = work_queue;
+      work_queue = item;
+      created = 1;
+    }
+  item->used = 1;
+  item->name = name;
+  item->cmdid = cmdid;
+  item->cld = cld;
+  item->wait_on_success = wait_on_success;
+  item->data = data;
+  item->writing = for_write;
+  item->hd = hd;
+  item->io_pending = 0;
+  item->got_ready = 0;
+  item->delayed_ready = 0;
+  item->got_error = 0;
+  item->aborting = 0;
+  item->finalize = fin_handler;
+  memset (&item->ov, 0, sizeof item->ov);
+  log_debug ("%s:%s: [%s:%p] created%s",
+             SRCNAME, __func__, item->name, item->hd, created?"":" (reusing)");
+  LeaveCriticalSection (&work_queue_lock);
+}
+
+
+/* Remove all items from the work queue belonging to the command with
+   the id CMDID.  */
+static int
+destroy_command (ULONG cmdid)
+{
+  work_item_t item;
+
+  EnterCriticalSection (&work_queue_lock);
+  for (item = work_queue; item; item = item->next)
+    if (item->used && item->cmdid == cmdid && !item->wait_on_success)
+      {
+        log_debug ("%s:%s: [%s:%p] cmdid=%lu registered for destroy",
+                   SRCNAME, __func__, item->name, item->hd, item->cmdid);
+        /* First send an I/O cancel in case the the last
+           GetOverlappedResult returned only a partial result.  This
+           works because we are always running within the
+           async_worker_thread.  */
+/*         if (!CancelIo (item->hd)) */
+/*           log_error_w32 (-1, "%s:%s: [%s:%p] CancelIo failed", */
+/*                          SRCNAME, __func__, item->name, item->hd); */
+        item->got_ready = 1;
+      }
+  LeaveCriticalSection (&work_queue_lock);
+}
+
+
+/* Process a status line.  */
+static int
+status_handler (closure_data_t cld, const char *line)
+{
+  gpg_error_t err;
+  int retval = 0;
+
+  log_debug ("%s:%s: cld %p, line `%s'", SRCNAME, __func__, cld, line);
+
+  if (*line == '#' || !*line)
+    ;
+  else if (line[0] == 'D' && line[1] == ' ')
+    {
+      line += 2;
+    }
+  else if (line[0] == 'S' && (!line[1] || line[1] == ' '))
+    {
+      for (line += 1; *line == ' '; line++)
+        ;
+    }  
+  else if (line[0] == 'O' && line[1] == 'K' && (!line[2] || line[2] == ' '))
+    {
+      for (line += 2; *line == ' '; line++)
+        ;
+      cld->final_err = 0;
+      retval = 1;
+    }
+  else if (!strncmp (line, "ERR", 3) && (!line[3] || line[3] == ' '))
+    {
+      for (line += 3; *line == ' '; line++)
+        ;
+      err = strtoul (line, NULL, 10);
+      if (!err)
+        err = gpg_error (GPG_ERR_ASS_INV_RESPONSE);
+      cld->final_err = err;
+      retval = 1;
+    }  
+  else if (!strncmp (line, "INQUIRE", 7) && (!line[7] || line[7] == ' '))
+    {
+      for (line += 7; *line == ' '; line++)
+        ;
+      /* We have no inquire handler thus get out of it immediately.  */
+      err = assuan_write_line (cld->assctx, "END");
+      if (err)
+        cld->last_err = err;
+    }
+  else if (!strncmp (line, "END", 3) && (!line[3] || line[3] == ' '))
+    {
+      for (line += 3; *line == ' '; line++)
+        ;
+    }
+  else
+    retval = -1; /* Invalid response.  */
+
+  return retval;
+}
+
+
+/* This write callback is used by GPGME to push data to our status
+   line handler.  The function should return the number of bytes
+   written, and -1 on error.  If an error occurs, ERRNO should be set
+   to describe the type of the error.  */
+static ssize_t
+status_in_cb (void *opaque, const void *buffer, size_t size)
+{
+  size_t orig_size = size;
+  closure_data_t cld = opaque;
+  status_buffer_t sb;
+  size_t nleft, nbytes;
+  char *p;
+
+  assert (cld);
+  if (!size)
+    return 0;
+
+  if (!(sb=cld->status_buffer))
+    {
+      cld->status_buffer = sb = xmalloc (sizeof *cld->status_buffer);
+      sb->eof = 0;
+      sb->linelen = 0;
+    }
+
+  do
+    {
+      assert (sb->linelen < ASSUAN_LINELENGTH);
+      nleft = ASSUAN_LINELENGTH - sb->linelen;
+      nbytes = size < nleft? size : nleft;
+      memcpy (sb->line+sb->linelen, buffer, nbytes);
+      sb->linelen += nbytes;
+      size -= nbytes;
+      p = memchr (sb->line, '\n', sb->linelen);
+      if (p && !cld->status_ready)
+        {
+          *p = 0;
+          if (p > sb->line && p[-1] == '\r')
+            p[-1] = 0;
+          switch (status_handler (cld, sb->line))
+            {
+            case 0: 
+              break;
+            case 1: /* Ready. */
+              cld->status_ready = 1;
+              destroy_command (cld->cmdid);
+              break;
+            default:
+              log_error ("%s:%s: invalid line from server", SRCNAME, __func__);
+              errno = EINVAL;
+              return -1;
+            }
+          sb->linelen -= (p+1 - sb->line);
+          memmove (sb->line, p+1, sb->linelen);
+        }
+      else if (sb->linelen >= ASSUAN_LINELENGTH)
+        {
+          log_error ("%s:%s: line from server too long", SRCNAME, __func__);
+          errno = ERANGE;
+          return -1;
+        }
+    }
+  while (size);
+  
+  return orig_size;
+}
+
+
+
+/* Start an asynchronous command.  Caller gives up owenership of
+   CLD.  */
+static gpg_error_t
+start_command (assuan_context_t ctx, closure_data_t cld,
+               ULONG cmdid, const char *line)
+{
+  gpg_error_t err;
+  assuan_fd_t fds[5];
+  int nfds;
+
+  /* Get the fd used by assuan for status channel reads.  This is the
+     first fd returned by assuan_get_active_fds for read fds.  */
+  nfds = assuan_get_active_fds (ctx, 0, fds, DIM (fds));
+  if (nfds < 1)
+    return gpg_error (GPG_ERR_GENERAL);	/* Ooops.  */
+
+  cld->cmdid = cmdid;
+  cld->status_cbs.write = status_in_cb;
+  cld->assctx = ctx;
+  /* Fixme: We might want to have reference counting for CLD to cope
+     with thye problem that the gpgme data object uses CLD which might
+     get invalidated at any time.  */
+  err = gpgme_data_new_from_cbs (&cld->status_data, &cld->status_cbs, cld);
+  if (err)
+    {
+      xfree (cld);
+      return err;
+    }
+
+  enqueue_callback ("status", ctx, cld->status_data, fds[0], 0,
+                    noclose_finalize_handler, cmdid, cld, 0);
+  cld = NULL; /* Now belongs to the status work item.  */
+
+  /* Process the work queue.  */
+  if (!SetEvent (work_queue_event))
+    log_error_w32 (-1, "%s:%s: SetEvent failed", SRCNAME, __func__);
+  /* Send the command. */
+  return assuan_write_line (ctx, line);
+}
+
+
+
+
+/* Note that this closure is called in the context of the
    waiter_thread.  */
 static void
-encrypt_closure (closure_data_t cld, gpg_error_t err)
+encrypt_closure (closure_data_t cld)
 {
-  engine_private_finished (cld->filter, err);
+  engine_private_finished (cld->filter, cld->final_err);
 }
 
 
 /* Encrypt the data from INDATA to the OUTDATA object for all
-   recpients given in the NULL terminated array KEYS.  If SIGN_KEY is
-   not NULL the message will also be signed.  On termination of the
-   encryption command engine_gpgme_finished() is called with
-   NOTIFY_DATA as the first argument.  
-
-   This global function is used to avoid allocating an extra context
-   just for this notification.  We abuse the gpgme_set_progress_cb
-   value for storing the pointer with the gpgme context.  */
+   recpients given in the NULL terminated array RECIPIENTS.  This
+   function terminates with success and then expects the caller to
+   wait for the result of the encryption using engine_wait.  FILTER is
+   used for asynchronous commnication with the engine module.  HWND is
+   the window handle of the current window and used to maintain the
+   correct relationship between a popups and the active window.  If
+   this function returns success, the data objects may only be
+   destroyed after an engine_wait or engine_cancel.  */
 int
 op_assuan_encrypt (protocol_t protocol, 
                    gpgme_data_t indata, gpgme_data_t outdata,
-                   void *notify_data, /* FIXME: Add hwnd */
+                   engine_filter_t filter, void *hwnd,
                    char **recipients)
 {
   gpg_error_t err;
   closure_data_t cld;
+  assuan_context_t ctx;
+  char line[1024];
+  HANDLE inpipe[2], outpipe[2];
+  ULONG cmdid;
+  pid_t pid;
+  int i;
+  char *p;
 
+  err = connect_uiserver (&ctx, &pid, &cmdid, hwnd);
+  if (err)
+    return err;
+
+  if ((err = create_io_pipe (inpipe, pid, 1)))
+    return err;
+  if ((err = create_io_pipe (outpipe, pid, 0)))
+    {
+      CloseHandle (inpipe[0]);
+      CloseHandle (outpipe[0]);
+      return err;
+    }
+
   cld = xcalloc (1, sizeof *cld);
   cld->closure = encrypt_closure;
-  cld->filter = notify_data;
+  cld->filter = filter;
 
+  err = assuan_transact (ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
+  if (err)
+    goto leave;
+  for (i=0; recipients && recipients[i]; i++)
+    {
+      snprintf (line, sizeof line, "RECIPIENT %s", recipients[i]);
+      for (p=line; *p; p++)
+        if (*p == '\n' || *p =='\r' )
+          *p = ' ';
+      err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+      if (err)
+        goto leave;
+    }
 
-  /* FIXME:  We should not hardcode always trust. */
-/*   if (sign_key) */
-/*     { */
-/*       gpgme_set_passphrase_cb (ctx, passphrase_callback_box, &cld->pw_cb); */
-/*       cld->pw_cb.ctx = ctx; */
-/*       cld->pw_cb.ttl = ttl; */
-/*       err = gpgme_signers_add (ctx, sign_key); */
-/*       if (!err) */
-/*         err = gpgme_op_encrypt_sign_start (ctx, keys, */
-/*                                            GPGME_ENCRYPT_ALWAYS_TRUST, */
-/*                                            indata, outdata); */
-/*     } */
-/*   else */
-/*     err = gpgme_op_encrypt_start (ctx, keys, GPGME_ENCRYPT_ALWAYS_TRUST,  */
-/*                                   indata, outdata); */
+  /* Note: We don't use real descriptor passing but a hack: We
+     duplicate the handle into the server process and the server then
+     uses this handle.  Eventually we should put this code into
+     assuan_sendfd.  */
+  snprintf (line, sizeof line, "INPUT FD=%ld", (unsigned long int)inpipe[0]);
+  err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+  if (err)
+    goto leave;
+  snprintf (line, sizeof line, "OUTPUT FD=%ld", (unsigned long int)outpipe[1]);
+  err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+  if (err)
+    goto leave;
 
-  err = -1;
+  enqueue_callback (" input", ctx, indata, inpipe[1], 1, finalize_handler,
+                    cmdid, NULL, 0); 
+  enqueue_callback ("output", ctx, outdata, outpipe[0], 0, finalize_handler, 
+                    cmdid, NULL, 1 /* Wait on success */); 
+  err = start_command (ctx, cld, cmdid, 
+                       (protocol == PROTOCOL_OPENPGP
+                        ? "ENCRYPT --protocol=OpenPGP"
+                        : protocol == PROTOCOL_SMIME
+                        ? "ENCRYPT --protocol=CMS"
+                        : "ENCRYPT --protocol=unknown-protocol"));
+  cld = NULL; /* Now owned by start_command.  */
+  if (err)
+    goto leave;
 
+
  leave:
   if (err)
     {
+      /* Fixme: Cancel stuff in the work_queue. */
+      if (inpipe[0] != INVALID_HANDLE_VALUE)
+        CloseHandle (inpipe[0]);
+      if (inpipe[1] != INVALID_HANDLE_VALUE)
+        CloseHandle (inpipe[1]);
+      if (outpipe[0] != INVALID_HANDLE_VALUE)
+        CloseHandle (outpipe[0]);
+      if (outpipe[1] != INVALID_HANDLE_VALUE)
+        CloseHandle (outpipe[1]);
       xfree (cld);
+      assuan_disconnect (ctx);
     }
+  else
+    engine_private_set_cancel (filter, ctx);
   return err;
 }
 
 
+
+int 
+op_assuan_sign (protocol_t protocol, 
+                gpgme_data_t indata, gpgme_data_t outdata,
+                engine_filter_t filter, void *hwnd)
+{
+  return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+}
 
+
+
+int 
+op_assuan_decrypt (protocol_t protocol,
+                   gpgme_data_t indata, gpgme_data_t outdata, 
+                   engine_filter_t filter, void *hwnd,
+                   int with_verify)
+{
+  return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+}
+
+
+
+int 
+op_assuan_verify (gpgme_protocol_t protocol, 
+                   gpgme_data_t data, const char *signature,
+                   engine_filter_t filter, void *hwnd)
+{
+  return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+}
+
+

Modified: trunk/src/engine-assuan.h
===================================================================
--- trunk/src/engine-assuan.h	2007-10-08 10:19:07 UTC (rev 187)
+++ trunk/src/engine-assuan.h	2007-10-10 13:23:41 UTC (rev 188)
@@ -31,20 +31,30 @@
 
 #include <gpgme.h>  /* We need it for gpgme_data_t.  */
 
+#include "engine.h"
 
-
 int  op_assuan_init (void);
 void op_assuan_deinit (void);
+void engine_assuan_cancel (void *cancel_data);
 
 int op_assuan_encrypt (protocol_t protocol, 
                        gpgme_data_t indata, gpgme_data_t outdata,
-                       void *notify_data, /* FIXME: Add hwnd */
+                       engine_filter_t notify_data, void *hwnd,
                        char **recipients);
+int op_assuan_sign (protocol_t protocol, 
+                    gpgme_data_t indata, gpgme_data_t outdata,
+                    engine_filter_t filter, void *hwnd);
+int op_assuan_decrypt (protocol_t protocol,
+                       gpgme_data_t indata, gpgme_data_t outdata, 
+                       engine_filter_t filter, void *hwnd,
+                       int with_verify);
+int op_assuan_verify (gpgme_protocol_t protocol, 
+                      gpgme_data_t data, const char *signature,
+                      engine_filter_t filter, void *hwnd);
 
 
 
 
-
 #ifdef __cplusplus
 }
 #endif

Modified: trunk/src/engine-gpgme.c
===================================================================
--- trunk/src/engine-gpgme.c	2007-10-08 10:19:07 UTC (rev 187)
+++ trunk/src/engine-gpgme.c	2007-10-10 13:23:41 UTC (rev 188)
@@ -410,14 +410,12 @@
 
 
 /* Encrypt the data from INDATA to the OUTDATA object for all
-   recpients given in the NULL terminated array KEYS.  If SIGN_KEY is
-   not NULL the message will also be signed.  On termination of the
-   encryption command engine_private_finished() is called with
-   FILTER as the first argument.  
-
-   This global function is used to avoid allocating an extra context
-   just for this notification.  We abuse the gpgme_set_progress_cb
-   value for storing the pointer with the gpgme context.  */
+   recpients given in the NULL terminated array RECIPIENTS.  This
+   function terminates with success and then expects the caller to
+   wait for the result of the encryption using engine_wait.  FILTER is
+   used for asynchronous commnication with the engine module.  HWND is
+   the window handle of the current window and used to maintain the
+   correct relationship between a popups and the active window.  */
 int
 op_gpgme_encrypt (protocol_t protocol, 
                   gpgme_data_t indata, gpgme_data_t outdata,

Modified: trunk/src/engine.c
===================================================================
--- trunk/src/engine.c	2007-10-08 10:19:07 UTC (rev 187)
+++ trunk/src/engine.c	2007-10-10 13:23:41 UTC (rev 188)
@@ -43,9 +43,12 @@
                                        SRCNAME, __func__, __LINE__); \
                         } while (0)
 
-static int debug_filter = 0;
+static int debug_filter = 1;
 
+/* This variable indicates whether the assuan engine is used.  */
+static int use_assuan;
 
+
 /* Definition of the key object.  */
 struct engine_keyinfo_s
 {
@@ -60,9 +63,12 @@
    accessed by one thread. */
 struct engine_filter_s
 {
+  int use_assuan;          /* The same as the global USE_ASSUAN.  */
+
   struct {
     CRITICAL_SECTION lock; /* The lock for the this object. */
     HANDLE condvar;        /* Manual reset event signaled if LENGTH > 0.  */
+    int nonblock;          /* Put gpgme data cb in non blocking mode.  */
     size_t length;         /* Number of bytes in BUFFER waiting to be
                               send down the pipe.  */
     char buffer[FILTER_BUFFER_SIZE];
@@ -77,6 +83,7 @@
   struct {
     CRITICAL_SECTION lock; /* The lock for the this object. */
     HANDLE condvar;        /* Manual reset event signaled if LENGTH == 0.  */
+    int nonblock;          /* Put gpgme data cb in non blocking mode.  */
     size_t length;         /* Number of bytes in BUFFER waiting to be
                               send back to the caller.  */
     char buffer[FILTER_BUFFER_SIZE];
@@ -155,6 +162,14 @@
   if (!filter->in.ready_event)
     log_error_w32 (-1, "%s:%s: CreateEvent failed", SRCNAME, __func__);
 
+  /* If we are using the assuan engine we need to make the gpgme read
+     callback non blocking.  */
+  if (use_assuan)
+    {
+      filter->use_assuan = 1;
+      filter->in.nonblock = 1;
+    }
+
   return filter;
 }
 
@@ -208,6 +223,13 @@
           return 0; /* Return EOF. */
         }
       release_in_lock (filter, __func__);
+      if (filter->in.nonblock)
+        {
+          errno = EAGAIN;
+          if (debug_filter)
+            log_debug ("%s:%s: leave; result=EAGAIN\n", SRCNAME, __func__);
+          return -1;
+        }
       if (debug_filter)
         log_debug ("%s:%s: waiting for in.condvar\n", SRCNAME, __func__);
       WaitForSingleObject (filter->in.condvar, 500);
@@ -257,6 +279,13 @@
   while (filter->out.length)
     {
       release_out_lock (filter, __func__);
+      if (filter->out.nonblock)
+        {
+          errno = EAGAIN;
+          if (debug_filter)
+            log_debug ("%s:%s: leave; result=EAGAIN\n", SRCNAME, __func__);
+          return -1;
+        }
       if (debug_filter)
         log_debug ("%s:%s: waiting for out.condvar\n", SRCNAME, __func__);
       WaitForSingleObject (filter->out.condvar, 500);
@@ -274,7 +303,7 @@
   release_out_lock (filter, __func__);
 
   if (debug_filter)
-    log_debug ("%s:%s: write; result=%d\n", SRCNAME, __func__, (int)nbytes);
+    log_debug ("%s:%s: leave; result=%d\n", SRCNAME, __func__, (int)nbytes);
   return nbytes;
 }
 
@@ -331,14 +360,17 @@
   err = op_assuan_init ();
   if (err)
     {
-/*       MessageBox (NULL, */
-/*                   _("The user interface server is not available or does " */
-/*                     "not work.  Using an internal user interface.\n\n" */
-/*                     "This is limited to the OpenPGP protocol and " */
-/*                     "thus S/MIME protected message are not readable."), */
-/*                   _("GpgOL"), MB_ICONWARNING|MB_OK); */
+      use_assuan = 0;
+      MessageBox (NULL,
+                  _("The user interface server is not available or does "
+                    "not work.  Using an internal user interface.\n\n"
+                    "This is limited to the OpenPGP protocol and "
+                    "thus S/MIME protected message are not readable."),
+                  _("GpgOL"), MB_ICONWARNING|MB_OK);
       err = op_gpgme_init ();
     }
+  else
+    use_assuan = 1;
 
   return err;
 }
@@ -605,7 +637,10 @@
     {
       log_debug ("%s:%s: filter %p: sending cancel command to backend",
                  SRCNAME, __func__, filter);
-      engine_gpgme_cancel (cancel_data);
+      if (filter->use_assuan)
+        engine_assuan_cancel (cancel_data);
+      else
+        engine_gpgme_cancel (cancel_data);
       if (WaitForSingleObject (filter->in.ready_event, INFINITE)
           != WAIT_OBJECT_0)
         log_error_w32 (-1, "%s:%s: WFSO failed", SRCNAME, __func__);
@@ -632,8 +667,12 @@
 {
   gpg_error_t err;
 
-  err = op_gpgme_encrypt (protocol, filter->indata, filter->outdata,
-                          filter, NULL, recipients);
+  if (filter->use_assuan)
+    err = op_assuan_encrypt (protocol, filter->indata, filter->outdata,
+                            filter, NULL, recipients);
+  else
+    err = op_gpgme_encrypt (protocol, filter->indata, filter->outdata,
+                            filter, NULL, recipients);
   return err;
 }
 
@@ -649,8 +688,12 @@
 {
   gpg_error_t err;
 
-  err = op_gpgme_sign (protocol, filter->indata, filter->outdata,
-                       filter, NULL);
+  if (filter->use_assuan)
+    err = op_assuan_sign (protocol, filter->indata, filter->outdata,
+                         filter, NULL);
+  else
+    err = op_gpgme_sign (protocol, filter->indata, filter->outdata,
+                         filter, NULL);
   return err;
 }
 
@@ -667,8 +710,12 @@
 {
   gpg_error_t err;
 
-  err = op_gpgme_decrypt (protocol, filter->indata, filter->outdata,
-                          filter, NULL, with_verify);
+  if (filter->use_assuan)
+    err = op_assuan_decrypt (protocol, filter->indata, filter->outdata,
+                            filter, NULL, with_verify);
+  else
+    err = op_gpgme_decrypt (protocol, filter->indata, filter->outdata,
+                            filter, NULL, with_verify);
   return err;
 }
 
@@ -693,7 +740,10 @@
       return gpg_error (GPG_ERR_NOT_SUPPORTED);
     }
 
-  err = op_gpgme_verify (protocol, filter->indata, signature, filter, NULL);
+  if (filter->use_assuan)
+    err = op_assuan_verify (protocol, filter->indata, signature, filter, NULL);
+  else
+    err = op_gpgme_verify (protocol, filter->indata, signature, filter, NULL);
   return err;
 }
 

Modified: trunk/src/ext-commands.cpp
===================================================================
--- trunk/src/ext-commands.cpp	2007-10-08 10:19:07 UTC (rev 187)
+++ trunk/src/ext-commands.cpp	2007-10-10 13:23:41 UTC (rev 188)
@@ -430,6 +430,9 @@
       pDisp = find_outlook_property (pEECB, "Close", &dispid);
       if (pDisp)
         {
+          /* Note that there is a report on the Net from 2005 by Amit
+             Joshi where he claims that in Outlook XP olDiscard does
+             not work but is treated like olSave.  */ 
           dispparams.rgvarg = &aVariant;
           dispparams.rgvarg[0].vt = VT_INT;
           dispparams.rgvarg[0].intVal = 1; /* olDiscard */

Modified: trunk/src/main.c
===================================================================
--- trunk/src/main.c	2007-10-08 10:19:07 UTC (rev 187)
+++ trunk/src/main.c	2007-10-10 13:23:41 UTC (rev 188)
@@ -31,6 +31,7 @@
 #include "msgcache.h"
 #include "mymapi.h"
 
+
 /* Registry key for this software. */
 #define REGKEY "Software\\GNU\\GnuPG"
 
@@ -197,15 +198,6 @@
 }
 
 
-/* Create a new boundary for use with MIME. */
-void
-create_boundary (char *buffer, size_t buflen)
-{
-
-
-}
-
-
 /* Acquire the mutex for logging.  Returns 0 on success. */
 static int 
 lock_log (void)
@@ -255,7 +247,11 @@
                      MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), 
                      tmpbuf, sizeof (tmpbuf)-1, NULL);
       fputs (": ", logfp);
-      fputs (tmpbuf, logfp);
+      if (*tmpbuf && tmpbuf[strlen (tmpbuf)-1] == '\n')
+        tmpbuf[strlen (tmpbuf)-1] = 0;
+      if (*tmpbuf && tmpbuf[strlen (tmpbuf)-1] == '\r')
+        tmpbuf[strlen (tmpbuf)-1] = 0;
+      fprintf (logfp, "%s (%d)", tmpbuf, w32err);
     }
   if (buf)
     {

Modified: trunk/src/util.h
===================================================================
--- trunk/src/util.h	2007-10-08 10:19:07 UTC (rev 187)
+++ trunk/src/util.h	2007-10-10 13:23:41 UTC (rev 188)
@@ -94,6 +94,9 @@
 void read_options (void);
 int write_options (void);
 
+/*-- Convenience macros. -- */
+#define DIM(v)		     (sizeof(v)/sizeof((v)[0]))
+#define DIMof(type,member)   DIM(((type *)0)->member)
 
 /*-- Macros to replace ctype ones to avoid locale problems. --*/
 #define spacep(p)   (*(p) == ' ' || *(p) == '\t')




More information about the Gnupg-commits mailing list