1 Return-Path: <jrollins@servo.finestructure.net>
\r
2 X-Original-To: notmuch@notmuchmail.org
\r
3 Delivered-To: notmuch@notmuchmail.org
\r
4 Received: from localhost (localhost [127.0.0.1])
\r
5 by olra.theworths.org (Postfix) with ESMTP id D1255429E53
\r
6 for <notmuch@notmuchmail.org>; Wed, 25 May 2011 18:01:39 -0700 (PDT)
\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org
\r
11 X-Spam-Status: No, score=-1.921 tagged_above=-999 required=5
\r
12 tests=[NO_DNS_FOR_FROM=0.379, RCVD_IN_DNSWL_MED=-2.3]
\r
14 Received: from olra.theworths.org ([127.0.0.1])
\r
15 by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)
\r
16 with ESMTP id LBv5ZjnunMC2 for <notmuch@notmuchmail.org>;
\r
17 Wed, 25 May 2011 18:01:36 -0700 (PDT)
\r
18 Received: from outgoing-mail.its.caltech.edu (outgoing-mail.its.caltech.edu
\r
20 by olra.theworths.org (Postfix) with ESMTP id 4A7D2429E39
\r
21 for <notmuch@notmuchmail.org>; Wed, 25 May 2011 18:01:34 -0700 (PDT)
\r
22 Received: from fire-doxen.imss.caltech.edu (localhost [127.0.0.1])
\r
23 by fire-doxen-postvirus (Postfix) with ESMTP id 4BFAF3281DB;
\r
24 Wed, 25 May 2011 17:55:19 -0700 (PDT)
\r
25 X-Spam-Scanned: at Caltech-IMSS on fire-doxen by amavisd-new
\r
26 Received: from servo.finestructure.net (gwave-104.ligo.caltech.edu
\r
27 [131.215.114.104]) (Authenticated sender: jrollins)
\r
28 by fire-doxen-submit (Postfix) with ESMTP id 37D343282CE;
\r
29 Wed, 25 May 2011 17:55:14 -0700 (PDT)
\r
30 Received: by servo.finestructure.net (Postfix, from userid 1000)
\r
31 id 82A837CB; Wed, 25 May 2011 18:01:26 -0700 (PDT)
\r
32 From: Jameson Graef Rollins <jrollins@finestructure.net>
\r
33 To: notmuch@notmuchmail.org
\r
34 Subject: [PATCH 09/11] Add decryption of PGP/MIME-encrypted parts with
\r
36 Date: Wed, 25 May 2011 18:01:18 -0700
\r
37 Message-Id: <1306371680-19441-10-git-send-email-jrollins@finestructure.net>
\r
38 X-Mailer: git-send-email 1.7.4.4
\r
39 In-Reply-To: <1306371680-19441-1-git-send-email-jrollins@finestructure.net>
\r
40 References: <1306371680-19441-1-git-send-email-jrollins@finestructure.net>
\r
41 X-BeenThere: notmuch@notmuchmail.org
\r
42 X-Mailman-Version: 2.1.13
\r
44 List-Id: "Use and development of the notmuch mail system."
\r
45 <notmuch.notmuchmail.org>
\r
46 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,
\r
47 <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>
\r
48 List-Archive: <http://notmuchmail.org/pipermail/notmuch>
\r
49 List-Post: <mailto:notmuch@notmuchmail.org>
\r
50 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>
\r
51 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,
\r
52 <mailto:notmuch-request@notmuchmail.org?subject=subscribe>
\r
53 X-List-Received-Date: Thu, 26 May 2011 01:01:40 -0000
\r
55 This adds support for decrypting PGP/MIME-encrypted parts to
\r
56 notmuch-show and notmuch-reply. The --decrypt option implies
\r
57 --verify. Once decryption (and possibly signature verification) is
\r
58 done, a new part_encstatus formatter is emitted, the part_sigstatus
\r
59 formatter is emitted, and the entire multipart/encrypted part is
\r
60 replaced by the contents of the encrypted part.
\r
62 At the moment only a json part_encstatus formatting function is
\r
63 available, even though decryption is done for all formats. Emacs
\r
66 notmuch-client.h | 2 ++
\r
67 notmuch-reply.c | 36 ++++++++++++++++++++++++++++--------
\r
68 notmuch-show.c | 25 ++++++++++++++++++++++++-
\r
69 notmuch.1 | 11 +++++++++++
\r
70 notmuch.c | 8 ++++++++
\r
71 show-message.c | 29 ++++++++++++++++++++++++++++-
\r
72 6 files changed, 101 insertions(+), 10 deletions(-)
\r
74 diff --git a/notmuch-client.h b/notmuch-client.h
\r
75 index dc4ed7a..8a27260 100644
\r
76 --- a/notmuch-client.h
\r
77 +++ b/notmuch-client.h
\r
78 @@ -67,6 +67,7 @@ typedef struct notmuch_show_format {
\r
79 const char *body_start;
\r
80 void (*part_start) (GMimeObject *part,
\r
82 + void (*part_encstatus) (int status);
\r
83 void (*part_sigstatus) (const GMimeSignatureValidity* validity);
\r
84 void (*part_content) (GMimeObject *part);
\r
85 void (*part_end) (GMimeObject *part);
\r
86 @@ -82,6 +83,7 @@ typedef struct notmuch_show_params {
\r
89 GMimeCipherContext* cryptoctx;
\r
91 } notmuch_show_params_t;
\r
93 /* There's no point in continuing when we've detected that we've done
\r
94 diff --git a/notmuch-reply.c b/notmuch-reply.c
\r
95 index 99bb15f..5265af6 100644
\r
96 --- a/notmuch-reply.c
\r
97 +++ b/notmuch-reply.c
\r
98 @@ -34,6 +34,7 @@ static const notmuch_show_format_t format_reply = {
\r
103 reply_part_content,
\r
106 @@ -438,7 +439,10 @@ guess_from_received_header (notmuch_config_t *config, notmuch_message_t *message
\r
110 -notmuch_reply_format_default(void *ctx, notmuch_config_t *config, notmuch_query_t *query)
\r
111 +notmuch_reply_format_default(void *ctx,
\r
112 + notmuch_config_t *config,
\r
113 + notmuch_query_t *query,
\r
114 + notmuch_show_params_t *params)
\r
116 GMimeMessage *reply;
\r
117 notmuch_messages_t *messages;
\r
118 @@ -446,9 +450,6 @@ notmuch_reply_format_default(void *ctx, notmuch_config_t *config, notmuch_query_
\r
119 const char *subject, *from_addr = NULL;
\r
120 const char *in_reply_to, *orig_references, *references;
\r
121 const notmuch_show_format_t *format = &format_reply;
\r
122 - notmuch_show_params_t params;
\r
123 - params.part = -1;
\r
124 - params.cryptoctx = NULL;
\r
126 for (messages = notmuch_query_search_messages (query);
\r
127 notmuch_messages_valid (messages);
\r
128 @@ -508,7 +509,7 @@ notmuch_reply_format_default(void *ctx, notmuch_config_t *config, notmuch_query_
\r
129 notmuch_message_get_header (message, "from"));
\r
131 show_message_body (notmuch_message_get_filename (message),
\r
132 - format, ¶ms);
\r
135 notmuch_message_destroy (message);
\r
137 @@ -517,7 +518,10 @@ notmuch_reply_format_default(void *ctx, notmuch_config_t *config, notmuch_query_
\r
139 /* This format is currently tuned for a git send-email --notmuch hook */
\r
141 -notmuch_reply_format_headers_only(void *ctx, notmuch_config_t *config, notmuch_query_t *query)
\r
142 +notmuch_reply_format_headers_only(void *ctx,
\r
143 + notmuch_config_t *config,
\r
144 + notmuch_query_t *query,
\r
145 + unused (notmuch_show_params_t *params))
\r
147 GMimeMessage *reply;
\r
148 notmuch_messages_t *messages;
\r
149 @@ -579,9 +583,12 @@ notmuch_reply_command (void *ctx, int argc, char *argv[])
\r
150 notmuch_query_t *query;
\r
151 char *opt, *query_string;
\r
153 - int (*reply_format_func)(void *ctx, notmuch_config_t *config, notmuch_query_t *query);
\r
154 + int (*reply_format_func)(void *ctx, notmuch_config_t *config, notmuch_query_t *query, notmuch_show_params_t *params);
\r
155 + notmuch_show_params_t params;
\r
157 reply_format_func = notmuch_reply_format_default;
\r
158 + params.part = -1;
\r
159 + params.cryptoctx = NULL;
\r
161 for (i = 0; i < argc && argv[i][0] == '-'; i++) {
\r
162 if (strcmp (argv[i], "--") == 0) {
\r
163 @@ -598,6 +605,16 @@ notmuch_reply_command (void *ctx, int argc, char *argv[])
\r
164 fprintf (stderr, "Invalid value for --format: %s\n", opt);
\r
167 + } else if ((STRNCMP_LITERAL (argv[i], "--decrypt") == 0)) {
\r
168 + if (params.cryptoctx == NULL) {
\r
169 + GMimeSession* session = g_object_new(notmuch_gmime_session_get_type(), NULL);
\r
170 + if (NULL == (params.cryptoctx = g_mime_gpg_context_new(session, "gpg")))
\r
171 + fprintf (stderr, "Failed to construct gpg context.\n");
\r
173 + g_mime_gpg_context_set_always_trust((GMimeGpgContext*)params.cryptoctx, FALSE);
\r
174 + g_object_unref (session);
\r
178 fprintf (stderr, "Unrecognized option: %s\n", argv[i]);
\r
180 @@ -633,11 +650,14 @@ notmuch_reply_command (void *ctx, int argc, char *argv[])
\r
184 - if (reply_format_func (ctx, config, query) != 0)
\r
185 + if (reply_format_func (ctx, config, query, ¶ms) != 0)
\r
188 notmuch_query_destroy (query);
\r
189 notmuch_database_close (notmuch);
\r
191 + if (params.cryptoctx)
\r
192 + g_object_unref(params.cryptoctx);
\r
196 diff --git a/notmuch-show.c b/notmuch-show.c
\r
197 index bb54e56..e90f07e 100644
\r
198 --- a/notmuch-show.c
\r
199 +++ b/notmuch-show.c
\r
200 @@ -45,6 +45,7 @@ static const notmuch_show_format_t format_text = {
\r
202 format_part_start_text,
\r
205 format_part_content_text,
\r
206 format_part_end_text,
\r
208 @@ -66,6 +67,9 @@ format_part_start_json (unused (GMimeObject *part),
\r
212 +format_part_encstatus_json (int status);
\r
215 format_part_sigstatus_json (const GMimeSignatureValidity* validity);
\r
218 @@ -80,6 +84,7 @@ static const notmuch_show_format_t format_json = {
\r
219 ", \"headers\": {", format_headers_json, "}",
\r
221 format_part_start_json,
\r
222 + format_part_encstatus_json,
\r
223 format_part_sigstatus_json,
\r
224 format_part_content_json,
\r
225 format_part_end_json,
\r
226 @@ -103,6 +108,7 @@ static const notmuch_show_format_t format_mbox = {
\r
234 @@ -119,6 +125,7 @@ static const notmuch_show_format_t format_raw = {
\r
239 format_part_content_raw,
\r
242 @@ -496,6 +503,18 @@ format_part_start_json (unused (GMimeObject *part), int *part_count)
\r
246 +format_part_encstatus_json (int status)
\r
248 + printf (", \"encstatus\": [{\"status\": ");
\r
250 + printf ("\"good\"");
\r
252 + printf ("\"bad\"");
\r
258 format_part_sigstatus_json (const GMimeSignatureValidity* validity)
\r
260 printf (", \"sigstatus\": [");
\r
261 @@ -822,6 +841,7 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[]))
\r
264 params.cryptoctx = NULL;
\r
265 + params.decrypt = 0;
\r
267 for (i = 0; i < argc && argv[i][0] == '-'; i++) {
\r
268 if (strcmp (argv[i], "--") == 0) {
\r
269 @@ -850,7 +870,8 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[]))
\r
270 params.part = atoi(argv[i] + sizeof ("--part=") - 1);
\r
271 } else if (STRNCMP_LITERAL (argv[i], "--entire-thread") == 0) {
\r
272 params.entire_thread = 1;
\r
273 - } else if (STRNCMP_LITERAL (argv[i], "--verify") == 0) {
\r
274 + } else if ((STRNCMP_LITERAL (argv[i], "--verify") == 0) ||
\r
275 + (STRNCMP_LITERAL (argv[i], "--decrypt") == 0)) {
\r
276 if (params.cryptoctx == NULL) {
\r
277 GMimeSession* session = g_object_new(notmuch_gmime_session_get_type(), NULL);
\r
278 if (NULL == (params.cryptoctx = g_mime_gpg_context_new(session, "gpg")))
\r
279 @@ -860,6 +881,8 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[]))
\r
280 g_object_unref (session);
\r
283 + if (STRNCMP_LITERAL (argv[i], "--decrypt") == 0)
\r
284 + params.decrypt = 1;
\r
286 fprintf (stderr, "Unrecognized option: %s\n", argv[i]);
\r
288 diff --git a/notmuch.1 b/notmuch.1
\r
289 index daa9c6f..c1aa4e3 100644
\r
292 @@ -332,6 +332,17 @@ of the signature will be reported (currently only supported with
\r
300 +Decrypt any MIME encrypted parts found in the selected content
\r
301 +(ie. "multipart/encrypted" parts). Status of the decryption will be
\r
302 +reported (currently only supported with --format=json) and the
\r
303 +multipart/encrypted part will be replaced by the decrypted
\r
309 is to display a single thread of email messages. For this, use a
\r
310 diff --git a/notmuch.c b/notmuch.c
\r
311 index cd3cb1b..93f3194 100644
\r
314 @@ -302,6 +302,14 @@ static command_t commands[] = {
\r
315 "\t\treported (currently only supported with --format=json) and\n"
\r
316 "\t\tthe multipart/signed part will be replaced by the signed data.\n"
\r
320 + "\t\tDecrypt any MIME encrypted parts found in the selected content\n"
\r
321 + "\t\t(ie. \"multipart/encrypted\" parts). Status of the decryption\n"
\r
322 + "\t\twill be reported (currently only supported with --format=json)\n"
\r
323 + "\t\tand the multipart/encrypted part will be replaced by the\n"
\r
324 + "\t\tdecrypted content.\n"
\r
327 "\tA common use of \"notmuch show\" is to display a single\n"
\r
328 "\tthread of email messages. For this, use a search term of\n"
\r
329 diff --git a/show-message.c b/show-message.c
\r
330 index c90f310..37252b2 100644
\r
331 --- a/show-message.c
\r
332 +++ b/show-message.c
\r
333 @@ -58,7 +58,34 @@ show_message_part (GMimeObject *part,
\r
334 GMimeMultipart *multipart = GMIME_MULTIPART (part);
\r
335 GError* err = NULL;
\r
337 - if (GMIME_IS_MULTIPART_SIGNED (part))
\r
338 + if (GMIME_IS_MULTIPART_ENCRYPTED (part) && params->decrypt)
\r
340 + if ( g_mime_multipart_get_count (multipart) != 2 ) {
\r
341 + /* this violates RFC 3156 section 4, so we won't bother with it. */
\r
343 + "Error: %d part(s) for a multipart/encrypted message (should be exactly 2)\n",
\r
344 + g_mime_multipart_get_count (multipart));
\r
346 + GMimeMultipartEncrypted *encrypteddata = GMIME_MULTIPART_ENCRYPTED (part);
\r
347 + GMimeObject *decryptedpart = g_mime_multipart_encrypted_decrypt (encrypteddata, params->cryptoctx, &err);
\r
348 + if (decryptedpart) {
\r
349 + if ((selected || state->in_zone) && format->part_encstatus)
\r
350 + format->part_encstatus (1);
\r
351 + const GMimeSignatureValidity *sigvalidity = g_mime_multipart_encrypted_get_signature_validity (encrypteddata);
\r
352 + if (!sigvalidity)
\r
353 + fprintf (stderr, "Failed to verify signed part: %s\n", (err ? err->message : "no error explanation given"));
\r
354 + if ((selected || state->in_zone) && format->part_sigstatus)
\r
355 + format->part_sigstatus (sigvalidity);
\r
356 + /* swap the part with the decrypted part */
\r
357 + part = decryptedpart;
\r
359 + fprintf (stderr, "Failed to decrypt part: %s\n", (err ? err->message : "no error explanation given"));
\r
360 + if ((selected || state->in_zone) && format->part_encstatus)
\r
361 + format->part_encstatus (0);
\r
365 + else if (GMIME_IS_MULTIPART_SIGNED (part))
\r
367 if ( g_mime_multipart_get_count (multipart) != 2 ) {
\r
368 /* this violates RFC 3156 section 5, so we won't bother with it. */
\r