]> git.cworth.org Git - notmuch/blob - util/repair.c
python-cffi: introduce stamp file
[notmuch] / util / repair.c
1 /* notmuch - Not much of an email program, (just index and search)
2  *
3  * Copyright © 2019 Daniel Kahn Gillmor
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see https://www.gnu.org/licenses/ .
17  *
18  * Authors: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
19  */
20
21 #include <stdbool.h>
22 #include "repair.h"
23
24
25 static bool
26 _notmuch_crypto_payload_has_legacy_display (GMimeObject *payload)
27 {
28     GMimeMultipart *mpayload;
29     const char *protected_header_parameter;
30     GMimeObject *first;
31
32     if (! g_mime_content_type_is_type (g_mime_object_get_content_type (payload),
33                                        "multipart", "mixed"))
34         return false;
35     protected_header_parameter = g_mime_object_get_content_type_parameter (payload,
36                                                                            "protected-headers");
37     if ((! protected_header_parameter) || strcmp (protected_header_parameter, "v1"))
38         return false;
39     if (! GMIME_IS_MULTIPART (payload))
40         return false;
41     mpayload = GMIME_MULTIPART (payload);
42     if (mpayload == NULL)
43         return false;
44     if (g_mime_multipart_get_count (mpayload) != 2)
45         return false;
46     first = g_mime_multipart_get_part (mpayload, 0);
47     /* Early implementations that generated "Legacy Display" parts used
48      * Content-Type: text/rfc822-headers, but text/plain is more widely
49      * rendered, so it is now the standard choice.  We accept either as a
50      * Legacy Display part. */
51     if (! (g_mime_content_type_is_type (g_mime_object_get_content_type (first),
52                                         "text", "plain") ||
53            g_mime_content_type_is_type (g_mime_object_get_content_type (first),
54                                         "text", "rfc822-headers")))
55         return false;
56     protected_header_parameter = g_mime_object_get_content_type_parameter (first,
57                                                                            "protected-headers");
58     if ((! protected_header_parameter) || strcmp (protected_header_parameter, "v1"))
59         return false;
60     if (! GMIME_IS_TEXT_PART (first))
61         return false;
62
63     return true;
64 }
65
66 GMimeObject *
67 _notmuch_repair_crypto_payload_skip_legacy_display (GMimeObject *payload)
68 {
69     if (_notmuch_crypto_payload_has_legacy_display (payload)) {
70         return g_mime_multipart_get_part (GMIME_MULTIPART (payload), 1);
71     } else {
72         return payload;
73     }
74 }
75
76 /* see
77  * https://tools.ietf.org/html/draft-dkg-openpgp-pgpmime-message-mangling-00#section-4.1.1 */
78 static bool
79 _notmuch_is_mixed_up_mangled (GMimeObject *part)
80 {
81     GMimeMultipart *mpart = NULL;
82     GMimeObject *parts[3] = { NULL, NULL, NULL };
83     GMimeContentType *type = NULL;
84     char *prelude_string = NULL;
85     bool prelude_is_empty;
86
87     if (part == NULL)
88         return false;
89     type = g_mime_object_get_content_type (part);
90     if (type == NULL)
91         return false;
92     if (! g_mime_content_type_is_type (type, "multipart", "mixed"))
93         return false;
94     if (! GMIME_IS_MULTIPART (part)) /* probably impossible */
95         return false;
96     mpart = GMIME_MULTIPART (part);
97     if (mpart == NULL)
98         return false;
99     if (g_mime_multipart_get_count (mpart) != 3)
100         return false;
101     parts[0] = g_mime_multipart_get_part (mpart, 0);
102     if (! g_mime_content_type_is_type (g_mime_object_get_content_type (parts[0]),
103                                        "text", "plain"))
104         return false;
105     if (! GMIME_IS_TEXT_PART (parts[0]))
106         return false;
107     parts[1] = g_mime_multipart_get_part (mpart, 1);
108     if (! g_mime_content_type_is_type (g_mime_object_get_content_type (parts[1]),
109                                        "application", "pgp-encrypted"))
110         return false;
111     parts[2] = g_mime_multipart_get_part (mpart, 2);
112     if (! g_mime_content_type_is_type (g_mime_object_get_content_type (parts[2]),
113                                        "application", "octet-stream"))
114         return false;
115
116     /* Is parts[0] length 0? */
117     prelude_string = g_mime_text_part_get_text (GMIME_TEXT_PART (parts[0]));
118     prelude_is_empty = (prelude_string[0] == '\0');
119     g_free (prelude_string);
120     if (! prelude_is_empty)
121         return false;
122
123     /* FIXME: after decoding and stripping whitespace, is parts[1]
124      * subpart just "Version: 1" ? */
125
126     /* FIXME: can we determine that parts[2] subpart is *only* PGP
127      * encrypted data?  I tried g_mime_part_get_openpgp_data () but
128      * found https://github.com/jstedfast/gmime/issues/60 */
129
130     return true;
131 }
132
133
134 /* see
135  * https://tools.ietf.org/html/draft-dkg-openpgp-pgpmime-message-mangling-00#section-4.1.2 */
136 GMimeObject *
137 _notmuch_repair_mixed_up_mangled (GMimeObject *part)
138 {
139     GMimeMultipart *mpart = NULL, *mpart_ret = NULL;
140     GMimeObject *ret = NULL;
141
142     if (! _notmuch_is_mixed_up_mangled (part))
143         return NULL;
144     mpart = GMIME_MULTIPART (part);
145     ret = GMIME_OBJECT (g_mime_multipart_encrypted_new ());
146     if (ret == NULL)
147         return NULL;
148     mpart_ret = GMIME_MULTIPART (ret);
149     if (mpart_ret == NULL) {
150         g_object_unref (ret);
151         return NULL;
152     }
153     g_mime_object_set_content_type_parameter (ret, "protocol", "application/pgp-encrypted");
154
155     g_mime_multipart_insert (mpart_ret, 0, g_mime_multipart_get_part (mpart, 1));
156     g_mime_multipart_insert (mpart_ret, 1, g_mime_multipart_get_part (mpart, 2));
157     return ret;
158 }