aboutsummaryrefslogtreecommitdiffhomepage
path: root/notmuch-show.c
diff options
context:
space:
mode:
authorGravatar Jameson Graef Rollins <jrollins@finestructure.net>2011-05-25 18:01:17 -0700
committerGravatar Carl Worth <cworth@cworth.org>2011-05-27 16:22:00 -0700
commit8b18efe171ef80cbb238a9446137c39908755d2d (patch)
tree68f344312f9c55057b1541d2acf2ce43bfd89054 /notmuch-show.c
parent627d752501b42326b9016271de33a6cb0fa2dc8c (diff)
Add signature verification of PGP/MIME-signed parts with --verify.
This is primarily for notmuch-show, although the functionality is added to show-message. Once signatures are processed a new part_sigstatus formatter is emitted, and the entire multipart/signed part is replaced with the contents of the signed part. At the moment only a json part_sigstatus formatting function is available. Emacs support to follow. The original work for this patch was done by Daniel Kahn Gillmor <dkg@fifthhorseman.net> whose help with this functionality I greatly appreciate.
Diffstat (limited to 'notmuch-show.c')
-rw-r--r--notmuch-show.c96
1 files changed, 96 insertions, 0 deletions
diff --git a/notmuch-show.c b/notmuch-show.c
index 363cdbfe..bb54e563 100644
--- a/notmuch-show.c
+++ b/notmuch-show.c
@@ -44,6 +44,7 @@ static const notmuch_show_format_t format_text = {
"\fheader{\n", format_headers_text, "\fheader}\n",
"\fbody{\n",
format_part_start_text,
+ NULL,
format_part_content_text,
format_part_end_text,
"",
@@ -65,6 +66,9 @@ format_part_start_json (unused (GMimeObject *part),
int *part_count);
static void
+format_part_sigstatus_json (const GMimeSignatureValidity* validity);
+
+static void
format_part_content_json (GMimeObject *part);
static void
@@ -76,6 +80,7 @@ static const notmuch_show_format_t format_json = {
", \"headers\": {", format_headers_json, "}",
", \"body\": [",
format_part_start_json,
+ format_part_sigstatus_json,
format_part_content_json,
format_part_end_json,
", ",
@@ -97,6 +102,7 @@ static const notmuch_show_format_t format_mbox = {
NULL,
NULL,
NULL,
+ NULL,
"",
"",
"", "",
@@ -112,6 +118,7 @@ static const notmuch_show_format_t format_raw = {
"", NULL, "",
"",
NULL,
+ NULL,
format_part_content_raw,
NULL,
"",
@@ -396,6 +403,22 @@ show_part_content (GMimeObject *part, GMimeStream *stream_out)
g_object_unref(stream_filter);
}
+static const char*
+signerstatustostring (GMimeSignerStatus x)
+{
+ switch (x) {
+ case GMIME_SIGNER_STATUS_NONE:
+ return "none";
+ case GMIME_SIGNER_STATUS_GOOD:
+ return "good";
+ case GMIME_SIGNER_STATUS_BAD:
+ return "bad";
+ case GMIME_SIGNER_STATUS_ERROR:
+ return "error";
+ }
+ return "unknown";
+}
+
static void
format_part_start_text (GMimeObject *part, int *part_count)
{
@@ -473,6 +496,65 @@ format_part_start_json (unused (GMimeObject *part), int *part_count)
}
static void
+format_part_sigstatus_json (const GMimeSignatureValidity* validity)
+{
+ printf (", \"sigstatus\": [");
+
+ if (!validity) {
+ printf ("]");
+ return;
+ }
+
+ const GMimeSigner *signer = g_mime_signature_validity_get_signers (validity);
+ int first = 1;
+ void *ctx_quote = talloc_new (NULL);
+
+ while (signer) {
+ if (first)
+ first = 0;
+ else
+ printf (", ");
+
+ printf ("{");
+
+ /* status */
+ printf ("\"status\": %s", json_quote_str (ctx_quote, signerstatustostring(signer->status)));
+
+ if (signer->status == GMIME_SIGNER_STATUS_GOOD)
+ {
+ if (signer->fingerprint)
+ printf (", \"fingerprint\": %s", json_quote_str (ctx_quote, signer->fingerprint));
+ /* these dates are seconds since the epoch; should we
+ * provide a more human-readable format string? */
+ if (signer->created)
+ printf (", \"created\": %d", (int) signer->created);
+ if (signer->expires)
+ printf (", \"expires\": %d", (int) signer->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 ((signer->name) && (signer->trust)) {
+ if ((signer->trust == GMIME_SIGNER_TRUST_FULLY) || (signer->trust == GMIME_SIGNER_TRUST_ULTIMATE))
+ printf (", \"userid\": %s", json_quote_str (ctx_quote, signer->name));
+ }
+ } else {
+ if (signer->keyid)
+ printf (", \"keyid\": %s", json_quote_str (ctx_quote, signer->keyid));
+ }
+ if (signer->errors != GMIME_SIGNER_ERROR_NONE) {
+ printf (", \"errors\": %x", signer->errors);
+ }
+
+ printf ("}");
+ signer = signer->next;
+ }
+
+ printf ("]");
+
+ talloc_free (ctx_quote);
+}
+
+static void
format_part_content_json (GMimeObject *part)
{
GMimeContentType *content_type = g_mime_object_get_content_type (GMIME_OBJECT (part));
@@ -739,6 +821,7 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[]))
params.entire_thread = 0;
params.raw = 0;
params.part = -1;
+ params.cryptoctx = NULL;
for (i = 0; i < argc && argv[i][0] == '-'; i++) {
if (strcmp (argv[i], "--") == 0) {
@@ -767,6 +850,16 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[]))
params.part = atoi(argv[i] + sizeof ("--part=") - 1);
} else if (STRNCMP_LITERAL (argv[i], "--entire-thread") == 0) {
params.entire_thread = 1;
+ } else if (STRNCMP_LITERAL (argv[i], "--verify") == 0) {
+ if (params.cryptoctx == NULL) {
+ GMimeSession* session = g_object_new(notmuch_gmime_session_get_type(), NULL);
+ if (NULL == (params.cryptoctx = g_mime_gpg_context_new(session, "gpg")))
+ fprintf (stderr, "Failed to construct gpg context.\n");
+ else
+ g_mime_gpg_context_set_always_trust((GMimeGpgContext*)params.cryptoctx, FALSE);
+ g_object_unref (session);
+ session = NULL;
+ }
} else {
fprintf (stderr, "Unrecognized option: %s\n", argv[i]);
return 1;
@@ -824,5 +917,8 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[]))
notmuch_query_destroy (query);
notmuch_database_close (notmuch);
+ if (params.cryptoctx)
+ g_object_unref(params.cryptoctx);
+
return 0;
}