[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