1 Return-Path: <schnouki@schnouki.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 92498421167
\r
6 for <notmuch@notmuchmail.org>; Tue, 17 Jan 2012 02:51:13 -0800 (PST)
\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org
\r
11 X-Spam-Status: No, score=-0.1 tagged_above=-999 required=5
\r
12 tests=[DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1]
\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 aSSHtdVvfF20 for <notmuch@notmuchmail.org>;
\r
17 Tue, 17 Jan 2012 02:51:09 -0800 (PST)
\r
18 Received: from ks3536.kimsufi.com (schnouki.net [87.98.217.222])
\r
19 by olra.theworths.org (Postfix) with ESMTP id E2362429E35
\r
20 for <notmuch@notmuchmail.org>; Tue, 17 Jan 2012 02:51:08 -0800 (PST)
\r
21 Received: from thor.loria.fr (thor.loria.fr [152.81.12.250])
\r
22 by ks3536.kimsufi.com (Postfix) with ESMTPSA id 26D076A002C;
\r
23 Tue, 17 Jan 2012 11:51:08 +0100 (CET)
\r
24 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=schnouki.net;
\r
25 s=key-schnouki; t=1326797468;
\r
26 bh=hQUKWmOzROeuIwxgc3SgfO0A4P47/IV0YTQ6a4B+vHY=;
\r
27 h=From:To:Subject:Date:Message-Id:In-Reply-To:References;
\r
28 b=Wg+XQRRBVz/gWqmDzrLy2Dsu5StcK2x9TxZKPi9NDtipxd/iM4VlwlkrGyFwBgSES
\r
29 BxWzDUaiX/p9Q3901cjS+rSpixVZ1fC9Hkr6FRbHPSeTodRwA9f06WlR5tpRqLntFR
\r
30 clmMlayDv7xDts8GGU8bevLCEAw+EY9XstJbcXk0=
\r
31 From: Thomas Jost <schnouki@schnouki.net>
\r
32 To: notmuch@notmuchmail.org
\r
33 Subject: [PATCH v2 2/2] Add pseudo-compatibility with gmime 2.6
\r
34 Date: Tue, 17 Jan 2012 11:50:53 +0100
\r
35 Message-Id: <1326797453-9405-2-git-send-email-schnouki@schnouki.net>
\r
36 X-Mailer: git-send-email 1.7.8.3
\r
37 In-Reply-To: <1326797453-9405-1-git-send-email-schnouki@schnouki.net>
\r
38 References: <8739bea9lc.fsf@thor.loria.fr>
\r
39 <1326797453-9405-1-git-send-email-schnouki@schnouki.net>
\r
40 X-BeenThere: notmuch@notmuchmail.org
\r
41 X-Mailman-Version: 2.1.13
\r
43 List-Id: "Use and development of the notmuch mail system."
\r
44 <notmuch.notmuchmail.org>
\r
45 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,
\r
46 <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>
\r
47 List-Archive: <http://notmuchmail.org/pipermail/notmuch>
\r
48 List-Post: <mailto:notmuch@notmuchmail.org>
\r
49 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>
\r
50 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,
\r
51 <mailto:notmuch-request@notmuchmail.org?subject=subscribe>
\r
52 X-List-Received-Date: Tue, 17 Jan 2012 10:51:13 -0000
\r
54 There are lots of API changes in gmime 2.6 crypto handling. By adding
\r
55 preprocessor directives, it is however possible to add gmime 2.6 compatibility
\r
56 while preserving compatibility with gmime 2.4 too.
\r
58 This is mostly based on id:"8762i8hrb9.fsf@bookbinder.fernseed.info".
\r
60 This was tested against both gmime 2.6.4 and 2.4.31. With gmime 2.4.31, the
\r
61 crypto tests all work fine (as expected). With gmime 2.6.4, one crypto test
\r
62 fails (signature verification with signer key unavailable) but this will be hard
\r
63 to fix since the new API does not report the reason why a signature verification
\r
64 fails (other than the human-readable error message).
\r
66 mime-node.c | 56 ++++++++++++++++++++++++++++++--
\r
67 notmuch-client.h | 28 +++++++++++++++-
\r
68 notmuch-reply.c | 7 ++++
\r
69 notmuch-show.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
\r
70 show-message.c | 4 ++
\r
71 5 files changed, 185 insertions(+), 5 deletions(-)
\r
73 diff --git a/mime-node.c b/mime-node.c
\r
74 index d26bb44..e575e1c 100644
\r
77 @@ -33,7 +33,11 @@ typedef struct mime_node_context {
\r
78 GMimeMessage *mime_message;
\r
80 /* Context provided by the caller. */
\r
82 + GMimeCryptoContext *cryptoctx;
\r
84 GMimeCipherContext *cryptoctx;
\r
86 notmuch_bool_t decrypt;
\r
87 } mime_node_context_t;
\r
89 @@ -57,8 +61,12 @@ _mime_node_context_free (mime_node_context_t *res)
\r
92 mime_node_open (const void *ctx, notmuch_message_t *message,
\r
93 - GMimeCipherContext *cryptoctx, notmuch_bool_t decrypt,
\r
94 - mime_node_t **root_out)
\r
96 + GMimeCryptoContext *cryptoctx,
\r
98 + GMimeCipherContext *cryptoctx,
\r
100 + notmuch_bool_t decrypt, mime_node_t **root_out)
\r
102 const char *filename = notmuch_message_get_filename (message);
\r
103 mime_node_context_t *mctx;
\r
104 @@ -112,12 +120,21 @@ DONE:
\r
110 +_signature_list_free (GMimeSignatureList **proxy)
\r
112 + g_object_unref (*proxy);
\r
117 _signature_validity_free (GMimeSignatureValidity **proxy)
\r
119 g_mime_signature_validity_free (*proxy);
\r
124 static mime_node_t *
\r
125 _mime_node_create (const mime_node_t *parent, GMimeObject *part)
\r
126 @@ -165,11 +182,22 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part)
\r
127 GMimeMultipartEncrypted *encrypteddata =
\r
128 GMIME_MULTIPART_ENCRYPTED (part);
\r
129 node->decrypt_attempted = TRUE;
\r
131 + GMimeDecryptResult *decrypt_result = NULL;
\r
132 + node->decrypted_child = g_mime_multipart_encrypted_decrypt
\r
133 + (encrypteddata, node->ctx->cryptoctx, &decrypt_result, &err);
\r
135 node->decrypted_child = g_mime_multipart_encrypted_decrypt
\r
136 (encrypteddata, node->ctx->cryptoctx, &err);
\r
138 if (node->decrypted_child) {
\r
139 - node->decrypt_success = node->verify_attempted = TRUE;
\r
140 + node->decrypt_success = node->verify_attempted =TRUE;
\r
142 + /* This may be NULL if the part is not signed. */
\r
143 + node->sig_list = g_mime_decrypt_result_get_signatures (decrypt_result);
\r
145 node->sig_validity = g_mime_multipart_encrypted_get_signature_validity (encrypteddata);
\r
148 fprintf (stderr, "Failed to decrypt part: %s\n",
\r
149 (err ? err->message : "no error explanation given"));
\r
150 @@ -182,6 +210,16 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part)
\r
151 "(must be exactly 2)\n",
\r
155 + GMimeSignatureList *sig_list = g_mime_multipart_signed_verify
\r
156 + (GMIME_MULTIPART_SIGNED (part), node->ctx->cryptoctx, &err);
\r
157 + node->verify_attempted = TRUE;
\r
158 + node->sig_list = sig_list;
\r
161 + fprintf (stderr, "Failed to verify signed part: %s\n",
\r
162 + (err ? err->message : "no error explanation given"));
\r
164 /* For some reason the GMimeSignatureValidity returned
\r
165 * here is not a const (inconsistent with that
\r
167 @@ -200,12 +238,24 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part)
\r
168 *proxy = sig_validity;
\r
169 talloc_set_destructor (proxy, _signature_validity_free);
\r
176 + /* sig_list may be created in both above cases, so we need to
\r
177 + * cleanly handle it here. */
\r
178 + if (node->sig_list) {
\r
179 + GMimeSignatureList **proxy =
\r
180 + talloc (node, GMimeSignatureList *);
\r
181 + *proxy = node->sig_list;
\r
182 + talloc_set_destructor (proxy, _signature_list_free);
\r
185 if (node->verify_attempted && !node->sig_validity)
\r
186 fprintf (stderr, "Failed to verify signed part: %s\n",
\r
187 (err ? err->message : "no error explanation given"));
\r
191 g_error_free (err);
\r
192 diff --git a/notmuch-client.h b/notmuch-client.h
\r
193 index 62ede28..9167042 100644
\r
194 --- a/notmuch-client.h
\r
195 +++ b/notmuch-client.h
\r
198 #include <gmime/gmime.h>
\r
200 +/* GMIME_CHECK_VERSION is only available in gmime >= 2.5. But so are
\r
201 + * GMIME_MAJOR_VERSION and friends. */
\r
202 +#ifdef GMIME_MAJOR_VERSION
\r
206 #include "notmuch.h"
\r
208 /* This is separate from notmuch-private.h because we're trying to
\r
209 @@ -69,7 +75,11 @@ typedef struct notmuch_show_format {
\r
210 void (*part_start) (GMimeObject *part,
\r
212 void (*part_encstatus) (int status);
\r
214 + void (*part_sigstatus) (GMimeSignatureList* siglist);
\r
216 void (*part_sigstatus) (const GMimeSignatureValidity* validity);
\r
218 void (*part_content) (GMimeObject *part);
\r
219 void (*part_end) (GMimeObject *part);
\r
220 const char *part_sep;
\r
221 @@ -83,7 +93,11 @@ typedef struct notmuch_show_params {
\r
226 + GMimeCryptoContext* cryptoctx;
\r
228 GMimeCipherContext* cryptoctx;
\r
231 } notmuch_show_params_t;
\r
233 @@ -290,11 +304,17 @@ typedef struct mime_node {
\r
235 /* True if signature verification on this part was attempted. */
\r
236 notmuch_bool_t verify_attempted;
\r
238 + /* The list of signatures for signed or encrypted containers. If
\r
239 + * there are no signatures, this will be NULL. */
\r
240 + GMimeSignatureList* sig_list;
\r
242 /* For signed or encrypted containers, the validity of the
\r
243 * signature. May be NULL if signature verification failed. If
\r
244 * there are simply no signatures, this will be non-NULL with an
\r
245 * empty signers list. */
\r
246 const GMimeSignatureValidity *sig_validity;
\r
249 /* Internal: Context inherited from the root iterator. */
\r
250 struct mime_node_context *ctx;
\r
251 @@ -319,8 +339,12 @@ typedef struct mime_node {
\r
254 mime_node_open (const void *ctx, notmuch_message_t *message,
\r
255 - GMimeCipherContext *cryptoctx, notmuch_bool_t decrypt,
\r
256 - mime_node_t **node_out);
\r
258 + GMimeCryptoContext *cryptoctx,
\r
260 + GMimeCipherContext *cryptoctx,
\r
262 + notmuch_bool_t decrypt, mime_node_t **node_out);
\r
264 /* Return a new MIME node for the requested child part of parent.
\r
265 * parent will be used as the talloc context for the returned child
\r
266 diff --git a/notmuch-reply.c b/notmuch-reply.c
\r
267 index 0f682db..b3d7127 100644
\r
268 --- a/notmuch-reply.c
\r
269 +++ b/notmuch-reply.c
\r
270 @@ -688,15 +688,22 @@ notmuch_reply_command (void *ctx, int argc, char *argv[])
\r
271 reply_format_func = notmuch_reply_format_default;
\r
275 + /* TODO: GMimePasswordRequestFunc */
\r
276 + params.cryptoctx = g_mime_gpg_context_new (NULL, "gpg");
\r
278 GMimeSession* session = g_object_new (g_mime_session_get_type(), NULL);
\r
279 params.cryptoctx = g_mime_gpg_context_new (session, "gpg");
\r
281 if (params.cryptoctx) {
\r
282 g_mime_gpg_context_set_always_trust ((GMimeGpgContext*) params.cryptoctx, FALSE);
\r
283 params.decrypt = TRUE;
\r
285 fprintf (stderr, "Failed to construct gpg context.\n");
\r
288 g_object_unref (session);
\r
292 config = notmuch_config_open (ctx, NULL, NULL);
\r
293 diff --git a/notmuch-show.c b/notmuch-show.c
\r
294 index 91f566c..10223e0 100644
\r
295 --- a/notmuch-show.c
\r
296 +++ b/notmuch-show.c
\r
297 @@ -76,7 +76,11 @@ static void
\r
298 format_part_encstatus_json (int status);
\r
302 +format_part_sigstatus_json (GMimeSignatureList* siglist);
\r
304 format_part_sigstatus_json (const GMimeSignatureValidity* validity);
\r
308 format_part_content_json (GMimeObject *part);
\r
309 @@ -486,6 +490,21 @@ show_text_part_content (GMimeObject *part, GMimeStream *stream_out)
\r
310 g_object_unref(stream_filter);
\r
314 +static const char*
\r
315 +signature_status_to_string (GMimeSignatureStatus x)
\r
318 + case GMIME_SIGNATURE_STATUS_GOOD:
\r
320 + case GMIME_SIGNATURE_STATUS_BAD:
\r
322 + case GMIME_SIGNATURE_STATUS_ERROR:
\r
325 + return "unknown";
\r
329 signer_status_to_string (GMimeSignerStatus x)
\r
331 @@ -501,6 +520,7 @@ signer_status_to_string (GMimeSignerStatus x)
\r
338 format_part_start_text (GMimeObject *part, int *part_count)
\r
339 @@ -592,6 +612,73 @@ format_part_encstatus_json (int status)
\r
345 +format_part_sigstatus_json (GMimeSignatureList *siglist)
\r
347 + printf (", \"sigstatus\": [");
\r
354 + void *ctx_quote = talloc_new (NULL);
\r
356 + for (i = 0; i < g_mime_signature_list_length (siglist); i++) {
\r
357 + GMimeSignature *signature = g_mime_signature_list_get_signature (siglist, i);
\r
365 + GMimeSignatureStatus status = g_mime_signature_get_status (signature);
\r
366 + printf ("\"status\": %s",
\r
367 + json_quote_str (ctx_quote,
\r
368 + signature_status_to_string (status)));
\r
370 + GMimeCertificate *certificate = g_mime_signature_get_certificate (signature);
\r
371 + if (status == GMIME_SIGNATURE_STATUS_GOOD) {
\r
373 + printf (", \"fingerprint\": %s", json_quote_str (ctx_quote, g_mime_certificate_get_fingerprint (certificate)));
\r
374 + /* these dates are seconds since the epoch; should we
\r
375 + * provide a more human-readable format string? */
\r
376 + time_t created = g_mime_signature_get_created (signature);
\r
377 + if (created != -1)
\r
378 + printf (", \"created\": %d", (int) created);
\r
379 + time_t expires = g_mime_signature_get_expires (signature);
\r
381 + printf (", \"expires\": %d", (int) expires);
\r
382 + /* output user id only if validity is FULL or ULTIMATE. */
\r
383 + /* note that gmime is using the term "trust" here, which
\r
384 + * is WRONG. It's actually user id "validity". */
\r
385 + if (certificate) {
\r
386 + const char *name = g_mime_certificate_get_name (certificate);
\r
387 + GMimeCertificateTrust trust = g_mime_certificate_get_trust (certificate);
\r
388 + if (name && (trust == GMIME_CERTIFICATE_TRUST_FULLY || trust == GMIME_CERTIFICATE_TRUST_ULTIMATE))
\r
389 + printf (", \"userid\": %s", json_quote_str (ctx_quote, name));
\r
391 + } else if (certificate) {
\r
392 + const char *key_id = g_mime_certificate_get_key_id (certificate);
\r
394 + printf (", \"keyid\": %s", json_quote_str (ctx_quote, key_id));
\r
397 + GMimeSignatureError errors = g_mime_signature_get_errors (signature);
\r
398 + if (errors != GMIME_SIGNATURE_ERROR_NONE) {
\r
399 + printf (", \"errors\": %d", errors);
\r
407 + talloc_free (ctx_quote);
\r
411 format_part_sigstatus_json (const GMimeSignatureValidity* validity)
\r
413 @@ -652,6 +739,7 @@ format_part_sigstatus_json (const GMimeSignatureValidity* validity)
\r
415 talloc_free (ctx_quote);
\r
420 format_part_content_json (GMimeObject *part)
\r
421 @@ -990,13 +1078,20 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[]))
\r
422 } else if ((STRNCMP_LITERAL (argv[i], "--verify") == 0) ||
\r
423 (STRNCMP_LITERAL (argv[i], "--decrypt") == 0)) {
\r
424 if (params.cryptoctx == NULL) {
\r
426 + /* TODO: GMimePasswordRequestFunc */
\r
427 + if (NULL == (params.cryptoctx = g_mime_gpg_context_new(NULL, "gpg")))
\r
429 GMimeSession* session = g_object_new(g_mime_session_get_type(), NULL);
\r
430 if (NULL == (params.cryptoctx = g_mime_gpg_context_new(session, "gpg")))
\r
432 fprintf (stderr, "Failed to construct gpg context.\n");
\r
434 g_mime_gpg_context_set_always_trust((GMimeGpgContext*)params.cryptoctx, FALSE);
\r
436 g_object_unref (session);
\r
440 if (STRNCMP_LITERAL (argv[i], "--decrypt") == 0)
\r
441 params.decrypt = 1;
\r
442 diff --git a/show-message.c b/show-message.c
\r
443 index 8768889..65269fd 100644
\r
444 --- a/show-message.c
\r
445 +++ b/show-message.c
\r
446 @@ -48,7 +48,11 @@ show_message_part (mime_node_t *node,
\r
447 format->part_encstatus (node->decrypt_success);
\r
449 if (node->verify_attempted && format->part_sigstatus)
\r
451 + format->part_sigstatus (node->sig_list);
\r
453 format->part_sigstatus (node->sig_validity);
\r
456 format->part_content (part);
\r