First PGP/MIME encrypted message sent.
authorWerner Koch <wk@gnupg.org>
Wed, 12 Sep 2007 16:40:23 +0000 (16:40 +0000)
committerWerner Koch <wk@gnupg.org>
Wed, 12 Sep 2007 16:40:23 +0000 (16:40 +0000)
Changed encryption to make use of callbacks.

17 files changed:
src/ChangeLog
src/Makefile.am
src/engine-assuan.c [new file with mode: 0644]
src/engine-assuan.h [new file with mode: 0644]
src/engine-gpgme.c
src/engine.c [new file with mode: 0644]
src/engine.h
src/ext-commands.cpp
src/message-events.cpp
src/message.cpp
src/message.h
src/mimemaker.c
src/mimemaker.h
src/ol-ext-callback.cpp
src/olflange.cpp
src/rfc822parse.c
src/versioninfo.rc.in

index 4b91341..a3a5a9b 100644 (file)
@@ -1,3 +1,14 @@
+2007-09-11  Werner Koch  <wk@g10code.com>
+
+       * engine-gpgme.c (op_encrypt_data): New.
+
+2007-09-08  Werner Koch  <wk@g10code.com>
+
+       * engine.c: New.
+       * engine.h: Rewrite.  Factor existing stuff out to ..
+       * engine-gpgme.h: .. new.
+       * engine-assuan.h, engine-assuan.c: New.
+
 2007-09-07  Werner Koch  <wk@g10code.com>
 
        * common.c (qp_decode): Handle softe line breaks. 
index c30e57f..25fcd34 100644 (file)
@@ -34,7 +34,9 @@ gpgol_SOURCES = \
        mimeparser.c mimeparser.h   \
        mimemaker.c mimemaker.h     \
        msgcache.c msgcache.h       \
-        engine-gpgme.c engine.h    \
+       engine.c engine.h           \
+        engine-assuan.c engine-assuan.h \
+        engine-gpgme.c engine-gpgme.h   \
        rfc822parse.c rfc822parse.h \
         common.h common.c util.h    \
        xmalloc.h                   \
diff --git a/src/engine-assuan.c b/src/engine-assuan.c
new file mode 100644 (file)
index 0000000..6228124
--- /dev/null
@@ -0,0 +1,1448 @@
+/* engine-assuan.c - Crypto engine suing an Assuan server
+ *     Copyright (C) 2005, 2006, 1007 g10 Code GmbH
+ *
+ * This file is part of GpgOL.
+ *
+ * GpgOL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 
+ * of the License, or (at your option) any later version.
+ *  
+ * GpgOL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GpgOL; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/* Please note that we assume UTF-8 strings everywhere except when
+   noted. */
+   
+#if 0
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+
+#define COBJMACROS
+#include <windows.h>
+#include <objidl.h> /* For IStream. */
+
+#include "common.h"
+#include "passcache.h"
+#include "engine.h"
+
+#define TRACEPOINT() do { log_debug ("%s:%s:%d: tracepoint\n", \
+                                       SRCNAME, __func__, __LINE__); \
+                        } while (0)
+
+
+static char *debug_file = NULL;
+static int init_done = 0;
+
+
+static void add_verify_attestation (gpgme_data_t at, 
+                                    gpgme_ctx_t ctx, 
+                                    gpgme_verify_result_t res,
+                                    const char *filename);
+
+
+
+static void
+cleanup (void)
+{
+  if (debug_file)
+    {
+      xfree (debug_file);
+      debug_file = NULL;
+    }
+}
+
+
+/* Enable or disable the debug mode. */
+void
+op_set_debug_mode (int val, const char *file)
+{
+  cleanup ();
+  /* FIXME: Enable assuan debugging. */
+}
+
+
+/* Cleanup static resources. */
+void
+op_deinit (void)
+{
+  cleanup ();
+}
+
+
+/* Initialize this operation subsystem. */
+int
+op_init (void)
+{
+  if (init_done == 1)
+    return 0;
+
+  /*FIXME*/
+
+
+  init_done = 1;
+  return 0;
+}
+
+
+/* The read callback used by Assuan to read data from an IStream object. */
+static ssize_t
+stream_read_cb (void *handle, void *buffer, size_t size)
+{
+  LPSTREAM stream = handle;
+  HRESULT hr;
+  ULONG nread;
+
+  /* For EOF detection we assume that Read returns no error and thus
+     nread will be 0.  The specs say that "Depending on the
+     implementation, either S_FALSE or an error code could be returned
+     when reading past the end of the stream"; thus we are not really
+     sure whether our assumption is correct.  OTOH, at another place
+     the docuemntation says that the implementation used by
+     ISequentialStream exhibits the same EOF behaviour has found on
+     the MSDOS FAT file system.  So we seem to have good karma. */
+  hr = IStream_Read (stream, buffer, size, &nread);
+  if (hr != S_OK)
+    {
+      log_debug ("%s:%s: Read failed: hr=%#lx", SRCNAME, __func__, hr);
+      errno = EIO;
+      return -1;
+    }
+  return nread;
+}
+
+/* The write callback used by Assuan to write data to an IStream object. */
+static ssize_t
+stream_write_cb (void *handle, const void *buffer, size_t size)
+{
+  LPSTREAM stream = handle;
+  HRESULT hr;
+  ULONG nwritten;
+
+  hr = IStream_Write (stream, buffer, size, &nwritten);
+  if (hr != S_OK)
+    {
+      log_debug ("%s:%s: Write failed: hr=%#lx", SRCNAME, __func__, hr);
+      errno = EIO;
+      return -1;
+    }
+  return nwritten;
+}
+
+
+
+\f
+/* Try to figure out why the encryption failed and provide a more
+   suitable error code than the one returned by the encryption
+   routine. */
+static gpgme_error_t
+check_encrypt_result (gpgme_ctx_t ctx, gpgme_error_t err)
+{
+  gpgme_encrypt_result_t res;
+
+  res = 0 /*gpgme_op_encrypt_result (ctx)*/;
+  if (!res)
+    return err;
+  if (res->invalid_recipients != NULL)
+    return gpg_error (GPG_ERR_UNUSABLE_PUBKEY);
+  /* XXX: we need to do more here! */
+  return err;
+}
+
+
+/* Encrypt the data in INBUF into a newly malloced buffer stored on
+   success at OUTBUF.  The caller should release this buffer using
+   gpgme_free.  The recipients are expected in the NULL terminated
+   array KEYS. If SIGN_KEY is not NULl, the data will also be signed
+   using this key.  TTL is the time the passphrase should be
+   cached. */
+int
+op_encrypt (const char *inbuf, char **outbuf, gpgme_key_t *keys,
+            gpgme_key_t sign_key, int ttl)
+{
+  struct passphrase_cb_s cb;
+  gpgme_data_t in = NULL;
+  gpgme_data_t out = NULL;
+  gpgme_error_t err;
+  gpgme_ctx_t ctx = NULL;
+    
+  memset (&cb, 0, sizeof cb);
+  cb.ttl = ttl;
+  cb.decrypt_cmd = 0;
+
+  *outbuf = NULL;
+
+  op_init ();
+  err = gpgme_new (&ctx);
+  if (err)
+    goto leave;
+
+  err = gpgme_data_new_from_mem (&in, inbuf, strlen (inbuf), 1);
+  if (err)
+    goto leave;
+
+  err = gpgme_data_new (&out);
+  if (err)
+    goto leave;
+
+  gpgme_set_textmode (ctx, 1);
+  gpgme_set_armor (ctx, 1);
+  if (sign_key)
+    {
+      gpgme_set_passphrase_cb (ctx, passphrase_callback_box, &cb);
+      cb.ctx = ctx;
+      err = gpgme_signers_add (ctx, sign_key);
+      if (!err)
+        err = gpgme_op_encrypt_sign (ctx, keys, GPGME_ENCRYPT_ALWAYS_TRUST,
+                                     in, out);
+      cb.ctx = NULL;
+      update_passphrase_cache (err, &cb);
+    }
+  else
+    err = gpgme_op_encrypt (ctx, keys, GPGME_ENCRYPT_ALWAYS_TRUST, in, out);
+  if (err)
+    err = check_encrypt_result (ctx, err);
+  else
+    {
+      /* Return the buffer but first make sure it is a string. */
+      if (gpgme_data_write (out, "", 1) == 1)
+        {
+          *outbuf = gpgme_data_release_and_get_mem (out, NULL);
+          out = NULL; 
+        }
+    }
+
+
+ leave:
+  if (ctx)
+    gpgme_release (ctx);
+  if (in)
+    gpgme_data_release (in);
+  if (out)
+    gpgme_data_release (out);
+  return err;
+}
+
+
+
+/* Encrypt the stream INSTREAM to the OUTSTREAM for all recpients
+   given in the NULL terminated array KEYS.  If SIGN_KEY is not NULL
+   the message will also be signed. */
+int
+op_encrypt_stream (LPSTREAM instream, LPSTREAM outstream, gpgme_key_t *keys,
+                   gpgme_key_t sign_key, int ttl)
+{
+  struct passphrase_cb_s cb;
+  struct gpgme_data_cbs cbs;
+  gpgme_data_t in = NULL;
+  gpgme_data_t out = NULL;
+  gpgme_ctx_t ctx = NULL;
+  gpgme_error_t err;
+
+  memset (&cbs, 0, sizeof cbs);
+  cbs.read = stream_read_cb;
+  cbs.write = stream_write_cb;
+
+  memset (&cb, 0, sizeof cb);
+  cb.ttl = ttl;
+  cb.decrypt_cmd = 0;
+
+  err = gpgme_data_new_from_cbs (&in, &cbs, instream);
+  if (err)
+    goto fail;
+
+  err = gpgme_data_new_from_cbs (&out, &cbs, outstream);
+  if (err)
+    goto fail;
+
+  err = gpgme_new (&ctx);
+  if (err)
+    goto fail;
+
+  gpgme_set_armor (ctx, 1);
+  /* FIXME:  We should not hardcode always trust. */
+  if (sign_key)
+    {
+      gpgme_set_passphrase_cb (ctx, passphrase_callback_box, &cb);
+      cb.ctx = ctx;
+      err = gpgme_signers_add (ctx, sign_key);
+      if (!err)
+        err = gpgme_op_encrypt_sign (ctx, keys, GPGME_ENCRYPT_ALWAYS_TRUST,
+                                     in, out);
+      cb.ctx = NULL;
+      update_passphrase_cache (err, &cb);
+    }
+  else
+    err = gpgme_op_encrypt (ctx, keys, GPGME_ENCRYPT_ALWAYS_TRUST, in, out);
+  if (err)
+    err = check_encrypt_result (ctx, err);
+
+ fail:
+  if (ctx)
+    gpgme_release (ctx);
+  if (in)
+    gpgme_data_release (in);
+  if (out)
+    gpgme_data_release (out);
+  return err;
+}
+
+\f
+/* Sign and encrypt the data in INBUF into a newly allocated buffer at
+   OUTBUF. Caller needs to free the returned buffer using gpgme_free. */
+int
+op_sign (const char *inbuf, char **outbuf, int mode,
+         gpgme_key_t sign_key, int ttl)
+{
+  struct passphrase_cb_s cb;
+  gpgme_error_t err;
+  gpgme_data_t in = NULL;
+  gpgme_data_t out = NULL;
+  gpgme_ctx_t ctx = NULL;
+
+  memset (&cb, 0, sizeof cb);
+  cb.ttl = ttl;
+  cb.decrypt_cmd = 0;
+
+  *outbuf = NULL;
+  op_init ();
+  
+  err = gpgme_new (&ctx);
+  if (err) 
+    goto leave;
+
+  err = gpgme_data_new_from_mem (&in, inbuf, strlen (inbuf), 1);
+  if (err)
+    goto leave;
+
+  err = gpgme_data_new (&out);
+  if (err)
+    goto leave;
+
+  if (sign_key)
+    gpgme_signers_add (ctx, sign_key);
+
+  if (mode == GPGME_SIG_MODE_CLEAR)
+    gpgme_set_textmode (ctx, 1);
+  gpgme_set_armor (ctx, 1);
+
+  gpgme_set_passphrase_cb (ctx, passphrase_callback_box, &cb);
+  cb.ctx = ctx;
+  err = gpgme_op_sign (ctx, in, out, mode);
+  cb.ctx = NULL;
+  update_passphrase_cache (err, &cb);
+
+  if (!err)
+    {
+      /* Return the buffer but first make sure it is a string. */
+      if (gpgme_data_write (out, "", 1) == 1)
+        {
+          *outbuf = gpgme_data_release_and_get_mem (out, NULL);
+          out = NULL; 
+        }
+    }
+
+ leave:
+  if (ctx)
+    gpgme_release (ctx);
+  if (in)
+    gpgme_data_release (in);
+  if (out)
+    gpgme_data_release (out);
+  return err;
+}
+
+
+/* Create a signature from INSTREAM and write it to OUTSTREAM.  Use
+   signature mode MODE and a passphrase caching time of TTL. */
+int
+op_sign_stream (LPSTREAM instream, LPSTREAM outstream, int mode,
+                gpgme_key_t sign_key, int ttl)
+{
+  struct gpgme_data_cbs cbs;
+  struct passphrase_cb_s cb;
+  gpgme_data_t in = NULL;
+  gpgme_data_t out = NULL;
+  gpgme_ctx_t ctx = NULL;
+  gpgme_error_t err;
+  
+  memset (&cbs, 0, sizeof cbs);
+  cbs.read = stream_read_cb;
+  cbs.write = stream_write_cb;
+
+  memset (&cb, 0, sizeof cb);
+  cb.ttl = ttl;
+  cb.decrypt_cmd = 0;
+
+  err = gpgme_data_new_from_cbs (&in, &cbs, instream);
+  if (err)
+    goto fail;
+
+  err = gpgme_data_new_from_cbs (&out, &cbs, outstream);
+  if (err)
+    goto fail;
+
+  err = gpgme_new (&ctx);
+  if (err)
+    goto fail;
+  
+  if (sign_key)
+    gpgme_signers_add (ctx, sign_key);
+
+  if (mode == GPGME_SIG_MODE_CLEAR)
+    gpgme_set_textmode (ctx, 1);
+  gpgme_set_armor (ctx, 1);
+
+  gpgme_set_passphrase_cb (ctx, passphrase_callback_box, &cb);
+  cb.ctx = ctx;
+  err = gpgme_op_sign (ctx, in, out, mode);
+  cb.ctx = NULL;
+  update_passphrase_cache (err, &cb);
+  
+ fail:
+  if (in)
+    gpgme_data_release (in);
+  if (out)
+    gpgme_data_release (out);
+  if (ctx)
+    gpgme_release (ctx);    
+  return err;
+}
+
+
+\f
+/* Run the decryption.  Decrypts INBUF to OUTBUF; caller needs to free
+   the returned result at OUTBUF using gpgme_free.  the result at
+   OUTBUF.  TTL is the time in seconds to cache a passphrase.  If
+   FILENAME is not NULL it will be displayed along with status
+   outputs. If ATTESTATION is not NULL a text with the result of the
+   signature verification will get printed to it. */
+int 
+op_decrypt (const char *inbuf, char **outbuf, int ttl, const char *filename,
+            gpgme_data_t attestation, int preview_mode)
+{
+  struct passphrase_cb_s cb;
+  gpgme_data_t in = NULL;
+  gpgme_data_t out = NULL;
+  gpgme_ctx_t ctx;
+  gpgme_error_t err;
+  
+  *outbuf = NULL;
+  op_init ();
+
+  memset (&cb, 0, sizeof cb);
+  cb.ttl = ttl;
+  cb.decrypt_cmd = 1;
+
+  err = gpgme_new (&ctx);
+  if (err)
+    return err;
+
+  err = gpgme_data_new_from_mem (&in, inbuf, strlen (inbuf), 1);
+  if (err)
+    goto leave;
+  err = gpgme_data_new (&out);
+  if (err)
+    goto leave;
+
+  gpgme_set_passphrase_cb (ctx, passphrase_callback_box, &cb);
+  cb.ctx = ctx;
+  if (preview_mode)
+    err = gpgme_op_decrypt (ctx, in, out);
+  else
+    err = gpgme_op_decrypt_verify (ctx, in, out);
+  cb.ctx = NULL;
+  update_passphrase_cache (err, &cb);
+
+  /* Act upon the result of the decryption operation. */
+  if (!err && preview_mode) 
+    ;
+  else if (!err) 
+    {
+      /* Decryption succeeded.  Store the result at OUTBUF. */
+      gpgme_verify_result_t res;
+
+      /* Return the buffer but first make sure it is a string. */
+      if (gpgme_data_write (out, "", 1) == 1)
+        {
+          *outbuf = gpgme_data_release_and_get_mem (out, NULL);
+          out = NULL; 
+        }
+
+      /* Now check the state of any signature. */
+      res = gpgme_op_verify_result (ctx);
+      if (res && res->signatures)
+        verify_dialog_box (gpgme_get_protocol (ctx), res, filename);
+      if (res && res->signatures && attestation)
+        add_verify_attestation (attestation, ctx, res, filename);
+    }
+  else if (gpgme_err_code (err) == GPG_ERR_DECRYPT_FAILED)
+    {
+      /* The decryption failed.  See whether we can determine the real
+         problem. */
+      gpgme_decrypt_result_t res;
+      res = gpgme_op_decrypt_result (ctx);
+      if (res != NULL && res->recipients != NULL &&
+          gpgme_err_code (res->recipients->status) == GPG_ERR_NO_SECKEY)
+        err = GPG_ERR_NO_SECKEY;
+      /* XXX: return the keyids */
+    }
+  else
+    {
+      /* Decryption failed for other reasons. */
+    }
+
+
+  /* If the callback indicated a cancel operation, set the error
+     accordingly. */
+  if (err && (cb.opts & OPT_FLAG_CANCEL))
+    err = gpg_error (GPG_ERR_CANCELED);
+  
+leave:    
+  if (ctx)
+    gpgme_release (ctx);
+  if (in)
+    gpgme_data_release (in);
+  if (out)
+    gpgme_data_release (out);
+  return err;
+}
+
+
+/* Decrypt the GPGME data object IN into the data object OUT.  Returns
+   0 on success or an gpgme error code on failure.  If FILENAME is not
+   NULL it will be displayed along with status outputs. If ATTESTATION
+   is not NULL a text with the result of the signature verification
+   will get printed to it. */
+static int
+decrypt_stream (gpgme_protocol_t protocol,
+                gpgme_data_t in, gpgme_data_t out, int ttl,
+                const char *filename, gpgme_data_t attestation, 
+                int preview_mode)
+{    
+  struct passphrase_cb_s cb;
+  gpgme_ctx_t ctx = NULL;
+  gpgme_error_t err;
+  
+  memset (&cb, 0, sizeof cb);
+  cb.ttl = ttl;
+  cb.decrypt_cmd = 1;
+
+  err = gpgme_new (&ctx);
+  if (err)
+    goto fail;
+
+  err = gpgme_set_protocol (ctx, protocol);
+  if (err)
+    goto fail;
+
+  /* GPGME does not support a command handler for gpgsm.  Thus we
+     can't set the passphrase callback.  */
+  if (protocol != GPGME_PROTOCOL_CMS)
+    gpgme_set_passphrase_cb (ctx, passphrase_callback_box, &cb);
+
+  cb.ctx = ctx;
+  if (preview_mode)
+    err = gpgme_op_decrypt (ctx, in, out);
+  else
+    err = gpgme_op_decrypt_verify (ctx, in, out);
+  cb.ctx = NULL;
+  update_passphrase_cache (err, &cb);
+  /* Act upon the result of the decryption operation. */
+  if (!err && preview_mode) 
+    ;
+  else if (!err) 
+    {
+      gpgme_verify_result_t res;
+
+      /* Decryption succeeded.  Now check the state of the signatures. */
+      res = gpgme_op_verify_result (ctx);
+      if (res && res->signatures)
+        verify_dialog_box (gpgme_get_protocol (ctx), res, filename);
+      if (res && res->signatures && attestation)
+        add_verify_attestation (attestation, ctx, res, filename);
+    }
+  else if (gpgme_err_code (err) == GPG_ERR_DECRYPT_FAILED)
+    {
+      /* The decryption failed.  See whether we can determine the real
+         problem. */
+      gpgme_decrypt_result_t res;
+      res = gpgme_op_decrypt_result (ctx);
+      if (res != NULL && res->recipients != NULL &&
+          gpgme_err_code (res->recipients->status) == GPG_ERR_NO_SECKEY)
+        err = GPG_ERR_NO_SECKEY;
+      /* XXX: return the keyids */
+    }
+  else
+    {
+      /* Decryption failed for other reasons. */
+    }
+
+
+  /* If the callback indicated a cancel operation, set the error
+     accordingly. */
+  if (err && (cb.opts & OPT_FLAG_CANCEL))
+    err = gpg_error (GPG_ERR_CANCELED);
+
+ fail:
+  if (ctx)
+    gpgme_release (ctx);
+  return err;
+}
+
+/* Decrypt the stream INSTREAM directly to the stream OUTSTREAM.
+   Returns 0 on success or an gpgme error code on failure.  If
+   FILENAME is not NULL it will be displayed along with status
+   outputs. */
+int
+op_decrypt_stream (LPSTREAM instream, LPSTREAM outstream, int ttl,
+                   const char *filename, gpgme_data_t attestation)
+{
+  struct gpgme_data_cbs cbs;
+  gpgme_data_t in = NULL;
+  gpgme_data_t out = NULL;    
+  gpgme_error_t err;
+  
+  memset (&cbs, 0, sizeof cbs);
+  cbs.read = stream_read_cb;
+  cbs.write = stream_write_cb;
+
+  err = gpgme_data_new_from_cbs (&in, &cbs, instream);
+  if (!err)
+    err = gpgme_data_new_from_cbs (&out, &cbs, outstream);
+  if (!err)
+    err = decrypt_stream (GPGME_PROTOCOL_OpenPGP,
+                          in, out, ttl, filename, attestation, 0);
+
+  if (in)
+    gpgme_data_release (in);
+  if (out)
+    gpgme_data_release (out);
+  return err;
+}
+
+
+/* Decrypt the stream INSTREAM directly to the newly allocated buffer
+   OUTBUF.  Caller needs to free the returned buffer using gpgme_free.
+   Returns 0 on success or an gpgme error code on failure.  If
+   FILENAME is not NULL it will be displayed along with status
+   outputs. */
+int
+op_decrypt_stream_to_buffer (LPSTREAM instream, char **outbuf, int ttl,
+                             const char *filename, gpgme_data_t attestation)
+{    
+  struct gpgme_data_cbs cbs;
+  gpgme_data_t in = NULL;
+  gpgme_data_t out = NULL;    
+  gpgme_error_t err;
+  
+  *outbuf = NULL;
+
+  memset (&cbs, 0, sizeof cbs);
+  cbs.read = stream_read_cb;
+
+  err = gpgme_data_new_from_cbs (&in, &cbs, instream);
+  if (!err)
+    err = gpgme_data_new (&out);
+  if (!err)
+    err = decrypt_stream (GPGME_PROTOCOL_OpenPGP,
+                          in, out, ttl, filename, attestation, 0);
+  if (!err)
+    {
+      /* Return the buffer but first make sure it is a string. */
+      if (gpgme_data_write (out, "", 1) == 1)
+        {
+          *outbuf = gpgme_data_release_and_get_mem (out, NULL);
+          out = NULL; 
+        }
+    }
+
+  if (in)
+    gpgme_data_release (in);
+  if (out)
+    gpgme_data_release (out);
+  return err;
+}
+
+
+/* Decrypt the stream INSTREAM directly to the GPGME data object OUT.
+   Returns 0 on success or an gpgme error code on failure.  If
+   FILENAME is not NULL it will be displayed along with status
+   outputs. */
+int
+op_decrypt_stream_to_gpgme (gpgme_protocol_t protocol,
+                            LPSTREAM instream, gpgme_data_t out, int ttl,
+                            const char *filename, gpgme_data_t attestation,
+                            int preview_mode)
+{
+  struct gpgme_data_cbs cbs;
+  gpgme_data_t in = NULL;
+  gpgme_error_t err;
+  
+  memset (&cbs, 0, sizeof cbs);
+  cbs.read = stream_read_cb;
+
+  err = gpgme_data_new_from_cbs (&in, &cbs, instream);
+  if (!err)
+    err = decrypt_stream (protocol, 
+                          in, out, ttl, filename, attestation, preview_mode);
+
+  if (in)
+    gpgme_data_release (in);
+  return err;
+}
+
+
+\f
+/* Verify a message in INBUF and return the new message (i.e. the one
+   with stripped off dash escaping) in a newly allocated buffer
+   OUTBUF. If OUTBUF is NULL only the verification result will be
+   displayed (this is suitable for PGP/MIME messages).  A dialog box
+   will show the result of the verification.  If FILENAME is not NULL
+   it will be displayed along with status outputs.  If ATTESTATION is
+   not NULL a text with the result of the signature verification will
+   get printed to it. Caller needs to free the returned buffer at
+   OUTBUF using gpgme_free. */
+int
+op_verify (const char *inbuf, char **outbuf, const char *filename,
+           gpgme_data_t attestation)
+{
+  gpgme_data_t in = NULL;
+  gpgme_data_t out = NULL;
+  gpgme_ctx_t ctx = NULL;
+  gpgme_error_t err;
+  gpgme_verify_result_t res = NULL;
+
+  if (outbuf)
+    *outbuf = NULL;
+
+  op_init ();
+
+  err = gpgme_new (&ctx);
+  if (err)
+    goto leave;
+
+  err = gpgme_data_new_from_mem (&in, inbuf, strlen (inbuf), 1);
+  if (err)
+    goto leave;
+
+  err = gpgme_data_new (&out);
+  if (err)
+    goto leave;
+
+  err = gpgme_op_verify (ctx, in, NULL, out);
+  if (!err)
+    {
+      if (outbuf) 
+        {
+          /* Return the buffer but first make sure it is a string. */
+          if (gpgme_data_write (out, "", 1) == 1)
+            {
+              *outbuf = gpgme_data_release_and_get_mem (out, NULL);
+              out = NULL; 
+            }
+       }
+      res = gpgme_op_verify_result (ctx);
+    }
+  if (res) 
+    verify_dialog_box (gpgme_get_protocol (ctx), res, filename);
+  if (res && attestation)
+    add_verify_attestation (attestation, ctx, res, filename);
+
+ leave:
+  if (out)
+    gpgme_data_release (out);
+  if (in)
+    gpgme_data_release (in);
+  if (ctx)
+    gpgme_release (ctx);
+  return err;
+}
+
+/* Verify a detached message where the data is to be read from the
+   DATA_STREAM and the signature itself is expected to be the string
+   SIG_STRING.  FILENAME will be shown by the verification status
+   dialog box.  If ATTESTATION is not NULL a text with the result of
+   the signature verification will get printed to it.  */ 
+int
+op_verify_detached_sig (LPSTREAM data_stream,
+                        const char *sig_string, const char *filename,
+                        gpgme_data_t attestation)
+{
+  struct gpgme_data_cbs cbs;
+  gpgme_data_t data = NULL;
+  gpgme_data_t sig = NULL;
+  gpgme_ctx_t ctx = NULL;
+  gpgme_error_t err;
+  gpgme_verify_result_t res = NULL;
+
+  memset (&cbs, 0, sizeof cbs);
+  cbs.read = stream_read_cb;
+  cbs.write = stream_write_cb;
+
+  op_init ();
+
+  err = gpgme_new (&ctx);
+  if (err)
+    goto leave;
+
+  err = gpgme_data_new_from_cbs (&data, &cbs, data_stream);
+  if (err)
+    goto leave;
+
+  err = gpgme_data_new_from_mem (&sig, sig_string, strlen (sig_string), 0);
+  if (err)
+    goto leave;
+
+  err = gpgme_op_verify (ctx, sig, data, NULL);
+  if (!err)
+    {
+      res = gpgme_op_verify_result (ctx);
+      if (res) 
+        verify_dialog_box (gpgme_get_protocol (ctx), res, filename);
+      if (res && attestation)
+        add_verify_attestation (attestation, ctx, res, filename);
+    }
+
+ leave:
+  if (data)
+    gpgme_data_release (data);
+  if (sig)
+    gpgme_data_release (sig);
+  if (ctx)
+    gpgme_release (ctx);
+  return err;
+}
+
+/* Verify a detached message where the data is in the string
+   DATA_STRING and the signature itself is expected to be the string
+   SIG_STRING.  FILENAME will be shown by the verification status
+   dialog box.  If ATTESTATION is not NULL a text with the result of
+   the signature verification will get printed to it.  */ 
+int
+op_verify_detached_sig_mem (const char *data_string,
+                            const char *sig_string, const char *filename,
+                            gpgme_data_t attestation)
+{
+  gpgme_data_t data = NULL;
+  gpgme_data_t sig = NULL;
+  gpgme_ctx_t ctx = NULL;
+  gpgme_error_t err;
+  gpgme_verify_result_t res = NULL;
+
+  op_init ();
+
+  err = gpgme_new (&ctx);
+  if (err)
+    goto leave;
+
+  err = gpgme_data_new_from_mem (&data, data_string, strlen (data_string), 0);
+  if (err)
+    goto leave;
+
+  err = gpgme_data_new_from_mem (&sig, sig_string, strlen (sig_string), 0);
+  if (err)
+    goto leave;
+
+  err = gpgme_op_verify (ctx, sig, data, NULL);
+  if (!err)
+    {
+      res = gpgme_op_verify_result (ctx);
+      if (res) 
+        verify_dialog_box (gpgme_get_protocol (ctx), res, filename);
+      if (res && attestation)
+        add_verify_attestation (attestation, ctx, res, filename);
+    }
+
+ leave:
+  if (data)
+    gpgme_data_release (data);
+  if (sig)
+    gpgme_data_release (sig);
+  if (ctx)
+    gpgme_release (ctx);
+  return err;
+}
+
+
+/* Verify a detached message where the data is in the gpgme object
+   DATA and the signature is the gpgme object SIG.  FILENAME will be
+   shown by the verification status dialog box.  If ATTESTATION is not
+   NULL a text with the result of the signature verification will get
+   printed to it.  */ 
+int
+op_verify_detached_sig_gpgme (gpgme_protocol_t protocol, 
+                              gpgme_data_t data, gpgme_data_t sig,
+                              const char *filename, gpgme_data_t attestation)
+{
+  gpgme_ctx_t ctx = NULL;
+  gpgme_error_t err;
+  gpgme_verify_result_t res = NULL;
+
+  op_init ();
+
+  err = gpgme_new (&ctx);
+  if (err)
+    goto leave;
+
+  err = gpgme_set_protocol (ctx, protocol);
+  if (err)
+    goto leave;
+
+  err = gpgme_op_verify (ctx, sig, data, NULL);
+  if (!err)
+    {
+      res = gpgme_op_verify_result (ctx);
+      if (res) 
+        verify_dialog_box (gpgme_get_protocol (ctx), res, filename);
+      if (res && attestation)
+        add_verify_attestation (attestation, ctx, res, filename);
+    }
+
+ leave:
+  if (ctx)
+    gpgme_release (ctx);
+  return err;
+}
+
+
+\f
+static void
+at_puts (gpgme_data_t a, const char *s)
+{
+  gpgme_data_write (a, s, strlen (s));
+}
+
+static void 
+at_print_time (gpgme_data_t a, time_t t)
+{
+  char buf[200];
+
+  strftime (buf, sizeof (buf)-1, "%c", localtime (&t));
+  at_puts (a, buf);
+}
+
+static void 
+at_fingerprint (gpgme_data_t a, gpgme_key_t key)
+{
+  const char *s;
+  int i, is_pgp;
+  char *buf, *p;
+  const char *prefix = _("Fingerprint: ");
+
+  if (!key)
+    return;
+  s = key->subkeys ? key->subkeys->fpr : NULL;
+  if (!s)
+    return;
+  is_pgp = (key->protocol == GPGME_PROTOCOL_OpenPGP);
+
+  buf = xmalloc ( strlen (prefix) + strlen(s) * 4 + 2 );
+  p = stpcpy (buf, prefix);
+  if (is_pgp && strlen (s) == 40)
+    { 
+      /* v4 style formatted. */
+      for (i=0; *s && s[1] && s[2] && s[3] && s[4]; s += 4, i++)
+        {
+          *p++ = s[0];
+          *p++ = s[1];
+          *p++ = s[2];
+          *p++ = s[3];
+          *p++ = ' ';
+          if (i == 4)
+            *p++ = ' ';
+        }
+    }
+  else
+    { 
+      /* v3 style or X.509 formatted. */
+      for (i=0; *s && s[1] && s[2]; s += 2, i++)
+        {
+          *p++ = s[0];
+          *p++ = s[1];
+          *p++ = is_pgp? ' ':':';
+          if (is_pgp && i == 7)
+            *p++ = ' ';
+        }
+    }
+
+  /* Just in case print remaining odd digits */
+  for (; *s; s++)
+    *p++ = *s;
+  *p++ = '\n';
+  *p = 0;
+  at_puts (a, buf);
+  xfree (buf);
+}
+
+
+/* Print common attributes of the signature summary SUM.  Returns
+   true if a severe warning has been encountered. */
+static int 
+at_sig_summary (gpgme_data_t a,  
+                unsigned long sum, gpgme_signature_t sig, gpgme_key_t key)
+{
+  int severe = 0;
+
+  if ((sum & GPGME_SIGSUM_VALID))
+    at_puts (a, _("This signature is valid\n"));
+  if ((sum & GPGME_SIGSUM_GREEN))
+    at_puts (a, _("signature state is \"green\"\n"));
+  if ((sum & GPGME_SIGSUM_RED))
+    at_puts (a, _("signature state is \"red\"\n"));
+
+  if ((sum & GPGME_SIGSUM_KEY_REVOKED))
+    {
+      at_puts (a, _("Warning: One of the keys has been revoked\n"));
+      severe = 1;
+    }
+  
+  if ((sum & GPGME_SIGSUM_KEY_EXPIRED))
+    {
+      time_t t = key->subkeys->expires ? key->subkeys->expires : 0;
+
+      if (t)
+        {
+          at_puts (a, _("Warning: The key used to create the "
+                        "signature expired at: "));
+          at_print_time (a, t);
+          at_puts (a, "\n");
+        }
+      else
+        at_puts (a, _("Warning: At least one certification key "
+                      "has expired\n"));
+    }
+
+  if ((sum & GPGME_SIGSUM_SIG_EXPIRED))
+    {
+      at_puts (a, _("Warning: The signature expired at: "));
+      at_print_time (a, sig ? sig->exp_timestamp : 0);
+      at_puts (a, "\n");
+    }
+
+  if ((sum & GPGME_SIGSUM_KEY_MISSING))
+    at_puts (a, _("Can't verify due to a missing key or certificate\n"));
+
+  if ((sum & GPGME_SIGSUM_CRL_MISSING))
+    {
+      at_puts (a, _("The CRL is not available\n"));
+      severe = 1;
+    }
+
+  if ((sum & GPGME_SIGSUM_CRL_TOO_OLD))
+    {
+      at_puts (a, _("Available CRL is too old\n"));
+      severe = 1;
+    }
+
+  if ((sum & GPGME_SIGSUM_BAD_POLICY))
+    at_puts (a, _("A policy requirement was not met\n"));
+
+  if ((sum & GPGME_SIGSUM_SYS_ERROR))
+    {
+      const char *t0 = NULL, *t1 = NULL;
+
+      at_puts (a, _("A system error occured"));
+
+      /* Try to figure out some more detailed system error information. */
+      if (sig)
+       {
+         t0 = "";
+         t1 = sig->wrong_key_usage ? "Wrong_Key_Usage" : "";
+       }
+
+      if (t0 || t1)
+        {
+          at_puts (a, ": ");
+          if (t0)
+            at_puts (a, t0);
+          if (t1 && !(t0 && !strcmp (t0, t1)))
+            {
+              if (t0)
+                at_puts (a, ",");
+              at_puts (a, t1);
+            }
+        }
+      at_puts (a, "\n");
+    }
+
+  return severe;
+}
+
+
+/* Print the validity of a key used for one signature. */
+static void 
+at_sig_validity (gpgme_data_t a, gpgme_signature_t sig)
+{
+  const char *txt = NULL;
+
+  switch (sig ? sig->validity : 0)
+    {
+    case GPGME_VALIDITY_UNKNOWN:
+      txt = _("WARNING: We have NO indication whether "
+              "the key belongs to the person named "
+              "as shown above\n");
+      break;
+    case GPGME_VALIDITY_UNDEFINED:
+      break;
+    case GPGME_VALIDITY_NEVER:
+      txt = _("WARNING: The key does NOT BELONG to "
+              "the person named as shown above\n");
+      break;
+    case GPGME_VALIDITY_MARGINAL:
+      txt = _("WARNING: It is NOT certain that the key "
+              "belongs to the person named as shown above\n");
+      break;
+    case GPGME_VALIDITY_FULL:
+    case GPGME_VALIDITY_ULTIMATE:
+      txt = NULL;
+      break;
+    }
+
+  if (txt)
+    at_puts (a, txt);
+}
+
+
+/* Print a text with the attestation of the signature verification
+   (which is in RES) to A.  FILENAME may also be used in the
+   attestation. */
+static void
+add_verify_attestation (gpgme_data_t a, gpgme_ctx_t ctx,
+                        gpgme_verify_result_t res, const char *filename)
+{
+  time_t created;
+  const char *fpr, *uid;
+  gpgme_key_t key = NULL;
+  int i, anybad = 0, anywarn = 0;
+  unsigned int sum;
+  gpgme_user_id_t uids = NULL;
+  gpgme_signature_t sig;
+  gpgme_error_t err;
+
+  if (!gpgme_data_seek (a, 0, SEEK_CUR))
+    {
+      /* Nothing yet written to the stream.  Insert the current time. */
+      at_puts (a, _("Verification started at: "));
+      at_print_time (a, time (NULL));
+      at_puts (a, "\n\n");
+    }
+
+  at_puts (a, _("Verification result for: "));
+  at_puts (a, filename ? filename : _("[unnamed part]"));
+  at_puts (a, "\n");
+  if (res)
+    {
+      for (sig = res->signatures; sig; sig = sig->next)
+        {
+          created = sig->timestamp;
+          fpr = sig->fpr;
+          sum = sig->summary;
+
+          if (gpg_err_code (sig->status) != GPG_ERR_NO_ERROR)
+            anybad = 1;
+
+          err = gpgme_get_key (ctx, fpr, &key, 0);
+          uid = !err && key->uids && key->uids->uid ? key->uids->uid : "[?]";
+
+          if ((sum & GPGME_SIGSUM_GREEN))
+            {
+              at_puts (a, _("Good signature from: "));
+              at_puts (a, uid);
+              at_puts (a, "\n");
+              for (i = 1, uids = key->uids; uids; i++, uids = uids->next)
+                {
+                  if (uids->revoked)
+                    continue;
+                  at_puts (a, _("                aka: "));
+                  at_puts (a, uids->uid);
+                  at_puts (a, "\n");
+                }
+              at_puts (a, _("            created: "));
+              at_print_time (a, created);
+              at_puts (a, "\n");
+              if (at_sig_summary (a, sum, sig, key))
+                anywarn = 1;
+              at_sig_validity (a, sig);
+            }
+          else if ((sum & GPGME_SIGSUM_RED))
+            {
+              at_puts (a, _("*BAD* signature claimed to be from: "));
+              at_puts (a, uid);
+              at_puts (a, "\n");
+              at_sig_summary (a, sum, sig, key);
+            }
+          else if (!anybad && key && (key->protocol == GPGME_PROTOCOL_OpenPGP))
+            { /* We can't decide (yellow) but this is a PGP key with a
+                 good signature, so we display what a PGP user
+                 expects: The name, fingerprint and the key validity
+                 (which is neither fully or ultimate). */
+              at_puts (a, _("Good signature from: "));
+              at_puts (a, uid);
+              at_puts (a, "\n");
+              at_puts (a, _("            created: "));
+              at_print_time (a, created);
+              at_puts (a, "\n");
+              at_sig_validity (a, sig);
+              at_fingerprint (a, key);
+              if (at_sig_summary (a, sum, sig, key))
+                anywarn = 1;
+            }
+          else /* can't decide (yellow) */
+            {
+              at_puts (a, _("Error checking signature"));
+              at_puts (a, "\n");
+              at_sig_summary (a, sum, sig, key);
+            }
+          
+          gpgme_key_release (key);
+        }
+
+      if (!anybad )
+        {
+          gpgme_sig_notation_t notation;
+          
+          for (sig = res->signatures; sig; sig = sig->next)
+            {
+              if (!sig->notations)
+                continue;
+              at_puts (a, _("*** Begin Notation (signature by: "));
+              at_puts (a, sig->fpr);
+              at_puts (a, ") ***\n");
+              for (notation = sig->notations; notation;
+                   notation = notation->next)
+                {
+                  if (notation->name)
+                    {
+                      at_puts (a, notation->name);
+                      at_puts (a, "=");
+                    }
+                  if (notation->value)
+                    {
+                      at_puts (a, notation->value);
+                      if (!(*notation->value
+                            && (notation->value[strlen (notation->value)-1]
+                                =='\n')))
+                        at_puts (a, "\n");
+                    }
+                }
+              at_puts (a, _("*** End Notation ***\n"));
+            }
+       }
+    }
+  at_puts (a, "\n");
+}
+
+
+
+\f
+/* Try to find a key for each item in array NAMES. Items not found are
+   stored as malloced strings in the newly allocated array UNKNOWN.
+   Found keys are stored in the newly allocated array KEYS.  Both
+   arrays are terminated by a NULL entry.  Caller needs to release
+   KEYS and UNKNOWN.
+
+   Returns: 0 on success. However success may also be that one or all
+   keys are unknown.
+*/
+int 
+op_lookup_keys (char **names, gpgme_key_t **keys, char ***unknown)
+{
+  gpgme_error_t err;
+  gpgme_ctx_t ctx;
+  size_t n;
+  int i, kpos, upos;
+  gpgme_key_t k, k2;
+
+  *keys = NULL;
+  *unknown = NULL;
+
+  err = gpgme_new (&ctx);
+  if (err)
+    return -1; /* Error. */
+
+  for (n=0; names[n]; n++)
+    ;
+
+  *keys =  xcalloc (n+1, sizeof *keys);
+  *unknown = xcalloc (n+1, sizeof *unknown);
+
+  for (i=kpos=upos=0; names[i]; i++)
+    {
+      k = NULL;
+      err = gpgme_op_keylist_start (ctx, names[i], 0);
+      if (!err)
+        {
+          err = gpgme_op_keylist_next (ctx, &k);
+          if (!err && !gpgme_op_keylist_next (ctx, &k2))
+            {
+              /* More than one matching key available.  Take this one
+                 as unknown. */
+              gpgme_key_release (k);
+              gpgme_key_release (k2);
+              k = k2 = NULL;
+            }
+        }
+      gpgme_op_keylist_end (ctx);
+
+      
+      /* only useable keys will be added otherwise they will be stored
+         in unknown (marked with their status). */
+      if (k && !k->revoked && !k->disabled && !k->expired)
+        (*keys)[kpos++] = k;
+      else if (k)
+       {
+         char *p, *fmt = "%s (%s)";
+         char *warn = k->revoked? "revoked" : k->expired? "expired" : "disabled";
+         
+         p = xcalloc (1, strlen (names[i]) + strlen (warn) + strlen (fmt) +1);
+         sprintf (p, fmt, names[i], warn);
+         (*unknown)[upos++] = p;
+         gpgme_key_release (k);
+       }
+      else if (!k)
+        (*unknown)[upos++] = xstrdup (names[i]);
+    }
+
+  gpgme_release (ctx);
+  return 0;
+}
+
+
+/* Return a GPGME key object matching PATTERN.  If no key matches or
+   the match is ambiguous, return NULL. */
+gpgme_key_t 
+op_get_one_key (char *pattern)
+{
+  gpgme_error_t err;
+  gpgme_ctx_t ctx;
+  gpgme_key_t k, k2;
+
+  err = gpgme_new (&ctx);
+  if (err)
+    return NULL; /* Error. */
+  err = gpgme_op_keylist_start (ctx, pattern, 0);
+  if (!err)
+    {
+      err = gpgme_op_keylist_next (ctx, &k);
+      if (!err && !gpgme_op_keylist_next (ctx, &k2))
+        {
+          /* More than one matching key available.  Return an error
+             instead. */
+          gpgme_key_release (k);
+          gpgme_key_release (k2);
+          k = k2 = NULL;
+        }
+    }
+  gpgme_op_keylist_end (ctx);
+  gpgme_release (ctx);
+  return k;
+}
+
+
+/* Copy the data from the GPGME object DAT to a newly created file
+   with name OUTFILE.  Returns 0 on success. */
+static gpgme_error_t
+data_to_file (gpgme_data_t *dat, const char *outfile)
+{
+  FILE *out;
+  char *buf;
+  size_t n=0;
+
+  out = fopen (outfile, "wb");
+  if (!out)
+    return GPG_ERR_UNKNOWN_ERRNO; /* FIXME: We need to check why we
+                                     can't use errno here. */
+  /* FIXME: Why at all are we using an in memory object wqhen we are
+     later going to write to a file anyway. */
+  buf = gpgme_data_release_and_get_mem (*dat, &n);
+  *dat = NULL;
+  if (!n)
+    {
+      fclose (out);
+      return GPG_ERR_EOF; /* FIXME:  wrap this into a gpgme_error() */
+    }
+  fwrite (buf, 1, n, out);
+  fclose (out);
+  /* FIXME: We have no error checking above. */
+  gpgme_free (buf);
+  return 0;
+}
+
+
+int
+op_export_keys (const char *pattern[], const char *outfile)
+{      
+    /* @untested@ */
+    gpgme_ctx_t ctx=NULL;
+    gpgme_data_t  out=NULL;    
+    gpgme_error_t err;
+
+    err = gpgme_new (&ctx);
+    if (err)
+       return err;
+    err = gpgme_data_new (&out);
+    if (err) {
+       gpgme_release (ctx);
+       return err;
+    }
+
+    gpgme_set_armor (ctx, 1);
+    err = gpgme_op_export_ext (ctx, pattern, 0, out);
+    if (!err)
+       data_to_file (&out, outfile);
+
+    gpgme_data_release (out);  
+    gpgme_release (ctx);  
+    return err;
+}
+
+
+const char *
+userid_from_key (gpgme_key_t k)
+{
+  if (k && k->uids && k->uids->uid)
+    return k->uids->uid;
+  else
+    return "?";
+}
+
+const char *
+keyid_from_key (gpgme_key_t k)
+{
+  
+  if (k && k->subkeys && k->subkeys->keyid)
+    return k->subkeys->keyid;
+  else
+    return "????????";
+}
+
+
+const char*
+op_strerror (int err)
+{
+  return gpgme_strerror (err);
+}
+
+
+const char*
+op_strsource (int err)
+{
+  return gpgme_strsource (err);
+}
+
+
+#endif
diff --git a/src/engine-assuan.h b/src/engine-assuan.h
new file mode 100644 (file)
index 0000000..7c5831a
--- /dev/null
@@ -0,0 +1,41 @@
+/* engine-assuan.h - Assuan server based crypto engine
+ *     Copyright (C) 2007 g10 Code GmbH
+ *
+ * This file is part of GpgOL.
+ *
+ * GpgOL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 
+ * of the License, or (at your option) any later version.
+ *  
+ * GpgOL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GpgOL; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef GPGOL_ENGINE_ASSUAN_H
+#define GPGOL_ENGINE_ASSUAN_H
+
+#ifdef __cplusplus
+extern "C" {
+#if 0
+}
+#endif
+#endif
+
+
+
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /*GPGOL_ENGINE_ASSUAN_H*/
index e4462fb..a01f313 100644 (file)
@@ -1,21 +1,22 @@
 /* engine-gpgme.c - Crypto engine with GPGME
- *     Copyright (C) 2005, 2006 g10 Code GmbH
+ *     Copyright (C) 2005, 2006, 2007 g10 Code GmbH
  *
- * This file is part of GPGol.
+ * This file is part of GpgOL.
  *
- * GPGol is free software; you can redistribute it and/or
+ * GpgOL is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License
  * as published by the Free Software Foundation; either version 2.1 
  * of the License, or (at your option) any later version.
  *  
- * GPGol is distributed in the hope that it will be useful,
+ * GpgOL is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Lesser General Public License
- * along with GPGol; if not, write to the Free Software Foundation, 
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GpgOL; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
  */
 
 /* Please note that we assume UTF-8 strings everywhere except when
@@ -37,6 +38,7 @@
 #include "common.h"
 #include "passcache.h"
 #include "engine.h"
+#include "engine-gpgme.h"
 
 #define TRACEPOINT() do { log_debug ("%s:%s:%d: tracepoint\n", \
                                        SRCNAME, __func__, __LINE__); \
@@ -47,6 +49,7 @@ static char *debug_file = NULL;
 static int init_done = 0;
 
 
+static DWORD WINAPI waiter_thread (void *dummy);
 static void add_verify_attestation (gpgme_data_t at, 
                                     gpgme_ctx_t ctx, 
                                     gpgme_verify_result_t res,
@@ -122,12 +125,57 @@ op_init (void)
                  gpgme_strerror (err));
       return err;
     }
-  
+
+  {
+    HANDLE th;
+    DWORD tid;
+
+    th = CreateThread (NULL, 64*1024, waiter_thread, NULL, 0, &tid);
+    if (th == INVALID_HANDLE_VALUE)
+      log_error ("failed to start the gpgme waiter thread\n");
+    else
+      CloseHandle (th);
+  }
+
   init_done = 1;
   return 0;
 }
 
 
+static DWORD WINAPI
+waiter_thread (void *dummy)
+{
+  gpgme_ctx_t ctx;
+  gpg_error_t err;
+  void *filter;
+
+  (void)dummy;
+
+  TRACEPOINT ();
+  for (;;)
+    {
+      /*  Note: We don't use hang becuase this will end up in a tight
+          loop and does not do a voluntary context switch.  Thus we do
+          this by ourself.  Actually it would be better to start
+          gpgme-Wait only if we really have something to do but that
+          is more complicated.  */
+      ctx = gpgme_wait (NULL, &err, 0);
+      if (ctx)
+        {
+          gpgme_get_progress_cb (ctx, NULL, &filter);
+          engine_gpgme_finished (filter, err);
+          gpgme_release (ctx);
+        }
+      else if (err)
+        log_debug ("%s:%s: gpgme_wait failed: %s\n", SRCNAME, __func__, 
+                   gpg_strerror (err));
+      else
+        Sleep (50);
+    }
+}
+
+
+
 /* The read callback used by GPGME to read data from an IStream object. */
 static ssize_t
 stream_read_cb (void *handle, void *buffer, size_t size)
@@ -154,6 +202,7 @@ stream_read_cb (void *handle, void *buffer, size_t size)
   return nread;
 }
 
+
 /* The write callback used by GPGME to write data to an IStream object. */
 static ssize_t
 stream_write_cb (void *handle, const void *buffer, size_t size)
@@ -360,6 +409,196 @@ op_encrypt_stream (LPSTREAM instream, LPSTREAM outstream, gpgme_key_t *keys,
   return err;
 }
 
+
+/* Release an array of GPGME keys. */
+static void 
+release_key_array (gpgme_key_t *keys)
+{
+  int i;
+
+  if (keys)
+    {
+      for (i = 0; keys[i]; i++) 
+       gpgme_key_release (keys[i]);
+      xfree (keys);
+    }
+}
+
+/* Return the number of strings in the array STRINGS. */
+static size_t
+count_strings (char **strings)
+{
+  size_t i;
+
+  for (i=0; strings[i]; i++)
+    ;
+  return i;
+}
+
+/* Return the number of keys in the gpgme_key_t array KEYS.  */
+static size_t
+count_keys (gpgme_key_t *keys)
+{
+  size_t i;
+  
+  for (i=0; keys[i]; i++)
+    ;
+  return i;
+}
+
+
+/* Return an array of gpgme key objects derived from thye list of
+   strings in RECPIENTS. */
+static gpg_error_t
+prepare_recipient_keys (gpgme_key_t **r_keys, char **recipients, HWND hwnd)
+{
+  gpg_error_t err;
+  gpgme_key_t *keys = NULL;
+  char **unknown = NULL;
+  size_t n_keys, n_unknown, n_recp;
+  int i;
+
+  *r_keys = NULL;
+  if (op_lookup_keys (recipients, &keys, &unknown))
+    {
+      log_debug ("%s:%s: leave (lookup keys failed)\n", SRCNAME, __func__);
+      return gpg_error (GPG_ERR_GENERAL);  
+    }
+
+  n_recp = count_strings (recipients);
+  n_keys = count_keys (keys);
+  n_unknown = count_strings (unknown);
+
+  log_debug ("%s:%s: found %d recipients, need %d, unknown=%d\n",
+             SRCNAME, __func__, (int)n_keys, (int)n_recp, (int)n_unknown);
+  
+  if (n_keys != n_recp)
+    {
+      unsigned int opts;
+      gpgme_key_t *keys2;
+
+      log_debug ("%s:%s: calling recipient_dialog_box2", SRCNAME, __func__);
+      opts = recipient_dialog_box2 (keys, unknown, &keys2);
+      release_key_array (keys);
+      keys = keys2;
+      if ( (opts & OPT_FLAG_CANCEL) ) 
+        {
+          err = gpg_error (GPG_ERR_CANCELED);
+          goto leave;
+       }
+    }
+
+
+  /* If a default key has been set, add it to the list of keys.  Check
+     that the key is actually available. */
+  if (opt.enable_default_key && opt.default_key && *opt.default_key)
+    {
+      gpgme_key_t defkey;
+      
+      defkey = op_get_one_key (opt.default_key);
+      if (!defkey)
+        {
+          MessageBox (hwnd,
+                      _("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!"),
+                      _("Encryption"), MB_ICONWARNING|MB_OK);
+        }
+      else
+        {
+          gpgme_key_t *tmpkeys;
+
+          n_keys = count_keys (keys) + 1;
+          tmpkeys = xcalloc (n_keys+1, sizeof *tmpkeys);
+          for (i = 0; keys[i]; i++) 
+            {
+              tmpkeys[i] = keys[i];
+              gpgme_key_ref (tmpkeys[i]);
+            }
+          tmpkeys[i++] = defkey;
+          tmpkeys[i] = NULL;
+          release_key_array (keys);
+          keys = tmpkeys;
+        }
+    }
+  
+  if (keys)
+    {
+      for (i=0; keys[i]; i++)
+        log_debug ("%s:%s: recp.%d 0x%s %s\n", SRCNAME, __func__,
+                   i, keyid_from_key (keys[i]), userid_from_key (keys[i]));
+    }
+  *r_keys = keys;
+  keys = NULL;
+  err = 0;
+
+ leave:
+  release_key_array (keys);
+  return 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.  */
+int
+op_encrypt_data (gpgme_data_t indata, gpgme_data_t outdata,
+                 void *notify_data, /* FIXME: Add hwnd */
+                 char **recipients, gpgme_key_t sign_key, int ttl)
+{
+  gpg_error_t err;
+  struct passphrase_cb_s cb;
+  gpgme_ctx_t ctx = NULL;
+  gpgme_key_t *keys = NULL;
+
+  memset (&cb, 0, sizeof cb);
+  cb.ttl = ttl;
+  cb.decrypt_cmd = 0;
+
+  err = prepare_recipient_keys (&keys, recipients, NULL);
+  if (err)
+    goto leave;
+
+  err = gpgme_new (&ctx);
+  if (err)
+    goto leave;
+  gpgme_set_progress_cb (ctx, NULL, notify_data);
+
+  gpgme_set_armor (ctx, 1);
+  /* FIXME:  We should not hardcode always trust. */
+  if (sign_key)
+    {
+      gpgme_set_passphrase_cb (ctx, passphrase_callback_box, &cb);
+      cb.ctx = ctx;
+      err = gpgme_signers_add (ctx, sign_key);
+      if (!err)
+        err = gpgme_op_encrypt_sign_start (ctx, keys,
+                                           GPGME_ENCRYPT_ALWAYS_TRUST,
+                                           indata, outdata);
+      cb.ctx = NULL;
+      update_passphrase_cache (err, &cb);
+    }
+  else
+    err = gpgme_op_encrypt_start (ctx, keys, GPGME_ENCRYPT_ALWAYS_TRUST, 
+                                  indata, outdata);
+/*   if (err) */
+/*     err = check_encrypt_result (ctx, err); */
+
+ leave:
+  if (ctx && err)
+    gpgme_release (ctx);
+  release_key_array (keys);
+  return err;
+}
+
+
 \f
 /* Sign and encrypt the data in INBUF into a newly allocated buffer at
    OUTBUF. Caller needs to free the returned buffer using gpgme_free. */
diff --git a/src/engine.c b/src/engine.c
new file mode 100644 (file)
index 0000000..f195a6f
--- /dev/null
@@ -0,0 +1,566 @@
+/* engine.c - Crypto engine dispatcher
+ *     Copyright (C) 2007 g10 Code GmbH
+ *
+ * This file is part of GpgOL.
+ *
+ * GpgOL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 
+ * of the License, or (at your option) any later version.
+ *  
+ * GpgOL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GpgOL; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+#include <assert.h>
+/*#define WIN32_LEAN_AND_MEAN  uncomment it after remove LPSTREAM*/
+#include <windows.h>
+#include <objidl.h> /* For LPSTREAM in engine-gpgme.h   FIXME: Remove it.  */
+
+#include "common.h"
+#include "engine.h"
+#include "engine-gpgme.h"
+#include "engine-assuan.h"
+
+#define FILTER_BUFFER_SIZE 128  /* FIXME: Increase it after testing  */
+
+
+#define TRACEPOINT() do { log_debug ("%s:%s:%d: tracepoint\n", \
+                                       SRCNAME, __func__, __LINE__); \
+                        } while (0)
+
+
+/* Definition of the key object.  */
+struct engine_keyinfo_s
+{
+  struct {
+    gpgme_key_t key;
+  } gpgme;
+
+};
+
+
+/* Definition of the filter object.  This object shall only be
+   accessed by one thread. */
+struct engine_filter_s
+{
+  struct {
+    CRITICAL_SECTION lock; /* The lock for the this object. */
+    HANDLE condvar;        /* Manual reset event signaled if LENGTH > 0.  */
+    size_t length;         /* Number of bytes in BUFFER waiting to be
+                              send down the pipe.  */
+    char buffer[FILTER_BUFFER_SIZE];
+    int got_eof;         /* End of file has been indicated.  */
+    /* These objects are only in this structure because we
+       use this structure's lock to protect them.  */
+    int ready;           /* Set to true if the gpgme process has finished.  */
+    HANDLE ready_event;  /* And the corresponding event.  */
+    gpg_error_t status;  /* Status of the gpgme process.  */
+  } in;
+
+  struct {
+    CRITICAL_SECTION lock; /* The lock for the this object. */
+    HANDLE condvar;        /* Manual reset event signaled if LENGTH == 0.  */
+    size_t length;         /* Number of bytes in BUFFER waiting to be
+                              send back to the caller.  */
+    char buffer[FILTER_BUFFER_SIZE];
+  } out;
+
+  /* The data sink as set by engine_create_filter.  */
+  int (*outfnc) (void *, const void *, size_t);
+  void *outfncdata;
+
+  /* Objects to be released by engine_wait/cancel.  */
+  struct gpgme_data_cbs cb_inbound;  /* Callback structure for gpgme.  */
+  struct gpgme_data_cbs cb_outbound; /* Ditto.  */
+  gpgme_data_t indata;               /* Input data.  */
+  gpgme_data_t outdata;              /* Output data.  */
+};
+
+
+
+\f
+/* Create a new filter object. */
+static engine_filter_t 
+create_filter (void)
+{
+  engine_filter_t filter;
+
+  filter = xcalloc (1, sizeof *filter);
+
+  InitializeCriticalSection (&filter->in.lock);
+  filter->in.condvar = CreateEvent (NULL, TRUE, 0, NULL);
+  if (!filter->in.condvar)
+    log_error_w32 (-1, "%s:%s: create in.condvar failed", SRCNAME, __func__);
+
+  InitializeCriticalSection (&filter->out.lock);
+  filter->out.condvar = CreateEvent (NULL, TRUE, 0, NULL);
+  if (!filter->out.condvar)
+    log_error_w32 (-1, "%s:%s: create out.condvar failed", SRCNAME, __func__);
+
+  /* Create an automatic event (it only used one time so the type is
+     actually not important).  */
+  filter->in.ready_event = CreateEvent (NULL, 0, 0, NULL);
+  if (!filter->in.ready_event)
+    log_error_w32 (-1, "%s:%s: CreateEvent failed", SRCNAME, __func__);
+  TRACEPOINT ();
+
+  return filter;
+}
+
+
+static void
+release_filter (engine_filter_t filter)
+{
+  if (filter)
+    {
+      TRACEPOINT ();
+      if (filter->in.condvar)
+        CloseHandle (filter->in.condvar);
+      if (filter->out.condvar)
+        CloseHandle (filter->out.condvar);
+      if (filter->in.ready_event)
+        CloseHandle (filter->in.ready_event);
+      gpgme_data_release (filter->indata);
+      gpgme_data_release (filter->outdata);
+      xfree (filter);
+    }
+}
+
+
+
+
+/* This read callback is used by GPGME to read data from a filter
+   object.  The function should return the number of bytes read, 0 on
+   EOF, and -1 on error.  If an error occurs, ERRNO should be set to
+   describe the type of the error.  */
+static ssize_t
+filter_gpgme_read_cb (void *handle, void *buffer, size_t size)
+{
+  engine_filter_t filter = handle;
+  int nbytes;
+
+  if (!filter || !buffer || !size)
+    {
+      errno = EINVAL;
+      return (ssize_t)(-1);
+    }
+
+  log_debug ("%s:%s: enter\n",  SRCNAME, __func__);
+  EnterCriticalSection (&filter->in.lock);
+  while (!filter->in.length)
+    {
+      if (filter->in.got_eof || filter->in.ready)
+        {
+          LeaveCriticalSection (&filter->in.lock);
+          log_debug ("%s:%s: returning EOF\n", SRCNAME, __func__);
+          return 0; /* Return EOF. */
+        }
+      LeaveCriticalSection (&filter->in.lock);
+      log_debug ("%s:%s: waiting for in.condvar\n", SRCNAME, __func__);
+      WaitForSingleObject (filter->in.condvar, 500);
+      EnterCriticalSection (&filter->in.lock);
+      log_debug ("%s:%s: continuing\n", SRCNAME, __func__);
+    }
+     
+  log_debug ("%s:%s: requested read size=%d (filter.in.length=%d)\n",
+             SRCNAME, __func__, (int)size, (int)filter->in.length);
+  nbytes = size < filter->in.length ? size : filter->in.length;
+  memcpy (buffer, filter->in.buffer, nbytes);
+  if (filter->in.length > nbytes)
+    memmove (filter->in.buffer, filter->in.buffer + nbytes, 
+             filter->in.length - nbytes);
+  filter->in.length -= nbytes;
+  LeaveCriticalSection (&filter->in.lock);
+
+  log_debug ("%s:%s: leave; result=%d\n",
+             SRCNAME, __func__, (int)nbytes);
+
+  return nbytes;
+}
+
+
+/* This write callback is used by GPGME to write data to the filter
+   object.  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
+filter_gpgme_write_cb (void *handle, const void *buffer, size_t size)
+{
+  engine_filter_t filter = handle;
+  int nbytes;
+
+  if (!filter || !buffer || !size)
+    {
+      errno = EINVAL;
+      return (ssize_t)(-1);
+    }
+
+  log_debug ("%s:%s: enter\n",  SRCNAME, __func__);
+  EnterCriticalSection (&filter->out.lock);
+  while (filter->out.length)
+    {
+      LeaveCriticalSection (&filter->out.lock);
+      log_debug ("%s:%s: waiting for out.condvar\n", SRCNAME, __func__);
+      WaitForSingleObject (filter->out.condvar, 500);
+      EnterCriticalSection (&filter->out.lock);
+      log_debug ("%s:%s: continuing\n", SRCNAME, __func__);
+    }
+
+  log_debug ("%s:%s: requested write size=%d\n",
+             SRCNAME, __func__, (int)size);
+  nbytes = size < FILTER_BUFFER_SIZE ? size : FILTER_BUFFER_SIZE;
+  memcpy (filter->out.buffer, buffer, nbytes);
+  filter->out.length = nbytes;
+  LeaveCriticalSection (&filter->out.lock);
+
+  log_debug ("%s:%s: write; result=%d\n", SRCNAME, __func__, (int)nbytes);
+  return nbytes;
+}
+
+/* This function is called by the gpgme backend to notify a filter
+   object about the final status of an operation.  It may not be
+   called by the engine-gpgme.c module. */
+void
+engine_gpgme_finished (engine_filter_t filter, gpg_error_t status)
+{
+  if (!filter)
+    {
+      log_debug ("%s:%s: called without argument\n", SRCNAME, __func__);
+      return;
+    }
+  log_debug ("%s:%s: filter %p: process terminated: %s <%s>\n", 
+             SRCNAME, __func__, filter, 
+             gpg_strerror (status), gpg_strsource (status));
+
+  EnterCriticalSection (&filter->in.lock);
+  if (filter->in.ready)
+    log_debug ("%s:%s: filter %p: Oops: already flagged as finished\n", 
+               SRCNAME, __func__, filter);
+  filter->in.ready = 1;
+  filter->in.status = status;
+  if (!SetEvent (filter->in.ready_event))
+    log_error_w32 (-1, "%s:%s: SetEvent failed", SRCNAME, __func__);
+  LeaveCriticalSection (&filter->in.lock);
+  log_debug ("%s:%s: leaving\n", SRCNAME, __func__);
+}
+
+
+
+
+\f
+/* Initialize the engine dispatcher.  */
+int
+engine_init (void)
+{
+  op_init ();
+  return 0;
+}
+
+
+/* Shutdown the engine dispatcher.  */
+void
+engine_deinit (void)
+{
+  op_deinit ();
+
+}
+
+
+\f
+/* Filter the INDATA of length INDATA and write the output using
+   OUTFNC.  OUTFNCDATA is passed as first argument to OUTFNC, followed
+   by the data to be written and its length.  FILTER is an object
+   returned for example by engine_encrypt_start.  The function returns
+   0 on success or an error code on error.
+
+   Passing INDATA as NULL and INDATALEN as 0, the filter will be
+   flushed, that is all remaining stuff will be written to OUTFNC.
+   This indicates EOF and the filter won't accept anymore input.  */
+int
+engine_filter (engine_filter_t filter, const void *indata, size_t indatalen)
+{
+  gpg_error_t err;
+  size_t nbytes;
+
+  log_debug ("%s:%s: enter; filter=%p\n", SRCNAME, __func__, filter); 
+  /* Our implementation is for now straightforward without any
+     additional buffer filling etc.  */
+  if (!filter || !filter->outfnc)
+    return gpg_error (GPG_ERR_INV_VALUE);
+  if (filter->in.length > FILTER_BUFFER_SIZE
+      || filter->out.length > FILTER_BUFFER_SIZE)
+    return gpg_error (GPG_ERR_BUG);
+  if (filter->in.got_eof)
+    return gpg_error (GPG_ERR_CONFLICT); /* EOF has already been indicated.  */
+
+  log_debug ("%s:%s: indata=%p indatalen=%d outfnc=%p\n",
+             SRCNAME, __func__, indata, (int)indatalen, filter->outfnc); 
+  for (;;)
+    {
+      /* If there is something to write out, do this now to make space
+         for more data.  */
+      EnterCriticalSection (&filter->out.lock);
+      while (filter->out.length)
+        {
+          TRACEPOINT ();
+          nbytes = filter->outfnc (filter->outfncdata, 
+                                   filter->out.buffer, filter->out.length);
+          if (nbytes == -1)
+            {
+              log_debug ("%s:%s: error writing data\n", SRCNAME, __func__);
+              LeaveCriticalSection (&filter->out.lock);
+              return gpg_error (GPG_ERR_EIO);
+            }
+          assert (nbytes < filter->out.length && nbytes >= 0);
+          if (nbytes < filter->out.length)
+            memmove (filter->out.buffer, filter->out.buffer + nbytes,
+                     filter->out.length - nbytes); 
+          filter->out.length =- nbytes;
+        }
+      if (!PulseEvent (filter->out.condvar))
+        log_error_w32 (-1, "%s:%s: PulseEvent(out) failed", SRCNAME, __func__);
+      LeaveCriticalSection (&filter->out.lock);
+      
+      EnterCriticalSection (&filter->in.lock);
+      if (!indata && !indatalen)
+        {
+          TRACEPOINT ();
+          filter->in.got_eof = 1;
+          /* Flush requested.  Tell the output function to also flush.  */
+          nbytes = filter->outfnc (filter->outfncdata, NULL, 0);
+          if (nbytes == -1)
+            {
+              log_debug ("%s:%s: error flushing data\n", SRCNAME, __func__);
+              err = gpg_error (GPG_ERR_EIO);
+            }
+          else
+            err = 0;
+          LeaveCriticalSection (&filter->in.lock);
+          return err; 
+        }
+
+      /* Fill the input buffer, relinquish control to the callback
+         processor and loop until all input data has been
+         processed.  */
+      if (!filter->in.length && indatalen)
+        {
+          TRACEPOINT ();
+          filter->in.length = (indatalen > FILTER_BUFFER_SIZE
+                               ? FILTER_BUFFER_SIZE : indatalen);
+          memcpy (filter->in.buffer, indata, filter->in.length);
+          indata    += filter->in.length;
+          indatalen -= filter->in.length;
+        }
+      if (!filter->in.length || (filter->in.ready && !filter->out.length))
+        {
+          LeaveCriticalSection (&filter->in.lock);
+          err = 0;
+          break;  /* the loop.  */
+        }
+      if (!PulseEvent (filter->in.condvar))
+        log_error_w32 (-1, "%s:%s: PulseEvent(in) failed", SRCNAME, __func__);
+      LeaveCriticalSection (&filter->in.lock);
+      Sleep (0);
+    }
+
+  log_debug ("%s:%s: leave; err=%d\n", SRCNAME, __func__, err); 
+  return err;
+}
+
+
+/* Create a new filter object which uses OUTFNC as its data sink.  If
+   OUTFNC is called with NULL/0 for the data to be written, the
+   function should do a flush.  OUTFNC is expected to return the
+   number of bytes actually written or -1 on error.  It may return 0
+   to indicate that no data has been written and the caller shall try
+   again.  OUTFNC and OUTFNCDATA are internally used by the engine
+   even after the call to this function.  There lifetime only ends
+   after an engine_wait or engine_cancel. */
+int
+engine_create_filter (engine_filter_t *r_filter,
+                      int (*outfnc) (void *, const void *, size_t),
+                      void *outfncdata)
+{
+  gpg_error_t err;
+  engine_filter_t filter;
+
+  filter = create_filter ();
+  filter->cb_inbound.read = filter_gpgme_read_cb;
+  filter->cb_outbound.write = filter_gpgme_write_cb;
+  filter->outfnc = outfnc;
+  filter->outfncdata = outfncdata;
+
+  err = gpgme_data_new_from_cbs (&filter->indata, 
+                                 &filter->cb_inbound, filter);
+  if (err)
+    goto failure;
+
+  err = gpgme_data_new_from_cbs (&filter->outdata,
+                                 &filter->cb_outbound, filter);
+  if (err)
+    goto failure;
+
+  *r_filter = filter;
+  return 0;
+
+ failure:
+  release_filter (filter);
+  return err;
+}
+
+
+
+/* Wait for FILTER to finish.  Returns 0 on success.  FILTER is not
+   valid after the function has returned success.  */
+int
+engine_wait (engine_filter_t filter)
+{
+  gpg_error_t err;
+  int more;
+
+  TRACEPOINT ();
+  if (!filter || !filter->outfnc)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  /* If we are here, engine_filter is not anymore called but there is
+     likely stuff in the output buffer which needs to be written
+     out.  */
+  /* Argh, Busy waiting.  As soon as we change fromCritical Sections
+     to a kernel based objects we should use WaitOnMultipleObjects to
+     wait for the out.lock as well as for the ready_event.  */
+  do 
+    {
+      EnterCriticalSection (&filter->out.lock);
+      while (filter->out.length)
+        {
+          int nbytes; 
+          TRACEPOINT ();
+          nbytes = filter->outfnc (filter->outfncdata, 
+                                   filter->out.buffer, filter->out.length);
+          if (nbytes == -1)
+            {
+              log_debug ("%s:%s: error writing data\n", SRCNAME, __func__);
+              LeaveCriticalSection (&filter->out.lock);
+              break;
+            }
+          assert (nbytes < filter->out.length && nbytes >= 0);
+          if (nbytes < filter->out.length)
+            memmove (filter->out.buffer, filter->out.buffer + nbytes,
+                     filter->out.length - nbytes); 
+          filter->out.length =- nbytes;
+        }
+      if (!PulseEvent (filter->out.condvar))
+        log_error_w32 (-1, "%s:%s: PulseEvent(out) failed", SRCNAME, __func__);
+      LeaveCriticalSection (&filter->out.lock);
+      EnterCriticalSection (&filter->in.lock);
+      more = !filter->in.ready;
+      LeaveCriticalSection (&filter->in.lock);
+      if (more)
+        Sleep (0);
+    }
+  while (more);
+  
+  if (WaitForSingleObject (filter->in.ready_event, INFINITE) != WAIT_OBJECT_0)
+    {
+      log_error_w32 (-1, "%s:%s: WFSO failed", SRCNAME, __func__);
+      return gpg_error (GPG_ERR_GENERAL);
+    }
+  err = filter->in.status;
+  log_debug ("%s:%s: filter %p ready: %s", SRCNAME, __func__, 
+             filter, gpg_strerror (err));
+
+  if (!err)
+    release_filter (filter);
+  return err;
+}
+
+
+/* Cancel FILTER. */
+void
+engine_cancel (engine_filter_t filter)
+{
+  if (!filter)
+    return;
+  
+  EnterCriticalSection (&filter->in.lock);
+  filter->in.ready = 1;
+  LeaveCriticalSection (&filter->in.lock);
+  log_debug ("%s:%s: filter %p canceled", SRCNAME, __func__, filter);
+  /* FIXME:  Here we need to kill the underlying gpgme process. */
+  release_filter (filter);
+}
+
+
+
+/* Start an encryption operation to all RECIPEINTS using PROTOCOL
+   RECIPIENTS is a NULL terminated array of rfc2822 addresses.  FILTER
+   is an object create by engine_create_filter.  The caller needs to
+   call engine_wait to finish the operation.  A filter object may not
+   be reused after having been used through this function.  However,
+   the lifetime of the filter object lasts until the final engine_wait
+   or engine_cabcel.  */
+int
+engine_encrypt_start (engine_filter_t filter,
+                      protocol_t protocol, char **recipients)
+{
+  gpg_error_t err;
+
+  err = op_encrypt_data (filter->indata, filter->outdata,
+                         filter, recipients, NULL, 0);
+  return err;
+}
+
+
+
+\f
+/* Release a KEY.  Do nothing if KEY is NULL.  */
+void
+engine_release_key (engine_keyinfo_t key)
+{
+  if (!key)
+    return;
+  gpgme_key_release (key->gpgme.key);
+  key->gpgme.key = NULL;
+}
+
+
+/* Return a signing key and store it at the R_KEY.  the caller sahll
+   set ENCRYPTING to true if the message will also be encrypted.  On
+   error true is returned and NULL stored at R_KEY.  Not ethat
+   depending on the actual engine use the retruned key might be NULL -
+   thus this shall not be used as an error indication. */
+int 
+engine_get_signer_key (engine_keyinfo_t *r_key, int encrypting)
+{
+  engine_keyinfo_t key;
+  gpgme_key_t sign_key = NULL;
+
+  *r_key = NULL;
+  if (signer_dialog_box (&sign_key, NULL, encrypting) == -1)
+    {
+      log_debug ("%s:%s: failed to get key from dialog\n", SRCNAME, __func__);
+      return gpg_error (GPG_ERR_CANCELED);  
+    }
+  key = xcalloc (1, sizeof **r_key);
+  key->gpgme.key = sign_key;
+
+  *r_key = key;
+  return 0;
+}
+
+
index 0b5398a..d2a992d 100644 (file)
@@ -1,21 +1,22 @@
-/* engine.h - Crypto engine
- *     Copyright (C) 2005 g10 Code GmbH
+/* engine.h - Crypto engine dispatcher
+ *     Copyright (C) 2007 g10 Code GmbH
  *
- * This file is part of GPGol.
+ * This file is part of GpgOL.
  *
- * GPGol is free software; you can redistribute it and/or
+ * GpgOL is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License
  * as published by the Free Software Foundation; either version 2.1 
  * of the License, or (at your option) any later version.
  *  
- * GPGol is distributed in the hope that it will be useful,
+ * GpgOL is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Lesser General Public License
- * along with GPGol; if not, write to the Free Software Foundation, 
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GpgOL; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
  */
 
 #ifndef GPGOL_ENGINE_H
@@ -28,75 +29,50 @@ extern "C" {
 #endif
 #endif
 
-#include <gpgme.h>
-
 typedef enum 
   {
     OP_SIG_NORMAL = 0,
     OP_SIG_DETACH = 1,
     OP_SIG_CLEAR  = 2
   } 
-op_sigtype_t;
+engine_sigtype_t;
 
 
-int op_init (void);
-void op_deinit (void);
+/* The key info object.  */
+struct engine_keyinfo_s;
+typedef struct engine_keyinfo_s *engine_keyinfo_t;
 
-#define op_debug_enable(file) op_set_debug_mode (5, (file))
-#define op_debug_disable() op_set_debug_mode (0, NULL)
-void op_set_debug_mode (int val, const char *file);
+/* The filter object.  */
+struct engine_filter_s;
+typedef struct engine_filter_s *engine_filter_t;
 
-int op_encrypt (const char *inbuf, char **outbuf,
-                gpgme_key_t *keys, gpgme_key_t sign_key, int ttl);
-int op_encrypt_stream (LPSTREAM instream, LPSTREAM outstream,
-                       gpgme_key_t *keys, gpgme_key_t sign_key, int ttl);
 
-int op_sign (const char *inbuf, char **outbuf, int mode,
-             gpgme_key_t sign_key, int ttl);
-int op_sign_stream (LPSTREAM instream, LPSTREAM outstream, int mode,
-                    gpgme_key_t sign_key, int ttl);
 
-int op_decrypt (const char *inbuf, char **outbuf, int ttl,
-                const char *filename, gpgme_data_t attestation, 
-                int preview_mode);
-int op_decrypt_stream (LPSTREAM instream, LPSTREAM outstream, int ttl,
-                       const char *filename, gpgme_data_t attestation);
-int op_decrypt_stream_to_buffer (LPSTREAM instream, char **outbuf, int ttl,
-                                 const char *filename,
-                                 gpgme_data_t attestation);
-int op_decrypt_stream_to_gpgme (gpgme_protocol_t protocol,
-                                LPSTREAM instream, gpgme_data_t out, int ttl,
-                                const char *filename, gpgme_data_t attestation,
-                                int preview_mode);
 
-int op_verify (const char *inbuf, char **outbuf, const char *filename,
-               gpgme_data_t attestation);
-int op_verify_detached_sig (LPSTREAM data, const char *sig,
-                            const char *filename, gpgme_data_t attestation);
-int op_verify_detached_sig_mem (const char *data_string,
-                                const char *sig_string, const char *filename,
-                                gpgme_data_t attestation);
-int op_verify_detached_sig_gpgme (gpgme_protocol_t protocol, 
-                                  gpgme_data_t data, gpgme_data_t sig,
-                                  const char *filename,
-                                  gpgme_data_t attestation);
+/*-- engine.c -- */
+int engine_init (void);
+void engine_deinit (void);
 
+/* This callback function is to be used only by engine-gpgme.c.   */
+void engine_gpgme_finished (engine_filter_t filter, gpg_error_t status);
 
-int op_export_keys (const char *pattern[], const char *outfile);
+int engine_filter (engine_filter_t filter,
+                   const void *indata, size_t indatalen);
+int engine_create_filter (engine_filter_t *r_filter,
+                          int (*outfnc) (void *, const void *, size_t),
+                          void *outfncdata);
+int engine_wait (engine_filter_t filter);
+void engine_cancel (engine_filter_t filter);
 
-int op_lookup_keys (char **names, gpgme_key_t **keys, char ***unknown);
-gpgme_key_t op_get_one_key (char *pattern);
+int engine_encrypt_start (engine_filter_t filter, 
+                          protocol_t protocol, char **recipients);
 
-const char *userid_from_key (gpgme_key_t k);
-const char *keyid_from_key (gpgme_key_t k);
 
-const char *op_strerror (int err);
-const char *op_strsource (int err);
 
+void engine_release_key (engine_keyinfo_t key);
+int  engine_get_signer_key (engine_keyinfo_t *r_key, int encrypting);
 
 #ifdef __cplusplus
 }
 #endif
-
-
 #endif /*GPGOL_ENGINE_H*/
index ad7f6bd..9bafd9e 100644 (file)
@@ -31,7 +31,6 @@
 #include "common.h"
 #include "display.h"
 #include "msgcache.h"
-#include "engine.h"
 #include "mapihelp.h"
 
 #include "olflange-ids.h"
index 425fbc1..9851851 100644 (file)
@@ -351,7 +351,7 @@ GpgolMessageEvents::OnWriteComplete (LPEXCHEXTCALLBACK pEECB, ULONG lFlags)
       if (m_pExchExt->m_gpgEncrypt && m_pExchExt->m_gpgSign)
         rc = mime_sign_encrypt (msg, PROTOCOL_OPENPGP);
       else if (m_pExchExt->m_gpgEncrypt && !m_pExchExt->m_gpgSign)
-        rc = mime_encrypt (msg, PROTOCOL_OPENPGP);
+        rc = message_encrypt (msg, hWnd);
       else if (!m_pExchExt->m_gpgEncrypt && m_pExchExt->m_gpgSign)
         rc = mime_sign (msg, PROTOCOL_OPENPGP);
       else
index cb4a33c..c803ac6 100644 (file)
@@ -30,6 +30,7 @@
 #include "common.h"
 #include "mapihelp.h"
 #include "mimeparser.h"
+#include "mimemaker.h"
 #include "message.h"
 
 #define TRACEPOINT() do { log_debug ("%s:%s:%d: tracepoint\n", \
@@ -497,6 +498,104 @@ message_decrypt (LPMESSAGE message, msgtype_t msgtype, int force)
 }
 
 
+\f
+
+/* Return an array of strings with the recipients of the message. On
+   success a malloced array is returned containing allocated strings
+   for each recipient.  The end of the array is marked by NULL.
+   Caller is responsible for releasing the array.  On failure NULL is
+   returned.  */
+static char **
+get_recipients (LPMESSAGE message)
+{
+  static SizedSPropTagArray (1L, PropRecipientNum) = {1L, {PR_EMAIL_ADDRESS}};
+  HRESULT hr;
+  LPMAPITABLE lpRecipientTable = NULL;
+  LPSRowSet lpRecipientRows = NULL;
+  unsigned int rowidx;
+  LPSPropValue row;
+  char **rset;
+  int rsetidx;
+
+
+  if (!message)
+    return NULL;
+
+  hr = message->GetRecipientTable (0, &lpRecipientTable);
+  if (FAILED (hr)) 
+    {
+      log_debug_w32 (-1, "%s:%s: GetRecipientTable failed", SRCNAME, __func__);
+      return NULL;
+    }
+
+  hr = HrQueryAllRows (lpRecipientTable, (LPSPropTagArray) &PropRecipientNum,
+                       NULL, NULL, 0L, &lpRecipientRows);
+  if (FAILED (hr)) 
+    {
+      log_debug_w32 (-1, "%s:%s: HrQueryAllRows failed", SRCNAME, __func__);
+      if (lpRecipientTable)
+        lpRecipientTable->Release();
+      return NULL;
+    }
+
+  rset = (char**)xcalloc (lpRecipientRows->cRows+1, sizeof *rset);
+
+  for (rowidx=0, rsetidx=0; rowidx < lpRecipientRows->cRows; rowidx++)
+    {
+      if (!lpRecipientRows->aRow[rowidx].cValues)
+        continue;
+      row = lpRecipientRows->aRow[rowidx].lpProps;
+
+      switch (PROP_TYPE (row->ulPropTag))
+        {
+        case PT_UNICODE:
+          if ((rset[rsetidx] = wchar_to_utf8 (row->Value.lpszW)))
+            rsetidx++;
+          else
+            log_debug ("%s:%s: error converting recipient to utf8\n",
+                       SRCNAME, __func__);
+          break;
+      
+        case PT_STRING8: /* Assume ASCII. */
+          rset[rsetidx++] = xstrdup (row->Value.lpszA);
+          break;
+          
+        default:
+          log_debug ("%s:%s: proptag=0x%08lx not supported\n",
+                     SRCNAME, __func__, row->ulPropTag);
+          break;
+        }
+    }
+
+  if (lpRecipientTable)
+    lpRecipientTable->Release();
+  if (lpRecipientRows)
+    FreeProws(lpRecipientRows);        
+  
+  log_debug ("%s:%s: got %d recipients:\n", SRCNAME, __func__, rsetidx);
+  for (rsetidx=0; rset[rsetidx]; rsetidx++)
+    log_debug ("%s:%s: \t`%s'\n", SRCNAME, __func__, rset[rsetidx]);
+
+  return rset;
+}
+
+
+static void
+release_recipient_array (char **recipients)
+{
+  int idx;
+
+  if (recipients)
+    {
+      for (idx=0; recipients[idx]; idx++)
+        xfree (recipients[idx]);
+      xfree (recipients);
+    }
+}
+
+
+
+
 
 \f
 #if 0
@@ -644,3 +743,35 @@ message_sign (LPMESSAGE message, HWND hwnd, int want_html)
 }
 
 #endif
+
+
+/* Encrypt the MESSAGE.  */
+int 
+message_encrypt (LPMESSAGE message, HWND hwnd)
+{
+  gpg_error_t err;
+  char **recipients;
+
+  recipients = get_recipients (message);
+  if (!recipients || !recipients[0])
+    {
+      MessageBox (hwnd, _("No recipients for encrypted message given"),
+                  "GpgOL", MB_ICONERROR|MB_OK);
+
+      err = gpg_error (GPG_ERR_GENERAL);
+    }
+  else
+    {
+      err = mime_encrypt (message, PROTOCOL_OPENPGP, recipients);
+      if (err)
+        {
+          char buf[200];
+          
+          snprintf (buf, sizeof buf,
+                    _("Encryption failed (%s)"), gpg_strerror (err));
+          MessageBox (hwnd, buf, "GpgOL", MB_ICONERROR|MB_OK);
+        }
+    }
+  release_recipient_array (recipients);
+  return err;
+}
index 5223ea3..699c497 100644 (file)
@@ -26,5 +26,7 @@
 int message_verify (LPMESSAGE message, msgtype_t msgtype, int force);
 int message_decrypt (LPMESSAGE message, msgtype_t msgtype, int force);
 
+int message_encrypt (LPMESSAGE message, HWND hwnd);
+
 
 #endif /*MESSAGE_H*/
index efa2b9e..7ce2e00 100644 (file)
@@ -230,6 +230,13 @@ write_buffer (sink_t sink, const void *data, size_t datalen)
   return sink->writefnc (sink, data, datalen);
 }
 
+/* Same as above but used for passing as callback function.  */
+static int
+write_buffer_voidarg (void *opaque, const void *data, size_t datalen)
+{
+  sink_t sink = opaque;
+  return write_buffer (sink, data, datalen);
+}
 
 
 /* Write the string TEXT to the IStream STREAM.  Returns 0 on sucsess,
@@ -1039,6 +1046,8 @@ mime_sign (LPMESSAGE message, protocol_t protocol)
   char *body = NULL;
   int n_att_usable;
 
+  memset (sink, 0, sizeof *sink);
+
   protocol = check_protocol (protocol);
   if (protocol == PROTOCOL_UNKNOWN)
     return -1;
@@ -1169,26 +1178,21 @@ mime_sign (LPMESSAGE message, protocol_t protocol)
 static int
 sink_encryption_write (sink_t encsink, const void *data, size_t datalen)
 {
-  sink_t outsink = encsink->cb_data;
+  engine_filter_t filter = encsink->cb_data;
 
-  if (!outsink)
+  if (!filter)
     {
       log_error ("%s:%s: sink not setup for writing", SRCNAME, __func__);
       return -1;
     }
-  if (!data)
-    {
-      /* Flush */
-      return 0;
-    }
 
-  return write_buffer (outsink, data, datalen);
+  return engine_filter (filter, data, datalen);
 }
 
 
 /* Encrypt the MESSAGE.  */
 int 
-mime_encrypt (LPMESSAGE message, protocol_t protocol)
+mime_encrypt (LPMESSAGE message, protocol_t protocol, char **recipients)
 {
   int result = -1;
   int rc;
@@ -1202,6 +1206,10 @@ mime_encrypt (LPMESSAGE message, protocol_t protocol)
   mapi_attach_item_t *att_table = NULL;
   char *body = NULL;
   int n_att_usable;
+  engine_filter_t filter;
+
+  memset (sink, 0, sizeof *sink);
+  memset (encsink, 0, sizeof *encsink);
 
   protocol = check_protocol (protocol);
   if (protocol == PROTOCOL_UNKNOWN)
@@ -1213,6 +1221,14 @@ mime_encrypt (LPMESSAGE message, protocol_t protocol)
   if (!attach)
     return -1;
 
+  /* Prepare the encryption.  We do this early as it is quite common
+     that some recipients are not be available and thus the encryption
+     will fail early. */
+  if (engine_create_filter (&filter, write_buffer_voidarg, sink))
+    goto failure;
+  if (engine_encrypt_start (filter, protocol, recipients))
+    goto failure;
+
   /* Get the attachment info and the body.  */
   body = mapi_get_body (message, NULL);
   if (body && !*body)
@@ -1257,9 +1273,8 @@ mime_encrypt (LPMESSAGE message, protocol_t protocol)
     goto failure;
 
   /* Create a new sink for encrypting the following stuff.  */
-  encsink->cb_data = sink;
+  encsink->cb_data = filter;
   encsink->writefnc = sink_encryption_write;
-
   
   if ((body && n_att_usable) || n_att_usable > 1)
     {
@@ -1291,12 +1306,13 @@ mime_encrypt (LPMESSAGE message, protocol_t protocol)
   if (*inner_boundary && (rc = write_boundary (encsink, inner_boundary, 1)))
     goto failure;
 
-  /* Flush the encryption sink and thus wait for the encryption to
-     get ready.  */
+  /* Flush the encryption sink and wait for the encryption to get
+     ready.  */
   if ((rc = write_buffer (encsink, NULL, 0)))
     goto failure;
-  /* FIXME: Release the encryption context etc.  Using the flush above
-     is not a clean way of doing it.  */
+  if ((rc = engine_wait (filter)))
+    goto failure;
+  filter = NULL; /* Not valid anymore.  */
   encsink->cb_data = NULL; /* Not needed anymore.  */
   
 
@@ -1313,8 +1329,7 @@ mime_encrypt (LPMESSAGE message, protocol_t protocol)
   result = 0;  /* Everything is fine, fall through the cleanup now.  */
 
  failure:
-  if (encsink->cb_data)
-    ;/*FIXME:  Cancel the encryption.  */
+  engine_cancel (filter);
   cancel_mapi_attachment (&attach, sink);
   xfree (body);
   mapi_release_attach_table (att_table);
index dbec526..2e8eea6 100644 (file)
@@ -29,7 +29,7 @@ extern "C" {
 #endif
 
 int mime_sign (LPMESSAGE message, protocol_t protocol);
-int mime_encrypt (LPMESSAGE message, protocol_t protocol);
+int mime_encrypt (LPMESSAGE message, protocol_t protocol, char **recipients);
 int mime_sign_encrypt (LPMESSAGE message, protocol_t protocol);
 
 
index c77b2c7..3c01099 100644 (file)
@@ -31,7 +31,6 @@
 #include "display.h"
 #include "common.h"
 #include "msgcache.h"
-#include "engine.h"
 #include "mapihelp.h"
 
 #include "olflange-ids.h"
index 042f798..2a0cc8c 100644 (file)
@@ -315,7 +315,7 @@ GpgolExt::GpgolExt (void)
   if (!g_initdll)
     {
       read_options ();
-      op_init ();
+      engine_init ();
       g_initdll = TRUE;
       log_debug ("%s:%s: first time initialization done\n",
                  SRCNAME, __func__);
@@ -336,7 +336,7 @@ GpgolExt::~GpgolExt (void)
     {
       if (g_initdll)
         {
-          op_deinit ();
+          engine_deinit ();
           write_options ();
           g_initdll = FALSE;
           log_debug ("%s:%s: DLL closed down\n", SRCNAME, __func__);
index 82d0b17..aa1c0c4 100644 (file)
@@ -342,9 +342,7 @@ transition_to_body (rfc822parse_t msg)
   if (!rc)
     {
       /* Store the boundary if we have multipart type. */
-      ctx = rfc822parse_parse_field (msg, "GnuPG-Content-Type", -1);
-      if (!ctx)
-        ctx = rfc822parse_parse_field (msg, "Content-Type", -1);
+      ctx = rfc822parse_parse_field (msg, "Content-Type", -1);
       if (ctx)
         {
           const char *s;
@@ -766,7 +764,6 @@ parse_field (HDR_LINE hdr)
     const unsigned char *name;
     size_t namelen;
   } tspecial_header[] = {
-    { "GnuPG-Content-Type", 18},
     { "Content-Type", 12},
     { "Content-Transfer-Encoding", 25},
     { "Content-Disposition", 19},
index 93d2682..727f469 100644 (file)
@@ -36,14 +36,14 @@ BEGIN
         BEGIN
             VALUE "Comments", "This plugin is available under the terms of the GNU Lesser General Public License.\0"
             VALUE "CompanyName", "g10 Code GmbH\0"
-            VALUE "FileDescription", "GPGol - GnuPG plugin for Outlook\0"
+            VALUE "FileDescription", "GpgOL - GnuPG plugin for Outlook\0"
             VALUE "FileVersion", "@VERSION@\0"
             VALUE "InternalName", "gpgol\0"
             VALUE "LegalCopyright", "Copyright © 2005 g10 Code GmbH\0"
             VALUE "LegalTrademarks", "\0"
             VALUE "OriginalFilename", "gpgol.dll\0"
             VALUE "PrivateBuild", "\0"
-            VALUE "ProductName", "GPGol\0"
+            VALUE "ProductName", "GpgOL\0"
             VALUE "ProductVersion", "@VERSION@\0"
             VALUE "SpecialBuild", "@BUILD_TIMESTAMP@\0"
         END