[Patch v5 7/8] lib: replace almost all fprintfs in library with _n_d_log
[notmuch-archives.git] / c3 / 9bd70186fe4032ff5d3f0e9f843445c3cfa902
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 0775F431FB6\r
6         for <notmuch@notmuchmail.org>; Sun, 27 Nov 2011 18:21:33 -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 RY53HJDbq1fF for <notmuch@notmuchmail.org>;\r
16         Sun, 27 Nov 2011 18:21:31 -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 EAD42429E28\r
20         for <notmuch@notmuchmail.org>; Sun, 27 Nov 2011 18:21:30 -0800 (PST)\r
21 X-AuditID: 12074422-b7ff56d00000092f-3a-4ed2f02aed69\r
22 Received: from mailhub-auth-3.mit.edu ( [18.9.21.43])\r
23         by dmz-mailsec-scanner-5.mit.edu (Symantec Messaging Gateway) with SMTP\r
24         id 96.86.02351.A20F2DE4; Sun, 27 Nov 2011 21:21:30 -0500 (EST)\r
25 Received: from outgoing.mit.edu (OUTGOING-AUTH.MIT.EDU [18.7.22.103])\r
26         by mailhub-auth-3.mit.edu (8.13.8/8.9.2) with ESMTP id pAS2LU0l020230; \r
27         Sun, 27 Nov 2011 21:21:30 -0500\r
28 Received: from drake.mit.edu\r
29         (209-6-116-242.c3-0.arl-ubr1.sbo-arl.ma.cable.rcn.com\r
30         [209.6.116.242]) (authenticated bits=0)\r
31         (User authenticated as amdragon@ATHENA.MIT.EDU)\r
32         by outgoing.mit.edu (8.13.6/8.12.4) with ESMTP id pAS2LSQ1020652\r
33         (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=NOT);\r
34         Sun, 27 Nov 2011 21:21:29 -0500 (EST)\r
35 Received: from amthrax by drake.mit.edu with local (Exim 4.76)\r
36         (envelope-from <amthrax@drake.mit.edu>)\r
37         id 1RUqqW-0003vR-Hb; Sun, 27 Nov 2011 21:21:28 -0500\r
38 From: Austin Clements <amdragon@MIT.EDU>\r
39 To: notmuch@notmuchmail.org\r
40 Subject:\r
41  [PATCH 2/4] Introduce a generic tree-like abstraction for MIME traversal.\r
42 Date: Sun, 27 Nov 2011 21:21:09 -0500\r
43 Message-Id: <1322446871-14986-3-git-send-email-amdragon@mit.edu>\r
44 X-Mailer: git-send-email 1.7.5.4\r
45 In-Reply-To: <1322446871-14986-1-git-send-email-amdragon@mit.edu>\r
46 References: <1322446871-14986-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+NgFlrAKsWRmVeSWpSXmKPExsUixCmqrav14ZKfwfQnchat3Z+ZLK5u7We3\r
52         2LPPy+L6zZnMDiweZ7vbWT3unuby2DnrLrvHs1W3mANYorhsUlJzMstSi/TtErgyNkxkLWhL\r
53         qZh6tJ+xgfGzXxcjJ4eEgInEy/N/mSBsMYkL99azdTFycQgJ7GOUaPt6jRHC2cAo8fj0UnYI\r
54         5z6TxNrGBqiy+YwSz88/B+tnE9CQ2LZ/OSOILSIgLbHz7mxWEJtZoE6ic+MZdhBbWKBMovHK\r
55         YaAaDg4WAVWJXVMSQcK8Ag4SL89ehTpDQeLXkVVgrZwCjhJzm7Ywg9hCQDXrlh1lhagXlDg5\r
56         8wkLyBhmAXWJ9fOEIDbJSzRvnc08gVFoFpKqWQhVs5BULWBkXsUom5JbpZubmJlTnJqsW5yc\r
57         mJeXWqRrqpebWaKXmlK6iREU+OwuSjsYfx5UOsQowMGoxMO78fIlPyHWxLLiytxDjJIcTEqi\r
58         vC/eAYX4kvJTKjMSizPii0pzUosPMUpwMCuJ8PYcBcrxpiRWVqUW5cOkpDlYlMR5uXY6+AkJ\r
59         pCeWpGanphakFsFkZTg4lCR4k94DNQoWpaanVqRl5pQgpJk4OEGG8wANDwGp4S0uSMwtzkyH\r
60         yJ9i1OXouH/9NKMQS15+XqqUOG8OSJEASFFGaR7cHFjCesUoDvSWMG84SBUPMNnBTXoFtIQJ\r
61         aAnHzAsgS0oSEVJSDYwlM2enBZ7OMWxfcPG/YjJ769eaKg7//bEvF58N2b31SJWAZOWnbxG7\r
62         f5RoZ77+GB2st1/pVmHFs+XpL/v/vrFqtN53mXerT8HRwuMbFn+8OkVK1EW12vZLAtsrg7XL\r
63         ek7dO9789Mufq2Ebj3MZhaaXmy3902Hdel/8b0NDSJAFa5zf5lZ/t2olluKMREMt5qLiRADL\r
64         Y1SFMwMAAA==\r
65 Cc: dkg@fifthhorseman.net\r
66 X-BeenThere: notmuch@notmuchmail.org\r
67 X-Mailman-Version: 2.1.13\r
68 Precedence: list\r
69 List-Id: "Use and development of the notmuch mail system."\r
70         <notmuch.notmuchmail.org>\r
71 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
72         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
73 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
74 List-Post: <mailto:notmuch@notmuchmail.org>\r
75 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
76 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
77         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
78 X-List-Received-Date: Mon, 28 Nov 2011 02:21:33 -0000\r
79 \r
80 This wraps all of the complex MIME part handling in a single, simple\r
81 function that gets part N from *any* MIME object, so traversing a MIME\r
82 part tree becomes a two-line for loop.  Furthermore, the MIME node\r
83 structure provides easy access to envelopes for message parts as well\r
84 as cryptographic information.\r
85 \r
86 This code is directly derived from the current show_message_body code\r
87 (much of it is identical), but the control relation is inverted:\r
88 instead of show_message_body controlling the traversal of the MIME\r
89 structure and invoking callbacks, the caller controls the traversal of\r
90 the MIME structure.\r
91 ---\r
92  Makefile.local   |    1 +\r
93  mime-node.c      |  234 ++++++++++++++++++++++++++++++++++++++++++++++++++++++\r
94  notmuch-client.h |   80 ++++++++++++++++++\r
95  3 files changed, 315 insertions(+), 0 deletions(-)\r
96  create mode 100644 mime-node.c\r
97 \r
98 diff --git a/Makefile.local b/Makefile.local\r
99 index c94402b..c46ed26 100644\r
100 --- a/Makefile.local\r
101 +++ b/Makefile.local\r
102 @@ -312,6 +312,7 @@ notmuch_client_srcs =               \\r
103         notmuch-time.c          \\r
104         query-string.c          \\r
105         show-message.c          \\r
106 +       mime-node.c             \\r
107         json.c\r
108  \r
109  notmuch_client_modules = $(notmuch_client_srcs:.c=.o)\r
110 diff --git a/mime-node.c b/mime-node.c\r
111 new file mode 100644\r
112 index 0000000..942738b\r
113 --- /dev/null\r
114 +++ b/mime-node.c\r
115 @@ -0,0 +1,234 @@\r
116 +/* notmuch - Not much of an email program, (just index and search)\r
117 + *\r
118 + * Copyright © 2009 Carl Worth\r
119 + * Copyright © 2009 Keith Packard\r
120 + *\r
121 + * This program is free software: you can redistribute it and/or modify\r
122 + * it under the terms of the GNU General Public License as published by\r
123 + * the Free Software Foundation, either version 3 of the License, or\r
124 + * (at your option) any later version.\r
125 + *\r
126 + * This program is distributed in the hope that it will be useful,\r
127 + * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
128 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
129 + * GNU General Public License for more details.\r
130 + *\r
131 + * You should have received a copy of the GNU General Public License\r
132 + * along with this program.  If not, see http://www.gnu.org/licenses/ .\r
133 + *\r
134 + * Authors: Carl Worth <cworth@cworth.org>\r
135 + *          Keith Packard <keithp@keithp.com>\r
136 + *          Austin Clements <aclements@csail.mit.edu>\r
137 + */\r
138 +\r
139 +#include "notmuch-client.h"\r
140 +\r
141 +/* Context that gets inherited from the root node. */\r
142 +typedef struct mime_node_context {\r
143 +    /* Per-message resources.  These are allocated internally and must\r
144 +     * be destroyed. */\r
145 +    FILE *file;\r
146 +    GMimeStream *stream;\r
147 +    GMimeParser *parser;\r
148 +    GMimeMessage *mime_message;\r
149 +    \r
150 +    /* Context provided by the caller. */\r
151 +    GMimeCipherContext *cryptoctx;\r
152 +    notmuch_bool_t decrypt;\r
153 +} mime_node_context_t;\r
154 +\r
155 +static int\r
156 +_mime_node_context_free (mime_node_context_t *res)\r
157 +{\r
158 +    if (res->mime_message)\r
159 +       g_object_unref (res->mime_message);\r
160 +\r
161 +    if (res->parser)\r
162 +       g_object_unref (res->parser);\r
163 +\r
164 +    if (res->stream)\r
165 +       g_object_unref (res->stream);\r
166 +\r
167 +    if (res->file)\r
168 +       fclose (res->file);\r
169 +\r
170 +    return 0;\r
171 +}\r
172 +\r
173 +notmuch_status_t\r
174 +mime_node_open (const void *ctx, notmuch_message_t *message,\r
175 +               GMimeCipherContext* cryptoctx, notmuch_bool_t decrypt,\r
176 +               mime_node_t **root_out)\r
177 +{\r
178 +    const char *filename = notmuch_message_get_filename (message);\r
179 +    mime_node_context_t *mctx;\r
180 +    mime_node_t *root = NULL;\r
181 +    notmuch_status_t status;\r
182 +\r
183 +    root = talloc_zero (ctx, mime_node_t);\r
184 +    if (root == NULL) {\r
185 +       fprintf (stderr, "Out of memory.\n");\r
186 +       status = NOTMUCH_STATUS_OUT_OF_MEMORY;\r
187 +       goto DONE;\r
188 +    }\r
189 +\r
190 +    /* Create the tree-wide context */\r
191 +    mctx = talloc_zero (root, mime_node_context_t);\r
192 +    if (mctx == NULL) {\r
193 +       fprintf (stderr, "Out of memory.\n");\r
194 +       status = NOTMUCH_STATUS_OUT_OF_MEMORY;\r
195 +       goto DONE;\r
196 +    }\r
197 +    talloc_set_destructor (mctx, _mime_node_context_free);\r
198 +\r
199 +    mctx->file = fopen (filename, "r");\r
200 +    if (! mctx->file) {\r
201 +       fprintf (stderr, "Error opening %s: %s\n", filename, strerror (errno));\r
202 +       status = NOTMUCH_STATUS_FILE_ERROR;\r
203 +       goto DONE;\r
204 +    }\r
205 +\r
206 +    mctx->stream = g_mime_stream_file_new (mctx->file);\r
207 +    g_mime_stream_file_set_owner (GMIME_STREAM_FILE (mctx->stream), FALSE);\r
208 +\r
209 +    mctx->parser = g_mime_parser_new_with_stream (mctx->stream);\r
210 +\r
211 +    mctx->mime_message = g_mime_parser_construct_message (mctx->parser);\r
212 +\r
213 +    mctx->cryptoctx = cryptoctx;\r
214 +    mctx->decrypt = decrypt;\r
215 +\r
216 +    /* Create the root node */\r
217 +    root->part = GMIME_OBJECT (mctx->mime_message);\r
218 +    root->envelope_file = message;\r
219 +    root->children = 1;\r
220 +    root->ctx = mctx;\r
221 +\r
222 +    *root_out = root;\r
223 +    return NOTMUCH_STATUS_SUCCESS;\r
224 +\r
225 +DONE:\r
226 +    talloc_free (root);\r
227 +    return status;\r
228 +}\r
229 +\r
230 +static int\r
231 +_signature_validity_free (GMimeSignatureValidity **proxy)\r
232 +{\r
233 +    g_mime_signature_validity_free (*proxy);\r
234 +    return 0;\r
235 +}\r
236 +\r
237 +static mime_node_t *\r
238 +_mime_node_create (const mime_node_t *parent, GMimeObject *part)\r
239 +{\r
240 +    mime_node_t *out = talloc_zero (parent, mime_node_t);\r
241 +    GError *err = NULL;\r
242 +\r
243 +    /* Set basic node properties */\r
244 +    out->part = part;\r
245 +    out->ctx = parent->ctx;\r
246 +    if (!talloc_reference (out, out->ctx)) {\r
247 +       fprintf (stderr, "Out of memory.\n");\r
248 +       talloc_free (out);\r
249 +       return NULL;\r
250 +    }\r
251 +\r
252 +    /* Deal with the different types of parts */\r
253 +    if (GMIME_IS_PART (part)) {\r
254 +       out->children = 0;\r
255 +    } else if (GMIME_IS_MULTIPART (part)) {\r
256 +       out->children = g_mime_multipart_get_count (GMIME_MULTIPART (part));\r
257 +    } else if (GMIME_IS_MESSAGE_PART (part)) {\r
258 +       /* Promote part to an envelope and open it */\r
259 +       GMimeMessagePart *message_part = GMIME_MESSAGE_PART (part);\r
260 +       GMimeMessage *message = g_mime_message_part_get_message (message_part);\r
261 +       out->envelope_part = message_part;\r
262 +       out->part = GMIME_OBJECT (message);\r
263 +       out->children = 1;\r
264 +    } else {\r
265 +       fprintf (stderr, "Warning: Unknown mime part type: %s.\n",\r
266 +                g_type_name (G_OBJECT_TYPE (part)));\r
267 +       talloc_free (out);\r
268 +       return NULL;\r
269 +    }\r
270 +\r
271 +    /* Handle PGP/MIME parts */\r
272 +    if (GMIME_IS_MULTIPART_ENCRYPTED (part) && out->ctx->decrypt) {\r
273 +       if (out->children != 2) {\r
274 +           /* this violates RFC 3156 section 4, so we won't bother with it. */\r
275 +           fprintf (stderr, "Error: %d part(s) for a multipart/encrypted "\r
276 +                    "message (should be exactly 2)\n",\r
277 +                    out->children);\r
278 +       } else {\r
279 +           out->is_encrypted = TRUE;\r
280 +           GMimeMultipartEncrypted *encrypteddata =\r
281 +               GMIME_MULTIPART_ENCRYPTED (part);\r
282 +           out->decrypted_child = g_mime_multipart_encrypted_decrypt\r
283 +               (encrypteddata, out->ctx->cryptoctx, &err);\r
284 +           if (out->decrypted_child) {\r
285 +               out->decrypt_success = TRUE;\r
286 +               out->is_signed = TRUE;\r
287 +               out->sig_validity = g_mime_multipart_encrypted_get_signature_validity (encrypteddata);\r
288 +           } else {\r
289 +               fprintf (stderr, "Failed to decrypt part: %s\n",\r
290 +                        (err ? err->message : "no error explanation given"));\r
291 +           }\r
292 +       }\r
293 +    } else if (GMIME_IS_MULTIPART_SIGNED (part) && out->ctx->cryptoctx) {\r
294 +       if (out->children != 2) {\r
295 +           /* this violates RFC 3156 section 5, so we won't bother with it. */\r
296 +           fprintf (stderr, "Error: %d part(s) for a multipart/signed message "\r
297 +                    "(should be exactly 2)\n",\r
298 +                    out->children);\r
299 +       } else {\r
300 +           out->is_signed = TRUE;\r
301 +           /* For some reason the GMimeSignatureValidity returned\r
302 +            * here is not a const (inconsistent with that\r
303 +            * returned by\r
304 +            * g_mime_multipart_encrypted_get_signature_validity,\r
305 +            * and therefore needs to be properly disposed of.\r
306 +            * Hopefully the API will become more consistent. */\r
307 +           GMimeSignatureValidity *sig_validity = g_mime_multipart_signed_verify\r
308 +               (GMIME_MULTIPART_SIGNED (part), out->ctx->cryptoctx, &err);\r
309 +           out->sig_validity = sig_validity;\r
310 +           if (sig_validity) {\r
311 +               GMimeSignatureValidity **proxy =\r
312 +                   talloc (out, 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 (out->is_signed && !out->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 out;\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 +    if (!parent || child < 0 || child >= parent->children)\r
333 +       return NULL;\r
334 +\r
335 +    if (GMIME_IS_MULTIPART (parent->part)) {\r
336 +       GMimeMultipart *multipart = GMIME_MULTIPART (parent->part);\r
337 +       if (child == 1 && parent->decrypted_child)\r
338 +           return _mime_node_create (parent, parent->decrypted_child);\r
339 +       return _mime_node_create (parent, g_mime_multipart_get_part (multipart, child));\r
340 +    } else if (GMIME_IS_MESSAGE (parent->part)) {\r
341 +       GMimeMessage *message = GMIME_MESSAGE (parent->part);\r
342 +       GMimeObject *child = g_mime_message_get_mime_part (message);\r
343 +       return _mime_node_create (parent, child);\r
344 +    } else {\r
345 +       /* This should have been caught by message_part_create */\r
346 +       INTERNAL_ERROR ("Unexpected GMimeObject type: %s",\r
347 +                       g_type_name (G_OBJECT_TYPE (parent->part)));\r
348 +    }\r
349 +}\r
350 diff --git a/notmuch-client.h b/notmuch-client.h\r
351 index d7fb6ee..58bd21c 100644\r
352 --- a/notmuch-client.h\r
353 +++ b/notmuch-client.h\r
354 @@ -238,4 +238,84 @@ notmuch_config_set_maildir_synchronize_flags (notmuch_config_t *config,\r
355  notmuch_bool_t\r
356  debugger_is_active (void);\r
357  \r
358 +/* mime-node.c */\r
359 +\r
360 +/* mime_node_t represents a single node in a MIME tree.  A MIME tree\r
361 + * abstracts the different ways of traversing different types of MIME\r
362 + * parts, allowing a MIME message to be viewed as a generic tree of\r
363 + * parts.  Message-type parts have one child, multipart-type parts\r
364 + * have multiple children, and leaf parts have zero children.\r
365 + */\r
366 +typedef struct mime_node {\r
367 +    /* The MIME object of this part.  This will be a GMimeMessage,\r
368 +     * GMimePart, GMimeMultipart, or a subclass of one of these.\r
369 +     *\r
370 +     * This will never be a GMimeMessagePart because GMimeMessagePart\r
371 +     * is structurally redundant with GMimeMessage.  If this part is a\r
372 +     * message (that is, 'part' is a GMimeMessage), then either\r
373 +     * envelope_file will be set to a notmuch_message_t (for top-level\r
374 +     * messages) or envelope_part will be set to a GMimeMessagePart\r
375 +     * (for embedded message parts).\r
376 +     */\r
377 +    GMimeObject *part;\r
378 +\r
379 +    /* If part is a GMimeMessage, these record the envelope of the\r
380 +     * message: either a notmuch_message_t representing a top-level\r
381 +     * message, or a GMimeMessagePart representing a MIME part\r
382 +     * containing a message.\r
383 +     */\r
384 +    notmuch_message_t *envelope_file;\r
385 +    GMimeMessagePart *envelope_part;\r
386 +\r
387 +    /* The number of children of this part. */\r
388 +    int children;\r
389 +\r
390 +    /* True if this is the container for an encrypted or signed part.\r
391 +     * This does *not* mean that decryption or signature verification\r
392 +     * succeeded. */\r
393 +    notmuch_bool_t is_encrypted, is_signed;\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 +    /* For signed or encrypted containers, the validity of the\r
399 +     * signature.  May be NULL if signature verification failed. */\r
400 +    const GMimeSignatureValidity *sig_validity;\r
401 +\r
402 +    /* Internal: Context inherited from the root iterator. */\r
403 +    struct mime_node_context *ctx;\r
404 +\r
405 +    /* Internal: For successfully decrypted multipart parts, the\r
406 +     * decrypted part to substitute for the second child. */\r
407 +    GMimeObject *decrypted_child;\r
408 +} mime_node_t;\r
409 +\r
410 +/* Construct a new MIME node pointing to the root message part of\r
411 + * message.  If cryptoctx is non-NULL, it will be used to verify\r
412 + * signatures on any child parts.  If decrypt is true, then cryptoctx\r
413 + * will additionally be used to decrypt any encrypted child parts.\r
414 + *\r
415 + * Return value:\r
416 + *\r
417 + * NOTMUCH_STATUS_SUCCESS: Root node is returned in *node_out.\r
418 + *\r
419 + * NOTMUCH_STATUS_FILE_ERROR: Failed to open message file.\r
420 + *\r
421 + * NOTMUCH_STATUS_OUT_OF_MEMORY: Out of memory.\r
422 + */\r
423 +notmuch_status_t\r
424 +mime_node_open (const void *ctx, notmuch_message_t *message,\r
425 +               GMimeCipherContext* cryptoctx, notmuch_bool_t decrypt,\r
426 +               mime_node_t **node_out);\r
427 +\r
428 +/* Return a new MIME node for the requested child part of parent.\r
429 + * parent will be used as the talloc context for the returned child\r
430 + * node.\r
431 + *\r
432 + * In case of any failure, this function returns NULL, (after printing\r
433 + * an error message on stderr).\r
434 + */\r
435 +mime_node_t *\r
436 +mime_node_child (const mime_node_t *parent, int child);\r
437 +\r
438  #endif\r
439 -- \r
440 1.7.5.4\r
441 \r