1 /*
2  * e-mail-parser-multipart-encrypted.c
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
11  * for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program; if not, see <http://www.gnu.org/licenses/>.
15  *
16  */
17 
18 #include "evolution-config.h"
19 
20 #include <glib/gi18n-lib.h>
21 
22 #include <libedataserver/libedataserver.h>
23 
24 #include "e-mail-formatter-utils.h"
25 #include "e-mail-parser-extension.h"
26 #include "e-mail-part-utils.h"
27 
28 typedef EMailParserExtension EMailParserMultipartEncrypted;
29 typedef EMailParserExtensionClass EMailParserMultipartEncryptedClass;
30 
31 GType e_mail_parser_multipart_encrypted_get_type (void);
32 
33 G_DEFINE_TYPE (
34 	EMailParserMultipartEncrypted,
35 	e_mail_parser_multipart_encrypted,
36 	E_TYPE_MAIL_PARSER_EXTENSION)
37 
38 static const gchar *parser_mime_types[] = {
39 	"multipart/encrypted",
40 	NULL
41 };
42 
43 static gboolean
empe_mp_encrypted_parse(EMailParserExtension * extension,EMailParser * parser,CamelMimePart * part,GString * part_id,GCancellable * cancellable,GQueue * out_mail_parts)44 empe_mp_encrypted_parse (EMailParserExtension *extension,
45                          EMailParser *parser,
46                          CamelMimePart *part,
47                          GString *part_id,
48                          GCancellable *cancellable,
49                          GQueue *out_mail_parts)
50 {
51 	CamelCipherContext *context;
52 	const gchar *protocol;
53 	CamelMimePart *opart;
54 	CamelCipherValidity *valid;
55 	CamelContentType *content_type;
56 	CamelMultipartEncrypted *mpe;
57 	GQueue work_queue = G_QUEUE_INIT;
58 	GList *head, *link;
59 	GError *local_error = NULL;
60 	gint len;
61 
62 	content_type = camel_mime_part_get_content_type (part);
63 
64 	/* When it's a guessed type, then rather not interpret it as an encrypted message */
65 	if (g_strcmp0 (camel_content_type_param (content_type, E_MAIL_PART_X_EVOLUTION_GUESSED), "1") == 0) {
66 		e_mail_parser_wrap_as_non_expandable_attachment (parser, part, part_id, out_mail_parts);
67 		return TRUE;
68 	}
69 
70 	mpe = (CamelMultipartEncrypted *) camel_medium_get_content ((CamelMedium *) part);
71 	if (!CAMEL_IS_MULTIPART_ENCRYPTED (mpe)) {
72 		e_mail_parser_error (
73 			parser, out_mail_parts,
74 			_("Could not parse MIME message. "
75 			"Displaying as source."));
76 		e_mail_parser_parse_part_as (
77 			parser, part, part_id,
78 			"application/vnd.evolution/source",
79 			cancellable, out_mail_parts);
80 
81 		return TRUE;
82 	}
83 
84 	/* Currently we only handle RFC2015-style PGP encryption. */
85 	protocol = camel_content_type_param (camel_data_wrapper_get_mime_type_field (CAMEL_DATA_WRAPPER (mpe)), "protocol");
86 	if (!protocol || g_ascii_strcasecmp (protocol, "application/pgp-encrypted") != 0) {
87 		e_mail_parser_error (
88 			parser, out_mail_parts,
89 			_("Unsupported encryption type for multipart/encrypted"));
90 		e_mail_parser_parse_part_as (
91 			parser, part, part_id, "multipart/mixed",
92 			cancellable, out_mail_parts);
93 
94 		return TRUE;
95 	}
96 
97 	context = camel_gpg_context_new (e_mail_parser_get_session (parser));
98 
99 	opart = camel_mime_part_new ();
100 	valid = camel_cipher_context_decrypt_sync (
101 		context, part, opart, cancellable, &local_error);
102 
103 	e_mail_part_preserve_charset_in_content_type (part, opart);
104 
105 	if (local_error != NULL) {
106 		e_mail_parser_error (
107 			parser, out_mail_parts,
108 			_("Could not parse PGP/MIME message: %s"),
109 			local_error->message);
110 		e_mail_parser_parse_part_as (
111 			parser, part, part_id, "multipart/mixed",
112 			cancellable, out_mail_parts);
113 
114 		g_object_unref (opart);
115 		g_object_unref (context);
116 		g_error_free (local_error);
117 
118 		return TRUE;
119 	}
120 
121 	len = part_id->len;
122 	g_string_append (part_id, ".encrypted-pgp");
123 
124 	g_warn_if_fail (e_mail_parser_parse_part (
125 		parser, opart, part_id, cancellable, &work_queue));
126 
127 	g_string_truncate (part_id, len);
128 
129 	head = g_queue_peek_head_link (&work_queue);
130 
131 	/* Update validity of all encrypted sub-parts */
132 	for (link = head; link != NULL; link = g_list_next (link)) {
133 		EMailPart *mail_part = link->data;
134 
135 		e_mail_part_update_validity (
136 			mail_part, valid,
137 			E_MAIL_PART_VALIDITY_ENCRYPTED |
138 			E_MAIL_PART_VALIDITY_PGP);
139 
140 		/* Do not traverse sub-messages */
141 		if (g_str_has_suffix (e_mail_part_get_id (mail_part), ".rfc822"))
142 			link = e_mail_formatter_find_rfc822_end_iter (link);
143 	}
144 
145 	e_queue_transfer (&work_queue, out_mail_parts);
146 
147 	/* Add a widget with details about the encryption, but only when
148 	 * the decrypted part isn't itself secured, in that case it has
149 	 * created the button itself. */
150 	if (!e_mail_part_is_secured (opart)) {
151 		EMailPart *mail_part;
152 
153 		g_string_append (part_id, ".encrypted-pgp.button");
154 
155 		e_mail_parser_parse_part_as (
156 			parser, part, part_id,
157 			"application/vnd.evolution.secure-button",
158 			cancellable, &work_queue);
159 
160 		mail_part = g_queue_peek_head (&work_queue);
161 
162 		if (mail_part != NULL)
163 			e_mail_part_update_validity (
164 				mail_part, valid,
165 				E_MAIL_PART_VALIDITY_ENCRYPTED |
166 				E_MAIL_PART_VALIDITY_PGP);
167 
168 		e_queue_transfer (&work_queue, out_mail_parts);
169 
170 		g_string_truncate (part_id, len);
171 	}
172 
173 	camel_cipher_validity_free (valid);
174 
175 	/* TODO: Make sure when we finalize this part, it is zero'd out */
176 	g_object_unref (opart);
177 	g_object_unref (context);
178 
179 	return TRUE;
180 }
181 
182 static void
e_mail_parser_multipart_encrypted_class_init(EMailParserExtensionClass * class)183 e_mail_parser_multipart_encrypted_class_init (EMailParserExtensionClass *class)
184 {
185 	class->mime_types = parser_mime_types;
186 	class->priority = G_PRIORITY_LOW;
187 	class->parse = empe_mp_encrypted_parse;
188 }
189 
190 static void
e_mail_parser_multipart_encrypted_init(EMailParserExtension * extension)191 e_mail_parser_multipart_encrypted_init (EMailParserExtension *extension)
192 {
193 }
194