[svn] GpgOL - r176 - trunk/src
svn author wk
cvs at cvs.gnupg.org
Wed Sep 12 18:40:52 CEST 2007
Author: wk
Date: 2007-09-12 18:40:23 +0200 (Wed, 12 Sep 2007)
New Revision: 176
Added:
trunk/src/engine-assuan.c
trunk/src/engine-assuan.h
trunk/src/engine.c
Modified:
trunk/src/ChangeLog
trunk/src/Makefile.am
trunk/src/engine-gpgme.c
trunk/src/engine.h
trunk/src/ext-commands.cpp
trunk/src/message-events.cpp
trunk/src/message.cpp
trunk/src/message.h
trunk/src/mimemaker.c
trunk/src/mimemaker.h
trunk/src/ol-ext-callback.cpp
trunk/src/olflange.cpp
trunk/src/rfc822parse.c
trunk/src/versioninfo.rc.in
Log:
First PGP/MIME encrypted message sent.
Changed encryption to make use of callbacks.
Modified: trunk/src/ChangeLog
===================================================================
--- trunk/src/ChangeLog 2007-09-08 11:18:38 UTC (rev 175)
+++ trunk/src/ChangeLog 2007-09-12 16:40:23 UTC (rev 176)
@@ -1,3 +1,14 @@
+2007-09-11 Werner Koch <wk at g10code.com>
+
+ * engine-gpgme.c (op_encrypt_data): New.
+
+2007-09-08 Werner Koch <wk at 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 at g10code.com>
* common.c (qp_decode): Handle softe line breaks.
Modified: trunk/src/Makefile.am
===================================================================
--- trunk/src/Makefile.am 2007-09-08 11:18:38 UTC (rev 175)
+++ trunk/src/Makefile.am 2007-09-12 16:40:23 UTC (rev 176)
@@ -34,7 +34,9 @@
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 \
Added: trunk/src/engine-assuan.c
===================================================================
--- trunk/src/engine-assuan.c 2007-09-08 11:18:38 UTC (rev 175)
+++ trunk/src/engine-assuan.c 2007-09-12 16:40:23 UTC (rev 176)
@@ -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;
+}
+
+
+
+
+/* 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;
+}
+
+
+/* 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;
+}
+
+
+
+/* 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;
+}
+
+
+
+/* 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;
+}
+
+
+
+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");
+}
+
+
+
+
+/* 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
Added: trunk/src/engine-assuan.h
===================================================================
--- trunk/src/engine-assuan.h 2007-09-08 11:18:38 UTC (rev 175)
+++ trunk/src/engine-assuan.h 2007-09-12 16:40:23 UTC (rev 176)
@@ -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*/
Modified: trunk/src/engine-gpgme.c
===================================================================
--- trunk/src/engine-gpgme.c 2007-09-08 11:18:38 UTC (rev 175)
+++ trunk/src/engine-gpgme.c 2007-09-12 16:40:23 UTC (rev 176)
@@ -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 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 @@
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 @@
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 @@
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;
+}
+
+
/* Sign and encrypt the data in INBUF into a newly allocated buffer at
OUTBUF. Caller needs to free the returned buffer using gpgme_free. */
Added: trunk/src/engine.c
===================================================================
--- trunk/src/engine.c 2007-09-08 11:18:38 UTC (rev 175)
+++ trunk/src/engine.c 2007-09-12 16:40:23 UTC (rev 176)
@@ -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. */
+};
+
+
+
+
+/* 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__);
+}
+
+
+
+
+
+/* Initialize the engine dispatcher. */
+int
+engine_init (void)
+{
+ op_init ();
+ return 0;
+}
+
+
+/* Shutdown the engine dispatcher. */
+void
+engine_deinit (void)
+{
+ op_deinit ();
+
+}
+
+
+
+/* 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;
+}
+
+
+
+
+/* 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;
+}
+
+
Modified: trunk/src/engine.h
===================================================================
--- trunk/src/engine.h 2007-09-08 11:18:38 UTC (rev 175)
+++ trunk/src/engine.h 2007-09-12 16:40:23 UTC (rev 176)
@@ -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 @@
#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*/
Modified: trunk/src/ext-commands.cpp
===================================================================
--- trunk/src/ext-commands.cpp 2007-09-08 11:18:38 UTC (rev 175)
+++ trunk/src/ext-commands.cpp 2007-09-12 16:40:23 UTC (rev 176)
@@ -31,7 +31,6 @@
#include "common.h"
#include "display.h"
#include "msgcache.h"
-#include "engine.h"
#include "mapihelp.h"
#include "olflange-ids.h"
Modified: trunk/src/message-events.cpp
===================================================================
--- trunk/src/message-events.cpp 2007-09-08 11:18:38 UTC (rev 175)
+++ trunk/src/message-events.cpp 2007-09-12 16:40:23 UTC (rev 176)
@@ -351,7 +351,7 @@
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
Modified: trunk/src/message.cpp
===================================================================
--- trunk/src/message.cpp 2007-09-08 11:18:38 UTC (rev 175)
+++ trunk/src/message.cpp 2007-09-12 16:40:23 UTC (rev 176)
@@ -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,7 +498,105 @@
}
+
+/* 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);
+ }
+}
+
+
+
+
+
#if 0
/* Sign the current message. Returns 0 on success. */
@@ -644,3 +743,35 @@
}
#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;
+}
Modified: trunk/src/message.h
===================================================================
--- trunk/src/message.h 2007-09-08 11:18:38 UTC (rev 175)
+++ trunk/src/message.h 2007-09-12 16:40:23 UTC (rev 176)
@@ -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*/
Modified: trunk/src/mimemaker.c
===================================================================
--- trunk/src/mimemaker.c 2007-09-08 11:18:38 UTC (rev 175)
+++ trunk/src/mimemaker.c 2007-09-12 16:40:23 UTC (rev 176)
@@ -230,6 +230,13 @@
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 @@
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 @@
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,7 +1206,11 @@
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)
return -1;
@@ -1213,6 +1221,14 @@
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 @@
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 @@
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 @@
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);
Modified: trunk/src/mimemaker.h
===================================================================
--- trunk/src/mimemaker.h 2007-09-08 11:18:38 UTC (rev 175)
+++ trunk/src/mimemaker.h 2007-09-12 16:40:23 UTC (rev 176)
@@ -29,7 +29,7 @@
#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);
Modified: trunk/src/ol-ext-callback.cpp
===================================================================
--- trunk/src/ol-ext-callback.cpp 2007-09-08 11:18:38 UTC (rev 175)
+++ trunk/src/ol-ext-callback.cpp 2007-09-12 16:40:23 UTC (rev 176)
@@ -31,7 +31,6 @@
#include "display.h"
#include "common.h"
#include "msgcache.h"
-#include "engine.h"
#include "mapihelp.h"
#include "olflange-ids.h"
Modified: trunk/src/olflange.cpp
===================================================================
--- trunk/src/olflange.cpp 2007-09-08 11:18:38 UTC (rev 175)
+++ trunk/src/olflange.cpp 2007-09-12 16:40:23 UTC (rev 176)
@@ -315,7 +315,7 @@
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 @@
{
if (g_initdll)
{
- op_deinit ();
+ engine_deinit ();
write_options ();
g_initdll = FALSE;
log_debug ("%s:%s: DLL closed down\n", SRCNAME, __func__);
Modified: trunk/src/rfc822parse.c
===================================================================
--- trunk/src/rfc822parse.c 2007-09-08 11:18:38 UTC (rev 175)
+++ trunk/src/rfc822parse.c 2007-09-12 16:40:23 UTC (rev 176)
@@ -342,9 +342,7 @@
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 @@
const unsigned char *name;
size_t namelen;
} tspecial_header[] = {
- { "GnuPG-Content-Type", 18},
{ "Content-Type", 12},
{ "Content-Transfer-Encoding", 25},
{ "Content-Disposition", 19},
Modified: trunk/src/versioninfo.rc.in
===================================================================
--- trunk/src/versioninfo.rc.in 2007-09-08 11:18:38 UTC (rev 175)
+++ trunk/src/versioninfo.rc.in 2007-09-12 16:40:23 UTC (rev 176)
@@ -36,14 +36,14 @@
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
More information about the Gnupg-commits
mailing list