Re: [PATCH 1/2] Add Google Inc. to AUTHORS as a contributor.
[notmuch-archives.git] / e3 / 4dd8c5ec97bf27c7c2efe2dd24c778eb9e9bf3
1 Return-Path: <jani@nikula.org>\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 63CC9429E25\r
6         for <notmuch@notmuchmail.org>; Tue, 29 Nov 2011 11:12:00 -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 wTtF7DwC5tuP for <notmuch@notmuchmail.org>;\r
16         Tue, 29 Nov 2011 11:11:59 -0800 (PST)\r
17 Received: from mail-bw0-f53.google.com (mail-bw0-f53.google.com\r
18         [209.85.214.53]) (using TLSv1 with cipher RC4-SHA (128/128 bits))\r
19         (No client certificate requested)\r
20         by olra.theworths.org (Postfix) with ESMTPS id 9F3AE431FB6\r
21         for <notmuch@notmuchmail.org>; Tue, 29 Nov 2011 11:11:58 -0800 (PST)\r
22 Received: by bkaq10 with SMTP id q10so11429066bka.26\r
23         for <notmuch@notmuchmail.org>; Tue, 29 Nov 2011 11:11:55 -0800 (PST)\r
24 Received: by 10.204.133.197 with SMTP id g5mr50160418bkt.43.1322593915613;\r
25         Tue, 29 Nov 2011 11:11:55 -0800 (PST)\r
26 Received: from localhost (dsl-hkibrasgw4-fe5cdc00-23.dhcp.inet.fi.\r
27         [80.220.92.23])\r
28         by mx.google.com with ESMTPS id iu9sm5294958bkc.0.2011.11.29.11.11.52\r
29         (version=SSLv3 cipher=OTHER); Tue, 29 Nov 2011 11:11:53 -0800 (PST)\r
30 From: Jani Nikula <jani@nikula.org>\r
31 To: Austin Clements <amdragon@MIT.EDU>, notmuch@notmuchmail.org\r
32 Subject: Re: [PATCH 2/4] Introduce a generic tree-like abstraction for MIME\r
33         traversal.\r
34 In-Reply-To: <1322446871-14986-3-git-send-email-amdragon@mit.edu>\r
35 References: <1322446871-14986-1-git-send-email-amdragon@mit.edu>\r
36         <1322446871-14986-3-git-send-email-amdragon@mit.edu>\r
37 User-Agent: Notmuch/0.10+51~gef3ae74 (http://notmuchmail.org) Emacs/23.3.1\r
38         (i686-pc-linux-gnu)\r
39 Date: Tue, 29 Nov 2011 21:11:49 +0200\r
40 Message-ID: <8739d6u4ju.fsf@nikula.org>\r
41 MIME-Version: 1.0\r
42 Content-Type: text/plain; charset=utf-8\r
43 Content-Transfer-Encoding: quoted-printable\r
44 Cc: dkg@fifthhorseman.net\r
45 X-BeenThere: notmuch@notmuchmail.org\r
46 X-Mailman-Version: 2.1.13\r
47 Precedence: list\r
48 List-Id: "Use and development of the notmuch mail system."\r
49         <notmuch.notmuchmail.org>\r
50 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
51         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
52 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
53 List-Post: <mailto:notmuch@notmuchmail.org>\r
54 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
55 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
56         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
57 X-List-Received-Date: Tue, 29 Nov 2011 19:12:00 -0000\r
58 \r
59 \r
60 Hi, generally looks good to me, but please find a few comments below.\r
61 \r
62 BR,\r
63 Jani.\r
64 \r
65 On Sun, 27 Nov 2011 21:21:09 -0500, Austin Clements <amdragon@MIT.EDU> wrot=\r
66 e:\r
67 > This wraps all of the complex MIME part handling in a single, simple\r
68 > function that gets part N from *any* MIME object, so traversing a MIME\r
69 > part tree becomes a two-line for loop.  Furthermore, the MIME node\r
70 > structure provides easy access to envelopes for message parts as well\r
71 > as cryptographic information.\r
72 >=20\r
73 > This code is directly derived from the current show_message_body code\r
74 > (much of it is identical), but the control relation is inverted:\r
75 > instead of show_message_body controlling the traversal of the MIME\r
76 > structure and invoking callbacks, the caller controls the traversal of\r
77 > the MIME structure.\r
78 > ---\r
79 >  Makefile.local   |    1 +\r
80 >  mime-node.c      |  234 ++++++++++++++++++++++++++++++++++++++++++++++++=\r
81 ++++++\r
82 >  notmuch-client.h |   80 ++++++++++++++++++\r
83 >  3 files changed, 315 insertions(+), 0 deletions(-)\r
84 >  create mode 100644 mime-node.c\r
85 >=20\r
86 > diff --git a/Makefile.local b/Makefile.local\r
87 > index c94402b..c46ed26 100644\r
88 > --- a/Makefile.local\r
89 > +++ b/Makefile.local\r
90 > @@ -312,6 +312,7 @@ notmuch_client_srcs =3D           \\r
91 >       notmuch-time.c          \\r
92 >       query-string.c          \\r
93 >       show-message.c          \\r
94 > +     mime-node.c             \\r
95 >       json.c\r
96 >=20=20\r
97 >  notmuch_client_modules =3D $(notmuch_client_srcs:.c=3D.o)\r
98 > diff --git a/mime-node.c b/mime-node.c\r
99 > new file mode 100644\r
100 > index 0000000..942738b\r
101 > --- /dev/null\r
102 > +++ b/mime-node.c\r
103 > @@ -0,0 +1,234 @@\r
104 > +/* notmuch - Not much of an email program, (just index and search)\r
105 > + *\r
106 > + * Copyright =C2=A9 2009 Carl Worth\r
107 > + * Copyright =C2=A9 2009 Keith Packard\r
108 > + *\r
109 > + * This program is free software: you can redistribute it and/or modify\r
110 > + * it under the terms of the GNU General Public License as published by\r
111 > + * the Free Software Foundation, either version 3 of the License, or\r
112 > + * (at your option) any later version.\r
113 > + *\r
114 > + * This program is distributed in the hope that it will be useful,\r
115 > + * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
116 > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
117 > + * GNU General Public License for more details.\r
118 > + *\r
119 > + * You should have received a copy of the GNU General Public License\r
120 > + * along with this program.  If not, see http://www.gnu.org/licenses/ .\r
121 > + *\r
122 > + * Authors: Carl Worth <cworth@cworth.org>\r
123 > + *          Keith Packard <keithp@keithp.com>\r
124 > + *          Austin Clements <aclements@csail.mit.edu>\r
125 > + */\r
126 > +\r
127 > +#include "notmuch-client.h"\r
128 > +\r
129 > +/* Context that gets inherited from the root node. */\r
130 > +typedef struct mime_node_context {\r
131 > +    /* Per-message resources.  These are allocated internally and must\r
132 > +     * be destroyed. */\r
133 > +    FILE *file;\r
134 > +    GMimeStream *stream;\r
135 > +    GMimeParser *parser;\r
136 > +    GMimeMessage *mime_message;\r
137 > +=20=20=20=20\r
138 \r
139 Leftover indentation spaces above.\r
140 \r
141 > +    /* Context provided by the caller. */\r
142 > +    GMimeCipherContext *cryptoctx;\r
143 > +    notmuch_bool_t decrypt;\r
144 > +} mime_node_context_t;\r
145 > +\r
146 > +static int\r
147 > +_mime_node_context_free (mime_node_context_t *res)\r
148 > +{\r
149 > +    if (res->mime_message)\r
150 > +     g_object_unref (res->mime_message);\r
151 > +\r
152 > +    if (res->parser)\r
153 > +     g_object_unref (res->parser);\r
154 > +\r
155 > +    if (res->stream)\r
156 > +     g_object_unref (res->stream);\r
157 > +\r
158 > +    if (res->file)\r
159 > +     fclose (res->file);\r
160 > +\r
161 > +    return 0;\r
162 > +}\r
163 > +\r
164 > +notmuch_status_t\r
165 > +mime_node_open (const void *ctx, notmuch_message_t *message,\r
166 > +             GMimeCipherContext* cryptoctx, notmuch_bool_t decrypt,\r
167 \r
168 The style here seems to be * next to the variable name, not type.\r
169 \r
170 > +             mime_node_t **root_out)\r
171 > +{\r
172 > +    const char *filename =3D notmuch_message_get_filename (message);\r
173 > +    mime_node_context_t *mctx;\r
174 > +    mime_node_t *root =3D NULL;\r
175 \r
176 No need to initialize as it's initialized right away below.\r
177 \r
178 > +    notmuch_status_t status;\r
179 > +\r
180 > +    root =3D talloc_zero (ctx, mime_node_t);\r
181 > +    if (root =3D=3D NULL) {\r
182 > +     fprintf (stderr, "Out of memory.\n");\r
183 > +     status =3D NOTMUCH_STATUS_OUT_OF_MEMORY;\r
184 > +     goto DONE;\r
185 > +    }\r
186 > +\r
187 > +    /* Create the tree-wide context */\r
188 > +    mctx =3D talloc_zero (root, mime_node_context_t);\r
189 > +    if (mctx =3D=3D NULL) {\r
190 > +     fprintf (stderr, "Out of memory.\n");\r
191 > +     status =3D NOTMUCH_STATUS_OUT_OF_MEMORY;\r
192 > +     goto DONE;\r
193 > +    }\r
194 > +    talloc_set_destructor (mctx, _mime_node_context_free);\r
195 > +\r
196 > +    mctx->file =3D fopen (filename, "r");\r
197 > +    if (! mctx->file) {\r
198 > +     fprintf (stderr, "Error opening %s: %s\n", filename, strerror (errno));\r
199 > +     status =3D NOTMUCH_STATUS_FILE_ERROR;\r
200 > +     goto DONE;\r
201 > +    }\r
202 > +\r
203 > +    mctx->stream =3D g_mime_stream_file_new (mctx->file);\r
204 \r
205 AFAICT the GMimeStreamFile object owns the FILE * pointer now, and\r
206 closes it later. Calling fclose() on it in _mime_node_context_free()\r
207 would be undefined behaviour. But please don't just take my word for it.\r
208 \r
209 > +    g_mime_stream_file_set_owner (GMIME_STREAM_FILE (mctx->stream), FALS=\r
210 E);\r
211 > +\r
212 > +    mctx->parser =3D g_mime_parser_new_with_stream (mctx->stream);\r
213 > +\r
214 > +    mctx->mime_message =3D g_mime_parser_construct_message (mctx->parser=\r
215 );\r
216 > +\r
217 > +    mctx->cryptoctx =3D cryptoctx;\r
218 > +    mctx->decrypt =3D decrypt;\r
219 > +\r
220 > +    /* Create the root node */\r
221 > +    root->part =3D GMIME_OBJECT (mctx->mime_message);\r
222 > +    root->envelope_file =3D message;\r
223 > +    root->children =3D 1;\r
224 > +    root->ctx =3D mctx;\r
225 > +\r
226 > +    *root_out =3D root;\r
227 > +    return NOTMUCH_STATUS_SUCCESS;\r
228 > +\r
229 > +DONE:\r
230 > +    talloc_free (root);\r
231 > +    return status;\r
232 > +}\r
233 > +\r
234 > +static int\r
235 > +_signature_validity_free (GMimeSignatureValidity **proxy)\r
236 > +{\r
237 > +    g_mime_signature_validity_free (*proxy);\r
238 > +    return 0;\r
239 > +}\r
240 > +\r
241 > +static mime_node_t *\r
242 > +_mime_node_create (const mime_node_t *parent, GMimeObject *part)\r
243 > +{\r
244 > +    mime_node_t *out =3D talloc_zero (parent, mime_node_t);\r
245 > +    GError *err =3D NULL;\r
246 > +\r
247 > +    /* Set basic node properties */\r
248 > +    out->part =3D part;\r
249 > +    out->ctx =3D parent->ctx;\r
250 > +    if (!talloc_reference (out, out->ctx)) {\r
251 > +     fprintf (stderr, "Out of memory.\n");\r
252 > +     talloc_free (out);\r
253 > +     return NULL;\r
254 > +    }\r
255 > +\r
256 > +    /* Deal with the different types of parts */\r
257 > +    if (GMIME_IS_PART (part)) {\r
258 > +     out->children =3D 0;\r
259 > +    } else if (GMIME_IS_MULTIPART (part)) {\r
260 > +     out->children =3D g_mime_multipart_get_count (GMIME_MULTIPART (part));\r
261 > +    } else if (GMIME_IS_MESSAGE_PART (part)) {\r
262 > +     /* Promote part to an envelope and open it */\r
263 > +     GMimeMessagePart *message_part =3D GMIME_MESSAGE_PART (part);\r
264 > +     GMimeMessage *message =3D g_mime_message_part_get_message (message_part=\r
265 );\r
266 > +     out->envelope_part =3D message_part;\r
267 > +     out->part =3D GMIME_OBJECT (message);\r
268 > +     out->children =3D 1;\r
269 > +    } else {\r
270 > +     fprintf (stderr, "Warning: Unknown mime part type: %s.\n",\r
271 > +              g_type_name (G_OBJECT_TYPE (part)));\r
272 > +     talloc_free (out);\r
273 > +     return NULL;\r
274 > +    }\r
275 > +\r
276 > +    /* Handle PGP/MIME parts */\r
277 > +    if (GMIME_IS_MULTIPART_ENCRYPTED (part) && out->ctx->decrypt) {\r
278 > +     if (out->children !=3D 2) {\r
279 > +         /* this violates RFC 3156 section 4, so we won't bother with it. */\r
280 > +         fprintf (stderr, "Error: %d part(s) for a multipart/encrypted "\r
281 > +                  "message (should be exactly 2)\n",\r
282 > +                  out->children);\r
283 > +     } else {\r
284 > +         out->is_encrypted =3D TRUE;\r
285 > +         GMimeMultipartEncrypted *encrypteddata =3D\r
286 > +             GMIME_MULTIPART_ENCRYPTED (part);\r
287 > +         out->decrypted_child =3D g_mime_multipart_encrypted_decrypt\r
288 > +             (encrypteddata, out->ctx->cryptoctx, &err);\r
289 > +         if (out->decrypted_child) {\r
290 > +             out->decrypt_success =3D TRUE;\r
291 > +             out->is_signed =3D TRUE;\r
292 > +             out->sig_validity =3D g_mime_multipart_encrypted_get_signature_validit=\r
293 y (encrypteddata);\r
294 > +         } else {\r
295 > +             fprintf (stderr, "Failed to decrypt part: %s\n",\r
296 > +                      (err ? err->message : "no error explanation given"));\r
297 > +         }\r
298 > +     }\r
299 > +    } else if (GMIME_IS_MULTIPART_SIGNED (part) && out->ctx->cryptoctx) {\r
300 > +     if (out->children !=3D 2) {\r
301 > +         /* this violates RFC 3156 section 5, so we won't bother with it. */\r
302 > +         fprintf (stderr, "Error: %d part(s) for a multipart/signed message "\r
303 > +                  "(should be exactly 2)\n",\r
304 > +                  out->children);\r
305 > +     } else {\r
306 > +         out->is_signed =3D TRUE;\r
307 > +         /* For some reason the GMimeSignatureValidity returned\r
308 > +          * here is not a const (inconsistent with that\r
309 > +          * returned by\r
310 > +          * g_mime_multipart_encrypted_get_signature_validity,\r
311 > +          * and therefore needs to be properly disposed of.\r
312 > +          * Hopefully the API will become more consistent. */\r
313 > +         GMimeSignatureValidity *sig_validity =3D g_mime_multipart_signed_ve=\r
314 rify\r
315 > +             (GMIME_MULTIPART_SIGNED (part), out->ctx->cryptoctx, &err);\r
316 > +         out->sig_validity =3D sig_validity;\r
317 > +         if (sig_validity) {\r
318 > +             GMimeSignatureValidity **proxy =3D\r
319 > +                 talloc (out, GMimeSignatureValidity *);\r
320 > +             *proxy =3D sig_validity;\r
321 > +             talloc_set_destructor (proxy, _signature_validity_free);\r
322 > +         }\r
323 > +     }\r
324 > +    }\r
325 > +\r
326 > +    if (out->is_signed && !out->sig_validity)\r
327 > +     fprintf (stderr, "Failed to verify signed part: %s\n",\r
328 > +              (err ? err->message : "no error explanation given"));\r
329 > +\r
330 > +    if (err)\r
331 > +     g_error_free (err);\r
332 > +\r
333 > +    return out;\r
334 > +}\r
335 > +\r
336 > +mime_node_t *\r
337 > +mime_node_child (const mime_node_t *parent, int child)\r
338 > +{\r
339 > +    if (!parent || child < 0 || child >=3D parent->children)\r
340 > +     return NULL;\r
341 > +\r
342 > +    if (GMIME_IS_MULTIPART (parent->part)) {\r
343 > +     GMimeMultipart *multipart =3D GMIME_MULTIPART (parent->part);\r
344 > +     if (child =3D=3D 1 && parent->decrypted_child)\r
345 > +         return _mime_node_create (parent, parent->decrypted_child);\r
346 > +     return _mime_node_create (parent, g_mime_multipart_get_part (multipart,=\r
347  child));\r
348 > +    } else if (GMIME_IS_MESSAGE (parent->part)) {\r
349 > +     GMimeMessage *message =3D GMIME_MESSAGE (parent->part);\r
350 > +     GMimeObject *child =3D g_mime_message_get_mime_part (message);\r
351 > +     return _mime_node_create (parent, child);\r
352 > +    } else {\r
353 > +     /* This should have been caught by message_part_create */\r
354 > +     INTERNAL_ERROR ("Unexpected GMimeObject type: %s",\r
355 > +                     g_type_name (G_OBJECT_TYPE (parent->part)));\r
356 > +    }\r
357 > +}\r
358 > diff --git a/notmuch-client.h b/notmuch-client.h\r
359 > index d7fb6ee..58bd21c 100644\r
360 > --- a/notmuch-client.h\r
361 > +++ b/notmuch-client.h\r
362 > @@ -238,4 +238,84 @@ notmuch_config_set_maildir_synchronize_flags (notmuc=\r
363 h_config_t *config,\r
364 >  notmuch_bool_t\r
365 >  debugger_is_active (void);\r
366 >=20=20\r
367 > +/* mime-node.c */\r
368 > +\r
369 > +/* mime_node_t represents a single node in a MIME tree.  A MIME tree\r
370 > + * abstracts the different ways of traversing different types of MIME\r
371 > + * parts, allowing a MIME message to be viewed as a generic tree of\r
372 > + * parts.  Message-type parts have one child, multipart-type parts\r
373 > + * have multiple children, and leaf parts have zero children.\r
374 > + */\r
375 > +typedef struct mime_node {\r
376 > +    /* The MIME object of this part.  This will be a GMimeMessage,\r
377 > +     * GMimePart, GMimeMultipart, or a subclass of one of these.\r
378 > +     *\r
379 > +     * This will never be a GMimeMessagePart because GMimeMessagePart\r
380 > +     * is structurally redundant with GMimeMessage.  If this part is a\r
381 > +     * message (that is, 'part' is a GMimeMessage), then either\r
382 > +     * envelope_file will be set to a notmuch_message_t (for top-level\r
383 > +     * messages) or envelope_part will be set to a GMimeMessagePart\r
384 > +     * (for embedded message parts).\r
385 > +     */\r
386 > +    GMimeObject *part;\r
387 > +\r
388 > +    /* If part is a GMimeMessage, these record the envelope of the\r
389 > +     * message: either a notmuch_message_t representing a top-level\r
390 > +     * message, or a GMimeMessagePart representing a MIME part\r
391 > +     * containing a message.\r
392 > +     */\r
393 > +    notmuch_message_t *envelope_file;\r
394 > +    GMimeMessagePart *envelope_part;\r
395 > +\r
396 > +    /* The number of children of this part. */\r
397 > +    int children;\r
398 > +\r
399 > +    /* True if this is the container for an encrypted or signed part.\r
400 > +     * This does *not* mean that decryption or signature verification\r
401 > +     * succeeded. */\r
402 > +    notmuch_bool_t is_encrypted, is_signed;\r
403 > +    /* True if decryption of this part's child succeeded.  In this\r
404 > +     * case, the decrypted part is substituted for the second child of\r
405 > +     * this part (which would usually be the encrypted data). */\r
406 > +    notmuch_bool_t decrypt_success;\r
407 > +    /* For signed or encrypted containers, the validity of the\r
408 > +     * signature.  May be NULL if signature verification failed. */\r
409 > +    const GMimeSignatureValidity *sig_validity;\r
410 > +\r
411 > +    /* Internal: Context inherited from the root iterator. */\r
412 > +    struct mime_node_context *ctx;\r
413 > +\r
414 > +    /* Internal: For successfully decrypted multipart parts, the\r
415 > +     * decrypted part to substitute for the second child. */\r
416 > +    GMimeObject *decrypted_child;\r
417 > +} mime_node_t;\r
418 > +\r
419 > +/* Construct a new MIME node pointing to the root message part of\r
420 > + * message.  If cryptoctx is non-NULL, it will be used to verify\r
421 > + * signatures on any child parts.  If decrypt is true, then cryptoctx\r
422 > + * will additionally be used to decrypt any encrypted child parts.\r
423 > + *\r
424 > + * Return value:\r
425 > + *\r
426 > + * NOTMUCH_STATUS_SUCCESS: Root node is returned in *node_out.\r
427 > + *\r
428 > + * NOTMUCH_STATUS_FILE_ERROR: Failed to open message file.\r
429 > + *\r
430 > + * NOTMUCH_STATUS_OUT_OF_MEMORY: Out of memory.\r
431 > + */\r
432 > +notmuch_status_t\r
433 > +mime_node_open (const void *ctx, notmuch_message_t *message,\r
434 > +             GMimeCipherContext* cryptoctx, notmuch_bool_t decrypt,\r
435 > +             mime_node_t **node_out);\r
436 > +\r
437 > +/* Return a new MIME node for the requested child part of parent.\r
438 > + * parent will be used as the talloc context for the returned child\r
439 > + * node.\r
440 > + *\r
441 > + * In case of any failure, this function returns NULL, (after printing\r
442 > + * an error message on stderr).\r
443 > + */\r
444 > +mime_node_t *\r
445 > +mime_node_child (const mime_node_t *parent, int child);\r
446 > +\r
447 >  #endif\r
448 > --=20\r
449 > 1.7.5.4\r
450 >=20\r
451 > _______________________________________________\r
452 > notmuch mailing list\r
453 > notmuch@notmuchmail.org\r
454 > http://notmuchmail.org/mailman/listinfo/notmuch\r