database error
[notmuch-archives.git] / 7f / 7a781f47a5164731fb8049ac0cf643e4923b05
1 Return-Path: <amthrax@drake.mit.edu>\r
2 X-Original-To: notmuch@notmuchmail.org\r
3 Delivered-To: notmuch@notmuchmail.org\r
4 Received: from localhost (localhost [127.0.0.1])\r
5         by olra.theworths.org (Postfix) with ESMTP id 74768429E3F\r
6         for <notmuch@notmuchmail.org>; Fri, 23 Dec 2011 19:46:05 -0800 (PST)\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
8 X-Spam-Flag: NO\r
9 X-Spam-Score: -0.7\r
10 X-Spam-Level: \r
11 X-Spam-Status: No, score=-0.7 tagged_above=-999 required=5\r
12         tests=[RCVD_IN_DNSWL_LOW=-0.7] autolearn=disabled\r
13 Received: from olra.theworths.org ([127.0.0.1])\r
14         by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
15         with ESMTP id ot5vCocRr1R8 for <notmuch@notmuchmail.org>;\r
16         Fri, 23 Dec 2011 19:46:04 -0800 (PST)\r
17 Received: from dmz-mailsec-scanner-5.mit.edu (DMZ-MAILSEC-SCANNER-5.MIT.EDU\r
18         [18.7.68.34])\r
19         by olra.theworths.org (Postfix) with ESMTP id F3BA7429E30\r
20         for <notmuch@notmuchmail.org>; Fri, 23 Dec 2011 19:46:03 -0800 (PST)\r
21 X-AuditID: 12074422-b7fd66d0000008f9-a5-4ef54afb93a9\r
22 Received: from mailhub-auth-4.mit.edu ( [18.7.62.39])\r
23         by dmz-mailsec-scanner-5.mit.edu (Symantec Messaging Gateway) with SMTP\r
24         id 24.61.02297.BFA45FE4; Fri, 23 Dec 2011 22:46:03 -0500 (EST)\r
25 Received: from outgoing.mit.edu (OUTGOING-AUTH.MIT.EDU [18.7.22.103])\r
26         by mailhub-auth-4.mit.edu (8.13.8/8.9.2) with ESMTP id pBO3k31Y015217; \r
27         Fri, 23 Dec 2011 22:46:03 -0500\r
28 Received: from drake.mit.edu (c-76-21-105-205.hsd1.ca.comcast.net\r
29         [76.21.105.205]) (authenticated bits=0)\r
30         (User authenticated as amdragon@ATHENA.MIT.EDU)\r
31         by outgoing.mit.edu (8.13.6/8.12.4) with ESMTP id pBO3k1hW017815\r
32         (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=NOT);\r
33         Fri, 23 Dec 2011 22:46:02 -0500 (EST)\r
34 Received: from amthrax by drake.mit.edu with local (Exim 4.77)\r
35         (envelope-from <amthrax@drake.mit.edu>)\r
36         id 1ReIYb-0007IE-1J; Fri, 23 Dec 2011 22:46:01 -0500\r
37 From: Austin Clements <amdragon@MIT.EDU>\r
38 To: notmuch@notmuchmail.org\r
39 Subject:\r
40  [PATCH v4 2/4] Introduce a generic tree-like abstraction for MIME traversal.\r
41 Date: Fri, 23 Dec 2011 22:45:46 -0500\r
42 Message-Id: <1324698348-27620-3-git-send-email-amdragon@mit.edu>\r
43 X-Mailer: git-send-email 1.7.7.3\r
44 In-Reply-To: <1324698348-27620-1-git-send-email-amdragon@mit.edu>\r
45 References: <1323460468-4030-1-git-send-email-amdragon@mit.edu>\r
46         <1324698348-27620-1-git-send-email-amdragon@mit.edu>\r
47 MIME-Version: 1.0\r
48 Content-Type: text/plain; charset=UTF-8\r
49 Content-Transfer-Encoding: 8bit\r
50 X-Brightmail-Tracker:\r
51  H4sIAAAAAAAAA+NgFmphleLIzCtJLcpLzFFi42IRYrdT1/3t9dXPYOU6OYvrN2cyOzB6PFt1\r
52         izmAMYrLJiU1J7MstUjfLoEr49j9HewFvakVP+6dY2lg3O/fxcjJISFgIrHm8E12CFtM4sK9\r
53         9WxdjFwcQgL7GCWmrf4C5WxglLg+6SM7hHOOSeLaoiNMEM58Ronbey8zg/SzCWhIbNu/nBHE\r
54         FhGQlth5dzZrFyMHB7OAmsSfLhWQsLBAtcSniTOZQGwWAVWJN8uvg5XzCjhIbP59gRXiDAWJ\r
55         c6vPgZ3EKeAosXXre0aQMUICZRLNZ4IgygUlTs58wgIxXV1i/TwhkDCzgLxE89bZzBMYhWYh\r
56         qZqFUDULSdUCRuZVjLIpuVW6uYmZOcWpybrFyYl5ealFuqZ6uZkleqkppZsYwQHsorSD8edB\r
57         pUOMAhyMSjy8zUu/+AmxJpYVV+YeYpTkYFIS5b3q9tVPiC8pP6UyI7E4I76oNCe1+BCjBAez\r
58         kgivZhJQOW9KYmVValE+TEqag0VJnFdd652fkEB6YklqdmpqQWoRTFaGg0NJglcBGKlCgkWp\r
59         6akVaZk5JQhpJg5OkOE8QMO5QWp4iwsSc4sz0yHypxh1Oa5u3XCWUYglLz8vVUqcdxlIkQBI\r
60         UUZpHtwcWOJ5xSgO9JYwrxRIFQ8wacFNegW0hAloSYwRyAfFJYkIKakGxjkxaqH2b/pP3c8+\r
61         zunb8CZuud6EyAl63jt9TZQeOQtUCq2pDwo/3iB+S+9sct0d75I/nxg8Z/LkiJ77lKUv//bE\r
62         +ahjF+T8s/48uh1XIq9dlprL4zx1zozXNcz2XY/mxh69ef0rNwO/3DvvjCQzJb9jq06Je5/d\r
63         17PzqvnzS1cdw3sXeDKZKLEUZyQaajEXFScCANH72QsXAwAA\r
64 X-BeenThere: notmuch@notmuchmail.org\r
65 X-Mailman-Version: 2.1.13\r
66 Precedence: list\r
67 List-Id: "Use and development of the notmuch mail system."\r
68         <notmuch.notmuchmail.org>\r
69 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
70         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
71 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
72 List-Post: <mailto:notmuch@notmuchmail.org>\r
73 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
74 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
75         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
76 X-List-Received-Date: Sat, 24 Dec 2011 03:46:05 -0000\r
77 \r
78 This wraps all of the complex MIME part handling in a single, simple\r
79 function that gets part N from *any* MIME object, so traversing a MIME\r
80 part tree becomes a two-line for loop.  Furthermore, the MIME node\r
81 structure provides easy access to envelopes for message parts as well\r
82 as cryptographic information.\r
83 \r
84 This code is directly derived from the current show_message_body code\r
85 (much of it is identical), but the control relation is inverted:\r
86 instead of show_message_body controlling the traversal of the MIME\r
87 structure and invoking callbacks, the caller controls the traversal of\r
88 the MIME structure.\r
89 ---\r
90  Makefile.local   |    1 +\r
91  mime-node.c      |  238 ++++++++++++++++++++++++++++++++++++++++++++++++++++++\r
92  notmuch-client.h |   83 +++++++++++++++++++\r
93  3 files changed, 322 insertions(+), 0 deletions(-)\r
94  create mode 100644 mime-node.c\r
95 \r
96 diff --git a/Makefile.local b/Makefile.local\r
97 index 97f397f..516f26e 100644\r
98 --- a/Makefile.local\r
99 +++ b/Makefile.local\r
100 @@ -315,6 +315,7 @@ notmuch_client_srcs =               \\r
101         notmuch-time.c          \\r
102         query-string.c          \\r
103         show-message.c          \\r
104 +       mime-node.c             \\r
105         json.c\r
106  \r
107  notmuch_client_modules = $(notmuch_client_srcs:.c=.o)\r
108 diff --git a/mime-node.c b/mime-node.c\r
109 new file mode 100644\r
110 index 0000000..fd8e754\r
111 --- /dev/null\r
112 +++ b/mime-node.c\r
113 @@ -0,0 +1,238 @@\r
114 +/* notmuch - Not much of an email program, (just index and search)\r
115 + *\r
116 + * Copyright © 2009 Carl Worth\r
117 + * Copyright © 2009 Keith Packard\r
118 + *\r
119 + * This program is free software: you can redistribute it and/or modify\r
120 + * it under the terms of the GNU General Public License as published by\r
121 + * the Free Software Foundation, either version 3 of the License, or\r
122 + * (at your option) any later version.\r
123 + *\r
124 + * This program is distributed in the hope that it will be useful,\r
125 + * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
126 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
127 + * GNU General Public License for more details.\r
128 + *\r
129 + * You should have received a copy of the GNU General Public License\r
130 + * along with this program.  If not, see http://www.gnu.org/licenses/ .\r
131 + *\r
132 + * Authors: Carl Worth <cworth@cworth.org>\r
133 + *          Keith Packard <keithp@keithp.com>\r
134 + *          Austin Clements <aclements@csail.mit.edu>\r
135 + */\r
136 +\r
137 +#include "notmuch-client.h"\r
138 +\r
139 +/* Context that gets inherited from the root node. */\r
140 +typedef struct mime_node_context {\r
141 +    /* Per-message resources.  These are allocated internally and must\r
142 +     * be destroyed. */\r
143 +    FILE *file;\r
144 +    GMimeStream *stream;\r
145 +    GMimeParser *parser;\r
146 +    GMimeMessage *mime_message;\r
147 +\r
148 +    /* Context provided by the caller. */\r
149 +    GMimeCipherContext *cryptoctx;\r
150 +    notmuch_bool_t decrypt;\r
151 +} mime_node_context_t;\r
152 +\r
153 +static int\r
154 +_mime_node_context_free (mime_node_context_t *res)\r
155 +{\r
156 +    if (res->mime_message)\r
157 +       g_object_unref (res->mime_message);\r
158 +\r
159 +    if (res->parser)\r
160 +       g_object_unref (res->parser);\r
161 +\r
162 +    if (res->stream)\r
163 +       g_object_unref (res->stream);\r
164 +\r
165 +    if (res->file)\r
166 +       fclose (res->file);\r
167 +\r
168 +    return 0;\r
169 +}\r
170 +\r
171 +notmuch_status_t\r
172 +mime_node_open (const void *ctx, notmuch_message_t *message,\r
173 +               GMimeCipherContext *cryptoctx, notmuch_bool_t decrypt,\r
174 +               mime_node_t **root_out)\r
175 +{\r
176 +    const char *filename = notmuch_message_get_filename (message);\r
177 +    mime_node_context_t *mctx;\r
178 +    mime_node_t *root;\r
179 +    notmuch_status_t status;\r
180 +\r
181 +    root = talloc_zero (ctx, mime_node_t);\r
182 +    if (root == NULL) {\r
183 +       fprintf (stderr, "Out of memory.\n");\r
184 +       status = NOTMUCH_STATUS_OUT_OF_MEMORY;\r
185 +       goto DONE;\r
186 +    }\r
187 +\r
188 +    /* Create the tree-wide context */\r
189 +    mctx = talloc_zero (root, mime_node_context_t);\r
190 +    if (mctx == NULL) {\r
191 +       fprintf (stderr, "Out of memory.\n");\r
192 +       status = NOTMUCH_STATUS_OUT_OF_MEMORY;\r
193 +       goto DONE;\r
194 +    }\r
195 +    talloc_set_destructor (mctx, _mime_node_context_free);\r
196 +\r
197 +    mctx->file = fopen (filename, "r");\r
198 +    if (! mctx->file) {\r
199 +       fprintf (stderr, "Error opening %s: %s\n", filename, strerror (errno));\r
200 +       status = NOTMUCH_STATUS_FILE_ERROR;\r
201 +       goto DONE;\r
202 +    }\r
203 +\r
204 +    mctx->stream = g_mime_stream_file_new (mctx->file);\r
205 +    g_mime_stream_file_set_owner (GMIME_STREAM_FILE (mctx->stream), FALSE);\r
206 +\r
207 +    mctx->parser = g_mime_parser_new_with_stream (mctx->stream);\r
208 +\r
209 +    mctx->mime_message = g_mime_parser_construct_message (mctx->parser);\r
210 +\r
211 +    mctx->cryptoctx = cryptoctx;\r
212 +    mctx->decrypt = decrypt;\r
213 +\r
214 +    /* Create the root node */\r
215 +    root->part = GMIME_OBJECT (mctx->mime_message);\r
216 +    root->envelope_file = message;\r
217 +    root->nchildren = 1;\r
218 +    root->ctx = mctx;\r
219 +\r
220 +    *root_out = root;\r
221 +    return NOTMUCH_STATUS_SUCCESS;\r
222 +\r
223 +DONE:\r
224 +    talloc_free (root);\r
225 +    return status;\r
226 +}\r
227 +\r
228 +static int\r
229 +_signature_validity_free (GMimeSignatureValidity **proxy)\r
230 +{\r
231 +    g_mime_signature_validity_free (*proxy);\r
232 +    return 0;\r
233 +}\r
234 +\r
235 +static mime_node_t *\r
236 +_mime_node_create (const mime_node_t *parent, GMimeObject *part)\r
237 +{\r
238 +    mime_node_t *node = talloc_zero (parent, mime_node_t);\r
239 +    GError *err = NULL;\r
240 +\r
241 +    /* Set basic node properties */\r
242 +    node->part = part;\r
243 +    node->ctx = parent->ctx;\r
244 +    if (!talloc_reference (node, node->ctx)) {\r
245 +       fprintf (stderr, "Out of memory.\n");\r
246 +       talloc_free (node);\r
247 +       return NULL;\r
248 +    }\r
249 +\r
250 +    /* Deal with the different types of parts */\r
251 +    if (GMIME_IS_PART (part)) {\r
252 +       node->nchildren = 0;\r
253 +    } else if (GMIME_IS_MULTIPART (part)) {\r
254 +       node->nchildren = g_mime_multipart_get_count (GMIME_MULTIPART (part));\r
255 +    } else if (GMIME_IS_MESSAGE_PART (part)) {\r
256 +       /* Promote part to an envelope and open it */\r
257 +       GMimeMessagePart *message_part = GMIME_MESSAGE_PART (part);\r
258 +       GMimeMessage *message = g_mime_message_part_get_message (message_part);\r
259 +       node->envelope_part = message_part;\r
260 +       node->part = GMIME_OBJECT (message);\r
261 +       node->nchildren = 1;\r
262 +    } else {\r
263 +       fprintf (stderr, "Warning: Unknown mime part type: %s.\n",\r
264 +                g_type_name (G_OBJECT_TYPE (part)));\r
265 +       talloc_free (node);\r
266 +       return NULL;\r
267 +    }\r
268 +\r
269 +    /* Handle PGP/MIME parts */\r
270 +    if (GMIME_IS_MULTIPART_ENCRYPTED (part)\r
271 +       && node->ctx->cryptoctx && node->ctx->decrypt) {\r
272 +       if (node->nchildren != 2) {\r
273 +           /* this violates RFC 3156 section 4, so we won't bother with it. */\r
274 +           fprintf (stderr, "Error: %d part(s) for a multipart/encrypted "\r
275 +                    "message (must be exactly 2)\n",\r
276 +                    node->nchildren);\r
277 +       } else {\r
278 +           GMimeMultipartEncrypted *encrypteddata =\r
279 +               GMIME_MULTIPART_ENCRYPTED (part);\r
280 +           node->decrypt_attempted = TRUE;\r
281 +           node->decrypted_child = g_mime_multipart_encrypted_decrypt\r
282 +               (encrypteddata, node->ctx->cryptoctx, &err);\r
283 +           if (node->decrypted_child) {\r
284 +               node->decrypt_success = node->sig_attempted = TRUE;\r
285 +               node->sig_validity = g_mime_multipart_encrypted_get_signature_validity (encrypteddata);\r
286 +           } else {\r
287 +               fprintf (stderr, "Failed to decrypt part: %s\n",\r
288 +                        (err ? err->message : "no error explanation given"));\r
289 +           }\r
290 +       }\r
291 +    } else if (GMIME_IS_MULTIPART_SIGNED (part) && node->ctx->cryptoctx) {\r
292 +       if (node->nchildren != 2) {\r
293 +           /* this violates RFC 3156 section 5, so we won't bother with it. */\r
294 +           fprintf (stderr, "Error: %d part(s) for a multipart/signed message "\r
295 +                    "(must be exactly 2)\n",\r
296 +                    node->nchildren);\r
297 +       } else {\r
298 +           /* For some reason the GMimeSignatureValidity returned\r
299 +            * here is not a const (inconsistent with that\r
300 +            * returned by\r
301 +            * g_mime_multipart_encrypted_get_signature_validity,\r
302 +            * and therefore needs to be properly disposed of.\r
303 +            *\r
304 +            * In GMime 2.6, they're both non-const, so we'll be able\r
305 +            * to clean up this asymmetry. */\r
306 +           GMimeSignatureValidity *sig_validity = g_mime_multipart_signed_verify\r
307 +               (GMIME_MULTIPART_SIGNED (part), node->ctx->cryptoctx, &err);\r
308 +           node->sig_attempted = TRUE;\r
309 +           node->sig_validity = sig_validity;\r
310 +           if (sig_validity) {\r
311 +               GMimeSignatureValidity **proxy =\r
312 +                   talloc (node, GMimeSignatureValidity *);\r
313 +               *proxy = sig_validity;\r
314 +               talloc_set_destructor (proxy, _signature_validity_free);\r
315 +           }\r
316 +       }\r
317 +    }\r
318 +\r
319 +    if (node->sig_attempted && !node->sig_validity)\r
320 +       fprintf (stderr, "Failed to verify signed part: %s\n",\r
321 +                (err ? err->message : "no error explanation given"));\r
322 +\r
323 +    if (err)\r
324 +       g_error_free (err);\r
325 +\r
326 +    return node;\r
327 +}\r
328 +\r
329 +mime_node_t *\r
330 +mime_node_child (const mime_node_t *parent, int child)\r
331 +{\r
332 +    GMimeObject *sub;\r
333 +\r
334 +    if (!parent || child < 0 || child >= parent->nchildren)\r
335 +       return NULL;\r
336 +\r
337 +    if (GMIME_IS_MULTIPART (parent->part)) {\r
338 +       if (child == 1 && parent->decrypted_child)\r
339 +           sub = parent->decrypted_child;\r
340 +       else\r
341 +           sub = g_mime_multipart_get_part\r
342 +               (GMIME_MULTIPART (parent->part), child);\r
343 +    } else if (GMIME_IS_MESSAGE (parent->part)) {\r
344 +       sub = g_mime_message_get_mime_part (GMIME_MESSAGE (parent->part));\r
345 +    } else {\r
346 +       /* This should have been caught by message_part_create */\r
347 +       INTERNAL_ERROR ("Unexpected GMimeObject type: %s",\r
348 +                       g_type_name (G_OBJECT_TYPE (parent->part)));\r
349 +    }\r
350 +    return _mime_node_create (parent, sub);\r
351 +}\r
352 diff --git a/notmuch-client.h b/notmuch-client.h\r
353 index c521efa..e23b51c 100644\r
354 --- a/notmuch-client.h\r
355 +++ b/notmuch-client.h\r
356 @@ -241,5 +241,88 @@ notmuch_run_hook (const char *db_path, const char *hook);\r
357  notmuch_bool_t\r
358  debugger_is_active (void);\r
359  \r
360 +/* mime-node.c */\r
361 +\r
362 +/* mime_node_t represents a single node in a MIME tree.  A MIME tree\r
363 + * abstracts the different ways of traversing different types of MIME\r
364 + * parts, allowing a MIME message to be viewed as a generic tree of\r
365 + * parts.  Message-type parts have one child, multipart-type parts\r
366 + * have multiple children, and leaf parts have zero children.\r
367 + */\r
368 +typedef struct mime_node {\r
369 +    /* The MIME object of this part.  This will be a GMimeMessage,\r
370 +     * GMimePart, GMimeMultipart, or a subclass of one of these.\r
371 +     *\r
372 +     * This will never be a GMimeMessagePart because GMimeMessagePart\r
373 +     * is structurally redundant with GMimeMessage.  If this part is a\r
374 +     * message (that is, 'part' is a GMimeMessage), then either\r
375 +     * envelope_file will be set to a notmuch_message_t (for top-level\r
376 +     * messages) or envelope_part will be set to a GMimeMessagePart\r
377 +     * (for embedded message parts).\r
378 +     */\r
379 +    GMimeObject *part;\r
380 +\r
381 +    /* If part is a GMimeMessage, these record the envelope of the\r
382 +     * message: either a notmuch_message_t representing a top-level\r
383 +     * message, or a GMimeMessagePart representing a MIME part\r
384 +     * containing a message.\r
385 +     */\r
386 +    notmuch_message_t *envelope_file;\r
387 +    GMimeMessagePart *envelope_part;\r
388 +\r
389 +    /* The number of children of this part. */\r
390 +    int nchildren;\r
391 +\r
392 +    /* True if decryption of this part was attempted. */\r
393 +    notmuch_bool_t decrypt_attempted;\r
394 +    /* True if decryption of this part's child succeeded.  In this\r
395 +     * case, the decrypted part is substituted for the second child of\r
396 +     * this part (which would usually be the encrypted data). */\r
397 +    notmuch_bool_t decrypt_success;\r
398 +\r
399 +    /* True if signature verification on this part was attempted. */\r
400 +    notmuch_bool_t sig_attempted;\r
401 +    /* For signed or encrypted containers, the validity of the\r
402 +     * signature.  May be NULL if signature verification failed.  If\r
403 +     * there are simply no signatures, this will be non-NULL with an\r
404 +     * empty signers list. */\r
405 +    const GMimeSignatureValidity *sig_validity;\r
406 +\r
407 +    /* Internal: Context inherited from the root iterator. */\r
408 +    struct mime_node_context *ctx;\r
409 +\r
410 +    /* Internal: For successfully decrypted multipart parts, the\r
411 +     * decrypted part to substitute for the second child. */\r
412 +    GMimeObject *decrypted_child;\r
413 +} mime_node_t;\r
414 +\r
415 +/* Construct a new MIME node pointing to the root message part of\r
416 + * message.  If cryptoctx is non-NULL, it will be used to verify\r
417 + * signatures on any child parts.  If decrypt is true, then cryptoctx\r
418 + * will additionally be used to decrypt any encrypted child parts.\r
419 + *\r
420 + * Return value:\r
421 + *\r
422 + * NOTMUCH_STATUS_SUCCESS: Root node is returned in *node_out.\r
423 + *\r
424 + * NOTMUCH_STATUS_FILE_ERROR: Failed to open message file.\r
425 + *\r
426 + * NOTMUCH_STATUS_OUT_OF_MEMORY: Out of memory.\r
427 + */\r
428 +notmuch_status_t\r
429 +mime_node_open (const void *ctx, notmuch_message_t *message,\r
430 +               GMimeCipherContext *cryptoctx, notmuch_bool_t decrypt,\r
431 +               mime_node_t **node_out);\r
432 +\r
433 +/* Return a new MIME node for the requested child part of parent.\r
434 + * parent will be used as the talloc context for the returned child\r
435 + * node.\r
436 + *\r
437 + * In case of any failure, this function returns NULL, (after printing\r
438 + * an error message on stderr).\r
439 + */\r
440 +mime_node_t *\r
441 +mime_node_child (const mime_node_t *parent, int child);\r
442 +\r
443  #include "command-line-arguments.h"\r
444  #endif\r
445 -- \r
446 1.7.7.3\r
447 \r