=?utf-8?q?=5Bbalsa/gtk3=5D_New_crypto_implementation_=28Albrecht_Dre?= =?utf-8?b?w58p?=
- From: Peter Bloomfield <PeterB src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [balsa/gtk3] New crypto implementation (Albrecht DreÃ)
- Date: Sat, 10 Dec 2011 23:02:29 +0000 (UTC)
commit 989c0bb2d9a63e111c16eee33484bf2842b30cba
Author: Peter Bloomfield <PeterBloomfield bellsouth net>
Date: Sat Dec 10 18:00:07 2011 -0500
New crypto implementation (Albrecht DreÃ)
* libbalsa/Makefile.am: new files.
* libbalsa/body.c (libbalsa_message_body_protect_state): use new
implementation.
* libbalsa/gmime-application-pkcs7.c
(g_mime_application_pkcs7_decrypt_verify),
(g_mime_application_pkcs7_encrypt): ditto.
* libbalsa/gmime-application-pkcs7.h: ditto.
* libbalsa/gmime-gpgme-context.c: new file.
* libbalsa/gmime-gpgme-context.h: ditto.
* libbalsa/gmime-gpgme-signature.c
(g_mime_gpgme_sigstat_new_from_gpgme_ctx),
(g_mime_gpgme_sigstat_finalize), (hex_decode),
(libbalsa_cert_subject_readable), (g_mime_gpgme_sigstat_init):
ditto.
* libbalsa/gmime-gpgme-signature.h: ditto.
* libbalsa/gmime-part-rfc2440.c (g_mime_part_check_rfc2440),
(g_mime_part_rfc2440_sign_encrypt), (g_mime_part_rfc2440_verify),
(g_mime_part_rfc2440_decrypt): use new implementation.
* libbalsa/gmime-part-rfc2440.h: ditto.
* libbalsa/rfc3156.c (libbalsa_sign_mime_object),
(libbalsa_encrypt_mime_object), (libbalsa_body_check_signature),
(libbalsa_body_decrypt), (libbalsa_rfc2440_sign_encrypt),
(libbalsa_rfc2440_verify), (libbalsa_rfc2440_decrypt),
(libbalsa_gpgme_validity_to_gchar_short), (append_time_t),
(libbalsa_signature_info_to_gchar): ditto.
* libbalsa/rfc3156.h: ditto.
* src/balsa-message.c (libbalsa_msg_part_2440): ditto.
* src/main.c (main): ditto.
ChangeLog | 33 +
libbalsa/Makefile.am | 16 +-
libbalsa/body.c | 2 +-
libbalsa/gmime-application-pkcs7.c | 353 ++--------
libbalsa/gmime-application-pkcs7.h | 61 +--
libbalsa/gmime-gpgme-context.c | 1415 ------------------------------------
libbalsa/gmime-gpgme-context.h | 125 ----
libbalsa/gmime-gpgme-signature.c | 199 +++---
libbalsa/gmime-gpgme-signature.h | 27 +-
libbalsa/gmime-multipart-crypt.c | 576 +++++++++++++++
libbalsa/gmime-multipart-crypt.h | 69 ++
libbalsa/gmime-part-rfc2440.c | 275 ++++----
libbalsa/gmime-part-rfc2440.h | 35 +-
libbalsa/libbalsa-gpgme-cb.c | 428 +++++++++++
libbalsa/libbalsa-gpgme-cb.h | 59 ++
libbalsa/libbalsa-gpgme.c | 968 ++++++++++++++++++++++++
libbalsa/libbalsa-gpgme.h | 115 +++
libbalsa/rfc3156.c | 1276 +++++----------------------------
libbalsa/rfc3156.h | 5 +-
src/balsa-message.c | 2 +-
src/main.c | 56 +--
21 files changed, 2741 insertions(+), 3354 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 52ad6f4..3763177 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,36 @@
+2011-12-10 Albrecht DreÃ
+
+ New crypto implementation
+
+ * libbalsa/Makefile.am: new files.
+ * libbalsa/body.c (libbalsa_message_body_protect_state): use new
+ implementation.
+ * libbalsa/gmime-application-pkcs7.c
+ (g_mime_application_pkcs7_decrypt_verify),
+ (g_mime_application_pkcs7_encrypt): ditto.
+ * libbalsa/gmime-application-pkcs7.h: ditto.
+ * libbalsa/gmime-gpgme-context.c: new file.
+ * libbalsa/gmime-gpgme-context.h: ditto.
+ * libbalsa/gmime-gpgme-signature.c
+ (g_mime_gpgme_sigstat_new_from_gpgme_ctx),
+ (g_mime_gpgme_sigstat_finalize), (hex_decode),
+ (libbalsa_cert_subject_readable), (g_mime_gpgme_sigstat_init):
+ ditto.
+ * libbalsa/gmime-gpgme-signature.h: ditto.
+ * libbalsa/gmime-part-rfc2440.c (g_mime_part_check_rfc2440),
+ (g_mime_part_rfc2440_sign_encrypt), (g_mime_part_rfc2440_verify),
+ (g_mime_part_rfc2440_decrypt): use new implementation.
+ * libbalsa/gmime-part-rfc2440.h: ditto.
+ * libbalsa/rfc3156.c (libbalsa_sign_mime_object),
+ (libbalsa_encrypt_mime_object), (libbalsa_body_check_signature),
+ (libbalsa_body_decrypt), (libbalsa_rfc2440_sign_encrypt),
+ (libbalsa_rfc2440_verify), (libbalsa_rfc2440_decrypt),
+ (libbalsa_gpgme_validity_to_gchar_short), (append_time_t),
+ (libbalsa_signature_info_to_gchar): ditto.
+ * libbalsa/rfc3156.h: ditto.
+ * src/balsa-message.c (libbalsa_msg_part_2440): ditto.
+ * src/main.c (main): ditto.
+
2011-12-03 Peter Bloomfield
* src/sendmsg-window.c (create_text_area): remove redundant
diff --git a/libbalsa/Makefile.am b/libbalsa/Makefile.am
index 525b04a..00bda16 100644
--- a/libbalsa/Makefile.am
+++ b/libbalsa/Makefile.am
@@ -5,8 +5,12 @@ noinst_LIBRARIES = libbalsa.a
if BUILD_WITH_GPGME
libbalsa_gpgme_extra = \
- gmime-gpgme-context.h \
- gmime-gpgme-context.c \
+ libbalsa-gpgme.h \
+ libbalsa-gpgme.c \
+ libbalsa-gpgme-cb.h \
+ libbalsa-gpgme-cb.c \
+ gmime-multipart-crypt.h \
+ gmime-multipart-crypt.c \
gmime-part-rfc2440.h \
gmime-part-rfc2440.c \
gmime-gpgme-signature.h \
@@ -15,8 +19,12 @@ libbalsa_gpgme_extra_dist =
else
libbalsa_gpgme_extra =
libbalsa_gpgme_extra_dist = \
- gmime-gpgme-context.h \
- gmime-gpgme-context.c \
+ libbalsa-gpgme.h \
+ libbalsa-gpgme.c \
+ libbalsa-gpgme-cb.h \
+ libbalsa-gpgme-cb.c \
+ gmime-multipart-crypt.h \
+ gmime-multipart-crypt.c \
gmime-part-rfc2440.h \
gmime-part-rfc2440.c \
gmime-gpgme-signature.h \
diff --git a/libbalsa/body.c b/libbalsa/body.c
index 9dd05de..22f1b04 100644
--- a/libbalsa/body.c
+++ b/libbalsa/body.c
@@ -851,7 +851,7 @@ libbalsa_message_body_protect_state(LibBalsaMessageBody *body)
at least marginal */
if (body->sig_info->validity >= GPGME_VALIDITY_MARGINAL &&
(body->sig_info->protocol == GPGME_PROTOCOL_CMS ||
- body->sig_info->trust >= GPGME_VALIDITY_MARGINAL))
+ body->sig_info->key->owner_trust >= GPGME_VALIDITY_MARGINAL))
return LIBBALSA_MSG_PROTECT_SIGN_GOOD;
else
return LIBBALSA_MSG_PROTECT_SIGN_NOTRUST;
diff --git a/libbalsa/gmime-application-pkcs7.c b/libbalsa/gmime-application-pkcs7.c
index 63b8087..cad055c 100644
--- a/libbalsa/gmime-application-pkcs7.c
+++ b/libbalsa/gmime-application-pkcs7.c
@@ -1,7 +1,7 @@
/* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
/*
* S/MIME application/pkcs7-mime support for gmime/balsa
- * Copyright (C) 2004 Albrecht Dreß<albrecht dress arcor de>
+ * Copyright (C) 2004 Albrecht Dreà <albrecht dress arcor de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -35,6 +35,7 @@
#include <gmime/gmime-multipart.h>
#include <gmime/gmime-multipart-signed.h>
#include <gmime/gmime-multipart-encrypted.h>
+#include "libbalsa-gpgme.h"
#include "gmime-application-pkcs7.h"
#include <glib/gi18n.h>
@@ -42,137 +43,6 @@
#define GMIME_PKCS7_ERR_QUARK (g_quark_from_static_string ("gmime-app-pkcs7"))
-#ifdef HAS_APPLICATION_PKCS7_MIME_SIGNED_SUPPORT
-
-/*
- * Note: this code has been shamelessly stolen from Jeff's multipart-signed implementation
- */
-static void
-sign_prepare (GMimeObject *mime_part)
-{
- GMimeContentEncoding encoding;
- GMimeObject *subpart;
-
- if (GMIME_IS_MULTIPART (mime_part)) {
- GList *lpart;
-
- if (GMIME_IS_MULTIPART_SIGNED (mime_part) || GMIME_IS_MULTIPART_ENCRYPTED (mime_part)) {
- /* must not modify these parts as they must be treated as opaque */
- return;
- }
-
- lpart = GMIME_MULTIPART (mime_part)->subparts;
- while (lpart) {
- subpart = GMIME_OBJECT (lpart->data);
- sign_prepare (subpart);
- lpart = lpart->next;
- }
- } else if (GMIME_IS_MESSAGE_PART (mime_part)) {
- subpart = GMIME_MESSAGE_PART (mime_part)->message->mime_part;
- sign_prepare (subpart);
- } else {
- encoding = g_mime_part_get_content_encoding (GMIME_PART (mime_part));
-
- if (encoding != GMIME_CONTENT_ENCODING_BASE64)
- g_mime_part_set_content_encoding (GMIME_PART (mime_part),
- GMIME_CONTENT_ENCODING_QUOTEDPRINTABLE);
- }
-}
-
-
-/*
- * Sign content using the context ctx for userid and write the signed
- * application/pkcs7-mime object to pkcs7. Return 0 on success and -1 on error.
- * In the latter case, fill err with more information about the reason.
- */
-int
-g_mime_application_pkcs7_sign (GMimePart *pkcs7, GMimeObject *content,
- GMimeCipherContext *ctx, const char *userid,
- GError **err)
-{
- GMimeDataWrapper *wrapper;
- GMimeStream *filtered_stream;
- GMimeStream *stream, *sig_data_stream;
- GMimeFilter *crlf_filter, *from_filter;
-
- g_return_val_if_fail (GMIME_IS_PART (pkcs7), -1);
-#ifndef HAVE_GMIME_2_5_7
- g_return_val_if_fail (GMIME_IS_CIPHER_CONTEXT (ctx), -1);
- g_return_val_if_fail (ctx->sign_protocol != NULL, -1);
-#else /* HAVE_GMIME_2_5_7 */
- g_return_val_if_fail (GMIME_IS_CRYPTO_CONTEXT (ctx), -1);
- g_return_val_if_fail(g_mime_crypto_context_get_signature_protocol(ctx)
- != NULL, -1);
-#endif /* HAVE_GMIME_2_5_7 */
- g_return_val_if_fail (GMIME_IS_OBJECT (content), -1);
-
- /* Prepare all the parts for signing... */
- sign_prepare (content);
-
- /* get the cleartext */
- stream = g_mime_stream_mem_new ();
- filtered_stream = g_mime_stream_filter_new (stream);
-
- /* See RFC 2633, Sect. 3.1- the following op's are "SHOULD", so we do it */
- from_filter = g_mime_filter_from_new (GMIME_FILTER_FROM_MODE_ARMOR);
- g_mime_stream_filter_add (GMIME_STREAM_FILTER (filtered_stream), from_filter);
- g_object_unref (from_filter);
-
- g_mime_object_write_to_stream (content, filtered_stream);
- g_mime_stream_flush (filtered_stream);
- g_object_unref (filtered_stream);
- g_mime_stream_reset (stream);
-
- filtered_stream = g_mime_stream_filter_new (stream);
- crlf_filter = g_mime_filter_crlf_new (TRUE,
- FALSE);
- g_mime_stream_filter_add (GMIME_STREAM_FILTER (filtered_stream), crlf_filter);
- g_object_unref (crlf_filter);
-
- /* construct the signed data stream */
- sig_data_stream = g_mime_stream_mem_new ();
-
- /* get the signed content */
-#ifndef HAVE_GMIME_2_5_7
- if (g_mime_cipher_context_sign (ctx, userid, GMIME_CIPHER_HASH_DEFAULT, filtered_stream, sig_data_stream, err) == -1)
-#else /* HAVE_GMIME_2_5_7 */
- if (g_mime_crypto_context_sign
- (ctx, userid, GMIME_CIPHER_HASH_DEFAULT, filtered_stream,
- sig_data_stream, err) == -1)
-#endif /* HAVE_GMIME_2_5_7 */
- {
- g_object_unref (filtered_stream);
- g_object_unref (sig_data_stream);
- g_object_unref (stream);
- return -1;
- }
-
- g_object_unref (filtered_stream);
- g_object_unref (stream);
- g_mime_stream_reset (sig_data_stream);
-
- /* set the pkcs7 mime part as content of the pkcs7 object */
- wrapper = g_mime_data_wrapper_new();
- g_mime_data_wrapper_set_stream(wrapper, sig_data_stream);
- g_object_unref(sig_data_stream);
- g_mime_part_set_content_object(GMIME_PART(pkcs7), wrapper);
- g_mime_part_set_filename(GMIME_PART(pkcs7), "smime.p7m");
- g_mime_part_set_content_encoding(GMIME_PART(pkcs7),
- GMIME_CONTENT_ENCODING_BASE64);
- g_object_unref(wrapper);
-
- /* set the content-type params for this part */
- g_mime_object_set_content_type_parameter(GMIME_OBJECT(pkcs7),
- "smime-type", "signed-data");
- g_mime_object_set_content_type_parameter(GMIME_OBJECT(pkcs7), "name",
- "smime.p7m");
-
- return 0;
-}
-#endif /* HAS_APPLICATION_PKCS7_MIME_SIGNED_SUPPORT */
-
-
-
/*
* Try to verify pkcs7 using the context ctx, fill validity with the resulting
* validity and return the "readable" decrypted part. If anything fails, the
@@ -181,15 +51,9 @@ g_mime_application_pkcs7_sign (GMimePart *pkcs7, GMimeObject *content,
* decrypting it again. In this case, validity is undefined.
*/
GMimeObject *
-#ifndef HAVE_GMIME_2_5_7
-g_mime_application_pkcs7_verify(GMimePart * pkcs7,
- GMimeSignatureValidity ** validity,
- GMimeCipherContext * ctx, GError ** err)
-#else /* HAVE_GMIME_2_5_7 */
-g_mime_application_pkcs7_verify(GMimePart * pkcs7,
- GMimeSignatureList ** list,
- GMimeCryptoContext * ctx, GError ** err)
-#endif /* HAVE_GMIME_2_5_7 */
+g_mime_application_pkcs7_decrypt_verify(GMimePart * pkcs7,
+ GMimeGpgmeSigstat ** signature,
+ GtkWindow * parent, GError ** err)
{
GMimeObject *decrypted;
GMimeDataWrapper *wrapper;
@@ -200,49 +64,41 @@ g_mime_application_pkcs7_verify(GMimePart * pkcs7,
const char *smime_type;
g_return_val_if_fail(GMIME_IS_PART(pkcs7), NULL);
-#ifndef HAVE_GMIME_2_5_7
- g_return_val_if_fail(GMIME_IS_CIPHER_CONTEXT(ctx), NULL);
- g_return_val_if_fail(ctx->encrypt_protocol != NULL, NULL);
-#else /* HAVE_GMIME_2_5_7 */
- g_return_val_if_fail(GMIME_IS_CRYPTO_CONTEXT(ctx), NULL);
- g_return_val_if_fail(g_mime_crypto_context_get_encryption_protocol(ctx)
- != NULL, NULL);
-#endif /* HAVE_GMIME_2_5_7 */
- /* some sanity checks */
+ /* get the smime type */
smime_type =
g_mime_object_get_content_type_parameter(GMIME_OBJECT(pkcs7),
"smime-type");
- if (g_ascii_strcasecmp(smime_type, "signed-data")) {
+ if (!smime_type
+ || (g_ascii_strcasecmp(smime_type, "enveloped-data")
+ && g_ascii_strcasecmp(smime_type, "signed-data"))) {
return NULL;
}
/* get the ciphertext stream */
wrapper = g_mime_part_get_content_object(GMIME_PART(pkcs7));
g_return_val_if_fail(wrapper, NULL); /* Incomplete part. */
- ciphertext = g_mime_stream_mem_new ();
- g_mime_data_wrapper_write_to_stream (wrapper, ciphertext);
+ ciphertext = g_mime_stream_mem_new();
+ g_mime_data_wrapper_write_to_stream(wrapper, ciphertext);
g_mime_stream_reset(ciphertext);
stream = g_mime_stream_mem_new();
filtered_stream = g_mime_stream_filter_new(stream);
- crlf_filter = g_mime_filter_crlf_new(FALSE,
- FALSE);
+ crlf_filter = g_mime_filter_crlf_new(FALSE, FALSE);
g_mime_stream_filter_add(GMIME_STREAM_FILTER(filtered_stream),
crlf_filter);
g_object_unref(crlf_filter);
/* get the cleartext */
-#ifndef HAVE_GMIME_2_5_7
- *validity = g_mime_cipher_context_verify(ctx, GMIME_CIPHER_HASH_DEFAULT,
- ciphertext, filtered_stream, err);
- if (!*validity)
-#else /* HAVE_GMIME_2_5_7 */
- *list = g_mime_crypto_context_verify(ctx, GMIME_CIPHER_ALGO_DEFAULT,
- ciphertext, filtered_stream, err);
- if (!*list)
-#endif /* HAVE_GMIME_2_5_7 */
- {
+ if (g_ascii_strcasecmp(smime_type, "enveloped-data") == 0)
+ *signature =
+ libbalsa_gpgme_decrypt(ciphertext, filtered_stream,
+ GPGME_PROTOCOL_CMS, parent, err);
+ else
+ *signature =
+ libbalsa_gpgme_verify(ciphertext, filtered_stream,
+ GPGME_PROTOCOL_CMS, TRUE, err);
+ if (!*signature) {
g_object_unref(filtered_stream);
g_object_unref(ciphertext);
g_object_unref(stream);
@@ -279,65 +135,47 @@ g_mime_application_pkcs7_verify(GMimePart * pkcs7,
* about the reason.
*/
int
-g_mime_application_pkcs7_encrypt (GMimePart *pkcs7, GMimeObject *content,
-#ifndef HAVE_GMIME_2_5_7
- GMimeCipherContext *ctx, GPtrArray *recipients,
-#else /* HAVE_GMIME_2_5_7 */
- GMimeCryptoContext *ctx,
- GPtrArray *recipients,
-#endif /* HAVE_GMIME_2_5_7 */
- GError **err)
+g_mime_application_pkcs7_encrypt(GMimePart * pkcs7, GMimeObject * content,
+ GPtrArray * recipients,
+ gboolean trust_all, GtkWindow * parent,
+ GError ** err)
{
GMimeDataWrapper *wrapper;
GMimeStream *filtered_stream;
GMimeStream *stream, *ciphertext;
GMimeFilter *crlf_filter;
- g_return_val_if_fail (GMIME_IS_PART (pkcs7), -1);
-#ifndef HAVE_GMIME_2_5_7
- g_return_val_if_fail (GMIME_IS_CIPHER_CONTEXT (ctx), -1);
- g_return_val_if_fail (ctx->encrypt_protocol != NULL, -1);
-#else /* HAVE_GMIME_2_5_7 */
- g_return_val_if_fail (GMIME_IS_CRYPTO_CONTEXT (ctx), -1);
- g_return_val_if_fail(g_mime_crypto_context_get_encryption_protocol(ctx)
- != NULL, -1);
-#endif /* HAVE_GMIME_2_5_7 */
- g_return_val_if_fail (GMIME_IS_OBJECT (content), -1);
+ g_return_val_if_fail(GMIME_IS_PART(pkcs7), -1);
+ g_return_val_if_fail(GMIME_IS_OBJECT(content), -1);
/* get the cleartext */
- stream = g_mime_stream_mem_new ();
- filtered_stream = g_mime_stream_filter_new (stream);
+ stream = g_mime_stream_mem_new();
+ filtered_stream = g_mime_stream_filter_new(stream);
- crlf_filter = g_mime_filter_crlf_new (TRUE,
- FALSE);
- g_mime_stream_filter_add (GMIME_STREAM_FILTER (filtered_stream), crlf_filter);
- g_object_unref (crlf_filter);
+ crlf_filter = g_mime_filter_crlf_new(TRUE, FALSE);
+ g_mime_stream_filter_add(GMIME_STREAM_FILTER(filtered_stream),
+ crlf_filter);
+ g_object_unref(crlf_filter);
- g_mime_object_write_to_stream (content, filtered_stream);
- g_mime_stream_flush (filtered_stream);
- g_object_unref (filtered_stream);
+ g_mime_object_write_to_stream(content, filtered_stream);
+ g_mime_stream_flush(filtered_stream);
+ g_object_unref(filtered_stream);
/* reset the content stream */
- g_mime_stream_reset (stream);
+ g_mime_stream_reset(stream);
/* encrypt the content stream */
- ciphertext = g_mime_stream_mem_new ();
-#ifndef HAVE_GMIME_2_5_7
- if (g_mime_cipher_context_encrypt (ctx, FALSE, NULL, recipients, stream, ciphertext, err) == -1)
-#else /* HAVE_GMIME_2_5_7 */
- if (g_mime_crypto_context_encrypt
- (ctx, FALSE, NULL,
- GMIME_CIPHER_ALGO_DEFAULT,
- recipients, stream, ciphertext, err) == -1)
-#endif /* HAVE_GMIME_2_5_7 */
- {
- g_object_unref (ciphertext);
- g_object_unref (stream);
+ ciphertext = g_mime_stream_mem_new();
+ if (libbalsa_gpgme_encrypt
+ (recipients, NULL, stream, ciphertext, GPGME_PROTOCOL_CMS, TRUE,
+ trust_all, parent, err) == -1) {
+ g_object_unref(ciphertext);
+ g_object_unref(stream);
return -1;
}
- g_object_unref (stream);
- g_mime_stream_reset (ciphertext);
+ g_object_unref(stream);
+ g_mime_stream_reset(ciphertext);
/* set the encrypted mime part as content of the pkcs7 object */
wrapper = g_mime_data_wrapper_new();
@@ -345,109 +183,16 @@ g_mime_application_pkcs7_encrypt (GMimePart *pkcs7, GMimeObject *content,
g_object_unref(ciphertext);
g_mime_part_set_content_object(GMIME_PART(pkcs7), wrapper);
g_mime_part_set_filename(GMIME_PART(pkcs7), "smime.p7m");
- g_mime_part_set_content_encoding(GMIME_PART(pkcs7), GMIME_CONTENT_ENCODING_BASE64);
+ g_mime_part_set_content_encoding(GMIME_PART(pkcs7),
+ GMIME_CONTENT_ENCODING_BASE64);
g_object_unref(wrapper);
/* set the content-type params for this part */
g_mime_object_set_content_type_parameter(GMIME_OBJECT(pkcs7),
- "smime-type", "enveloped-data");
+ "smime-type",
+ "enveloped-data");
g_mime_object_set_content_type_parameter(GMIME_OBJECT(pkcs7), "name",
"smime.p7m");
return 0;
}
-
-
-/*
- * Decrypt the application/pkcs7-mime part pkcs7 unsing the context ctx and
- * return the decrypted gmime object or NULL on error. In the latter case, fill
- * err with more information about the reason.
- */
-GMimeObject *
-#ifndef HAVE_GMIME_2_5_7
-g_mime_application_pkcs7_decrypt (GMimePart *pkcs7, GMimeCipherContext *ctx,
- GError **err)
-#else /* HAVE_GMIME_2_5_7 */
-g_mime_application_pkcs7_decrypt (GMimePart *pkcs7,
- GMimeCryptoContext *ctx,
- GError **err)
-#endif /* HAVE_GMIME_2_5_7 */
-{
- GMimeObject *decrypted;
- GMimeDataWrapper *wrapper;
- GMimeStream *stream, *ciphertext;
- GMimeStream *filtered_stream;
- GMimeFilter *crlf_filter;
- GMimeParser *parser;
- const char *smime_type;
-
- g_return_val_if_fail(GMIME_IS_PART(pkcs7), NULL);
-#ifndef HAVE_GMIME_2_5_7
- g_return_val_if_fail(GMIME_IS_CIPHER_CONTEXT(ctx), NULL);
- g_return_val_if_fail(ctx->encrypt_protocol != NULL, NULL);
-#else /* HAVE_GMIME_2_5_7 */
- g_return_val_if_fail(GMIME_IS_CRYPTO_CONTEXT(ctx), NULL);
- g_return_val_if_fail(g_mime_crypto_context_get_encryption_protocol(ctx)
- != NULL, NULL);
-#endif /* HAVE_GMIME_2_5_7 */
-
- /* some sanity checks */
- smime_type =
- g_mime_object_get_content_type_parameter(GMIME_OBJECT(pkcs7),
- "smime-type");
- if (!smime_type || (g_ascii_strcasecmp(smime_type, "enveloped-data") &&
- g_ascii_strcasecmp(smime_type, "signed-data"))) {
- return NULL;
- }
-
- /* get the ciphertext stream */
- wrapper = g_mime_part_get_content_object(GMIME_PART(pkcs7));
- g_return_val_if_fail(wrapper, NULL); /* Incomplete part. */
- ciphertext = g_mime_stream_mem_new();
- g_mime_data_wrapper_write_to_stream (wrapper, ciphertext);
- g_mime_stream_reset(ciphertext);
-
- stream = g_mime_stream_mem_new();
- filtered_stream = g_mime_stream_filter_new(stream);
- crlf_filter = g_mime_filter_crlf_new(FALSE,
- FALSE);
- g_mime_stream_filter_add(GMIME_STREAM_FILTER(filtered_stream),
- crlf_filter);
- g_object_unref(crlf_filter);
-
- /* get the cleartext */
-#ifndef HAVE_GMIME_2_5_7
- if (g_mime_cipher_context_decrypt(ctx, ciphertext, filtered_stream, err) == NULL)
-#else /* HAVE_GMIME_2_5_7 */
- if (g_mime_crypto_context_decrypt
- (ctx, ciphertext, filtered_stream, err) == NULL)
-#endif /* HAVE_GMIME_2_5_7 */
- {
- g_object_unref(filtered_stream);
- g_object_unref(ciphertext);
- g_object_unref(stream);
-
- return NULL;
- }
-
- g_mime_stream_flush(filtered_stream);
- g_object_unref(filtered_stream);
- g_object_unref(ciphertext);
-
- g_mime_stream_reset (stream);
- parser = g_mime_parser_new();
- g_mime_parser_init_with_stream(parser, stream);
- g_object_unref(stream);
-
- decrypted = g_mime_parser_construct_part(parser);
- g_object_unref(parser);
-
- if (decrypted)
- g_object_ref(decrypted);
- else
- g_set_error(err, GMIME_PKCS7_ERR_QUARK, 42,
- _("Failed to decrypt MIME part: parse error"));
-
- return decrypted;
-}
-
diff --git a/libbalsa/gmime-application-pkcs7.h b/libbalsa/gmime-application-pkcs7.h
index 6678ff5..6b9e0e7 100644
--- a/libbalsa/gmime-application-pkcs7.h
+++ b/libbalsa/gmime-application-pkcs7.h
@@ -1,7 +1,7 @@
/* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
/*
* S/MIME application/pkcs7-mime support for gmime/balsa
- * Copyright (C) 2004 Albrecht Dreß<albrecht dress arcor de>
+ * Copyright (C) 2004 Albrecht Dreà <albrecht dress arcor de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -27,56 +27,21 @@ extern "C" {
#pragma }
#endif /* __cplusplus */
-#include <gmime/gmime-part.h>
-#ifndef HAVE_GMIME_2_5_7
-#include <gmime/gmime-cipher-context.h>
-#else /* HAVE_GMIME_2_5_7 */
-#include <gmime/gmime-crypto-context.h>
-#endif /* HAVE_GMIME_2_5_7 */
+#include <gmime/gmime.h>
-#undef HAS_APPLICATION_PKCS7_MIME_SIGNED_SUPPORT
-
-#ifdef HAS_APPLICATION_PKCS7_MIME_SIGNED_SUPPORT
-/* Note: application/pkcs7-mime signed parts are not used within balsa, as
- * they can not be viewed by mMUA's without S/MIME support. Therefore,
- * Balsa always encodes S/MIME signed stuff as multipart/signed. */
-int g_mime_application_pkcs7_sign(GMimePart * pkcs7,
+ GMimeObject *g_mime_application_pkcs7_decrypt_verify(GMimePart * pkcs7,
+ GMimeGpgmeSigstat
+ ** signature,
+ GtkWindow *
+ parent,
+ GError ** err);
+ int g_mime_application_pkcs7_encrypt(GMimePart * pkcs7,
GMimeObject * content,
-#ifndef HAVE_GMIME_2_5_7
- GMimeCipherContext * ctx,
-#else /* HAVE_GMIME_2_5_7 */
- GMimeCryptoContext * ctx,
-#endif /* HAVE_GMIME_2_5_7 */
- const char *userid, GError ** err);
-#endif
-
-#ifndef HAVE_GMIME_2_5_7
-GMimeObject *g_mime_application_pkcs7_verify(GMimePart * pkcs7,
- GMimeSignatureValidity ** validity,
- GMimeCipherContext * ctx, GError ** err);
-#else /* HAVE_GMIME_2_5_7 */
-GMimeObject *g_mime_application_pkcs7_verify(GMimePart * pkcs7,
- GMimeSignatureList ** validity,
- GMimeCryptoContext * ctx, GError ** err);
-#endif /* HAVE_GMIME_2_5_7 */
-
-int g_mime_application_pkcs7_encrypt(GMimePart * pkcs7,
- GMimeObject * content,
-#ifndef HAVE_GMIME_2_5_7
- GMimeCipherContext * ctx,
-#else /* HAVE_GMIME_2_5_7 */
- GMimeCryptoContext * ctx,
-#endif /* HAVE_GMIME_2_5_7 */
- GPtrArray * recipients, GError ** err);
-
-#ifndef HAVE_GMIME_2_5_7
-GMimeObject *g_mime_application_pkcs7_decrypt(GMimePart * pkcs7,
- GMimeCipherContext * ctx, GError ** err);
-#else /* HAVE_GMIME_2_5_7 */
-GMimeObject *g_mime_application_pkcs7_decrypt(GMimePart * pkcs7,
- GMimeCryptoContext * ctx, GError ** err);
-#endif /* HAVE_GMIME_2_5_7 */
+ GPtrArray * recipients,
+ gboolean trust_all,
+ GtkWindow * parent,
+ GError ** err);
#ifdef __cplusplus
}
diff --git a/libbalsa/gmime-gpgme-signature.c b/libbalsa/gmime-gpgme-signature.c
index d78441b..b1b67d0 100644
--- a/libbalsa/gmime-gpgme-signature.c
+++ b/libbalsa/gmime-gpgme-signature.c
@@ -1,7 +1,7 @@
/* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
/*
* gmime/gpgme glue layer library
- * Copyright (C) 2004 Albrecht Dreß<albrecht dress arcor de>
+ * Copyright (C) 2004-2011 Albrecht Dreà <albrecht dress arcor de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -23,10 +23,11 @@
# include "config.h"
#endif /* HAVE_CONFIG_H */
+#include <ctype.h>
#include <gpgme.h>
#include <string.h>
#include <glib.h>
-#include "gmime-gpgme-context.h"
+#include "libbalsa-gpgme.h"
#include "gmime-gpgme-signature.h"
@@ -37,7 +38,6 @@ static void g_mime_gpgme_sigstat_class_init(GMimeGpgmeSigstatClass *
klass);
static void g_mime_gpgme_sigstat_finalize(GMimeGpgmeSigstat * self);
static void g_mime_gpgme_sigstat_init(GMimeGpgmeSigstat * self);
-static gchar *fix_EMail_info(gchar * str);
/* GMimeGpgmeSigstat related stuff */
@@ -83,9 +83,6 @@ g_mime_gpgme_sigstat_new_from_gpgme_ctx(gpgme_ctx_t ctx)
{
GMimeGpgmeSigstat *sig_stat;
gpgme_verify_result_t result;
- gpgme_key_t key;
- gpgme_subkey_t subkey;
- gpgme_user_id_t uid;
gpgme_error_t err;
g_return_val_if_fail(ctx, NULL);
@@ -104,53 +101,14 @@ g_mime_gpgme_sigstat_new_from_gpgme_ctx(gpgme_ctx_t ctx)
sig_stat->fingerprint = g_strdup(result->signatures->fpr);
sig_stat->sign_time = result->signatures->timestamp;
sig_stat->status = gpgme_err_code(result->signatures->status);
+ sig_stat->validity = result->signatures->validity;
/* try to get the related key */
- err = gpgme_get_key(ctx, sig_stat->fingerprint, &key, 0);
- if (err != GPG_ERR_NO_ERROR) {
+ err = gpgme_get_key(ctx, sig_stat->fingerprint, &sig_stat->key, 0);
+ if (err != GPG_ERR_NO_ERROR)
g_message("could not retrieve the key with fingerprint %s: %s: %s",
sig_stat->fingerprint, gpgme_strsource(err),
gpgme_strerror(err));
- return sig_stat;
- }
- if (key == NULL)
- return sig_stat;
-
- /* the key is available */
- sig_stat->protocol = key->protocol;
- sig_stat->issuer_serial = g_strdup(key->issuer_serial);
- sig_stat->issuer_name = g_strdup(key->issuer_name);
- sig_stat->chain_id = g_strdup(key->chain_id);
- sig_stat->trust = key->owner_trust;
- uid = key->uids;
-
- /* Note: there is no way to determine which user id has been used to
- * create the signature. We therefore pick the validity of the primary
- * one and scan uid's to get useable name, email and uid strings */
- sig_stat->validity = uid->validity;
- while (uid) {
- if (!sig_stat->sign_name && uid->name && *uid->name)
- sig_stat->sign_name = g_strdup(uid->name);
- if (!sig_stat->sign_email && uid->email && *uid->email)
- sig_stat->sign_email = g_strdup(uid->email);
- if (!sig_stat->sign_uid && uid->uid && *uid->uid)
- sig_stat->sign_uid = fix_EMail_info(g_strdup(uid->uid));
- uid = uid->next;
- }
-
- /* get the subkey which can sign */
- subkey = key->subkeys;
- while (subkey && !subkey->can_sign)
- subkey = subkey->next;
- if (subkey) {
- sig_stat->key_created = subkey->timestamp;
- sig_stat->key_expires = subkey->expires;
- sig_stat->key_revoked = subkey->revoked;
- sig_stat->key_expired = subkey->expired;
- sig_stat->key_disabled = subkey->disabled;
- sig_stat->key_invalid = subkey->invalid;
- }
- gpgme_key_unref(key);
return sig_stat;
}
@@ -166,84 +124,107 @@ g_mime_gpgme_sigstat_class_init(GMimeGpgmeSigstatClass * klass)
(GObjectFinalizeFunc) g_mime_gpgme_sigstat_finalize;
}
-
static void
g_mime_gpgme_sigstat_finalize(GMimeGpgmeSigstat * self)
{
- g_free(self->sign_name);
- self->sign_name = NULL;
- g_free(self->sign_email);
- self->sign_email = NULL;
g_free(self->fingerprint);
self->fingerprint = NULL;
- g_free(self->sign_uid);
- self->sign_uid = NULL;
- g_free(self->issuer_serial);
- self->issuer_serial = NULL;
- g_free(self->issuer_name);
- self->issuer_name = NULL;
- g_free(self->chain_id);
- self->chain_id = NULL;
+ if (self->key)
+ gpgme_key_unref(self->key);
+ self->key = NULL;
g_mime_gpgme_sigstat_parent_class->finalize(G_OBJECT(self));
}
-
-static void
-g_mime_gpgme_sigstat_init(GMimeGpgmeSigstat * self)
+static gchar *
+hex_decode(const gchar *hexstr)
{
- self->status = GPG_ERR_NOT_SIGNED;
- self->sign_name = NULL;
- self->sign_email = NULL;
- self->fingerprint = NULL;
- self->sign_uid = NULL;
- self->issuer_serial = NULL;
- self->issuer_name = NULL;
- self->chain_id = NULL;
+ gchar *result;
+ gchar *outp;
+ int inlen;
+
+ inlen = strlen(hexstr);
+ if ((inlen & 1) == 1)
+ return g_strdup(hexstr);
+
+ result = g_new0(gchar, (inlen >> 1) + 1);
+ for (outp = result; *hexstr; outp++) {
+ if (isdigit(*hexstr))
+ *outp = (*hexstr - '0') << 4;
+ else
+ *outp = (toupper(*hexstr) -'A' + 10) << 4;
+ hexstr++;
+ if (isdigit(*hexstr))
+ *outp |= *hexstr - '0';
+ else
+ *outp |= toupper(*hexstr) -'A' + 10;
+ hexstr++;
+ }
+ return result;
}
-
/*
- * Change an EMail field in a S/MIME certificate to human-readable text.
+ * Change some fields in a S/MIME certificate to human-readable text.
* Note: doesn't do any sophisticated error-checking...
*/
-static gchar *
-fix_EMail_info(gchar * str)
+gchar *
+libbalsa_cert_subject_readable(const gchar *subject)
{
- gchar *p = strstr(str, "1.2.840.113549.1.9.1=#");
+ const struct {
+ gchar *ldap_id;
+ gchar *readable;
+ } ldap_id_list[] = {
+ { .ldap_id = "2.5.4.4", .readable = "sn" },
+ { .ldap_id = "2.5.4.5", .readable = "serialNumber" },
+ { .ldap_id = "2.5.4.42", .readable = "givenName" },
+ { .ldap_id = "1.2.840.113549.1.9.1", .readable = "email" },
+ { .ldap_id = NULL, .readable = NULL }
+ }, *ldap_elem;
+ gchar **elements;
+ gint n;
GString *result;
- if (!p)
- return str;
-
- *p = '\0';
- p += 22;
- result = g_string_new(str);
- result = g_string_append(result, "EMail=");
- while (*p != '\0' && *p != ',') {
- gchar x = 0;
-
- if (*p >= 'A' && *p <= 'F')
- x = (*p - 'A' + 10) << 4;
- else if (*p >= 'a' && *p <= 'f')
- x = (*p - 'a' + 10) << 4;
- else if (*p >= '0' && *p <= '9')
- x = (*p - '0') << 4;
- p++;
- if (*p != '\0' && *p != ',') {
- if (*p >= 'A' && *p <= 'F')
- x += *p - 'A' + 10;
- else if (*p >= 'a' && *p <= 'f')
- x += *p - 'a' + 10;
- else if (*p >= '0' && *p <= '9')
- x += *p - '0';
- p++;
- }
- result = g_string_append_c(result, x);
+ if (!subject)
+ return NULL;
+
+ result = g_string_new(NULL);
+ elements = g_strsplit(subject, ",", -1);
+ for (n = 0; elements[n]; n++) {
+ gchar *equals;
+
+ equals = strchr(elements[n], '=');
+ if (equals) {
+ *equals++ = '\0';
+ for (ldap_elem = ldap_id_list;
+ ldap_elem->ldap_id && strcmp(ldap_elem->ldap_id, elements[n]);
+ ldap_elem++);
+ if (ldap_elem->ldap_id)
+ result = g_string_append(result, ldap_elem->readable);
+ else
+ result = g_string_append(result, elements[n]);
+ result = g_string_append_c(result, '=');
+
+ if (*equals == '#') {
+ gchar *decoded;
+
+ decoded = hex_decode(equals + 1);
+ result = g_string_append(result, decoded);
+ g_free(decoded);
+ } else
+ result = g_string_append(result, equals);
+ } else
+ result = g_string_append(result, elements[n]);
+ if (elements[n + 1])
+ result = g_string_append_c(result, ',');
}
- result = g_string_append(result, p);
- g_free(str);
- p = result->str;
- g_string_free(result, FALSE);
- return p;
+ g_strfreev(elements);
+ return g_string_free(result, FALSE);
+}
+
+static void
+g_mime_gpgme_sigstat_init(GMimeGpgmeSigstat * self)
+{
+ self->status = GPG_ERR_NOT_SIGNED;
+ self->key = NULL;
+ self->fingerprint = NULL;
}
diff --git a/libbalsa/gmime-gpgme-signature.h b/libbalsa/gmime-gpgme-signature.h
index 73a2dfa..f036af9 100644
--- a/libbalsa/gmime-gpgme-signature.h
+++ b/libbalsa/gmime-gpgme-signature.h
@@ -1,7 +1,7 @@
/* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
/*
* gmime/gpgme glue layer library
- * Copyright (C) 2004 Albrecht Dreß<albrecht dress arcor de>
+ * Copyright (C) 2004-2011 Albrecht Dreà <albrecht dress arcor de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -22,7 +22,7 @@
#define __GMIME_GPGME_SIGNATURE_H__
#include <gpgme.h>
-#include <glib-object.h>
+#include <glib.h>
#ifdef __cplusplus
@@ -43,30 +43,20 @@ extern "C" {
typedef struct _GMimeGpgmeSigstat GMimeGpgmeSigstat;
typedef struct _GMimeGpgmeSigstatClass GMimeGpgmeSigstatClass;
-
+typedef struct _sig_uid_t sig_uid_t;
struct _GMimeGpgmeSigstat {
GObject parent;
- /* various data gathered by gpgme's verify */
+ /* results form gpgme's verify operation */
gpgme_protocol_t protocol;
gpgme_error_t status;
gpgme_validity_t validity;
- gpgme_validity_t trust;
- gchar *sign_name;
- gchar *sign_email;
gchar *fingerprint;
- gchar *sign_uid;
- gchar *issuer_serial;
- gchar *issuer_name;
- gchar *chain_id;
- time_t key_created;
- time_t key_expires;
- gboolean key_revoked;
- gboolean key_expired;
- gboolean key_disabled;
- gboolean key_invalid;
time_t sign_time;
+
+ /* information about the key used to create the signature */
+ gpgme_key_t key;
};
struct _GMimeGpgmeSigstatClass {
@@ -79,6 +69,9 @@ GMimeGpgmeSigstat *g_mime_gpgme_sigstat_new(void);
GMimeGpgmeSigstat *g_mime_gpgme_sigstat_new_from_gpgme_ctx(gpgme_ctx_t
ctx);
+gchar *libbalsa_cert_subject_readable(const gchar *subject);
+
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/libbalsa/gmime-multipart-crypt.c b/libbalsa/gmime-multipart-crypt.c
new file mode 100644
index 0000000..17b59fd
--- /dev/null
+++ b/libbalsa/gmime-multipart-crypt.c
@@ -0,0 +1,576 @@
+/* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
+/*
+ * gmime/gpgme implementation for multipart/signed and multipart/encrypted
+ * Copyright (C) 2011 Albrecht Dreà <albrecht dress arcor de>
+ *
+ * The functions in this module were copied from the original GMime
+ * implementation of multipart/signed and multipart/encrypted parts.
+ * However, instead of using the complex GMime crypto contexts (which have
+ * a varying API over the different versions), this module directly calls
+ * the GpgME backend functions implemented in libbalsa-gpgme.[hc].
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <unistd.h>
+#include <glib/gi18n.h>
+#include <glib.h>
+#include <gtk/gtk.h>
+#include "libbalsa-gpgme.h"
+#include "gmime-multipart-crypt.h"
+
+
+/**
+ * sign_prepare:
+ * @mime_part: MIME part
+ *
+ * Prepare a part (and all subparts) to be signed. To do this we need
+ * to set the encoding of all parts (that are not already encoded to
+ * either QP or Base64) to QP.
+ **/
+static void
+sign_prepare(GMimeObject * mime_part)
+{
+ GMimeContentEncoding encoding;
+ GMimeMultipart *multipart;
+ GMimeObject *subpart;
+ int i, n;
+
+ if (GMIME_IS_MULTIPART(mime_part)) {
+ multipart = (GMimeMultipart *) mime_part;
+
+ if (GMIME_IS_MULTIPART_SIGNED(multipart) ||
+ GMIME_IS_MULTIPART_ENCRYPTED(multipart)) {
+ /* must not modify these parts as they must be treated as opaque */
+ return;
+ }
+
+ n = g_mime_multipart_get_count(multipart);
+ for (i = 0; i < n; i++) {
+ subpart = g_mime_multipart_get_part(multipart, i);
+ sign_prepare(subpart);
+ }
+ } else if (GMIME_IS_MESSAGE_PART(mime_part)) {
+ subpart = GMIME_MESSAGE_PART(mime_part)->message->mime_part;
+ sign_prepare(subpart);
+ } else {
+ encoding = g_mime_part_get_content_encoding(GMIME_PART(mime_part));
+ if (encoding != GMIME_CONTENT_ENCODING_BASE64)
+ g_mime_part_set_content_encoding(GMIME_PART(mime_part),
+ GMIME_CONTENT_ENCODING_QUOTEDPRINTABLE);
+ }
+}
+
+
+int
+g_mime_gpgme_mps_sign(GMimeMultipartSigned * mps, GMimeObject * content,
+ const gchar * userid, gpgme_protocol_t protocol,
+ GtkWindow * parent, GError ** err)
+{
+ GMimeStream *stream;
+ GMimeStream *filtered;
+ GMimeStream *sigstream;
+ GMimeFilter *filter;
+ GMimeContentType *content_type;
+ GMimeDataWrapper *wrapper;
+ GMimeParser *parser;
+ GMimePart *signature;
+ const gchar *sig_type;
+ gpgme_hash_algo_t hash_algo;
+
+ g_return_val_if_fail(GMIME_IS_MULTIPART_SIGNED(mps), -1);
+ g_return_val_if_fail(GMIME_IS_OBJECT(content), -1);
+
+ /* Prepare all the parts for signing... */
+ sign_prepare(content);
+
+ /* get the cleartext */
+ stream = g_mime_stream_mem_new();
+ filtered = g_mime_stream_filter_new(stream);
+
+ /* Note: see rfc3156, section 3 - second note */
+ filter = g_mime_filter_from_new(GMIME_FILTER_FROM_MODE_ARMOR);
+ g_mime_stream_filter_add(GMIME_STREAM_FILTER(filtered), filter);
+ g_object_unref(filter);
+
+ /* Note: see rfc3156, section 5.4 (this is the main difference between rfc2015 and rfc3156) */
+ filter = g_mime_filter_strip_new();
+ g_mime_stream_filter_add(GMIME_STREAM_FILTER(filtered), filter);
+ g_object_unref(filter);
+
+ g_mime_object_write_to_stream(content, filtered);
+ g_mime_stream_flush(filtered);
+ g_object_unref(filtered);
+ g_mime_stream_reset(stream);
+
+ /* Note: see rfc2015 or rfc3156, section 5.1 */
+ filtered = g_mime_stream_filter_new(stream);
+ filter = g_mime_filter_crlf_new(TRUE, FALSE);
+ g_mime_stream_filter_add(GMIME_STREAM_FILTER(filtered), filter);
+ g_object_unref(filter);
+
+ /* construct the signature stream */
+ sigstream = g_mime_stream_mem_new();
+
+ /* sign the content stream */
+ hash_algo =
+ libbalsa_gpgme_sign(userid, filtered, sigstream, protocol, FALSE,
+ parent, err);
+ if (hash_algo == GPGME_MD_NONE) {
+ g_object_unref(sigstream);
+ g_object_unref(filtered);
+ g_object_unref(stream);
+ return -1;
+ }
+
+ g_object_unref(filtered);
+ g_mime_stream_reset(sigstream);
+ g_mime_stream_reset(stream);
+
+ /* set the multipart/signed protocol and micalg */
+ content_type = g_mime_object_get_content_type(GMIME_OBJECT(mps));
+ if (protocol == GPGME_PROTOCOL_OpenPGP) {
+ gchar *micalg;
+
+ micalg =
+ g_strdup_printf("PGP-%s", gpgme_hash_algo_name(hash_algo));
+ g_mime_content_type_set_parameter(content_type, "micalg", micalg);
+ g_free(micalg);
+ sig_type = "application/pgp-signature";
+ } else {
+ g_mime_content_type_set_parameter(content_type, "micalg",
+ gpgme_hash_algo_name(hash_algo));
+ sig_type = "application/pkcs7-signature";
+ }
+ g_mime_content_type_set_parameter(content_type, "protocol", sig_type);
+ g_mime_multipart_set_boundary(GMIME_MULTIPART(mps), NULL);
+
+ /* construct the content part */
+ parser = g_mime_parser_new_with_stream(stream);
+ content = g_mime_parser_construct_part(parser);
+ g_object_unref(stream);
+ g_object_unref(parser);
+
+ /* construct the signature part */
+ content_type = g_mime_content_type_new_from_string(sig_type);
+ signature =
+ g_mime_part_new_with_type(content_type->type,
+ content_type->subtype);
+ g_object_unref(content_type);
+
+ wrapper = g_mime_data_wrapper_new();
+ g_mime_data_wrapper_set_stream(wrapper, sigstream);
+ g_mime_part_set_content_object(signature, wrapper);
+ g_object_unref(sigstream);
+ g_object_unref(wrapper);
+
+ /* FIXME: temporary hack, this info should probably be set in
+ * the CipherContext class - maybe ::sign can take/output a
+ * GMimePart instead. */
+ if (protocol == GPGME_PROTOCOL_CMS) {
+ g_mime_part_set_content_encoding(signature,
+ GMIME_CONTENT_ENCODING_BASE64);
+ g_mime_part_set_filename(signature, "smime.p7m");
+ }
+
+ /* save the content and signature parts */
+ /* FIXME: make sure there aren't any other parts?? */
+ g_mime_multipart_add(GMIME_MULTIPART(mps), content);
+ g_mime_multipart_add(GMIME_MULTIPART(mps), (GMimeObject *) signature);
+ g_object_unref(signature);
+ g_object_unref(content);
+
+ return 0;
+}
+
+
+GMimeGpgmeSigstat *
+g_mime_gpgme_mps_verify(GMimeMultipartSigned * mps, GError ** error)
+{
+ const gchar *protocol;
+ gpgme_protocol_t crypto_prot;
+ gchar *content_type;
+ GMimeObject *content;
+ GMimeObject *signature;
+ GMimeStream *stream;
+ GMimeStream *filtered_stream;
+ GMimeFilter *crlf_filter;
+ GMimeDataWrapper *wrapper;
+ GMimeStream *sigstream;
+ GMimeGpgmeSigstat *result;
+
+ g_return_val_if_fail(GMIME_IS_MULTIPART_SIGNED(mps), NULL);
+
+ if (g_mime_multipart_get_count((GMimeMultipart *) mps) < 2) {
+ g_set_error(error, GMIME_ERROR, GMIME_ERROR_PARSE_ERROR, "%s",
+ _
+ ("Cannot verify multipart/signed part due to missing subparts."));
+ return NULL;
+ }
+
+ /* grab the protocol so we can configure the GpgME context */
+ protocol =
+ g_mime_object_get_content_type_parameter(GMIME_OBJECT(mps),
+ "protocol");
+ if (protocol) {
+ if (g_ascii_strcasecmp("application/pgp-signature", protocol) == 0)
+ crypto_prot = GPGME_PROTOCOL_OpenPGP;
+#if defined(HAVE_SMIME)
+ else if (g_ascii_strcasecmp
+ ("application/pkcs7-signature", protocol) == 0
+ || g_ascii_strcasecmp("application/x-pkcs7-signature",
+ protocol) == 0)
+ crypto_prot = GPGME_PROTOCOL_CMS;
+#endif
+ else
+ crypto_prot = GPGME_PROTOCOL_UNKNOWN;
+ } else
+ crypto_prot = GPGME_PROTOCOL_UNKNOWN;
+
+ /* eject on unknown protocols */
+ if (crypto_prot == GPGME_PROTOCOL_UNKNOWN) {
+ g_set_error(error, GPGME_ERROR_QUARK, GPG_ERR_INV_VALUE,
+ _("unsupported protocol '%s'"), protocol);
+ return NULL;
+ }
+
+ signature =
+ g_mime_multipart_get_part(GMIME_MULTIPART(mps),
+ GMIME_MULTIPART_SIGNED_SIGNATURE);
+
+ /* make sure the protocol matches the signature content-type */
+ content_type = g_mime_content_type_to_string(signature->content_type);
+ if (g_ascii_strcasecmp(content_type, protocol) != 0) {
+ g_set_error(error, GMIME_ERROR, GMIME_ERROR_PARSE_ERROR, "%s",
+ _
+ ("Cannot verify multipart/signed part: signature content-type does not match protocol."));
+ g_free(content_type);
+ return NULL;
+ }
+ g_free(content_type);
+
+ content =
+ g_mime_multipart_get_part(GMIME_MULTIPART(mps),
+ GMIME_MULTIPART_SIGNED_CONTENT);
+
+ /* get the content stream */
+ stream = g_mime_stream_mem_new();
+ filtered_stream = g_mime_stream_filter_new(stream);
+
+ /* Note: see rfc2015 or rfc3156, section 5.1 */
+ crlf_filter = g_mime_filter_crlf_new(TRUE, FALSE);
+ g_mime_stream_filter_add(GMIME_STREAM_FILTER(filtered_stream),
+ crlf_filter);
+ g_object_unref(crlf_filter);
+
+ g_mime_object_write_to_stream(content, filtered_stream);
+ g_mime_stream_flush(filtered_stream);
+ g_object_unref(filtered_stream);
+ g_mime_stream_reset(stream);
+
+ /* get the signature stream */
+ wrapper = g_mime_part_get_content_object(GMIME_PART(signature));
+
+ /* FIXME: temporary hack for Balsa to support S/MIME,
+ * ::verify() should probably take a mime part so it can
+ * decode this itself if it needs to. */
+ if (crypto_prot == GPGME_PROTOCOL_CMS) {
+ sigstream = g_mime_stream_mem_new();
+ g_mime_data_wrapper_write_to_stream(wrapper, sigstream);
+ } else {
+ sigstream = g_mime_data_wrapper_get_stream(wrapper);
+ }
+
+ g_mime_stream_reset(sigstream);
+
+ /* verify the signature */
+ result =
+ libbalsa_gpgme_verify(stream, sigstream, crypto_prot, FALSE,
+ error);
+ g_object_unref(stream);
+
+ return result;
+}
+
+
+int
+g_mime_gpgme_mpe_encrypt(GMimeMultipartEncrypted * mpe,
+ GMimeObject * content, GPtrArray * recipients,
+ gboolean trust_all, GtkWindow * parent,
+ GError ** err)
+{
+ GMimeStream *filtered_stream;
+ GMimeStream *ciphertext;
+ GMimeStream *stream;
+ GMimePart *version_part;
+ GMimePart *encrypted_part;
+ GMimeContentType *content_type;
+ GMimeDataWrapper *wrapper;
+ GMimeFilter *crlf_filter;
+
+ g_return_val_if_fail(GMIME_IS_MULTIPART_ENCRYPTED(mpe), -1);
+ g_return_val_if_fail(GMIME_IS_OBJECT(content), -1);
+
+ /* get the cleartext */
+ stream = g_mime_stream_mem_new();
+ filtered_stream = g_mime_stream_filter_new(stream);
+
+ crlf_filter = g_mime_filter_crlf_new(TRUE, FALSE);
+ g_mime_stream_filter_add(GMIME_STREAM_FILTER(filtered_stream),
+ crlf_filter);
+ g_object_unref(crlf_filter);
+
+ g_mime_object_write_to_stream(content, filtered_stream);
+ g_mime_stream_flush(filtered_stream);
+ g_object_unref(filtered_stream);
+
+ /* reset the content stream */
+ g_mime_stream_reset(stream);
+
+ /* encrypt the content stream */
+ ciphertext = g_mime_stream_mem_new();
+ if (libbalsa_gpgme_encrypt
+ (recipients, NULL, stream, ciphertext, GPGME_PROTOCOL_OpenPGP,
+ FALSE, trust_all, parent, err) == -1) {
+ g_object_unref(ciphertext);
+ g_object_unref(stream);
+ return -1;
+ }
+
+ g_object_unref(stream);
+ g_mime_stream_reset(ciphertext);
+
+ /* construct the version part */
+ content_type =
+ g_mime_content_type_new_from_string("application/pgp-encrypted");
+ version_part =
+ g_mime_part_new_with_type(content_type->type,
+ content_type->subtype);
+ g_object_unref(content_type);
+
+ content_type =
+ g_mime_content_type_new_from_string("application/pgp-encrypted");
+ g_mime_object_set_content_type(GMIME_OBJECT(version_part),
+ content_type);
+ g_mime_part_set_content_encoding(version_part,
+ GMIME_CONTENT_ENCODING_7BIT);
+ stream =
+ g_mime_stream_mem_new_with_buffer("Version: 1\n",
+ strlen("Version: 1\n"));
+ wrapper =
+ g_mime_data_wrapper_new_with_stream(stream,
+ GMIME_CONTENT_ENCODING_7BIT);
+ g_mime_part_set_content_object(version_part, wrapper);
+ g_object_unref(wrapper);
+ g_object_unref(stream);
+
+#if !defined(HAVE_GMIME_2_6)
+ mpe->decrypted = content;
+ g_object_ref(content);
+#endif
+
+ /* construct the encrypted mime part */
+ encrypted_part =
+ g_mime_part_new_with_type("application", "octet-stream");
+ g_mime_part_set_content_encoding(encrypted_part,
+ GMIME_CONTENT_ENCODING_7BIT);
+ wrapper =
+ g_mime_data_wrapper_new_with_stream(ciphertext,
+ GMIME_CONTENT_ENCODING_7BIT);
+ g_mime_part_set_content_object(encrypted_part, wrapper);
+ g_object_unref(ciphertext);
+ g_object_unref(wrapper);
+
+ /* save the version and encrypted parts */
+ /* FIXME: make sure there aren't any other parts?? */
+ g_mime_multipart_add(GMIME_MULTIPART(mpe), GMIME_OBJECT(version_part));
+ g_mime_multipart_add(GMIME_MULTIPART(mpe),
+ GMIME_OBJECT(encrypted_part));
+ g_object_unref(encrypted_part);
+ g_object_unref(version_part);
+
+ /* set the content-type params for this multipart/encrypted part */
+ g_mime_object_set_content_type_parameter(GMIME_OBJECT(mpe), "protocol",
+ "application/pgp-encrypted");
+ g_mime_multipart_set_boundary(GMIME_MULTIPART(mpe), NULL);
+
+ return 0;
+}
+
+
+static GMimeStream *
+g_mime_data_wrapper_get_decoded_stream(GMimeDataWrapper * wrapper)
+{
+ GMimeStream *decoded_stream;
+ GMimeFilter *decoder;
+
+ g_mime_stream_reset(wrapper->stream);
+
+ switch (wrapper->encoding) {
+ case GMIME_CONTENT_ENCODING_BASE64:
+ case GMIME_CONTENT_ENCODING_QUOTEDPRINTABLE:
+ case GMIME_CONTENT_ENCODING_UUENCODE:
+ decoder = g_mime_filter_basic_new(wrapper->encoding, FALSE);
+ decoded_stream = g_mime_stream_filter_new(wrapper->stream);
+ g_mime_stream_filter_add(GMIME_STREAM_FILTER(decoded_stream),
+ decoder);
+ g_object_unref(decoder);
+ break;
+ default:
+ decoded_stream = wrapper->stream;
+ g_object_ref(wrapper->stream);
+ break;
+ }
+
+ return decoded_stream;
+}
+
+
+GMimeObject *
+g_mime_gpgme_mpe_decrypt(GMimeMultipartEncrypted * mpe,
+ GMimeGpgmeSigstat ** signature,
+ GtkWindow * parent, GError ** err)
+{
+ GMimeObject *decrypted, *version, *encrypted;
+ GMimeStream *stream, *ciphertext;
+ GMimeStream *filtered_stream;
+ GMimeContentType *mime_type;
+ GMimeGpgmeSigstat *sigstat;
+ GMimeDataWrapper *wrapper;
+ GMimeFilter *crlf_filter;
+ GMimeParser *parser;
+ const char *protocol;
+ char *content_type;
+
+ g_return_val_if_fail(GMIME_IS_MULTIPART_ENCRYPTED(mpe), NULL);
+
+#if !defined(HAVE_GMIME_2_6)
+ if (mpe->decrypted) {
+ /* we seem to have already decrypted the part */
+ return mpe->decrypted;
+ }
+#endif
+
+ if (signature && *signature) {
+ g_object_unref(G_OBJECT(*signature));
+ *signature = NULL;
+ }
+
+ protocol =
+ g_mime_object_get_content_type_parameter(GMIME_OBJECT(mpe),
+ "protocol");
+
+ /* make sure the protocol is present and matches the cipher encrypt protocol */
+ if (!protocol
+ || g_ascii_strcasecmp("application/pgp-encrypted",
+ protocol) != 0) {
+ g_set_error(err, GMIME_ERROR, GMIME_ERROR_PROTOCOL_ERROR,
+ _
+ ("Cannot decrypt multipart/encrypted part: unsupported encryption protocol '%s'."),
+ protocol ? protocol : _("(none)"));
+ return NULL;
+ }
+
+ version =
+ g_mime_multipart_get_part(GMIME_MULTIPART(mpe),
+ GMIME_MULTIPART_ENCRYPTED_VERSION);
+
+ /* make sure the protocol matches the version part's content-type */
+ content_type = g_mime_content_type_to_string(version->content_type);
+ if (g_ascii_strcasecmp(content_type, protocol) != 0) {
+ g_set_error(err, GMIME_ERROR, GMIME_ERROR_PROTOCOL_ERROR, "%s",
+ _
+ ("Cannot decrypt multipart/encrypted part: content-type does not match protocol."));
+ g_free(content_type);
+ return NULL;
+ }
+ g_free(content_type);
+
+ /* get the encrypted part and check that it is of type application/octet-stream */
+ encrypted =
+ g_mime_multipart_get_part(GMIME_MULTIPART(mpe),
+ GMIME_MULTIPART_ENCRYPTED_CONTENT);
+ mime_type = g_mime_object_get_content_type(encrypted);
+ if (!g_mime_content_type_is_type
+ (mime_type, "application", "octet-stream")) {
+ g_set_error(err, GMIME_ERROR, GMIME_ERROR_PROTOCOL_ERROR, "%s",
+ _
+ ("Cannot decrypt multipart/encrypted part: unexpected content type"));
+ return NULL;
+ }
+
+ /* get the ciphertext stream */
+ wrapper = g_mime_part_get_content_object(GMIME_PART(encrypted));
+ ciphertext = g_mime_data_wrapper_get_decoded_stream(wrapper);
+ g_mime_stream_reset(ciphertext);
+
+ stream = g_mime_stream_mem_new();
+ filtered_stream = g_mime_stream_filter_new(stream);
+ crlf_filter = g_mime_filter_crlf_new(FALSE, FALSE);
+ g_mime_stream_filter_add(GMIME_STREAM_FILTER(filtered_stream),
+ crlf_filter);
+ g_object_unref(crlf_filter);
+
+ /* get the cleartext */
+ sigstat =
+ libbalsa_gpgme_decrypt(ciphertext, filtered_stream,
+ GPGME_PROTOCOL_OpenPGP, parent, err);
+ if (!sigstat) {
+ g_object_unref(filtered_stream);
+ g_object_unref(ciphertext);
+ g_object_unref(stream);
+ return NULL;
+ }
+
+ g_mime_stream_flush(filtered_stream);
+ g_object_unref(filtered_stream);
+ g_object_unref(ciphertext);
+
+ g_mime_stream_reset(stream);
+ parser = g_mime_parser_new();
+ g_mime_parser_init_with_stream(parser, stream);
+ g_object_unref(stream);
+
+ decrypted = g_mime_parser_construct_part(parser);
+ g_object_unref(parser);
+
+ if (!decrypted) {
+ g_set_error(err, GMIME_ERROR, GMIME_ERROR_PARSE_ERROR, "%s",
+ _
+ ("Cannot decrypt multipart/encrypted part: failed to parse decrypted content"));
+ g_object_unref(G_OBJECT(sigstat));
+ return NULL;
+ }
+
+
+ /* cache the decrypted part */
+#if !defined(HAVE_GMIME_2_6)
+ mpe->decrypted = decrypted;
+#endif
+ if (signature) {
+ if (sigstat->status != GPG_ERR_NOT_SIGNED)
+ *signature = sigstat;
+ else
+ g_object_unref(G_OBJECT(sigstat));
+ }
+
+ return decrypted;
+}
diff --git a/libbalsa/gmime-multipart-crypt.h b/libbalsa/gmime-multipart-crypt.h
new file mode 100644
index 0000000..f7cc428
--- /dev/null
+++ b/libbalsa/gmime-multipart-crypt.h
@@ -0,0 +1,69 @@
+/* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
+/*
+ * gmime/gpgme implementation for multipart/signed and multipart/encrypted
+ * Copyright (C) 2011 Albrecht Dreà <albrecht dress arcor de>
+ *
+ * The functions in this module were copied from the original GMime
+ * implementation of multipart/signed and multipart/encrypted parts.
+ * However, instead of using the complex GMime crypto contexts (which have
+ * a varying API over the different versions), this module directly calls
+ * the GpgME backend functions implemented in libbalsa-gpgme.[hc].
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef __GMIME_MULTIPART_CRYPT_H__
+#define __GMIME_MULTIPART_CRYPT_H__
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <gmime/gmime.h>
+#include <gpgme.h>
+#include "gmime-gpgme-signature.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#ifdef MAKE_EMACS_HAPPY
+}
+#endif
+#endif /* __cplusplus */
+
+
+GMimeGpgmeSigstat *g_mime_gpgme_mps_verify(GMimeMultipartSigned * mps,
+ GError ** error);
+int g_mime_gpgme_mps_sign(GMimeMultipartSigned * mps,
+ GMimeObject * content, const gchar * userid,
+ gpgme_protocol_t protocol, GtkWindow * parent,
+ GError ** err);
+int g_mime_gpgme_mpe_encrypt(GMimeMultipartEncrypted * mpe,
+ GMimeObject * content, GPtrArray * recipients,
+ gboolean trust_all, GtkWindow * parent,
+ GError ** err);
+GMimeObject *g_mime_gpgme_mpe_decrypt(GMimeMultipartEncrypted * mpe,
+ GMimeGpgmeSigstat ** signature,
+ GtkWindow * parent, GError ** err);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __GMIME_MULTIPART_CRYPT_H__ */
diff --git a/libbalsa/gmime-part-rfc2440.c b/libbalsa/gmime-part-rfc2440.c
index e79c4cb..4c032c6 100644
--- a/libbalsa/gmime-part-rfc2440.c
+++ b/libbalsa/gmime-part-rfc2440.c
@@ -1,7 +1,7 @@
/* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
/*
- * gmime/gpgme glue layer library
- * Copyright (C) 2004 Albrecht Dreß<albrecht dress arcor de>
+ * gmime/gpgme implementation for RFC2440 parts
+ * Copyright (C) 2004-2011 Albrecht Dreà <albrecht dress arcor de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,21 +20,35 @@
*/
#if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H
-# include "config.h"
+#include "config.h"
#endif /* HAVE_CONFIG_H */
-#include "gmime-part-rfc2440.h"
#include <string.h>
+#include <glib.h>
+#include <gtk/gtk.h>
#include <gmime/gmime.h>
+#include "libbalsa-gpgme.h"
+#include "gmime-part-rfc2440.h"
#define RFC2440_BUF_LEN 4096
+
+/** \brief Check for a part's RFC 2440 protection
+ *
+ * \param part GMime part which shall be inspected.
+ * \return The OpenPGP (RFC 2440) protection of the part.
+ *
+ * Check if the passed part is RFC 2440 signed or encrypted by looking for
+ * the "magic" strings defined there. Note that parts which include extra
+ * data before the beginning or after the end of signed or encryped matter
+ * are always classified as GMIME_PART_RFC2440_NONE.
+ */
GMimePartRfc2440Mode
g_mime_part_check_rfc2440(GMimePart * part)
{
- GMimeDataWrapper * wrapper;
- GMimeStream * stream;
+ GMimeDataWrapper *wrapper;
+ GMimeStream *stream;
ssize_t slen;
gchar buf[RFC2440_BUF_LEN];
GMimePartRfc2440Mode retval = GMIME_PART_RFC2440_NONE;
@@ -58,7 +72,7 @@ g_mime_part_check_rfc2440(GMimePart * part)
strstr(buf, "-----END PGP MESSAGE-----"))
retval = GMIME_PART_RFC2440_ENCRYPTED;
else if (!strncmp(buf, "-----BEGIN PGP SIGNED MESSAGE-----", 34)) {
- gchar * p1, * p2;
+ gchar *p1, *p2;
p1 = strstr(buf, "-----BEGIN PGP SIGNATURE-----");
p2 = strstr(buf, "-----END PGP SIGNATURE-----");
@@ -68,14 +82,15 @@ g_mime_part_check_rfc2440(GMimePart * part)
} else {
/* check if the beginning of the stream matches */
g_mime_stream_read(stream, buf, 34);
- g_mime_stream_seek(stream, 1 - RFC2440_BUF_LEN, GMIME_STREAM_SEEK_END);
+ g_mime_stream_seek(stream, 1 - RFC2440_BUF_LEN,
+ GMIME_STREAM_SEEK_END);
if (!strncmp(buf, "-----BEGIN PGP MESSAGE-----", 27)) {
g_mime_stream_read(stream, buf, RFC2440_BUF_LEN - 1);
buf[RFC2440_BUF_LEN - 1] = '\0';
if (strstr(buf, "-----END PGP MESSAGE-----"))
retval = GMIME_PART_RFC2440_ENCRYPTED;
} else if (!strncmp(buf, "-----BEGIN PGP SIGNED MESSAGE-----", 34)) {
- gchar * p1, * p2;
+ gchar *p1, *p2;
g_mime_stream_read(stream, buf, RFC2440_BUF_LEN - 1);
buf[RFC2440_BUF_LEN - 1] = '\0';
@@ -90,20 +105,30 @@ g_mime_part_check_rfc2440(GMimePart * part)
}
-/*
- * RFC2440 sign, encrypt or sign and encrypt a gmime part using the
- * gmime gpgme context ctx. If sign_userid is not NULL, part will be
- * signed. If recipients is not NULL, encrypt part for recipients. If
- * both are not null, part will be first signed and then
+/** \brief RFC 2440 sign and/or encrypt a GMime part
+ *
+ * \param part GMime part which shall be signed and/or encrypted.
+ * \param sign_userid User ID of the signer, or NULL fo no signature.
+ * \param recipients Array of User ID for which the matter shall be
+ * encrypted using their public keys, or NULL for no encryption.
+ * \param trust_all_keys TRUE if all low-truct keys shall be accepted for
+ * encryption.
+ * \param parent Parent window to be passed to the callback functions.
+ * \param error Filled with error information on error.
+ * \return 0 on success, or -1 on error.
+ *
+ * RFC2440 sign, encrypt or sign and encrypt a gmime part. If sign_userid
+ * is not NULL, part will be signed. If recipients is not NULL, encrypt
+ * part for recipients. If both are not null, part will be both signed and
* encrypted. Returns 0 on success or -1 on fail. If any operation
* failed, an exception will be set on err to provide more
* information.
*/
int
-g_mime_part_rfc2440_sign_encrypt(GMimePart * part,
- GMimeGpgmeContext * ctx,
+g_mime_part_rfc2440_sign_encrypt(GMimePart * part, const char *sign_userid,
GPtrArray * recipients,
- const char *sign_userid, GError ** err)
+ gboolean trust_all, GtkWindow * parent,
+ GError ** err)
{
GMimeDataWrapper *wrapper;
GMimeStream *stream, *cipherstream;
@@ -111,14 +136,6 @@ g_mime_part_rfc2440_sign_encrypt(GMimePart * part,
gint result;
g_return_val_if_fail(GMIME_IS_PART(part), -1);
- g_return_val_if_fail(GMIME_IS_GPGME_CONTEXT(ctx), -1);
-#ifndef HAVE_GMIME_2_5_7
- g_return_val_if_fail(GMIME_CIPHER_CONTEXT(ctx)->sign_protocol != NULL,
- -1);
-#else /* HAVE_GMIME_2_5_7 */
- g_return_val_if_fail(g_mime_crypto_context_get_signature_protocol
- (GMIME_CRYPTO_CONTEXT(ctx)) != NULL, -1);
-#endif /* HAVE_GMIME_2_5_7 */
g_return_val_if_fail(recipients != NULL || sign_userid != NULL, -1);
/* get the raw content */
@@ -133,43 +150,31 @@ g_mime_part_rfc2440_sign_encrypt(GMimePart * part,
g_mime_stream_mem_set_owner(GMIME_STREAM_MEM(cipherstream), TRUE);
/* do the crypto operation */
- ctx->singlepart_mode = TRUE;
- if (recipients == NULL)
- result =
-#ifndef HAVE_GMIME_2_5_7
- g_mime_cipher_context_sign(GMIME_CIPHER_CONTEXT(ctx), sign_userid,
- GMIME_CIPHER_HASH_DEFAULT, stream,
- cipherstream, err);
-#else /* HAVE_GMIME_2_5_7 */
- g_mime_crypto_context_sign(GMIME_CRYPTO_CONTEXT(ctx), sign_userid,
- GMIME_CIPHER_ALGO_DEFAULT, stream,
- cipherstream, err);
-#endif /* HAVE_GMIME_2_5_7 */
+ if (recipients == NULL) {
+ if (libbalsa_gpgme_sign
+ (sign_userid, stream, cipherstream, GPGME_PROTOCOL_OpenPGP,
+ TRUE, parent, err) == GPGME_MD_NONE)
+ result = -1;
else
+ result = 0;
+ } else
result =
-#ifndef HAVE_GMIME_2_5_7
- g_mime_cipher_context_encrypt(GMIME_CIPHER_CONTEXT(ctx),
- sign_userid != NULL, sign_userid,
- recipients, stream, cipherstream, err);
-#else /* HAVE_GMIME_2_5_7 */
- g_mime_crypto_context_encrypt(GMIME_CRYPTO_CONTEXT(ctx),
- sign_userid != NULL, sign_userid,
- GMIME_CIPHER_ALGO_DEFAULT,
- recipients, stream, cipherstream, err);
-#endif /* HAVE_GMIME_2_5_7 */
+ libbalsa_gpgme_encrypt(recipients, sign_userid, stream,
+ cipherstream, GPGME_PROTOCOL_OpenPGP,
+ TRUE, trust_all, parent, err);
if (result == -1) {
g_object_unref(cipherstream);
return -1;
}
- /* add the headers to encrypted ascii armor output: as there is no "insert"
- * method for the byte array, first remove the leading "BEGIN PGP MESSAGE"
- * (27 chars) and prepend it again... */
+ /* add the headers to encrypted ascii armor output: as there is no
+ * "insert" method for the byte array, first remove the leading "BEGIN
+ * PGP MESSAGE" (27 chars) and prepend it again... */
if (recipients && g_mime_object_get_content_type(GMIME_OBJECT(part))) {
const gchar *charset =
g_mime_object_get_content_type_parameter(GMIME_OBJECT(part),
"charset");
- gchar * rfc2440header;
+ gchar *rfc2440header;
rfc2440header =
g_strdup_printf("-----BEGIN PGP MESSAGE-----\nCharset: %s\n"
@@ -189,16 +194,19 @@ g_mime_part_rfc2440_sign_encrypt(GMimePart * part,
/*
* Paranoia: set the encoding of a signed part to quoted-printable (not
* requested by RFC2440, but it's safer...). If we encrypted use 7-bit
- * and set the charset to us-ascii instead, as gpg added it's own armor.
+ * and set the charset to us-ascii instead, as gpg added it's own
+ * armor.
*/
if (recipients == NULL) {
- if (g_mime_part_get_content_encoding(part) != GMIME_CONTENT_ENCODING_BASE64)
+ if (g_mime_part_get_content_encoding(part) !=
+ GMIME_CONTENT_ENCODING_BASE64)
g_mime_part_set_content_encoding(part,
GMIME_CONTENT_ENCODING_QUOTEDPRINTABLE);
g_mime_data_wrapper_set_encoding(wrapper,
GMIME_CONTENT_ENCODING_DEFAULT);
} else {
- g_mime_part_set_content_encoding(part, GMIME_CONTENT_ENCODING_7BIT);
+ g_mime_part_set_content_encoding(part,
+ GMIME_CONTENT_ENCODING_7BIT);
g_mime_data_wrapper_set_encoding(wrapper,
GMIME_CONTENT_ENCODING_7BIT);
g_mime_object_set_content_type_parameter(GMIME_OBJECT(part),
@@ -213,38 +221,27 @@ g_mime_part_rfc2440_sign_encrypt(GMimePart * part,
}
-/*
- * Verify the signature of the RFC 2440 signed part using the gpgme
- * context ctx and return the validity as determined by the crypto
- * routines or NULL on error. In the latter case, an exception will be
- * set on err to provide more information. Upon success, the content
- * of part is replaced by the verified output of the crypto engine.
+/* \brief Verify a RFC 2440 signed part
+ *
+ * \param part GMime part which shall be verified.
+ * \param error Filled with error information on error.
+ * \return A new signature status object on success, or NULL on error.
+ *
+ * Verify the signature of the RFC 2440 signed part and return the
+ * signature status as determined by the crypto routines or NULL on error.
+ * In the latter case, an exception will be set on err to provide more
+ * information. Upon success, the content of part is replaced by the
+ * verified output of the crypto engine.
*/
-#ifndef HAVE_GMIME_2_5_7
-GMimeSignatureValidity *
-#else /* HAVE_GMIME_2_5_7 */
-GMimeSignatureList *
-#endif /* HAVE_GMIME_2_5_7 */
-g_mime_part_rfc2440_verify(GMimePart * part,
- GMimeGpgmeContext * ctx, GError ** err)
+GMimeGpgmeSigstat *
+g_mime_part_rfc2440_verify(GMimePart * part, GError ** err)
{
- GMimeStream *stream, *plainstream;
- GMimeDataWrapper * wrapper;
-#ifndef HAVE_GMIME_2_5_7
- GMimeSignatureValidity *valid;
-#else /* HAVE_GMIME_2_5_7 */
- GMimeSignatureList *list;
-#endif /* HAVE_GMIME_2_5_7 */
+ GMimeStream *stream;
+ GMimeStream *plainstream;
+ GMimeDataWrapper *wrapper;
+ GMimeGpgmeSigstat *result;
g_return_val_if_fail(GMIME_IS_PART(part), NULL);
- g_return_val_if_fail(GMIME_IS_GPGME_CONTEXT(ctx), NULL);
-#ifndef HAVE_GMIME_2_5_7
- g_return_val_if_fail(GMIME_CIPHER_CONTEXT(ctx)->sign_protocol != NULL,
- NULL);
-#else /* HAVE_GMIME_2_5_7 */
- g_return_val_if_fail(g_mime_crypto_context_get_signature_protocol
- (GMIME_CRYPTO_CONTEXT(ctx)) != NULL, NULL);
-#endif /* HAVE_GMIME_2_5_7 */
/* get the raw content */
wrapper = g_mime_part_get_content_object(GMIME_PART(part));
@@ -257,26 +254,12 @@ g_mime_part_rfc2440_verify(GMimePart * part,
plainstream = g_mime_stream_mem_new();
/* verify the signature */
- ctx->singlepart_mode = TRUE;
-#ifndef HAVE_GMIME_2_5_7
- valid =
- g_mime_cipher_context_verify(GMIME_CIPHER_CONTEXT(ctx),
- GMIME_CIPHER_HASH_DEFAULT, stream,
- plainstream, err);
-#else /* HAVE_GMIME_2_5_7 */
- list =
- g_mime_crypto_context_verify(GMIME_CRYPTO_CONTEXT(ctx),
- GMIME_CIPHER_ALGO_DEFAULT, stream,
- plainstream, err);
-#endif /* HAVE_GMIME_2_5_7 */
+ result =
+ libbalsa_gpgme_verify(stream, plainstream, GPGME_PROTOCOL_OpenPGP,
+ TRUE, err);
/* upon success, replace the signed content by the checked one */
-#ifndef HAVE_GMIME_2_5_7
- if (valid)
-#else /* HAVE_GMIME_2_5_7 */
- if (list)
-#endif /* HAVE_GMIME_2_5_7 */
- {
+ if (result) {
GMimeDataWrapper *wrapper = g_mime_data_wrapper_new();
g_mime_data_wrapper_set_stream(wrapper, plainstream);
@@ -285,49 +268,36 @@ g_mime_part_rfc2440_verify(GMimePart * part,
}
g_object_unref(plainstream);
-#ifndef HAVE_GMIME_2_5_7
- return valid;
-#else /* HAVE_GMIME_2_5_7 */
- return list;
-#endif /* HAVE_GMIME_2_5_7 */
+ return result;
}
-/*
- * Decrypt the RFC 2440 encrypted part using the gmime gpgme context
- * ctx and return 0 on success or -1 for fail. In the latter case, an
- * exception will be set on err to provide more information. Upon
+/* \brief Decrypt a RFC 2440 encrypted part
+ *
+ * \param part GMime part which shall be verified.
+ * \param parent Parent window to be passed to the passphrase callback
+ * function.
+ * \param error Filled with error information on error.
+ * \return A new signature status object on success, or NULL on error.
+ *
+ * Decrypt the RFC 2440 encrypted part and return the signature status as
+ * determined by the crypto routines or NULL on error. In the latter case,
+ * an exception will be set on err to provide more information. Upon
* success, the content of part is replaced by the decrypted output of
* the crypto engine. If the input was also signed, the signature is
- * verified and the result is placed in ctx by the underlying gpgme
- * context.
+ * verified.
*/
-#ifndef HAVE_GMIME_2_5_7
-GMimeSignatureValidity *
-#else /* HAVE_GMIME_2_5_7 */
-GMimeDecryptResult *
-#endif /* HAVE_GMIME_2_5_7 */
-g_mime_part_rfc2440_decrypt(GMimePart * part,
- GMimeGpgmeContext * ctx, GError ** err)
+GMimeGpgmeSigstat *
+g_mime_part_rfc2440_decrypt(GMimePart * part, GtkWindow * parent,
+ GError ** err)
{
- GMimeStream *stream, *plainstream;
- GMimeDataWrapper * wrapper;
-#ifndef HAVE_GMIME_2_5_7
- GMimeSignatureValidity *result;
-#else /* HAVE_GMIME_2_5_7 */
- GMimeDecryptResult *result;
-#endif /* HAVE_GMIME_2_5_7 */
+ GMimeStream *stream;
+ GMimeStream *plainstream;
+ GMimeDataWrapper *wrapper;
+ GMimeGpgmeSigstat *result;
gchar *headbuf = g_malloc0(1024);
g_return_val_if_fail(GMIME_IS_PART(part), NULL);
- g_return_val_if_fail(GMIME_IS_GPGME_CONTEXT(ctx), NULL);
-#ifndef HAVE_GMIME_2_5_7
- g_return_val_if_fail(GMIME_CIPHER_CONTEXT(ctx)->encrypt_protocol !=
- NULL, NULL);
-#else /* HAVE_GMIME_2_5_7 */
- g_return_val_if_fail(g_mime_crypto_context_get_encryption_protocol
- (GMIME_CRYPTO_CONTEXT(ctx)) != NULL, NULL);
-#endif /* HAVE_GMIME_2_5_7 */
/* get the raw content */
wrapper = g_mime_part_get_content_object(part);
@@ -344,13 +314,8 @@ g_mime_part_rfc2440_decrypt(GMimePart * part,
/* decrypt and (if possible) verify the input */
result =
-#ifndef HAVE_GMIME_2_5_7
- g_mime_cipher_context_decrypt(GMIME_CIPHER_CONTEXT(ctx), stream,
- plainstream, err);
-#else /* HAVE_GMIME_2_5_7 */
- g_mime_crypto_context_decrypt(GMIME_CRYPTO_CONTEXT(ctx), stream,
- plainstream, err);
-#endif /* HAVE_GMIME_2_5_7 */
+ libbalsa_gpgme_decrypt(stream, plainstream, GPGME_PROTOCOL_OpenPGP,
+ parent, err);
if (result != NULL) {
GMimeStream *filter_stream;
@@ -361,7 +326,8 @@ g_mime_part_rfc2440_decrypt(GMimePart * part,
/* strip crlf off encrypted stuff coming from Winbloze crap */
filter_stream = g_mime_stream_filter_new(plainstream);
filter = g_mime_filter_crlf_new(FALSE, FALSE);
- g_mime_stream_filter_add(GMIME_STREAM_FILTER(filter_stream), filter);
+ g_mime_stream_filter_add(GMIME_STREAM_FILTER(filter_stream),
+ filter);
g_object_unref(filter);
/* replace the old contents by the decrypted stuff */
@@ -372,14 +338,15 @@ g_mime_part_rfc2440_decrypt(GMimePart * part,
g_mime_stream_write_to_stream(filter_stream, out_stream);
g_object_unref(filter_stream);
- g_mime_part_set_content_encoding(part, GMIME_CONTENT_ENCODING_8BIT);
+ g_mime_part_set_content_encoding(part,
+ GMIME_CONTENT_ENCODING_8BIT);
/*
- * Set the charset of the decrypted content to the RFC 2440 "Charset:"
- * header. If it is not present, RFC 2440 defines that the contents
- * should be utf-8, but real-life applications (e.g. pgp4pine) tend to
- * use "some" charset, so "unknown-8bit" is a safe choice in this case
- * and if no other charset is given.
+ * Set the charset of the decrypted content to the RFC 2440
+ * "Charset:" header. If it is not present, RFC 2440 defines that
+ * the contents should be utf-8, but real-life applications (e.g.
+ * pgp4pine) tend to use "some" charset, so "unknown-8bit" is a
+ * safe choice in this case and if no other charset is given.
*/
if (g_mime_object_get_content_type(GMIME_OBJECT(part))) {
gchar *up_headbuf = g_ascii_strup(headbuf, -1);
@@ -393,13 +360,17 @@ g_mime_part_rfc2440_decrypt(GMimePart * part,
while (*line_end > ' ')
line_end++;
*line_end = '\0';
- g_mime_object_set_content_type_parameter(GMIME_OBJECT(part),
- "charset", p);
+ g_mime_object_set_content_type_parameter(GMIME_OBJECT
+ (part), "charset",
+ p);
} else {
- if (!g_ascii_strcasecmp("us-ascii",
- g_mime_object_get_content_type_parameter(GMIME_OBJECT(part),
+ if (!g_ascii_strcasecmp
+ ("us-ascii",
+ g_mime_object_get_content_type_parameter(GMIME_OBJECT
+ (part),
"charset")))
- g_mime_object_set_content_type_parameter(GMIME_OBJECT(part),
+ g_mime_object_set_content_type_parameter(GMIME_OBJECT
+ (part),
"charset",
"unknown-8bit");
}
diff --git a/libbalsa/gmime-part-rfc2440.h b/libbalsa/gmime-part-rfc2440.h
index cc1901a..a757031 100644
--- a/libbalsa/gmime-part-rfc2440.h
+++ b/libbalsa/gmime-part-rfc2440.h
@@ -1,7 +1,7 @@
/* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
/*
- * gmime/gpgme glue layer library
- * Copyright (C) 2004 Albrecht Dreß<albrecht dress arcor de>
+ * gmime/gpgme implementation for RFC2440 parts
+ * Copyright (C) 2004-2011 Albrecht Dreà <albrecht dress arcor de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -23,19 +23,18 @@
#define __GMIME_PART_RFC2440_H__
+#include <glib.h>
+#include <gtk/gtk.h>
#include <gmime/gmime.h>
-#include "gmime-gpgme-context.h"
#ifdef __cplusplus
extern "C" {
-# ifdef MAKE_EMACS_HAPPY
+#ifdef MAKE_EMACS_HAPPY
}
-# endif
+#endif
#endif /* __cplusplus */
-
-
typedef enum _GMimePartRfc2440Mode GMimePartRfc2440Mode;
enum _GMimePartRfc2440Mode {
GMIME_PART_RFC2440_NONE,
@@ -49,25 +48,15 @@ GMimePartRfc2440Mode g_mime_part_check_rfc2440(GMimePart * part);
/* crypto routines */
int g_mime_part_rfc2440_sign_encrypt(GMimePart * part,
- GMimeGpgmeContext * ctx,
- GPtrArray * recipients,
const char *sign_userid,
+ GPtrArray * recipients,
+ gboolean trust_all,
+ GtkWindow * parent, GError ** err);
+GMimeGpgmeSigstat *g_mime_part_rfc2440_verify(GMimePart * part,
GError ** err);
-#ifndef HAVE_GMIME_2_5_7
-GMimeSignatureValidity *g_mime_part_rfc2440_verify(GMimePart * part,
- GMimeGpgmeContext * ctx,
- GError ** err);
-GMimeSignatureValidity *g_mime_part_rfc2440_decrypt(GMimePart * part,
- GMimeGpgmeContext *
- ctx, GError ** err);
-#else /* HAVE_GMIME_2_5_7 */
-GMimeSignatureList *g_mime_part_rfc2440_verify(GMimePart * part,
- GMimeGpgmeContext * ctx,
- GError ** err);
-GMimeDecryptResult *g_mime_part_rfc2440_decrypt(GMimePart * part,
- GMimeGpgmeContext * ctx,
+GMimeGpgmeSigstat *g_mime_part_rfc2440_decrypt(GMimePart * part,
+ GtkWindow * parent,
GError ** err);
-#endif /* HAVE_GMIME_2_5_7 */
#ifdef __cplusplus
}
diff --git a/libbalsa/libbalsa-gpgme-cb.c b/libbalsa/libbalsa-gpgme-cb.c
new file mode 100644
index 0000000..a2eba03
--- /dev/null
+++ b/libbalsa/libbalsa-gpgme-cb.c
@@ -0,0 +1,428 @@
+/* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
+/*
+ * gpgme standard callback functions for balsa
+ * Copyright (C) 2011 Albrecht Dreà <albrecht dress arcor de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
+#if HAVE_MACOSX_DESKTOP
+#include "macosx-helpers.h"
+#endif
+
+#ifdef BALSA_USE_THREADS
+#include <pthread.h>
+#include "misc.h"
+#endif
+
+#include <gpgme.h>
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include "rfc3156.h"
+#include "libbalsa-gpgme-cb.h"
+
+
+/* stuff to get a key fingerprint from a selection list */
+enum {
+ GPG_KEY_USER_ID_COLUMN = 0,
+ GPG_KEY_ID_COLUMN,
+ GPG_KEY_LENGTH_COLUMN,
+ GPG_KEY_VALIDITY_COLUMN,
+ GPG_KEY_PTR_COLUMN,
+ GPG_KEY_NUM_COLUMNS
+};
+
+#ifdef BALSA_USE_THREADS
+/* FIXME: is this really necessary? */
+typedef struct {
+ pthread_cond_t cond;
+ const gchar *uid_hint;
+ const gchar *passphrase_info;
+ gint was_bad;
+ GtkWindow *parent;
+ gchar *res;
+} ask_passphrase_data_t;
+#endif
+
+
+static void key_selection_changed_cb(GtkTreeSelection * selection,
+ gpgme_key_t * key);
+static gchar *get_passphrase_real(const gchar * uid_hint,
+ const gchar * passphrase_info,
+ int prev_was_bad, GtkWindow * parent);
+
+#ifdef BALSA_USE_THREADS
+static gboolean get_passphrase_idle(gpointer data);
+#endif
+
+
+gpgme_error_t
+lb_gpgme_passphrase(void *hook, const gchar * uid_hint,
+ const gchar * passphrase_info, int prev_was_bad,
+ int fd)
+{
+ int foo, bar;
+ gchar *passwd;
+ gchar *p;
+ GtkWindow *parent;
+
+ if (hook && GTK_IS_WINDOW(hook))
+ parent = (GtkWindow *) hook;
+ else
+ parent = NULL;
+
+#ifdef BALSA_USE_THREADS
+ if (!libbalsa_am_i_subthread())
+ passwd =
+ get_passphrase_real(uid_hint, passphrase_info, prev_was_bad,
+ parent);
+ else {
+ static pthread_mutex_t get_passphrase_lock =
+ PTHREAD_MUTEX_INITIALIZER;
+ ask_passphrase_data_t apd;
+
+ pthread_mutex_lock(&get_passphrase_lock);
+ pthread_cond_init(&apd.cond, NULL);
+ apd.uid_hint = uid_hint;
+ apd.was_bad = prev_was_bad;
+ apd.passphrase_info = passphrase_info;
+ apd.parent = parent;
+ g_idle_add(get_passphrase_idle, &apd);
+ pthread_cond_wait(&apd.cond, &get_passphrase_lock);
+ pthread_cond_destroy(&apd.cond);
+ pthread_mutex_unlock(&get_passphrase_lock);
+ passwd = apd.res;
+ }
+#else
+ passwd = get_passphrase_real(context, uid_hint, prev_was_bad);
+#endif /* BALSA_USE_THREADS */
+
+ if (!passwd) {
+ foo = write(fd, "\n", 1);
+ return foo > 0 ? GPG_ERR_CANCELED : GPG_ERR_EIO;
+ }
+
+ /* send the passphrase and erase the string */
+ foo = write(fd, passwd, strlen(passwd));
+ for (p = passwd; *p; p++)
+ *p = random();
+ g_free(passwd);
+ bar = write(fd, "\n", 1);
+ return foo > 0 && bar > 0 ? GPG_ERR_NO_ERROR : GPG_ERR_EIO;
+}
+
+
+gpgme_key_t
+lb_gpgme_select_key(const gchar * user_name, gboolean secret, GList * keys,
+ gpgme_protocol_t protocol, GtkWindow * parent)
+{
+ static const gchar *col_titles[] =
+ { N_("User ID"), N_("Key ID"), N_("Length"), N_("Validity") };
+ GtkWidget *dialog;
+ GtkWidget *vbox;
+ GtkWidget *label;
+ GtkWidget *scrolled_window;
+ GtkWidget *tree_view;
+ GtkTreeStore *model;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ gint i, last_col;
+ gchar *prompt;
+ gchar *upcase_name;
+ gpgme_key_t use_key = NULL;
+
+ /* FIXME: create dialog according to the Gnome HIG */
+ dialog = gtk_dialog_new_with_buttons(_("Select key"),
+ parent,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_OK, GTK_RESPONSE_OK,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL, NULL);
+#if HAVE_MACOSX_DESKTOP
+ libbalsa_macosx_menu_for_parent(dialog, parent);
+#endif
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_add(GTK_CONTAINER
+ (gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
+ vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
+ if (secret)
+ prompt =
+ g_strdup_printf(_("Select the private key for the signer %s"),
+ user_name);
+ else
+ prompt = g_strdup_printf(_
+ ("Select the public key for the recipient %s"),
+ user_name);
+ label = gtk_label_new(prompt);
+ gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+ g_free(prompt);
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0);
+
+ scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW
+ (scrolled_window),
+ GTK_SHADOW_ETCHED_IN);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+ GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+ gtk_box_pack_start(GTK_BOX(vbox), scrolled_window, TRUE, TRUE, 0);
+
+ model = gtk_tree_store_new(GPG_KEY_NUM_COLUMNS, G_TYPE_STRING, /* user ID */
+ G_TYPE_STRING, /* key ID */
+ G_TYPE_INT, /* length */
+ G_TYPE_STRING, /* validity (gpg encrypt only) */
+ G_TYPE_POINTER); /* key */
+
+ tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
+ g_signal_connect(G_OBJECT(selection), "changed",
+ G_CALLBACK(key_selection_changed_cb), &use_key);
+
+ /* add the keys */
+ upcase_name = g_ascii_strup(user_name, -1);
+ while (keys) {
+ gpgme_key_t key = (gpgme_key_t) keys->data;
+ gpgme_subkey_t subkey = key->subkeys;
+ gpgme_user_id_t uid = key->uids;
+ gchar *uid_info = NULL;
+ gboolean uid_found;
+
+ /* find the relevant subkey */
+ while (subkey && ((secret && !subkey->can_sign) ||
+ (!secret && !subkey->can_encrypt)))
+ subkey = subkey->next;
+
+ /* find the relevant uid */
+ uid_found = FALSE;
+ while (uid && !uid_found) {
+ g_free(uid_info);
+ uid_info = libbalsa_cert_subject_readable(uid->uid);
+
+ /* check the email field which may or may not be present */
+ if (uid->email && !g_ascii_strcasecmp(uid->email, user_name))
+ uid_found = TRUE;
+ else {
+ /* no email or no match, check the uid */
+ gchar *upcase_uid = g_ascii_strup(uid_info, -1);
+
+ if (strstr(upcase_uid, upcase_name))
+ uid_found = TRUE;
+ else
+ uid = uid->next;
+ g_free(upcase_uid);
+ }
+ }
+
+ /* append the element */
+ if (subkey && uid) {
+ gtk_tree_store_append(GTK_TREE_STORE(model), &iter, NULL);
+ gtk_tree_store_set(GTK_TREE_STORE(model), &iter,
+ GPG_KEY_USER_ID_COLUMN, uid_info,
+ GPG_KEY_ID_COLUMN, subkey->keyid,
+ GPG_KEY_LENGTH_COLUMN, subkey->length,
+ GPG_KEY_VALIDITY_COLUMN,
+ libbalsa_gpgme_validity_to_gchar_short(uid->
+ validity),
+ GPG_KEY_PTR_COLUMN, key, -1);
+ }
+ g_free(uid_info);
+ keys = g_list_next(keys);
+ }
+ g_free(upcase_name);
+
+ g_object_unref(G_OBJECT(model));
+ /* show the validity only if we are asking for a gpg public key */
+ last_col = (protocol == GPGME_PROTOCOL_CMS || secret) ?
+ GPG_KEY_LENGTH_COLUMN : GPG_KEY_VALIDITY_COLUMN;
+ for (i = 0; i <= last_col; i++) {
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ renderer = gtk_cell_renderer_text_new();
+ column =
+ gtk_tree_view_column_new_with_attributes(_(col_titles[i]),
+ renderer, "text", i,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ }
+
+ gtk_container_add(GTK_CONTAINER(scrolled_window), tree_view);
+ gtk_window_set_default_size(GTK_WINDOW(dialog), 500, 300);
+ gtk_widget_show_all(gtk_dialog_get_content_area(GTK_DIALOG(dialog)));
+
+ if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_OK)
+ use_key = NULL;
+ gtk_widget_destroy(dialog);
+
+ return use_key;
+}
+
+
+gboolean
+lb_gpgme_accept_low_trust_key(const gchar * user_name,
+ const gpgme_user_id_t user_id,
+ GtkWindow * parent)
+{
+ GtkWidget *dialog;
+ gint result;
+ gchar *message1;
+ gchar *message2;
+
+ /* paranoia checks */
+ g_return_val_if_fail(user_id != NULL, FALSE);
+
+ /* build the message */
+ message1 =
+ g_strdup_printf(_("Insufficient trust for recipient %s"),
+ user_name);
+ message2 =
+ g_strdup_printf(_
+ ("The validity of the key with user ID \"%s\" is \"%s\"."),
+ user_id->uid,
+ libbalsa_gpgme_validity_to_gchar_short(user_id->
+ validity));
+ dialog =
+ gtk_message_dialog_new_with_markup(parent,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_WARNING,
+ GTK_BUTTONS_YES_NO,
+ "<b>%s</b>\n\n%s\n%s", message1,
+ message2,
+ _("Use this key anyway?"));
+#if HAVE_MACOSX_DESKTOP
+ libbalsa_macosx_menu_for_parent(dialog, parent);
+#endif
+
+ /* ask the user */
+ result = gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+
+ return result == GTK_RESPONSE_YES;
+
+}
+
+
+#include "padlock-keyhole.xpm"
+
+
+static gchar *
+get_passphrase_real(const gchar * uid_hint, const gchar * passphrase_info,
+ int prev_was_bad, GtkWindow * parent)
+{
+ static GdkPixbuf *padlock_keyhole = NULL;
+ GtkWidget *dialog, *entry, *vbox, *hbox;
+ gchar *prompt, *passwd;
+
+ /* FIXME: create dialog according to the Gnome HIG */
+ dialog = gtk_dialog_new_with_buttons(_("Enter Passphrase"), parent,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_OK, GTK_RESPONSE_OK,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL, NULL);
+#if HAVE_MACOSX_DESKTOP
+ libbalsa_macosx_menu_for_parent(dialog, parent);
+#endif
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 12);
+ gtk_container_add(GTK_CONTAINER
+ (gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
+ hbox);
+
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_add(GTK_CONTAINER(hbox), vbox);
+ if (!padlock_keyhole)
+ padlock_keyhole =
+ gdk_pixbuf_new_from_xpm_data(padlock_keyhole_xpm);
+ gtk_box_pack_start(GTK_BOX(vbox),
+ gtk_image_new_from_pixbuf(padlock_keyhole), FALSE,
+ FALSE, 0);
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_add(GTK_CONTAINER(hbox), vbox);
+ if (prev_was_bad)
+ prompt =
+ g_strdup_printf(_
+ ("%s\nThe passphrase for this key was bad, please try again!\n\nKey: %s"),
+ passphrase_info, uid_hint);
+ else
+ prompt =
+ g_strdup_printf(_
+ ("%s\nPlease enter the passphrase for the secret key!\n\nKey: %s"),
+ passphrase_info, uid_hint);
+ gtk_container_add(GTK_CONTAINER(vbox), gtk_label_new(prompt));
+ g_free(prompt);
+ entry = gtk_entry_new();
+ gtk_container_add(GTK_CONTAINER(vbox), entry);
+
+ gtk_widget_show_all(gtk_dialog_get_content_area(GTK_DIALOG(dialog)));
+ gtk_entry_set_width_chars(GTK_ENTRY(entry), 40);
+ gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
+
+ gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
+ gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
+ gtk_widget_grab_focus(entry);
+ if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
+ passwd = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
+ else
+ passwd = NULL;
+
+ gtk_widget_destroy(dialog);
+
+ return passwd;
+}
+
+
+#ifdef BALSA_USE_THREADS
+/* get_passphrase_idle:
+ called in MT mode by the main thread.
+ */
+static gboolean
+get_passphrase_idle(gpointer data)
+{
+ ask_passphrase_data_t *apd = (ask_passphrase_data_t *) data;
+
+ gdk_threads_enter();
+ apd->res =
+ get_passphrase_real(apd->uid_hint, apd->passphrase_info,
+ apd->was_bad, apd->parent);
+ gdk_threads_leave();
+ pthread_cond_signal(&apd->cond);
+ return FALSE;
+}
+#endif
+
+
+/* callback function if a new row is selected in the list */
+static void
+key_selection_changed_cb(GtkTreeSelection * selection, gpgme_key_t * key)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+
+ if (gtk_tree_selection_get_selected(selection, &model, &iter))
+ gtk_tree_model_get(model, &iter, GPG_KEY_PTR_COLUMN, key, -1);
+}
diff --git a/libbalsa/libbalsa-gpgme-cb.h b/libbalsa/libbalsa-gpgme-cb.h
new file mode 100644
index 0000000..c0364bf
--- /dev/null
+++ b/libbalsa/libbalsa-gpgme-cb.h
@@ -0,0 +1,59 @@
+/* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
+/*
+ * gpgme standard callback functions for balsa
+ * Copyright (C) 2011 Albrecht Dreà <albrecht dress arcor de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef LIBBALSA_GPGME_CB_H_
+#define LIBBALSA_GPGME_CB_H_
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gpgme.h>
+#include <glib.h>
+#include <gtk/gtk.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#ifdef MAKE_EMACS_HAPPY
+}
+#endif
+#endif /* __cplusplus */
+
+
+gpgme_error_t lb_gpgme_passphrase(void *hook, const gchar * uid_hint,
+ const gchar * passphrase_info,
+ int prev_was_bad, int fd);
+gpgme_key_t lb_gpgme_select_key(const gchar * user_name, gboolean secret,
+ GList * keys, gpgme_protocol_t protocol,
+ GtkWindow * parent);
+gboolean lb_gpgme_accept_low_trust_key(const gchar * user_name,
+ const gpgme_user_id_t user_id,
+ GtkWindow * parent);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* LIBBALSA_GPGME_CB_H_ */
diff --git a/libbalsa/libbalsa-gpgme.c b/libbalsa/libbalsa-gpgme.c
new file mode 100644
index 0000000..6932acb
--- /dev/null
+++ b/libbalsa/libbalsa-gpgme.c
@@ -0,0 +1,968 @@
+/* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
+/*
+ * gpgme low-level stuff for gmime/balsa
+ * Copyright (C) 2011 Albrecht Dreà <albrecht dress arcor de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
+#ifdef BALSA_USE_THREADS
+#include <pthread.h>
+#include "misc.h"
+#endif
+
+#if HAVE_MACOSX_DESKTOP
+#include "macosx-helpers.h"
+#endif
+
+#include <string.h>
+#include <gpgme.h>
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <gmime/gmime.h>
+#include "gmime-gpgme-signature.h"
+#include "libbalsa-gpgme.h"
+
+
+static void g_set_error_from_gpgme(GError ** error,
+ gpgme_error_t gpgme_err,
+ const gchar * message);
+static gpgme_error_t gpgme_new_with_protocol(gpgme_ctx_t * ctx,
+ gpgme_protocol_t protocol,
+ GtkWindow * parent,
+ GError ** error);
+static gboolean gpgme_add_signer(gpgme_ctx_t ctx, const gchar * signer,
+ GtkWindow * parent, GError ** error);
+static gpgme_key_t *gpgme_build_recipients(gpgme_ctx_t ctx,
+ GPtrArray * rcpt_list,
+ gboolean accept_low_trust,
+ GtkWindow * parent,
+ GError ** error);
+static void release_keylist(gpgme_key_t * keylist);
+
+/* callbacks for gpgme file handling */
+static ssize_t g_mime_gpgme_stream_rd(GMimeStream * stream, void *buffer,
+ size_t size);
+static ssize_t g_mime_gpgme_stream_wr(GMimeStream * stream, void *buffer,
+ size_t size);
+static void cb_data_release(void *handle);
+
+
+#if defined(ENABLE_NLS)
+static const gchar *get_utf8_locale(int category);
+#endif
+
+
+static gboolean has_proto_openpgp = FALSE;
+static gboolean has_proto_cms = FALSE;
+
+static gpgme_passphrase_cb_t gpgme_passphrase_cb = NULL;
+static lbgpgme_select_key_cb select_key_cb = NULL;
+static lbgpgme_accept_low_trust_cb accept_low_trust_cb = NULL;
+
+
+/** \brief Initialise GpgME
+ *
+ * \param get_passphrase Callback function to read a passphrase from the
+ * user. Note that this function is used \em only for OpenPGP and
+ * \em only if no GPG Agent is running and can therefore usually be
+ * NULL. The first (HOOK) argument the passed function accepts
+ * shall be the parent GtkWindow.
+ * \param select_key Callback function to let the user select a key from a
+ * list if more than one is available.
+ * \param accept_low_trust Callback function to ask the user whether a low
+ * trust key shall be accepted.
+ *
+ * Initialise the GpgME backend and remember the callback functions.
+ *
+ * \note This function \em must be called before using any other function
+ * from this module.
+ */
+void
+libbalsa_gpgme_init(gpgme_passphrase_cb_t get_passphrase,
+ lbgpgme_select_key_cb select_key,
+ lbgpgme_accept_low_trust_cb accept_low_trust)
+{
+ gpgme_engine_info_t e;
+ const gchar *agent_info;
+
+ /* initialise the gpgme library */
+ g_message("init gpgme version %s", gpgme_check_version(NULL));
+
+#ifdef HAVE_GPG
+ /* configure the GnuPG engine if a specific gpg path has been
+ * detected */
+ gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP, GPG_PATH, NULL);
+#endif
+
+#ifdef ENABLE_NLS
+ gpgme_set_locale(NULL, LC_CTYPE, get_utf8_locale(LC_CTYPE));
+ gpgme_set_locale(NULL, LC_MESSAGES, get_utf8_locale(LC_MESSAGES));
+#endif /* ENABLE_NLS */
+
+ /* dump the available engines */
+ if (gpgme_get_engine_info(&e) == GPG_ERR_NO_ERROR) {
+ while (e) {
+ g_message("protocol %s: engine %s (home %s, version %s)",
+ gpgme_get_protocol_name(e->protocol),
+ e->file_name, e->home_dir, e->version);
+ e = e->next;
+ }
+ }
+
+ /* check for gpg-agent */
+ agent_info = g_getenv("GPG_AGENT_INFO");
+ if (agent_info) {
+ g_message("gpg-agent found: %s", agent_info);
+ gpgme_passphrase_cb = NULL;
+ } else {
+ gpgme_passphrase_cb = get_passphrase;
+ }
+
+ /* verify that the engines we need are there */
+ if (gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP) ==
+ GPG_ERR_NO_ERROR) {
+ g_message("OpenPGP protocol supported");
+ has_proto_openpgp = TRUE;
+ } else {
+ g_warning
+ ("OpenPGP protocol not supported, basic crypto will not work!");
+ has_proto_openpgp = FALSE;
+ }
+
+#ifdef HAVE_SMIME
+ if (gpgme_engine_check_version(GPGME_PROTOCOL_CMS) ==
+ GPG_ERR_NO_ERROR) {
+ g_message("CMS (aka S/MIME) protocol supported");
+ has_proto_cms = TRUE;
+ } else {
+ g_warning("CMS protocol not supported, S/MIME will not work!");
+ has_proto_cms = FALSE;
+ }
+#else
+ g_message("built without CMS (aka S/MIME) protocol support");
+ has_proto_cms = FALSE;
+#endif
+
+ /* remember callbacks */
+ select_key = select_key_cb;
+ accept_low_trust = accept_low_trust_cb;
+}
+
+
+/** \brief Check if a crypto engine is available
+ *
+ * \param protocol Protocol for which the engine is checked.
+ * \return TRUE is the engine for the passed protocol is available.
+ *
+ * Check the availability of the crypto engine for a specific protocol.
+ */
+gboolean
+libbalsa_gpgme_check_crypto_engine(gpgme_protocol_t protocol)
+{
+ switch (protocol) {
+ case GPGME_PROTOCOL_OpenPGP:
+ return has_proto_openpgp;
+ case GPGME_PROTOCOL_CMS:
+ return has_proto_cms;
+ default:
+ return FALSE;
+ }
+}
+
+
+/** \brief Verify a signature
+ *
+ * \param content GMime stream of the signed matter.
+ * \param sig_plain GMime signature stream for a detached signature, or the
+ * output stream for the checked matter in single-part mode.
+ * \param protocol GpgME crypto protocol of the signature.
+ * \param singlepart_mode TRUE indicates single-part mode (i.e. sig_plain
+ * an output stream).
+ * \param error Filled with error information on error.
+ * \return A new signature status object on success, or NULL on error.
+ *
+ * Verify a signature by calling GpgME on the passed streams, and create a
+ * new signature object on success.
+ */
+GMimeGpgmeSigstat *
+libbalsa_gpgme_verify(GMimeStream * content, GMimeStream * sig_plain,
+ gpgme_protocol_t protocol, gboolean singlepart_mode,
+ GError ** error)
+{
+ gpgme_error_t err;
+ gpgme_ctx_t ctx;
+ struct gpgme_data_cbs cbs = {
+ (gpgme_data_read_cb_t) g_mime_gpgme_stream_rd, /* read method */
+ (gpgme_data_write_cb_t) g_mime_gpgme_stream_wr, /* write method */
+ NULL, /* seek method */
+ cb_data_release /* release method */
+ };
+ gpgme_data_t cont_data;
+ gpgme_data_t sig_plain_data;
+ GMimeGpgmeSigstat *result;
+
+ /* paranoia checks */
+ g_return_val_if_fail(GMIME_IS_STREAM(content), NULL);
+ g_return_val_if_fail(GMIME_IS_STREAM(sig_plain), NULL);
+ g_return_val_if_fail(protocol == GPGME_PROTOCOL_OpenPGP ||
+ protocol == GPGME_PROTOCOL_CMS, NULL);
+
+ /* create the GpgME context */
+ if ((err =
+ gpgme_new_with_protocol(&ctx, protocol, NULL,
+ error)) != GPG_ERR_NO_ERROR)
+ return NULL;
+
+ /* create the message stream */
+ if ((err =
+ gpgme_data_new_from_cbs(&cont_data, &cbs,
+ content)) != GPG_ERR_NO_ERROR) {
+ g_set_error_from_gpgme(error, err,
+ _("could not get data from stream"));
+ gpgme_release(ctx);
+ return NULL;
+ }
+
+ /* create data object for the detached signature stream or the
+ * "decrypted" plaintext */
+ if ((err =
+ gpgme_data_new_from_cbs(&sig_plain_data, &cbs,
+ sig_plain)) != GPG_ERR_NO_ERROR) {
+ g_set_error_from_gpgme(error, err,
+ _("could not get data from stream"));
+ gpgme_data_release(cont_data);
+ gpgme_release(ctx);
+ return NULL;
+ }
+
+ /* verify the signature */
+ if (singlepart_mode)
+ err = gpgme_op_verify(ctx, cont_data, NULL, sig_plain_data);
+ else
+ err = gpgme_op_verify(ctx, sig_plain_data, cont_data, NULL);
+ if (err != GPG_ERR_NO_ERROR) {
+ g_set_error_from_gpgme(error, err,
+ _("signature verification failed"));
+ result = g_mime_gpgme_sigstat_new();
+ result->status = err;
+ result->protocol = gpgme_get_protocol(ctx);
+ } else
+ result = g_mime_gpgme_sigstat_new_from_gpgme_ctx(ctx);
+
+ /* release gmgme data buffers, destroy the context and return the
+ * signature object */
+ gpgme_data_release(cont_data);
+ gpgme_data_release(sig_plain_data);
+ gpgme_release(ctx);
+ return result;
+}
+
+
+/** \brief Sign data
+ *
+ * \param userid User ID of the signer.
+ * \param istream GMime input stream.
+ * \param ostream GMime output stream.
+ * \param protocol GpgME crypto protocol of the signature.
+ * \param singlepart_mode TRUE indicates single-part mode (integrated
+ * signature), FALSE a detached signature.
+ * \param parent Parent window to be passed to the passphrase callback
+ * function.
+ * \param error Filled with error information on error.
+ * \return The hash algorithm used for creating the signature, or
+ * GPGME_MD_NONE on error.
+ *
+ * Sign the passed matter and write the detached signature or the signed
+ * input and the signature, respectively, to the output stream. The global
+ * callback to read the passphrase for the user's private key will be
+ * called by GpgME if no GPG Agent is running.
+ */
+gpgme_hash_algo_t
+libbalsa_gpgme_sign(const gchar * userid, GMimeStream * istream,
+ GMimeStream * ostream, gpgme_protocol_t protocol,
+ gboolean singlepart_mode, GtkWindow * parent,
+ GError ** error)
+{
+ gpgme_error_t err;
+ gpgme_ctx_t ctx;
+ gpgme_sig_mode_t sig_mode;
+ gpgme_data_t in;
+ gpgme_data_t out;
+ gpgme_hash_algo_t hash_algo;
+ struct gpgme_data_cbs cbs = {
+ (gpgme_data_read_cb_t) g_mime_gpgme_stream_rd, /* read method */
+ (gpgme_data_write_cb_t) g_mime_gpgme_stream_wr, /* write method */
+ NULL, /* seek method */
+ cb_data_release /* release method */
+ };
+
+ /* paranoia checks */
+ g_return_val_if_fail(GMIME_IS_STREAM(istream), GPGME_MD_NONE);
+ g_return_val_if_fail(GMIME_IS_STREAM(ostream), GPGME_MD_NONE);
+ g_return_val_if_fail(protocol == GPGME_PROTOCOL_OpenPGP ||
+ protocol == GPGME_PROTOCOL_CMS, GPGME_MD_NONE);
+
+ /* create the GpgME context */
+ if ((err =
+ gpgme_new_with_protocol(&ctx, protocol, parent,
+ error)) != GPG_ERR_NO_ERROR)
+ return GPGME_MD_NONE;
+
+ /* set the signature mode */
+ if (singlepart_mode) {
+ if (protocol == GPGME_PROTOCOL_OpenPGP)
+ sig_mode = GPGME_SIG_MODE_CLEAR;
+ else
+ sig_mode = GPGME_SIG_MODE_NORMAL;
+ } else
+ sig_mode = GPGME_SIG_MODE_DETACH;
+
+ /* find the secret key for the "sign_for" address */
+ if (!gpgme_add_signer(ctx, userid, parent, error)) {
+ gpgme_release(ctx);
+ return GPGME_MD_NONE;
+ }
+
+ /* OpenPGP signatures are ASCII armored */
+ gpgme_set_armor(ctx, protocol == GPGME_PROTOCOL_OpenPGP);
+
+ /* create gpgme data objects */
+ if ((err =
+ gpgme_data_new_from_cbs(&in, &cbs,
+ istream)) != GPG_ERR_NO_ERROR) {
+ g_set_error_from_gpgme(error, err,
+ _("could not get data from stream"));
+ gpgme_release(ctx);
+ return GPGME_MD_NONE;
+ }
+ if ((err =
+ gpgme_data_new_from_cbs(&out, &cbs,
+ ostream)) != GPG_ERR_NO_ERROR) {
+ g_set_error_from_gpgme(error, err,
+ _("could not create new data object"));
+ gpgme_data_release(in);
+ gpgme_release(ctx);
+ return GPGME_MD_NONE;
+ }
+
+ /* sign and get the used hash algorithm */
+ err = gpgme_op_sign(ctx, in, out, sig_mode);
+ if (err != GPG_ERR_NO_ERROR) {
+ g_set_error_from_gpgme(error, err, _("signing failed"));
+ hash_algo = GPGME_MD_NONE;
+ } else
+ hash_algo = gpgme_op_sign_result(ctx)->signatures->hash_algo;
+
+ /* clean up */
+ gpgme_data_release(in);
+ gpgme_data_release(out);
+ gpgme_release(ctx);
+ return hash_algo;
+}
+
+
+/** \brief Encrypt data
+ *
+ * \param recipients Array of User ID for which the matter shall be
+ * encrypted using their public keys.
+ * \param sign_for User ID of the signer or NULL if the matter shall not be
+ * signed. Note that combined signing and encryption is allowed \em
+ * only in OpenPGP single-part (i.e. RFC 2440) mode.
+ * \param istream GMime input stream.
+ * \param ostream GMime output stream.
+ * \param protocol GpgME crypto protocol to use for encryption.
+ * \param singlepart_mode TRUE indicates single-part mode (integrated
+ * signature), FALSE a detached signature.
+ * \param trust_all_keys TRUE if all low-truct keys shall be accepted for
+ * encryption. Otherwise, the function will use the global callback
+ * to ask the user whether a low-trust key shall be accepted.
+ * \param parent Parent window to be passed to the callback functions.
+ * \param error Filled with error information on error.
+ * \return 0 on success, or -1 on error.
+ *
+ * Encrypt the passed matter and write the result to the output stream.
+ * Combined signing and encryption is allowed for single-part OpenPGP mode
+ * only.
+ */
+int
+libbalsa_gpgme_encrypt(GPtrArray * recipients, const char *sign_for,
+ GMimeStream * istream, GMimeStream * ostream,
+ gpgme_protocol_t protocol, gboolean singlepart_mode,
+ gboolean trust_all_keys, GtkWindow * parent,
+ GError ** error)
+{
+ gpgme_ctx_t ctx;
+ gpgme_error_t err;
+ gpgme_key_t *rcpt_keys;
+ gpgme_data_t plain;
+ gpgme_data_t crypt;
+ struct gpgme_data_cbs cbs = {
+ (gpgme_data_read_cb_t) g_mime_gpgme_stream_rd, /* read method */
+ (gpgme_data_write_cb_t) g_mime_gpgme_stream_wr, /* write method */
+ NULL, /* seek method */
+ cb_data_release /* release method */
+ };
+
+ /* paranoia checks */
+ g_return_val_if_fail(recipients != NULL, -1);
+ g_return_val_if_fail(GMIME_IS_STREAM(istream), -1);
+ g_return_val_if_fail(GMIME_IS_STREAM(ostream), GPGME_MD_NONE);
+ g_return_val_if_fail(protocol == GPGME_PROTOCOL_OpenPGP ||
+ protocol == GPGME_PROTOCOL_CMS, -1);
+
+ /* create the GpgME context */
+ if ((err =
+ gpgme_new_with_protocol(&ctx, protocol, parent,
+ error)) != GPG_ERR_NO_ERROR)
+ return -1;
+
+ /* sign & encrypt is valid only for single-part OpenPGP */
+ if (sign_for != NULL
+ && (!singlepart_mode || protocol != GPGME_PROTOCOL_OpenPGP)) {
+ if (error)
+ g_set_error(error, GPGME_ERROR_QUARK, GPG_ERR_INV_ENGINE,
+ _
+ ("combined signing and encryption is defined only for RFC 2440"));
+ gpgme_release(ctx);
+ return -1;
+ }
+
+ /* if requested, find the secret key for "userid" */
+ if (sign_for && !gpgme_add_signer(ctx, sign_for, parent, error)) {
+ gpgme_release(ctx);
+ return -1;
+ }
+
+ /* build the list of recipients */
+ if (!
+ (rcpt_keys =
+ gpgme_build_recipients(ctx, recipients, trust_all_keys, parent,
+ error))) {
+ gpgme_release(ctx);
+ return -1;
+ }
+
+ /* create the data objects */
+ if (protocol == GPGME_PROTOCOL_OpenPGP) {
+ gpgme_set_armor(ctx, 1);
+ gpgme_set_textmode(ctx, singlepart_mode);
+ } else {
+ gpgme_set_armor(ctx, 0);
+ gpgme_set_textmode(ctx, 0);
+ }
+ if ((err =
+ gpgme_data_new_from_cbs(&plain, &cbs,
+ istream)) != GPG_ERR_NO_ERROR) {
+ g_set_error_from_gpgme(error, err,
+ _("could not get data from stream"));
+ release_keylist(rcpt_keys);
+ gpgme_release(ctx);
+ return -1;
+ }
+ if ((err =
+ gpgme_data_new_from_cbs(&crypt, &cbs,
+ ostream)) != GPG_ERR_NO_ERROR) {
+ g_set_error_from_gpgme(error, err,
+ _("could not create new data object"));
+ release_keylist(rcpt_keys);
+ gpgme_data_release(plain);
+ gpgme_release(ctx);
+ return -1;
+ }
+
+ /* do the encrypt or sign and encrypt operation
+ * Note: we set "always trust" here, as if we detected an untrusted key
+ * earlier, the user already accepted it */
+ if (sign_for)
+ err =
+ gpgme_op_encrypt_sign(ctx, rcpt_keys,
+ GPGME_ENCRYPT_ALWAYS_TRUST, plain,
+ crypt);
+ else
+ err =
+ gpgme_op_encrypt(ctx, rcpt_keys, GPGME_ENCRYPT_ALWAYS_TRUST,
+ plain, crypt);
+
+ release_keylist(rcpt_keys);
+ gpgme_data_release(plain);
+ gpgme_data_release(crypt);
+ gpgme_release(ctx);
+ if (err != GPG_ERR_NO_ERROR) {
+ if (sign_for)
+ g_set_error_from_gpgme(error, err,
+ _("signing and encryption failed"));
+ else
+ g_set_error_from_gpgme(error, err, _("encryption failed"));
+ return -1;
+ } else
+ return 0;
+}
+
+
+/** \brief Decrypt data
+ *
+ * \param istream GMime input (encrypted) stream.
+ * \param ostream GMime output (decrypted) stream.
+ * \param protocol GpgME crypto protocol to use.
+ * \param parent Parent window to be passed to the passphrase callback
+ * function.
+ * \param error Filled with error information on error.
+ * \return A new signature status object on success, or NULL on error.
+ *
+ * Decrypt and -if applicable- verify the signature of the passed data
+ * stream. If the input is not signed the returned signature status will
+ * be GPG_ERR_NOT_SIGNED.
+ */
+GMimeGpgmeSigstat *
+libbalsa_gpgme_decrypt(GMimeStream * crypted, GMimeStream * plain,
+ gpgme_protocol_t protocol, GtkWindow * parent,
+ GError ** error)
+{
+ gpgme_ctx_t ctx;
+ gpgme_error_t err;
+ gpgme_data_t plain_data;
+ gpgme_data_t crypt_data;
+ GMimeGpgmeSigstat *result;
+ struct gpgme_data_cbs cbs = {
+ (gpgme_data_read_cb_t) g_mime_gpgme_stream_rd, /* read method */
+ (gpgme_data_write_cb_t) g_mime_gpgme_stream_wr, /* write method */
+ NULL, /* seek method */
+ cb_data_release /* release method */
+ };
+
+ /* paranoia checks */
+ g_return_val_if_fail(GMIME_IS_STREAM(crypted), NULL);
+ g_return_val_if_fail(GMIME_IS_STREAM(plain), NULL);
+ g_return_val_if_fail(protocol == GPGME_PROTOCOL_OpenPGP ||
+ protocol == GPGME_PROTOCOL_CMS, NULL);
+
+ /* create the GpgME context */
+ if ((err =
+ gpgme_new_with_protocol(&ctx, protocol, parent,
+ error)) != GPG_ERR_NO_ERROR)
+ return NULL;
+
+ /* create the data streams */
+ if ((err =
+ gpgme_data_new_from_cbs(&crypt_data, &cbs,
+ crypted)) != GPG_ERR_NO_ERROR) {
+ g_set_error_from_gpgme(error, err,
+ _("could not get data from stream"));
+ gpgme_release(ctx);
+ return NULL;
+ }
+ if ((err =
+ gpgme_data_new_from_cbs(&plain_data, &cbs,
+ plain)) != GPG_ERR_NO_ERROR) {
+ g_set_error_from_gpgme(error, err,
+ _("could not create new data object"));
+ gpgme_data_release(crypt_data);
+ gpgme_release(ctx);
+ return NULL;
+ }
+
+ /* try to decrypt */
+ if ((err =
+ gpgme_op_decrypt_verify(ctx, crypt_data,
+ plain_data)) != GPG_ERR_NO_ERROR) {
+ g_set_error_from_gpgme(error, err, _("decryption failed"));
+ result = NULL;
+ } else {
+ /* decryption successful, check for signature */
+ result = g_mime_gpgme_sigstat_new_from_gpgme_ctx(ctx);
+ }
+
+ /* clean up */
+ gpgme_data_release(plain_data);
+ gpgme_data_release(crypt_data);
+ gpgme_release(ctx);
+
+ return result;
+}
+
+/* ---- local stuff ---------------------------------------------------- */
+
+#define UTF8_VALID_STR(s) \
+ do { \
+ if ((s) && !g_utf8_validate(s, -1, NULL)) { \
+ gsize bwr; \
+ gchar * newstr = g_locale_to_utf8(s, -1, NULL, &bwr, NULL); \
+ \
+ g_free(s); \
+ s = newstr; \
+ } \
+ } while (0)
+
+
+/*
+ * set a GError form GpgME information
+ */
+static void
+g_set_error_from_gpgme(GError ** error, gpgme_error_t gpgme_err,
+ const gchar * message)
+{
+ gchar *errstr;
+ gchar *srcstr;
+
+ if (!error)
+ return;
+
+ srcstr = g_strdup(gpgme_strsource(gpgme_err));
+ UTF8_VALID_STR(srcstr);
+ errstr = g_strdup(gpgme_strerror(gpgme_err));
+ UTF8_VALID_STR(errstr);
+ g_set_error(error, GPGME_ERROR_QUARK, gpgme_err, "%s: %s: %s", srcstr,
+ message, errstr);
+ g_free(srcstr);
+ g_free(errstr);
+}
+
+
+/*
+ * callback to get data from a stream
+ */
+static ssize_t
+g_mime_gpgme_stream_rd(GMimeStream * stream, void *buffer, size_t size)
+{
+ ssize_t result;
+
+ result = g_mime_stream_read(stream, buffer, size);
+ if (result == -1 && g_mime_stream_eos(stream))
+ result = 0;
+
+ return result;
+}
+
+
+/*
+ * callback to write data to a stream
+ */
+static ssize_t
+g_mime_gpgme_stream_wr(GMimeStream * stream, void *buffer, size_t size)
+{
+ return g_mime_stream_write(stream, buffer, size);
+}
+
+
+/*
+ * dummy function for callback based gpgme data objects
+ */
+static void
+cb_data_release(void *handle)
+{
+ /* must just be present... bug or feature?!? */
+}
+
+
+/*
+ * create a GpgME context for the passed protocol
+ */
+static gpgme_error_t
+gpgme_new_with_protocol(gpgme_ctx_t * ctx, gpgme_protocol_t protocol,
+ GtkWindow * parent, GError ** error)
+{
+ gpgme_error_t err;
+
+
+ /* create the GpgME context */
+ if ((err = gpgme_new(ctx)) != GPG_ERR_NO_ERROR) {
+ g_set_error_from_gpgme(error, err, _("could not create context"));
+ } else {
+ if ((err = gpgme_set_protocol(*ctx, protocol)) != GPG_ERR_NO_ERROR) {
+ gchar *errmsg =
+ g_strdup_printf(_("could not set protocol '%s'"),
+ gpgme_get_protocol_name(protocol));
+
+ g_set_error_from_gpgme(error, err, errmsg);
+ g_free(errmsg);
+ gpgme_release(*ctx);
+ } else {
+ if (protocol == GPGME_PROTOCOL_OpenPGP)
+ gpgme_set_passphrase_cb(*ctx, gpgme_passphrase_cb, parent);
+ }
+ }
+
+ return err;
+}
+
+
+/*
+ * Get a key for name. If secret_only is set, choose only secret (private)
+ * keys (signing). Otherwise, choose only public keys (encryption).
+ * If multiple keys would match, call the key selection CB (if present). If
+ * no matching key could be found or if any error occurs, return NULL and
+ * set error.
+ */
+#define KEY_IS_OK(k) (!((k)->expired || (k)->revoked || \
+ (k)->disabled || (k)->invalid))
+static gpgme_key_t
+get_key_from_name(gpgme_ctx_t ctx, const gchar * name, gboolean secret,
+ gboolean accept_all, GtkWindow * parent, GError ** error)
+{
+ GList *keys = NULL;
+ gpgme_key_t key;
+ gpgme_error_t err;
+ gboolean found_bad;
+ time_t now = time(NULL);
+
+ /* let gpgme list keys */
+ if ((err =
+ gpgme_op_keylist_start(ctx, name, secret)) != GPG_ERR_NO_ERROR) {
+ gchar *msg =
+ g_strdup_printf(_("could not list keys for \"%s\""), name);
+
+ g_set_error_from_gpgme(error, err, msg);
+ g_free(msg);
+ return NULL;
+ }
+
+ found_bad = FALSE;
+ while ((err = gpgme_op_keylist_next(ctx, &key)) == GPG_ERR_NO_ERROR) {
+ /* check if this key and the relevant subkey are usable */
+ if (KEY_IS_OK(key)) {
+ gpgme_subkey_t subkey = key->subkeys;
+
+ while (subkey && ((secret && !subkey->can_sign) ||
+ (!secret && !subkey->can_encrypt)))
+ subkey = subkey->next;
+
+ if (subkey && KEY_IS_OK(subkey) &&
+ (subkey->expires == 0 || subkey->expires > now))
+ keys = g_list_append(keys, key);
+ else
+ found_bad = TRUE;
+ } else
+ found_bad = TRUE;
+ }
+
+ if (gpg_err_code(err) != GPG_ERR_EOF) {
+ gchar *msg =
+ g_strdup_printf(_("could not list keys for \"%s\""), name);
+
+ g_set_error_from_gpgme(error, err, msg);
+ g_free(msg);
+ gpgme_op_keylist_end(ctx);
+ g_list_foreach(keys, (GFunc) gpgme_key_unref, NULL);
+ g_list_free(keys);
+ return NULL;
+ }
+ gpgme_op_keylist_end(ctx);
+
+ if (!keys) {
+ if (error) {
+ if (strchr(name, '@')) {
+ if (found_bad)
+ g_set_error(error, GPGME_ERROR_QUARK,
+ GPG_ERR_KEY_SELECTION,
+ _
+ ("%s: a key for %s is present, but it is expired, disabled, revoked or invalid"),
+ "gmime-gpgme", name);
+ else
+ g_set_error(error, GPGME_ERROR_QUARK,
+ GPG_ERR_KEY_SELECTION,
+ _("%s: could not find a key for %s"),
+ "gmime-gpgme", name);
+ } else {
+ if (found_bad)
+ g_set_error(error, GPGME_ERROR_QUARK,
+ GPG_ERR_KEY_SELECTION,
+ _
+ ("%s: a key with id %s is present, but it is expired, disabled, revoked or invalid"),
+ "gmime-gpgme", name);
+ else
+ g_set_error(error, GPGME_ERROR_QUARK,
+ GPG_ERR_KEY_SELECTION,
+ _("%s: could not find a key with id %s"),
+ "gmime-gpgme", name);
+ }
+ }
+ return NULL;
+ }
+
+ /* let the user select a key from the list if there is more than one */
+ if (g_list_length(keys) > 1) {
+ if (select_key_cb)
+ key =
+ select_key_cb(name, secret, keys, gpgme_get_protocol(ctx),
+ parent);
+ else {
+ if (error)
+ g_set_error(error, GPGME_ERROR_QUARK,
+ GPG_ERR_KEY_SELECTION,
+ _("%s: multiple keys for %s"), "gmime-gpgme",
+ name);
+ key = NULL;
+ }
+ if (key)
+ gpgme_key_ref(key);
+ g_list_foreach(keys, (GFunc) gpgme_key_unref, NULL);
+ } else
+ key = (gpgme_key_t) keys->data;
+ g_list_free(keys);
+
+ /* OpenPGP: ask the user if a low-validity key should be trusted for
+ * encryption */
+ // FIXME - shouldn't we do the same for S/MIME?
+ if (key && !secret && !accept_all
+ && gpgme_get_protocol(ctx) == GPGME_PROTOCOL_OpenPGP) {
+ gpgme_user_id_t uid = key->uids;
+ gchar *upcase_name = g_ascii_strup(name, -1);
+ gboolean found = FALSE;
+
+ while (!found && uid) {
+ /* check the email field which may or may not be present */
+ if (uid->email && !g_ascii_strcasecmp(uid->email, name))
+ found = TRUE;
+ else {
+ /* no email or no match, check the uid */
+ gchar *upcase_uid = g_ascii_strup(uid->uid, -1);
+
+ if (strstr(upcase_uid, upcase_name))
+ found = TRUE;
+ else
+ uid = uid->next;
+ g_free(upcase_uid);
+ }
+ }
+ g_free(upcase_name);
+
+ /* ask the user if a low-validity key shall be used */
+ if (uid && uid->validity < GPGME_VALIDITY_FULL) {
+ if (!accept_low_trust_cb
+ || !accept_low_trust_cb(name, uid, parent)) {
+ gpgme_key_unref(key);
+ key = NULL;
+ if (error)
+ g_set_error(error, GPGME_ERROR_QUARK,
+ GPG_ERR_KEY_SELECTION,
+ _("%s: insufficient validity for uid %s"),
+ "gmime-gpgme", name);
+ }
+ }
+ }
+
+ return key;
+}
+
+
+/*
+ * Add signer to ctx's list of signers and return TRUE on success or FALSE
+ * on error.
+ */
+static gboolean
+gpgme_add_signer(gpgme_ctx_t ctx, const gchar * signer, GtkWindow * parent,
+ GError ** error)
+{
+ gpgme_key_t key = NULL;
+
+ /* note: private (secret) key has never low trust... */
+ if (!
+ (key = get_key_from_name(ctx, signer, TRUE, FALSE, parent, error)))
+ return FALSE;
+
+ /* set the key (the previous operation guaranteed that it exists, no
+ * need 2 check return values...) */
+ gpgme_signers_add(ctx, key);
+ gpgme_key_unref(key);
+
+ return TRUE;
+}
+
+
+/*
+ * Build a NULL-terminated array of keys for all recipients in rcpt_list
+ * and return it. The caller has to take care that it's released. If
+ * something goes wrong, NULL is returned.
+ */
+static gpgme_key_t *
+gpgme_build_recipients(gpgme_ctx_t ctx, GPtrArray * rcpt_list,
+ gboolean accept_low_trust, GtkWindow * parent,
+ GError ** error)
+{
+ gpgme_key_t *rcpt = g_new0(gpgme_key_t, rcpt_list->len + 1);
+ guint num_rcpts;
+
+ /* try to find the public key for every recipient */
+ for (num_rcpts = 0; num_rcpts < rcpt_list->len; num_rcpts++) {
+ gchar *name = (gchar *) g_ptr_array_index(rcpt_list, num_rcpts);
+ gpgme_key_t key;
+
+ if (!
+ (key =
+ get_key_from_name(ctx, name, FALSE, accept_low_trust, parent,
+ error))) {
+ release_keylist(rcpt);
+ return NULL;
+ }
+
+ /* set the recipient */
+ rcpt[num_rcpts] = key;
+ }
+
+ return rcpt;
+}
+
+
+/*
+ * helper function: unref all keys in the NULL-terminated array keylist and
+ * finally release the array itself
+ */
+static void
+release_keylist(gpgme_key_t * keylist)
+{
+ gpgme_key_t *key = keylist;
+
+ while (*key) {
+ gpgme_key_unref(*key);
+ key++;
+ }
+ g_free(keylist);
+}
+
+
+#if defined(ENABLE_NLS)
+/*
+ * convert a locale name to utf-8
+ */
+static const gchar *
+get_utf8_locale(int category)
+{
+ gchar *locale;
+ static gchar localebuf[64]; /* should be large enough */
+ gchar *dot;
+
+ if (!(locale = setlocale(category, NULL)))
+ return NULL;
+ strncpy(localebuf, locale, 57);
+ localebuf[57] = '\0';
+ dot = strchr(localebuf, '.');
+ if (!dot)
+ dot = localebuf + strlen(localebuf);
+ strcpy(dot, ".UTF-8");
+ return localebuf;
+}
+#endif
diff --git a/libbalsa/libbalsa-gpgme.h b/libbalsa/libbalsa-gpgme.h
new file mode 100644
index 0000000..3a865f5
--- /dev/null
+++ b/libbalsa/libbalsa-gpgme.h
@@ -0,0 +1,115 @@
+/* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
+/*
+ * gpgme low-level stuff for gmime/balsa
+ * Copyright (C) 2011 Albrecht Dreà <albrecht dress arcor de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef LIBBALSA_GPGME_H_
+#define LIBBALSA_GPGME_H_
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gpgme.h>
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <gmime/gmime.h>
+#include "gmime-gpgme-signature.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#ifdef MAKE_EMACS_HAPPY
+}
+#endif
+#endif /* __cplusplus */
+
+
+#define GPG_ERR_KEY_SELECTION GPG_ERR_USER_14
+#define GPG_ERR_TRY_AGAIN GPG_ERR_USER_15
+#define GPG_ERR_NOT_SIGNED GPG_ERR_USER_16
+
+#define GPGME_ERROR_QUARK (g_quark_from_static_string("gmime-gpgme"))
+
+
+/** Callback to select a key from a list
+ * Parameters:
+ * - user name
+ * - TRUE is a secret key shall be selected
+ * - list of available keys (gpgme_key_t data elements)
+ * - protocol
+ * - parent window
+ */ typedef gpgme_key_t(*lbgpgme_select_key_cb) (const gchar *,
+ gboolean,
+ GList *,
+ gpgme_protocol_t,
+ GtkWindow *);
+
+/** Callback to ask the user whether a key with low trust shall be accepted
+ * Parameters:
+ * - user name
+ * - GpgME user ID
+ * - parent window
+ */
+typedef gboolean(*lbgpgme_accept_low_trust_cb) (const gchar *,
+ const gpgme_user_id_t,
+ GtkWindow *);
+
+
+
+void libbalsa_gpgme_init(gpgme_passphrase_cb_t get_passphrase,
+ lbgpgme_select_key_cb select_key_cb,
+ lbgpgme_accept_low_trust_cb accept_low_trust);
+gboolean libbalsa_gpgme_check_crypto_engine(gpgme_protocol_t protocol);
+
+GMimeGpgmeSigstat *libbalsa_gpgme_verify(GMimeStream * content,
+ GMimeStream * sig_plain,
+ gpgme_protocol_t protocol,
+ gboolean singlepart_mode,
+ GError ** error);
+
+gpgme_hash_algo_t libbalsa_gpgme_sign(const gchar * userid,
+ GMimeStream * istream,
+ GMimeStream * ostream,
+ gpgme_protocol_t protocol,
+ gboolean singlepart_mode,
+ GtkWindow * parent, GError ** error);
+
+int libbalsa_gpgme_encrypt(GPtrArray * recipients,
+ const char *sign_for,
+ GMimeStream * istream,
+ GMimeStream * ostream,
+ gpgme_protocol_t protocol,
+ gboolean singlepart_mode,
+ gboolean trust_all_keys,
+ GtkWindow * parent, GError ** error);
+
+GMimeGpgmeSigstat *libbalsa_gpgme_decrypt(GMimeStream * crypted,
+ GMimeStream * plain,
+ gpgme_protocol_t protocol,
+ GtkWindow * parent,
+ GError ** error);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* LIBBALSA_GPGME_H_ */
diff --git a/libbalsa/rfc3156.c b/libbalsa/rfc3156.c
index e6072c4..c11f64e 100644
--- a/libbalsa/rfc3156.c
+++ b/libbalsa/rfc3156.c
@@ -35,7 +35,7 @@
#include "libbalsa.h"
#include "libbalsa_private.h"
-#include "gmime-gpgme-context.h"
+#include "gmime-multipart-crypt.h"
#include "gmime-gpgme-signature.h"
#include "gmime-part-rfc2440.h"
@@ -52,88 +52,16 @@
# include "macosx-helpers.h"
#endif
-#include "padlock-keyhole.xpm"
#include <glib/gi18n.h>
/* local prototypes */
-static const gchar *libbalsa_gpgme_validity_to_gchar_short(gpgme_validity_t
- validity);
-static gpgme_error_t get_passphrase_cb(void *opaque, const char *uid_hint,
- const char *passph_info,
- int prev_wasbad, int fd);
-static gpgme_key_t select_key_from_list(const gchar * name,
- gboolean is_secret,
- GMimeGpgmeContext * ctx,
- GList * keys);
-static gboolean accept_low_trust_key(const gchar * name,
- gpgme_user_id_t uid,
- GMimeGpgmeContext * ctx);
static gboolean gpg_updates_trustdb(void);
-static gchar *fix_EMail_info(gchar * str);
static gboolean have_pub_key_for(gpgme_ctx_t gpgme_ctx,
InternetAddressList * recipients);
/* ==== public functions =================================================== */
-gboolean
-libbalsa_check_crypto_engine(gpgme_protocol_t protocol)
-{
- gpgme_error_t err;
-
- err = gpgme_engine_check_version(protocol);
- if (gpgme_err_code(err) != GPG_ERR_NO_ERROR) {
- gpgme_engine_info_t info;
- GString *message = g_string_new("");
- err = gpgme_get_engine_info(&info);
- if (err == GPG_ERR_NO_ERROR) {
- while (info && info->protocol != protocol)
- info = info->next;
- if (!info)
- g_string_append_printf(message,
- _
- ("Gpgme has been compiled without support for protocol %s."),
- gpgme_get_protocol_name(protocol));
- else if (info->file_name && !info->version) {
- g_string_append_printf(message,
- _
- ("Crypto engine %s is not installed properly."),
- info->file_name);
- if (protocol == GPGME_PROTOCOL_OpenPGP)
- g_string_append_printf(message,
- _(" Hint: check the `gnupg2' (preferred) or `gnupg' package."));
- else if (protocol == GPGME_PROTOCOL_CMS)
- g_string_append_printf(message,
- _(" Hint: check the `gpgsm' package."));
- } else if (info->file_name && info->version && info->req_version)
- g_string_append_printf(message,
- _
- ("Crypto engine %s version %s is installed, but at least version %s is required."),
- info->file_name, info->version,
- info->req_version);
-
- else
- g_string_append_printf(message,
- _
- ("Unknown problem with engine for protocol %s."),
- gpgme_get_protocol_name(protocol));
- } else
- g_string_append_printf(message,
- _
- ("%s: could not retrieve crypto engine information: %s."),
- gpgme_strsource(err),
- gpgme_strerror(err));
- g_string_append_printf(message,
- _("\nDisable support for protocol %s."),
- gpgme_get_protocol_name(protocol));
- libbalsa_information(LIBBALSA_INFORMATION_ERROR, "%s", message->str);
- g_string_free(message, TRUE);
- return FALSE;
- } else
- return TRUE;
-}
-
-
static gboolean
body_is_type(LibBalsaMessageBody * body, const gchar * type,
const gchar * sub_type)
@@ -266,42 +194,6 @@ libbalsa_message_body_protection(LibBalsaMessageBody * body)
return result;
}
-#if defined(HAVE_GMIME_2_6)
-static gboolean
-#ifndef HAVE_GMIME_2_5_7
-password_request_func(GMimeCipherContext * ctx, const char *user_id,
- const char *prompt_ctx, gboolean reprompt,
- GMimeStream * response, GError ** err)
-#else /* HAVE_GMIME_2_5_7 */
-password_request_func(GMimeCryptoContext * ctx, const char *user_id,
- const char *prompt_ctx, gboolean reprompt,
- GMimeStream * response, GError ** err)
-#endif /* HAVE_GMIME_2_5_7 */
-{
- gint fd;
- gchar *name_used;
- gboolean rc;
-
- fd = g_file_open_tmp(NULL, &name_used, NULL);
- if (fd < 0)
- return FALSE;
-
- rc = get_passphrase_cb(ctx, user_id, prompt_ctx, reprompt, fd)
- == GPG_ERR_NO_ERROR;
- if (rc) {
- GMimeStream *stream = g_mime_stream_fs_new(fd);
- g_mime_stream_reset(stream);
- g_mime_stream_write_to_stream(response, stream);
-
- g_object_unref(stream);
- }
-
- unlink(name_used);
- g_free(name_used);
-
- return rc;
-}
-#endif /* HAVE_GMIME_2_6 */
/* === RFC 2633/ RFC 3156 crypto routines === */
/*
@@ -317,10 +209,6 @@ libbalsa_sign_mime_object(GMimeObject ** content, const gchar * rfc822_for,
gpgme_protocol_t protocol, GtkWindow * parent,
GError ** error)
{
-#if !defined(HAVE_GMIME_2_6)
- GMimeSession *session;
-#endif /* HAVE_GMIME_2_6 */
- GMimeGpgmeContext *ctx;
GMimeMultipartSigned *mps;
/* paranoia checks */
@@ -334,70 +222,18 @@ libbalsa_sign_mime_object(GMimeObject ** content, const gchar * rfc822_for,
if (protocol == GPGME_PROTOCOL_OpenPGP && gpg_updates_trustdb())
return FALSE;
-#if defined(HAVE_GMIME_2_6)
- /* create a GMimeGpgmeContext */
- ctx = GMIME_GPGME_CONTEXT(g_mime_gpgme_context_new
- (password_request_func, protocol, error));
- if (ctx == NULL)
- return FALSE;
-#else /* HAVE_GMIME_2_6 */
- /* create a session and a GMimeGpgmeContext */
- session = g_object_new(g_mime_session_get_type(), NULL, NULL);
- ctx = GMIME_GPGME_CONTEXT(g_mime_gpgme_context_new(session, protocol,
- error));
- if (ctx == NULL) {
- g_object_unref(session);
- return FALSE;
- }
-#endif /* HAVE_GMIME_2_6 */
-
- /* set the callbacks for the passphrase entry and the key selection */
- if (g_getenv("GPG_AGENT_INFO"))
- ctx->passphrase_cb = NULL; /* use gpg-agent */
- else {
- ctx->passphrase_cb = get_passphrase_cb;
- g_object_set_data(G_OBJECT(ctx), "passphrase-info",
- _
- ("Enter passphrase to unlock the secret key for signing"));
- }
- ctx->key_select_cb = select_key_from_list;
- g_object_set_data(G_OBJECT(ctx), "parent-window", parent);
-
/* call gpgme to create the signature */
if (!(mps = g_mime_multipart_signed_new())) {
- g_object_unref(ctx);
-#if !defined(HAVE_GMIME_2_6)
- g_object_unref(session);
-#endif /* HAVE_GMIME_2_6 */
return FALSE;
}
-#ifndef HAVE_GMIME_2_5_7
- if (g_mime_multipart_signed_sign
- (mps, *content, GMIME_CIPHER_CONTEXT(ctx), rfc822_for,
- GMIME_CIPHER_HASH_DEFAULT, error) != 0)
-#else /* HAVE_GMIME_2_5_7 */
- if (g_mime_multipart_signed_sign
- (mps, *content, GMIME_CRYPTO_CONTEXT(ctx), rfc822_for,
- GMIME_DIGEST_ALGO_DEFAULT, error) != 0)
-#endif /* HAVE_GMIME_2_5_7 */
- {
+ if (g_mime_gpgme_mps_sign(mps, *content, rfc822_for, protocol, parent, error) != 0) {
g_object_unref(mps);
- g_object_unref(ctx);
-#if !defined(HAVE_GMIME_2_6)
- g_object_unref(session);
-#endif /* HAVE_GMIME_2_6 */
return FALSE;
}
- g_mime_object_set_content_type_parameter(GMIME_OBJECT(mps),
- "micalg", ctx->micalg);
g_object_unref(G_OBJECT(*content));
*content = GMIME_OBJECT(mps);
- g_object_unref(ctx);
-#if !defined(HAVE_GMIME_2_6)
- g_object_unref(session);
-#endif /* HAVE_GMIME_2_6 */
return TRUE;
}
@@ -412,10 +248,6 @@ libbalsa_encrypt_mime_object(GMimeObject ** content, GList * rfc822_for,
gpgme_protocol_t protocol, gboolean always_trust,
GtkWindow * parent, GError ** error)
{
-#if !defined(HAVE_GMIME_2_6)
- GMimeSession *session;
-#endif /* HAVE_GMIME_2_6 */
- GMimeGpgmeContext *ctx;
GMimeObject *encrypted_obj = NULL;
GPtrArray *recipients;
int result = -1;
@@ -431,32 +263,6 @@ libbalsa_encrypt_mime_object(GMimeObject ** content, GList * rfc822_for,
if (protocol == GPGME_PROTOCOL_OpenPGP && gpg_updates_trustdb())
return FALSE;
-#if !defined(HAVE_GMIME_2_6)
- /* create a session and a GMimeGpgmeContext */
- session = g_object_new(g_mime_session_get_type(), NULL, NULL);
- ctx = GMIME_GPGME_CONTEXT(g_mime_gpgme_context_new(session, protocol,
- error));
- if (ctx == NULL) {
- g_object_unref(session);
- return FALSE;
-#else /* HAVE_GMIME_2_6 */
- /* create a GMimeGpgmeContext */
- ctx = GMIME_GPGME_CONTEXT(g_mime_gpgme_context_new
- (password_request_func, protocol, error));
- if (ctx == NULL)
- return FALSE;
-#endif /* HAVE_GMIME_2_6 */
-#if !defined(HAVE_GMIME_2_6)
- }
-#endif /* HAVE_GMIME_2_6 */
-
- /* set the callback for the key selection (no secret needed here) */
- ctx->key_select_cb = select_key_from_list;
- if (!always_trust)
- ctx->key_trust_cb = accept_low_trust_key;
- ctx->always_trust_uid = always_trust;
- g_object_set_data(G_OBJECT(ctx), "parent-window", parent);
-
/* convert the key list to a GPtrArray */
recipients = g_ptr_array_new();
while (rfc822_for) {
@@ -470,60 +276,27 @@ libbalsa_encrypt_mime_object(GMimeObject ** content, GList * rfc822_for,
GMimeMultipartEncrypted *mpe = g_mime_multipart_encrypted_new();
encrypted_obj = GMIME_OBJECT(mpe);
- result =
-#ifndef HAVE_GMIME_2_5_7
- g_mime_multipart_encrypted_encrypt(mpe, *content,
- GMIME_CIPHER_CONTEXT(ctx),
- FALSE, NULL,
- recipients, error);
-#else /* HAVE_GMIME_2_5_7 */
- g_mime_multipart_encrypted_encrypt(mpe, *content,
- GMIME_CRYPTO_CONTEXT(ctx),
- FALSE, NULL,
- GMIME_DIGEST_ALGO_DEFAULT,
- recipients, error);
-#endif /* HAVE_GMIME_2_5_7 */
+ result = g_mime_gpgme_mpe_encrypt(mpe, *content, recipients, always_trust, parent, error);
}
#ifdef HAVE_SMIME
else {
GMimePart *pkcs7 =
g_mime_part_new_with_type("application", "pkcs7-mime");
- encrypted_obj = GMIME_OBJECT(pkcs7);
- ctx->singlepart_mode = TRUE;
- result =
-#ifndef HAVE_GMIME_2_5_7
- g_mime_application_pkcs7_encrypt(pkcs7, *content,
- GMIME_CIPHER_CONTEXT(ctx),
- recipients, error);
-#else /* HAVE_GMIME_2_5_7 */
- g_mime_application_pkcs7_encrypt(pkcs7, *content,
- GMIME_CRYPTO_CONTEXT(ctx),
- recipients, error);
-#endif /* HAVE_GMIME_2_5_7 */
+ result = g_mime_application_pkcs7_encrypt(pkcs7, *content, recipients, always_trust, parent, error);
}
#endif
+ g_ptr_array_free(recipients, FALSE);
/* error checking */
if (result != 0) {
- g_ptr_array_free(recipients, FALSE);
- g_object_unref(ctx);
-#if !defined(HAVE_GMIME_2_6)
- g_object_unref(session);
-#endif /* HAVE_GMIME_2_6 */
g_object_unref(encrypted_obj);
return FALSE;
- }
-
- g_ptr_array_free(recipients, FALSE);
+ } else {
g_object_unref(G_OBJECT(*content));
*content = GMIME_OBJECT(encrypted_obj);
- g_object_unref(ctx);
-#if !defined(HAVE_GMIME_2_6)
- g_object_unref(session);
-#endif /* HAVE_GMIME_2_6 */
-
return TRUE;
+ }
}
@@ -589,18 +362,8 @@ gboolean
libbalsa_body_check_signature(LibBalsaMessageBody * body,
gpgme_protocol_t protocol)
{
-#if !defined(HAVE_GMIME_2_6)
- GMimeSession *session;
-#endif /* HAVE_GMIME_2_6 */
-#ifndef HAVE_GMIME_2_5_7
- GMimeCipherContext *g_mime_ctx;
- GMimeSignatureValidity *valid;
-#else /* HAVE_GMIME_2_5_7 */
- GMimeCryptoContext *g_mime_ctx;
- GMimeSignatureList *valid;
-#endif /* HAVE_GMIME_2_5_7 */
- GMimeGpgmeContext *ctx;
GError *error = NULL;
+ GMimeGpgmeSigstat *result;
/* paranoia checks */
g_return_val_if_fail(body, FALSE);
@@ -622,56 +385,10 @@ libbalsa_body_check_signature(LibBalsaMessageBody * body,
if (body->parts->next->sig_info)
g_object_unref(G_OBJECT(body->parts->next->sig_info));
- /* try to create GMimeGpgMEContext */
-#if !defined(HAVE_GMIME_2_6)
- session = g_object_new(g_mime_session_get_type(), NULL, NULL);
- g_mime_ctx = g_mime_gpgme_context_new(session, protocol, &error);
-#else /* HAVE_GMIME_2_6 */
- g_mime_ctx =
- g_mime_gpgme_context_new(password_request_func, protocol, &error);
-#endif /* HAVE_GMIME_2_6 */
- if (g_mime_ctx == NULL) {
- if (error) {
- libbalsa_information(LIBBALSA_INFORMATION_ERROR, "%s: %s",
- _("creating a gpgme context failed"),
- error->message);
- g_error_free(error);
- } else
- libbalsa_information(LIBBALSA_INFORMATION_ERROR,
- _("creating a gpgme context failed"));
-#if !defined(HAVE_GMIME_2_6)
- g_object_unref(session);
-#endif /* HAVE_GMIME_2_6 */
- body->parts->next->sig_info = g_mime_gpgme_sigstat_new();
- body->parts->next->sig_info->status = GPGME_SIG_STAT_ERROR;
- return FALSE;
- }
- ctx = GMIME_GPGME_CONTEXT(g_mime_ctx);
-
- /* S/MIME uses the protocol application/pkcs7-signature, but some ancient
- mailers, not yet knowing RFC 2633, use application/x-pkcs7-signature,
- so tweak the context if necessary... */
- if (protocol == GPGME_PROTOCOL_CMS) {
- const char * cms_protocol =
- g_mime_object_get_content_type_parameter(GMIME_OBJECT (body->mime_part),
- "protocol");
- if (!g_ascii_strcasecmp(cms_protocol, "application/x-pkcs7-signature"))
-#ifndef HAVE_GMIME_2_5_7
- g_mime_ctx->sign_protocol = cms_protocol;
-#else /* HAVE_GMIME_2_5_7 */
- ctx->sign_protocol = cms_protocol;
-#endif /* HAVE_GMIME_2_5_7 */
- }
-
/* verify the signature */
-
libbalsa_mailbox_lock_store(body->message->mailbox);
- valid = g_mime_multipart_signed_verify(GMIME_MULTIPART_SIGNED
- (body->mime_part), g_mime_ctx,
- &error);
- libbalsa_mailbox_unlock_store(body->message->mailbox);
-
- if (valid == NULL) {
+ result = g_mime_gpgme_mps_verify(GMIME_MULTIPART_SIGNED(body->mime_part), &error);
+ if (!result || result->status != GPG_ERR_NO_ERROR) {
if (error) {
libbalsa_information(LIBBALSA_INFORMATION_ERROR, "%s: %s",
_("signature verification failed"),
@@ -681,19 +398,9 @@ libbalsa_body_check_signature(LibBalsaMessageBody * body,
libbalsa_information(LIBBALSA_INFORMATION_ERROR,
_("signature verification failed"));
}
- if (ctx->sig_state) {
- body->parts->next->sig_info = ctx->sig_state;
- g_object_ref(G_OBJECT(body->parts->next->sig_info));
- }
-#ifndef HAVE_GMIME_2_5_7
- g_mime_signature_validity_free(valid);
-#else /* HAVE_GMIME_2_5_7 */
- g_object_unref(valid);
-#endif /* HAVE_GMIME_2_5_7 */
- g_object_unref(g_mime_ctx);
-#if !defined(HAVE_GMIME_2_6)
- g_object_unref(session);
-#endif /* HAVE_GMIME_2_6 */
+
+ body->parts->next->sig_info = result;
+ libbalsa_mailbox_unlock_store(body->message->mailbox);
return TRUE;
}
@@ -704,18 +411,14 @@ libbalsa_body_check_signature(LibBalsaMessageBody * body,
* decrypted bodies. Otherwise, the original body is returned.
*/
LibBalsaMessageBody *
-libbalsa_body_decrypt(LibBalsaMessageBody * body,
- gpgme_protocol_t protocol, GtkWindow * parent)
+libbalsa_body_decrypt(LibBalsaMessageBody *body, gpgme_protocol_t protocol, GtkWindow *parent)
{
-#if !defined(HAVE_GMIME_2_6)
- GMimeSession *session;
-#endif /* HAVE_GMIME_2_6 */
- GMimeGpgmeContext *ctx;
GMimeObject *mime_obj = NULL;
GError *error = NULL;
LibBalsaMessage *message;
+ GMimeGpgmeSigstat *sig_state = NULL;
#ifdef HAVE_SMIME
- gboolean smime_signed = FALSE;
+ gboolean smime_encrypted = FALSE;
#endif
/* paranoia checks */
@@ -743,97 +446,17 @@ libbalsa_body_decrypt(LibBalsaMessageBody * body,
if (!smime_type || !GMIME_IS_PART(body->mime_part))
return body;
- if (!g_ascii_strcasecmp(smime_type, "signed-data"))
- smime_signed = TRUE;
- else if (!g_ascii_strcasecmp(smime_type, "enveloped-data"))
- smime_signed = FALSE;
- else
- return body;
+ if (!g_ascii_strcasecmp(smime_type, "enveloped-data"))
+ smime_encrypted = FALSE;
}
#endif
-#if !defined(HAVE_GMIME_2_6)
- /* create a session and a GMimeGpgmeContext */
- session = g_object_new(g_mime_session_get_type(), NULL, NULL);
- ctx = GMIME_GPGME_CONTEXT(g_mime_gpgme_context_new(session, protocol,
- &error));
-#else /* HAVE_GMIME_2_6 */
- /* create a GMimeGpgmeContext */
- ctx = GMIME_GPGME_CONTEXT(g_mime_gpgme_context_new
- (password_request_func, protocol, &error));
-#endif /* HAVE_GMIME_2_6 */
- if (ctx == NULL) {
- if (error) {
- libbalsa_information(LIBBALSA_INFORMATION_ERROR, "%s: %s",
- _("creating a gpgme context failed"),
- error->message);
- g_error_free(error);
- } else
- libbalsa_information(LIBBALSA_INFORMATION_ERROR,
- _("creating a gpgme context failed"));
-#if !defined(HAVE_GMIME_2_6)
- g_object_unref(session);
-#endif /* HAVE_GMIME_2_6 */
- return body;
- }
-
- /* set the callback for the passphrase entry */
- if (g_getenv("GPG_AGENT_INFO"))
- ctx->passphrase_cb = NULL; /* use gpg-agent */
- else {
- ctx->passphrase_cb = get_passphrase_cb;
- g_object_set_data(G_OBJECT(ctx), "parent-window", parent);
- g_object_set_data(G_OBJECT(ctx), "passphrase-info",
- _("Enter passphrase to decrypt message"));
- }
-
libbalsa_mailbox_lock_store(body->message->mailbox);
if (protocol == GPGME_PROTOCOL_OpenPGP)
- mime_obj =
-#ifndef HAVE_GMIME_2_5_7
- g_mime_multipart_encrypted_decrypt(GMIME_MULTIPART_ENCRYPTED(body->mime_part),
- GMIME_CIPHER_CONTEXT(ctx),
- &error);
-#else /* HAVE_GMIME_2_5_7 */
- g_mime_multipart_encrypted_decrypt(GMIME_MULTIPART_ENCRYPTED(body->mime_part),
- GMIME_CRYPTO_CONTEXT(ctx),
- NULL,
- &error);
-#endif /* HAVE_GMIME_2_5_7 */
+ mime_obj = g_mime_gpgme_mpe_decrypt(GMIME_MULTIPART_ENCRYPTED(body->mime_part), &sig_state, parent, &error);
#ifdef HAVE_SMIME
- else if (smime_signed) {
-#ifndef HAVE_GMIME_2_5_7
- GMimeSignatureValidity *valid;
-#else /* HAVE_GMIME_2_5_7 */
- GMimeSignatureList *valid;
-#endif /* HAVE_GMIME_2_5_7 */
-
- ctx->singlepart_mode = TRUE;
-#ifndef HAVE_GMIME_2_5_7
- mime_obj =
- g_mime_application_pkcs7_verify(GMIME_PART(body->mime_part),
- &valid,
- GMIME_CIPHER_CONTEXT(ctx),
- &error);
- g_mime_signature_validity_free(valid);
- } else
- mime_obj =
- g_mime_application_pkcs7_decrypt(GMIME_PART(body->mime_part),
- GMIME_CIPHER_CONTEXT(ctx),
- &error);
-#else /* HAVE_GMIME_2_5_7 */
- mime_obj =
- g_mime_application_pkcs7_verify(GMIME_PART(body->mime_part),
- &valid,
- GMIME_CRYPTO_CONTEXT(ctx),
- &error);
- g_object_unref(valid);
- } else
- mime_obj =
- g_mime_application_pkcs7_decrypt(GMIME_PART(body->mime_part),
- GMIME_CRYPTO_CONTEXT(ctx),
- &error);
-#endif /* HAVE_GMIME_2_5_7 */
+ else
+ mime_obj = g_mime_application_pkcs7_decrypt_verify(GMIME_PART(body->mime_part), &sig_state, parent, &error);
#endif
libbalsa_mailbox_unlock_store(body->message->mailbox);
@@ -848,10 +471,6 @@ libbalsa_body_decrypt(LibBalsaMessageBody * body,
} else
libbalsa_information(LIBBALSA_INFORMATION_ERROR,
_("decryption failed"));
- g_object_unref(ctx);
-#if !defined(HAVE_GMIME_2_6)
- g_object_unref(session);
-#endif /* HAVE_GMIME_2_6 */
return body;
}
message = body->message;
@@ -863,18 +482,14 @@ libbalsa_body_decrypt(LibBalsaMessageBody * body,
body->was_encrypted = TRUE;
#ifdef HAVE_SMIME
else
- body->was_encrypted = !smime_signed;
+ body->was_encrypted = smime_encrypted;
#endif
libbalsa_message_body_set_mime_body(body, mime_obj);
- if (ctx->sig_state && ctx->sig_state->status != GPG_ERR_NOT_SIGNED) {
- g_object_ref(ctx->sig_state);
- body->sig_info = ctx->sig_state;
- }
- g_object_unref(ctx);
-#if !defined(HAVE_GMIME_2_6)
- g_object_unref(session);
-#endif /* HAVE_GMIME_2_6 */
+ if (sig_state && sig_state->status != GPG_ERR_NOT_SIGNED)
+ body->sig_info = sig_state;
+ else
+ g_object_unref(G_OBJECT(sig_state));
return body;
}
@@ -883,14 +498,9 @@ libbalsa_body_decrypt(LibBalsaMessageBody * body,
/* routines dealing with RFC2440 */
gboolean
-libbalsa_rfc2440_sign_encrypt(GMimePart * part, const gchar * sign_for,
- GList * encrypt_for, gboolean always_trust,
- GtkWindow * parent, GError ** error)
+libbalsa_rfc2440_sign_encrypt(GMimePart *part, const gchar *sign_for, GList *encrypt_for, gboolean always_trust,
+ GtkWindow *parent, GError **error)
{
-#if !defined(HAVE_GMIME_2_6)
- GMimeSession *session;
-#endif /* HAVE_GMIME_2_6 */
- GMimeGpgmeContext *ctx;
GPtrArray *recipients;
gint result;
@@ -902,42 +512,6 @@ libbalsa_rfc2440_sign_encrypt(GMimePart * part, const gchar * sign_for,
if (gpg_updates_trustdb())
return FALSE;
-#if !defined(HAVE_GMIME_2_6)
- /* create a session and a GMimeGpgmeContext */
- session = g_object_new(g_mime_session_get_type(), NULL, NULL);
- ctx = GMIME_GPGME_CONTEXT(g_mime_gpgme_context_new(session,
- GPGME_PROTOCOL_OpenPGP,
- error));
- if (ctx == NULL) {
- g_object_unref(session);
-#else /* HAVE_GMIME_2_6 */
- /* create a GMimeGpgmeContext */
- ctx = GMIME_GPGME_CONTEXT(g_mime_gpgme_context_new
- (password_request_func, GPGME_PROTOCOL_OpenPGP, error));
- if (ctx == NULL)
-#endif /* HAVE_GMIME_2_6 */
- return FALSE;
-#if !defined(HAVE_GMIME_2_6)
- }
-#endif /* HAVE_GMIME_2_6 */
-
- /* set the callback for the key selection and the passphrase */
- if (sign_for) {
- if (g_getenv("GPG_AGENT_INFO"))
- ctx->passphrase_cb = NULL; /* use gpg-agent */
- else {
- ctx->passphrase_cb = get_passphrase_cb;
- g_object_set_data(G_OBJECT(ctx), "passphrase-info",
- _
- ("Enter passphrase to unlock the secret key for signing"));
- }
- }
- ctx->key_select_cb = select_key_from_list;
- if (!always_trust)
- ctx->key_trust_cb = accept_low_trust_key;
- ctx->always_trust_uid = always_trust;
- g_object_set_data(G_OBJECT(ctx), "parent-window", parent);
-
/* convert the key list to a GPtrArray */
if (encrypt_for) {
recipients = g_ptr_array_new();
@@ -949,16 +523,10 @@ libbalsa_rfc2440_sign_encrypt(GMimePart * part, const gchar * sign_for,
recipients = NULL;
/* sign and/or encrypt */
- result =
- g_mime_part_rfc2440_sign_encrypt(part, ctx, recipients, sign_for,
- error);
+ result = g_mime_part_rfc2440_sign_encrypt(part, sign_for, recipients, always_trust, parent, error);
/* clean up */
if (recipients)
g_ptr_array_free(recipients, FALSE);
- g_object_unref(ctx);
-#if !defined(HAVE_GMIME_2_6)
- g_object_unref(session);
-#endif /* HAVE_GMIME_2_6 */
return (result == 0) ? TRUE : FALSE;
}
@@ -970,15 +538,7 @@ libbalsa_rfc2440_sign_encrypt(GMimePart * part, const gchar * sign_for,
gpgme_error_t
libbalsa_rfc2440_verify(GMimePart * part, GMimeGpgmeSigstat ** sig_info)
{
-#if !defined(HAVE_GMIME_2_6)
- GMimeSession *session;
-#endif /* HAVE_GMIME_2_6 */
- GMimeGpgmeContext *ctx;
-#ifndef HAVE_GMIME_2_5_7
- GMimeSignatureValidity *valid;
-#else /* HAVE_GMIME_2_5_7 */
- GMimeSignatureList *valid;
-#endif /* HAVE_GMIME_2_5_7 */
+ GMimeGpgmeSigstat *result;
GError *error = NULL;
gpgme_error_t retval;
@@ -995,36 +555,9 @@ libbalsa_rfc2440_verify(GMimePart * part, GMimeGpgmeSigstat ** sig_info)
if (gpg_updates_trustdb())
return GPG_ERR_TRY_AGAIN;
-#if !defined(HAVE_GMIME_2_6)
- /* create a session and a GMimeGpgmeContext */
- session = g_object_new(g_mime_session_get_type(), NULL, NULL);
- ctx = GMIME_GPGME_CONTEXT(g_mime_gpgme_context_new(session,
- GPGME_PROTOCOL_OpenPGP,
- &error));
-#else /* HAVE_GMIME_2_6 */
- /* create a GMimeGpgmeContext */
- ctx = GMIME_GPGME_CONTEXT(g_mime_gpgme_context_new
- (password_request_func, GPGME_PROTOCOL_OpenPGP, &error));
-#endif /* HAVE_GMIME_2_6 */
- if (ctx == NULL) {
- if (error) {
- libbalsa_information(LIBBALSA_INFORMATION_ERROR, "%s: %s",
- _("creating a gpgme context failed"),
- error->message);
- g_error_free(error);
- } else
- libbalsa_information(LIBBALSA_INFORMATION_ERROR,
- _("creating a gpgme context failed"));
-#if !defined(HAVE_GMIME_2_6)
- g_object_unref(session);
-#endif /* HAVE_GMIME_2_6 */
- return FALSE;
- }
-
/* verify */
- valid = g_mime_part_rfc2440_verify(part, ctx, &error);
-
- if (valid == NULL) {
+ result = g_mime_part_rfc2440_verify(part, &error);
+ if (!result || result->status != GPG_ERR_NO_ERROR) {
if (error) {
libbalsa_information(LIBBALSA_INFORMATION_ERROR, "%s: %s",
_("signature verification failed"),
@@ -1036,30 +569,16 @@ libbalsa_rfc2440_verify(GMimePart * part, GMimeGpgmeSigstat ** sig_info)
_("signature verification failed"));
retval = GPG_ERR_GENERAL;
}
- g_object_unref(ctx);
-#if !defined(HAVE_GMIME_2_6)
- g_object_unref(session);
-#endif /* HAVE_GMIME_2_6 */
- return retval;
- }
+ } else
+ retval = result->status;
/* return the signature info if requested */
- if (sig_info) {
- g_object_ref(ctx->sig_state);
- *sig_info = ctx->sig_state;
+ if (result) {
+ if (sig_info)
+ *sig_info = result;
+ else
+ g_object_unref(G_OBJECT(result));
}
-
- /* clean up */
-#ifndef HAVE_GMIME_2_5_7
- g_mime_signature_validity_free(valid);
-#else /* HAVE_GMIME_2_5_7 */
- g_object_unref(valid);
-#endif /* HAVE_GMIME_2_5_7 */
- retval = ctx->sig_state->status;
- g_object_unref(ctx);
-#if !defined(HAVE_GMIME_2_6)
- g_object_unref(session);
-#endif /* HAVE_GMIME_2_6 */
return retval;
}
@@ -1073,11 +592,8 @@ gpgme_error_t
libbalsa_rfc2440_decrypt(GMimePart * part, GMimeGpgmeSigstat ** sig_info,
GtkWindow * parent)
{
-#if !defined(HAVE_GMIME_2_6)
- GMimeSession *session;
-#endif /* HAVE_GMIME_2_6 */
- GMimeGpgmeContext *ctx;
GError *error = NULL;
+ GMimeGpgmeSigstat *result;
gpgme_error_t retval;
/* paranoia checks */
@@ -1093,44 +609,11 @@ libbalsa_rfc2440_decrypt(GMimePart * part, GMimeGpgmeSigstat ** sig_info,
if (gpg_updates_trustdb())
return GPG_ERR_TRY_AGAIN;
-#if !defined(HAVE_GMIME_2_6)
- /* create a session and a GMimeGpgmeContext */
- session = g_object_new(g_mime_session_get_type(), NULL, NULL);
- ctx =
- GMIME_GPGME_CONTEXT(g_mime_gpgme_context_new
- (session, GPGME_PROTOCOL_OpenPGP, &error));
-#else /* HAVE_GMIME_2_6 */
- /* create a GMimeGpgmeContext */
- ctx = GMIME_GPGME_CONTEXT(g_mime_gpgme_context_new
- (password_request_func, GPGME_PROTOCOL_OpenPGP, &error));
-#endif /* HAVE_GMIME_2_6 */
- if (ctx == NULL) {
- if (error) {
- libbalsa_information(LIBBALSA_INFORMATION_ERROR, "%s: %s",
- _("creating a gpgme context failed"),
- error->message);
- g_error_free(error);
- } else
- libbalsa_information(LIBBALSA_INFORMATION_ERROR,
- _("creating a gpgme context failed"));
-#if !defined(HAVE_GMIME_2_6)
- g_object_unref(session);
-#endif /* HAVE_GMIME_2_6 */
- return GPG_ERR_GENERAL;
- }
-
- /* set the callback for the passphrase */
- if (g_getenv("GPG_AGENT_INFO"))
- ctx->passphrase_cb = NULL; /* use gpg-agent */
- else {
- ctx->passphrase_cb = get_passphrase_cb;
- g_object_set_data(G_OBJECT(ctx), "passphrase-info",
- _("Enter passphrase to decrypt message"));
- g_object_set_data(G_OBJECT(ctx), "parent-window", parent);
- }
+ // g_object_set_data(G_OBJECT(ctx), "parent-window", parent); FIXME - pass downstream
/* decrypt */
- if (g_mime_part_rfc2440_decrypt(part, ctx, &error) == NULL) {
+ result = g_mime_part_rfc2440_decrypt(part, parent, &error);
+ if (result == NULL) {
if (error) {
if (error->code != GPG_ERR_CANCELED)
libbalsa_information(LIBBALSA_INFORMATION_ERROR, "%s: %s",
@@ -1144,28 +627,18 @@ libbalsa_rfc2440_decrypt(GMimePart * part, GMimeGpgmeSigstat ** sig_info,
("decryption and signature verification failed"));
retval = GPG_ERR_GENERAL;
}
- g_object_unref(ctx);
-#if !defined(HAVE_GMIME_2_6)
- g_object_unref(session);
-#endif /* HAVE_GMIME_2_6 */
return retval;
- }
+ } else
+ retval = result->status;
- retval = GPG_ERR_NO_ERROR;
- if (ctx->sig_state) {
- retval = ctx->sig_state->status;
- /* return the signature info if requested & available */
- if (sig_info && ctx->sig_state->status != GPG_ERR_NOT_SIGNED) {
- g_object_ref(ctx->sig_state);
- *sig_info = ctx->sig_state;
- }
+ /* return the signature info if requested */
+ if (result) {
+ if (sig_info && result->status != GPG_ERR_NOT_SIGNED)
+ *sig_info = result;
+ else
+ g_object_unref(G_OBJECT(result));
}
- /* clean up */
- g_object_unref(ctx);
-#if !defined(HAVE_GMIME_2_6)
- g_object_unref(session);
-#endif /* HAVE_GMIME_2_6 */
return retval;
}
@@ -1204,6 +677,8 @@ libbalsa_gpgme_sig_stat_to_gchar(gpgme_error_t stat)
return _("An error prevented the signature verification.");
}
}
+
+
const gchar *
libbalsa_gpgme_validity_to_gchar(gpgme_validity_t validity)
{
@@ -1224,6 +699,30 @@ libbalsa_gpgme_validity_to_gchar(gpgme_validity_t validity)
return _("bad validity");
}
}
+
+
+const gchar *
+libbalsa_gpgme_validity_to_gchar_short(gpgme_validity_t validity)
+{
+ switch (validity) {
+ case GPGME_VALIDITY_UNKNOWN:
+ return _("unknown");
+ case GPGME_VALIDITY_UNDEFINED:
+ return _("undefined");
+ case GPGME_VALIDITY_NEVER:
+ return _("never");
+ case GPGME_VALIDITY_MARGINAL:
+ return _("marginal");
+ case GPGME_VALIDITY_FULL:
+ return _("full");
+ case GPGME_VALIDITY_ULTIMATE:
+ return _("ultimate");
+ default:
+ return _("bad validity");
+ }
+}
+
+
const gchar *
libbalsa_gpgme_sig_protocol_name(gpgme_protocol_t protocol)
{
@@ -1237,14 +736,18 @@ libbalsa_gpgme_sig_protocol_name(gpgme_protocol_t protocol)
}
}
-#define APPEND_TIMET(str,fmt,t) \
- do { \
- if (t) { \
- gchar * _tbuf = libbalsa_date_to_utf8(&t, date_string); \
- g_string_append_printf(str, fmt, _tbuf); \
- g_free(_tbuf); \
- } \
- } while (0)
+static inline void
+append_time_t(GString *str, const gchar *format, time_t *when,
+ const gchar * date_string)
+{
+ if (*when != (time_t) 0) {
+ gchar *tbuf = libbalsa_date_to_utf8(when, date_string);
+ g_string_append_printf(str, format, tbuf);
+ g_free(tbuf);
+ } else {
+ g_string_append_printf(str, format, _("never"));
+ }
+}
gchar *
libbalsa_signature_info_to_gchar(GMimeGpgmeSigstat * info,
@@ -1259,74 +762,124 @@ libbalsa_signature_info_to_gchar(GMimeGpgmeSigstat * info,
msg =
g_string_append(msg,
libbalsa_gpgme_sig_stat_to_gchar(info->status));
- if (info->sign_uid && strlen(info->sign_uid))
- g_string_append_printf(msg, _("\nUser ID: %s"), info->sign_uid);
-
- else if (info->sign_name && strlen(info->sign_name)) {
- g_string_append_printf(msg, _("\nSigned by: %s"), info->sign_name);
- if (info->sign_email && strlen(info->sign_email))
- g_string_append_printf(msg, " <%s>", info->sign_email);
- } else if (info->sign_email && strlen(info->sign_email))
- g_string_append_printf(msg, _("\nMail address: %s"),
- info->sign_email);
- APPEND_TIMET(msg, _("\nSigned on: %s"), info->sign_time);
- g_string_append_printf(msg, _("\nUser ID validity: %s"),
+ g_string_append_printf(msg, _("\nSignature validity: %s"),
libbalsa_gpgme_validity_to_gchar(info->
validity));
- if (info->protocol == GPGME_PROTOCOL_OpenPGP)
+ append_time_t(msg, _("\nSigned on: %s"), &info->sign_time, date_string);
+ if (info->protocol == GPGME_PROTOCOL_OpenPGP && info->key)
g_string_append_printf(msg, _("\nKey owner trust: %s"),
libbalsa_gpgme_validity_to_gchar_short
- (info->trust));
+ (info->key->owner_trust));
if (info->fingerprint)
g_string_append_printf(msg, _("\nKey fingerprint: %s"),
info->fingerprint);
- /* Subkey creation date */
- APPEND_TIMET(msg, _("\nSubkey created on: %s"), info->key_created);
- /* Subkey expiration date */
- APPEND_TIMET(msg, _("\nSubkey expires on: %s"), info->key_expires);
- if (info->key_revoked || info->key_expired || info->key_disabled ||
- info->key_invalid) {
+
+ /* add key information */
+ if (info->key) {
+ gpgme_user_id_t uid;
+ gpgme_subkey_t subkey;
+
+ /* user ID's */
+ if ((uid = info->key->uids)) {
+ gchar *lead_text;
+
+ uid = info->key->uids;
+ if (uid->next) {
+ msg = g_string_append(msg, _("\nUser ID's:"));
+ lead_text = "\n\342\200\242";
+ } else {
+ msg = g_string_append(msg, _("\nUser ID:"));
+ lead_text = "";
+ }
+
+ /* Note: there is no way to determine which user id has been used
+ * to create the signature. A broken client may even use an
+ * invalid and/or revoked one. We therefore add all to the
+ * result. */
+ while (uid) {
+ msg = g_string_append(msg, lead_text);
+ if (uid->revoked)
+ msg = g_string_append(msg, _(" [Revoked]"));
+ if (uid->invalid)
+ msg = g_string_append(msg, _(" [Invalid]"));
+
+ if (uid->uid && *(uid->uid)) {
+ gchar *uid_readable =
+ libbalsa_cert_subject_readable(uid->uid);
+ g_string_append_printf(msg, " %s", uid_readable);
+ g_free(uid_readable);
+ } else {
+ if (uid->name && *(uid->name))
+ g_string_append_printf(msg, " %s", uid->name);
+ if (uid->email && *(uid->email))
+ g_string_append_printf(msg, " <%s>", uid->email);
+ if (uid->comment && *(uid->comment))
+ g_string_append_printf(msg, " (%s)", uid->comment);
+ }
+
+ uid = uid->next;
+ }
+ }
+
+ /* subkey */
+ if ((subkey = info->key->subkeys)) {
+ /* find the one which can sign */
+ while (subkey && !subkey->can_sign)
+ subkey = subkey->next;
+
+ if (subkey) {
+ append_time_t(msg, _("\nSubkey created on: %s"),
+ &subkey->timestamp, date_string);
+ append_time_t(msg, _("\nSubkey expires on: %s"),
+ &subkey->expires, date_string);
+ if (subkey->revoked || subkey->expired || subkey->disabled ||
+ subkey->invalid) {
GString * attrs = g_string_new("");
int count = 0;
- if (info->key_revoked) {
+ if (subkey->revoked) {
count++;
attrs = g_string_append(attrs, _(" revoked"));
}
- if (info->key_expired) {
+ if (subkey->expired) {
if (count++)
attrs = g_string_append_c(attrs, ',');
attrs = g_string_append(attrs, _(" expired"));
}
- if (info->key_disabled) {
+ if (subkey->disabled) {
if (count)
attrs = g_string_append_c(attrs, ',');
attrs = g_string_append(attrs, _(" disabled"));
}
- if (info->key_invalid) {
+ if (subkey->invalid) {
if (count)
attrs = g_string_append_c(attrs, ',');
attrs = g_string_append(attrs, _(" invalid"));
}
- /* ngettext: string begins with a single space, so no space after
- * the colon is correct punctuation (in English). */
+ /* ngettext: string begins with a single space, so no space
+ * after the colon is correct punctuation (in English). */
g_string_append_printf(msg, ngettext("\nSubkey attribute:%s",
"\nSubkey attributes:%s",
count),
attrs->str);
g_string_free(attrs, TRUE);
}
- if (info->issuer_name) {
- gchar * issuer = fix_EMail_info(g_strdup(info->issuer_name));
-
- g_string_append_printf(msg, _("\nIssuer name: %s"), issuer);
- g_free(issuer);
+ }
+ }
+
+ if (info->key->issuer_name) {
+ gchar *issuer_name =
+ libbalsa_cert_subject_readable(info->key->issuer_name);
+ g_string_append_printf(msg, _("\nIssuer name: %s"), issuer_name);
+ g_free(issuer_name);
}
- if (info->issuer_serial)
+ if (info->key->issuer_serial)
g_string_append_printf(msg, _("\nIssuer serial number: %s"),
- info->issuer_serial);
- if (info->chain_id)
- g_string_append_printf(msg, _("\nChain ID: %s"), info->chain_id);
+ info->key->issuer_serial);
+ if (info->key->chain_id)
+ g_string_append_printf(msg, _("\nChain ID: %s"), info->key->chain_id);
+ }
+
retval = msg->str;
g_string_free(msg, FALSE);
return retval;
@@ -1462,489 +1015,6 @@ check_gpg_child(gpointer data)
/* ==== local stuff ======================================================== */
-static const gchar *
-libbalsa_gpgme_validity_to_gchar_short(gpgme_validity_t validity)
-{
- switch (validity) {
- case GPGME_VALIDITY_UNKNOWN:
- return _("unknown");
- case GPGME_VALIDITY_UNDEFINED:
- return _("undefined");
- case GPGME_VALIDITY_NEVER:
- return _("never");
- case GPGME_VALIDITY_MARGINAL:
- return _("marginal");
- case GPGME_VALIDITY_FULL:
- return _("full");
- case GPGME_VALIDITY_ULTIMATE:
- return _("ultimate");
- default:
- return _("bad validity");
- }
-}
-
-
-#define OID_EMAIL "1.2.840.113549.1.9.1=#"
-#define OID_EMAIL_LEN 22
-static gchar *
-fix_EMail_info(gchar * str)
-{
- gchar *p;
- GString *result;
-
- /* check for any EMail info */
- p = strstr(str, OID_EMAIL);
- if (!p)
- return str;
-
- *p = '\0';
- p += OID_EMAIL_LEN;
- result = g_string_new(str);
- while (p) {
- gchar *next;
-
- result = g_string_append(result, "EMail=");
- /* convert the info from hex until we reach some other char */
- while (g_ascii_isxdigit(*p)) {
- gchar c = g_ascii_xdigit_value(*p++) << 4;
-
- if (g_ascii_isxdigit(*p))
- result =
- g_string_append_c(result, c + g_ascii_xdigit_value(*p++));
- }
-
- /* find more */
- next = strstr(p, OID_EMAIL);
- if (next) {
- *next = '\0';
- next += OID_EMAIL_LEN;
- }
- result = g_string_append(result, p);
- p = next;
- }
- g_free(str);
- p = result->str;
- g_string_free(result, FALSE);
- return p;
-}
-
-
-/* stuff to get a key fingerprint from a selection list */
-enum {
- GPG_KEY_USER_ID_COLUMN = 0,
- GPG_KEY_ID_COLUMN,
- GPG_KEY_LENGTH_COLUMN,
- GPG_KEY_VALIDITY_COLUMN,
- GPG_KEY_PTR_COLUMN,
- GPG_KEY_NUM_COLUMNS
-};
-
-static gchar *col_titles[] =
- { N_("User ID"), N_("Key ID"), N_("Length"), N_("Validity") };
-
-/* callback function if a new row is selected in the list */
-static void
-key_selection_changed_cb(GtkTreeSelection * selection, gpgme_key_t * key)
-{
- GtkTreeIter iter;
- GtkTreeModel *model;
-
- if (gtk_tree_selection_get_selected(selection, &model, &iter))
- gtk_tree_model_get(model, &iter, GPG_KEY_PTR_COLUMN, key, -1);
-}
-
-
-/*
- * Select a key for the mail address for_address from the gpgme_key_t's in keys
- * and return either the selected key or NULL if the dialog was cancelled.
- * secret_only controls the dialog message.
- */
-static gpgme_key_t
-select_key_from_list(const gchar * name, gboolean is_secret,
- GMimeGpgmeContext * ctx, GList * keys)
-{
- GtkWidget *dialog;
- GtkWidget *vbox;
- GtkWidget *label;
- GtkWidget *scrolled_window;
- GtkWidget *tree_view;
- GtkTreeStore *model;
- GtkTreeSelection *selection;
- GtkTreeIter iter;
- gint i, last_col;
- gchar *prompt;
- gchar *upcase_name;
- gpgme_protocol_t protocol;
- GtkWindow *parent;
- gpgme_key_t use_key = NULL;
-
- g_return_val_if_fail(ctx != NULL, NULL);
- g_return_val_if_fail(keys != NULL, NULL);
- protocol = gpgme_get_protocol(ctx->gpgme_ctx);
- parent = GTK_WINDOW(g_object_get_data(G_OBJECT(ctx), "parent-window"));
-
- /* FIXME: create dialog according to the Gnome HIG */
- dialog = gtk_dialog_new_with_buttons(_("Select key"),
- parent,
- GTK_DIALOG_DESTROY_WITH_PARENT,
- GTK_STOCK_OK, GTK_RESPONSE_OK,
- GTK_STOCK_CANCEL,
- GTK_RESPONSE_CANCEL, NULL);
-#if HAVE_MACOSX_DESKTOP
- libbalsa_macosx_menu_for_parent(dialog, parent);
-#endif
- vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
- gtk_container_add(GTK_CONTAINER
- (gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
- vbox);
- gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
- if (is_secret)
- prompt =
- g_strdup_printf(_("Select the private key for the signer %s"),
- name);
- else
- prompt = g_strdup_printf(_
- ("Select the public key for the recipient %s"),
- name);
- label = gtk_label_new(prompt);
- gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
- g_free(prompt);
- gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0);
-
- scrolled_window = gtk_scrolled_window_new(NULL, NULL);
- gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW
- (scrolled_window),
- GTK_SHADOW_ETCHED_IN);
- gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
- GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
- gtk_box_pack_start(GTK_BOX(vbox), scrolled_window, TRUE, TRUE, 0);
-
- model =
- gtk_tree_store_new(GPG_KEY_NUM_COLUMNS,
- G_TYPE_STRING, /* user ID */
- G_TYPE_STRING, /* key ID */
- G_TYPE_INT, /* length */
- G_TYPE_STRING, /* validity (gpg encrypt only) */
- G_TYPE_POINTER); /* key */
-
- tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
- selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
- gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
- g_signal_connect(G_OBJECT(selection), "changed",
- G_CALLBACK(key_selection_changed_cb), &use_key);
-
- /* add the keys */
- upcase_name = g_ascii_strup(name, -1);
- while (keys) {
- gpgme_key_t key = (gpgme_key_t) keys->data;
- gpgme_subkey_t subkey = key->subkeys;
- gpgme_user_id_t uid = key->uids;
- gchar *uid_info = NULL;
- gboolean uid_found;
-
- /* find the relevant subkey */
- while (subkey && ((is_secret && !subkey->can_sign) ||
- (!is_secret && !subkey->can_encrypt)))
- subkey = subkey->next;
-
- /* find the relevant uid */
- uid_found = FALSE;
- while (uid && !uid_found) {
- g_free(uid_info);
- uid_info = fix_EMail_info(g_strdup(uid->uid));
-
- /* check the email field which may or may not be present */
- if (uid->email && !g_ascii_strcasecmp(uid->email, name))
- uid_found = TRUE;
- else {
- /* no email or no match, check the uid */
- gchar * upcase_uid = g_ascii_strup(uid_info, -1);
-
- if (strstr(upcase_uid, upcase_name))
- uid_found = TRUE;
- else
- uid = uid->next;
- g_free(upcase_uid);
- }
- }
-
- /* append the element */
- if (subkey && uid) {
- gtk_tree_store_append(GTK_TREE_STORE(model), &iter, NULL);
- gtk_tree_store_set(GTK_TREE_STORE(model), &iter,
- GPG_KEY_USER_ID_COLUMN, uid_info,
- GPG_KEY_ID_COLUMN, subkey->keyid,
- GPG_KEY_LENGTH_COLUMN, subkey->length,
- GPG_KEY_VALIDITY_COLUMN,
- libbalsa_gpgme_validity_to_gchar_short
- (uid->validity),
- GPG_KEY_PTR_COLUMN, key,
- -1);
- }
- g_free(uid_info);
- keys = g_list_next(keys);
- }
- g_free(upcase_name);
-
- g_object_unref(G_OBJECT(model));
- /* show the validity only if we are asking for a gpg public key */
- last_col = (protocol == GPGME_PROTOCOL_CMS || is_secret) ?
- GPG_KEY_LENGTH_COLUMN : GPG_KEY_VALIDITY_COLUMN;
- for (i = 0; i <= last_col; i++) {
- GtkCellRenderer *renderer;
- GtkTreeViewColumn *column;
-
- renderer = gtk_cell_renderer_text_new();
- column =
- gtk_tree_view_column_new_with_attributes(_(col_titles[i]),
- renderer, "text", i,
- NULL);
- gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
- gtk_tree_view_column_set_resizable(column, TRUE);
- }
-
- gtk_container_add(GTK_CONTAINER(scrolled_window), tree_view);
- gtk_window_set_default_size(GTK_WINDOW(dialog), 500, 300);
- gtk_widget_show_all(gtk_dialog_get_content_area(GTK_DIALOG(dialog)));
-
- if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_OK)
- use_key = NULL;
- gtk_widget_destroy(dialog);
-
- return use_key;
-}
-
-
-/*
- * Display a dialog to select whether a key with a low trust level shall be accepted
- */
-static gboolean
-accept_low_trust_key(const gchar * name, gpgme_user_id_t uid,
- GMimeGpgmeContext * ctx)
-{
- GtkWidget *dialog;
- GtkWindow *parent;
- gint result;
- gchar *message1;
- gchar *message2;
-
- /* paranoia checks */
- g_return_val_if_fail(ctx != NULL, FALSE);
- g_return_val_if_fail(uid != NULL, FALSE);
- parent = GTK_WINDOW(g_object_get_data(G_OBJECT(ctx), "parent-window"));
-
- /* build the message */
- message1 =
- g_strdup_printf(_("Insufficient trust for recipient %s"), name);
- message2 =
- g_strdup_printf(_("The validity of the key with user ID \"%s\" is \"%s\"."),
- uid->uid,
- libbalsa_gpgme_validity_to_gchar_short(uid->validity));
- dialog =
- gtk_message_dialog_new_with_markup(parent,
- GTK_DIALOG_DESTROY_WITH_PARENT,
- GTK_MESSAGE_WARNING,
- GTK_BUTTONS_YES_NO,
- "<b>%s</b>\n\n%s\n%s",
- message1,
- message2,
- _("Use this key anyway?"));
-#if HAVE_MACOSX_DESKTOP
- libbalsa_macosx_menu_for_parent(dialog, parent);
-#endif
-
- /* ask the user */
- result = gtk_dialog_run(GTK_DIALOG(dialog));
- gtk_widget_destroy(dialog);
-
- return result == GTK_RESPONSE_YES;
-}
-
-
-/*
- * display a dialog to read the passphrase
- */
-static gchar *
-get_passphrase_real(GMimeGpgmeContext * ctx, const gchar * uid_hint,
- int prev_was_bad)
-{
- static GdkPixbuf *padlock_keyhole = NULL;
- GtkWidget *dialog, *entry, *vbox, *hbox;
- gchar *prompt, *passwd;
- const gchar *title =
- g_object_get_data(G_OBJECT(ctx), "passphrase-title");
- GtkWindow *parent = g_object_get_data(G_OBJECT(ctx), "parent-window");
-
- /* FIXME: create dialog according to the Gnome HIG */
- dialog = gtk_dialog_new_with_buttons(title, parent,
- GTK_DIALOG_DESTROY_WITH_PARENT,
- GTK_STOCK_OK, GTK_RESPONSE_OK,
- GTK_STOCK_CANCEL,
- GTK_RESPONSE_CANCEL, NULL);
-#if HAVE_MACOSX_DESKTOP
- libbalsa_macosx_menu_for_parent(dialog, parent);
-#endif
- hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12);
- gtk_container_set_border_width(GTK_CONTAINER(hbox), 12);
- gtk_container_add(GTK_CONTAINER
- (gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
- hbox);
-
- vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
- gtk_container_add(GTK_CONTAINER(hbox), vbox);
- if (!padlock_keyhole)
- padlock_keyhole =
- gdk_pixbuf_new_from_xpm_data(padlock_keyhole_xpm);
- gtk_box_pack_start(GTK_BOX(vbox),
- gtk_image_new_from_pixbuf(padlock_keyhole), FALSE,
- FALSE, 0);
- vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
- gtk_container_add(GTK_CONTAINER(hbox), vbox);
- if (prev_was_bad)
- prompt =
- g_strdup_printf(_
- ("The passphrase for this key was bad, please try again!\n\nKey: %s"),
- uid_hint);
- else
- prompt =
- g_strdup_printf(_
- ("Please enter the passphrase for the secret key!\n\nKey: %s"),
- uid_hint);
- gtk_container_add(GTK_CONTAINER(vbox), gtk_label_new(prompt));
- g_free(prompt);
- entry = gtk_entry_new();
- gtk_container_add(GTK_CONTAINER(vbox), entry);
-
- gtk_widget_show_all(gtk_dialog_get_content_area(GTK_DIALOG(dialog)));
- gtk_entry_set_width_chars(GTK_ENTRY(entry), 40);
- gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
-
- gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
- gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
- gtk_widget_grab_focus(entry);
- if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
- passwd = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
- else
- passwd = NULL;
-
- gtk_widget_destroy(dialog);
-
- return passwd;
-}
-
-
-#ifdef BALSA_USE_THREADS
-/* FIXME: is this really necessary? */
-typedef struct {
- pthread_cond_t cond;
- GMimeGpgmeContext *ctx;
- const gchar *desc;
- gint was_bad;
- gchar *res;
-} AskPassphraseData;
-
-/* get_passphrase_idle:
- called in MT mode by the main thread.
- */
-static gboolean
-get_passphrase_idle(gpointer data)
-{
- AskPassphraseData *apd = (AskPassphraseData *) data;
- gdk_threads_enter();
- apd->res = get_passphrase_real(apd->ctx, apd->desc, apd->was_bad);
- gdk_threads_leave();
- pthread_cond_signal(&apd->cond);
- return FALSE;
-}
-#endif
-
-
-/*
- * Helper function: overwrite a sting in memory with random data
- */
-static inline void
-wipe_string(gchar * password)
-{
- while (*password)
- *password++ = random();
-}
-
-
-/*
- * Called by gpgme to get the passphrase for a key.
- */
-static gpgme_error_t
-get_passphrase_cb(void *opaque, const char *uid_hint,
- const char *passph_info, int prev_was_bad, int fd)
-{
- GMimeGpgmeContext *context;
- gchar *passwd = NULL;
- int foo, bar;
-
- if (!opaque || !GMIME_IS_GPGME_CONTEXT(opaque)) {
- foo = write(fd, "\n", 1);
- return foo > 0 ? GPG_ERR_USER_1 : GPG_ERR_EIO;
- }
- context = GMIME_GPGME_CONTEXT(opaque);
-
-#ifdef ENABLE_PCACHE
- if (!pcache)
- pcache = init_pcache();
-
- /* check if we have the passphrase already cached... */
- if ((passwd = check_cache(pcache, uid_hint, prev_was_bad))) {
- foo = write(fd, passwd, strlen(passwd));
- bar = write(fd, "\n", 1);
- wipe_string(passwd);
- g_free(passwd);
- return foo > 0 && bar > 0 ? GPG_ERR_NO_ERROR : GPG_ERR_EIO;
- }
-#endif
-
-#ifdef BALSA_USE_THREADS
- if (!libbalsa_am_i_subthread())
-#ifdef ENABLE_PCACHE
- passwd =
- get_passphrase_real(context, uid_hint, prev_was_bad, pcache);
-
-#else
- passwd = get_passphrase_real(context, uid_hint, prev_was_bad);
-#endif
- else {
- static pthread_mutex_t get_passphrase_lock =
- PTHREAD_MUTEX_INITIALIZER;
- AskPassphraseData apd;
-
- pthread_mutex_lock(&get_passphrase_lock);
- pthread_cond_init(&apd.cond, NULL);
- apd.ctx = context;
- apd.desc = uid_hint;
- apd.was_bad = prev_was_bad;
- g_idle_add(get_passphrase_idle, &apd);
- pthread_cond_wait(&apd.cond, &get_passphrase_lock);
-
- pthread_cond_destroy(&apd.cond);
- pthread_mutex_unlock(&get_passphrase_lock);
- passwd = apd.res;
- }
-#else
- passwd = get_passphrase_real(context, uid_hint, prev_was_bad);
-#endif /* BALSA_USE_THREADS */
-
- if (!passwd) {
- foo = write(fd, "\n", 1);
- return foo > 0 ? GPG_ERR_CANCELED : GPG_ERR_EIO;
- }
-
- /* send the passphrase and erase the string */
- foo = write(fd, passwd, strlen(passwd));
- wipe_string(passwd);
- g_free(passwd);
- bar = write(fd, "\n", 1);
- return foo > 0 && bar > 0 ? GPG_ERR_NO_ERROR : GPG_ERR_EIO;
-}
-
-
/*
* return TRUE is gpg is currently updating the trust database (indicated by
* the file ~/.gnupg/trustdb.gpg.lock)
diff --git a/libbalsa/rfc3156.h b/libbalsa/rfc3156.h
index 4f4be8c..1331b33 100644
--- a/libbalsa/rfc3156.h
+++ b/libbalsa/rfc3156.h
@@ -37,7 +37,7 @@
#include <gpgme.h>
#include "libbalsa.h"
#include "misc.h"
-#include "gmime-gpgme-context.h"
+#include "gmime-gpgme-signature.h"
/* bits to define the protection mode: signed or encrypted */
@@ -60,8 +60,6 @@
#define GPG_ERR_NOT_SIGNED GPG_ERR_USER_16
-gboolean libbalsa_check_crypto_engine(gpgme_protocol_t protocol);
-
gint libbalsa_message_body_protection(LibBalsaMessageBody * body);
gboolean libbalsa_can_encrypt_for_all(InternetAddressList * recipients,
gpgme_protocol_t protocol);
@@ -108,6 +106,7 @@ gpgme_error_t libbalsa_rfc2440_decrypt(GMimePart * part,
const gchar *libbalsa_gpgme_sig_protocol_name(gpgme_protocol_t protocol);
const gchar *libbalsa_gpgme_sig_stat_to_gchar(gpgme_error_t stat);
const gchar *libbalsa_gpgme_validity_to_gchar(gpgme_validity_t validity);
+const gchar *libbalsa_gpgme_validity_to_gchar_short(gpgme_validity_t validity);
gchar *libbalsa_signature_info_to_gchar(GMimeGpgmeSigstat * info,
const gchar * date_string);
diff --git a/src/balsa-message.c b/src/balsa-message.c
index 1bdbf2f..f6b34f4 100644
--- a/src/balsa-message.c
+++ b/src/balsa-message.c
@@ -3037,7 +3037,7 @@ libbalsa_msg_part_2440(LibBalsaMessage * message, LibBalsaMessageBody * body,
if (sig_res == GPG_ERR_NO_ERROR) {
if (body->sig_info->validity >= GPGME_VALIDITY_MARGINAL &&
- body->sig_info->trust >= GPGME_VALIDITY_MARGINAL)
+ body->sig_info->key->owner_trust >= GPGME_VALIDITY_MARGINAL)
libbalsa_information(LIBBALSA_INFORMATION_DEBUG,
_("Detected a good signature"));
else
diff --git a/src/main.c b/src/main.c
index 9f00192..e44ddc1 100644
--- a/src/main.c
+++ b/src/main.c
@@ -63,8 +63,8 @@
#include "libinit_balsa/assistant_init.h"
#ifdef HAVE_GPGME
-#include <string.h>
-#include <gpgme.h>
+#include "libbalsa-gpgme.h"
+#include "libbalsa-gpgme-cb.h"
#endif
#ifdef BALSA_USE_THREADS
@@ -757,26 +757,6 @@ balsa_progress_set_activity(gboolean set, const gchar * text)
gdk_threads_leave();
}
-#if defined(ENABLE_NLS) && defined(HAVE_GPGME)
-static const gchar *
-get_utf8_locale(int category)
-{
- gchar * locale;
- static gchar localebuf[64]; /* should be large enough */
- gchar * dot;
-
- if (!(locale = setlocale(category, NULL)))
- return NULL;
- strncpy(localebuf, locale, 57);
- localebuf[57] = '\0';
- dot = strchr(localebuf, '.');
- if (!dot)
- dot = localebuf + strlen(localebuf);
- strcpy(dot, ".UTF-8");
- return localebuf;
-}
-#endif
-
/* -------------------------- main --------------------------------- */
int
main(int argc, char *argv[])
@@ -846,27 +826,9 @@ main(int argc, char *argv[])
#endif /* HAVE_UNIQUE */
#ifdef HAVE_GPGME
- /* initialise the gpgme library */
- g_message("init gpgme version %s", gpgme_check_version(NULL));
-#ifdef HAVE_GPG
- /* configure the GnuPG engine if a specific gpg path has been detected*/
- gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP, GPG_PATH, NULL);
-#endif
-#ifdef ENABLE_NLS
- gpgme_set_locale(NULL, LC_CTYPE, get_utf8_locale(LC_CTYPE));
- gpgme_set_locale(NULL, LC_MESSAGES, get_utf8_locale(LC_MESSAGES));
-#endif /* ENABLE_NLS */
- { /* scope */
- gpgme_engine_info_t e;
-
- if (gpgme_get_engine_info(&e) == GPG_ERR_NO_ERROR)
- while (e) {
- g_message("protocol %s: engine %s (home %s, version %s)",
- gpgme_get_protocol_name(e->protocol),
- e->file_name, e->home_dir, e->version);
- e = e->next;
- }
- }
+ /* initialise the gpgme library and set the callback funcs */
+ libbalsa_gpgme_init(lb_gpgme_passphrase, lb_gpgme_select_key,
+ lb_gpgme_accept_low_trust_key);
#endif
balsa_app_init();
@@ -932,13 +894,9 @@ main(int argc, char *argv[])
#ifdef HAVE_GPGME
balsa_app.has_openpgp =
- libbalsa_check_crypto_engine(GPGME_PROTOCOL_OpenPGP);
-#ifdef HAVE_SMIME
+ libbalsa_gpgme_check_crypto_engine(GPGME_PROTOCOL_OpenPGP);
balsa_app.has_smime =
- libbalsa_check_crypto_engine(GPGME_PROTOCOL_CMS);
-#else
- balsa_app.has_smime = FALSE;
-#endif /* HAVE_SMIME */
+ libbalsa_gpgme_check_crypto_engine(GPGME_PROTOCOL_CMS);
#endif /* HAVE_GPGME */
if (opt_compose_email || opt_attach_list) {
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]