+Return-Path: <amthrax@drake.mit.edu>\r
+X-Original-To: notmuch@notmuchmail.org\r
+Delivered-To: notmuch@notmuchmail.org\r
+Received: from localhost (localhost [127.0.0.1])\r
+ by olra.theworths.org (Postfix) with ESMTP id 1BCDE431FD0\r
+ for <notmuch@notmuchmail.org>; Sat, 24 Dec 2011 15:41:38 -0800 (PST)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: -0.7\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=-0.7 tagged_above=-999 required=5\r
+ tests=[RCVD_IN_DNSWL_LOW=-0.7] autolearn=disabled\r
+Received: from olra.theworths.org ([127.0.0.1])\r
+ by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
+ with ESMTP id NV2D614DegWA for <notmuch@notmuchmail.org>;\r
+ Sat, 24 Dec 2011 15:41:36 -0800 (PST)\r
+Received: from dmz-mailsec-scanner-6.mit.edu (DMZ-MAILSEC-SCANNER-6.MIT.EDU\r
+ [18.7.68.35])\r
+ by olra.theworths.org (Postfix) with ESMTP id 78E7E431FB6\r
+ for <notmuch@notmuchmail.org>; Sat, 24 Dec 2011 15:41:36 -0800 (PST)\r
+X-AuditID: 12074423-b7f9c6d0000008c3-3d-4ef6632f1dda\r
+Received: from mailhub-auth-2.mit.edu ( [18.7.62.36])\r
+ by dmz-mailsec-scanner-6.mit.edu (Symantec Messaging Gateway) with SMTP\r
+ id 6A.DD.02243.F2366FE4; Sat, 24 Dec 2011 18:41:35 -0500 (EST)\r
+Received: from outgoing.mit.edu (OUTGOING-AUTH.MIT.EDU [18.7.22.103])\r
+ by mailhub-auth-2.mit.edu (8.13.8/8.9.2) with ESMTP id pBONfYhG014698; \r
+ Sat, 24 Dec 2011 18:41:35 -0500\r
+Received: from drake.mit.edu (c-76-21-105-205.hsd1.ca.comcast.net\r
+ [76.21.105.205]) (authenticated bits=0)\r
+ (User authenticated as amdragon@ATHENA.MIT.EDU)\r
+ by outgoing.mit.edu (8.13.6/8.12.4) with ESMTP id pBONfW1h023823\r
+ (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=NOT);\r
+ Sat, 24 Dec 2011 18:41:34 -0500 (EST)\r
+Received: from amthrax by drake.mit.edu with local (Exim 4.77)\r
+ (envelope-from <amthrax@drake.mit.edu>)\r
+ id 1ReWiB-0007fr-3y; Sat, 24 Dec 2011 13:52:51 -0500\r
+From: Austin Clements <amdragon@MIT.EDU>\r
+To: notmuch@notmuchmail.org\r
+Subject:\r
+ [PATCH v5 2/4] Introduce a generic tree-like abstraction for MIME traversal.\r
+Date: Sat, 24 Dec 2011 13:52:44 -0500\r
+Message-Id: <1324752766-29315-3-git-send-email-amdragon@mit.edu>\r
+X-Mailer: git-send-email 1.7.7.3\r
+In-Reply-To: <1324752766-29315-1-git-send-email-amdragon@mit.edu>\r
+References: <1324698348-27620-1-git-send-email-amdragon@mit.edu>\r
+ <1324752766-29315-1-git-send-email-amdragon@mit.edu>\r
+MIME-Version: 1.0\r
+Content-Type: text/plain; charset=UTF-8\r
+Content-Transfer-Encoding: 8bit\r
+X-Brightmail-Tracker:\r
+ H4sIAAAAAAAAA+NgFmplleLIzCtJLcpLzFFi42IRYrdT0dVP/uZncPGrjsX1mzOZHRg9nq26\r
+ xRzAGMVlk5Kak1mWWqRvl8CV8fjNTsaCmakVS/r/MjUwnvbvYuTkkBAwkfjw7TQbhC0mceHe\r
+ eiCbi0NIYB+jRO/Z8ywQzgZGie7+F1CZc0wSexo/sEM48xklpu7rYgfpZxPQkNi2fzkjiC0i\r
+ IC2x8+5s1i5GDg5mATWJP10qIGFhgWqJkz8/MYHYLAKqEq++rgCzeQUcJF43tLFAnKEgcW71\r
+ ObCRnAKOEtdaJoOdJyRQLrGzdyYLRL2gxMmZT1ggxqtLrJ8nBBJmFpCXaN46m3kCo9AsJFWz\r
+ EKpmIalawMi8ilE2JbdKNzcxM6c4NVm3ODkxLy+1SNdMLzezRC81pXQTIziEXZR3MP45qHSI\r
+ UYCDUYmHt3npFz8h1sSy4srcQ4ySHExKorwT4775CfEl5adUZiQWZ8QXleakFh9ilOBgVhLh\r
+ 1UwCKudNSaysSi3Kh0lJc7AoifNqaL3zExJITyxJzU5NLUgtgsnKcHAoSfD6JgENFSxKTU+t\r
+ SMvMKUFIM3FwggznARq+BKSGt7ggMbc4Mx0if4pRl+Pq1g1nGYVY8vLzUqXEee1BigRAijJK\r
+ 8+DmwFLPK0ZxoLeEeSNBqniAaQtu0iugJUxAS2KMQD4oLklESEk1MJ5696J73iZb7gmKYRon\r
+ wj895G9UaXi2snd6pc23xJ8RK4wCplrbJDAx5SeeON/Ir6GV1OpuvW5WmdR2xxu35m/tm7a4\r
+ niUnLPuXmuMulYTflX8XOXv/36SZxGLWdmDjrtx/mZdZmRZIqRq8rdslltZ2unBy1R6jQI5H\r
+ L7rnM7+MDPJRYv+vrMRSnJFoqMVcVJwIALn0GSwYAwAA\r
+X-BeenThere: notmuch@notmuchmail.org\r
+X-Mailman-Version: 2.1.13\r
+Precedence: list\r
+List-Id: "Use and development of the notmuch mail system."\r
+ <notmuch.notmuchmail.org>\r
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
+ <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
+List-Post: <mailto:notmuch@notmuchmail.org>\r
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
+ <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
+X-List-Received-Date: Sat, 24 Dec 2011 23:41:38 -0000\r
+\r
+This wraps all of the complex MIME part handling in a single, simple\r
+function that gets part N from *any* MIME object, so traversing a MIME\r
+part tree becomes a two-line for loop. Furthermore, the MIME node\r
+structure provides easy access to envelopes for message parts as well\r
+as cryptographic information.\r
+\r
+This code is directly derived from the current show_message_body code\r
+(much of it is identical), but the control relation is inverted:\r
+instead of show_message_body controlling the traversal of the MIME\r
+structure and invoking callbacks, the caller controls the traversal of\r
+the MIME structure.\r
+---\r
+ Makefile.local | 1 +\r
+ mime-node.c | 238 ++++++++++++++++++++++++++++++++++++++++++++++++++++++\r
+ notmuch-client.h | 83 +++++++++++++++++++\r
+ 3 files changed, 322 insertions(+), 0 deletions(-)\r
+ create mode 100644 mime-node.c\r
+\r
+diff --git a/Makefile.local b/Makefile.local\r
+index 97f397f..516f26e 100644\r
+--- a/Makefile.local\r
++++ b/Makefile.local\r
+@@ -315,6 +315,7 @@ notmuch_client_srcs = \\r
+ notmuch-time.c \\r
+ query-string.c \\r
+ show-message.c \\r
++ mime-node.c \\r
+ json.c\r
+ \r
+ notmuch_client_modules = $(notmuch_client_srcs:.c=.o)\r
+diff --git a/mime-node.c b/mime-node.c\r
+new file mode 100644\r
+index 0000000..760c55f\r
+--- /dev/null\r
++++ b/mime-node.c\r
+@@ -0,0 +1,238 @@\r
++/* notmuch - Not much of an email program, (just index and search)\r
++ *\r
++ * Copyright © 2009 Carl Worth\r
++ * Copyright © 2009 Keith Packard\r
++ *\r
++ * This program is free software: you can redistribute it and/or modify\r
++ * it under the terms of the GNU General Public License as published by\r
++ * the Free Software Foundation, either version 3 of the License, or\r
++ * (at your option) any later version.\r
++ *\r
++ * This program is distributed in the hope that it will be useful,\r
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
++ * GNU General Public License for more details.\r
++ *\r
++ * You should have received a copy of the GNU General Public License\r
++ * along with this program. If not, see http://www.gnu.org/licenses/ .\r
++ *\r
++ * Authors: Carl Worth <cworth@cworth.org>\r
++ * Keith Packard <keithp@keithp.com>\r
++ * Austin Clements <aclements@csail.mit.edu>\r
++ */\r
++\r
++#include "notmuch-client.h"\r
++\r
++/* Context that gets inherited from the root node. */\r
++typedef struct mime_node_context {\r
++ /* Per-message resources. These are allocated internally and must\r
++ * be destroyed. */\r
++ FILE *file;\r
++ GMimeStream *stream;\r
++ GMimeParser *parser;\r
++ GMimeMessage *mime_message;\r
++\r
++ /* Context provided by the caller. */\r
++ GMimeCipherContext *cryptoctx;\r
++ notmuch_bool_t decrypt;\r
++} mime_node_context_t;\r
++\r
++static int\r
++_mime_node_context_free (mime_node_context_t *res)\r
++{\r
++ if (res->mime_message)\r
++ g_object_unref (res->mime_message);\r
++\r
++ if (res->parser)\r
++ g_object_unref (res->parser);\r
++\r
++ if (res->stream)\r
++ g_object_unref (res->stream);\r
++\r
++ if (res->file)\r
++ fclose (res->file);\r
++\r
++ return 0;\r
++}\r
++\r
++notmuch_status_t\r
++mime_node_open (const void *ctx, notmuch_message_t *message,\r
++ GMimeCipherContext *cryptoctx, notmuch_bool_t decrypt,\r
++ mime_node_t **root_out)\r
++{\r
++ const char *filename = notmuch_message_get_filename (message);\r
++ mime_node_context_t *mctx;\r
++ mime_node_t *root;\r
++ notmuch_status_t status;\r
++\r
++ root = talloc_zero (ctx, mime_node_t);\r
++ if (root == NULL) {\r
++ fprintf (stderr, "Out of memory.\n");\r
++ status = NOTMUCH_STATUS_OUT_OF_MEMORY;\r
++ goto DONE;\r
++ }\r
++\r
++ /* Create the tree-wide context */\r
++ mctx = talloc_zero (root, mime_node_context_t);\r
++ if (mctx == NULL) {\r
++ fprintf (stderr, "Out of memory.\n");\r
++ status = NOTMUCH_STATUS_OUT_OF_MEMORY;\r
++ goto DONE;\r
++ }\r
++ talloc_set_destructor (mctx, _mime_node_context_free);\r
++\r
++ mctx->file = fopen (filename, "r");\r
++ if (! mctx->file) {\r
++ fprintf (stderr, "Error opening %s: %s\n", filename, strerror (errno));\r
++ status = NOTMUCH_STATUS_FILE_ERROR;\r
++ goto DONE;\r
++ }\r
++\r
++ mctx->stream = g_mime_stream_file_new (mctx->file);\r
++ g_mime_stream_file_set_owner (GMIME_STREAM_FILE (mctx->stream), FALSE);\r
++\r
++ mctx->parser = g_mime_parser_new_with_stream (mctx->stream);\r
++\r
++ mctx->mime_message = g_mime_parser_construct_message (mctx->parser);\r
++\r
++ mctx->cryptoctx = cryptoctx;\r
++ mctx->decrypt = decrypt;\r
++\r
++ /* Create the root node */\r
++ root->part = GMIME_OBJECT (mctx->mime_message);\r
++ root->envelope_file = message;\r
++ root->nchildren = 1;\r
++ root->ctx = mctx;\r
++\r
++ *root_out = root;\r
++ return NOTMUCH_STATUS_SUCCESS;\r
++\r
++DONE:\r
++ talloc_free (root);\r
++ return status;\r
++}\r
++\r
++static int\r
++_signature_validity_free (GMimeSignatureValidity **proxy)\r
++{\r
++ g_mime_signature_validity_free (*proxy);\r
++ return 0;\r
++}\r
++\r
++static mime_node_t *\r
++_mime_node_create (const mime_node_t *parent, GMimeObject *part)\r
++{\r
++ mime_node_t *node = talloc_zero (parent, mime_node_t);\r
++ GError *err = NULL;\r
++\r
++ /* Set basic node properties */\r
++ node->part = part;\r
++ node->ctx = parent->ctx;\r
++ if (!talloc_reference (node, node->ctx)) {\r
++ fprintf (stderr, "Out of memory.\n");\r
++ talloc_free (node);\r
++ return NULL;\r
++ }\r
++\r
++ /* Deal with the different types of parts */\r
++ if (GMIME_IS_PART (part)) {\r
++ node->nchildren = 0;\r
++ } else if (GMIME_IS_MULTIPART (part)) {\r
++ node->nchildren = g_mime_multipart_get_count (GMIME_MULTIPART (part));\r
++ } else if (GMIME_IS_MESSAGE_PART (part)) {\r
++ /* Promote part to an envelope and open it */\r
++ GMimeMessagePart *message_part = GMIME_MESSAGE_PART (part);\r
++ GMimeMessage *message = g_mime_message_part_get_message (message_part);\r
++ node->envelope_part = message_part;\r
++ node->part = GMIME_OBJECT (message);\r
++ node->nchildren = 1;\r
++ } else {\r
++ fprintf (stderr, "Warning: Unknown mime part type: %s.\n",\r
++ g_type_name (G_OBJECT_TYPE (part)));\r
++ talloc_free (node);\r
++ return NULL;\r
++ }\r
++\r
++ /* Handle PGP/MIME parts */\r
++ if (GMIME_IS_MULTIPART_ENCRYPTED (part)\r
++ && node->ctx->cryptoctx && node->ctx->decrypt) {\r
++ if (node->nchildren != 2) {\r
++ /* this violates RFC 3156 section 4, so we won't bother with it. */\r
++ fprintf (stderr, "Error: %d part(s) for a multipart/encrypted "\r
++ "message (must be exactly 2)\n",\r
++ node->nchildren);\r
++ } else {\r
++ GMimeMultipartEncrypted *encrypteddata =\r
++ GMIME_MULTIPART_ENCRYPTED (part);\r
++ node->decrypt_attempted = TRUE;\r
++ node->decrypted_child = g_mime_multipart_encrypted_decrypt\r
++ (encrypteddata, node->ctx->cryptoctx, &err);\r
++ if (node->decrypted_child) {\r
++ node->decrypt_success = node->verify_attempted = TRUE;\r
++ node->sig_validity = g_mime_multipart_encrypted_get_signature_validity (encrypteddata);\r
++ } else {\r
++ fprintf (stderr, "Failed to decrypt part: %s\n",\r
++ (err ? err->message : "no error explanation given"));\r
++ }\r
++ }\r
++ } else if (GMIME_IS_MULTIPART_SIGNED (part) && node->ctx->cryptoctx) {\r
++ if (node->nchildren != 2) {\r
++ /* this violates RFC 3156 section 5, so we won't bother with it. */\r
++ fprintf (stderr, "Error: %d part(s) for a multipart/signed message "\r
++ "(must be exactly 2)\n",\r
++ node->nchildren);\r
++ } else {\r
++ /* For some reason the GMimeSignatureValidity returned\r
++ * here is not a const (inconsistent with that\r
++ * returned by\r
++ * g_mime_multipart_encrypted_get_signature_validity,\r
++ * and therefore needs to be properly disposed of.\r
++ *\r
++ * In GMime 2.6, they're both non-const, so we'll be able\r
++ * to clean up this asymmetry. */\r
++ GMimeSignatureValidity *sig_validity = g_mime_multipart_signed_verify\r
++ (GMIME_MULTIPART_SIGNED (part), node->ctx->cryptoctx, &err);\r
++ node->verify_attempted = TRUE;\r
++ node->sig_validity = sig_validity;\r
++ if (sig_validity) {\r
++ GMimeSignatureValidity **proxy =\r
++ talloc (node, GMimeSignatureValidity *);\r
++ *proxy = sig_validity;\r
++ talloc_set_destructor (proxy, _signature_validity_free);\r
++ }\r
++ }\r
++ }\r
++\r
++ if (node->verify_attempted && !node->sig_validity)\r
++ fprintf (stderr, "Failed to verify signed part: %s\n",\r
++ (err ? err->message : "no error explanation given"));\r
++\r
++ if (err)\r
++ g_error_free (err);\r
++\r
++ return node;\r
++}\r
++\r
++mime_node_t *\r
++mime_node_child (const mime_node_t *parent, int child)\r
++{\r
++ GMimeObject *sub;\r
++\r
++ if (!parent || child < 0 || child >= parent->nchildren)\r
++ return NULL;\r
++\r
++ if (GMIME_IS_MULTIPART (parent->part)) {\r
++ if (child == 1 && parent->decrypted_child)\r
++ sub = parent->decrypted_child;\r
++ else\r
++ sub = g_mime_multipart_get_part\r
++ (GMIME_MULTIPART (parent->part), child);\r
++ } else if (GMIME_IS_MESSAGE (parent->part)) {\r
++ sub = g_mime_message_get_mime_part (GMIME_MESSAGE (parent->part));\r
++ } else {\r
++ /* This should have been caught by message_part_create */\r
++ INTERNAL_ERROR ("Unexpected GMimeObject type: %s",\r
++ g_type_name (G_OBJECT_TYPE (parent->part)));\r
++ }\r
++ return _mime_node_create (parent, sub);\r
++}\r
+diff --git a/notmuch-client.h b/notmuch-client.h\r
+index c521efa..64b255c 100644\r
+--- a/notmuch-client.h\r
++++ b/notmuch-client.h\r
+@@ -241,5 +241,88 @@ notmuch_run_hook (const char *db_path, const char *hook);\r
+ notmuch_bool_t\r
+ debugger_is_active (void);\r
+ \r
++/* mime-node.c */\r
++\r
++/* mime_node_t represents a single node in a MIME tree. A MIME tree\r
++ * abstracts the different ways of traversing different types of MIME\r
++ * parts, allowing a MIME message to be viewed as a generic tree of\r
++ * parts. Message-type parts have one child, multipart-type parts\r
++ * have multiple children, and leaf parts have zero children.\r
++ */\r
++typedef struct mime_node {\r
++ /* The MIME object of this part. This will be a GMimeMessage,\r
++ * GMimePart, GMimeMultipart, or a subclass of one of these.\r
++ *\r
++ * This will never be a GMimeMessagePart because GMimeMessagePart\r
++ * is structurally redundant with GMimeMessage. If this part is a\r
++ * message (that is, 'part' is a GMimeMessage), then either\r
++ * envelope_file will be set to a notmuch_message_t (for top-level\r
++ * messages) or envelope_part will be set to a GMimeMessagePart\r
++ * (for embedded message parts).\r
++ */\r
++ GMimeObject *part;\r
++\r
++ /* If part is a GMimeMessage, these record the envelope of the\r
++ * message: either a notmuch_message_t representing a top-level\r
++ * message, or a GMimeMessagePart representing a MIME part\r
++ * containing a message.\r
++ */\r
++ notmuch_message_t *envelope_file;\r
++ GMimeMessagePart *envelope_part;\r
++\r
++ /* The number of children of this part. */\r
++ int nchildren;\r
++\r
++ /* True if decryption of this part was attempted. */\r
++ notmuch_bool_t decrypt_attempted;\r
++ /* True if decryption of this part's child succeeded. In this\r
++ * case, the decrypted part is substituted for the second child of\r
++ * this part (which would usually be the encrypted data). */\r
++ notmuch_bool_t decrypt_success;\r
++\r
++ /* True if signature verification on this part was attempted. */\r
++ notmuch_bool_t verify_attempted;\r
++ /* For signed or encrypted containers, the validity of the\r
++ * signature. May be NULL if signature verification failed. If\r
++ * there are simply no signatures, this will be non-NULL with an\r
++ * empty signers list. */\r
++ const GMimeSignatureValidity *sig_validity;\r
++\r
++ /* Internal: Context inherited from the root iterator. */\r
++ struct mime_node_context *ctx;\r
++\r
++ /* Internal: For successfully decrypted multipart parts, the\r
++ * decrypted part to substitute for the second child. */\r
++ GMimeObject *decrypted_child;\r
++} mime_node_t;\r
++\r
++/* Construct a new MIME node pointing to the root message part of\r
++ * message. If cryptoctx is non-NULL, it will be used to verify\r
++ * signatures on any child parts. If decrypt is true, then cryptoctx\r
++ * will additionally be used to decrypt any encrypted child parts.\r
++ *\r
++ * Return value:\r
++ *\r
++ * NOTMUCH_STATUS_SUCCESS: Root node is returned in *node_out.\r
++ *\r
++ * NOTMUCH_STATUS_FILE_ERROR: Failed to open message file.\r
++ *\r
++ * NOTMUCH_STATUS_OUT_OF_MEMORY: Out of memory.\r
++ */\r
++notmuch_status_t\r
++mime_node_open (const void *ctx, notmuch_message_t *message,\r
++ GMimeCipherContext *cryptoctx, notmuch_bool_t decrypt,\r
++ mime_node_t **node_out);\r
++\r
++/* Return a new MIME node for the requested child part of parent.\r
++ * parent will be used as the talloc context for the returned child\r
++ * node.\r
++ *\r
++ * In case of any failure, this function returns NULL, (after printing\r
++ * an error message on stderr).\r
++ */\r
++mime_node_t *\r
++mime_node_child (const mime_node_t *parent, int child);\r
++\r
+ #include "command-line-arguments.h"\r
+ #endif\r
+-- \r
+1.7.7.3\r
+\r