From 2d843ca887fecdf0fc3ea412c5922f0c64cc24b4 Mon Sep 17 00:00:00 2001 From: "Foivos S. Zakkak" Date: Sun, 19 Oct 2014 03:20:21 +0300 Subject: [PATCH 1/3] Add Decryption field Add a decryption field of the form Decryption: 2 part(s) decrypted 1 part(s) failed Meaning that 2 encrypted mime parts where successfully decrypted and 1 part failed. Note that the number 2 refers to the number of successfully decrypted mime parts and not the number of successfully decrypted encryptes multiparts, i.e., if an encrypted multipart contains 4 parts and decryption is successful the field will be Decryption: 4 part(s) decrypted TODO: Add details button listing the names and indexes of the decrypted (or not) mime-parts --- lib/mu-msg-part.c | 61 +++++++++++++++++++++++++++-------------------- lib/mu-msg-sexp.c | 20 ++++++++++++++-- mu4e/mu4e-vars.el | 7 +++++- mu4e/mu4e-view.el | 27 ++++++++++++++++++++- 4 files changed, 85 insertions(+), 30 deletions(-) diff --git a/lib/mu-msg-part.c b/lib/mu-msg-part.c index 8f431528..79ef0168 100644 --- a/lib/mu-msg-part.c +++ b/lib/mu-msg-part.c @@ -153,11 +153,10 @@ accumulate_text (MuMsg *msg, MuMsgPart *part, GString **gstrp) } /* declaration, so we can use it earlier */ -static gboolean handle_mime_object (MuMsg *msg, - GMimeObject *mobj, GMimeObject *parent, - MuMsgOptions opts, - unsigned *index, MuMsgPartForeachFunc func, - gpointer user_data); +static gboolean +handle_mime_object (MuMsg *msg, GMimeObject *mobj, GMimeObject *parent, + MuMsgOptions opts, unsigned *index, gboolean decrypted, + MuMsgPartForeachFunc func, gpointer user_data); static char* get_text_from_mime_msg (MuMsg *msg, GMimeMessage *mmsg, MuMsgOptions opts) @@ -172,6 +171,7 @@ get_text_from_mime_msg (MuMsg *msg, GMimeMessage *mmsg, MuMsgOptions opts) (GMimeObject *) mmsg, opts, &index, + FALSE, (MuMsgPartForeachFunc)accumulate_text, &gstr); @@ -375,8 +375,7 @@ get_console_pw (const char* user_id, const char *prompt_ctx, static gboolean -handle_encrypted_part (MuMsg *msg, - GMimeMultipartEncrypted *part, GMimeObject *parent, +handle_encrypted_part (MuMsg *msg, GMimeMultipartEncrypted *part, MuMsgOptions opts, unsigned *index, MuMsgPartForeachFunc func, gpointer user_data) { @@ -399,20 +398,20 @@ handle_encrypted_part (MuMsg *msg, } if (dec) { - rv = handle_mime_object (msg, dec, parent, opts, - index, func, user_data); + rv = handle_mime_object (msg, dec, (GMimeObject *) part, + opts, index, TRUE, func, user_data); g_object_unref (dec); } else { - // On failure to decrypt, list the encrypted part as - // an attachment + // On failure to decrypt list the encrypted part as an + // attachment GMimeObject *encrypted; encrypted = g_mime_multipart_get_part (GMIME_MULTIPART (part), 1); g_return_val_if_fail (GMIME_IS_PART(encrypted), FALSE); - rv = handle_mime_object (msg, encrypted, parent, opts, - index, func, user_data); + rv = handle_mime_object (msg, encrypted, (GMimeObject *) part, + opts, index, FALSE, func, user_data); } return rv; @@ -423,7 +422,7 @@ handle_encrypted_part (MuMsg *msg, /* call 'func' with information about this MIME-part */ static gboolean handle_part (MuMsg *msg, GMimePart *part, GMimeObject *parent, - MuMsgOptions opts, unsigned *index, + MuMsgOptions opts, unsigned *index, gboolean decrypted, MuMsgPartForeachFunc func, gpointer user_data) { GMimeContentType *ct; @@ -434,6 +433,12 @@ handle_part (MuMsg *msg, GMimePart *part, GMimeObject *parent, msgpart.size = get_part_size (part); msgpart.part_type = MU_MSG_PART_TYPE_LEAF; msgpart.part_type |= get_disposition ((GMimeObject*)part); + if (decrypted) + msgpart.part_type |= MU_MSG_PART_TYPE_DECRYPTED; + else if ((opts & MU_MSG_OPTION_DECRYPT) && + GMIME_IS_MULTIPART_ENCRYPTED (parent)) + msgpart.part_type |= MU_MSG_PART_TYPE_ENCRYPTED; + ct = g_mime_object_get_content_type ((GMimeObject*)part); if (GMIME_IS_CONTENT_TYPE(ct)) { @@ -466,7 +471,7 @@ handle_part (MuMsg *msg, GMimePart *part, GMimeObject *parent, /* call 'func' with information about this MIME-part */ static gboolean handle_message_part (MuMsg *msg, GMimeMessagePart *mimemsgpart, GMimeObject *parent, - MuMsgOptions opts, unsigned *index, + MuMsgOptions opts, unsigned *index, gboolean decrypted, MuMsgPartForeachFunc func, gpointer user_data) { MuMsgPart msgpart; @@ -494,6 +499,7 @@ handle_message_part (MuMsg *msg, GMimeMessagePart *mimemsgpart, GMimeObject *par (GMimeObject *) mmsg, opts, index, + decrypted, func, user_data); } @@ -503,7 +509,8 @@ handle_message_part (MuMsg *msg, GMimeMessagePart *mimemsgpart, GMimeObject *par static gboolean handle_multipart (MuMsg *msg, GMimeMultipart *mpart, MuMsgOptions opts, - unsigned *index, MuMsgPartForeachFunc func, gpointer user_data) + unsigned *index, gboolean decrypted, + MuMsgPartForeachFunc func, gpointer user_data) { gboolean res; GMimeObject *part; @@ -513,7 +520,8 @@ handle_multipart (MuMsg *msg, GMimeMultipart *mpart, MuMsgOptions opts, for (i = 0; i < mpart->children->len; i++) { part = (GMimeObject *) mpart->children->pdata[i]; res &= handle_mime_object (msg, part, (GMimeObject *) mpart, - opts, index, func, user_data); + opts, index, decrypted, + func, user_data); } return res; @@ -521,18 +529,18 @@ handle_multipart (MuMsg *msg, GMimeMultipart *mpart, MuMsgOptions opts, static gboolean -handle_mime_object (MuMsg *msg, - GMimeObject *mobj, GMimeObject *parent, MuMsgOptions opts, - unsigned *index, MuMsgPartForeachFunc func, gpointer user_data) +handle_mime_object (MuMsg *msg, GMimeObject *mobj, GMimeObject *parent, + MuMsgOptions opts, unsigned *index, gboolean decrypted, + MuMsgPartForeachFunc func, gpointer user_data) { if (GMIME_IS_PART (mobj)) return handle_part (msg, GMIME_PART(mobj), parent, - opts, index, func, user_data); + opts, index, decrypted, func, user_data); else if (GMIME_IS_MESSAGE_PART (mobj)) return handle_message_part (msg, GMIME_MESSAGE_PART(mobj), - parent, opts, index, func, user_data); + parent, opts, index, decrypted, func, user_data); else if ((opts & MU_MSG_OPTION_VERIFY) && GMIME_IS_MULTIPART_SIGNED (mobj)) { gboolean verified, signedpart; @@ -543,18 +551,18 @@ handle_mime_object (MuMsg *msg, // Only process the first part (the second one is the signature) signedpart = handle_mime_object (msg, g_mime_multipart_get_part (GMIME_MULTIPART (mobj), 0), - mobj, opts, index, func, user_data); + mobj, opts, index, decrypted, func, user_data); return verified && signedpart; } else if ((opts & MU_MSG_OPTION_DECRYPT) && GMIME_IS_MULTIPART_ENCRYPTED (mobj)) return handle_encrypted_part (msg, GMIME_MULTIPART_ENCRYPTED (mobj), - parent, opts, index, func, user_data); + opts, index, func, user_data); else if (GMIME_IS_MULTIPART (mobj)) return handle_multipart - (msg, GMIME_MULTIPART (mobj), - opts, index, func, user_data); + (msg, GMIME_MULTIPART (mobj), opts, + index, decrypted, func, user_data); return TRUE; } @@ -576,6 +584,7 @@ mu_msg_part_foreach (MuMsg *msg, MuMsgOptions opts, (GMimeObject *) msg->_file->_mime_msg, opts, &index, + FALSE, func, user_data); } diff --git a/lib/mu-msg-sexp.c b/lib/mu-msg-sexp.c index c9e4a66a..a4615400 100644 --- a/lib/mu-msg-sexp.c +++ b/lib/mu-msg-sexp.c @@ -300,6 +300,21 @@ sig_verdict (MuMsgPart *mpart) } } +static const char* +dec_verdict (MuMsgPart *mpart) +{ + MuMsgPartType ptype; + + ptype = mpart->part_type; + + if (ptype & MU_MSG_PART_TYPE_DECRYPTED) + return ":decryption succeeded"; + else if (ptype & MU_MSG_PART_TYPE_ENCRYPTED) + return ":decryption failed"; + else + return ""; +} + static gchar * get_part_type_string (MuMsgPartType ptype) @@ -348,7 +363,7 @@ each_part (MuMsg *msg, MuMsgPart *part, PartInfo *pinfo) tmp = g_strdup_printf ("%s(:index %d :name \"%s\" :mime-type \"%s/%s\"%s%s " ":type %s " - ":attachment %s :size %i %s)", + ":attachment %s :size %i %s %s)", pinfo->parts ? pinfo->parts: "", part->index, name ? name : "noname", @@ -358,7 +373,8 @@ each_part (MuMsg *msg, MuMsgPart *part, PartInfo *pinfo) parttype, mu_msg_part_maybe_attachment (part) ? "t" : "nil", (int)part->size, - sig_verdict (part)); + sig_verdict (part), + dec_verdict (part)); g_free (pinfo->parts); pinfo->parts = tmp; diff --git a/mu4e/mu4e-vars.el b/mu4e/mu4e-vars.el index 50a6c3e9..ffa9bb3c 100644 --- a/mu4e/mu4e-vars.el +++ b/mu4e/mu4e-vars.el @@ -435,7 +435,7 @@ I.e. a message with the draft flag set." '((t :inherit font-lock-preprocessor-face)) "Face for the mark in the headers list." :group 'mu4e-faces) - + (defface mu4e-header-key-face '((t :inherit message-header-name-face :bold t)) "Face for a header key (such as \"Foo\" in \"Subject:\ Foo\")." @@ -625,6 +625,11 @@ mu4e-compose-mode." :shortname "Sgn" :help "Check for the cryptographic signature" :sortable nil)) + (:decryption . + ( :name "Decryption" + :shortname "Dec" + :help "Check the cryptographic decryption status" + :sortable nil)) (:size . ( :name "Size" :shortname "Size" diff --git a/mu4e/mu4e-view.el b/mu4e/mu4e-view.el index c223aaee..9a518b15 100644 --- a/mu4e/mu4e-view.el +++ b/mu4e/mu4e-view.el @@ -50,7 +50,8 @@ :group 'mu4e) (defcustom mu4e-view-fields - '(:from :to :cc :subject :flags :date :maildir :mailing-list :tags :attachments :signature) + '(:from :to :cc :subject :flags :date :maildir :mailing-list :tags + :attachments :signature :decryption) "Header fields to display in the message view buffer. For the complete list of available headers, see `mu4e-header-info'." :type (list 'symbol) @@ -227,6 +228,8 @@ found." (:attachments (mu4e~view-construct-attachments-header msg)) ;; pgp-signatures (:signature (mu4e~view-construct-signature-header msg)) + ;; pgp-decryption + (:decryption (mu4e~view-construct-decryption-header msg)) (t (mu4e~view-construct-header field (mu4e~view-custom-field msg field)))))) mu4e-view-fields "") @@ -403,6 +406,28 @@ add text-properties to VAL." (val (when val (concat val " (" btn ")")))) (mu4e~view-construct-header :signature val t))) +(defun mu4e~view-construct-decryption-header (msg) + "Construct a Decryption: header, if there are any encrypted parts." + (let* ((parts (mu4e-message-field msg :parts)) + (verdicts + (remove-if 'null + (mapcar (lambda (part) (mu4e-message-part-field part :decryption)) + parts))) + (succeeded (remove-if (lambda (v) (eq v 'failed)) verdicts)) + (failed (remove-if (lambda (v) (eq v 'succeeded)) verdicts)) + (succ (when succeeded + (propertize + (concat (number-to-string (length succeeded)) + " part(s) decrypted") + 'face 'mu4e-ok-face))) + (fail (when failed + (propertize + (concat (number-to-string (length failed)) + " part(s) failed") + 'face 'mu4e-warning-face))) + (val (concat succ fail))) + (mu4e~view-construct-header :decryption val t))) + (defun mu4e~view-open-attach-from-binding () "Open the attachement at point, or click location." (interactive) From 8f8bc520236a4dcc004b03ba5d1c4a210951c575 Mon Sep 17 00:00:00 2001 From: "Foivos S. Zakkak" Date: Sun, 19 Oct 2014 03:39:32 +0300 Subject: [PATCH 2/3] Revert "Do not handle signature parts after verification" This reverts commit 6e9b9ad2d0c81dc0ad8f7a5c1b4981742d9fee22. Unfortunately the reverted commit breaks the Signature field for encrypted and, at the same time, signed messages. TODO: details button in the Signatures field does not work for such cases because the signature is encrypted. Conflicts: lib/mu-msg-part.c --- lib/mu-msg-part.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/lib/mu-msg-part.c b/lib/mu-msg-part.c index 79ef0168..30ffbc68 100644 --- a/lib/mu-msg-part.c +++ b/lib/mu-msg-part.c @@ -452,9 +452,9 @@ handle_part (MuMsg *msg, GMimePart *part, GMimeObject *parent, msgpart.part_type |= MU_MSG_PART_TYPE_TEXT_HTML; } - /* put the verification info in the signed part */ + /* put the verification info in the pgp-signature part */ msgpart.sig_status_report = NULL; - if ((opts & MU_MSG_OPTION_VERIFY) && GMIME_IS_MULTIPART_SIGNED (parent)) + if (g_ascii_strcasecmp (msgpart.subtype, "pgp-signature") == 0) msgpart.sig_status_report = (MuMsgPartSigStatusReport*) g_object_get_data (G_OBJECT(parent), SIG_STATUS_REPORT); @@ -543,17 +543,15 @@ handle_mime_object (MuMsg *msg, GMimeObject *mobj, GMimeObject *parent, parent, opts, index, decrypted, func, user_data); else if ((opts & MU_MSG_OPTION_VERIFY) && GMIME_IS_MULTIPART_SIGNED (mobj)) { - gboolean verified, signedpart; + gboolean verified, multipart; verified = check_signature (msg, GMIME_MULTIPART_SIGNED (mobj), opts); + multipart = handle_multipart + (msg, GMIME_MULTIPART (mobj), opts, + index, decrypted, func, user_data); - // Only process the first part (the second one is the signature) - signedpart = handle_mime_object - (msg, g_mime_multipart_get_part (GMIME_MULTIPART (mobj), 0), - mobj, opts, index, decrypted, func, user_data); - - return verified && signedpart; + return verified && multipart; } else if ((opts & MU_MSG_OPTION_DECRYPT) && GMIME_IS_MULTIPART_ENCRYPTED (mobj)) return handle_encrypted_part From aaef9493cdb01c017786a7212a98a215fc190164 Mon Sep 17 00:00:00 2001 From: "Foivos S. Zakkak" Date: Sun, 19 Oct 2014 03:53:28 +0300 Subject: [PATCH 3/3] Fix signatures' detail box for encrypted messages In the case of encrypted and signed messages the signature field's details box did not work due to missing flags to the mu verify command. This commit fixes this issue. --- mu4e/mu4e-view.el | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/mu4e/mu4e-view.el b/mu4e/mu4e-view.el index 9a518b15..867b47c5 100644 --- a/mu4e/mu4e-view.el +++ b/mu4e/mu4e-view.el @@ -1324,9 +1324,12 @@ or message-at-point." (interactive) (let* ((msg (or msg (mu4e-message-at-point))) (path (mu4e-message-field msg :path)) - (cmd (format "%s verify --verbose %s" + (cmd (format "%s verify --verbose %s %s" mu4e-mu-binary - (shell-quote-argument path))) + (shell-quote-argument path) + (if mu4e-decryption-policy + "--decrypt --use-agent" + ""))) (output (shell-command-to-string cmd)) ;; create a new one (buf (get-buffer-create mu4e~verify-buffer-name))