* [PATCH] Add pseudo-compatibility with gmime 2.6 [not found] <8762gbtd6p.fsf@schnouki.net> @ 2012-01-16 23:56 ` Thomas Jost 2012-01-17 0:43 ` Tim Stoakes ` (3 more replies) 0 siblings, 4 replies; 42+ messages in thread From: Thomas Jost @ 2012-01-16 23:56 UTC (permalink / raw) To: notmuch There are lots of API changes in gmime 2.6 crypto handling. By adding preprocessor directives, it is however possible to add gmime 2.6 compatibility while preserving compatibility with gmime 2.4 too. This is mostly based on id:"8762i8hrb9.fsf@bookbinder.fernseed.info". This was tested against both gmime 2.6.4 and 2.4.31. With gmime 2.4.31, the crypto tests all work fine (as expected). With gmime 2.6.4, one crypto test fails (signature verification with signer key unavailable) but this will be hard to fix since the new API does not report the reason why a signature verification fails (other than the human-readable error message). --- mime-node.c | 50 ++++++++++++++++++++++++++- notmuch-client.h | 27 ++++++++++++++- notmuch-reply.c | 7 ++++ notmuch-show.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ show-message.c | 4 ++ 5 files changed, 181 insertions(+), 4 deletions(-) diff --git a/mime-node.c b/mime-node.c index d26bb44..ae2473d 100644 --- a/mime-node.c +++ b/mime-node.c @@ -33,7 +33,11 @@ typedef struct mime_node_context { GMimeMessage *mime_message; /* Context provided by the caller. */ +#ifdef GMIME_26 + GMimeCryptoContext *cryptoctx; +#else GMimeCipherContext *cryptoctx; +#endif notmuch_bool_t decrypt; } mime_node_context_t; @@ -57,8 +61,12 @@ _mime_node_context_free (mime_node_context_t *res) notmuch_status_t mime_node_open (const void *ctx, notmuch_message_t *message, - GMimeCipherContext *cryptoctx, notmuch_bool_t decrypt, - mime_node_t **root_out) +#ifdef GMIME_26 + GMimeCryptoContext *cryptoctx, +#else + GMimeCipherContext *cryptoctx, +#endif + notmuch_bool_t decrypt, mime_node_t **root_out) { const char *filename = notmuch_message_get_filename (message); mime_node_context_t *mctx; @@ -112,12 +120,21 @@ DONE: return status; } +#ifdef GMIME_26 +static int +_signature_list_free (GMimeSignatureList **proxy) +{ + g_object_unref (*proxy); + return 0; +} +#else static int _signature_validity_free (GMimeSignatureValidity **proxy) { g_mime_signature_validity_free (*proxy); return 0; } +#endif static mime_node_t * _mime_node_create (const mime_node_t *parent, GMimeObject *part) @@ -165,11 +182,23 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) GMimeMultipartEncrypted *encrypteddata = GMIME_MULTIPART_ENCRYPTED (part); node->decrypt_attempted = TRUE; +#ifdef GMIME_26 + GMimeDecryptResult *decrypt_result = g_mime_decrypt_result_new (); + node->decrypted_child = g_mime_multipart_encrypted_decrypt + (encrypteddata, node->ctx->cryptoctx, &decrypt_result, &err); + if (node->decrypted_child) { + node->decrypt_success = node->verify_attempted = TRUE; + node->sig_list = g_mime_decrypt_result_get_signatures (decrypt_result); + if (!node->sig_list) + fprintf (stderr, "Failed to get signatures: %s\n", + (err ? err->message : "no error explanation given")); +#else node->decrypted_child = g_mime_multipart_encrypted_decrypt (encrypteddata, node->ctx->cryptoctx, &err); if (node->decrypted_child) { node->decrypt_success = node->verify_attempted = TRUE; node->sig_validity = g_mime_multipart_encrypted_get_signature_validity (encrypteddata); +#endif } else { fprintf (stderr, "Failed to decrypt part: %s\n", (err ? err->message : "no error explanation given")); @@ -182,6 +211,18 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) "(must be exactly 2)\n", node->nchildren); } else { +#ifdef GMIME_26 + GMimeSignatureList *sig_list = g_mime_multipart_signed_verify + (GMIME_MULTIPART_SIGNED (part), node->ctx->cryptoctx, &err); + node->verify_attempted = TRUE; + node->sig_list = sig_list; + if (sig_list) { + GMimeSignatureList **proxy = + talloc (node, GMimeSignatureList *); + *proxy = sig_list; + talloc_set_destructor (proxy, _signature_list_free); + } +#else /* For some reason the GMimeSignatureValidity returned * here is not a const (inconsistent with that * returned by @@ -200,10 +241,15 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) *proxy = sig_validity; talloc_set_destructor (proxy, _signature_validity_free); } +#endif } } +#ifdef GMIME_26 + if (node->verify_attempted && !node->sig_list) +#else if (node->verify_attempted && !node->sig_validity) +#endif fprintf (stderr, "Failed to verify signed part: %s\n", (err ? err->message : "no error explanation given")); diff --git a/notmuch-client.h b/notmuch-client.h index 517c010..e85f882 100644 --- a/notmuch-client.h +++ b/notmuch-client.h @@ -30,6 +30,12 @@ #include <gmime/gmime.h> +/* GMIME_CHECK_VERSION is only available in gmime >= 2.5. But so are + * GMIME_MAJOR_VERSION and friends. */ +#ifdef GMIME_MAJOR_VERSION +#define GMIME_26 +#endif + #include "notmuch.h" /* This is separate from notmuch-private.h because we're trying to @@ -69,7 +75,11 @@ typedef struct notmuch_show_format { void (*part_start) (GMimeObject *part, int *part_count); void (*part_encstatus) (int status); +#ifdef GMIME_26 + void (*part_sigstatus) (GMimeSignatureList* siglist); +#else void (*part_sigstatus) (const GMimeSignatureValidity* validity); +#endif void (*part_content) (GMimeObject *part); void (*part_end) (GMimeObject *part); const char *part_sep; @@ -83,7 +93,11 @@ typedef struct notmuch_show_params { int entire_thread; int raw; int part; +#ifdef GMIME_26 + GMimeCryptoContext* cryptoctx; +#else GMimeCipherContext* cryptoctx; +#endif int decrypt; } notmuch_show_params_t; @@ -286,7 +300,12 @@ typedef struct mime_node { * signature. May be NULL if signature verification failed. If * there are simply no signatures, this will be non-NULL with an * empty signers list. */ +#ifdef GMIME_26 + /* TODO: update the above comment... */ + GMimeSignatureList* sig_list; +#else const GMimeSignatureValidity *sig_validity; +#endif /* Internal: Context inherited from the root iterator. */ struct mime_node_context *ctx; @@ -311,8 +330,12 @@ typedef struct mime_node { */ notmuch_status_t mime_node_open (const void *ctx, notmuch_message_t *message, - GMimeCipherContext *cryptoctx, notmuch_bool_t decrypt, - mime_node_t **node_out); +#ifdef GMIME_26 + GMimeCryptoContext *cryptoctx, +#else + GMimeCipherContext *cryptoctx, +#endif + notmuch_bool_t decrypt, mime_node_t **node_out); /* Return a new MIME node for the requested child part of parent. * parent will be used as the talloc context for the returned child diff --git a/notmuch-reply.c b/notmuch-reply.c index da3acce..dc37c51 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -688,15 +688,22 @@ notmuch_reply_command (void *ctx, int argc, char *argv[]) reply_format_func = notmuch_reply_format_default; if (decrypt) { +#ifdef GMIME_26 + /* TODO: GMimePasswordRequestFunc */ + params.cryptoctx = g_mime_gpg_context_new (NULL, "gpg"); +#else GMimeSession* session = g_object_new (g_mime_session_get_type(), NULL); params.cryptoctx = g_mime_gpg_context_new (session, "gpg"); +#endif if (params.cryptoctx) { g_mime_gpg_context_set_always_trust ((GMimeGpgContext*) params.cryptoctx, FALSE); params.decrypt = TRUE; } else { fprintf (stderr, "Failed to construct gpg context.\n"); } +#ifndef GMIME_26 g_object_unref (session); +#endif } config = notmuch_config_open (ctx, NULL, NULL); diff --git a/notmuch-show.c b/notmuch-show.c index d14dac9..263ab72 100644 --- a/notmuch-show.c +++ b/notmuch-show.c @@ -76,7 +76,11 @@ static void format_part_encstatus_json (int status); static void +#ifdef GMIME_26 +format_part_sigstatus_json (GMimeSignatureList* siglist); +#else format_part_sigstatus_json (const GMimeSignatureValidity* validity); +#endif static void format_part_content_json (GMimeObject *part); @@ -486,6 +490,21 @@ show_text_part_content (GMimeObject *part, GMimeStream *stream_out) g_object_unref(stream_filter); } +#ifdef GMIME_26 +static const char* +signature_status_to_string (GMimeSignatureStatus x) +{ + switch (x) { + case GMIME_SIGNATURE_STATUS_GOOD: + return "good"; + case GMIME_SIGNATURE_STATUS_BAD: + return "bad"; + case GMIME_SIGNATURE_STATUS_ERROR: + return "error"; + } + return "unknown"; +} +#else static const char* signer_status_to_string (GMimeSignerStatus x) { @@ -501,6 +520,7 @@ signer_status_to_string (GMimeSignerStatus x) } return "unknown"; } +#endif static void format_part_start_text (GMimeObject *part, int *part_count) @@ -592,6 +612,75 @@ format_part_encstatus_json (int status) printf ("}]"); } +#ifdef GMIME_26 +static void +format_part_sigstatus_json (GMimeSignatureList *siglist) +{ + printf (", \"sigstatus\": ["); + + if (!siglist) { + printf ("]"); + return; + } + + void *ctx_quote = talloc_new (NULL); + int i; + for (i = 0; i < g_mime_signature_list_length (siglist); ++i) { + GMimeSignature *signature = g_mime_signature_list_get_signature (siglist, i); + + if (i > 0) + printf (", "); + + printf ("{"); + + /* status */ + GMimeSignatureStatus status = g_mime_signature_get_status (signature); + printf ("\"status\": %s", + json_quote_str (ctx_quote, + signature_status_to_string (status))); + + GMimeCertificate *certificate = g_mime_signature_get_certificate (signature); + if (status == GMIME_SIGNATURE_STATUS_GOOD) + { + if (certificate) + printf (", \"fingerprint\": %s", json_quote_str (ctx_quote, g_mime_certificate_get_fingerprint (certificate))); + /* these dates are seconds since the epoch; should we + * provide a more human-readable format string? */ + time_t created = g_mime_signature_get_created (signature); + if (created != -1) + printf (", \"created\": %d", (int) created); + time_t expires = g_mime_signature_get_expires (signature); + if (expires > 0) + printf (", \"expires\": %d", (int) expires); + /* output user id only if validity is FULL or ULTIMATE. */ + /* note that gmime is using the term "trust" here, which + * is WRONG. It's actually user id "validity". */ + if (certificate) + { + const char *name = g_mime_certificate_get_name (certificate); + GMimeCertificateTrust trust = g_mime_certificate_get_trust (certificate); + if (name && (trust == GMIME_CERTIFICATE_TRUST_FULLY || trust == GMIME_CERTIFICATE_TRUST_ULTIMATE)) + printf (", \"userid\": %s", json_quote_str (ctx_quote, name)); + } + } else if (certificate) { + const char *key_id = g_mime_certificate_get_key_id (certificate); + if (key_id) + printf (", \"keyid\": %s", json_quote_str (ctx_quote, key_id)); + } + + GMimeSignatureError errors = g_mime_signature_get_errors (signature); + if (errors != GMIME_SIGNATURE_ERROR_NONE) { + printf (", \"errors\": %x", errors); + } + + printf ("}"); + } + + printf ("]"); + + talloc_free (ctx_quote); +} +#else static void format_part_sigstatus_json (const GMimeSignatureValidity* validity) { @@ -652,6 +741,7 @@ format_part_sigstatus_json (const GMimeSignatureValidity* validity) talloc_free (ctx_quote); } +#endif static void format_part_content_json (GMimeObject *part) @@ -990,13 +1080,20 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[])) } else if ((STRNCMP_LITERAL (argv[i], "--verify") == 0) || (STRNCMP_LITERAL (argv[i], "--decrypt") == 0)) { if (params.cryptoctx == NULL) { +#ifdef GMIME_26 + /* TODO: GMimePasswordRequestFunc */ + if (NULL == (params.cryptoctx = g_mime_gpg_context_new(NULL, "gpg"))) +#else GMimeSession* session = g_object_new(g_mime_session_get_type(), NULL); if (NULL == (params.cryptoctx = g_mime_gpg_context_new(session, "gpg"))) +#endif fprintf (stderr, "Failed to construct gpg context.\n"); else g_mime_gpg_context_set_always_trust((GMimeGpgContext*)params.cryptoctx, FALSE); +#ifndef GMIME_26 g_object_unref (session); session = NULL; +#endif } if (STRNCMP_LITERAL (argv[i], "--decrypt") == 0) params.decrypt = 1; diff --git a/show-message.c b/show-message.c index 8768889..65269fd 100644 --- a/show-message.c +++ b/show-message.c @@ -48,7 +48,11 @@ show_message_part (mime_node_t *node, format->part_encstatus (node->decrypt_success); if (node->verify_attempted && format->part_sigstatus) +#ifdef GMIME_26 + format->part_sigstatus (node->sig_list); +#else format->part_sigstatus (node->sig_validity); +#endif format->part_content (part); -- 1.7.8.3 ^ permalink raw reply related [flat|nested] 42+ messages in thread
* Re: [PATCH] Add pseudo-compatibility with gmime 2.6 2012-01-16 23:56 ` [PATCH] Add pseudo-compatibility with gmime 2.6 Thomas Jost @ 2012-01-17 0:43 ` Tim Stoakes 2012-01-17 1:08 ` Kazuo Teramoto ` (2 subsequent siblings) 3 siblings, 0 replies; 42+ messages in thread From: Tim Stoakes @ 2012-01-17 0:43 UTC (permalink / raw) To: Thomas Jost; +Cc: notmuch Thomas Jost(schnouki@schnouki.net)@170112-00:56: > There are lots of API changes in gmime 2.6 crypto handling. By adding > preprocessor directives, it is however possible to add gmime 2.6 compatibility > while preserving compatibility with gmime 2.4 too. > > This is mostly based on id:"8762i8hrb9.fsf@bookbinder.fernseed.info". > > This was tested against both gmime 2.6.4 and 2.4.31. With gmime 2.4.31, the > crypto tests all work fine (as expected). With gmime 2.6.4, one crypto test > fails (signature verification with signer key unavailable) but this will be hard > to fix since the new API does not report the reason why a signature verification > fails (other than the human-readable error message). +1 from me. I literally just coded up a similar patch, and the first email I see with the newly compiled notmuch is this one. I've now tried Thomas's (better) patch with gmime-2.6.4 and notmuch 7ddd849015759a329bf8fef8c8b5a93359408962, and can confirm it works fine for me. This build breakage is otherwise quite painful. Thanks Thomas. Tim -- Tim Stoakes ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH] Add pseudo-compatibility with gmime 2.6 2012-01-16 23:56 ` [PATCH] Add pseudo-compatibility with gmime 2.6 Thomas Jost 2012-01-17 0:43 ` Tim Stoakes @ 2012-01-17 1:08 ` Kazuo Teramoto 2012-01-17 3:47 ` Austin Clements 2012-01-17 10:19 ` Adrian Perez 3 siblings, 0 replies; 42+ messages in thread From: Kazuo Teramoto @ 2012-01-17 1:08 UTC (permalink / raw) To: notmuch [-- Attachment #1: Type: text/plain, Size: 385 bytes --] On Tue, Jan 17, 2012 at 12:56:39AM +0100, Thomas Jost wrote: > There are lots of API changes in gmime 2.6 crypto handling. By adding > preprocessor directives, it is however possible to add gmime 2.6 compatibility > while preserving compatibility with gmime 2.4 too. I tested this with gmime 2.4.31 and 2.6.4. Works for me. In a related note: Arch Linux started to ship gmime 2.6.4. [-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --] ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH] Add pseudo-compatibility with gmime 2.6 2012-01-16 23:56 ` [PATCH] Add pseudo-compatibility with gmime 2.6 Thomas Jost 2012-01-17 0:43 ` Tim Stoakes 2012-01-17 1:08 ` Kazuo Teramoto @ 2012-01-17 3:47 ` Austin Clements 2012-01-17 10:48 ` Adrian Perez 2012-01-17 10:48 ` Thomas Jost 2012-01-17 10:19 ` Adrian Perez 3 siblings, 2 replies; 42+ messages in thread From: Austin Clements @ 2012-01-17 3:47 UTC (permalink / raw) To: Thomas Jost; +Cc: notmuch Quoth Thomas Jost on Jan 17 at 12:56 am: > There are lots of API changes in gmime 2.6 crypto handling. By adding > preprocessor directives, it is however possible to add gmime 2.6 compatibility > while preserving compatibility with gmime 2.4 too. Awesome. Comments inline below. > This is mostly based on id:"8762i8hrb9.fsf@bookbinder.fernseed.info". > > This was tested against both gmime 2.6.4 and 2.4.31. With gmime 2.4.31, the > crypto tests all work fine (as expected). With gmime 2.6.4, one crypto test > fails (signature verification with signer key unavailable) but this will be hard > to fix since the new API does not report the reason why a signature verification > fails (other than the human-readable error message). What is the result of this failing test? > --- > mime-node.c | 50 ++++++++++++++++++++++++++- > notmuch-client.h | 27 ++++++++++++++- > notmuch-reply.c | 7 ++++ > notmuch-show.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ > show-message.c | 4 ++ > 5 files changed, 181 insertions(+), 4 deletions(-) > > diff --git a/mime-node.c b/mime-node.c > index d26bb44..ae2473d 100644 > --- a/mime-node.c > +++ b/mime-node.c > @@ -33,7 +33,11 @@ typedef struct mime_node_context { > GMimeMessage *mime_message; > > /* Context provided by the caller. */ > +#ifdef GMIME_26 > + GMimeCryptoContext *cryptoctx; > +#else > GMimeCipherContext *cryptoctx; > +#endif > notmuch_bool_t decrypt; > } mime_node_context_t; > > @@ -57,8 +61,12 @@ _mime_node_context_free (mime_node_context_t *res) > > notmuch_status_t > mime_node_open (const void *ctx, notmuch_message_t *message, > - GMimeCipherContext *cryptoctx, notmuch_bool_t decrypt, > - mime_node_t **root_out) > +#ifdef GMIME_26 > + GMimeCryptoContext *cryptoctx, > +#else > + GMimeCipherContext *cryptoctx, > +#endif > + notmuch_bool_t decrypt, mime_node_t **root_out) > { > const char *filename = notmuch_message_get_filename (message); > mime_node_context_t *mctx; > @@ -112,12 +120,21 @@ DONE: > return status; > } > > +#ifdef GMIME_26 > +static int > +_signature_list_free (GMimeSignatureList **proxy) > +{ > + g_object_unref (*proxy); > + return 0; > +} > +#else > static int > _signature_validity_free (GMimeSignatureValidity **proxy) > { > g_mime_signature_validity_free (*proxy); > return 0; > } > +#endif > > static mime_node_t * > _mime_node_create (const mime_node_t *parent, GMimeObject *part) > @@ -165,11 +182,23 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) > GMimeMultipartEncrypted *encrypteddata = > GMIME_MULTIPART_ENCRYPTED (part); > node->decrypt_attempted = TRUE; > +#ifdef GMIME_26 > + GMimeDecryptResult *decrypt_result = g_mime_decrypt_result_new (); I think g_mime_multipart_encrypted_decrypt allocates the GMimeDecryptResult for you, so this will just leak memory. > + node->decrypted_child = g_mime_multipart_encrypted_decrypt > + (encrypteddata, node->ctx->cryptoctx, &decrypt_result, &err); > + if (node->decrypted_child) { > + node->decrypt_success = node->verify_attempted = TRUE; > + node->sig_list = g_mime_decrypt_result_get_signatures (decrypt_result); > + if (!node->sig_list) > + fprintf (stderr, "Failed to get signatures: %s\n", > + (err ? err->message : "no error explanation given")); My understanding is that g_mime_decrypt_result_get_signatures returns NULL if there are no signatures and that this isn't an error. This differs from 2.4, which would return an empty but non-NULL list. Also, I believe you have to free the sig_list in both branches now, which means the talloc_set_destructor can be moved to common logic outside of the if decrypted/signed. > +#else > node->decrypted_child = g_mime_multipart_encrypted_decrypt > (encrypteddata, node->ctx->cryptoctx, &err); > if (node->decrypted_child) { > node->decrypt_success = node->verify_attempted = TRUE; > node->sig_validity = g_mime_multipart_encrypted_get_signature_validity (encrypteddata); > +#endif It's confusing to have the open braces in the #ifdef'd region with a matching close brace outside of it (and I imagine this confuses editors and uncrustify, too). You could either copy the else part in both branches of the #ifdef or avoid duplicated code with something like #ifdef GMIME_26 .. node->decrypted_child = .. #else .. node->decrypted_child = .. #endif if (node->decrypted_child) { node->decrypt_success = node->verify_attempted = TRUE; #ifdef GMIME_26 node->sig_list = .. #else node->sig_validity = .. #endif } else { fprintf (stderr, ..); } > } else { > fprintf (stderr, "Failed to decrypt part: %s\n", > (err ? err->message : "no error explanation given")); > @@ -182,6 +211,18 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) > "(must be exactly 2)\n", > node->nchildren); > } else { > +#ifdef GMIME_26 > + GMimeSignatureList *sig_list = g_mime_multipart_signed_verify > + (GMIME_MULTIPART_SIGNED (part), node->ctx->cryptoctx, &err); > + node->verify_attempted = TRUE; > + node->sig_list = sig_list; > + if (sig_list) { > + GMimeSignatureList **proxy = > + talloc (node, GMimeSignatureList *); > + *proxy = sig_list; > + talloc_set_destructor (proxy, _signature_list_free); > + } > +#else > /* For some reason the GMimeSignatureValidity returned > * here is not a const (inconsistent with that > * returned by > @@ -200,10 +241,15 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) > *proxy = sig_validity; > talloc_set_destructor (proxy, _signature_validity_free); > } > +#endif > } > } > > +#ifdef GMIME_26 > + if (node->verify_attempted && !node->sig_list) Hmm. This is correct for signed parts, but will incorrectly trigger for an encrypted part with no signatures. For 2.6, I think this error checking may have to move into the branches of the if encrypted/signed since for encrypted parts you have to check if g_mime_multipart_encrypted_decrypt returned NULL. > +#else > if (node->verify_attempted && !node->sig_validity) > +#endif > fprintf (stderr, "Failed to verify signed part: %s\n", > (err ? err->message : "no error explanation given")); > > diff --git a/notmuch-client.h b/notmuch-client.h > index 517c010..e85f882 100644 > --- a/notmuch-client.h > +++ b/notmuch-client.h > @@ -30,6 +30,12 @@ > > #include <gmime/gmime.h> > > +/* GMIME_CHECK_VERSION is only available in gmime >= 2.5. But so are > + * GMIME_MAJOR_VERSION and friends. */ Hah. > +#ifdef GMIME_MAJOR_VERSION > +#define GMIME_26 > +#endif > + > #include "notmuch.h" > > /* This is separate from notmuch-private.h because we're trying to > @@ -69,7 +75,11 @@ typedef struct notmuch_show_format { > void (*part_start) (GMimeObject *part, > int *part_count); > void (*part_encstatus) (int status); > +#ifdef GMIME_26 > + void (*part_sigstatus) (GMimeSignatureList* siglist); > +#else > void (*part_sigstatus) (const GMimeSignatureValidity* validity); > +#endif > void (*part_content) (GMimeObject *part); > void (*part_end) (GMimeObject *part); > const char *part_sep; > @@ -83,7 +93,11 @@ typedef struct notmuch_show_params { > int entire_thread; > int raw; > int part; > +#ifdef GMIME_26 > + GMimeCryptoContext* cryptoctx; > +#else > GMimeCipherContext* cryptoctx; > +#endif > int decrypt; > } notmuch_show_params_t; > > @@ -286,7 +300,12 @@ typedef struct mime_node { > * signature. May be NULL if signature verification failed. If > * there are simply no signatures, this will be non-NULL with an > * empty signers list. */ > +#ifdef GMIME_26 > + /* TODO: update the above comment... */ Since this behaves very differently in 2.6, I think documenting it is important (and being very careful about the differences). Maybe /* The list of signatures for signed or encrypted containers. If * there are no signatures, this will be NULL. */ > + GMimeSignatureList* sig_list; > +#else > const GMimeSignatureValidity *sig_validity; > +#endif > > /* Internal: Context inherited from the root iterator. */ > struct mime_node_context *ctx; > @@ -311,8 +330,12 @@ typedef struct mime_node { > */ > notmuch_status_t > mime_node_open (const void *ctx, notmuch_message_t *message, > - GMimeCipherContext *cryptoctx, notmuch_bool_t decrypt, > - mime_node_t **node_out); > +#ifdef GMIME_26 > + GMimeCryptoContext *cryptoctx, > +#else > + GMimeCipherContext *cryptoctx, > +#endif > + notmuch_bool_t decrypt, mime_node_t **node_out); > > /* Return a new MIME node for the requested child part of parent. > * parent will be used as the talloc context for the returned child > diff --git a/notmuch-reply.c b/notmuch-reply.c > index da3acce..dc37c51 100644 > --- a/notmuch-reply.c > +++ b/notmuch-reply.c > @@ -688,15 +688,22 @@ notmuch_reply_command (void *ctx, int argc, char *argv[]) > reply_format_func = notmuch_reply_format_default; > > if (decrypt) { > +#ifdef GMIME_26 > + /* TODO: GMimePasswordRequestFunc */ > + params.cryptoctx = g_mime_gpg_context_new (NULL, "gpg"); > +#else > GMimeSession* session = g_object_new (g_mime_session_get_type(), NULL); > params.cryptoctx = g_mime_gpg_context_new (session, "gpg"); > +#endif > if (params.cryptoctx) { > g_mime_gpg_context_set_always_trust ((GMimeGpgContext*) params.cryptoctx, FALSE); > params.decrypt = TRUE; > } else { > fprintf (stderr, "Failed to construct gpg context.\n"); > } > +#ifndef GMIME_26 > g_object_unref (session); > +#endif > } > > config = notmuch_config_open (ctx, NULL, NULL); > diff --git a/notmuch-show.c b/notmuch-show.c > index d14dac9..263ab72 100644 > --- a/notmuch-show.c > +++ b/notmuch-show.c > @@ -76,7 +76,11 @@ static void > format_part_encstatus_json (int status); > > static void > +#ifdef GMIME_26 > +format_part_sigstatus_json (GMimeSignatureList* siglist); > +#else > format_part_sigstatus_json (const GMimeSignatureValidity* validity); > +#endif > > static void > format_part_content_json (GMimeObject *part); > @@ -486,6 +490,21 @@ show_text_part_content (GMimeObject *part, GMimeStream *stream_out) > g_object_unref(stream_filter); > } > > +#ifdef GMIME_26 > +static const char* > +signature_status_to_string (GMimeSignatureStatus x) > +{ > + switch (x) { > + case GMIME_SIGNATURE_STATUS_GOOD: > + return "good"; > + case GMIME_SIGNATURE_STATUS_BAD: > + return "bad"; > + case GMIME_SIGNATURE_STATUS_ERROR: > + return "error"; > + } > + return "unknown"; > +} > +#else > static const char* > signer_status_to_string (GMimeSignerStatus x) > { > @@ -501,6 +520,7 @@ signer_status_to_string (GMimeSignerStatus x) > } > return "unknown"; > } > +#endif > > static void > format_part_start_text (GMimeObject *part, int *part_count) > @@ -592,6 +612,75 @@ format_part_encstatus_json (int status) > printf ("}]"); > } > > +#ifdef GMIME_26 > +static void > +format_part_sigstatus_json (GMimeSignatureList *siglist) > +{ > + printf (", \"sigstatus\": ["); > + > + if (!siglist) { > + printf ("]"); > + return; > + } > + > + void *ctx_quote = talloc_new (NULL); > + int i; > + for (i = 0; i < g_mime_signature_list_length (siglist); ++i) { Style nit: notmuch uses i++. > + GMimeSignature *signature = g_mime_signature_list_get_signature (siglist, i); > + > + if (i > 0) > + printf (", "); > + > + printf ("{"); > + > + /* status */ > + GMimeSignatureStatus status = g_mime_signature_get_status (signature); > + printf ("\"status\": %s", > + json_quote_str (ctx_quote, > + signature_status_to_string (status))); > + > + GMimeCertificate *certificate = g_mime_signature_get_certificate (signature); > + if (status == GMIME_SIGNATURE_STATUS_GOOD) > + { Style nit: break after brace. (Presumably this is copied from the existing format_part_sigstatus_json, but since it's technically new code there's no reason not to fix these up.) > + if (certificate) > + printf (", \"fingerprint\": %s", json_quote_str (ctx_quote, g_mime_certificate_get_fingerprint (certificate))); > + /* these dates are seconds since the epoch; should we > + * provide a more human-readable format string? */ > + time_t created = g_mime_signature_get_created (signature); > + if (created != -1) > + printf (", \"created\": %d", (int) created); > + time_t expires = g_mime_signature_get_expires (signature); > + if (expires > 0) > + printf (", \"expires\": %d", (int) expires); Is it intentional that the two above checks are different? I would think the second should be expires != -1. > + /* output user id only if validity is FULL or ULTIMATE. */ > + /* note that gmime is using the term "trust" here, which > + * is WRONG. It's actually user id "validity". */ > + if (certificate) > + { Break after brace. > + const char *name = g_mime_certificate_get_name (certificate); > + GMimeCertificateTrust trust = g_mime_certificate_get_trust (certificate); > + if (name && (trust == GMIME_CERTIFICATE_TRUST_FULLY || trust == GMIME_CERTIFICATE_TRUST_ULTIMATE)) > + printf (", \"userid\": %s", json_quote_str (ctx_quote, name)); > + } > + } else if (certificate) { > + const char *key_id = g_mime_certificate_get_key_id (certificate); > + if (key_id) > + printf (", \"keyid\": %s", json_quote_str (ctx_quote, key_id)); > + } > + > + GMimeSignatureError errors = g_mime_signature_get_errors (signature); > + if (errors != GMIME_SIGNATURE_ERROR_NONE) { > + printf (", \"errors\": %x", errors); This should be %d (I would say 0x%x, but JSON doesn't support hex literals). I see this bug came from the original format_part_sigstatus_json. Maybe there should be a quick patch before this one that fixes the source of the bug? > + } > + > + printf ("}"); > + } > + > + printf ("]"); > + > + talloc_free (ctx_quote); > +} > +#else > static void > format_part_sigstatus_json (const GMimeSignatureValidity* validity) > { > @@ -652,6 +741,7 @@ format_part_sigstatus_json (const GMimeSignatureValidity* validity) > > talloc_free (ctx_quote); > } > +#endif > > static void > format_part_content_json (GMimeObject *part) > @@ -990,13 +1080,20 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[])) > } else if ((STRNCMP_LITERAL (argv[i], "--verify") == 0) || > (STRNCMP_LITERAL (argv[i], "--decrypt") == 0)) { > if (params.cryptoctx == NULL) { > +#ifdef GMIME_26 > + /* TODO: GMimePasswordRequestFunc */ > + if (NULL == (params.cryptoctx = g_mime_gpg_context_new(NULL, "gpg"))) > +#else > GMimeSession* session = g_object_new(g_mime_session_get_type(), NULL); > if (NULL == (params.cryptoctx = g_mime_gpg_context_new(session, "gpg"))) > +#endif > fprintf (stderr, "Failed to construct gpg context.\n"); > else > g_mime_gpg_context_set_always_trust((GMimeGpgContext*)params.cryptoctx, FALSE); > +#ifndef GMIME_26 > g_object_unref (session); > session = NULL; > +#endif > } > if (STRNCMP_LITERAL (argv[i], "--decrypt") == 0) > params.decrypt = 1; > diff --git a/show-message.c b/show-message.c > index 8768889..65269fd 100644 > --- a/show-message.c > +++ b/show-message.c > @@ -48,7 +48,11 @@ show_message_part (mime_node_t *node, > format->part_encstatus (node->decrypt_success); > > if (node->verify_attempted && format->part_sigstatus) > +#ifdef GMIME_26 > + format->part_sigstatus (node->sig_list); > +#else > format->part_sigstatus (node->sig_validity); > +#endif > > format->part_content (part); > ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH] Add pseudo-compatibility with gmime 2.6 2012-01-17 3:47 ` Austin Clements @ 2012-01-17 10:48 ` Adrian Perez 2012-01-17 10:48 ` Thomas Jost 1 sibling, 0 replies; 42+ messages in thread From: Adrian Perez @ 2012-01-17 10:48 UTC (permalink / raw) To: notmuch [-- Attachment #1: Type: text/plain, Size: 17908 bytes --] Hi, Just a couple of comments inline :) On Mon, 16 Jan 2012 22:47:14 -0500, Austin Clements <amdragon@MIT.EDU> wrote: > Quoth Thomas Jost on Jan 17 at 12:56 am: > > There are lots of API changes in gmime 2.6 crypto handling. By adding > > preprocessor directives, it is however possible to add gmime 2.6 compatibility > > while preserving compatibility with gmime 2.4 too. > > Awesome. Comments inline below. > > > This is mostly based on id:"8762i8hrb9.fsf@bookbinder.fernseed.info". > > > > This was tested against both gmime 2.6.4 and 2.4.31. With gmime 2.4.31, the > > crypto tests all work fine (as expected). With gmime 2.6.4, one crypto test > > fails (signature verification with signer key unavailable) but this will be hard > > to fix since the new API does not report the reason why a signature verification > > fails (other than the human-readable error message). > > What is the result of this failing test? > > > --- > > mime-node.c | 50 ++++++++++++++++++++++++++- > > notmuch-client.h | 27 ++++++++++++++- > > notmuch-reply.c | 7 ++++ > > notmuch-show.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ > > show-message.c | 4 ++ > > 5 files changed, 181 insertions(+), 4 deletions(-) > > > > diff --git a/mime-node.c b/mime-node.c > > index d26bb44..ae2473d 100644 > > --- a/mime-node.c > > +++ b/mime-node.c > > @@ -33,7 +33,11 @@ typedef struct mime_node_context { > > GMimeMessage *mime_message; > > > > /* Context provided by the caller. */ > > +#ifdef GMIME_26 > > + GMimeCryptoContext *cryptoctx; > > +#else > > GMimeCipherContext *cryptoctx; > > +#endif > > notmuch_bool_t decrypt; > > } mime_node_context_t; > > > > @@ -57,8 +61,12 @@ _mime_node_context_free (mime_node_context_t *res) > > > > notmuch_status_t > > mime_node_open (const void *ctx, notmuch_message_t *message, > > - GMimeCipherContext *cryptoctx, notmuch_bool_t decrypt, > > - mime_node_t **root_out) > > +#ifdef GMIME_26 > > + GMimeCryptoContext *cryptoctx, > > +#else > > + GMimeCipherContext *cryptoctx, > > +#endif > > + notmuch_bool_t decrypt, mime_node_t **root_out) > > { > > const char *filename = notmuch_message_get_filename (message); > > mime_node_context_t *mctx; > > @@ -112,12 +120,21 @@ DONE: > > return status; > > } > > > > +#ifdef GMIME_26 > > +static int > > +_signature_list_free (GMimeSignatureList **proxy) > > +{ > > + g_object_unref (*proxy); > > + return 0; > > +} > > +#else > > static int > > _signature_validity_free (GMimeSignatureValidity **proxy) > > { > > g_mime_signature_validity_free (*proxy); > > return 0; > > } > > +#endif > > > > static mime_node_t * > > _mime_node_create (const mime_node_t *parent, GMimeObject *part) > > @@ -165,11 +182,23 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) > > GMimeMultipartEncrypted *encrypteddata = > > GMIME_MULTIPART_ENCRYPTED (part); > > node->decrypt_attempted = TRUE; > > +#ifdef GMIME_26 > > + GMimeDecryptResult *decrypt_result = g_mime_decrypt_result_new (); > > I think g_mime_multipart_encrypted_decrypt allocates the > GMimeDecryptResult for you, so this will just leak memory. Yes, this is a leak. The documentation says that the function is responsible of allocating the GMimeDecryptResult. The g_object_unref() is still needed in notmuch's code to free it, though. See http://git.gnome.org/browse/gmime/tree/gmime/gmime-multipart-encrypted.c#n282 > > + node->decrypted_child = g_mime_multipart_encrypted_decrypt > > + (encrypteddata, node->ctx->cryptoctx, &decrypt_result, &err); > > + if (node->decrypted_child) { > > + node->decrypt_success = node->verify_attempted = TRUE; > > + node->sig_list = g_mime_decrypt_result_get_signatures (decrypt_result); > > + if (!node->sig_list) > > + fprintf (stderr, "Failed to get signatures: %s\n", > > + (err ? err->message : "no error explanation given")); > > My understanding is that g_mime_decrypt_result_get_signatures returns > NULL if there are no signatures and that this isn't an error. This > differs from 2.4, which would return an empty but non-NULL list. > > Also, I believe you have to free the sig_list in both branches now, > which means the talloc_set_destructor can be moved to common logic > outside of the if decrypted/signed. > > > +#else > > node->decrypted_child = g_mime_multipart_encrypted_decrypt > > (encrypteddata, node->ctx->cryptoctx, &err); > > if (node->decrypted_child) { > > node->decrypt_success = node->verify_attempted = TRUE; > > node->sig_validity = g_mime_multipart_encrypted_get_signature_validity (encrypteddata); > > +#endif > > It's confusing to have the open braces in the #ifdef'd region with a > matching close brace outside of it (and I imagine this confuses > editors and uncrustify, too). You could either copy the else part in > both branches of the #ifdef or avoid duplicated code with something > like > > #ifdef GMIME_26 > .. node->decrypted_child = .. > #else > .. node->decrypted_child = .. > #endif > if (node->decrypted_child) { > node->decrypt_success = node->verify_attempted = TRUE; > #ifdef GMIME_26 > node->sig_list = .. > #else > node->sig_validity = .. > #endif > } else { > fprintf (stderr, ..); > } > > > } else { > > fprintf (stderr, "Failed to decrypt part: %s\n", > > (err ? err->message : "no error explanation given")); > > @@ -182,6 +211,18 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) > > "(must be exactly 2)\n", > > node->nchildren); > > } else { > > +#ifdef GMIME_26 > > + GMimeSignatureList *sig_list = g_mime_multipart_signed_verify > > + (GMIME_MULTIPART_SIGNED (part), node->ctx->cryptoctx, &err); > > + node->verify_attempted = TRUE; > > + node->sig_list = sig_list; > > + if (sig_list) { > > + GMimeSignatureList **proxy = > > + talloc (node, GMimeSignatureList *); > > + *proxy = sig_list; > > + talloc_set_destructor (proxy, _signature_list_free); > > + } > > +#else > > /* For some reason the GMimeSignatureValidity returned > > * here is not a const (inconsistent with that > > * returned by > > @@ -200,10 +241,15 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) > > *proxy = sig_validity; > > talloc_set_destructor (proxy, _signature_validity_free); > > } > > +#endif > > } > > } > > > > +#ifdef GMIME_26 > > + if (node->verify_attempted && !node->sig_list) > > Hmm. This is correct for signed parts, but will incorrectly trigger > for an encrypted part with no signatures. For 2.6, I think this error > checking may have to move into the branches of the if encrypted/signed > since for encrypted parts you have to check if > g_mime_multipart_encrypted_decrypt returned NULL. > > > +#else > > if (node->verify_attempted && !node->sig_validity) > > +#endif > > fprintf (stderr, "Failed to verify signed part: %s\n", > > (err ? err->message : "no error explanation given")); > > > > diff --git a/notmuch-client.h b/notmuch-client.h > > index 517c010..e85f882 100644 > > --- a/notmuch-client.h > > +++ b/notmuch-client.h > > @@ -30,6 +30,12 @@ > > > > #include <gmime/gmime.h> > > > > +/* GMIME_CHECK_VERSION is only available in gmime >= 2.5. But so are > > + * GMIME_MAJOR_VERSION and friends. */ > > Hah. > > > +#ifdef GMIME_MAJOR_VERSION > > +#define GMIME_26 > > +#endif > > + > > #include "notmuch.h" > > > > /* This is separate from notmuch-private.h because we're trying to > > @@ -69,7 +75,11 @@ typedef struct notmuch_show_format { > > void (*part_start) (GMimeObject *part, > > int *part_count); > > void (*part_encstatus) (int status); > > +#ifdef GMIME_26 > > + void (*part_sigstatus) (GMimeSignatureList* siglist); > > +#else > > void (*part_sigstatus) (const GMimeSignatureValidity* validity); > > +#endif > > void (*part_content) (GMimeObject *part); > > void (*part_end) (GMimeObject *part); > > const char *part_sep; > > @@ -83,7 +93,11 @@ typedef struct notmuch_show_params { > > int entire_thread; > > int raw; > > int part; > > +#ifdef GMIME_26 > > + GMimeCryptoContext* cryptoctx; > > +#else > > GMimeCipherContext* cryptoctx; > > +#endif > > int decrypt; > > } notmuch_show_params_t; > > > > @@ -286,7 +300,12 @@ typedef struct mime_node { > > * signature. May be NULL if signature verification failed. If > > * there are simply no signatures, this will be non-NULL with an > > * empty signers list. */ > > +#ifdef GMIME_26 > > + /* TODO: update the above comment... */ > > Since this behaves very differently in 2.6, I think documenting it is > important (and being very careful about the differences). Maybe > > /* The list of signatures for signed or encrypted containers. If > * there are no signatures, this will be NULL. */ > > > + GMimeSignatureList* sig_list; > > +#else > > const GMimeSignatureValidity *sig_validity; > > +#endif > > > > /* Internal: Context inherited from the root iterator. */ > > struct mime_node_context *ctx; > > @@ -311,8 +330,12 @@ typedef struct mime_node { > > */ > > notmuch_status_t > > mime_node_open (const void *ctx, notmuch_message_t *message, > > - GMimeCipherContext *cryptoctx, notmuch_bool_t decrypt, > > - mime_node_t **node_out); > > +#ifdef GMIME_26 > > + GMimeCryptoContext *cryptoctx, > > +#else > > + GMimeCipherContext *cryptoctx, > > +#endif > > + notmuch_bool_t decrypt, mime_node_t **node_out); > > > > /* Return a new MIME node for the requested child part of parent. > > * parent will be used as the talloc context for the returned child > > diff --git a/notmuch-reply.c b/notmuch-reply.c > > index da3acce..dc37c51 100644 > > --- a/notmuch-reply.c > > +++ b/notmuch-reply.c > > @@ -688,15 +688,22 @@ notmuch_reply_command (void *ctx, int argc, char *argv[]) > > reply_format_func = notmuch_reply_format_default; > > > > if (decrypt) { > > +#ifdef GMIME_26 > > + /* TODO: GMimePasswordRequestFunc */ > > + params.cryptoctx = g_mime_gpg_context_new (NULL, "gpg"); > > +#else > > GMimeSession* session = g_object_new (g_mime_session_get_type(), NULL); > > params.cryptoctx = g_mime_gpg_context_new (session, "gpg"); > > +#endif > > if (params.cryptoctx) { > > g_mime_gpg_context_set_always_trust ((GMimeGpgContext*) params.cryptoctx, FALSE); > > params.decrypt = TRUE; > > } else { > > fprintf (stderr, "Failed to construct gpg context.\n"); > > } > > +#ifndef GMIME_26 > > g_object_unref (session); > > +#endif > > } > > > > config = notmuch_config_open (ctx, NULL, NULL); > > diff --git a/notmuch-show.c b/notmuch-show.c > > index d14dac9..263ab72 100644 > > --- a/notmuch-show.c > > +++ b/notmuch-show.c > > @@ -76,7 +76,11 @@ static void > > format_part_encstatus_json (int status); > > > > static void > > +#ifdef GMIME_26 > > +format_part_sigstatus_json (GMimeSignatureList* siglist); > > +#else > > format_part_sigstatus_json (const GMimeSignatureValidity* validity); > > +#endif > > > > static void > > format_part_content_json (GMimeObject *part); > > @@ -486,6 +490,21 @@ show_text_part_content (GMimeObject *part, GMimeStream *stream_out) > > g_object_unref(stream_filter); > > } > > > > +#ifdef GMIME_26 > > +static const char* > > +signature_status_to_string (GMimeSignatureStatus x) > > +{ > > + switch (x) { > > + case GMIME_SIGNATURE_STATUS_GOOD: > > + return "good"; > > + case GMIME_SIGNATURE_STATUS_BAD: > > + return "bad"; > > + case GMIME_SIGNATURE_STATUS_ERROR: > > + return "error"; > > + } > > + return "unknown"; > > +} > > +#else > > static const char* > > signer_status_to_string (GMimeSignerStatus x) > > { > > @@ -501,6 +520,7 @@ signer_status_to_string (GMimeSignerStatus x) > > } > > return "unknown"; > > } > > +#endif > > > > static void > > format_part_start_text (GMimeObject *part, int *part_count) > > @@ -592,6 +612,75 @@ format_part_encstatus_json (int status) > > printf ("}]"); > > } > > > > +#ifdef GMIME_26 > > +static void > > +format_part_sigstatus_json (GMimeSignatureList *siglist) > > +{ > > + printf (", \"sigstatus\": ["); > > + > > + if (!siglist) { > > + printf ("]"); > > + return; > > + } > > + > > + void *ctx_quote = talloc_new (NULL); > > + int i; > > + for (i = 0; i < g_mime_signature_list_length (siglist); ++i) { > > Style nit: notmuch uses i++. > > > + GMimeSignature *signature = g_mime_signature_list_get_signature (siglist, i); > > + > > + if (i > 0) > > + printf (", "); > > + > > + printf ("{"); > > + > > + /* status */ > > + GMimeSignatureStatus status = g_mime_signature_get_status (signature); > > + printf ("\"status\": %s", > > + json_quote_str (ctx_quote, > > + signature_status_to_string (status))); > > + > > + GMimeCertificate *certificate = g_mime_signature_get_certificate (signature); > > + if (status == GMIME_SIGNATURE_STATUS_GOOD) > > + { > > Style nit: break after brace. > > (Presumably this is copied from the existing > format_part_sigstatus_json, but since it's technically new code > there's no reason not to fix these up.) > > > + if (certificate) > > + printf (", \"fingerprint\": %s", json_quote_str (ctx_quote, g_mime_certificate_get_fingerprint (certificate))); > > + /* these dates are seconds since the epoch; should we > > + * provide a more human-readable format string? */ > > + time_t created = g_mime_signature_get_created (signature); > > + if (created != -1) > > + printf (", \"created\": %d", (int) created); > > + time_t expires = g_mime_signature_get_expires (signature); > > + if (expires > 0) > > + printf (", \"expires\": %d", (int) expires); > > Is it intentional that the two above checks are different? I would > think the second should be expires != -1. Yes, it should check for “expires != -1”. Also, wouldn't it be needed to cast do “expires != (time_t) -1” to avoid compiler warnings? (time_t may be different from int.) > > + /* output user id only if validity is FULL or ULTIMATE. */ > > + /* note that gmime is using the term "trust" here, which > > + * is WRONG. It's actually user id "validity". */ > > + if (certificate) > > + { > > Break after brace. > > > + const char *name = g_mime_certificate_get_name (certificate); > > + GMimeCertificateTrust trust = g_mime_certificate_get_trust (certificate); > > + if (name && (trust == GMIME_CERTIFICATE_TRUST_FULLY || trust == GMIME_CERTIFICATE_TRUST_ULTIMATE)) > > + printf (", \"userid\": %s", json_quote_str (ctx_quote, name)); > > + } > > + } else if (certificate) { > > + const char *key_id = g_mime_certificate_get_key_id (certificate); > > + if (key_id) > > + printf (", \"keyid\": %s", json_quote_str (ctx_quote, key_id)); > > + } > > + > > + GMimeSignatureError errors = g_mime_signature_get_errors (signature); > > + if (errors != GMIME_SIGNATURE_ERROR_NONE) { > > + printf (", \"errors\": %x", errors); > > This should be %d (I would say 0x%x, but JSON doesn't support hex > literals). I see this bug came from the original > format_part_sigstatus_json. Maybe there should be a quick patch > before this one that fixes the source of the bug? > > > + } > > + > > + printf ("}"); > > + } > > + > > + printf ("]"); > > + > > + talloc_free (ctx_quote); > > +} > > +#else > > static void > > format_part_sigstatus_json (const GMimeSignatureValidity* validity) > > { > > @@ -652,6 +741,7 @@ format_part_sigstatus_json (const GMimeSignatureValidity* validity) > > > > talloc_free (ctx_quote); > > } > > +#endif > > > > static void > > format_part_content_json (GMimeObject *part) > > @@ -990,13 +1080,20 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[])) > > } else if ((STRNCMP_LITERAL (argv[i], "--verify") == 0) || > > (STRNCMP_LITERAL (argv[i], "--decrypt") == 0)) { > > if (params.cryptoctx == NULL) { > > +#ifdef GMIME_26 > > + /* TODO: GMimePasswordRequestFunc */ > > + if (NULL == (params.cryptoctx = g_mime_gpg_context_new(NULL, "gpg"))) > > +#else > > GMimeSession* session = g_object_new(g_mime_session_get_type(), NULL); > > if (NULL == (params.cryptoctx = g_mime_gpg_context_new(session, "gpg"))) > > +#endif > > fprintf (stderr, "Failed to construct gpg context.\n"); > > else > > g_mime_gpg_context_set_always_trust((GMimeGpgContext*)params.cryptoctx, FALSE); > > +#ifndef GMIME_26 > > g_object_unref (session); > > session = NULL; > > +#endif > > } > > if (STRNCMP_LITERAL (argv[i], "--decrypt") == 0) > > params.decrypt = 1; > > diff --git a/show-message.c b/show-message.c > > index 8768889..65269fd 100644 > > --- a/show-message.c > > +++ b/show-message.c > > @@ -48,7 +48,11 @@ show_message_part (mime_node_t *node, > > format->part_encstatus (node->decrypt_success); > > > > if (node->verify_attempted && format->part_sigstatus) > > +#ifdef GMIME_26 > > + format->part_sigstatus (node->sig_list); > > +#else > > format->part_sigstatus (node->sig_validity); > > +#endif > > > > format->part_content (part); > > -- Adrian Perez <aperez@igalia.com> - Sent from my toaster Igalia - Free Software Engineering [-- Attachment #2: Type: application/pgp-signature, Size: 197 bytes --] ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH] Add pseudo-compatibility with gmime 2.6 2012-01-17 3:47 ` Austin Clements 2012-01-17 10:48 ` Adrian Perez @ 2012-01-17 10:48 ` Thomas Jost 2012-01-17 10:50 ` [PATCH v2 1/2] show: don't use hex literals in JSON output Thomas Jost 2012-01-17 20:38 ` [PATCH] " Austin Clements 1 sibling, 2 replies; 42+ messages in thread From: Thomas Jost @ 2012-01-17 10:48 UTC (permalink / raw) To: Austin Clements; +Cc: notmuch [-- Attachment #1: Type: text/plain, Size: 19831 bytes --] On Mon, 16 Jan 2012 22:47:14 -0500, Austin Clements <amdragon@MIT.EDU> wrote: > Quoth Thomas Jost on Jan 17 at 12:56 am: > > There are lots of API changes in gmime 2.6 crypto handling. By adding > > preprocessor directives, it is however possible to add gmime 2.6 compatibility > > while preserving compatibility with gmime 2.4 too. > > Awesome. Comments inline below. Thanks for reviewing this so quickly. Replies inline, and new patches incoming soon. > > This is mostly based on id:"8762i8hrb9.fsf@bookbinder.fernseed.info". > > > > This was tested against both gmime 2.6.4 and 2.4.31. With gmime 2.4.31, the > > crypto tests all work fine (as expected). With gmime 2.6.4, one crypto test > > fails (signature verification with signer key unavailable) but this will be hard > > to fix since the new API does not report the reason why a signature verification > > fails (other than the human-readable error message). > > What is the result of this failing test? The test looks like that: FAIL signature verification with signer key unavailable --- crypto.4.expected 2012-01-16 23:05:06.765651183 +0000 +++ crypto.4.output 2012-01-16 23:05:06.765651183 +0000 @@ -12,9 +12,7 @@ "Bcc": "", "Date": "01 Jan 2000 12:00:00 -0000"}, "body": [{"id": 1, - "sigstatus": [{"status": "error", - "keyid": "6D92612D94E46381", - "errors": 2}], + "sigstatus": [], "content-type": "multipart/signed", "content": [{"id": 2, "content-type": "text/plain", Failed to verify signed part: gpg: keyblock resource `/home/schnouki/dev/notmuch/test/tmp.crypto/gnupg/pubring.gpg': file open error gpg: armor header: Version: GnuPG v1.4.11 (GNU/Linux) gpg: Signature made Mon Jan 16 23:05:06 2012 UTC using RSA key ID 94E46381 gpg: Can't check signature: public key not found So basically if a signer public key is missing, we can't get the status signature: empty "sigstatus" field in the JSON output, "Unknown signature status" in Emacs. IMHO this is a bug in gmime 2.6, and I think I know what causes it. I'll file a bug in gmime and hopefully they'll find a cleaner way to fix it than the one I came up with :) > > > --- > > mime-node.c | 50 ++++++++++++++++++++++++++- > > notmuch-client.h | 27 ++++++++++++++- > > notmuch-reply.c | 7 ++++ > > notmuch-show.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ > > show-message.c | 4 ++ > > 5 files changed, 181 insertions(+), 4 deletions(-) > > > > diff --git a/mime-node.c b/mime-node.c > > index d26bb44..ae2473d 100644 > > --- a/mime-node.c > > +++ b/mime-node.c > > @@ -33,7 +33,11 @@ typedef struct mime_node_context { > > GMimeMessage *mime_message; > > > > /* Context provided by the caller. */ > > +#ifdef GMIME_26 > > + GMimeCryptoContext *cryptoctx; > > +#else > > GMimeCipherContext *cryptoctx; > > +#endif > > notmuch_bool_t decrypt; > > } mime_node_context_t; > > > > @@ -57,8 +61,12 @@ _mime_node_context_free (mime_node_context_t *res) > > > > notmuch_status_t > > mime_node_open (const void *ctx, notmuch_message_t *message, > > - GMimeCipherContext *cryptoctx, notmuch_bool_t decrypt, > > - mime_node_t **root_out) > > +#ifdef GMIME_26 > > + GMimeCryptoContext *cryptoctx, > > +#else > > + GMimeCipherContext *cryptoctx, > > +#endif > > + notmuch_bool_t decrypt, mime_node_t **root_out) > > { > > const char *filename = notmuch_message_get_filename (message); > > mime_node_context_t *mctx; > > @@ -112,12 +120,21 @@ DONE: > > return status; > > } > > > > +#ifdef GMIME_26 > > +static int > > +_signature_list_free (GMimeSignatureList **proxy) > > +{ > > + g_object_unref (*proxy); > > + return 0; > > +} > > +#else > > static int > > _signature_validity_free (GMimeSignatureValidity **proxy) > > { > > g_mime_signature_validity_free (*proxy); > > return 0; > > } > > +#endif > > > > static mime_node_t * > > _mime_node_create (const mime_node_t *parent, GMimeObject *part) > > @@ -165,11 +182,23 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) > > GMimeMultipartEncrypted *encrypteddata = > > GMIME_MULTIPART_ENCRYPTED (part); > > node->decrypt_attempted = TRUE; > > +#ifdef GMIME_26 > > + GMimeDecryptResult *decrypt_result = g_mime_decrypt_result_new (); > > I think g_mime_multipart_encrypted_decrypt allocates the > GMimeDecryptResult for you, so this will just leak memory. You're right, fixed. Will it be freed along with node->decrypted_child, or do we need to handle this ourself? > > > + node->decrypted_child = g_mime_multipart_encrypted_decrypt > > + (encrypteddata, node->ctx->cryptoctx, &decrypt_result, &err); > > + if (node->decrypted_child) { > > + node->decrypt_success = node->verify_attempted = TRUE; > > + node->sig_list = g_mime_decrypt_result_get_signatures (decrypt_result); > > + if (!node->sig_list) > > + fprintf (stderr, "Failed to get signatures: %s\n", > > + (err ? err->message : "no error explanation given")); > > My understanding is that g_mime_decrypt_result_get_signatures returns > NULL if there are no signatures and that this isn't an error. This > differs from 2.4, which would return an empty but non-NULL list. > > Also, I believe you have to free the sig_list in both branches now, > which means the talloc_set_destructor can be moved to common logic > outside of the if decrypted/signed. Hmmm, you're right about g_mime_decrypt_result_get_signatures. I should have RTFM. Also moved talloc_set_destructor outside the if/else, thanks. > > > +#else > > node->decrypted_child = g_mime_multipart_encrypted_decrypt > > (encrypteddata, node->ctx->cryptoctx, &err); > > if (node->decrypted_child) { > > node->decrypt_success = node->verify_attempted = TRUE; > > node->sig_validity = g_mime_multipart_encrypted_get_signature_validity (encrypteddata); > > +#endif > > It's confusing to have the open braces in the #ifdef'd region with a > matching close brace outside of it (and I imagine this confuses > editors and uncrustify, too). You could either copy the else part in > both branches of the #ifdef or avoid duplicated code with something > like > > #ifdef GMIME_26 > .. node->decrypted_child = .. > #else > .. node->decrypted_child = .. > #endif > if (node->decrypted_child) { > node->decrypt_success = node->verify_attempted = TRUE; > #ifdef GMIME_26 > node->sig_list = .. > #else > node->sig_validity = .. > #endif > } else { > fprintf (stderr, ..); > } Right. Avoids duplication and makes it easier to read. > > > } else { > > fprintf (stderr, "Failed to decrypt part: %s\n", > > (err ? err->message : "no error explanation given")); > > @@ -182,6 +211,18 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) > > "(must be exactly 2)\n", > > node->nchildren); > > } else { > > +#ifdef GMIME_26 > > + GMimeSignatureList *sig_list = g_mime_multipart_signed_verify > > + (GMIME_MULTIPART_SIGNED (part), node->ctx->cryptoctx, &err); > > + node->verify_attempted = TRUE; > > + node->sig_list = sig_list; > > + if (sig_list) { > > + GMimeSignatureList **proxy = > > + talloc (node, GMimeSignatureList *); > > + *proxy = sig_list; > > + talloc_set_destructor (proxy, _signature_list_free); > > + } > > +#else > > /* For some reason the GMimeSignatureValidity returned > > * here is not a const (inconsistent with that > > * returned by > > @@ -200,10 +241,15 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) > > *proxy = sig_validity; > > talloc_set_destructor (proxy, _signature_validity_free); > > } > > +#endif > > } > > } > > > > +#ifdef GMIME_26 > > + if (node->verify_attempted && !node->sig_list) > > Hmm. This is correct for signed parts, but will incorrectly trigger > for an encrypted part with no signatures. For 2.6, I think this error > checking may have to move into the branches of the if encrypted/signed > since for encrypted parts you have to check if > g_mime_multipart_encrypted_decrypt returned NULL. That sound right. The weird part is that it did not cause anything to fail in the test suite... Anyway, for 2.6 I moved this test into the "if signed" branch since it's perfectly acceptable to have sig_list == NULL for an encrypted part. > > > +#else > > if (node->verify_attempted && !node->sig_validity) > > +#endif > > fprintf (stderr, "Failed to verify signed part: %s\n", > > (err ? err->message : "no error explanation given")); > > > > diff --git a/notmuch-client.h b/notmuch-client.h > > index 517c010..e85f882 100644 > > --- a/notmuch-client.h > > +++ b/notmuch-client.h > > @@ -30,6 +30,12 @@ > > > > #include <gmime/gmime.h> > > > > +/* GMIME_CHECK_VERSION is only available in gmime >= 2.5. But so are > > + * GMIME_MAJOR_VERSION and friends. */ > > Hah. > > > +#ifdef GMIME_MAJOR_VERSION > > +#define GMIME_26 > > +#endif > > + > > #include "notmuch.h" > > > > /* This is separate from notmuch-private.h because we're trying to > > @@ -69,7 +75,11 @@ typedef struct notmuch_show_format { > > void (*part_start) (GMimeObject *part, > > int *part_count); > > void (*part_encstatus) (int status); > > +#ifdef GMIME_26 > > + void (*part_sigstatus) (GMimeSignatureList* siglist); > > +#else > > void (*part_sigstatus) (const GMimeSignatureValidity* validity); > > +#endif > > void (*part_content) (GMimeObject *part); > > void (*part_end) (GMimeObject *part); > > const char *part_sep; > > @@ -83,7 +93,11 @@ typedef struct notmuch_show_params { > > int entire_thread; > > int raw; > > int part; > > +#ifdef GMIME_26 > > + GMimeCryptoContext* cryptoctx; > > +#else > > GMimeCipherContext* cryptoctx; > > +#endif > > int decrypt; > > } notmuch_show_params_t; > > > > @@ -286,7 +300,12 @@ typedef struct mime_node { > > * signature. May be NULL if signature verification failed. If > > * there are simply no signatures, this will be non-NULL with an > > * empty signers list. */ > > +#ifdef GMIME_26 > > + /* TODO: update the above comment... */ > > Since this behaves very differently in 2.6, I think documenting it is > important (and being very careful about the differences). Maybe > > /* The list of signatures for signed or encrypted containers. If > * there are no signatures, this will be NULL. */ Added, thanks. > > > + GMimeSignatureList* sig_list; > > +#else > > const GMimeSignatureValidity *sig_validity; > > +#endif > > > > /* Internal: Context inherited from the root iterator. */ > > struct mime_node_context *ctx; > > @@ -311,8 +330,12 @@ typedef struct mime_node { > > */ > > notmuch_status_t > > mime_node_open (const void *ctx, notmuch_message_t *message, > > - GMimeCipherContext *cryptoctx, notmuch_bool_t decrypt, > > - mime_node_t **node_out); > > +#ifdef GMIME_26 > > + GMimeCryptoContext *cryptoctx, > > +#else > > + GMimeCipherContext *cryptoctx, > > +#endif > > + notmuch_bool_t decrypt, mime_node_t **node_out); > > > > /* Return a new MIME node for the requested child part of parent. > > * parent will be used as the talloc context for the returned child > > diff --git a/notmuch-reply.c b/notmuch-reply.c > > index da3acce..dc37c51 100644 > > --- a/notmuch-reply.c > > +++ b/notmuch-reply.c > > @@ -688,15 +688,22 @@ notmuch_reply_command (void *ctx, int argc, char *argv[]) > > reply_format_func = notmuch_reply_format_default; > > > > if (decrypt) { > > +#ifdef GMIME_26 > > + /* TODO: GMimePasswordRequestFunc */ > > + params.cryptoctx = g_mime_gpg_context_new (NULL, "gpg"); > > +#else > > GMimeSession* session = g_object_new (g_mime_session_get_type(), NULL); > > params.cryptoctx = g_mime_gpg_context_new (session, "gpg"); > > +#endif > > if (params.cryptoctx) { > > g_mime_gpg_context_set_always_trust ((GMimeGpgContext*) params.cryptoctx, FALSE); > > params.decrypt = TRUE; > > } else { > > fprintf (stderr, "Failed to construct gpg context.\n"); > > } > > +#ifndef GMIME_26 > > g_object_unref (session); > > +#endif > > } > > > > config = notmuch_config_open (ctx, NULL, NULL); > > diff --git a/notmuch-show.c b/notmuch-show.c > > index d14dac9..263ab72 100644 > > --- a/notmuch-show.c > > +++ b/notmuch-show.c > > @@ -76,7 +76,11 @@ static void > > format_part_encstatus_json (int status); > > > > static void > > +#ifdef GMIME_26 > > +format_part_sigstatus_json (GMimeSignatureList* siglist); > > +#else > > format_part_sigstatus_json (const GMimeSignatureValidity* validity); > > +#endif > > > > static void > > format_part_content_json (GMimeObject *part); > > @@ -486,6 +490,21 @@ show_text_part_content (GMimeObject *part, GMimeStream *stream_out) > > g_object_unref(stream_filter); > > } > > > > +#ifdef GMIME_26 > > +static const char* > > +signature_status_to_string (GMimeSignatureStatus x) > > +{ > > + switch (x) { > > + case GMIME_SIGNATURE_STATUS_GOOD: > > + return "good"; > > + case GMIME_SIGNATURE_STATUS_BAD: > > + return "bad"; > > + case GMIME_SIGNATURE_STATUS_ERROR: > > + return "error"; > > + } > > + return "unknown"; > > +} > > +#else > > static const char* > > signer_status_to_string (GMimeSignerStatus x) > > { > > @@ -501,6 +520,7 @@ signer_status_to_string (GMimeSignerStatus x) > > } > > return "unknown"; > > } > > +#endif > > > > static void > > format_part_start_text (GMimeObject *part, int *part_count) > > @@ -592,6 +612,75 @@ format_part_encstatus_json (int status) > > printf ("}]"); > > } > > > > +#ifdef GMIME_26 > > +static void > > +format_part_sigstatus_json (GMimeSignatureList *siglist) > > +{ > > + printf (", \"sigstatus\": ["); > > + > > + if (!siglist) { > > + printf ("]"); > > + return; > > + } > > + > > + void *ctx_quote = talloc_new (NULL); > > + int i; > > + for (i = 0; i < g_mime_signature_list_length (siglist); ++i) { > > Style nit: notmuch uses i++. Ack. > > > + GMimeSignature *signature = g_mime_signature_list_get_signature (siglist, i); > > + > > + if (i > 0) > > + printf (", "); > > + > > + printf ("{"); > > + > > + /* status */ > > + GMimeSignatureStatus status = g_mime_signature_get_status (signature); > > + printf ("\"status\": %s", > > + json_quote_str (ctx_quote, > > + signature_status_to_string (status))); > > + > > + GMimeCertificate *certificate = g_mime_signature_get_certificate (signature); > > + if (status == GMIME_SIGNATURE_STATUS_GOOD) > > + { > > Style nit: break after brace. > > (Presumably this is copied from the existing > format_part_sigstatus_json, but since it's technically new code > there's no reason not to fix these up.) Ack too. (Copied from the original patch, which was probably copied from the existing code...) > > > + if (certificate) > > + printf (", \"fingerprint\": %s", json_quote_str (ctx_quote, g_mime_certificate_get_fingerprint (certificate))); > > + /* these dates are seconds since the epoch; should we > > + * provide a more human-readable format string? */ > > + time_t created = g_mime_signature_get_created (signature); > > + if (created != -1) > > + printf (", \"created\": %d", (int) created); > > + time_t expires = g_mime_signature_get_expires (signature); > > + if (expires > 0) > > + printf (", \"expires\": %d", (int) expires); > > Is it intentional that the two above checks are different? I would > think the second should be expires != -1. It was so in the original patch, which caused one of the tests to fail. -1 means "unknown", and AFAICT 0 means "never expires". The current behaviour is to add the "expires" field only if there is an expiration date, so we need to check if expires > 0. > > > + /* output user id only if validity is FULL or ULTIMATE. */ > > + /* note that gmime is using the term "trust" here, which > > + * is WRONG. It's actually user id "validity". */ > > + if (certificate) > > + { > > Break after brace. > > > + const char *name = g_mime_certificate_get_name (certificate); > > + GMimeCertificateTrust trust = g_mime_certificate_get_trust (certificate); > > + if (name && (trust == GMIME_CERTIFICATE_TRUST_FULLY || trust == GMIME_CERTIFICATE_TRUST_ULTIMATE)) > > + printf (", \"userid\": %s", json_quote_str (ctx_quote, name)); > > + } > > + } else if (certificate) { > > + const char *key_id = g_mime_certificate_get_key_id (certificate); > > + if (key_id) > > + printf (", \"keyid\": %s", json_quote_str (ctx_quote, key_id)); > > + } > > + > > + GMimeSignatureError errors = g_mime_signature_get_errors (signature); > > + if (errors != GMIME_SIGNATURE_ERROR_NONE) { > > + printf (", \"errors\": %x", errors); > > This should be %d (I would say 0x%x, but JSON doesn't support hex > literals). I see this bug came from the original > format_part_sigstatus_json. Maybe there should be a quick patch > before this one that fixes the source of the bug? Right. I'll add a first patch to change this in the original format_part_sigstatus_json. > > > + } > > + > > + printf ("}"); > > + } > > + > > + printf ("]"); > > + > > + talloc_free (ctx_quote); > > +} > > +#else > > static void > > format_part_sigstatus_json (const GMimeSignatureValidity* validity) > > { > > @@ -652,6 +741,7 @@ format_part_sigstatus_json (const GMimeSignatureValidity* validity) > > > > talloc_free (ctx_quote); > > } > > +#endif > > > > static void > > format_part_content_json (GMimeObject *part) > > @@ -990,13 +1080,20 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[])) > > } else if ((STRNCMP_LITERAL (argv[i], "--verify") == 0) || > > (STRNCMP_LITERAL (argv[i], "--decrypt") == 0)) { > > if (params.cryptoctx == NULL) { > > +#ifdef GMIME_26 > > + /* TODO: GMimePasswordRequestFunc */ > > + if (NULL == (params.cryptoctx = g_mime_gpg_context_new(NULL, "gpg"))) > > +#else > > GMimeSession* session = g_object_new(g_mime_session_get_type(), NULL); > > if (NULL == (params.cryptoctx = g_mime_gpg_context_new(session, "gpg"))) > > +#endif > > fprintf (stderr, "Failed to construct gpg context.\n"); > > else > > g_mime_gpg_context_set_always_trust((GMimeGpgContext*)params.cryptoctx, FALSE); > > +#ifndef GMIME_26 > > g_object_unref (session); > > session = NULL; > > +#endif > > } > > if (STRNCMP_LITERAL (argv[i], "--decrypt") == 0) > > params.decrypt = 1; > > diff --git a/show-message.c b/show-message.c > > index 8768889..65269fd 100644 > > --- a/show-message.c > > +++ b/show-message.c > > @@ -48,7 +48,11 @@ show_message_part (mime_node_t *node, > > format->part_encstatus (node->decrypt_success); > > > > if (node->verify_attempted && format->part_sigstatus) > > +#ifdef GMIME_26 > > + format->part_sigstatus (node->sig_list); > > +#else > > format->part_sigstatus (node->sig_validity); > > +#endif > > > > format->part_content (part); > > -- Thomas/Schnouki [-- Attachment #2: Type: application/pgp-signature, Size: 489 bytes --] ^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH v2 1/2] show: don't use hex literals in JSON output 2012-01-17 10:48 ` Thomas Jost @ 2012-01-17 10:50 ` Thomas Jost 2012-01-17 10:50 ` [PATCH v2 2/2] Add pseudo-compatibility with gmime 2.6 Thomas Jost 2012-01-17 20:38 ` [PATCH] " Austin Clements 1 sibling, 1 reply; 42+ messages in thread From: Thomas Jost @ 2012-01-17 10:50 UTC (permalink / raw) To: notmuch JSON does not support hex literals (0x..) so numbers must be formatted as %d instead of %x. --- notmuch-show.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/notmuch-show.c b/notmuch-show.c index d14dac9..91f566c 100644 --- a/notmuch-show.c +++ b/notmuch-show.c @@ -641,7 +641,7 @@ format_part_sigstatus_json (const GMimeSignatureValidity* validity) printf (", \"keyid\": %s", json_quote_str (ctx_quote, signer->keyid)); } if (signer->errors != GMIME_SIGNER_ERROR_NONE) { - printf (", \"errors\": %x", signer->errors); + printf (", \"errors\": %d", signer->errors); } printf ("}"); -- 1.7.8.3 ^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH v2 2/2] Add pseudo-compatibility with gmime 2.6 2012-01-17 10:50 ` [PATCH v2 1/2] show: don't use hex literals in JSON output Thomas Jost @ 2012-01-17 10:50 ` Thomas Jost 2012-01-17 12:48 ` Tomi Ollila ` (2 more replies) 0 siblings, 3 replies; 42+ messages in thread From: Thomas Jost @ 2012-01-17 10:50 UTC (permalink / raw) To: notmuch There are lots of API changes in gmime 2.6 crypto handling. By adding preprocessor directives, it is however possible to add gmime 2.6 compatibility while preserving compatibility with gmime 2.4 too. This is mostly based on id:"8762i8hrb9.fsf@bookbinder.fernseed.info". This was tested against both gmime 2.6.4 and 2.4.31. With gmime 2.4.31, the crypto tests all work fine (as expected). With gmime 2.6.4, one crypto test fails (signature verification with signer key unavailable) but this will be hard to fix since the new API does not report the reason why a signature verification fails (other than the human-readable error message). --- mime-node.c | 56 ++++++++++++++++++++++++++++++-- notmuch-client.h | 28 +++++++++++++++- notmuch-reply.c | 7 ++++ notmuch-show.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ show-message.c | 4 ++ 5 files changed, 185 insertions(+), 5 deletions(-) diff --git a/mime-node.c b/mime-node.c index d26bb44..e575e1c 100644 --- a/mime-node.c +++ b/mime-node.c @@ -33,7 +33,11 @@ typedef struct mime_node_context { GMimeMessage *mime_message; /* Context provided by the caller. */ +#ifdef GMIME_26 + GMimeCryptoContext *cryptoctx; +#else GMimeCipherContext *cryptoctx; +#endif notmuch_bool_t decrypt; } mime_node_context_t; @@ -57,8 +61,12 @@ _mime_node_context_free (mime_node_context_t *res) notmuch_status_t mime_node_open (const void *ctx, notmuch_message_t *message, - GMimeCipherContext *cryptoctx, notmuch_bool_t decrypt, - mime_node_t **root_out) +#ifdef GMIME_26 + GMimeCryptoContext *cryptoctx, +#else + GMimeCipherContext *cryptoctx, +#endif + notmuch_bool_t decrypt, mime_node_t **root_out) { const char *filename = notmuch_message_get_filename (message); mime_node_context_t *mctx; @@ -112,12 +120,21 @@ DONE: return status; } +#ifdef GMIME_26 +static int +_signature_list_free (GMimeSignatureList **proxy) +{ + g_object_unref (*proxy); + return 0; +} +#else static int _signature_validity_free (GMimeSignatureValidity **proxy) { g_mime_signature_validity_free (*proxy); return 0; } +#endif static mime_node_t * _mime_node_create (const mime_node_t *parent, GMimeObject *part) @@ -165,11 +182,22 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) GMimeMultipartEncrypted *encrypteddata = GMIME_MULTIPART_ENCRYPTED (part); node->decrypt_attempted = TRUE; +#ifdef GMIME_26 + GMimeDecryptResult *decrypt_result = NULL; + node->decrypted_child = g_mime_multipart_encrypted_decrypt + (encrypteddata, node->ctx->cryptoctx, &decrypt_result, &err); +#else node->decrypted_child = g_mime_multipart_encrypted_decrypt (encrypteddata, node->ctx->cryptoctx, &err); +#endif if (node->decrypted_child) { - node->decrypt_success = node->verify_attempted = TRUE; + node->decrypt_success = node->verify_attempted =TRUE; +#ifdef GMIME_26 + /* This may be NULL if the part is not signed. */ + node->sig_list = g_mime_decrypt_result_get_signatures (decrypt_result); +#else node->sig_validity = g_mime_multipart_encrypted_get_signature_validity (encrypteddata); +#endif } else { fprintf (stderr, "Failed to decrypt part: %s\n", (err ? err->message : "no error explanation given")); @@ -182,6 +210,16 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) "(must be exactly 2)\n", node->nchildren); } else { +#ifdef GMIME_26 + GMimeSignatureList *sig_list = g_mime_multipart_signed_verify + (GMIME_MULTIPART_SIGNED (part), node->ctx->cryptoctx, &err); + node->verify_attempted = TRUE; + node->sig_list = sig_list; + + if (!sig_list) + fprintf (stderr, "Failed to verify signed part: %s\n", + (err ? err->message : "no error explanation given")); +#else /* For some reason the GMimeSignatureValidity returned * here is not a const (inconsistent with that * returned by @@ -200,12 +238,24 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) *proxy = sig_validity; talloc_set_destructor (proxy, _signature_validity_free); } +#endif } } +#ifdef GMIME_26 + /* sig_list may be created in both above cases, so we need to + * cleanly handle it here. */ + if (node->sig_list) { + GMimeSignatureList **proxy = + talloc (node, GMimeSignatureList *); + *proxy = node->sig_list; + talloc_set_destructor (proxy, _signature_list_free); + } +#else if (node->verify_attempted && !node->sig_validity) fprintf (stderr, "Failed to verify signed part: %s\n", (err ? err->message : "no error explanation given")); +#endif if (err) g_error_free (err); diff --git a/notmuch-client.h b/notmuch-client.h index 62ede28..9167042 100644 --- a/notmuch-client.h +++ b/notmuch-client.h @@ -30,6 +30,12 @@ #include <gmime/gmime.h> +/* GMIME_CHECK_VERSION is only available in gmime >= 2.5. But so are + * GMIME_MAJOR_VERSION and friends. */ +#ifdef GMIME_MAJOR_VERSION +#define GMIME_26 +#endif + #include "notmuch.h" /* This is separate from notmuch-private.h because we're trying to @@ -69,7 +75,11 @@ typedef struct notmuch_show_format { void (*part_start) (GMimeObject *part, int *part_count); void (*part_encstatus) (int status); +#ifdef GMIME_26 + void (*part_sigstatus) (GMimeSignatureList* siglist); +#else void (*part_sigstatus) (const GMimeSignatureValidity* validity); +#endif void (*part_content) (GMimeObject *part); void (*part_end) (GMimeObject *part); const char *part_sep; @@ -83,7 +93,11 @@ typedef struct notmuch_show_params { int entire_thread; int raw; int part; +#ifdef GMIME_26 + GMimeCryptoContext* cryptoctx; +#else GMimeCipherContext* cryptoctx; +#endif int decrypt; } notmuch_show_params_t; @@ -290,11 +304,17 @@ typedef struct mime_node { /* True if signature verification on this part was attempted. */ notmuch_bool_t verify_attempted; +#ifdef GMIME_26 + /* The list of signatures for signed or encrypted containers. If + * there are no signatures, this will be NULL. */ + GMimeSignatureList* sig_list; +#else /* For signed or encrypted containers, the validity of the * signature. May be NULL if signature verification failed. If * there are simply no signatures, this will be non-NULL with an * empty signers list. */ const GMimeSignatureValidity *sig_validity; +#endif /* Internal: Context inherited from the root iterator. */ struct mime_node_context *ctx; @@ -319,8 +339,12 @@ typedef struct mime_node { */ notmuch_status_t mime_node_open (const void *ctx, notmuch_message_t *message, - GMimeCipherContext *cryptoctx, notmuch_bool_t decrypt, - mime_node_t **node_out); +#ifdef GMIME_26 + GMimeCryptoContext *cryptoctx, +#else + GMimeCipherContext *cryptoctx, +#endif + notmuch_bool_t decrypt, mime_node_t **node_out); /* Return a new MIME node for the requested child part of parent. * parent will be used as the talloc context for the returned child diff --git a/notmuch-reply.c b/notmuch-reply.c index 0f682db..b3d7127 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -688,15 +688,22 @@ notmuch_reply_command (void *ctx, int argc, char *argv[]) reply_format_func = notmuch_reply_format_default; if (decrypt) { +#ifdef GMIME_26 + /* TODO: GMimePasswordRequestFunc */ + params.cryptoctx = g_mime_gpg_context_new (NULL, "gpg"); +#else GMimeSession* session = g_object_new (g_mime_session_get_type(), NULL); params.cryptoctx = g_mime_gpg_context_new (session, "gpg"); +#endif if (params.cryptoctx) { g_mime_gpg_context_set_always_trust ((GMimeGpgContext*) params.cryptoctx, FALSE); params.decrypt = TRUE; } else { fprintf (stderr, "Failed to construct gpg context.\n"); } +#ifndef GMIME_26 g_object_unref (session); +#endif } config = notmuch_config_open (ctx, NULL, NULL); diff --git a/notmuch-show.c b/notmuch-show.c index 91f566c..10223e0 100644 --- a/notmuch-show.c +++ b/notmuch-show.c @@ -76,7 +76,11 @@ static void format_part_encstatus_json (int status); static void +#ifdef GMIME_26 +format_part_sigstatus_json (GMimeSignatureList* siglist); +#else format_part_sigstatus_json (const GMimeSignatureValidity* validity); +#endif static void format_part_content_json (GMimeObject *part); @@ -486,6 +490,21 @@ show_text_part_content (GMimeObject *part, GMimeStream *stream_out) g_object_unref(stream_filter); } +#ifdef GMIME_26 +static const char* +signature_status_to_string (GMimeSignatureStatus x) +{ + switch (x) { + case GMIME_SIGNATURE_STATUS_GOOD: + return "good"; + case GMIME_SIGNATURE_STATUS_BAD: + return "bad"; + case GMIME_SIGNATURE_STATUS_ERROR: + return "error"; + } + return "unknown"; +} +#else static const char* signer_status_to_string (GMimeSignerStatus x) { @@ -501,6 +520,7 @@ signer_status_to_string (GMimeSignerStatus x) } return "unknown"; } +#endif static void format_part_start_text (GMimeObject *part, int *part_count) @@ -592,6 +612,73 @@ format_part_encstatus_json (int status) printf ("}]"); } +#ifdef GMIME_26 +static void +format_part_sigstatus_json (GMimeSignatureList *siglist) +{ + printf (", \"sigstatus\": ["); + + if (!siglist) { + printf ("]"); + return; + } + + void *ctx_quote = talloc_new (NULL); + int i; + for (i = 0; i < g_mime_signature_list_length (siglist); i++) { + GMimeSignature *signature = g_mime_signature_list_get_signature (siglist, i); + + if (i > 0) + printf (", "); + + printf ("{"); + + /* status */ + GMimeSignatureStatus status = g_mime_signature_get_status (signature); + printf ("\"status\": %s", + json_quote_str (ctx_quote, + signature_status_to_string (status))); + + GMimeCertificate *certificate = g_mime_signature_get_certificate (signature); + if (status == GMIME_SIGNATURE_STATUS_GOOD) { + if (certificate) + printf (", \"fingerprint\": %s", json_quote_str (ctx_quote, g_mime_certificate_get_fingerprint (certificate))); + /* these dates are seconds since the epoch; should we + * provide a more human-readable format string? */ + time_t created = g_mime_signature_get_created (signature); + if (created != -1) + printf (", \"created\": %d", (int) created); + time_t expires = g_mime_signature_get_expires (signature); + if (expires > 0) + printf (", \"expires\": %d", (int) expires); + /* output user id only if validity is FULL or ULTIMATE. */ + /* note that gmime is using the term "trust" here, which + * is WRONG. It's actually user id "validity". */ + if (certificate) { + const char *name = g_mime_certificate_get_name (certificate); + GMimeCertificateTrust trust = g_mime_certificate_get_trust (certificate); + if (name && (trust == GMIME_CERTIFICATE_TRUST_FULLY || trust == GMIME_CERTIFICATE_TRUST_ULTIMATE)) + printf (", \"userid\": %s", json_quote_str (ctx_quote, name)); + } + } else if (certificate) { + const char *key_id = g_mime_certificate_get_key_id (certificate); + if (key_id) + printf (", \"keyid\": %s", json_quote_str (ctx_quote, key_id)); + } + + GMimeSignatureError errors = g_mime_signature_get_errors (signature); + if (errors != GMIME_SIGNATURE_ERROR_NONE) { + printf (", \"errors\": %d", errors); + } + + printf ("}"); + } + + printf ("]"); + + talloc_free (ctx_quote); +} +#else static void format_part_sigstatus_json (const GMimeSignatureValidity* validity) { @@ -652,6 +739,7 @@ format_part_sigstatus_json (const GMimeSignatureValidity* validity) talloc_free (ctx_quote); } +#endif static void format_part_content_json (GMimeObject *part) @@ -990,13 +1078,20 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[])) } else if ((STRNCMP_LITERAL (argv[i], "--verify") == 0) || (STRNCMP_LITERAL (argv[i], "--decrypt") == 0)) { if (params.cryptoctx == NULL) { +#ifdef GMIME_26 + /* TODO: GMimePasswordRequestFunc */ + if (NULL == (params.cryptoctx = g_mime_gpg_context_new(NULL, "gpg"))) +#else GMimeSession* session = g_object_new(g_mime_session_get_type(), NULL); if (NULL == (params.cryptoctx = g_mime_gpg_context_new(session, "gpg"))) +#endif fprintf (stderr, "Failed to construct gpg context.\n"); else g_mime_gpg_context_set_always_trust((GMimeGpgContext*)params.cryptoctx, FALSE); +#ifndef GMIME_26 g_object_unref (session); session = NULL; +#endif } if (STRNCMP_LITERAL (argv[i], "--decrypt") == 0) params.decrypt = 1; diff --git a/show-message.c b/show-message.c index 8768889..65269fd 100644 --- a/show-message.c +++ b/show-message.c @@ -48,7 +48,11 @@ show_message_part (mime_node_t *node, format->part_encstatus (node->decrypt_success); if (node->verify_attempted && format->part_sigstatus) +#ifdef GMIME_26 + format->part_sigstatus (node->sig_list); +#else format->part_sigstatus (node->sig_validity); +#endif format->part_content (part); -- 1.7.8.3 ^ permalink raw reply related [flat|nested] 42+ messages in thread
* Re: [PATCH v2 2/2] Add pseudo-compatibility with gmime 2.6 2012-01-17 10:50 ` [PATCH v2 2/2] Add pseudo-compatibility with gmime 2.6 Thomas Jost @ 2012-01-17 12:48 ` Tomi Ollila 2012-01-17 13:23 ` Thomas Jost 2012-01-17 22:25 ` Austin Clements 2012-01-18 17:19 ` [PATCH v2 2/2] Add pseudo-compatibility with gmime 2.6 Tom Prince 2 siblings, 1 reply; 42+ messages in thread From: Tomi Ollila @ 2012-01-17 12:48 UTC (permalink / raw) To: Thomas Jost, notmuch On Tue, 17 Jan 2012 11:50:53 +0100, Thomas Jost <schnouki@schnouki.net> wrote: > There are lots of API changes in gmime 2.6 crypto handling. By adding > preprocessor directives, it is however possible to add gmime 2.6 compatibility > while preserving compatibility with gmime 2.4 too. > > This is mostly based on id:"8762i8hrb9.fsf@bookbinder.fernseed.info". > > This was tested against both gmime 2.6.4 and 2.4.31. With gmime 2.4.31, the > crypto tests all work fine (as expected). With gmime 2.6.4, one crypto test > fails (signature verification with signer key unavailable) but this will be hard > to fix since the new API does not report the reason why a signature verification > fails (other than the human-readable error message). > --- LGTM. Some whitespace things but most of those were there already; I'd have one uncrustify round to be applied to the source and after that be more strict about those... actually gmime 2.4 has GMIME_CHECK_VERSION defined as g_mime_check_version (major, minor, micro) so the comment about it is not entirely accurate (it is unusable in our context, though) Tomi ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v2 2/2] Add pseudo-compatibility with gmime 2.6 2012-01-17 12:48 ` Tomi Ollila @ 2012-01-17 13:23 ` Thomas Jost 0 siblings, 0 replies; 42+ messages in thread From: Thomas Jost @ 2012-01-17 13:23 UTC (permalink / raw) To: Tomi Ollila, notmuch [-- Attachment #1: Type: text/plain, Size: 2152 bytes --] On Tue, 17 Jan 2012 14:48:34 +0200, Tomi Ollila <tomi.ollila@iki.fi> wrote: > On Tue, 17 Jan 2012 11:50:53 +0100, Thomas Jost <schnouki@schnouki.net> wrote: > > There are lots of API changes in gmime 2.6 crypto handling. By adding > > preprocessor directives, it is however possible to add gmime 2.6 compatibility > > while preserving compatibility with gmime 2.4 too. > > > > This is mostly based on id:"8762i8hrb9.fsf@bookbinder.fernseed.info". > > > > This was tested against both gmime 2.6.4 and 2.4.31. With gmime 2.4.31, the > > crypto tests all work fine (as expected). With gmime 2.6.4, one crypto test > > fails (signature verification with signer key unavailable) but this will be hard > > to fix since the new API does not report the reason why a signature verification > > fails (other than the human-readable error message). > > --- > > LGTM. Some whitespace things but most of those were there already; > I'd have one uncrustify round to be applied to the source and after > that be more strict about those... Thanks! I'll fix the whitespace issues in these patches when the source in Git is uncrustified then. > actually gmime 2.4 has GMIME_CHECK_VERSION defined as > g_mime_check_version (major, minor, micro) so the comment about it > is not entirely accurate (it is unusable in our context, though) Oops, yes. What I really meant is that GMIME_MAJOR_VERSION is not available as a preprocessor constant in 2.4, and GMIME_CHECK_VERSION is unusable in our context since it just calls a runtime function. By the way, how do you guys feel about setting GMIME_26 in notmuch-client.h? Is that good enough, or should it be done in configure so that gcc is called with "-DGMIME_26"? I filed a bug about gmime 2.6 incorrect handling of signatures with missing public keys: https://bugzilla.gnome.org/show_bug.cgi?id=668085. A patch is attached there, feel free to test and comment. (Arch Linux users: I made a little PKGBUILD that includes this patch if you want to build your own gmime 2.6.4: http://fichiers.schnouki.net/tmp/gmime-2.6.4-1.src.tar.gz) Best regards, -- Thomas/Schnouki [-- Attachment #2: Type: application/pgp-signature, Size: 489 bytes --] ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v2 2/2] Add pseudo-compatibility with gmime 2.6 2012-01-17 10:50 ` [PATCH v2 2/2] Add pseudo-compatibility with gmime 2.6 Thomas Jost 2012-01-17 12:48 ` Tomi Ollila @ 2012-01-17 22:25 ` Austin Clements 2012-01-18 8:15 ` Tomi Ollila 2012-01-19 23:32 ` Thomas Jost 2012-01-18 17:19 ` [PATCH v2 2/2] Add pseudo-compatibility with gmime 2.6 Tom Prince 2 siblings, 2 replies; 42+ messages in thread From: Austin Clements @ 2012-01-17 22:25 UTC (permalink / raw) To: Thomas Jost; +Cc: notmuch Quoth Thomas Jost on Jan 17 at 11:50 am: > There are lots of API changes in gmime 2.6 crypto handling. By adding > preprocessor directives, it is however possible to add gmime 2.6 compatibility > while preserving compatibility with gmime 2.4 too. > > This is mostly based on id:"8762i8hrb9.fsf@bookbinder.fernseed.info". > > This was tested against both gmime 2.6.4 and 2.4.31. With gmime 2.4.31, the > crypto tests all work fine (as expected). With gmime 2.6.4, one crypto test > fails (signature verification with signer key unavailable) but this will be hard > to fix since the new API does not report the reason why a signature verification > fails (other than the human-readable error message). > --- > mime-node.c | 56 ++++++++++++++++++++++++++++++-- > notmuch-client.h | 28 +++++++++++++++- > notmuch-reply.c | 7 ++++ > notmuch-show.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ > show-message.c | 4 ++ > 5 files changed, 185 insertions(+), 5 deletions(-) > > diff --git a/mime-node.c b/mime-node.c > index d26bb44..e575e1c 100644 > --- a/mime-node.c > +++ b/mime-node.c > @@ -33,7 +33,11 @@ typedef struct mime_node_context { > GMimeMessage *mime_message; > > /* Context provided by the caller. */ > +#ifdef GMIME_26 > + GMimeCryptoContext *cryptoctx; > +#else > GMimeCipherContext *cryptoctx; > +#endif > notmuch_bool_t decrypt; > } mime_node_context_t; > > @@ -57,8 +61,12 @@ _mime_node_context_free (mime_node_context_t *res) > > notmuch_status_t > mime_node_open (const void *ctx, notmuch_message_t *message, > - GMimeCipherContext *cryptoctx, notmuch_bool_t decrypt, > - mime_node_t **root_out) > +#ifdef GMIME_26 > + GMimeCryptoContext *cryptoctx, > +#else > + GMimeCipherContext *cryptoctx, > +#endif > + notmuch_bool_t decrypt, mime_node_t **root_out) > { > const char *filename = notmuch_message_get_filename (message); > mime_node_context_t *mctx; > @@ -112,12 +120,21 @@ DONE: > return status; > } > > +#ifdef GMIME_26 > +static int > +_signature_list_free (GMimeSignatureList **proxy) > +{ > + g_object_unref (*proxy); > + return 0; > +} > +#else > static int > _signature_validity_free (GMimeSignatureValidity **proxy) > { > g_mime_signature_validity_free (*proxy); > return 0; > } > +#endif > > static mime_node_t * > _mime_node_create (const mime_node_t *parent, GMimeObject *part) > @@ -165,11 +182,22 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) > GMimeMultipartEncrypted *encrypteddata = > GMIME_MULTIPART_ENCRYPTED (part); > node->decrypt_attempted = TRUE; > +#ifdef GMIME_26 > + GMimeDecryptResult *decrypt_result = NULL; Reading through the GMime code, it looks like we do have to unref decrypt_result. I think this is easy, though. Right after you call g_mime_decrypt_result_get_signatures below, do: g_object_ref (node->sig_list); g_object_unref (decrypt_result); > + node->decrypted_child = g_mime_multipart_encrypted_decrypt > + (encrypteddata, node->ctx->cryptoctx, &decrypt_result, &err); > +#else > node->decrypted_child = g_mime_multipart_encrypted_decrypt > (encrypteddata, node->ctx->cryptoctx, &err); > +#endif > if (node->decrypted_child) { > - node->decrypt_success = node->verify_attempted = TRUE; > + node->decrypt_success = node->verify_attempted =TRUE; Whitespace. (There should be no diff on the above line) > +#ifdef GMIME_26 > + /* This may be NULL if the part is not signed. */ > + node->sig_list = g_mime_decrypt_result_get_signatures (decrypt_result); > +#else > node->sig_validity = g_mime_multipart_encrypted_get_signature_validity (encrypteddata); > +#endif > } else { > fprintf (stderr, "Failed to decrypt part: %s\n", > (err ? err->message : "no error explanation given")); > @@ -182,6 +210,16 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) > "(must be exactly 2)\n", > node->nchildren); > } else { > +#ifdef GMIME_26 > + GMimeSignatureList *sig_list = g_mime_multipart_signed_verify > + (GMIME_MULTIPART_SIGNED (part), node->ctx->cryptoctx, &err); > + node->verify_attempted = TRUE; > + node->sig_list = sig_list; Just a nit. This could be node->sig_list = g_mime_multipart_signed_verify ( ... ) To me, the local variable just makes this code more verbose without adding anything. Up to you. > + > + if (!sig_list) > + fprintf (stderr, "Failed to verify signed part: %s\n", > + (err ? err->message : "no error explanation given")); > +#else > /* For some reason the GMimeSignatureValidity returned > * here is not a const (inconsistent with that > * returned by > @@ -200,12 +238,24 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) > *proxy = sig_validity; > talloc_set_destructor (proxy, _signature_validity_free); > } > +#endif > } > } > > +#ifdef GMIME_26 > + /* sig_list may be created in both above cases, so we need to > + * cleanly handle it here. */ > + if (node->sig_list) { > + GMimeSignatureList **proxy = > + talloc (node, GMimeSignatureList *); This doesn't need to be split into two lines. > + *proxy = node->sig_list; > + talloc_set_destructor (proxy, _signature_list_free); > + } > +#else > if (node->verify_attempted && !node->sig_validity) > fprintf (stderr, "Failed to verify signed part: %s\n", > (err ? err->message : "no error explanation given")); > +#endif I'd rather see the above as a separate #ifdef GMIME_26 and #ifndef GMIME_26, since they aren't logical alternates of each other. > > if (err) > g_error_free (err); > diff --git a/notmuch-client.h b/notmuch-client.h > index 62ede28..9167042 100644 > --- a/notmuch-client.h > +++ b/notmuch-client.h > @@ -30,6 +30,12 @@ > > #include <gmime/gmime.h> > > +/* GMIME_CHECK_VERSION is only available in gmime >= 2.5. But so are > + * GMIME_MAJOR_VERSION and friends. */ > +#ifdef GMIME_MAJOR_VERSION > +#define GMIME_26 > +#endif > + > #include "notmuch.h" > > /* This is separate from notmuch-private.h because we're trying to > @@ -69,7 +75,11 @@ typedef struct notmuch_show_format { > void (*part_start) (GMimeObject *part, > int *part_count); > void (*part_encstatus) (int status); > +#ifdef GMIME_26 > + void (*part_sigstatus) (GMimeSignatureList* siglist); > +#else > void (*part_sigstatus) (const GMimeSignatureValidity* validity); > +#endif > void (*part_content) (GMimeObject *part); > void (*part_end) (GMimeObject *part); > const char *part_sep; > @@ -83,7 +93,11 @@ typedef struct notmuch_show_params { > int entire_thread; > int raw; > int part; > +#ifdef GMIME_26 > + GMimeCryptoContext* cryptoctx; > +#else > GMimeCipherContext* cryptoctx; > +#endif > int decrypt; > } notmuch_show_params_t; > > @@ -290,11 +304,17 @@ typedef struct mime_node { > > /* True if signature verification on this part was attempted. */ > notmuch_bool_t verify_attempted; > +#ifdef GMIME_26 > + /* The list of signatures for signed or encrypted containers. If > + * there are no signatures, this will be NULL. */ Spacing. > + GMimeSignatureList* sig_list; > +#else > /* For signed or encrypted containers, the validity of the > * signature. May be NULL if signature verification failed. If > * there are simply no signatures, this will be non-NULL with an > * empty signers list. */ > const GMimeSignatureValidity *sig_validity; > +#endif > > /* Internal: Context inherited from the root iterator. */ > struct mime_node_context *ctx; > @@ -319,8 +339,12 @@ typedef struct mime_node { > */ > notmuch_status_t > mime_node_open (const void *ctx, notmuch_message_t *message, > - GMimeCipherContext *cryptoctx, notmuch_bool_t decrypt, > - mime_node_t **node_out); > +#ifdef GMIME_26 > + GMimeCryptoContext *cryptoctx, > +#else > + GMimeCipherContext *cryptoctx, > +#endif > + notmuch_bool_t decrypt, mime_node_t **node_out); > > /* Return a new MIME node for the requested child part of parent. > * parent will be used as the talloc context for the returned child > diff --git a/notmuch-reply.c b/notmuch-reply.c > index 0f682db..b3d7127 100644 > --- a/notmuch-reply.c > +++ b/notmuch-reply.c > @@ -688,15 +688,22 @@ notmuch_reply_command (void *ctx, int argc, char *argv[]) > reply_format_func = notmuch_reply_format_default; > > if (decrypt) { > +#ifdef GMIME_26 > + /* TODO: GMimePasswordRequestFunc */ > + params.cryptoctx = g_mime_gpg_context_new (NULL, "gpg"); > +#else > GMimeSession* session = g_object_new (g_mime_session_get_type(), NULL); > params.cryptoctx = g_mime_gpg_context_new (session, "gpg"); > +#endif > if (params.cryptoctx) { > g_mime_gpg_context_set_always_trust ((GMimeGpgContext*) params.cryptoctx, FALSE); > params.decrypt = TRUE; > } else { > fprintf (stderr, "Failed to construct gpg context.\n"); > } > +#ifndef GMIME_26 > g_object_unref (session); > +#endif > } > > config = notmuch_config_open (ctx, NULL, NULL); > diff --git a/notmuch-show.c b/notmuch-show.c > index 91f566c..10223e0 100644 > --- a/notmuch-show.c > +++ b/notmuch-show.c > @@ -76,7 +76,11 @@ static void > format_part_encstatus_json (int status); > > static void > +#ifdef GMIME_26 > +format_part_sigstatus_json (GMimeSignatureList* siglist); > +#else > format_part_sigstatus_json (const GMimeSignatureValidity* validity); > +#endif > > static void > format_part_content_json (GMimeObject *part); > @@ -486,6 +490,21 @@ show_text_part_content (GMimeObject *part, GMimeStream *stream_out) > g_object_unref(stream_filter); > } > > +#ifdef GMIME_26 > +static const char* > +signature_status_to_string (GMimeSignatureStatus x) > +{ > + switch (x) { > + case GMIME_SIGNATURE_STATUS_GOOD: > + return "good"; > + case GMIME_SIGNATURE_STATUS_BAD: > + return "bad"; > + case GMIME_SIGNATURE_STATUS_ERROR: > + return "error"; > + } > + return "unknown"; > +} > +#else > static const char* > signer_status_to_string (GMimeSignerStatus x) > { > @@ -501,6 +520,7 @@ signer_status_to_string (GMimeSignerStatus x) > } > return "unknown"; > } > +#endif > > static void > format_part_start_text (GMimeObject *part, int *part_count) > @@ -592,6 +612,73 @@ format_part_encstatus_json (int status) > printf ("}]"); > } > > +#ifdef GMIME_26 > +static void > +format_part_sigstatus_json (GMimeSignatureList *siglist) > +{ > + printf (", \"sigstatus\": ["); > + > + if (!siglist) { > + printf ("]"); > + return; > + } Indentation. > + > + void *ctx_quote = talloc_new (NULL); > + int i; > + for (i = 0; i < g_mime_signature_list_length (siglist); i++) { > + GMimeSignature *signature = g_mime_signature_list_get_signature (siglist, i); > + > + if (i > 0) > + printf (", "); > + > + printf ("{"); > + > + /* status */ > + GMimeSignatureStatus status = g_mime_signature_get_status (signature); > + printf ("\"status\": %s", > + json_quote_str (ctx_quote, > + signature_status_to_string (status))); > + > + GMimeCertificate *certificate = g_mime_signature_get_certificate (signature); > + if (status == GMIME_SIGNATURE_STATUS_GOOD) { > + if (certificate) > + printf (", \"fingerprint\": %s", json_quote_str (ctx_quote, g_mime_certificate_get_fingerprint (certificate))); > + /* these dates are seconds since the epoch; should we > + * provide a more human-readable format string? */ > + time_t created = g_mime_signature_get_created (signature); > + if (created != -1) > + printf (", \"created\": %d", (int) created); > + time_t expires = g_mime_signature_get_expires (signature); > + if (expires > 0) > + printf (", \"expires\": %d", (int) expires); > + /* output user id only if validity is FULL or ULTIMATE. */ > + /* note that gmime is using the term "trust" here, which > + * is WRONG. It's actually user id "validity". */ > + if (certificate) { > + const char *name = g_mime_certificate_get_name (certificate); > + GMimeCertificateTrust trust = g_mime_certificate_get_trust (certificate); > + if (name && (trust == GMIME_CERTIFICATE_TRUST_FULLY || trust == GMIME_CERTIFICATE_TRUST_ULTIMATE)) > + printf (", \"userid\": %s", json_quote_str (ctx_quote, name)); > + } > + } else if (certificate) { > + const char *key_id = g_mime_certificate_get_key_id (certificate); > + if (key_id) > + printf (", \"keyid\": %s", json_quote_str (ctx_quote, key_id)); > + } > + > + GMimeSignatureError errors = g_mime_signature_get_errors (signature); > + if (errors != GMIME_SIGNATURE_ERROR_NONE) { > + printf (", \"errors\": %d", errors); > + } > + > + printf ("}"); > + } > + > + printf ("]"); > + > + talloc_free (ctx_quote); > +} > +#else > static void > format_part_sigstatus_json (const GMimeSignatureValidity* validity) > { > @@ -652,6 +739,7 @@ format_part_sigstatus_json (const GMimeSignatureValidity* validity) > > talloc_free (ctx_quote); > } > +#endif > > static void > format_part_content_json (GMimeObject *part) > @@ -990,13 +1078,20 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[])) > } else if ((STRNCMP_LITERAL (argv[i], "--verify") == 0) || > (STRNCMP_LITERAL (argv[i], "--decrypt") == 0)) { > if (params.cryptoctx == NULL) { > +#ifdef GMIME_26 > + /* TODO: GMimePasswordRequestFunc */ > + if (NULL == (params.cryptoctx = g_mime_gpg_context_new(NULL, "gpg"))) > +#else > GMimeSession* session = g_object_new(g_mime_session_get_type(), NULL); > if (NULL == (params.cryptoctx = g_mime_gpg_context_new(session, "gpg"))) > +#endif > fprintf (stderr, "Failed to construct gpg context.\n"); > else > g_mime_gpg_context_set_always_trust((GMimeGpgContext*)params.cryptoctx, FALSE); > +#ifndef GMIME_26 > g_object_unref (session); > session = NULL; > +#endif > } > if (STRNCMP_LITERAL (argv[i], "--decrypt") == 0) > params.decrypt = 1; > diff --git a/show-message.c b/show-message.c > index 8768889..65269fd 100644 > --- a/show-message.c > +++ b/show-message.c > @@ -48,7 +48,11 @@ show_message_part (mime_node_t *node, > format->part_encstatus (node->decrypt_success); > > if (node->verify_attempted && format->part_sigstatus) > +#ifdef GMIME_26 > + format->part_sigstatus (node->sig_list); > +#else > format->part_sigstatus (node->sig_validity); > +#endif > > format->part_content (part); > ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v2 2/2] Add pseudo-compatibility with gmime 2.6 2012-01-17 22:25 ` Austin Clements @ 2012-01-18 8:15 ` Tomi Ollila 2012-01-18 17:35 ` Austin Clements 2012-01-19 23:32 ` Thomas Jost 1 sibling, 1 reply; 42+ messages in thread From: Tomi Ollila @ 2012-01-18 8:15 UTC (permalink / raw) To: notmuch On Tue, 17 Jan 2012 17:25:46 -0500, Austin Clements <amdragon@MIT.EDU> wrote: > Quoth Thomas Jost on Jan 17 at 11:50 am: > > > > +#ifdef GMIME_26 > > + /* sig_list may be created in both above cases, so we need to > > + * cleanly handle it here. */ > > + if (node->sig_list) { > > + GMimeSignatureList **proxy = > > + talloc (node, GMimeSignatureList *); > > This doesn't need to be split into two lines. > > > + *proxy = node->sig_list; > > + talloc_set_destructor (proxy, _signature_list_free); > > + } > > +#else > > if (node->verify_attempted && !node->sig_validity) > > fprintf (stderr, "Failed to verify signed part: %s\n", > > (err ? err->message : "no error explanation given")); > > +#endif > > I'd rather see the above as a separate #ifdef GMIME_26 and #ifndef > GMIME_26, since they aren't logical alternates of each other. That reminds me that it should then be like GMIME_ATLEAST_26, so that this might be useful when GMIME > 2.6 is available... Tomi ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v2 2/2] Add pseudo-compatibility with gmime 2.6 2012-01-18 8:15 ` Tomi Ollila @ 2012-01-18 17:35 ` Austin Clements 2012-01-19 23:33 ` Thomas Jost 0 siblings, 1 reply; 42+ messages in thread From: Austin Clements @ 2012-01-18 17:35 UTC (permalink / raw) To: Tomi Ollila; +Cc: notmuch Quoth Tomi Ollila on Jan 18 at 10:15 am: > On Tue, 17 Jan 2012 17:25:46 -0500, Austin Clements <amdragon@MIT.EDU> wrote: > > Quoth Thomas Jost on Jan 17 at 11:50 am: > > > > > > +#ifdef GMIME_26 > > > + /* sig_list may be created in both above cases, so we need to > > > + * cleanly handle it here. */ > > > + if (node->sig_list) { > > > + GMimeSignatureList **proxy = > > > + talloc (node, GMimeSignatureList *); > > > > This doesn't need to be split into two lines. > > > > > + *proxy = node->sig_list; > > > + talloc_set_destructor (proxy, _signature_list_free); > > > + } > > > +#else > > > if (node->verify_attempted && !node->sig_validity) > > > fprintf (stderr, "Failed to verify signed part: %s\n", > > > (err ? err->message : "no error explanation given")); > > > +#endif > > > > I'd rather see the above as a separate #ifdef GMIME_26 and #ifndef > > GMIME_26, since they aren't logical alternates of each other. > > That reminds me that it should then be like GMIME_ATLEAST_26, so > that this might be useful when GMIME > 2.6 is available... Hopefully before GMIME 2.8 comes out, we'll be able to remove all of the GMIME 2.4 compatibility. But GMIME_ATLEAST_26 would be fine, too. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v2 2/2] Add pseudo-compatibility with gmime 2.6 2012-01-18 17:35 ` Austin Clements @ 2012-01-19 23:33 ` Thomas Jost 0 siblings, 0 replies; 42+ messages in thread From: Thomas Jost @ 2012-01-19 23:33 UTC (permalink / raw) To: Austin Clements, Tomi Ollila; +Cc: notmuch [-- Attachment #1: Type: text/plain, Size: 1579 bytes --] On Wed, 18 Jan 2012 12:35:34 -0500, Austin Clements <amdragon@MIT.EDU> wrote: > Quoth Tomi Ollila on Jan 18 at 10:15 am: > > On Tue, 17 Jan 2012 17:25:46 -0500, Austin Clements <amdragon@MIT.EDU> wrote: > > > Quoth Thomas Jost on Jan 17 at 11:50 am: > > > > > > > > +#ifdef GMIME_26 > > > > + /* sig_list may be created in both above cases, so we need to > > > > + * cleanly handle it here. */ > > > > + if (node->sig_list) { > > > > + GMimeSignatureList **proxy = > > > > + talloc (node, GMimeSignatureList *); > > > > > > This doesn't need to be split into two lines. > > > > > > > + *proxy = node->sig_list; > > > > + talloc_set_destructor (proxy, _signature_list_free); > > > > + } > > > > +#else > > > > if (node->verify_attempted && !node->sig_validity) > > > > fprintf (stderr, "Failed to verify signed part: %s\n", > > > > (err ? err->message : "no error explanation given")); > > > > +#endif > > > > > > I'd rather see the above as a separate #ifdef GMIME_26 and #ifndef > > > GMIME_26, since they aren't logical alternates of each other. > > > > That reminds me that it should then be like GMIME_ATLEAST_26, so > > that this might be useful when GMIME > 2.6 is available... > > Hopefully before GMIME 2.8 comes out, we'll be able to remove all of > the GMIME 2.4 compatibility. But GMIME_ATLEAST_26 would be fine, too. Heh, maybe things will change again in 2.8 and "ATLEAST_26" will become "ONLY_26"... But changed to GMIME_ATLEAST_26 anyway, thanks for the suggestion :) -- Thomas/Schnouki [-- Attachment #2: Type: application/pgp-signature, Size: 489 bytes --] ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v2 2/2] Add pseudo-compatibility with gmime 2.6 2012-01-17 22:25 ` Austin Clements 2012-01-18 8:15 ` Tomi Ollila @ 2012-01-19 23:32 ` Thomas Jost 2012-01-20 0:06 ` [PATCH v3 0/2] gmime 2.6 compatibilty, 3rd iteration Thomas Jost 1 sibling, 1 reply; 42+ messages in thread From: Thomas Jost @ 2012-01-19 23:32 UTC (permalink / raw) To: Austin Clements; +Cc: notmuch [-- Attachment #1: Type: text/plain, Size: 16260 bytes --] Thanks for this review Austin. New version coming soon. On Tue, 17 Jan 2012 17:25:46 -0500, Austin Clements <amdragon@MIT.EDU> wrote: > Quoth Thomas Jost on Jan 17 at 11:50 am: > > There are lots of API changes in gmime 2.6 crypto handling. By adding > > preprocessor directives, it is however possible to add gmime 2.6 compatibility > > while preserving compatibility with gmime 2.4 too. > > > > This is mostly based on id:"8762i8hrb9.fsf@bookbinder.fernseed.info". > > > > This was tested against both gmime 2.6.4 and 2.4.31. With gmime 2.4.31, the > > crypto tests all work fine (as expected). With gmime 2.6.4, one crypto test > > fails (signature verification with signer key unavailable) but this will be hard > > to fix since the new API does not report the reason why a signature verification > > fails (other than the human-readable error message). > > --- > > mime-node.c | 56 ++++++++++++++++++++++++++++++-- > > notmuch-client.h | 28 +++++++++++++++- > > notmuch-reply.c | 7 ++++ > > notmuch-show.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ > > show-message.c | 4 ++ > > 5 files changed, 185 insertions(+), 5 deletions(-) > > > > diff --git a/mime-node.c b/mime-node.c > > index d26bb44..e575e1c 100644 > > --- a/mime-node.c > > +++ b/mime-node.c > > @@ -33,7 +33,11 @@ typedef struct mime_node_context { > > GMimeMessage *mime_message; > > > > /* Context provided by the caller. */ > > +#ifdef GMIME_26 > > + GMimeCryptoContext *cryptoctx; > > +#else > > GMimeCipherContext *cryptoctx; > > +#endif > > notmuch_bool_t decrypt; > > } mime_node_context_t; > > > > @@ -57,8 +61,12 @@ _mime_node_context_free (mime_node_context_t *res) > > > > notmuch_status_t > > mime_node_open (const void *ctx, notmuch_message_t *message, > > - GMimeCipherContext *cryptoctx, notmuch_bool_t decrypt, > > - mime_node_t **root_out) > > +#ifdef GMIME_26 > > + GMimeCryptoContext *cryptoctx, > > +#else > > + GMimeCipherContext *cryptoctx, > > +#endif > > + notmuch_bool_t decrypt, mime_node_t **root_out) > > { > > const char *filename = notmuch_message_get_filename (message); > > mime_node_context_t *mctx; > > @@ -112,12 +120,21 @@ DONE: > > return status; > > } > > > > +#ifdef GMIME_26 > > +static int > > +_signature_list_free (GMimeSignatureList **proxy) > > +{ > > + g_object_unref (*proxy); > > + return 0; > > +} > > +#else > > static int > > _signature_validity_free (GMimeSignatureValidity **proxy) > > { > > g_mime_signature_validity_free (*proxy); > > return 0; > > } > > +#endif > > > > static mime_node_t * > > _mime_node_create (const mime_node_t *parent, GMimeObject *part) > > @@ -165,11 +182,22 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) > > GMimeMultipartEncrypted *encrypteddata = > > GMIME_MULTIPART_ENCRYPTED (part); > > node->decrypt_attempted = TRUE; > > +#ifdef GMIME_26 > > + GMimeDecryptResult *decrypt_result = NULL; > > Reading through the GMime code, it looks like we do have to unref > decrypt_result. I think this is easy, though. Right after you call > g_mime_decrypt_result_get_signatures below, do: > > g_object_ref (node->sig_list); > g_object_unref (decrypt_result); Added, thanks! > > > + node->decrypted_child = g_mime_multipart_encrypted_decrypt > > + (encrypteddata, node->ctx->cryptoctx, &decrypt_result, &err); > > +#else > > node->decrypted_child = g_mime_multipart_encrypted_decrypt > > (encrypteddata, node->ctx->cryptoctx, &err); > > +#endif > > if (node->decrypted_child) { > > - node->decrypt_success = node->verify_attempted = TRUE; > > + node->decrypt_success = node->verify_attempted =TRUE; > > Whitespace. (There should be no diff on the above line) Oops, my bad. > > > +#ifdef GMIME_26 > > + /* This may be NULL if the part is not signed. */ > > + node->sig_list = g_mime_decrypt_result_get_signatures (decrypt_result); > > +#else > > node->sig_validity = g_mime_multipart_encrypted_get_signature_validity (encrypteddata); > > +#endif > > } else { > > fprintf (stderr, "Failed to decrypt part: %s\n", > > (err ? err->message : "no error explanation given")); > > @@ -182,6 +210,16 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) > > "(must be exactly 2)\n", > > node->nchildren); > > } else { > > +#ifdef GMIME_26 > > + GMimeSignatureList *sig_list = g_mime_multipart_signed_verify > > + (GMIME_MULTIPART_SIGNED (part), node->ctx->cryptoctx, &err); > > + node->verify_attempted = TRUE; > > + node->sig_list = sig_list; > > Just a nit. This could be > node->sig_list = g_mime_multipart_signed_verify ( ... ) > To me, the local variable just makes this code more verbose without > adding anything. Up to you. Yep, the local variable is useless. Removed it. > > > + > > + if (!sig_list) > > + fprintf (stderr, "Failed to verify signed part: %s\n", > > + (err ? err->message : "no error explanation given")); > > +#else > > /* For some reason the GMimeSignatureValidity returned > > * here is not a const (inconsistent with that > > * returned by > > @@ -200,12 +238,24 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) > > *proxy = sig_validity; > > talloc_set_destructor (proxy, _signature_validity_free); > > } > > +#endif > > } > > } > > > > +#ifdef GMIME_26 > > + /* sig_list may be created in both above cases, so we need to > > + * cleanly handle it here. */ > > + if (node->sig_list) { > > + GMimeSignatureList **proxy = > > + talloc (node, GMimeSignatureList *); > > This doesn't need to be split into two lines. You're right. It was more readable when that piece of code was more indented, but now one line is fine :) > > > + *proxy = node->sig_list; > > + talloc_set_destructor (proxy, _signature_list_free); > > + } > > +#else > > if (node->verify_attempted && !node->sig_validity) > > fprintf (stderr, "Failed to verify signed part: %s\n", > > (err ? err->message : "no error explanation given")); > > +#endif > > I'd rather see the above as a separate #ifdef GMIME_26 and #ifndef > GMIME_26, since they aren't logical alternates of each other. Agreed. > > > > > if (err) > > g_error_free (err); > > diff --git a/notmuch-client.h b/notmuch-client.h > > index 62ede28..9167042 100644 > > --- a/notmuch-client.h > > +++ b/notmuch-client.h > > @@ -30,6 +30,12 @@ > > > > #include <gmime/gmime.h> > > > > +/* GMIME_CHECK_VERSION is only available in gmime >= 2.5. But so are > > + * GMIME_MAJOR_VERSION and friends. */ > > +#ifdef GMIME_MAJOR_VERSION > > +#define GMIME_26 > > +#endif > > + > > #include "notmuch.h" > > > > /* This is separate from notmuch-private.h because we're trying to > > @@ -69,7 +75,11 @@ typedef struct notmuch_show_format { > > void (*part_start) (GMimeObject *part, > > int *part_count); > > void (*part_encstatus) (int status); > > +#ifdef GMIME_26 > > + void (*part_sigstatus) (GMimeSignatureList* siglist); > > +#else > > void (*part_sigstatus) (const GMimeSignatureValidity* validity); > > +#endif > > void (*part_content) (GMimeObject *part); > > void (*part_end) (GMimeObject *part); > > const char *part_sep; > > @@ -83,7 +93,11 @@ typedef struct notmuch_show_params { > > int entire_thread; > > int raw; > > int part; > > +#ifdef GMIME_26 > > + GMimeCryptoContext* cryptoctx; > > +#else > > GMimeCipherContext* cryptoctx; > > +#endif > > int decrypt; > > } notmuch_show_params_t; > > > > @@ -290,11 +304,17 @@ typedef struct mime_node { > > > > /* True if signature verification on this part was attempted. */ > > notmuch_bool_t verify_attempted; > > +#ifdef GMIME_26 > > + /* The list of signatures for signed or encrypted containers. If > > + * there are no signatures, this will be NULL. */ > > Spacing. > > > + GMimeSignatureList* sig_list; > > +#else > > /* For signed or encrypted containers, the validity of the > > * signature. May be NULL if signature verification failed. If > > * there are simply no signatures, this will be non-NULL with an > > * empty signers list. */ > > const GMimeSignatureValidity *sig_validity; > > +#endif > > > > /* Internal: Context inherited from the root iterator. */ > > struct mime_node_context *ctx; > > @@ -319,8 +339,12 @@ typedef struct mime_node { > > */ > > notmuch_status_t > > mime_node_open (const void *ctx, notmuch_message_t *message, > > - GMimeCipherContext *cryptoctx, notmuch_bool_t decrypt, > > - mime_node_t **node_out); > > +#ifdef GMIME_26 > > + GMimeCryptoContext *cryptoctx, > > +#else > > + GMimeCipherContext *cryptoctx, > > +#endif > > + notmuch_bool_t decrypt, mime_node_t **node_out); > > > > /* Return a new MIME node for the requested child part of parent. > > * parent will be used as the talloc context for the returned child > > diff --git a/notmuch-reply.c b/notmuch-reply.c > > index 0f682db..b3d7127 100644 > > --- a/notmuch-reply.c > > +++ b/notmuch-reply.c > > @@ -688,15 +688,22 @@ notmuch_reply_command (void *ctx, int argc, char *argv[]) > > reply_format_func = notmuch_reply_format_default; > > > > if (decrypt) { > > +#ifdef GMIME_26 > > + /* TODO: GMimePasswordRequestFunc */ > > + params.cryptoctx = g_mime_gpg_context_new (NULL, "gpg"); > > +#else > > GMimeSession* session = g_object_new (g_mime_session_get_type(), NULL); > > params.cryptoctx = g_mime_gpg_context_new (session, "gpg"); > > +#endif > > if (params.cryptoctx) { > > g_mime_gpg_context_set_always_trust ((GMimeGpgContext*) params.cryptoctx, FALSE); > > params.decrypt = TRUE; > > } else { > > fprintf (stderr, "Failed to construct gpg context.\n"); > > } > > +#ifndef GMIME_26 > > g_object_unref (session); > > +#endif > > } > > > > config = notmuch_config_open (ctx, NULL, NULL); > > diff --git a/notmuch-show.c b/notmuch-show.c > > index 91f566c..10223e0 100644 > > --- a/notmuch-show.c > > +++ b/notmuch-show.c > > @@ -76,7 +76,11 @@ static void > > format_part_encstatus_json (int status); > > > > static void > > +#ifdef GMIME_26 > > +format_part_sigstatus_json (GMimeSignatureList* siglist); > > +#else > > format_part_sigstatus_json (const GMimeSignatureValidity* validity); > > +#endif > > > > static void > > format_part_content_json (GMimeObject *part); > > @@ -486,6 +490,21 @@ show_text_part_content (GMimeObject *part, GMimeStream *stream_out) > > g_object_unref(stream_filter); > > } > > > > +#ifdef GMIME_26 > > +static const char* > > +signature_status_to_string (GMimeSignatureStatus x) > > +{ > > + switch (x) { > > + case GMIME_SIGNATURE_STATUS_GOOD: > > + return "good"; > > + case GMIME_SIGNATURE_STATUS_BAD: > > + return "bad"; > > + case GMIME_SIGNATURE_STATUS_ERROR: > > + return "error"; > > + } > > + return "unknown"; > > +} > > +#else > > static const char* > > signer_status_to_string (GMimeSignerStatus x) > > { > > @@ -501,6 +520,7 @@ signer_status_to_string (GMimeSignerStatus x) > > } > > return "unknown"; > > } > > +#endif > > > > static void > > format_part_start_text (GMimeObject *part, int *part_count) > > @@ -592,6 +612,73 @@ format_part_encstatus_json (int status) > > printf ("}]"); > > } > > > > +#ifdef GMIME_26 > > +static void > > +format_part_sigstatus_json (GMimeSignatureList *siglist) > > +{ > > + printf (", \"sigstatus\": ["); > > + > > + if (!siglist) { > > + printf ("]"); > > + return; > > + } > > Indentation. > > > + > > + void *ctx_quote = talloc_new (NULL); > > + int i; > > + for (i = 0; i < g_mime_signature_list_length (siglist); i++) { > > + GMimeSignature *signature = g_mime_signature_list_get_signature (siglist, i); > > + > > + if (i > 0) > > + printf (", "); > > + > > + printf ("{"); > > + > > + /* status */ > > + GMimeSignatureStatus status = g_mime_signature_get_status (signature); > > + printf ("\"status\": %s", > > + json_quote_str (ctx_quote, > > + signature_status_to_string (status))); > > + > > + GMimeCertificate *certificate = g_mime_signature_get_certificate (signature); > > + if (status == GMIME_SIGNATURE_STATUS_GOOD) { > > + if (certificate) > > + printf (", \"fingerprint\": %s", json_quote_str (ctx_quote, g_mime_certificate_get_fingerprint (certificate))); > > + /* these dates are seconds since the epoch; should we > > + * provide a more human-readable format string? */ > > + time_t created = g_mime_signature_get_created (signature); > > + if (created != -1) > > + printf (", \"created\": %d", (int) created); > > + time_t expires = g_mime_signature_get_expires (signature); > > + if (expires > 0) > > + printf (", \"expires\": %d", (int) expires); > > + /* output user id only if validity is FULL or ULTIMATE. */ > > + /* note that gmime is using the term "trust" here, which > > + * is WRONG. It's actually user id "validity". */ > > + if (certificate) { > > + const char *name = g_mime_certificate_get_name (certificate); > > + GMimeCertificateTrust trust = g_mime_certificate_get_trust (certificate); > > + if (name && (trust == GMIME_CERTIFICATE_TRUST_FULLY || trust == GMIME_CERTIFICATE_TRUST_ULTIMATE)) > > + printf (", \"userid\": %s", json_quote_str (ctx_quote, name)); > > + } > > + } else if (certificate) { > > + const char *key_id = g_mime_certificate_get_key_id (certificate); > > + if (key_id) > > + printf (", \"keyid\": %s", json_quote_str (ctx_quote, key_id)); > > + } > > + > > + GMimeSignatureError errors = g_mime_signature_get_errors (signature); > > + if (errors != GMIME_SIGNATURE_ERROR_NONE) { > > + printf (", \"errors\": %d", errors); > > + } > > + > > + printf ("}"); > > + } > > + > > + printf ("]"); > > + > > + talloc_free (ctx_quote); > > +} > > +#else > > static void > > format_part_sigstatus_json (const GMimeSignatureValidity* validity) > > { > > @@ -652,6 +739,7 @@ format_part_sigstatus_json (const GMimeSignatureValidity* validity) > > > > talloc_free (ctx_quote); > > } > > +#endif > > > > static void > > format_part_content_json (GMimeObject *part) > > @@ -990,13 +1078,20 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[])) > > } else if ((STRNCMP_LITERAL (argv[i], "--verify") == 0) || > > (STRNCMP_LITERAL (argv[i], "--decrypt") == 0)) { > > if (params.cryptoctx == NULL) { > > +#ifdef GMIME_26 > > + /* TODO: GMimePasswordRequestFunc */ > > + if (NULL == (params.cryptoctx = g_mime_gpg_context_new(NULL, "gpg"))) > > +#else > > GMimeSession* session = g_object_new(g_mime_session_get_type(), NULL); > > if (NULL == (params.cryptoctx = g_mime_gpg_context_new(session, "gpg"))) > > +#endif > > fprintf (stderr, "Failed to construct gpg context.\n"); > > else > > g_mime_gpg_context_set_always_trust((GMimeGpgContext*)params.cryptoctx, FALSE); > > +#ifndef GMIME_26 > > g_object_unref (session); > > session = NULL; > > +#endif > > } > > if (STRNCMP_LITERAL (argv[i], "--decrypt") == 0) > > params.decrypt = 1; > > diff --git a/show-message.c b/show-message.c > > index 8768889..65269fd 100644 > > --- a/show-message.c > > +++ b/show-message.c > > @@ -48,7 +48,11 @@ show_message_part (mime_node_t *node, > > format->part_encstatus (node->decrypt_success); > > > > if (node->verify_attempted && format->part_sigstatus) > > +#ifdef GMIME_26 > > + format->part_sigstatus (node->sig_list); > > +#else > > format->part_sigstatus (node->sig_validity); > > +#endif > > > > format->part_content (part); > > -- Thomas/Schnouki [-- Attachment #2: Type: application/pgp-signature, Size: 489 bytes --] ^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH v3 0/2] gmime 2.6 compatibilty, 3rd iteration 2012-01-19 23:32 ` Thomas Jost @ 2012-01-20 0:06 ` Thomas Jost 2012-01-20 0:06 ` [PATCH v3 1/2] show: don't use hex literals in JSON output Thomas Jost 2012-01-20 0:06 ` [PATCH v3 2/2] Add compatibility with gmime 2.6 Thomas Jost 0 siblings, 2 replies; 42+ messages in thread From: Thomas Jost @ 2012-01-20 0:06 UTC (permalink / raw) To: notmuch Hi list, Here's another update of the patches to add gmime 2.6 compatibilty while still preserving compatibility with gmime 2.4. Any comments or review will be much appreciated. The changes compared to the previous version ([1] and [2]) are pretty minor: - space and indentation fixes - correctly dereference the instance of GMimeDecryptResult allocated by g_mime_decrypt_result_get_signatures() - remove a useless local variable - rename the preprocessor constant GMIME_26 to GMIME_ATLEAST_26 - mark one crypto test as broken when using gmime 2.6 (because of a bug in gmime [3]) The first patch is really not specific to gmime so it could probably be pushed right away. Thanks to Austin Clements, Tomi Ollila and Adrian Perez for their reviews of the previous patches! Best regards, Thomas [1] id:"1326797453-9405-1-git-send-email-schnouki@schnouki.net" [2] id:"1326797453-9405-2-git-send-email-schnouki@schnouki.net" [3] https://bugzilla.gnome.org/show_bug.cgi?id=668085 Thomas Jost (2): show: don't use hex literals in JSON output Add compatibility with gmime 2.6 mime-node.c | 60 +++++++++++++++++++++++++++++++-- notmuch-client.h | 30 +++++++++++++++- notmuch-reply.c | 7 ++++ notmuch-show.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++- show-message.c | 4 ++ test/crypto | 2 + 6 files changed, 193 insertions(+), 7 deletions(-) -- 1.7.8.4 ^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH v3 1/2] show: don't use hex literals in JSON output 2012-01-20 0:06 ` [PATCH v3 0/2] gmime 2.6 compatibilty, 3rd iteration Thomas Jost @ 2012-01-20 0:06 ` Thomas Jost 2012-01-20 0:06 ` [PATCH v3 2/2] Add compatibility with gmime 2.6 Thomas Jost 1 sibling, 0 replies; 42+ messages in thread From: Thomas Jost @ 2012-01-20 0:06 UTC (permalink / raw) To: notmuch JSON does not support hex literals (0x..) so numbers must be formatted as %d instead of %x. --- notmuch-show.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/notmuch-show.c b/notmuch-show.c index d14dac9..91f566c 100644 --- a/notmuch-show.c +++ b/notmuch-show.c @@ -641,7 +641,7 @@ format_part_sigstatus_json (const GMimeSignatureValidity* validity) printf (", \"keyid\": %s", json_quote_str (ctx_quote, signer->keyid)); } if (signer->errors != GMIME_SIGNER_ERROR_NONE) { - printf (", \"errors\": %x", signer->errors); + printf (", \"errors\": %d", signer->errors); } printf ("}"); -- 1.7.8.4 ^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH v3 2/2] Add compatibility with gmime 2.6 2012-01-20 0:06 ` [PATCH v3 0/2] gmime 2.6 compatibilty, 3rd iteration Thomas Jost 2012-01-20 0:06 ` [PATCH v3 1/2] show: don't use hex literals in JSON output Thomas Jost @ 2012-01-20 0:06 ` Thomas Jost 2012-01-20 4:10 ` Austin Clements 1 sibling, 1 reply; 42+ messages in thread From: Thomas Jost @ 2012-01-20 0:06 UTC (permalink / raw) To: notmuch There are lots of API changes in gmime 2.6 crypto handling. By adding preprocessor directives, it is however possible to add gmime 2.6 compatibility while preserving compatibility with gmime 2.4 too. This is mostly based on id:"8762i8hrb9.fsf@bookbinder.fernseed.info". This was tested against both gmime 2.6.4 and 2.4.31. With gmime 2.4.31, the crypto tests all work fine (as expected). With gmime 2.6.4, one crypto test is currently broken (signature verification with signer key unavailable), most likely because of a bug in gmime which will hopefully be fixed in a future version. --- mime-node.c | 60 ++++++++++++++++++++++++++++++++-- notmuch-client.h | 30 ++++++++++++++++- notmuch-reply.c | 7 ++++ notmuch-show.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ show-message.c | 4 ++ test/crypto | 2 + 6 files changed, 192 insertions(+), 6 deletions(-) diff --git a/mime-node.c b/mime-node.c index d26bb44..ad19f5e 100644 --- a/mime-node.c +++ b/mime-node.c @@ -33,7 +33,11 @@ typedef struct mime_node_context { GMimeMessage *mime_message; /* Context provided by the caller. */ +#ifdef GMIME_ATLEAST_26 + GMimeCryptoContext *cryptoctx; +#else GMimeCipherContext *cryptoctx; +#endif notmuch_bool_t decrypt; } mime_node_context_t; @@ -57,8 +61,12 @@ _mime_node_context_free (mime_node_context_t *res) notmuch_status_t mime_node_open (const void *ctx, notmuch_message_t *message, - GMimeCipherContext *cryptoctx, notmuch_bool_t decrypt, - mime_node_t **root_out) +#ifdef GMIME_ATLEAST_26 + GMimeCryptoContext *cryptoctx, +#else + GMimeCipherContext *cryptoctx, +#endif + notmuch_bool_t decrypt, mime_node_t **root_out) { const char *filename = notmuch_message_get_filename (message); mime_node_context_t *mctx; @@ -112,12 +120,21 @@ DONE: return status; } +#ifdef GMIME_ATLEAST_26 +static int +_signature_list_free (GMimeSignatureList **proxy) +{ + g_object_unref (*proxy); + return 0; +} +#else static int _signature_validity_free (GMimeSignatureValidity **proxy) { g_mime_signature_validity_free (*proxy); return 0; } +#endif static mime_node_t * _mime_node_create (const mime_node_t *parent, GMimeObject *part) @@ -165,11 +182,24 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) GMimeMultipartEncrypted *encrypteddata = GMIME_MULTIPART_ENCRYPTED (part); node->decrypt_attempted = TRUE; +#ifdef GMIME_ATLEAST_26 + GMimeDecryptResult *decrypt_result = NULL; + node->decrypted_child = g_mime_multipart_encrypted_decrypt + (encrypteddata, node->ctx->cryptoctx, &decrypt_result, &err); +#else node->decrypted_child = g_mime_multipart_encrypted_decrypt (encrypteddata, node->ctx->cryptoctx, &err); +#endif if (node->decrypted_child) { node->decrypt_success = node->verify_attempted = TRUE; +#ifdef GMIME_ATLEAST_26 + /* This may be NULL if the part is not signed. */ + node->sig_list = g_mime_decrypt_result_get_signatures (decrypt_result); + g_object_ref (node->sig_list); + g_object_unref (decrypt_result); +#else node->sig_validity = g_mime_multipart_encrypted_get_signature_validity (encrypteddata); +#endif } else { fprintf (stderr, "Failed to decrypt part: %s\n", (err ? err->message : "no error explanation given")); @@ -182,6 +212,15 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) "(must be exactly 2)\n", node->nchildren); } else { +#ifdef GMIME_ATLEAST_26 + node->sig_list = g_mime_multipart_signed_verify + (GMIME_MULTIPART_SIGNED (part), node->ctx->cryptoctx, &err); + node->verify_attempted = TRUE; + + if (!node->sig_list) + fprintf (stderr, "Failed to verify signed part: %s\n", + (err ? err->message : "no error explanation given")); +#else /* For some reason the GMimeSignatureValidity returned * here is not a const (inconsistent with that * returned by @@ -195,17 +234,30 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) node->verify_attempted = TRUE; node->sig_validity = sig_validity; if (sig_validity) { - GMimeSignatureValidity **proxy = - talloc (node, GMimeSignatureValidity *); + GMimeSignatureValidity **proxy = talloc (node, GMimeSignatureValidity *); *proxy = sig_validity; talloc_set_destructor (proxy, _signature_validity_free); } +#endif } } +#ifdef GMIME_ATLEAST_26 + /* sig_list may be created in both above cases, so we need to + * cleanly handle it here. */ + if (node->sig_list) { + GMimeSignatureList **proxy = + talloc (node, GMimeSignatureList *); + *proxy = node->sig_list; + talloc_set_destructor (proxy, _signature_list_free); + } +#endif + +#ifndef GMIME_ATLEAST_26 if (node->verify_attempted && !node->sig_validity) fprintf (stderr, "Failed to verify signed part: %s\n", (err ? err->message : "no error explanation given")); +#endif if (err) g_error_free (err); diff --git a/notmuch-client.h b/notmuch-client.h index 62ede28..9c1d383 100644 --- a/notmuch-client.h +++ b/notmuch-client.h @@ -30,6 +30,14 @@ #include <gmime/gmime.h> +/* GMIME_CHECK_VERSION in gmime 2.4 is not usable from the + * preprocessor (it calls a runtime function). But since + * GMIME_MAJOR_VERSION and friends were added in gmime 2.6, we can use + * these to check the version number. */ +#ifdef GMIME_MAJOR_VERSION +#define GMIME_ATLEAST_26 +#endif + #include "notmuch.h" /* This is separate from notmuch-private.h because we're trying to @@ -69,7 +77,11 @@ typedef struct notmuch_show_format { void (*part_start) (GMimeObject *part, int *part_count); void (*part_encstatus) (int status); +#ifdef GMIME_ATLEAST_26 + void (*part_sigstatus) (GMimeSignatureList* siglist); +#else void (*part_sigstatus) (const GMimeSignatureValidity* validity); +#endif void (*part_content) (GMimeObject *part); void (*part_end) (GMimeObject *part); const char *part_sep; @@ -83,7 +95,11 @@ typedef struct notmuch_show_params { int entire_thread; int raw; int part; +#ifdef GMIME_ATLEAST_26 + GMimeCryptoContext* cryptoctx; +#else GMimeCipherContext* cryptoctx; +#endif int decrypt; } notmuch_show_params_t; @@ -290,11 +306,17 @@ typedef struct mime_node { /* True if signature verification on this part was attempted. */ notmuch_bool_t verify_attempted; +#ifdef GMIME_ATLEAST_26 + /* The list of signatures for signed or encrypted containers. If + * there are no signatures, this will be NULL. */ + GMimeSignatureList* sig_list; +#else /* For signed or encrypted containers, the validity of the * signature. May be NULL if signature verification failed. If * there are simply no signatures, this will be non-NULL with an * empty signers list. */ const GMimeSignatureValidity *sig_validity; +#endif /* Internal: Context inherited from the root iterator. */ struct mime_node_context *ctx; @@ -319,8 +341,12 @@ typedef struct mime_node { */ notmuch_status_t mime_node_open (const void *ctx, notmuch_message_t *message, - GMimeCipherContext *cryptoctx, notmuch_bool_t decrypt, - mime_node_t **node_out); +#ifdef GMIME_ATLEAST_26 + GMimeCryptoContext *cryptoctx, +#else + GMimeCipherContext *cryptoctx, +#endif + notmuch_bool_t decrypt, mime_node_t **node_out); /* Return a new MIME node for the requested child part of parent. * parent will be used as the talloc context for the returned child diff --git a/notmuch-reply.c b/notmuch-reply.c index 0f682db..bf67960 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -688,15 +688,22 @@ notmuch_reply_command (void *ctx, int argc, char *argv[]) reply_format_func = notmuch_reply_format_default; if (decrypt) { +#ifdef GMIME_ATLEAST_26 + /* TODO: GMimePasswordRequestFunc */ + params.cryptoctx = g_mime_gpg_context_new (NULL, "gpg"); +#else GMimeSession* session = g_object_new (g_mime_session_get_type(), NULL); params.cryptoctx = g_mime_gpg_context_new (session, "gpg"); +#endif if (params.cryptoctx) { g_mime_gpg_context_set_always_trust ((GMimeGpgContext*) params.cryptoctx, FALSE); params.decrypt = TRUE; } else { fprintf (stderr, "Failed to construct gpg context.\n"); } +#ifndef GMIME_ATLEAST_26 g_object_unref (session); +#endif } config = notmuch_config_open (ctx, NULL, NULL); diff --git a/notmuch-show.c b/notmuch-show.c index 91f566c..190210c 100644 --- a/notmuch-show.c +++ b/notmuch-show.c @@ -76,7 +76,11 @@ static void format_part_encstatus_json (int status); static void +#ifdef GMIME_ATLEAST_26 +format_part_sigstatus_json (GMimeSignatureList* siglist); +#else format_part_sigstatus_json (const GMimeSignatureValidity* validity); +#endif static void format_part_content_json (GMimeObject *part); @@ -486,6 +490,21 @@ show_text_part_content (GMimeObject *part, GMimeStream *stream_out) g_object_unref(stream_filter); } +#ifdef GMIME_ATLEAST_26 +static const char* +signature_status_to_string (GMimeSignatureStatus x) +{ + switch (x) { + case GMIME_SIGNATURE_STATUS_GOOD: + return "good"; + case GMIME_SIGNATURE_STATUS_BAD: + return "bad"; + case GMIME_SIGNATURE_STATUS_ERROR: + return "error"; + } + return "unknown"; +} +#else static const char* signer_status_to_string (GMimeSignerStatus x) { @@ -501,6 +520,7 @@ signer_status_to_string (GMimeSignerStatus x) } return "unknown"; } +#endif static void format_part_start_text (GMimeObject *part, int *part_count) @@ -592,6 +612,73 @@ format_part_encstatus_json (int status) printf ("}]"); } +#ifdef GMIME_ATLEAST_26 +static void +format_part_sigstatus_json (GMimeSignatureList *siglist) +{ + printf (", \"sigstatus\": ["); + + if (!siglist) { + printf ("]"); + return; + } + + void *ctx_quote = talloc_new (NULL); + int i; + for (i = 0; i < g_mime_signature_list_length (siglist); i++) { + GMimeSignature *signature = g_mime_signature_list_get_signature (siglist, i); + + if (i > 0) + printf (", "); + + printf ("{"); + + /* status */ + GMimeSignatureStatus status = g_mime_signature_get_status (signature); + printf ("\"status\": %s", + json_quote_str (ctx_quote, + signature_status_to_string (status))); + + GMimeCertificate *certificate = g_mime_signature_get_certificate (signature); + if (status == GMIME_SIGNATURE_STATUS_GOOD) { + if (certificate) + printf (", \"fingerprint\": %s", json_quote_str (ctx_quote, g_mime_certificate_get_fingerprint (certificate))); + /* these dates are seconds since the epoch; should we + * provide a more human-readable format string? */ + time_t created = g_mime_signature_get_created (signature); + if (created != -1) + printf (", \"created\": %d", (int) created); + time_t expires = g_mime_signature_get_expires (signature); + if (expires > 0) + printf (", \"expires\": %d", (int) expires); + /* output user id only if validity is FULL or ULTIMATE. */ + /* note that gmime is using the term "trust" here, which + * is WRONG. It's actually user id "validity". */ + if (certificate) { + const char *name = g_mime_certificate_get_name (certificate); + GMimeCertificateTrust trust = g_mime_certificate_get_trust (certificate); + if (name && (trust == GMIME_CERTIFICATE_TRUST_FULLY || trust == GMIME_CERTIFICATE_TRUST_ULTIMATE)) + printf (", \"userid\": %s", json_quote_str (ctx_quote, name)); + } + } else if (certificate) { + const char *key_id = g_mime_certificate_get_key_id (certificate); + if (key_id) + printf (", \"keyid\": %s", json_quote_str (ctx_quote, key_id)); + } + + GMimeSignatureError errors = g_mime_signature_get_errors (signature); + if (errors != GMIME_SIGNATURE_ERROR_NONE) { + printf (", \"errors\": %d", errors); + } + + printf ("}"); + } + + printf ("]"); + + talloc_free (ctx_quote); +} +#else static void format_part_sigstatus_json (const GMimeSignatureValidity* validity) { @@ -652,6 +739,7 @@ format_part_sigstatus_json (const GMimeSignatureValidity* validity) talloc_free (ctx_quote); } +#endif static void format_part_content_json (GMimeObject *part) @@ -990,13 +1078,20 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[])) } else if ((STRNCMP_LITERAL (argv[i], "--verify") == 0) || (STRNCMP_LITERAL (argv[i], "--decrypt") == 0)) { if (params.cryptoctx == NULL) { +#ifdef GMIME_ATLEAST_26 + /* TODO: GMimePasswordRequestFunc */ + if (NULL == (params.cryptoctx = g_mime_gpg_context_new(NULL, "gpg"))) +#else GMimeSession* session = g_object_new(g_mime_session_get_type(), NULL); if (NULL == (params.cryptoctx = g_mime_gpg_context_new(session, "gpg"))) +#endif fprintf (stderr, "Failed to construct gpg context.\n"); else g_mime_gpg_context_set_always_trust((GMimeGpgContext*)params.cryptoctx, FALSE); +#ifndef GMIME_ATLEAST_26 g_object_unref (session); session = NULL; +#endif } if (STRNCMP_LITERAL (argv[i], "--decrypt") == 0) params.decrypt = 1; diff --git a/show-message.c b/show-message.c index 8768889..83ecf81 100644 --- a/show-message.c +++ b/show-message.c @@ -48,7 +48,11 @@ show_message_part (mime_node_t *node, format->part_encstatus (node->decrypt_success); if (node->verify_attempted && format->part_sigstatus) +#ifdef GMIME_ATLEAST_26 + format->part_sigstatus (node->sig_list); +#else format->part_sigstatus (node->sig_validity); +#endif format->part_content (part); diff --git a/test/crypto b/test/crypto index 0af4aa8..3779abc 100755 --- a/test/crypto +++ b/test/crypto @@ -104,6 +104,8 @@ test_expect_equal \ "$expected" test_begin_subtest "signature verification with signer key unavailable" +# this is broken with current versions of gmime-2.6 +(ldd $(which notmuch) | grep -q gmime-2.6) && test_subtest_known_broken # move the gnupghome temporarily out of the way mv "${GNUPGHOME}"{,.bak} output=$(notmuch show --format=json --verify subject:"test signed message 001" \ -- 1.7.8.4 ^ permalink raw reply related [flat|nested] 42+ messages in thread
* Re: [PATCH v3 2/2] Add compatibility with gmime 2.6 2012-01-20 0:06 ` [PATCH v3 2/2] Add compatibility with gmime 2.6 Thomas Jost @ 2012-01-20 4:10 ` Austin Clements 2012-01-20 9:37 ` Thomas Jost 0 siblings, 1 reply; 42+ messages in thread From: Austin Clements @ 2012-01-20 4:10 UTC (permalink / raw) To: Thomas Jost; +Cc: notmuch Nearly there. A few more small comments. Quoth Thomas Jost on Jan 20 at 1:06 am: > There are lots of API changes in gmime 2.6 crypto handling. By adding > preprocessor directives, it is however possible to add gmime 2.6 compatibility > while preserving compatibility with gmime 2.4 too. > > This is mostly based on id:"8762i8hrb9.fsf@bookbinder.fernseed.info". > > This was tested against both gmime 2.6.4 and 2.4.31. With gmime 2.4.31, the > crypto tests all work fine (as expected). With gmime 2.6.4, one crypto test is > currently broken (signature verification with signer key unavailable), most > likely because of a bug in gmime which will hopefully be fixed in a future > version. > --- > mime-node.c | 60 ++++++++++++++++++++++++++++++++-- > notmuch-client.h | 30 ++++++++++++++++- > notmuch-reply.c | 7 ++++ > notmuch-show.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ > show-message.c | 4 ++ > test/crypto | 2 + > 6 files changed, 192 insertions(+), 6 deletions(-) > > diff --git a/mime-node.c b/mime-node.c > index d26bb44..ad19f5e 100644 > --- a/mime-node.c > +++ b/mime-node.c > @@ -33,7 +33,11 @@ typedef struct mime_node_context { > GMimeMessage *mime_message; > > /* Context provided by the caller. */ > +#ifdef GMIME_ATLEAST_26 > + GMimeCryptoContext *cryptoctx; > +#else > GMimeCipherContext *cryptoctx; > +#endif > notmuch_bool_t decrypt; > } mime_node_context_t; > > @@ -57,8 +61,12 @@ _mime_node_context_free (mime_node_context_t *res) > > notmuch_status_t > mime_node_open (const void *ctx, notmuch_message_t *message, > - GMimeCipherContext *cryptoctx, notmuch_bool_t decrypt, > - mime_node_t **root_out) > +#ifdef GMIME_ATLEAST_26 > + GMimeCryptoContext *cryptoctx, > +#else > + GMimeCipherContext *cryptoctx, > +#endif > + notmuch_bool_t decrypt, mime_node_t **root_out) > { > const char *filename = notmuch_message_get_filename (message); > mime_node_context_t *mctx; > @@ -112,12 +120,21 @@ DONE: > return status; > } > > +#ifdef GMIME_ATLEAST_26 > +static int > +_signature_list_free (GMimeSignatureList **proxy) > +{ > + g_object_unref (*proxy); > + return 0; > +} > +#else > static int > _signature_validity_free (GMimeSignatureValidity **proxy) > { > g_mime_signature_validity_free (*proxy); > return 0; > } > +#endif > > static mime_node_t * > _mime_node_create (const mime_node_t *parent, GMimeObject *part) > @@ -165,11 +182,24 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) > GMimeMultipartEncrypted *encrypteddata = > GMIME_MULTIPART_ENCRYPTED (part); > node->decrypt_attempted = TRUE; > +#ifdef GMIME_ATLEAST_26 > + GMimeDecryptResult *decrypt_result = NULL; > + node->decrypted_child = g_mime_multipart_encrypted_decrypt > + (encrypteddata, node->ctx->cryptoctx, &decrypt_result, &err); > +#else > node->decrypted_child = g_mime_multipart_encrypted_decrypt > (encrypteddata, node->ctx->cryptoctx, &err); > +#endif > if (node->decrypted_child) { > node->decrypt_success = node->verify_attempted = TRUE; > +#ifdef GMIME_ATLEAST_26 > + /* This may be NULL if the part is not signed. */ > + node->sig_list = g_mime_decrypt_result_get_signatures (decrypt_result); > + g_object_ref (node->sig_list); Apparently you can't g_object_ref NULL, so there should be a conditional around this. (Does g_mime_decrypt_result_get_signatures *really* return NULL for an empty list? I feel like various tests should have failed in various versions of this patch if it did.) > + g_object_unref (decrypt_result); > +#else > node->sig_validity = g_mime_multipart_encrypted_get_signature_validity (encrypteddata); > +#endif > } else { > fprintf (stderr, "Failed to decrypt part: %s\n", > (err ? err->message : "no error explanation given")); > @@ -182,6 +212,15 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) > "(must be exactly 2)\n", > node->nchildren); > } else { > +#ifdef GMIME_ATLEAST_26 > + node->sig_list = g_mime_multipart_signed_verify > + (GMIME_MULTIPART_SIGNED (part), node->ctx->cryptoctx, &err); > + node->verify_attempted = TRUE; > + > + if (!node->sig_list) > + fprintf (stderr, "Failed to verify signed part: %s\n", > + (err ? err->message : "no error explanation given")); > +#else > /* For some reason the GMimeSignatureValidity returned > * here is not a const (inconsistent with that > * returned by > @@ -195,17 +234,30 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) > node->verify_attempted = TRUE; > node->sig_validity = sig_validity; > if (sig_validity) { > - GMimeSignatureValidity **proxy = > - talloc (node, GMimeSignatureValidity *); > + GMimeSignatureValidity **proxy = talloc (node, GMimeSignatureValidity *); (See below) > *proxy = sig_validity; > talloc_set_destructor (proxy, _signature_validity_free); > } > +#endif > } > } > > +#ifdef GMIME_ATLEAST_26 > + /* sig_list may be created in both above cases, so we need to > + * cleanly handle it here. */ > + if (node->sig_list) { > + GMimeSignatureList **proxy = > + talloc (node, GMimeSignatureList *); Oops. I think you un-split the wrong line. The one up above is now too long and this one is still unnecessarily split. > + *proxy = node->sig_list; > + talloc_set_destructor (proxy, _signature_list_free); > + } > +#endif > + > +#ifndef GMIME_ATLEAST_26 > if (node->verify_attempted && !node->sig_validity) > fprintf (stderr, "Failed to verify signed part: %s\n", > (err ? err->message : "no error explanation given")); > +#endif > > if (err) > g_error_free (err); > diff --git a/notmuch-client.h b/notmuch-client.h > index 62ede28..9c1d383 100644 > --- a/notmuch-client.h > +++ b/notmuch-client.h > @@ -30,6 +30,14 @@ > > #include <gmime/gmime.h> > > +/* GMIME_CHECK_VERSION in gmime 2.4 is not usable from the > + * preprocessor (it calls a runtime function). But since > + * GMIME_MAJOR_VERSION and friends were added in gmime 2.6, we can use > + * these to check the version number. */ > +#ifdef GMIME_MAJOR_VERSION > +#define GMIME_ATLEAST_26 > +#endif > + > #include "notmuch.h" > > /* This is separate from notmuch-private.h because we're trying to > @@ -69,7 +77,11 @@ typedef struct notmuch_show_format { > void (*part_start) (GMimeObject *part, > int *part_count); > void (*part_encstatus) (int status); > +#ifdef GMIME_ATLEAST_26 > + void (*part_sigstatus) (GMimeSignatureList* siglist); > +#else > void (*part_sigstatus) (const GMimeSignatureValidity* validity); > +#endif > void (*part_content) (GMimeObject *part); > void (*part_end) (GMimeObject *part); > const char *part_sep; > @@ -83,7 +95,11 @@ typedef struct notmuch_show_params { > int entire_thread; > int raw; > int part; > +#ifdef GMIME_ATLEAST_26 > + GMimeCryptoContext* cryptoctx; > +#else > GMimeCipherContext* cryptoctx; > +#endif > int decrypt; > } notmuch_show_params_t; > > @@ -290,11 +306,17 @@ typedef struct mime_node { > > /* True if signature verification on this part was attempted. */ > notmuch_bool_t verify_attempted; > +#ifdef GMIME_ATLEAST_26 > + /* The list of signatures for signed or encrypted containers. If > + * there are no signatures, this will be NULL. */ > + GMimeSignatureList* sig_list; > +#else > /* For signed or encrypted containers, the validity of the > * signature. May be NULL if signature verification failed. If > * there are simply no signatures, this will be non-NULL with an > * empty signers list. */ > const GMimeSignatureValidity *sig_validity; > +#endif > > /* Internal: Context inherited from the root iterator. */ > struct mime_node_context *ctx; > @@ -319,8 +341,12 @@ typedef struct mime_node { > */ > notmuch_status_t > mime_node_open (const void *ctx, notmuch_message_t *message, > - GMimeCipherContext *cryptoctx, notmuch_bool_t decrypt, > - mime_node_t **node_out); > +#ifdef GMIME_ATLEAST_26 > + GMimeCryptoContext *cryptoctx, > +#else > + GMimeCipherContext *cryptoctx, > +#endif > + notmuch_bool_t decrypt, mime_node_t **node_out); > > /* Return a new MIME node for the requested child part of parent. > * parent will be used as the talloc context for the returned child > diff --git a/notmuch-reply.c b/notmuch-reply.c > index 0f682db..bf67960 100644 > --- a/notmuch-reply.c > +++ b/notmuch-reply.c > @@ -688,15 +688,22 @@ notmuch_reply_command (void *ctx, int argc, char *argv[]) > reply_format_func = notmuch_reply_format_default; > > if (decrypt) { > +#ifdef GMIME_ATLEAST_26 > + /* TODO: GMimePasswordRequestFunc */ > + params.cryptoctx = g_mime_gpg_context_new (NULL, "gpg"); > +#else > GMimeSession* session = g_object_new (g_mime_session_get_type(), NULL); > params.cryptoctx = g_mime_gpg_context_new (session, "gpg"); > +#endif > if (params.cryptoctx) { > g_mime_gpg_context_set_always_trust ((GMimeGpgContext*) params.cryptoctx, FALSE); > params.decrypt = TRUE; > } else { > fprintf (stderr, "Failed to construct gpg context.\n"); > } > +#ifndef GMIME_ATLEAST_26 > g_object_unref (session); > +#endif > } > > config = notmuch_config_open (ctx, NULL, NULL); > diff --git a/notmuch-show.c b/notmuch-show.c > index 91f566c..190210c 100644 > --- a/notmuch-show.c > +++ b/notmuch-show.c > @@ -76,7 +76,11 @@ static void > format_part_encstatus_json (int status); > > static void > +#ifdef GMIME_ATLEAST_26 > +format_part_sigstatus_json (GMimeSignatureList* siglist); > +#else > format_part_sigstatus_json (const GMimeSignatureValidity* validity); > +#endif > > static void > format_part_content_json (GMimeObject *part); > @@ -486,6 +490,21 @@ show_text_part_content (GMimeObject *part, GMimeStream *stream_out) > g_object_unref(stream_filter); > } > > +#ifdef GMIME_ATLEAST_26 > +static const char* > +signature_status_to_string (GMimeSignatureStatus x) > +{ > + switch (x) { > + case GMIME_SIGNATURE_STATUS_GOOD: > + return "good"; > + case GMIME_SIGNATURE_STATUS_BAD: > + return "bad"; > + case GMIME_SIGNATURE_STATUS_ERROR: > + return "error"; > + } > + return "unknown"; > +} > +#else > static const char* > signer_status_to_string (GMimeSignerStatus x) > { > @@ -501,6 +520,7 @@ signer_status_to_string (GMimeSignerStatus x) > } > return "unknown"; > } > +#endif > > static void > format_part_start_text (GMimeObject *part, int *part_count) > @@ -592,6 +612,73 @@ format_part_encstatus_json (int status) > printf ("}]"); > } > > +#ifdef GMIME_ATLEAST_26 > +static void > +format_part_sigstatus_json (GMimeSignatureList *siglist) > +{ > + printf (", \"sigstatus\": ["); > + > + if (!siglist) { > + printf ("]"); > + return; > + } > + > + void *ctx_quote = talloc_new (NULL); > + int i; > + for (i = 0; i < g_mime_signature_list_length (siglist); i++) { > + GMimeSignature *signature = g_mime_signature_list_get_signature (siglist, i); > + > + if (i > 0) > + printf (", "); > + > + printf ("{"); > + > + /* status */ > + GMimeSignatureStatus status = g_mime_signature_get_status (signature); > + printf ("\"status\": %s", > + json_quote_str (ctx_quote, > + signature_status_to_string (status))); > + > + GMimeCertificate *certificate = g_mime_signature_get_certificate (signature); > + if (status == GMIME_SIGNATURE_STATUS_GOOD) { > + if (certificate) > + printf (", \"fingerprint\": %s", json_quote_str (ctx_quote, g_mime_certificate_get_fingerprint (certificate))); > + /* these dates are seconds since the epoch; should we > + * provide a more human-readable format string? */ > + time_t created = g_mime_signature_get_created (signature); > + if (created != -1) > + printf (", \"created\": %d", (int) created); > + time_t expires = g_mime_signature_get_expires (signature); > + if (expires > 0) > + printf (", \"expires\": %d", (int) expires); > + /* output user id only if validity is FULL or ULTIMATE. */ > + /* note that gmime is using the term "trust" here, which > + * is WRONG. It's actually user id "validity". */ > + if (certificate) { > + const char *name = g_mime_certificate_get_name (certificate); > + GMimeCertificateTrust trust = g_mime_certificate_get_trust (certificate); > + if (name && (trust == GMIME_CERTIFICATE_TRUST_FULLY || trust == GMIME_CERTIFICATE_TRUST_ULTIMATE)) > + printf (", \"userid\": %s", json_quote_str (ctx_quote, name)); > + } > + } else if (certificate) { > + const char *key_id = g_mime_certificate_get_key_id (certificate); > + if (key_id) > + printf (", \"keyid\": %s", json_quote_str (ctx_quote, key_id)); > + } > + > + GMimeSignatureError errors = g_mime_signature_get_errors (signature); > + if (errors != GMIME_SIGNATURE_ERROR_NONE) { > + printf (", \"errors\": %d", errors); > + } > + > + printf ("}"); > + } > + > + printf ("]"); > + > + talloc_free (ctx_quote); > +} > +#else > static void > format_part_sigstatus_json (const GMimeSignatureValidity* validity) > { > @@ -652,6 +739,7 @@ format_part_sigstatus_json (const GMimeSignatureValidity* validity) > > talloc_free (ctx_quote); > } > +#endif > > static void > format_part_content_json (GMimeObject *part) > @@ -990,13 +1078,20 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[])) > } else if ((STRNCMP_LITERAL (argv[i], "--verify") == 0) || > (STRNCMP_LITERAL (argv[i], "--decrypt") == 0)) { > if (params.cryptoctx == NULL) { > +#ifdef GMIME_ATLEAST_26 > + /* TODO: GMimePasswordRequestFunc */ > + if (NULL == (params.cryptoctx = g_mime_gpg_context_new(NULL, "gpg"))) > +#else > GMimeSession* session = g_object_new(g_mime_session_get_type(), NULL); > if (NULL == (params.cryptoctx = g_mime_gpg_context_new(session, "gpg"))) > +#endif > fprintf (stderr, "Failed to construct gpg context.\n"); > else > g_mime_gpg_context_set_always_trust((GMimeGpgContext*)params.cryptoctx, FALSE); > +#ifndef GMIME_ATLEAST_26 > g_object_unref (session); > session = NULL; > +#endif > } > if (STRNCMP_LITERAL (argv[i], "--decrypt") == 0) > params.decrypt = 1; > diff --git a/show-message.c b/show-message.c > index 8768889..83ecf81 100644 > --- a/show-message.c > +++ b/show-message.c > @@ -48,7 +48,11 @@ show_message_part (mime_node_t *node, > format->part_encstatus (node->decrypt_success); > > if (node->verify_attempted && format->part_sigstatus) > +#ifdef GMIME_ATLEAST_26 > + format->part_sigstatus (node->sig_list); > +#else > format->part_sigstatus (node->sig_validity); > +#endif > > format->part_content (part); > > diff --git a/test/crypto b/test/crypto > index 0af4aa8..3779abc 100755 > --- a/test/crypto > +++ b/test/crypto > @@ -104,6 +104,8 @@ test_expect_equal \ > "$expected" > > test_begin_subtest "signature verification with signer key unavailable" > +# this is broken with current versions of gmime-2.6 > +(ldd $(which notmuch) | grep -q gmime-2.6) && test_subtest_known_broken Just to be nitpicky, you should either escape the . in the regexp or pass -F to grep. Otherwise I think this hack is fine (though it might have to get a little fancier once GMime fixes this bug). > # move the gnupghome temporarily out of the way > mv "${GNUPGHOME}"{,.bak} > output=$(notmuch show --format=json --verify subject:"test signed message 001" \ ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 2/2] Add compatibility with gmime 2.6 2012-01-20 4:10 ` Austin Clements @ 2012-01-20 9:37 ` Thomas Jost 2012-01-20 9:39 ` [PATCH v4 1/3] show: don't use hex literals in JSON output Thomas Jost 0 siblings, 1 reply; 42+ messages in thread From: Thomas Jost @ 2012-01-20 9:37 UTC (permalink / raw) To: Austin Clements; +Cc: notmuch [-- Attachment #1: Type: text/plain, Size: 18802 bytes --] On Thu, 19 Jan 2012 23:10:44 -0500, Austin Clements <amdragon@MIT.EDU> wrote: > Nearly there. A few more small comments. Thanks again :) Will post new version soon, including a new patch to update NEWS and INSTALL. > Quoth Thomas Jost on Jan 20 at 1:06 am: > > There are lots of API changes in gmime 2.6 crypto handling. By adding > > preprocessor directives, it is however possible to add gmime 2.6 compatibility > > while preserving compatibility with gmime 2.4 too. > > > > This is mostly based on id:"8762i8hrb9.fsf@bookbinder.fernseed.info". > > > > This was tested against both gmime 2.6.4 and 2.4.31. With gmime 2.4.31, the > > crypto tests all work fine (as expected). With gmime 2.6.4, one crypto test is > > currently broken (signature verification with signer key unavailable), most > > likely because of a bug in gmime which will hopefully be fixed in a future > > version. > > --- > > mime-node.c | 60 ++++++++++++++++++++++++++++++++-- > > notmuch-client.h | 30 ++++++++++++++++- > > notmuch-reply.c | 7 ++++ > > notmuch-show.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ > > show-message.c | 4 ++ > > test/crypto | 2 + > > 6 files changed, 192 insertions(+), 6 deletions(-) > > > > diff --git a/mime-node.c b/mime-node.c > > index d26bb44..ad19f5e 100644 > > --- a/mime-node.c > > +++ b/mime-node.c > > @@ -33,7 +33,11 @@ typedef struct mime_node_context { > > GMimeMessage *mime_message; > > > > /* Context provided by the caller. */ > > +#ifdef GMIME_ATLEAST_26 > > + GMimeCryptoContext *cryptoctx; > > +#else > > GMimeCipherContext *cryptoctx; > > +#endif > > notmuch_bool_t decrypt; > > } mime_node_context_t; > > > > @@ -57,8 +61,12 @@ _mime_node_context_free (mime_node_context_t *res) > > > > notmuch_status_t > > mime_node_open (const void *ctx, notmuch_message_t *message, > > - GMimeCipherContext *cryptoctx, notmuch_bool_t decrypt, > > - mime_node_t **root_out) > > +#ifdef GMIME_ATLEAST_26 > > + GMimeCryptoContext *cryptoctx, > > +#else > > + GMimeCipherContext *cryptoctx, > > +#endif > > + notmuch_bool_t decrypt, mime_node_t **root_out) > > { > > const char *filename = notmuch_message_get_filename (message); > > mime_node_context_t *mctx; > > @@ -112,12 +120,21 @@ DONE: > > return status; > > } > > > > +#ifdef GMIME_ATLEAST_26 > > +static int > > +_signature_list_free (GMimeSignatureList **proxy) > > +{ > > + g_object_unref (*proxy); > > + return 0; > > +} > > +#else > > static int > > _signature_validity_free (GMimeSignatureValidity **proxy) > > { > > g_mime_signature_validity_free (*proxy); > > return 0; > > } > > +#endif > > > > static mime_node_t * > > _mime_node_create (const mime_node_t *parent, GMimeObject *part) > > @@ -165,11 +182,24 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) > > GMimeMultipartEncrypted *encrypteddata = > > GMIME_MULTIPART_ENCRYPTED (part); > > node->decrypt_attempted = TRUE; > > +#ifdef GMIME_ATLEAST_26 > > + GMimeDecryptResult *decrypt_result = NULL; > > + node->decrypted_child = g_mime_multipart_encrypted_decrypt > > + (encrypteddata, node->ctx->cryptoctx, &decrypt_result, &err); > > +#else > > node->decrypted_child = g_mime_multipart_encrypted_decrypt > > (encrypteddata, node->ctx->cryptoctx, &err); > > +#endif > > if (node->decrypted_child) { > > node->decrypt_success = node->verify_attempted = TRUE; > > +#ifdef GMIME_ATLEAST_26 > > + /* This may be NULL if the part is not signed. */ > > + node->sig_list = g_mime_decrypt_result_get_signatures (decrypt_result); > > + g_object_ref (node->sig_list); > > Apparently you can't g_object_ref NULL, so there should be a > conditional around this. Right, added. Thanks. > (Does g_mime_decrypt_result_get_signatures *really* return NULL for an > empty list? I feel like various tests should have failed in various > versions of this patch if it did.) Yes it does. I added some fprintf() in gmime (in gpg_decrypt() and g_mime_decrypt_result_get_signatures()). Decryption tests (after "emacs delivery of encrypted message with attachment") clearly show that g_mime_decrypt_result_get_signatures returns NULL when there's no signatures in an encrypted part. I guess the reason why tests did not fail is that format_part_sigstatus_json just displays an empty JSON array if sig_list is NULL. And that's also what's expected when an encrypted part has no signature. I had a quick look at the GObject code; apparently if you pass NULL to g_object_ref (or anything that is not a GObject) it will just return NULL. So that's why this one did not fail either. > > > + g_object_unref (decrypt_result); > > +#else > > node->sig_validity = g_mime_multipart_encrypted_get_signature_validity (encrypteddata); > > +#endif > > } else { > > fprintf (stderr, "Failed to decrypt part: %s\n", > > (err ? err->message : "no error explanation given")); > > @@ -182,6 +212,15 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) > > "(must be exactly 2)\n", > > node->nchildren); > > } else { > > +#ifdef GMIME_ATLEAST_26 > > + node->sig_list = g_mime_multipart_signed_verify > > + (GMIME_MULTIPART_SIGNED (part), node->ctx->cryptoctx, &err); > > + node->verify_attempted = TRUE; > > + > > + if (!node->sig_list) > > + fprintf (stderr, "Failed to verify signed part: %s\n", > > + (err ? err->message : "no error explanation given")); > > +#else > > /* For some reason the GMimeSignatureValidity returned > > * here is not a const (inconsistent with that > > * returned by > > @@ -195,17 +234,30 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) > > node->verify_attempted = TRUE; > > node->sig_validity = sig_validity; > > if (sig_validity) { > > - GMimeSignatureValidity **proxy = > > - talloc (node, GMimeSignatureValidity *); > > + GMimeSignatureValidity **proxy = talloc (node, GMimeSignatureValidity *); > > (See below) > > > *proxy = sig_validity; > > talloc_set_destructor (proxy, _signature_validity_free); > > } > > +#endif > > } > > } > > > > +#ifdef GMIME_ATLEAST_26 > > + /* sig_list may be created in both above cases, so we need to > > + * cleanly handle it here. */ > > + if (node->sig_list) { > > + GMimeSignatureList **proxy = > > + talloc (node, GMimeSignatureList *); > > Oops. I think you un-split the wrong line. The one up above is now > too long and this one is still unnecessarily split. Argh. Sorry about that. (I guess hacking while watching DS9 is not a good idea: too distracting. Won't do it again until next time.) > > > + *proxy = node->sig_list; > > + talloc_set_destructor (proxy, _signature_list_free); > > + } > > +#endif > > + > > +#ifndef GMIME_ATLEAST_26 > > if (node->verify_attempted && !node->sig_validity) > > fprintf (stderr, "Failed to verify signed part: %s\n", > > (err ? err->message : "no error explanation given")); > > +#endif > > > > if (err) > > g_error_free (err); > > diff --git a/notmuch-client.h b/notmuch-client.h > > index 62ede28..9c1d383 100644 > > --- a/notmuch-client.h > > +++ b/notmuch-client.h > > @@ -30,6 +30,14 @@ > > > > #include <gmime/gmime.h> > > > > +/* GMIME_CHECK_VERSION in gmime 2.4 is not usable from the > > + * preprocessor (it calls a runtime function). But since > > + * GMIME_MAJOR_VERSION and friends were added in gmime 2.6, we can use > > + * these to check the version number. */ > > +#ifdef GMIME_MAJOR_VERSION > > +#define GMIME_ATLEAST_26 > > +#endif > > + > > #include "notmuch.h" > > > > /* This is separate from notmuch-private.h because we're trying to > > @@ -69,7 +77,11 @@ typedef struct notmuch_show_format { > > void (*part_start) (GMimeObject *part, > > int *part_count); > > void (*part_encstatus) (int status); > > +#ifdef GMIME_ATLEAST_26 > > + void (*part_sigstatus) (GMimeSignatureList* siglist); > > +#else > > void (*part_sigstatus) (const GMimeSignatureValidity* validity); > > +#endif > > void (*part_content) (GMimeObject *part); > > void (*part_end) (GMimeObject *part); > > const char *part_sep; > > @@ -83,7 +95,11 @@ typedef struct notmuch_show_params { > > int entire_thread; > > int raw; > > int part; > > +#ifdef GMIME_ATLEAST_26 > > + GMimeCryptoContext* cryptoctx; > > +#else > > GMimeCipherContext* cryptoctx; > > +#endif > > int decrypt; > > } notmuch_show_params_t; > > > > @@ -290,11 +306,17 @@ typedef struct mime_node { > > > > /* True if signature verification on this part was attempted. */ > > notmuch_bool_t verify_attempted; > > +#ifdef GMIME_ATLEAST_26 > > + /* The list of signatures for signed or encrypted containers. If > > + * there are no signatures, this will be NULL. */ > > + GMimeSignatureList* sig_list; > > +#else > > /* For signed or encrypted containers, the validity of the > > * signature. May be NULL if signature verification failed. If > > * there are simply no signatures, this will be non-NULL with an > > * empty signers list. */ > > const GMimeSignatureValidity *sig_validity; > > +#endif > > > > /* Internal: Context inherited from the root iterator. */ > > struct mime_node_context *ctx; > > @@ -319,8 +341,12 @@ typedef struct mime_node { > > */ > > notmuch_status_t > > mime_node_open (const void *ctx, notmuch_message_t *message, > > - GMimeCipherContext *cryptoctx, notmuch_bool_t decrypt, > > - mime_node_t **node_out); > > +#ifdef GMIME_ATLEAST_26 > > + GMimeCryptoContext *cryptoctx, > > +#else > > + GMimeCipherContext *cryptoctx, > > +#endif > > + notmuch_bool_t decrypt, mime_node_t **node_out); > > > > /* Return a new MIME node for the requested child part of parent. > > * parent will be used as the talloc context for the returned child > > diff --git a/notmuch-reply.c b/notmuch-reply.c > > index 0f682db..bf67960 100644 > > --- a/notmuch-reply.c > > +++ b/notmuch-reply.c > > @@ -688,15 +688,22 @@ notmuch_reply_command (void *ctx, int argc, char *argv[]) > > reply_format_func = notmuch_reply_format_default; > > > > if (decrypt) { > > +#ifdef GMIME_ATLEAST_26 > > + /* TODO: GMimePasswordRequestFunc */ > > + params.cryptoctx = g_mime_gpg_context_new (NULL, "gpg"); > > +#else > > GMimeSession* session = g_object_new (g_mime_session_get_type(), NULL); > > params.cryptoctx = g_mime_gpg_context_new (session, "gpg"); > > +#endif > > if (params.cryptoctx) { > > g_mime_gpg_context_set_always_trust ((GMimeGpgContext*) params.cryptoctx, FALSE); > > params.decrypt = TRUE; > > } else { > > fprintf (stderr, "Failed to construct gpg context.\n"); > > } > > +#ifndef GMIME_ATLEAST_26 > > g_object_unref (session); > > +#endif > > } > > > > config = notmuch_config_open (ctx, NULL, NULL); > > diff --git a/notmuch-show.c b/notmuch-show.c > > index 91f566c..190210c 100644 > > --- a/notmuch-show.c > > +++ b/notmuch-show.c > > @@ -76,7 +76,11 @@ static void > > format_part_encstatus_json (int status); > > > > static void > > +#ifdef GMIME_ATLEAST_26 > > +format_part_sigstatus_json (GMimeSignatureList* siglist); > > +#else > > format_part_sigstatus_json (const GMimeSignatureValidity* validity); > > +#endif > > > > static void > > format_part_content_json (GMimeObject *part); > > @@ -486,6 +490,21 @@ show_text_part_content (GMimeObject *part, GMimeStream *stream_out) > > g_object_unref(stream_filter); > > } > > > > +#ifdef GMIME_ATLEAST_26 > > +static const char* > > +signature_status_to_string (GMimeSignatureStatus x) > > +{ > > + switch (x) { > > + case GMIME_SIGNATURE_STATUS_GOOD: > > + return "good"; > > + case GMIME_SIGNATURE_STATUS_BAD: > > + return "bad"; > > + case GMIME_SIGNATURE_STATUS_ERROR: > > + return "error"; > > + } > > + return "unknown"; > > +} > > +#else > > static const char* > > signer_status_to_string (GMimeSignerStatus x) > > { > > @@ -501,6 +520,7 @@ signer_status_to_string (GMimeSignerStatus x) > > } > > return "unknown"; > > } > > +#endif > > > > static void > > format_part_start_text (GMimeObject *part, int *part_count) > > @@ -592,6 +612,73 @@ format_part_encstatus_json (int status) > > printf ("}]"); > > } > > > > +#ifdef GMIME_ATLEAST_26 > > +static void > > +format_part_sigstatus_json (GMimeSignatureList *siglist) > > +{ > > + printf (", \"sigstatus\": ["); > > + > > + if (!siglist) { > > + printf ("]"); > > + return; > > + } > > + > > + void *ctx_quote = talloc_new (NULL); > > + int i; > > + for (i = 0; i < g_mime_signature_list_length (siglist); i++) { > > + GMimeSignature *signature = g_mime_signature_list_get_signature (siglist, i); > > + > > + if (i > 0) > > + printf (", "); > > + > > + printf ("{"); > > + > > + /* status */ > > + GMimeSignatureStatus status = g_mime_signature_get_status (signature); > > + printf ("\"status\": %s", > > + json_quote_str (ctx_quote, > > + signature_status_to_string (status))); > > + > > + GMimeCertificate *certificate = g_mime_signature_get_certificate (signature); > > + if (status == GMIME_SIGNATURE_STATUS_GOOD) { > > + if (certificate) > > + printf (", \"fingerprint\": %s", json_quote_str (ctx_quote, g_mime_certificate_get_fingerprint (certificate))); > > + /* these dates are seconds since the epoch; should we > > + * provide a more human-readable format string? */ > > + time_t created = g_mime_signature_get_created (signature); > > + if (created != -1) > > + printf (", \"created\": %d", (int) created); > > + time_t expires = g_mime_signature_get_expires (signature); > > + if (expires > 0) > > + printf (", \"expires\": %d", (int) expires); > > + /* output user id only if validity is FULL or ULTIMATE. */ > > + /* note that gmime is using the term "trust" here, which > > + * is WRONG. It's actually user id "validity". */ > > + if (certificate) { > > + const char *name = g_mime_certificate_get_name (certificate); > > + GMimeCertificateTrust trust = g_mime_certificate_get_trust (certificate); > > + if (name && (trust == GMIME_CERTIFICATE_TRUST_FULLY || trust == GMIME_CERTIFICATE_TRUST_ULTIMATE)) > > + printf (", \"userid\": %s", json_quote_str (ctx_quote, name)); > > + } > > + } else if (certificate) { > > + const char *key_id = g_mime_certificate_get_key_id (certificate); > > + if (key_id) > > + printf (", \"keyid\": %s", json_quote_str (ctx_quote, key_id)); > > + } > > + > > + GMimeSignatureError errors = g_mime_signature_get_errors (signature); > > + if (errors != GMIME_SIGNATURE_ERROR_NONE) { > > + printf (", \"errors\": %d", errors); > > + } > > + > > + printf ("}"); > > + } > > + > > + printf ("]"); > > + > > + talloc_free (ctx_quote); > > +} > > +#else > > static void > > format_part_sigstatus_json (const GMimeSignatureValidity* validity) > > { > > @@ -652,6 +739,7 @@ format_part_sigstatus_json (const GMimeSignatureValidity* validity) > > > > talloc_free (ctx_quote); > > } > > +#endif > > > > static void > > format_part_content_json (GMimeObject *part) > > @@ -990,13 +1078,20 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[])) > > } else if ((STRNCMP_LITERAL (argv[i], "--verify") == 0) || > > (STRNCMP_LITERAL (argv[i], "--decrypt") == 0)) { > > if (params.cryptoctx == NULL) { > > +#ifdef GMIME_ATLEAST_26 > > + /* TODO: GMimePasswordRequestFunc */ > > + if (NULL == (params.cryptoctx = g_mime_gpg_context_new(NULL, "gpg"))) > > +#else > > GMimeSession* session = g_object_new(g_mime_session_get_type(), NULL); > > if (NULL == (params.cryptoctx = g_mime_gpg_context_new(session, "gpg"))) > > +#endif > > fprintf (stderr, "Failed to construct gpg context.\n"); > > else > > g_mime_gpg_context_set_always_trust((GMimeGpgContext*)params.cryptoctx, FALSE); > > +#ifndef GMIME_ATLEAST_26 > > g_object_unref (session); > > session = NULL; > > +#endif > > } > > if (STRNCMP_LITERAL (argv[i], "--decrypt") == 0) > > params.decrypt = 1; > > diff --git a/show-message.c b/show-message.c > > index 8768889..83ecf81 100644 > > --- a/show-message.c > > +++ b/show-message.c > > @@ -48,7 +48,11 @@ show_message_part (mime_node_t *node, > > format->part_encstatus (node->decrypt_success); > > > > if (node->verify_attempted && format->part_sigstatus) > > +#ifdef GMIME_ATLEAST_26 > > + format->part_sigstatus (node->sig_list); > > +#else > > format->part_sigstatus (node->sig_validity); > > +#endif > > > > format->part_content (part); > > > > diff --git a/test/crypto b/test/crypto > > index 0af4aa8..3779abc 100755 > > --- a/test/crypto > > +++ b/test/crypto > > @@ -104,6 +104,8 @@ test_expect_equal \ > > "$expected" > > > > test_begin_subtest "signature verification with signer key unavailable" > > +# this is broken with current versions of gmime-2.6 > > +(ldd $(which notmuch) | grep -q gmime-2.6) && test_subtest_known_broken > > Just to be nitpicky, you should either escape the . in the regexp or > pass -F to grep. Otherwise I think this hack is fine (though it might > have to get a little fancier once GMime fixes this bug). Added -F :) I guess we could also use pkg-config to test if gmime 2.6 is present. It would also be simpler to test the version number once gmime fixes this bug: pkg-config --atleast-version=2.6.x gmime-2.6 || test_subtest_known_broken But this would mean assuming that notmuch is built against a system-wide gmime. Or it would require setting PKG_CONFIG_PATH before tests... Complicated. So IMHO once gmime fixes this bug we should just remove the test_subtest_known_broken and maybe add something like this in notmuch-client.h: #ifdef GMIME_MAJOR_VERSION #define GMIME_ATLEAST_26 #if !GMIME_CHECK_VERSION(2, 6, x) #warning "Building against an old and buggy version of gmime. Please update to 2.6.x." #endif #endif > > > # move the gnupghome temporarily out of the way > > mv "${GNUPGHOME}"{,.bak} > > output=$(notmuch show --format=json --verify subject:"test signed message 001" \ -- Thomas/Schnouki [-- Attachment #2: Type: application/pgp-signature, Size: 489 bytes --] ^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH v4 1/3] show: don't use hex literals in JSON output 2012-01-20 9:37 ` Thomas Jost @ 2012-01-20 9:39 ` Thomas Jost 2012-01-20 9:39 ` [PATCH v4 2/3] Add compatibility with gmime 2.6 Thomas Jost ` (2 more replies) 0 siblings, 3 replies; 42+ messages in thread From: Thomas Jost @ 2012-01-20 9:39 UTC (permalink / raw) To: notmuch JSON does not support hex literals (0x..) so numbers must be formatted as %d instead of %x. --- notmuch-show.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/notmuch-show.c b/notmuch-show.c index d14dac9..91f566c 100644 --- a/notmuch-show.c +++ b/notmuch-show.c @@ -641,7 +641,7 @@ format_part_sigstatus_json (const GMimeSignatureValidity* validity) printf (", \"keyid\": %s", json_quote_str (ctx_quote, signer->keyid)); } if (signer->errors != GMIME_SIGNER_ERROR_NONE) { - printf (", \"errors\": %x", signer->errors); + printf (", \"errors\": %d", signer->errors); } printf ("}"); -- 1.7.8.4 ^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH v4 2/3] Add compatibility with gmime 2.6 2012-01-20 9:39 ` [PATCH v4 1/3] show: don't use hex literals in JSON output Thomas Jost @ 2012-01-20 9:39 ` Thomas Jost 2012-01-20 16:33 ` Austin Clements ` (2 more replies) 2012-01-20 9:39 ` [PATCH v4 3/3] Update NEWS and INSTALL about " Thomas Jost 2012-01-20 11:55 ` [PATCH v4 1/3] show: don't use hex literals in JSON output David Bremner 2 siblings, 3 replies; 42+ messages in thread From: Thomas Jost @ 2012-01-20 9:39 UTC (permalink / raw) To: notmuch There are lots of API changes in gmime 2.6 crypto handling. By adding preprocessor directives, it is however possible to add gmime 2.6 compatibility while preserving compatibility with gmime 2.4 too. This is mostly based on id:"8762i8hrb9.fsf@bookbinder.fernseed.info". This was tested against both gmime 2.6.4 and 2.4.31. With gmime 2.4.31, the crypto tests all work fine (as expected). With gmime 2.6.4, one crypto test is currently broken (signature verification with signer key unavailable), most likely because of a bug in gmime which will hopefully be fixed in a future version. --- mime-node.c | 57 +++++++++++++++++++++++++++++++- notmuch-client.h | 30 ++++++++++++++++- notmuch-reply.c | 7 ++++ notmuch-show.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ show-message.c | 4 ++ test/crypto | 2 + 6 files changed, 191 insertions(+), 4 deletions(-) diff --git a/mime-node.c b/mime-node.c index d26bb44..27077f7 100644 --- a/mime-node.c +++ b/mime-node.c @@ -33,7 +33,11 @@ typedef struct mime_node_context { GMimeMessage *mime_message; /* Context provided by the caller. */ +#ifdef GMIME_ATLEAST_26 + GMimeCryptoContext *cryptoctx; +#else GMimeCipherContext *cryptoctx; +#endif notmuch_bool_t decrypt; } mime_node_context_t; @@ -57,8 +61,12 @@ _mime_node_context_free (mime_node_context_t *res) notmuch_status_t mime_node_open (const void *ctx, notmuch_message_t *message, - GMimeCipherContext *cryptoctx, notmuch_bool_t decrypt, - mime_node_t **root_out) +#ifdef GMIME_ATLEAST_26 + GMimeCryptoContext *cryptoctx, +#else + GMimeCipherContext *cryptoctx, +#endif + notmuch_bool_t decrypt, mime_node_t **root_out) { const char *filename = notmuch_message_get_filename (message); mime_node_context_t *mctx; @@ -112,12 +120,21 @@ DONE: return status; } +#ifdef GMIME_ATLEAST_26 +static int +_signature_list_free (GMimeSignatureList **proxy) +{ + g_object_unref (*proxy); + return 0; +} +#else static int _signature_validity_free (GMimeSignatureValidity **proxy) { g_mime_signature_validity_free (*proxy); return 0; } +#endif static mime_node_t * _mime_node_create (const mime_node_t *parent, GMimeObject *part) @@ -165,11 +182,25 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) GMimeMultipartEncrypted *encrypteddata = GMIME_MULTIPART_ENCRYPTED (part); node->decrypt_attempted = TRUE; +#ifdef GMIME_ATLEAST_26 + GMimeDecryptResult *decrypt_result = NULL; + node->decrypted_child = g_mime_multipart_encrypted_decrypt + (encrypteddata, node->ctx->cryptoctx, &decrypt_result, &err); +#else node->decrypted_child = g_mime_multipart_encrypted_decrypt (encrypteddata, node->ctx->cryptoctx, &err); +#endif if (node->decrypted_child) { node->decrypt_success = node->verify_attempted = TRUE; +#ifdef GMIME_ATLEAST_26 + /* This may be NULL if the part is not signed. */ + node->sig_list = g_mime_decrypt_result_get_signatures (decrypt_result); + if (node->sig_list) + g_object_ref (node->sig_list); + g_object_unref (decrypt_result); +#else node->sig_validity = g_mime_multipart_encrypted_get_signature_validity (encrypteddata); +#endif } else { fprintf (stderr, "Failed to decrypt part: %s\n", (err ? err->message : "no error explanation given")); @@ -182,6 +213,15 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) "(must be exactly 2)\n", node->nchildren); } else { +#ifdef GMIME_ATLEAST_26 + node->sig_list = g_mime_multipart_signed_verify + (GMIME_MULTIPART_SIGNED (part), node->ctx->cryptoctx, &err); + node->verify_attempted = TRUE; + + if (!node->sig_list) + fprintf (stderr, "Failed to verify signed part: %s\n", + (err ? err->message : "no error explanation given")); +#else /* For some reason the GMimeSignatureValidity returned * here is not a const (inconsistent with that * returned by @@ -200,12 +240,25 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) *proxy = sig_validity; talloc_set_destructor (proxy, _signature_validity_free); } +#endif } } +#ifdef GMIME_ATLEAST_26 + /* sig_list may be created in both above cases, so we need to + * cleanly handle it here. */ + if (node->sig_list) { + GMimeSignatureList **proxy = talloc (node, GMimeSignatureList *); + *proxy = node->sig_list; + talloc_set_destructor (proxy, _signature_list_free); + } +#endif + +#ifndef GMIME_ATLEAST_26 if (node->verify_attempted && !node->sig_validity) fprintf (stderr, "Failed to verify signed part: %s\n", (err ? err->message : "no error explanation given")); +#endif if (err) g_error_free (err); diff --git a/notmuch-client.h b/notmuch-client.h index 62ede28..9c1d383 100644 --- a/notmuch-client.h +++ b/notmuch-client.h @@ -30,6 +30,14 @@ #include <gmime/gmime.h> +/* GMIME_CHECK_VERSION in gmime 2.4 is not usable from the + * preprocessor (it calls a runtime function). But since + * GMIME_MAJOR_VERSION and friends were added in gmime 2.6, we can use + * these to check the version number. */ +#ifdef GMIME_MAJOR_VERSION +#define GMIME_ATLEAST_26 +#endif + #include "notmuch.h" /* This is separate from notmuch-private.h because we're trying to @@ -69,7 +77,11 @@ typedef struct notmuch_show_format { void (*part_start) (GMimeObject *part, int *part_count); void (*part_encstatus) (int status); +#ifdef GMIME_ATLEAST_26 + void (*part_sigstatus) (GMimeSignatureList* siglist); +#else void (*part_sigstatus) (const GMimeSignatureValidity* validity); +#endif void (*part_content) (GMimeObject *part); void (*part_end) (GMimeObject *part); const char *part_sep; @@ -83,7 +95,11 @@ typedef struct notmuch_show_params { int entire_thread; int raw; int part; +#ifdef GMIME_ATLEAST_26 + GMimeCryptoContext* cryptoctx; +#else GMimeCipherContext* cryptoctx; +#endif int decrypt; } notmuch_show_params_t; @@ -290,11 +306,17 @@ typedef struct mime_node { /* True if signature verification on this part was attempted. */ notmuch_bool_t verify_attempted; +#ifdef GMIME_ATLEAST_26 + /* The list of signatures for signed or encrypted containers. If + * there are no signatures, this will be NULL. */ + GMimeSignatureList* sig_list; +#else /* For signed or encrypted containers, the validity of the * signature. May be NULL if signature verification failed. If * there are simply no signatures, this will be non-NULL with an * empty signers list. */ const GMimeSignatureValidity *sig_validity; +#endif /* Internal: Context inherited from the root iterator. */ struct mime_node_context *ctx; @@ -319,8 +341,12 @@ typedef struct mime_node { */ notmuch_status_t mime_node_open (const void *ctx, notmuch_message_t *message, - GMimeCipherContext *cryptoctx, notmuch_bool_t decrypt, - mime_node_t **node_out); +#ifdef GMIME_ATLEAST_26 + GMimeCryptoContext *cryptoctx, +#else + GMimeCipherContext *cryptoctx, +#endif + notmuch_bool_t decrypt, mime_node_t **node_out); /* Return a new MIME node for the requested child part of parent. * parent will be used as the talloc context for the returned child diff --git a/notmuch-reply.c b/notmuch-reply.c index 0f682db..bf67960 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -688,15 +688,22 @@ notmuch_reply_command (void *ctx, int argc, char *argv[]) reply_format_func = notmuch_reply_format_default; if (decrypt) { +#ifdef GMIME_ATLEAST_26 + /* TODO: GMimePasswordRequestFunc */ + params.cryptoctx = g_mime_gpg_context_new (NULL, "gpg"); +#else GMimeSession* session = g_object_new (g_mime_session_get_type(), NULL); params.cryptoctx = g_mime_gpg_context_new (session, "gpg"); +#endif if (params.cryptoctx) { g_mime_gpg_context_set_always_trust ((GMimeGpgContext*) params.cryptoctx, FALSE); params.decrypt = TRUE; } else { fprintf (stderr, "Failed to construct gpg context.\n"); } +#ifndef GMIME_ATLEAST_26 g_object_unref (session); +#endif } config = notmuch_config_open (ctx, NULL, NULL); diff --git a/notmuch-show.c b/notmuch-show.c index 91f566c..190210c 100644 --- a/notmuch-show.c +++ b/notmuch-show.c @@ -76,7 +76,11 @@ static void format_part_encstatus_json (int status); static void +#ifdef GMIME_ATLEAST_26 +format_part_sigstatus_json (GMimeSignatureList* siglist); +#else format_part_sigstatus_json (const GMimeSignatureValidity* validity); +#endif static void format_part_content_json (GMimeObject *part); @@ -486,6 +490,21 @@ show_text_part_content (GMimeObject *part, GMimeStream *stream_out) g_object_unref(stream_filter); } +#ifdef GMIME_ATLEAST_26 +static const char* +signature_status_to_string (GMimeSignatureStatus x) +{ + switch (x) { + case GMIME_SIGNATURE_STATUS_GOOD: + return "good"; + case GMIME_SIGNATURE_STATUS_BAD: + return "bad"; + case GMIME_SIGNATURE_STATUS_ERROR: + return "error"; + } + return "unknown"; +} +#else static const char* signer_status_to_string (GMimeSignerStatus x) { @@ -501,6 +520,7 @@ signer_status_to_string (GMimeSignerStatus x) } return "unknown"; } +#endif static void format_part_start_text (GMimeObject *part, int *part_count) @@ -592,6 +612,73 @@ format_part_encstatus_json (int status) printf ("}]"); } +#ifdef GMIME_ATLEAST_26 +static void +format_part_sigstatus_json (GMimeSignatureList *siglist) +{ + printf (", \"sigstatus\": ["); + + if (!siglist) { + printf ("]"); + return; + } + + void *ctx_quote = talloc_new (NULL); + int i; + for (i = 0; i < g_mime_signature_list_length (siglist); i++) { + GMimeSignature *signature = g_mime_signature_list_get_signature (siglist, i); + + if (i > 0) + printf (", "); + + printf ("{"); + + /* status */ + GMimeSignatureStatus status = g_mime_signature_get_status (signature); + printf ("\"status\": %s", + json_quote_str (ctx_quote, + signature_status_to_string (status))); + + GMimeCertificate *certificate = g_mime_signature_get_certificate (signature); + if (status == GMIME_SIGNATURE_STATUS_GOOD) { + if (certificate) + printf (", \"fingerprint\": %s", json_quote_str (ctx_quote, g_mime_certificate_get_fingerprint (certificate))); + /* these dates are seconds since the epoch; should we + * provide a more human-readable format string? */ + time_t created = g_mime_signature_get_created (signature); + if (created != -1) + printf (", \"created\": %d", (int) created); + time_t expires = g_mime_signature_get_expires (signature); + if (expires > 0) + printf (", \"expires\": %d", (int) expires); + /* output user id only if validity is FULL or ULTIMATE. */ + /* note that gmime is using the term "trust" here, which + * is WRONG. It's actually user id "validity". */ + if (certificate) { + const char *name = g_mime_certificate_get_name (certificate); + GMimeCertificateTrust trust = g_mime_certificate_get_trust (certificate); + if (name && (trust == GMIME_CERTIFICATE_TRUST_FULLY || trust == GMIME_CERTIFICATE_TRUST_ULTIMATE)) + printf (", \"userid\": %s", json_quote_str (ctx_quote, name)); + } + } else if (certificate) { + const char *key_id = g_mime_certificate_get_key_id (certificate); + if (key_id) + printf (", \"keyid\": %s", json_quote_str (ctx_quote, key_id)); + } + + GMimeSignatureError errors = g_mime_signature_get_errors (signature); + if (errors != GMIME_SIGNATURE_ERROR_NONE) { + printf (", \"errors\": %d", errors); + } + + printf ("}"); + } + + printf ("]"); + + talloc_free (ctx_quote); +} +#else static void format_part_sigstatus_json (const GMimeSignatureValidity* validity) { @@ -652,6 +739,7 @@ format_part_sigstatus_json (const GMimeSignatureValidity* validity) talloc_free (ctx_quote); } +#endif static void format_part_content_json (GMimeObject *part) @@ -990,13 +1078,20 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[])) } else if ((STRNCMP_LITERAL (argv[i], "--verify") == 0) || (STRNCMP_LITERAL (argv[i], "--decrypt") == 0)) { if (params.cryptoctx == NULL) { +#ifdef GMIME_ATLEAST_26 + /* TODO: GMimePasswordRequestFunc */ + if (NULL == (params.cryptoctx = g_mime_gpg_context_new(NULL, "gpg"))) +#else GMimeSession* session = g_object_new(g_mime_session_get_type(), NULL); if (NULL == (params.cryptoctx = g_mime_gpg_context_new(session, "gpg"))) +#endif fprintf (stderr, "Failed to construct gpg context.\n"); else g_mime_gpg_context_set_always_trust((GMimeGpgContext*)params.cryptoctx, FALSE); +#ifndef GMIME_ATLEAST_26 g_object_unref (session); session = NULL; +#endif } if (STRNCMP_LITERAL (argv[i], "--decrypt") == 0) params.decrypt = 1; diff --git a/show-message.c b/show-message.c index 8768889..83ecf81 100644 --- a/show-message.c +++ b/show-message.c @@ -48,7 +48,11 @@ show_message_part (mime_node_t *node, format->part_encstatus (node->decrypt_success); if (node->verify_attempted && format->part_sigstatus) +#ifdef GMIME_ATLEAST_26 + format->part_sigstatus (node->sig_list); +#else format->part_sigstatus (node->sig_validity); +#endif format->part_content (part); diff --git a/test/crypto b/test/crypto index 0af4aa8..446a58b 100755 --- a/test/crypto +++ b/test/crypto @@ -104,6 +104,8 @@ test_expect_equal \ "$expected" test_begin_subtest "signature verification with signer key unavailable" +# this is broken with current versions of gmime-2.6 +(ldd $(which notmuch) | grep -Fq gmime-2.6) && test_subtest_known_broken # move the gnupghome temporarily out of the way mv "${GNUPGHOME}"{,.bak} output=$(notmuch show --format=json --verify subject:"test signed message 001" \ -- 1.7.8.4 ^ permalink raw reply related [flat|nested] 42+ messages in thread
* Re: [PATCH v4 2/3] Add compatibility with gmime 2.6 2012-01-20 9:39 ` [PATCH v4 2/3] Add compatibility with gmime 2.6 Thomas Jost @ 2012-01-20 16:33 ` Austin Clements 2012-01-20 22:45 ` Tomi Ollila 2012-01-21 13:10 ` David Bremner 2 siblings, 0 replies; 42+ messages in thread From: Austin Clements @ 2012-01-20 16:33 UTC (permalink / raw) To: Thomas Jost; +Cc: notmuch LGTM! Quoth Thomas Jost on Jan 20 at 10:39 am: > There are lots of API changes in gmime 2.6 crypto handling. By adding > preprocessor directives, it is however possible to add gmime 2.6 compatibility > while preserving compatibility with gmime 2.4 too. > > This is mostly based on id:"8762i8hrb9.fsf@bookbinder.fernseed.info". > > This was tested against both gmime 2.6.4 and 2.4.31. With gmime 2.4.31, the > crypto tests all work fine (as expected). With gmime 2.6.4, one crypto test is > currently broken (signature verification with signer key unavailable), most > likely because of a bug in gmime which will hopefully be fixed in a future > version. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 2/3] Add compatibility with gmime 2.6 2012-01-20 9:39 ` [PATCH v4 2/3] Add compatibility with gmime 2.6 Thomas Jost 2012-01-20 16:33 ` Austin Clements @ 2012-01-20 22:45 ` Tomi Ollila 2012-01-21 13:10 ` David Bremner 2 siblings, 0 replies; 42+ messages in thread From: Tomi Ollila @ 2012-01-20 22:45 UTC (permalink / raw) To: Thomas Jost, notmuch On Fri, 20 Jan 2012 10:39:24 +0100, Thomas Jost <schnouki@schnouki.net> wrote: > There are lots of API changes in gmime 2.6 crypto handling. By adding > preprocessor directives, it is however possible to add gmime 2.6 compatibility > while preserving compatibility with gmime 2.4 too. > > This is mostly based on id:"8762i8hrb9.fsf@bookbinder.fernseed.info". > > This was tested against both gmime 2.6.4 and 2.4.31. With gmime 2.4.31, the > crypto tests all work fine (as expected). With gmime 2.6.4, one crypto test is > currently broken (signature verification with signer key unavailable), most > likely because of a bug in gmime which will hopefully be fixed in a future > version. > --- +1 Tomi ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 2/3] Add compatibility with gmime 2.6 2012-01-20 9:39 ` [PATCH v4 2/3] Add compatibility with gmime 2.6 Thomas Jost 2012-01-20 16:33 ` Austin Clements 2012-01-20 22:45 ` Tomi Ollila @ 2012-01-21 13:10 ` David Bremner 2 siblings, 0 replies; 42+ messages in thread From: David Bremner @ 2012-01-21 13:10 UTC (permalink / raw) To: Thomas Jost, notmuch On Fri, 20 Jan 2012 10:39:24 +0100, Thomas Jost <schnouki@schnouki.net> wrote: > There are lots of API changes in gmime 2.6 crypto handling. By adding > preprocessor directives, it is however possible to add gmime 2.6 compatibility > while preserving compatibility with gmime 2.4 too. > pushed. d ^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH v4 3/3] Update NEWS and INSTALL about gmime 2.6 2012-01-20 9:39 ` [PATCH v4 1/3] show: don't use hex literals in JSON output Thomas Jost 2012-01-20 9:39 ` [PATCH v4 2/3] Add compatibility with gmime 2.6 Thomas Jost @ 2012-01-20 9:39 ` Thomas Jost 2012-01-20 16:39 ` Austin Clements 2012-01-21 13:11 ` [PATCH v4 3/3] Update NEWS and INSTALL " David Bremner 2012-01-20 11:55 ` [PATCH v4 1/3] show: don't use hex literals in JSON output David Bremner 2 siblings, 2 replies; 42+ messages in thread From: Thomas Jost @ 2012-01-20 9:39 UTC (permalink / raw) To: notmuch --- INSTALL | 12 ++++++------ NEWS | 9 +++++++++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/INSTALL b/INSTALL index e51b397..bc98f1d 100644 --- a/INSTALL +++ b/INSTALL @@ -20,8 +20,8 @@ configure stage. Dependencies ------------ -Notmuch depends on three libraries: Xapian, GMime 2.4, and Talloc -which are each described below: +Notmuch depends on three libraries: Xapian, GMime 2.4 or 2.6, and +Talloc which are each described below: Xapian ------ @@ -39,14 +39,14 @@ which are each described below: reading mail while notmuch would wait for Xapian when removing the "inbox" and "unread" tags from messages in a thread. - GMime 2.4 - --------- - GMime 2.4 provides decoding of MIME email messages for Notmuch. + GMime 2.4 or 2.6 + ---------------- + GMime provides decoding of MIME email messages for Notmuch. Without GMime, Notmuch would not be able to extract and index the actual text from email message encoded as BASE64, etc. - GMime 2.4 is available from http://spruce.sourceforge.net/gmime/ + GMime is available from http://spruce.sourceforge.net/gmime/ Talloc ------ diff --git a/NEWS b/NEWS index 6afa912..e78472c 100644 --- a/NEWS +++ b/NEWS @@ -36,6 +36,15 @@ New functions notmuch_query_add_tag_exclude supports the new tag exclusion feature. +Build fixes +----------- + +Compatibility with GMime 2.6 + + It is now possible to build notmuch against both GMime 2.4 and 2.6. + However they may be some issues in PGP signature verification + because of a bug in current versions of GMime 2.6. + Notmuch 0.11 (2012-01-13) ========================= -- 1.7.8.4 ^ permalink raw reply related [flat|nested] 42+ messages in thread
* Re: [PATCH v4 3/3] Update NEWS and INSTALL about gmime 2.6 2012-01-20 9:39 ` [PATCH v4 3/3] Update NEWS and INSTALL about " Thomas Jost @ 2012-01-20 16:39 ` Austin Clements 2012-01-22 0:29 ` [PATCH] Fix NEWS " Thomas Jost 2012-01-21 13:11 ` [PATCH v4 3/3] Update NEWS and INSTALL " David Bremner 1 sibling, 1 reply; 42+ messages in thread From: Austin Clements @ 2012-01-20 16:39 UTC (permalink / raw) To: Thomas Jost; +Cc: notmuch Quoth Thomas Jost on Jan 20 at 10:39 am: > --- > INSTALL | 12 ++++++------ > NEWS | 9 +++++++++ > 2 files changed, 15 insertions(+), 6 deletions(-) > > diff --git a/INSTALL b/INSTALL > index e51b397..bc98f1d 100644 > --- a/INSTALL > +++ b/INSTALL > @@ -20,8 +20,8 @@ configure stage. > > Dependencies > ------------ > -Notmuch depends on three libraries: Xapian, GMime 2.4, and Talloc > -which are each described below: > +Notmuch depends on three libraries: Xapian, GMime 2.4 or 2.6, and > +Talloc which are each described below: > > Xapian > ------ > @@ -39,14 +39,14 @@ which are each described below: > reading mail while notmuch would wait for Xapian when removing > the "inbox" and "unread" tags from messages in a thread. > > - GMime 2.4 > - --------- > - GMime 2.4 provides decoding of MIME email messages for Notmuch. > + GMime 2.4 or 2.6 > + ---------------- > + GMime provides decoding of MIME email messages for Notmuch. > > Without GMime, Notmuch would not be able to extract and index > the actual text from email message encoded as BASE64, etc. > > - GMime 2.4 is available from http://spruce.sourceforge.net/gmime/ > + GMime is available from http://spruce.sourceforge.net/gmime/ > > Talloc > ------ > diff --git a/NEWS b/NEWS > index 6afa912..e78472c 100644 > --- a/NEWS > +++ b/NEWS > @@ -36,6 +36,15 @@ New functions > notmuch_query_add_tag_exclude supports the new tag exclusion > feature. > > +Build fixes > +----------- > + > +Compatibility with GMime 2.6 > + > + It is now possible to build notmuch against both GMime 2.4 and 2.6. > + However they may be some issues in PGP signature verification Typo and comma: "However, there". It would be good to describe the bug, lest people hold off on upgrading because they think it's something more dire. Maybe "However, a bug in current GMime 2.6 causes notmuch not to report signatures where the signer key is unavailable (GNOME bug 668085)." > + because of a bug in current versions of GMime 2.6. > + > Notmuch 0.11 (2012-01-13) > ========================= > ^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH] Fix NEWS about gmime 2.6 2012-01-20 16:39 ` Austin Clements @ 2012-01-22 0:29 ` Thomas Jost 2012-01-22 12:55 ` David Bremner 0 siblings, 1 reply; 42+ messages in thread From: Thomas Jost @ 2012-01-22 0:29 UTC (permalink / raw) To: notmuch Previous version had a typo ("they may be" instead of "there may be") and was lacking a proper description of the gmime bug. --- Argh. Did not know how to tell it in proper English, ended with a huge typo. Sorry about that, and thanks for noticing and fixing it :) NEWS | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index e78472c..2c2d9e9 100644 --- a/NEWS +++ b/NEWS @@ -42,8 +42,8 @@ Build fixes Compatibility with GMime 2.6 It is now possible to build notmuch against both GMime 2.4 and 2.6. - However they may be some issues in PGP signature verification - because of a bug in current versions of GMime 2.6. + However, a bug in current GMime 2.6 causes notmuch not to report + signatures where the signer key is unavailable (GNOME bug 668085). Notmuch 0.11 (2012-01-13) ========================= -- 1.7.8.4 ^ permalink raw reply related [flat|nested] 42+ messages in thread
* Re: [PATCH] Fix NEWS about gmime 2.6 2012-01-22 0:29 ` [PATCH] Fix NEWS " Thomas Jost @ 2012-01-22 12:55 ` David Bremner 0 siblings, 0 replies; 42+ messages in thread From: David Bremner @ 2012-01-22 12:55 UTC (permalink / raw) To: Thomas Jost, notmuch On Sun, 22 Jan 2012 01:29:41 +0100, Thomas Jost <schnouki@schnouki.net> wrote: > Previous version had a typo ("they may be" instead of "there may be") > and was lacking a proper description of the gmime bug. > --- pushed, d ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 3/3] Update NEWS and INSTALL about gmime 2.6 2012-01-20 9:39 ` [PATCH v4 3/3] Update NEWS and INSTALL about " Thomas Jost 2012-01-20 16:39 ` Austin Clements @ 2012-01-21 13:11 ` David Bremner 1 sibling, 0 replies; 42+ messages in thread From: David Bremner @ 2012-01-21 13:11 UTC (permalink / raw) To: Thomas Jost, notmuch On Fri, 20 Jan 2012 10:39:25 +0100, Thomas Jost <schnouki@schnouki.net> wrote: > --- > INSTALL | 12 ++++++------ > NEWS | 9 +++++++++ > 2 files changed, 15 insertions(+), 6 deletions(-) Pushed. Note, I'm still waiting for an update of 1/3, or to convinced otherwise. d ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 1/3] show: don't use hex literals in JSON output 2012-01-20 9:39 ` [PATCH v4 1/3] show: don't use hex literals in JSON output Thomas Jost 2012-01-20 9:39 ` [PATCH v4 2/3] Add compatibility with gmime 2.6 Thomas Jost 2012-01-20 9:39 ` [PATCH v4 3/3] Update NEWS and INSTALL about " Thomas Jost @ 2012-01-20 11:55 ` David Bremner 2012-01-20 12:33 ` Thomas Jost 2 siblings, 1 reply; 42+ messages in thread From: David Bremner @ 2012-01-20 11:55 UTC (permalink / raw) To: Thomas Jost, notmuch On Fri, 20 Jan 2012 10:39:23 +0100, Thomas Jost <schnouki@schnouki.net> wrote: > JSON does not support hex literals (0x..) so numbers must be formatted as %d > instead of %x. > --- > notmuch-show.c | 2 +- > 1 files changed, 1 insertions(+), 1 deletions(-) Probably I'm just being lazy here, but can you explain why this change does not require a corresponding change on the emacs side? d ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 1/3] show: don't use hex literals in JSON output 2012-01-20 11:55 ` [PATCH v4 1/3] show: don't use hex literals in JSON output David Bremner @ 2012-01-20 12:33 ` Thomas Jost 2012-01-20 13:20 ` David Bremner 0 siblings, 1 reply; 42+ messages in thread From: Thomas Jost @ 2012-01-20 12:33 UTC (permalink / raw) To: David Bremner, notmuch [-- Attachment #1: Type: text/plain, Size: 1581 bytes --] On Fri, 20 Jan 2012 07:55:03 -0400, David Bremner <david@tethera.net> wrote: > On Fri, 20 Jan 2012 10:39:23 +0100, Thomas Jost <schnouki@schnouki.net> wrote: > > JSON does not support hex literals (0x..) so numbers must be formatted as %d > > instead of %x. > > --- > > notmuch-show.c | 2 +- > > 1 files changed, 1 insertions(+), 1 deletions(-) > > Probably I'm just being lazy here, but can you explain why this change > does not require a corresponding change on the emacs side? Because Emacs already does the right thing. JSON numbers are supposed to be decimal only (see http://json.org/: digits are 0-9 only), but the current code could result in displaying a hexadecimal number instead ("c" instead of "12"). This would then trigger an error in Emacs, or in any other correct JSON parser. However we are quite lucky: because of the possible values of the gmime error codes, such an error cannot happen. The most common gmime error codes are 1 (expired signature), 2 (no public key), 4 (expired key) and 8 (revoked key). The other possible value is 16 (unsupported algorithm) but obviously it is much more rare. If this happens, the current code will add '"errors": 10' (hex for 16...). This is valid JSON (it looks like a decimal number) but it is incorrect (should be 16, not 10). With this patch, notmuch will correctly display '"errors": 16' if such a case happens. (By the way, this issue was spotted by Austin Clements in id:"20120117034714.GG16740@mit.edu", so he deserves the credits :)) Regards, -- Thomas/Schnouki [-- Attachment #2: Type: application/pgp-signature, Size: 489 bytes --] ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 1/3] show: don't use hex literals in JSON output 2012-01-20 12:33 ` Thomas Jost @ 2012-01-20 13:20 ` David Bremner 2012-01-22 0:20 ` [PATCH] " Thomas Jost 0 siblings, 1 reply; 42+ messages in thread From: David Bremner @ 2012-01-20 13:20 UTC (permalink / raw) To: Thomas Jost, notmuch On Fri, 20 Jan 2012 13:33:58 +0100, Thomas Jost <schnouki@schnouki.net> wrote: > The most common gmime error codes are 1 (expired signature), 2 (no > public key), 4 (expired key) and 8 (revoked key). The other possible > value is 16 (unsupported algorithm) but obviously it is much more rare. > If this happens, the current code will add '"errors": 10' (hex for > 16...). This is valid JSON (it looks like a decimal number) but it is > incorrect (should be 16, not 10). Maybe something like this for the commit message? d ^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH] show: don't use hex literals in JSON output 2012-01-20 13:20 ` David Bremner @ 2012-01-22 0:20 ` Thomas Jost 2012-01-22 12:56 ` David Bremner 0 siblings, 1 reply; 42+ messages in thread From: Thomas Jost @ 2012-01-22 0:20 UTC (permalink / raw) To: notmuch JSON does not support hex literals (0x..) so numbers must be formatted as %d instead of %x. Currently, the possible values for the gmime error code are 1 (expired signature), 2 (no public key), 4 (expired key) and 8 (revoked key). The other possible value is 16 (unsupported algorithm) but obviously it is much more rare. If this happens, the current code will add '"errors": 10'. This is valid JSON (it looks like a decimal number) but it is incorrect (should be 16, not 10). Since this is just an issue in the JSON encoder, no changes are needed on the Emacs side (or in other UIs using the JSON output). --- notmuch-show.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/notmuch-show.c b/notmuch-show.c index 43ee211..7b40568 100644 --- a/notmuch-show.c +++ b/notmuch-show.c @@ -728,7 +728,7 @@ format_part_sigstatus_json (const GMimeSignatureValidity* validity) printf (", \"keyid\": %s", json_quote_str (ctx_quote, signer->keyid)); } if (signer->errors != GMIME_SIGNER_ERROR_NONE) { - printf (", \"errors\": %x", signer->errors); + printf (", \"errors\": %d", signer->errors); } printf ("}"); -- 1.7.8.4 ^ permalink raw reply related [flat|nested] 42+ messages in thread
* Re: [PATCH] show: don't use hex literals in JSON output 2012-01-22 0:20 ` [PATCH] " Thomas Jost @ 2012-01-22 12:56 ` David Bremner 0 siblings, 0 replies; 42+ messages in thread From: David Bremner @ 2012-01-22 12:56 UTC (permalink / raw) To: Thomas Jost, notmuch On Sun, 22 Jan 2012 01:20:57 +0100, Thomas Jost <schnouki@schnouki.net> wrote: > JSON does not support hex literals (0x..) so numbers must be formatted > as %d instead of %x. pushed, d ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v2 2/2] Add pseudo-compatibility with gmime 2.6 2012-01-17 10:50 ` [PATCH v2 2/2] Add pseudo-compatibility with gmime 2.6 Thomas Jost 2012-01-17 12:48 ` Tomi Ollila 2012-01-17 22:25 ` Austin Clements @ 2012-01-18 17:19 ` Tom Prince 2012-01-18 18:00 ` Tomi Ollila 2 siblings, 1 reply; 42+ messages in thread From: Tom Prince @ 2012-01-18 17:19 UTC (permalink / raw) To: Thomas Jost, notmuch On Tue, 17 Jan 2012 11:50:53 +0100, Thomas Jost <schnouki@schnouki.net> wrote: > This was tested against both gmime 2.6.4 and 2.4.31. With gmime 2.4.31, the > crypto tests all work fine (as expected). With gmime 2.6.4, one crypto test > fails (signature verification with signer key unavailable) but this will be hard > to fix since the new API does not report the reason why a signature verification > fails (other than the human-readable error message). How did you test against multiple versions? Using different machines? If there was a way for configure (or something to pick the version, I would setup the buildbot to test against both, so we don't regress either. Tom ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v2 2/2] Add pseudo-compatibility with gmime 2.6 2012-01-18 17:19 ` [PATCH v2 2/2] Add pseudo-compatibility with gmime 2.6 Tom Prince @ 2012-01-18 18:00 ` Tomi Ollila 2012-01-19 23:47 ` Thomas Jost 0 siblings, 1 reply; 42+ messages in thread From: Tomi Ollila @ 2012-01-18 18:00 UTC (permalink / raw) To: Tom Prince, Thomas Jost, notmuch On Wed, 18 Jan 2012 12:19:45 -0500, Tom Prince <tom.prince@ualberta.net> wrote: > > How did you test against multiple versions? Using different machines? If > there was a way for configure (or something to pick the version, I would > setup the buildbot to test against both, so we don't regress either. I currently compile notmuch on Fedora 15 so that I have LIBRARY_PATH=/my/own/path/to/x86_64-linux/lib and CPATH=/my/own/path/to/x86_64-linux/include and gmime 2.4 development files are located in these directories. I'll be hiding gmime 2.4 stuff from these soon in order to build against 2.6 stuff. Tomi ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v2 2/2] Add pseudo-compatibility with gmime 2.6 2012-01-18 18:00 ` Tomi Ollila @ 2012-01-19 23:47 ` Thomas Jost 0 siblings, 0 replies; 42+ messages in thread From: Thomas Jost @ 2012-01-19 23:47 UTC (permalink / raw) To: Tomi Ollila, Tom Prince, notmuch [-- Attachment #1: Type: text/plain, Size: 1285 bytes --] On Wed, 18 Jan 2012 20:00:12 +0200, Tomi Ollila <tomi.ollila@iki.fi> wrote: > On Wed, 18 Jan 2012 12:19:45 -0500, Tom Prince <tom.prince@ualberta.net> wrote: > > > > How did you test against multiple versions? Using different machines? If > > there was a way for configure (or something to pick the version, I would > > setup the buildbot to test against both, so we don't regress either. > > I currently compile notmuch on Fedora 15 so that I have > > LIBRARY_PATH=/my/own/path/to/x86_64-linux/lib > and > CPATH=/my/own/path/to/x86_64-linux/include > > and gmime 2.4 development files are located in these > directories. I'll be hiding gmime 2.4 stuff from these > soon in order to build against 2.6 stuff. > > Tomi For testing gmime 2.4 and 2.6, I just uninstalled 2.6 and reinstalled 2.4 (kept the binary package on purpose -- not a problem since notmuch is the only package using gmime on my system). When hacking the gmime git in order to fix https://bugzilla.gnome.org/show_bug.cgi?id=668085, I set some environment variables before calling "./configure" and building notmuch: LDFLAGS="-Wl,-rpath,/home/schnouki/dev/gmime/gmime/.libs" ./configure --prefix=/usr --sysconfdir=/etc make ldd ./notmuch Regards, -- Thomas/Schnouki [-- Attachment #2: Type: application/pgp-signature, Size: 489 bytes --] ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH] Add pseudo-compatibility with gmime 2.6 2012-01-17 10:48 ` Thomas Jost 2012-01-17 10:50 ` [PATCH v2 1/2] show: don't use hex literals in JSON output Thomas Jost @ 2012-01-17 20:38 ` Austin Clements 2012-01-19 23:52 ` Thomas Jost 1 sibling, 1 reply; 42+ messages in thread From: Austin Clements @ 2012-01-17 20:38 UTC (permalink / raw) To: Thomas Jost; +Cc: notmuch Quoth Thomas Jost on Jan 17 at 11:48 am: > On Mon, 16 Jan 2012 22:47:14 -0500, Austin Clements <amdragon@MIT.EDU> wrote: > > Quoth Thomas Jost on Jan 17 at 12:56 am: > > > This is mostly based on id:"8762i8hrb9.fsf@bookbinder.fernseed.info". > > > > > > This was tested against both gmime 2.6.4 and 2.4.31. With gmime 2.4.31, the > > > crypto tests all work fine (as expected). With gmime 2.6.4, one crypto test > > > fails (signature verification with signer key unavailable) but this will be hard > > > to fix since the new API does not report the reason why a signature verification > > > fails (other than the human-readable error message). > > > > What is the result of this failing test? > > The test looks like that: > > FAIL signature verification with signer key unavailable > --- crypto.4.expected 2012-01-16 23:05:06.765651183 +0000 > +++ crypto.4.output 2012-01-16 23:05:06.765651183 +0000 > @@ -12,9 +12,7 @@ > "Bcc": "", > "Date": "01 Jan 2000 12:00:00 -0000"}, > "body": [{"id": 1, > - "sigstatus": [{"status": "error", > - "keyid": "6D92612D94E46381", > - "errors": 2}], > + "sigstatus": [], > "content-type": "multipart/signed", > "content": [{"id": 2, > "content-type": "text/plain", > Failed to verify signed part: gpg: keyblock resource `/home/schnouki/dev/notmuch/test/tmp.crypto/gnupg/pubring.gpg': file open error > gpg: armor header: Version: GnuPG v1.4.11 (GNU/Linux) > gpg: Signature made Mon Jan 16 23:05:06 2012 UTC using RSA key ID 94E46381 > gpg: Can't check signature: public key not found > > So basically if a signer public key is missing, we can't get the status > signature: empty "sigstatus" field in the JSON output, "Unknown > signature status" in Emacs. > > IMHO this is a bug in gmime 2.6, and I think I know what causes it. I'll > file a bug in gmime and hopefully they'll find a cleaner way to fix it > than the one I came up with :) Oh, okay. That does seem like a bug in GMime. Do you think it would be possible to mark this test as "broken" if notmuch is using GMime 2.6? Off the top of my head, I can't think of an easy way to plumb that information through to the test suite. I don't think we should push any patches that knowingly break a test, even if it's in just one configuration. > > > > > --- > > > mime-node.c | 50 ++++++++++++++++++++++++++- > > > notmuch-client.h | 27 ++++++++++++++- > > > notmuch-reply.c | 7 ++++ > > > notmuch-show.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ > > > show-message.c | 4 ++ > > > 5 files changed, 181 insertions(+), 4 deletions(-) > > > > > > diff --git a/mime-node.c b/mime-node.c > > > index d26bb44..ae2473d 100644 > > > --- a/mime-node.c > > > +++ b/mime-node.c > > > @@ -33,7 +33,11 @@ typedef struct mime_node_context { > > > GMimeMessage *mime_message; > > > > > > /* Context provided by the caller. */ > > > +#ifdef GMIME_26 > > > + GMimeCryptoContext *cryptoctx; > > > +#else > > > GMimeCipherContext *cryptoctx; > > > +#endif > > > notmuch_bool_t decrypt; > > > } mime_node_context_t; > > > > > > @@ -57,8 +61,12 @@ _mime_node_context_free (mime_node_context_t *res) > > > > > > notmuch_status_t > > > mime_node_open (const void *ctx, notmuch_message_t *message, > > > - GMimeCipherContext *cryptoctx, notmuch_bool_t decrypt, > > > - mime_node_t **root_out) > > > +#ifdef GMIME_26 > > > + GMimeCryptoContext *cryptoctx, > > > +#else > > > + GMimeCipherContext *cryptoctx, > > > +#endif > > > + notmuch_bool_t decrypt, mime_node_t **root_out) > > > { > > > const char *filename = notmuch_message_get_filename (message); > > > mime_node_context_t *mctx; > > > @@ -112,12 +120,21 @@ DONE: > > > return status; > > > } > > > > > > +#ifdef GMIME_26 > > > +static int > > > +_signature_list_free (GMimeSignatureList **proxy) > > > +{ > > > + g_object_unref (*proxy); > > > + return 0; > > > +} > > > +#else > > > static int > > > _signature_validity_free (GMimeSignatureValidity **proxy) > > > { > > > g_mime_signature_validity_free (*proxy); > > > return 0; > > > } > > > +#endif > > > > > > static mime_node_t * > > > _mime_node_create (const mime_node_t *parent, GMimeObject *part) > > > @@ -165,11 +182,23 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) > > > GMimeMultipartEncrypted *encrypteddata = > > > GMIME_MULTIPART_ENCRYPTED (part); > > > node->decrypt_attempted = TRUE; > > > +#ifdef GMIME_26 > > > + GMimeDecryptResult *decrypt_result = g_mime_decrypt_result_new (); > > > > I think g_mime_multipart_encrypted_decrypt allocates the > > GMimeDecryptResult for you, so this will just leak memory. > > You're right, fixed. Will it be freed along with node->decrypted_child, > or do we need to handle this ourself? That would be nice to know. My guess would be that we have to free it ourselves (or dereference it, at any rate), but the documentation doesn't say. > > > } else { > > > fprintf (stderr, "Failed to decrypt part: %s\n", > > > (err ? err->message : "no error explanation given")); > > > @@ -182,6 +211,18 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) > > > "(must be exactly 2)\n", > > > node->nchildren); > > > } else { > > > +#ifdef GMIME_26 > > > + GMimeSignatureList *sig_list = g_mime_multipart_signed_verify > > > + (GMIME_MULTIPART_SIGNED (part), node->ctx->cryptoctx, &err); > > > + node->verify_attempted = TRUE; > > > + node->sig_list = sig_list; > > > + if (sig_list) { > > > + GMimeSignatureList **proxy = > > > + talloc (node, GMimeSignatureList *); > > > + *proxy = sig_list; > > > + talloc_set_destructor (proxy, _signature_list_free); > > > + } > > > +#else > > > /* For some reason the GMimeSignatureValidity returned > > > * here is not a const (inconsistent with that > > > * returned by > > > @@ -200,10 +241,15 @@ _mime_node_create (const mime_node_t *parent, GMimeObject *part) > > > *proxy = sig_validity; > > > talloc_set_destructor (proxy, _signature_validity_free); > > > } > > > +#endif > > > } > > > } > > > > > > +#ifdef GMIME_26 > > > + if (node->verify_attempted && !node->sig_list) > > > > Hmm. This is correct for signed parts, but will incorrectly trigger > > for an encrypted part with no signatures. For 2.6, I think this error > > checking may have to move into the branches of the if encrypted/signed > > since for encrypted parts you have to check if > > g_mime_multipart_encrypted_decrypt returned NULL. > > That sound right. The weird part is that it did not cause anything to > fail in the test suite... It would be worth adding a test with an encrypted but unsigned part. I don't know enough GPG myself to do that. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH] Add pseudo-compatibility with gmime 2.6 2012-01-17 20:38 ` [PATCH] " Austin Clements @ 2012-01-19 23:52 ` Thomas Jost 2012-01-20 13:32 ` Tomi Ollila 0 siblings, 1 reply; 42+ messages in thread From: Thomas Jost @ 2012-01-19 23:52 UTC (permalink / raw) To: Austin Clements; +Cc: notmuch [-- Attachment #1: Type: text/plain, Size: 3896 bytes --] On Tue, 17 Jan 2012 15:38:36 -0500, Austin Clements <amdragon@MIT.EDU> wrote: > Quoth Thomas Jost on Jan 17 at 11:48 am: > > On Mon, 16 Jan 2012 22:47:14 -0500, Austin Clements <amdragon@MIT.EDU> wrote: > > > Quoth Thomas Jost on Jan 17 at 12:56 am: > > > > This is mostly based on id:"8762i8hrb9.fsf@bookbinder.fernseed.info". > > > > > > > > This was tested against both gmime 2.6.4 and 2.4.31. With gmime 2.4.31, the > > > > crypto tests all work fine (as expected). With gmime 2.6.4, one crypto test > > > > fails (signature verification with signer key unavailable) but this will be hard > > > > to fix since the new API does not report the reason why a signature verification > > > > fails (other than the human-readable error message). > > > > > > What is the result of this failing test? > > > > The test looks like that: > > > > FAIL signature verification with signer key unavailable > > --- crypto.4.expected 2012-01-16 23:05:06.765651183 +0000 > > +++ crypto.4.output 2012-01-16 23:05:06.765651183 +0000 > > @@ -12,9 +12,7 @@ > > "Bcc": "", > > "Date": "01 Jan 2000 12:00:00 -0000"}, > > "body": [{"id": 1, > > - "sigstatus": [{"status": "error", > > - "keyid": "6D92612D94E46381", > > - "errors": 2}], > > + "sigstatus": [], > > "content-type": "multipart/signed", > > "content": [{"id": 2, > > "content-type": "text/plain", > > Failed to verify signed part: gpg: keyblock resource `/home/schnouki/dev/notmuch/test/tmp.crypto/gnupg/pubring.gpg': file open error > > gpg: armor header: Version: GnuPG v1.4.11 (GNU/Linux) > > gpg: Signature made Mon Jan 16 23:05:06 2012 UTC using RSA key ID 94E46381 > > gpg: Can't check signature: public key not found > > > > So basically if a signer public key is missing, we can't get the status > > signature: empty "sigstatus" field in the JSON output, "Unknown > > signature status" in Emacs. > > > > IMHO this is a bug in gmime 2.6, and I think I know what causes it. I'll > > file a bug in gmime and hopefully they'll find a cleaner way to fix it > > than the one I came up with :) > > Oh, okay. That does seem like a bug in GMime. Do you think it would > be possible to mark this test as "broken" if notmuch is using GMime > 2.6? Off the top of my head, I can't think of an easy way to plumb > that information through to the test suite. I don't think we should > push any patches that knowingly break a test, even if it's in just one > configuration. Here is how I did: (ldd notmuch | grep -q gmime-2.6) && test_subtest_known_broken ldd notmuch will show "/path/to/libgmime-2.4.so.*" or "libgmime-2.6.so.*" so we can easily check this in the test suite. It's a little hacky but it seems to work. AFAIK ldd is a pretty standard tool so it should be available (almost) everywhere. However if you have a better idea I'll be glad to hear it. > > > > +#ifdef GMIME_26 > > > > + if (node->verify_attempted && !node->sig_list) > > > > > > Hmm. This is correct for signed parts, but will incorrectly trigger > > > for an encrypted part with no signatures. For 2.6, I think this error > > > checking may have to move into the branches of the if encrypted/signed > > > since for encrypted parts you have to check if > > > g_mime_multipart_encrypted_decrypt returned NULL. > > > > That sound right. The weird part is that it did not cause anything to > > fail in the test suite... > > It would be worth adding a test with an encrypted but unsigned part. > I don't know enough GPG myself to do that. It looks like there's already one: "emacs delivery of encrypted message with attachment" + following decryptions. Regards, -- Thomas/Schnouki [-- Attachment #2: Type: application/pgp-signature, Size: 489 bytes --] ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH] Add pseudo-compatibility with gmime 2.6 2012-01-19 23:52 ` Thomas Jost @ 2012-01-20 13:32 ` Tomi Ollila 0 siblings, 0 replies; 42+ messages in thread From: Tomi Ollila @ 2012-01-20 13:32 UTC (permalink / raw) To: Thomas Jost; +Cc: notmuch On Fri, 20 Jan 2012 00:52:47 +0100, Thomas Jost <schnouki@schnouki.net> wrote: > > Here is how I did: > > (ldd notmuch | grep -q gmime-2.6) && test_subtest_known_broken > > ldd notmuch will show "/path/to/libgmime-2.4.so.*" or > "libgmime-2.6.so.*" so we can easily check this in the test suite. > It's a little hacky but it seems to work. AFAIK ldd is a pretty standard > tool so it should be available (almost) everywhere. However if you have > a better idea I'll be glad to hear it. The "hack" is good in a sense that if that check fails in any case the test_subtest_known_broken is not executed and we get FAIL instead of BROKEN. The subshell is unneeded: ldd notmuch | grep -q gmime-2.6 && test_subtest_known_broken does the trick (potentially less forks)... ok now I have to test ;) haha: $ rm xfoo.* xbar.* $ strace -ff -o xfoo sh -c '(ldd /bin/ls | grep -q libc) && echo foo' foo $ ls xfoo.* xfoo.14277 xfoo.14279 xfoo.14281 xfoo.14283 xfoo.14285 xfoo.14278 xfoo.14280 xfoo.14282 xfoo.14284 $ strace -ff -o xbar sh -c 'ldd /bin/ls | grep -q libc && echo foo' foo $ ls xbar.* xbar.14292 xbar.14294 xbar.14296 xbar.14298 xbar.14293 xbar.14295 xbar.14297 xbar.14299 Tomi ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH] Add pseudo-compatibility with gmime 2.6 2012-01-16 23:56 ` [PATCH] Add pseudo-compatibility with gmime 2.6 Thomas Jost ` (2 preceding siblings ...) 2012-01-17 3:47 ` Austin Clements @ 2012-01-17 10:19 ` Adrian Perez 3 siblings, 0 replies; 42+ messages in thread From: Adrian Perez @ 2012-01-17 10:19 UTC (permalink / raw) To: notmuch [-- Attachment #1: Type: text/plain, Size: 547 bytes --] Hi, On Tue, 17 Jan 2012 00:56:39 +0100, Thomas Jost <schnouki@schnouki.net> wrote: > There are lots of API changes in gmime 2.6 crypto handling. By adding > preprocessor directives, it is however possible to add gmime 2.6 compatibility > while preserving compatibility with gmime 2.4 too. This approach is better than just dropping support for 2.4 (as my patch does), so I would favor integrating this one instead of mine :D Br. -- Adrian Perez <aperez@igalia.com> - Sent from my toaster Igalia - Free Software Engineering [-- Attachment #2: Type: application/pgp-signature, Size: 197 bytes --] ^ permalink raw reply [flat|nested] 42+ messages in thread
end of thread, other threads:[~2012-01-22 12:56 UTC | newest] Thread overview: 42+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- [not found] <8762gbtd6p.fsf@schnouki.net> 2012-01-16 23:56 ` [PATCH] Add pseudo-compatibility with gmime 2.6 Thomas Jost 2012-01-17 0:43 ` Tim Stoakes 2012-01-17 1:08 ` Kazuo Teramoto 2012-01-17 3:47 ` Austin Clements 2012-01-17 10:48 ` Adrian Perez 2012-01-17 10:48 ` Thomas Jost 2012-01-17 10:50 ` [PATCH v2 1/2] show: don't use hex literals in JSON output Thomas Jost 2012-01-17 10:50 ` [PATCH v2 2/2] Add pseudo-compatibility with gmime 2.6 Thomas Jost 2012-01-17 12:48 ` Tomi Ollila 2012-01-17 13:23 ` Thomas Jost 2012-01-17 22:25 ` Austin Clements 2012-01-18 8:15 ` Tomi Ollila 2012-01-18 17:35 ` Austin Clements 2012-01-19 23:33 ` Thomas Jost 2012-01-19 23:32 ` Thomas Jost 2012-01-20 0:06 ` [PATCH v3 0/2] gmime 2.6 compatibilty, 3rd iteration Thomas Jost 2012-01-20 0:06 ` [PATCH v3 1/2] show: don't use hex literals in JSON output Thomas Jost 2012-01-20 0:06 ` [PATCH v3 2/2] Add compatibility with gmime 2.6 Thomas Jost 2012-01-20 4:10 ` Austin Clements 2012-01-20 9:37 ` Thomas Jost 2012-01-20 9:39 ` [PATCH v4 1/3] show: don't use hex literals in JSON output Thomas Jost 2012-01-20 9:39 ` [PATCH v4 2/3] Add compatibility with gmime 2.6 Thomas Jost 2012-01-20 16:33 ` Austin Clements 2012-01-20 22:45 ` Tomi Ollila 2012-01-21 13:10 ` David Bremner 2012-01-20 9:39 ` [PATCH v4 3/3] Update NEWS and INSTALL about " Thomas Jost 2012-01-20 16:39 ` Austin Clements 2012-01-22 0:29 ` [PATCH] Fix NEWS " Thomas Jost 2012-01-22 12:55 ` David Bremner 2012-01-21 13:11 ` [PATCH v4 3/3] Update NEWS and INSTALL " David Bremner 2012-01-20 11:55 ` [PATCH v4 1/3] show: don't use hex literals in JSON output David Bremner 2012-01-20 12:33 ` Thomas Jost 2012-01-20 13:20 ` David Bremner 2012-01-22 0:20 ` [PATCH] " Thomas Jost 2012-01-22 12:56 ` David Bremner 2012-01-18 17:19 ` [PATCH v2 2/2] Add pseudo-compatibility with gmime 2.6 Tom Prince 2012-01-18 18:00 ` Tomi Ollila 2012-01-19 23:47 ` Thomas Jost 2012-01-17 20:38 ` [PATCH] " Austin Clements 2012-01-19 23:52 ` Thomas Jost 2012-01-20 13:32 ` Tomi Ollila 2012-01-17 10:19 ` Adrian Perez
Code repositories for project(s) associated with this public inbox https://yhetil.org/notmuch.git/ This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).