cli: modify mime_node_open to take new crypto struct as argument
[notmuch] / mime-node.c
1 /* notmuch - Not much of an email program, (just index and search)
2  *
3  * Copyright © 2009 Carl Worth
4  * Copyright © 2009 Keith Packard
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see http://www.gnu.org/licenses/ .
18  *
19  * Authors: Carl Worth <cworth@cworth.org>
20  *          Keith Packard <keithp@keithp.com>
21  *          Austin Clements <aclements@csail.mit.edu>
22  */
23
24 #include "notmuch-client.h"
25
26 /* Context that gets inherited from the root node. */
27 typedef struct mime_node_context {
28     /* Per-message resources.  These are allocated internally and must
29      * be destroyed. */
30     FILE *file;
31     GMimeStream *stream;
32     GMimeParser *parser;
33     GMimeMessage *mime_message;
34
35     /* Context provided by the caller. */
36     notmuch_crypto_context_t *cryptoctx;
37     notmuch_bool_t decrypt;
38 } mime_node_context_t;
39
40 static int
41 _mime_node_context_free (mime_node_context_t *res)
42 {
43     if (res->mime_message)
44         g_object_unref (res->mime_message);
45
46     if (res->parser)
47         g_object_unref (res->parser);
48
49     if (res->stream)
50         g_object_unref (res->stream);
51
52     if (res->file)
53         fclose (res->file);
54
55     return 0;
56 }
57
58 notmuch_status_t
59 mime_node_open (const void *ctx, notmuch_message_t *message,
60                 notmuch_crypto_t *crypto, mime_node_t **root_out)
61 {
62     const char *filename = notmuch_message_get_filename (message);
63     mime_node_context_t *mctx;
64     mime_node_t *root;
65     notmuch_status_t status;
66
67     root = talloc_zero (ctx, mime_node_t);
68     if (root == NULL) {
69         fprintf (stderr, "Out of memory.\n");
70         status = NOTMUCH_STATUS_OUT_OF_MEMORY;
71         goto DONE;
72     }
73
74     /* Create the tree-wide context */
75     mctx = talloc_zero (root, mime_node_context_t);
76     if (mctx == NULL) {
77         fprintf (stderr, "Out of memory.\n");
78         status = NOTMUCH_STATUS_OUT_OF_MEMORY;
79         goto DONE;
80     }
81     talloc_set_destructor (mctx, _mime_node_context_free);
82
83     mctx->file = fopen (filename, "r");
84     if (! mctx->file) {
85         fprintf (stderr, "Error opening %s: %s\n", filename, strerror (errno));
86         status = NOTMUCH_STATUS_FILE_ERROR;
87         goto DONE;
88     }
89
90     mctx->stream = g_mime_stream_file_new (mctx->file);
91     if (!mctx->stream) {
92         fprintf (stderr, "Out of memory.\n");
93         status = NOTMUCH_STATUS_OUT_OF_MEMORY;
94         goto DONE;
95     }
96     g_mime_stream_file_set_owner (GMIME_STREAM_FILE (mctx->stream), FALSE);
97
98     mctx->parser = g_mime_parser_new_with_stream (mctx->stream);
99     if (!mctx->parser) {
100         fprintf (stderr, "Out of memory.\n");
101         status = NOTMUCH_STATUS_OUT_OF_MEMORY;
102         goto DONE;
103     }
104
105     mctx->mime_message = g_mime_parser_construct_message (mctx->parser);
106     if (!mctx->mime_message) {
107         fprintf (stderr, "Failed to parse %s\n", filename);
108         status = NOTMUCH_STATUS_FILE_ERROR;
109         goto DONE;
110     }
111
112     mctx->cryptoctx = crypto->gpgctx;
113     mctx->decrypt = crypto->decrypt;
114
115     /* Create the root node */
116     root->part = GMIME_OBJECT (mctx->mime_message);
117     root->envelope_file = message;
118     root->nchildren = 1;
119     root->ctx = mctx;
120
121     root->parent = NULL;
122     root->part_num = 0;
123     root->next_child = 0;
124     root->next_part_num = 1;
125
126     *root_out = root;
127     return NOTMUCH_STATUS_SUCCESS;
128
129 DONE:
130     talloc_free (root);
131     return status;
132 }
133
134 #ifdef GMIME_ATLEAST_26
135 static int
136 _signature_list_free (GMimeSignatureList **proxy)
137 {
138     g_object_unref (*proxy);
139     return 0;
140 }
141 #else
142 static int
143 _signature_validity_free (GMimeSignatureValidity **proxy)
144 {
145     g_mime_signature_validity_free (*proxy);
146     return 0;
147 }
148 #endif
149
150 static mime_node_t *
151 _mime_node_create (mime_node_t *parent, GMimeObject *part)
152 {
153     mime_node_t *node = talloc_zero (parent, mime_node_t);
154     GError *err = NULL;
155
156     /* Set basic node properties */
157     node->part = part;
158     node->ctx = parent->ctx;
159     if (!talloc_reference (node, node->ctx)) {
160         fprintf (stderr, "Out of memory.\n");
161         talloc_free (node);
162         return NULL;
163     }
164     node->parent = parent;
165     node->part_num = node->next_part_num = -1;
166     node->next_child = 0;
167
168     /* Deal with the different types of parts */
169     if (GMIME_IS_PART (part)) {
170         node->nchildren = 0;
171     } else if (GMIME_IS_MULTIPART (part)) {
172         node->nchildren = g_mime_multipart_get_count (GMIME_MULTIPART (part));
173     } else if (GMIME_IS_MESSAGE_PART (part)) {
174         /* Promote part to an envelope and open it */
175         GMimeMessagePart *message_part = GMIME_MESSAGE_PART (part);
176         GMimeMessage *message = g_mime_message_part_get_message (message_part);
177         node->envelope_part = message_part;
178         node->part = GMIME_OBJECT (message);
179         node->nchildren = 1;
180     } else {
181         fprintf (stderr, "Warning: Unknown mime part type: %s.\n",
182                  g_type_name (G_OBJECT_TYPE (part)));
183         talloc_free (node);
184         return NULL;
185     }
186
187     /* Handle PGP/MIME parts */
188     if (GMIME_IS_MULTIPART_ENCRYPTED (part)
189         && node->ctx->cryptoctx && node->ctx->decrypt) {
190         if (node->nchildren != 2) {
191             /* this violates RFC 3156 section 4, so we won't bother with it. */
192             fprintf (stderr, "Error: %d part(s) for a multipart/encrypted "
193                      "message (must be exactly 2)\n",
194                      node->nchildren);
195         } else {
196             GMimeMultipartEncrypted *encrypteddata =
197                 GMIME_MULTIPART_ENCRYPTED (part);
198             node->decrypt_attempted = TRUE;
199 #ifdef GMIME_ATLEAST_26
200             GMimeDecryptResult *decrypt_result = NULL;
201             node->decrypted_child = g_mime_multipart_encrypted_decrypt
202                 (encrypteddata, node->ctx->cryptoctx, &decrypt_result, &err);
203 #else
204             node->decrypted_child = g_mime_multipart_encrypted_decrypt
205                 (encrypteddata, node->ctx->cryptoctx, &err);
206 #endif
207             if (node->decrypted_child) {
208                 node->decrypt_success = node->verify_attempted = TRUE;
209 #ifdef GMIME_ATLEAST_26
210                 /* This may be NULL if the part is not signed. */
211                 node->sig_list = g_mime_decrypt_result_get_signatures (decrypt_result);
212                 if (node->sig_list)
213                     g_object_ref (node->sig_list);
214                 g_object_unref (decrypt_result);
215 #else
216                 node->sig_validity = g_mime_multipart_encrypted_get_signature_validity (encrypteddata);
217 #endif
218             } else {
219                 fprintf (stderr, "Failed to decrypt part: %s\n",
220                          (err ? err->message : "no error explanation given"));
221             }
222         }
223     } else if (GMIME_IS_MULTIPART_SIGNED (part) && node->ctx->cryptoctx) {
224         if (node->nchildren != 2) {
225             /* this violates RFC 3156 section 5, so we won't bother with it. */
226             fprintf (stderr, "Error: %d part(s) for a multipart/signed message "
227                      "(must be exactly 2)\n",
228                      node->nchildren);
229         } else {
230 #ifdef GMIME_ATLEAST_26
231             node->sig_list = g_mime_multipart_signed_verify
232                 (GMIME_MULTIPART_SIGNED (part), node->ctx->cryptoctx, &err);
233             node->verify_attempted = TRUE;
234
235             if (!node->sig_list)
236                 fprintf (stderr, "Failed to verify signed part: %s\n",
237                          (err ? err->message : "no error explanation given"));
238 #else
239             /* For some reason the GMimeSignatureValidity returned
240              * here is not a const (inconsistent with that
241              * returned by
242              * g_mime_multipart_encrypted_get_signature_validity,
243              * and therefore needs to be properly disposed of.
244              *
245              * In GMime 2.6, they're both non-const, so we'll be able
246              * to clean up this asymmetry. */
247             GMimeSignatureValidity *sig_validity = g_mime_multipart_signed_verify
248                 (GMIME_MULTIPART_SIGNED (part), node->ctx->cryptoctx, &err);
249             node->verify_attempted = TRUE;
250             node->sig_validity = sig_validity;
251             if (sig_validity) {
252                 GMimeSignatureValidity **proxy =
253                     talloc (node, GMimeSignatureValidity *);
254                 *proxy = sig_validity;
255                 talloc_set_destructor (proxy, _signature_validity_free);
256             }
257 #endif
258         }
259     }
260
261 #ifdef GMIME_ATLEAST_26
262     /* sig_list may be created in both above cases, so we need to
263      * cleanly handle it here. */
264     if (node->sig_list) {
265         GMimeSignatureList **proxy = talloc (node, GMimeSignatureList *);
266         *proxy = node->sig_list;
267         talloc_set_destructor (proxy, _signature_list_free);
268     }
269 #endif
270
271 #ifndef GMIME_ATLEAST_26
272     if (node->verify_attempted && !node->sig_validity)
273         fprintf (stderr, "Failed to verify signed part: %s\n",
274                  (err ? err->message : "no error explanation given"));
275 #endif
276
277     if (err)
278         g_error_free (err);
279
280     return node;
281 }
282
283 mime_node_t *
284 mime_node_child (mime_node_t *parent, int child)
285 {
286     GMimeObject *sub;
287     mime_node_t *node;
288
289     if (!parent || child < 0 || child >= parent->nchildren)
290         return NULL;
291
292     if (GMIME_IS_MULTIPART (parent->part)) {
293         if (child == 1 && parent->decrypted_child)
294             sub = parent->decrypted_child;
295         else
296             sub = g_mime_multipart_get_part
297                 (GMIME_MULTIPART (parent->part), child);
298     } else if (GMIME_IS_MESSAGE (parent->part)) {
299         sub = g_mime_message_get_mime_part (GMIME_MESSAGE (parent->part));
300     } else {
301         /* This should have been caught by message_part_create */
302         INTERNAL_ERROR ("Unexpected GMimeObject type: %s",
303                         g_type_name (G_OBJECT_TYPE (parent->part)));
304     }
305     node = _mime_node_create (parent, sub);
306
307     if (child == parent->next_child && parent->next_part_num != -1) {
308         /* We're traversing in depth-first order.  Record the child's
309          * depth-first numbering. */
310         node->part_num = parent->next_part_num;
311         node->next_part_num = node->part_num + 1;
312
313         /* Prepare the parent for its next depth-first child. */
314         parent->next_child++;
315         parent->next_part_num = -1;
316
317         if (node->nchildren == 0) {
318             /* We've reached a leaf, so find the parent that has more
319              * children and set it up to number its next child. */
320             mime_node_t *iter = node->parent;
321             while (iter && iter->next_child == iter->nchildren)
322                 iter = iter->parent;
323             if (iter)
324                 iter->next_part_num = node->part_num + 1;
325         }
326     }
327
328     return node;
329 }
330
331 static mime_node_t *
332 _mime_node_seek_dfs_walk (mime_node_t *node, int *n)
333 {
334     mime_node_t *ret = NULL;
335     int i;
336
337     if (*n == 0)
338         return node;
339
340     *n -= 1;
341     for (i = 0; i < node->nchildren && !ret; i++) {
342         mime_node_t *child = mime_node_child (node, i);
343         ret = _mime_node_seek_dfs_walk (child, n);
344         if (!ret)
345             talloc_free (child);
346     }
347     return ret;
348 }
349
350 mime_node_t *
351 mime_node_seek_dfs (mime_node_t *node, int n)
352 {
353     if (n < 0)
354         return NULL;
355     return _mime_node_seek_dfs_walk (node, &n);
356 }