[evolution-patches] Mailer bug #127521: Inline PGP support for evolution (part A: only sending)



This (part A) does not verify "clearsigned" messages yet. It only offers a
choice to inline signature (instead of sending multipart-mime message).

Part B (to be submitted shortly) will verify the clearsigned messages.

-Bohumir
Index: camel/ChangeLog
===================================================================
RCS file: /cvs/gnome/evolution/camel/ChangeLog,v
retrieving revision 1.1922
diff -u -r1.1922 ChangeLog
--- camel/ChangeLog	26 Nov 2003 19:32:15 -0000	1.1922
+++ camel/ChangeLog	6 Jan 2004 12:07:05 -0000
@@ -1,3 +1,12 @@
+2004-01-06  Bohumir Jelinek  <bj48 msstate edu>
+
+	* camel-cipher-context.c (cipher_clearsign): signs plain text
+	(camel_cipher_dw_to_canon_stream): filters plain text
+	* camel-cipher-context.h: added prototypes for above
+	* camel-gpg-context.c: added GPG_CTX_MODE_CLEARSIGN enumeration
+	(gpg_clearsign): signs plain text
+	* e-msg-composer.c (build_message): modified to offer clearsign
+	
 2003-11-26  JP Rosevear <jpr ximian com>
 
 	* Makefile.am: make sure we always dist the smime stuff
Index: camel/camel-cipher-context.c
===================================================================
RCS file: /cvs/gnome/evolution/camel/camel-cipher-context.c,v
retrieving revision 1.16
diff -u -r1.16 camel-cipher-context.c
--- camel/camel-cipher-context.c	24 Nov 2003 22:38:12 -0000	1.16
+++ camel/camel-cipher-context.c	6 Jan 2004 12:07:05 -0000
@@ -102,6 +102,15 @@
 	return -1;
 }
 
+static int
+cipher_clearsign (CamelCipherContext *ctx, const char *userid, CamelCipherHash hash,
+		  struct _CamelDataWrapper *plain, struct _CamelDataWrapper *current, CamelException *ex)
+{
+	camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
+			     _("Inline signing is not supported by this cipher"));
+	return -1;
+}
+
 /**
  * camel_cipher_sign:
  * @context: Cipher Context
@@ -133,6 +142,35 @@
 	return retval;
 }
 
+/**
+ * camel_cipher_clearsign:
+ * @context: Cipher Context
+ * @userid: private key to use to sign the stream
+ * @hash: preferred Message-Integrity-Check hash algorithm
+ * @iopart: Input/output part.
+ * @ex: exception
+ *
+ * Appends signature at the end of plain text part @iopart.
+ *
+ * Return value: 0 for success or -1 for failure.
+ **/
+int
+camel_cipher_clearsign (CamelCipherContext *context, const char *userid, CamelCipherHash hash,
+		   struct _CamelDataWrapper *plain, struct _CamelDataWrapper *current, CamelException *ex)
+{
+	int retval;
+	
+	g_return_val_if_fail (CAMEL_IS_CIPHER_CONTEXT (context), -1);
+	
+	CIPHER_LOCK(context);
+	
+	retval = CCC_CLASS (context)->clearsign (context, userid, hash, plain, current, ex);
+	
+	CIPHER_UNLOCK(context);
+	
+	return retval;
+}
+
 static CamelCipherValidity *
 cipher_verify (CamelCipherContext *context, struct _CamelMimePart *sigpart, CamelException *ex)
 {
@@ -490,6 +528,7 @@
 	camel_cipher_context_class->hash_to_id = cipher_hash_to_id;
 	camel_cipher_context_class->id_to_hash = cipher_id_to_hash;
 	camel_cipher_context_class->sign = cipher_sign;
+	camel_cipher_context_class->clearsign = cipher_clearsign;
 	camel_cipher_context_class->verify = cipher_verify;
 	camel_cipher_context_class->encrypt = cipher_encrypt;
 	camel_cipher_context_class->decrypt = cipher_decrypt;
@@ -583,3 +622,37 @@
 
 	return res;
 }
+
+/**
+ * camel_cipher_dw_to_canon_stream:
+ * @dw: data wrapper to write.
+ * @flags: flags for the canonicalisation filter (CamelMimeFilterCanon)
+ * @ostream: stream to write canonicalised output to.
+ * 
+ * Writes a dw to a stream in a canonicalised format, suitable for signing/encrypting.
+ *
+ * Return value: -1 on error;
+ **/
+int
+camel_cipher_dw_to_canon_stream(CamelDataWrapper *dw, guint32 flags, CamelStream *ostream)
+{
+	CamelStreamFilter *filter;
+	CamelMimeFilter *canon;
+	int res = -1;
+
+	filter = camel_stream_filter_new_with_stream(ostream);
+
+	canon = camel_mime_filter_canon_new(flags);
+	camel_stream_filter_add(filter, canon);
+	camel_object_unref(canon);
+
+	if (camel_data_wrapper_write_to_stream(dw, (CamelStream *)filter) != -1
+	    && camel_stream_flush((CamelStream *)filter) != -1)
+		res = 0;
+
+	camel_object_unref(filter);
+
+	camel_stream_reset(ostream);
+
+	return res;
+}
Index: camel/camel-cipher-context.h
===================================================================
RCS file: /cvs/gnome/evolution/camel/camel-cipher-context.h,v
retrieving revision 1.14
diff -u -r1.14 camel-cipher-context.h
--- camel/camel-cipher-context.h	24 Nov 2003 22:38:12 -0000	1.14
+++ camel/camel-cipher-context.h	6 Jan 2004 12:07:06 -0000
@@ -105,6 +105,9 @@
 	int                   (*sign)      (CamelCipherContext *context, const char *userid, CamelCipherHash hash,
 					    struct _CamelMimePart *ipart, struct _CamelMimePart *opart, CamelException *ex);
 	
+	int                   (*clearsign) (CamelCipherContext *context, const char *userid, CamelCipherHash hash,
+					    struct _CamelDataWrapper *plain, struct _CamelDataWrapper *current, CamelException *ex);
+	
 	CamelCipherValidity * (*verify)    (CamelCipherContext *context, struct _CamelMimePart *ipart, CamelException *ex);
 	
 	int                   (*encrypt)   (CamelCipherContext *context, const char *userid,
@@ -139,6 +142,8 @@
 /* cipher routines */
 int                  camel_cipher_sign (CamelCipherContext *context, const char *userid, CamelCipherHash hash,
 					struct _CamelMimePart *ipart, struct _CamelMimePart *opart, CamelException *ex);
+int                  camel_cipher_clearsign (CamelCipherContext *context, const char *userid, CamelCipherHash hash,
+					     struct _CamelDataWrapper *plain, struct _CamelDataWrapper *current, CamelException *ex);
 CamelCipherValidity *camel_cipher_verify (CamelCipherContext *context, struct _CamelMimePart *ipart, CamelException *ex);
 int                  camel_cipher_encrypt (CamelCipherContext *context, const char *userid,
 					   GPtrArray *recipients, struct _CamelMimePart *ipart, struct _CamelMimePart *opart,
@@ -166,6 +171,7 @@
 
 /* utility functions */
 int		     camel_cipher_canonical_to_stream(CamelMimePart *part, guint32 flags, CamelStream *ostream);
+int		     camel_cipher_dw_to_canon_stream(CamelDataWrapper *dw, guint32 flags, CamelStream *ostream);
 
 #ifdef __cplusplus
 }
Index: camel/camel-gpg-context.c
===================================================================
RCS file: /cvs/gnome/evolution/camel/camel-gpg-context.c,v
retrieving revision 1.40
diff -u -r1.40 camel-gpg-context.c
--- camel/camel-gpg-context.c	13 Nov 2003 23:17:30 -0000	1.40
+++ camel/camel-gpg-context.c	6 Jan 2004 12:07:06 -0000
@@ -148,6 +148,7 @@
 
 enum _GpgCtxMode {
 	GPG_CTX_MODE_SIGN,
+	GPG_CTX_MODE_CLEARSIGN,
 	GPG_CTX_MODE_VERIFY,
 	GPG_CTX_MODE_ENCRYPT,
 	GPG_CTX_MODE_DECRYPT,
@@ -294,7 +295,7 @@
 gpg_ctx_set_mode (struct _GpgCtx *gpg, enum _GpgCtxMode mode)
 {
 	gpg->mode = mode;
-	gpg->need_passwd = ((gpg->mode == GPG_CTX_MODE_SIGN) || (gpg->mode == GPG_CTX_MODE_DECRYPT));
+	gpg->need_passwd = ((gpg->mode == GPG_CTX_MODE_SIGN) || (gpg->mode == GPG_CTX_MODE_DECRYPT) || (gpg->mode == GPG_CTX_MODE_CLEARSIGN));
 }
 
 static void
@@ -494,6 +495,18 @@
 		g_ptr_array_add (argv, "--output");
 		g_ptr_array_add (argv, "-");
 		break;
+	case GPG_CTX_MODE_CLEARSIGN:
+		g_ptr_array_add (argv, "--clearsign");
+		hash_str = gpg_hash_str (gpg->hash);
+		if (hash_str)
+			g_ptr_array_add (argv, (char *) hash_str);
+		if (gpg->userid) {
+			g_ptr_array_add (argv, "-u");
+			g_ptr_array_add (argv, (char *) gpg->userid);
+		}
+		g_ptr_array_add (argv, "--output");
+		g_ptr_array_add (argv, "-");
+		break;
 	case GPG_CTX_MODE_VERIFY:
 		if (!camel_session_is_online (gpg->session)) {
 			/* this is a deprecated flag to gpg since 1.0.7 */
@@ -806,6 +819,11 @@
 				/* FIXME: save this state? */
 			}
 			break;
+		case GPG_CTX_MODE_CLEARSIGN:
+			if (!strncmp (status, "SIG_CREATED ", 12)) {
+				/* FIXME: save this state? */
+			}
+			break;
 		case GPG_CTX_MODE_VERIFY:
 			if (!strncmp (status, "TRUST_", 6)) {
 				status += 6;
@@ -1087,6 +1105,9 @@
 	case GPG_CTX_MODE_SIGN:
 		mode = "sign";
 		break;
+	case GPG_CTX_MODE_CLEARSIGN:
+		mode = "clearsign";
+		break;
 	case GPG_CTX_MODE_VERIFY:
 		mode = "verify";
 		break;
@@ -1310,6 +1331,81 @@
 }
 
 
+static int
+gpg_clearsign (CamelCipherContext *context, const char *userid, CamelCipherHash hash, CamelDataWrapper *plain, CamelDataWrapper *current, CamelException *ex)
+{
+	struct _GpgCtx *gpg;
+	CamelStream *ostream = camel_stream_mem_new(), *istream;
+	CamelContentType *ct;
+	int res = -1;
+
+	/* Note: see rfc2015 or rfc3156, section 5 */
+
+	/* FIXME: stream this, we stream output at least */
+	istream = camel_stream_mem_new();
+	if (camel_cipher_dw_to_canon_stream(plain, CAMEL_MIME_FILTER_CANON_STRIP|CAMEL_MIME_FILTER_CANON_CRLF|CAMEL_MIME_FILTER_CANON_FROM,
+					     istream) == -1) {
+		camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
+				     _("Could not generate signing data: %s"), g_strerror(errno));
+		goto fail;
+	}
+
+	gpg = gpg_ctx_new (context->session);
+	gpg_ctx_set_mode (gpg, GPG_CTX_MODE_CLEARSIGN);
+	gpg_ctx_set_hash (gpg, hash);
+	gpg_ctx_set_userid (gpg, userid);
+	gpg_ctx_set_istream (gpg, istream);
+	gpg_ctx_set_ostream (gpg, ostream);
+	
+	if (gpg_ctx_op_start (gpg) == -1) {
+		camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+				      _("Failed to execute gpg: %s"), g_strerror (errno));
+		goto fail;
+	}
+	
+	while (!gpg_ctx_op_complete (gpg)) {
+		if (camel_operation_cancel_check (NULL)) {
+			camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL,
+					     _("Cancelled."));
+			gpg_ctx_op_cancel (gpg);
+			goto fail;
+		}
+		
+		if (gpg_ctx_op_step (gpg, ex) == -1) {
+			gpg_ctx_op_cancel (gpg);
+			goto fail;
+		}
+	}
+	
+	if (gpg_ctx_op_wait (gpg) != 0) {
+		const char *diagnostics;
+		
+		diagnostics = gpg_ctx_get_diagnostics (gpg);
+		camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
+				     diagnostics && *diagnostics ? diagnostics :
+				     _("Failed to execute gpg."));
+		goto fail;
+	}
+
+	res = 0;
+
+	camel_stream_reset(ostream);
+	camel_data_wrapper_construct_from_stream(current, ostream);
+
+	ct = camel_content_type_new("application", "pgp");
+	camel_content_type_set_param(ct, "x-action", "sign");
+	camel_content_type_set_param(ct, "format", "text");
+	camel_data_wrapper_set_mime_type_field(current, ct);
+	camel_content_type_unref(ct);
+
+fail:
+	camel_object_unref(ostream);
+	gpg_ctx_free (gpg);
+	
+	return res;
+}
+
+
 static char *
 swrite (CamelMimePart *sigpart)
 {
@@ -1740,6 +1836,7 @@
 	cipher_class->hash_to_id = gpg_hash_to_id;
 	cipher_class->id_to_hash = gpg_id_to_hash;
 	cipher_class->sign = gpg_sign;
+	cipher_class->clearsign = gpg_clearsign;
 	cipher_class->verify = gpg_verify;
 	cipher_class->encrypt = gpg_encrypt;
 	cipher_class->decrypt = gpg_decrypt;
Index: composer/e-msg-composer.c
===================================================================
RCS file: /cvs/gnome/evolution/composer/e-msg-composer.c,v
retrieving revision 1.424
diff -u -r1.424 e-msg-composer.c
--- composer/e-msg-composer.c	26 Nov 2003 21:51:40 -0000	1.424
+++ composer/e-msg-composer.c	6 Jan 2004 12:07:06 -0000
@@ -370,6 +370,7 @@
 	CamelException ex;
 	GByteArray *data;
 	int i;
+	gboolean clearsign = FALSE;
 	
 	if (composer->persist_stream_interface == CORBA_OBJECT_NIL)
 		return NULL;
@@ -575,11 +576,8 @@
 		CamelInternetAddress *from = NULL;
 		CamelCipherContext *cipher;
 
-		part = camel_mime_part_new ();
-		camel_medium_set_content_object (CAMEL_MEDIUM (part), current);
 		if (current == plain)
 			camel_mime_part_set_encoding (part, plain_encoding);
-		camel_object_unref (current);
 
 		if (hdrs->account && hdrs->account->pgp_key && *hdrs->account->pgp_key) {
 			pgp_userid = hdrs->account->pgp_key;
@@ -589,19 +587,44 @@
 		}
 		
 		if (composer->pgp_sign) {
-			CamelMimePart *npart = camel_mime_part_new();
+
+			if (current == plain) {
+				/*
+				 * For plain text message, ask user if he wants to use "clearsign"
+				 */
+				clearsign = em_utils_prompt_user ((GtkWindow *) composer, GTK_RESPONSE_YES, NULL,
+								  _("PGP signature of plain text message can be inlined.\nInline?"));
+			}
 
 			cipher = mail_crypto_get_pgp_cipher_context(hdrs->account);
-			camel_cipher_sign(cipher, pgp_userid, CAMEL_CIPHER_HASH_SHA1, part, npart, &ex);
-			camel_object_unref(cipher);
-			
-			if (camel_exception_is_set(&ex)) {
-				camel_object_unref(npart);
-				goto exception;
+			if (!clearsign) {
+				part = camel_mime_part_new ();
+				camel_medium_set_content_object (CAMEL_MEDIUM (part), current);
+				camel_object_unref (current);
+				CamelMimePart *npart = camel_mime_part_new();
+
+				camel_cipher_sign(cipher, pgp_userid, CAMEL_CIPHER_HASH_SHA1, part, npart, &ex);
+
+				if (camel_exception_is_set(&ex)) {
+					camel_object_unref(npart);
+					goto exception;
+				}
+
+				camel_object_unref(part);
+				part = npart;
 			}
+			else {
+				current = camel_data_wrapper_new ();
+				camel_cipher_clearsign(cipher, pgp_userid, CAMEL_CIPHER_HASH_SHA1, plain, current, &ex);
+
+				if (camel_exception_is_set(&ex)) {
+					camel_object_unref(current);
+					goto exception;
+				}
 
-			camel_object_unref(part);
-			part = npart;
+			}
+			camel_object_unref(cipher);
+			
 		}
 		
 		if (composer->pgp_encrypt) {
@@ -630,9 +653,11 @@
 		if (from)
 			camel_object_unref (from);	
 	
-		current = camel_medium_get_content_object (CAMEL_MEDIUM (part));
-		camel_object_ref (current);
-		camel_object_unref (part);
+		if (!clearsign) {
+			current = camel_medium_get_content_object (CAMEL_MEDIUM (part));
+			camel_object_ref (current);
+			camel_object_unref (part);
+		}
 	}
 	
 #if defined (HAVE_NSS) && defined (SMIME_SUPPORTED)


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]