Namespaced.
[platform/upstream/evolution-data-server.git] / camel / camel-multipart-encrypted.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
4  *
5  *  Copyright 2002 Ximian, Inc. (www.ximian.com)
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
20  *
21  */
22
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <stdio.h>
29 #include <string.h>
30
31 #include "camel-multipart-encrypted.h"
32 #include "camel-mime-filter-crlf.h"
33 #include "camel-stream-filter.h"
34 #include "camel-stream-mem.h"
35 #include "camel-stream-fs.h"
36 #include "camel-mime-utils.h"
37 #include "camel-mime-part.h"
38
39 static void camel_multipart_encrypted_class_init (CamelMultipartEncryptedClass *klass);
40 static void camel_multipart_encrypted_init (gpointer object, gpointer klass);
41 static void camel_multipart_encrypted_finalize (CamelObject *object);
42
43 static void set_mime_type_field (CamelDataWrapper *data_wrapper, CamelContentType *mime_type);
44
45
46 static CamelMultipartClass *parent_class = NULL;
47
48
49 CamelType
50 camel_multipart_encrypted_get_type (void)
51 {
52         static CamelType type = CAMEL_INVALID_TYPE;
53         
54         if (type == CAMEL_INVALID_TYPE) {
55                 type = camel_type_register (camel_multipart_get_type (),
56                                             "CamelMultipartEncrypted",
57                                             sizeof (CamelMultipartEncrypted),
58                                             sizeof (CamelMultipartEncryptedClass),
59                                             (CamelObjectClassInitFunc) camel_multipart_encrypted_class_init,
60                                             NULL,
61                                             (CamelObjectInitFunc) camel_multipart_encrypted_init,
62                                             (CamelObjectFinalizeFunc) camel_multipart_encrypted_finalize);
63         }
64         
65         return type;
66 }
67
68
69 static void
70 camel_multipart_encrypted_class_init (CamelMultipartEncryptedClass *klass)
71 {
72         CamelDataWrapperClass *camel_data_wrapper_class = CAMEL_DATA_WRAPPER_CLASS (klass);
73         
74         parent_class = (CamelMultipartClass *) camel_multipart_get_type ();
75         
76         /* virtual method overload */
77         camel_data_wrapper_class->set_mime_type_field = set_mime_type_field;
78 }
79
80 static void
81 camel_multipart_encrypted_init (gpointer object, gpointer klass)
82 {
83         CamelMultipartEncrypted *multipart = (CamelMultipartEncrypted *) object;
84         
85         camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (multipart), "multipart/encrypted");
86         
87         multipart->decrypted = NULL;
88 }
89
90 static void
91 camel_multipart_encrypted_finalize (CamelObject *object)
92 {
93         CamelMultipartEncrypted *mpe = (CamelMultipartEncrypted *) object;
94         
95         g_free (mpe->protocol);
96         
97         if (mpe->decrypted)
98                 camel_object_unref (mpe->decrypted);
99 }
100
101 /* we snoop the mime type to get the protocol */
102 static void
103 set_mime_type_field (CamelDataWrapper *data_wrapper, CamelContentType *mime_type)
104 {
105         CamelMultipartEncrypted *mpe = (CamelMultipartEncrypted *) data_wrapper;
106         
107         if (mime_type) {
108                 const char *protocol;
109                 
110                 protocol = camel_content_type_param (mime_type, "protocol");
111                 g_free (mpe->protocol);
112                 mpe->protocol = g_strdup (protocol);
113         }
114         
115         ((CamelDataWrapperClass *) parent_class)->set_mime_type_field (data_wrapper, mime_type);
116 }
117
118
119 /**
120  * camel_multipart_encrypted_new:
121  *
122  * Create a new CamelMultipartEncrypted object.
123  *
124  * A MultipartEncrypted should be used to store and create parts of
125  * type "multipart/encrypted".
126  *
127  * Returns a new CamelMultipartEncrypted
128  **/
129 CamelMultipartEncrypted *
130 camel_multipart_encrypted_new (void)
131 {
132         CamelMultipartEncrypted *multipart;
133         
134         multipart = (CamelMultipartEncrypted *) camel_object_new (CAMEL_MULTIPART_ENCRYPTED_TYPE);
135         
136         return multipart;
137 }
138
139
140 int
141 camel_multipart_encrypted_encrypt (CamelMultipartEncrypted *mpe, CamelMimePart *content,
142                                    CamelCipherContext *cipher, const char *userid,
143                                    GPtrArray *recipients, CamelException *ex)
144 {
145         CamelMimePart *version_part, *encrypted_part;
146         CamelContentType *mime_type;
147         CamelDataWrapper *wrapper;
148         CamelStream *filtered_stream;
149         CamelStream *stream, *ciphertext;
150         CamelMimeFilter *crlf_filter;
151         
152         g_return_val_if_fail (CAMEL_IS_MULTIPART_ENCRYPTED (mpe), -1);
153         g_return_val_if_fail (CAMEL_IS_CIPHER_CONTEXT (cipher), -1);
154         g_return_val_if_fail (cipher->encrypt_protocol != NULL, -1);
155         g_return_val_if_fail (CAMEL_IS_MIME_PART (content), -1);
156         
157         /* get the cleartext */
158         stream = camel_stream_mem_new ();
159         filtered_stream = (CamelStream *) camel_stream_filter_new_with_stream (stream);
160         
161         crlf_filter = camel_mime_filter_crlf_new (CAMEL_MIME_FILTER_CRLF_ENCODE,
162                                                   CAMEL_MIME_FILTER_CRLF_MODE_CRLF_ONLY);
163         camel_stream_filter_add (CAMEL_STREAM_FILTER (filtered_stream), crlf_filter);
164         camel_object_unref (crlf_filter);
165         
166         camel_data_wrapper_write_to_stream ((CamelDataWrapper *) content, filtered_stream);
167         camel_stream_flush (filtered_stream);
168         camel_object_unref (filtered_stream);
169         
170         /* reset the content stream */
171         camel_stream_reset (stream);
172         
173         /* encrypt the content stream */
174         ciphertext = camel_stream_mem_new ();
175         if (camel_cipher_encrypt (cipher, FALSE, userid, recipients, stream, ciphertext, ex) == -1) {
176                 camel_object_unref (ciphertext);
177                 camel_object_unref (stream);
178                 return -1;
179         }
180         
181         camel_object_unref (stream);
182         camel_stream_reset (ciphertext);
183         
184         /* construct the version part */
185         stream = camel_stream_mem_new ();
186         camel_stream_write_string (stream, "Version: 1\n");
187         camel_stream_reset (stream);
188         
189         version_part = camel_mime_part_new ();
190         wrapper = camel_data_wrapper_new ();
191         camel_data_wrapper_set_mime_type (wrapper, cipher->encrypt_protocol);
192         camel_data_wrapper_construct_from_stream (wrapper, stream);
193         camel_object_unref (stream);
194         camel_medium_set_content_object ((CamelMedium *) version_part, wrapper);
195         camel_object_unref (wrapper);
196         
197         /* construct the encrypted mime part */
198         encrypted_part = camel_mime_part_new ();
199         wrapper = camel_data_wrapper_new ();
200         camel_data_wrapper_set_mime_type (wrapper, "application/octet-stream; name=encrypted.asc");
201         camel_data_wrapper_construct_from_stream (wrapper, ciphertext);
202         camel_object_unref (ciphertext);
203         camel_medium_set_content_object ((CamelMedium *) encrypted_part, wrapper);
204         camel_object_unref (wrapper);
205         
206         /* save the version and encrypted parts */
207         /* FIXME: make sure there aren't any other parts?? */
208         camel_multipart_add_part (CAMEL_MULTIPART (mpe), version_part);
209         camel_object_unref (version_part);
210         camel_multipart_add_part (CAMEL_MULTIPART (mpe), encrypted_part);
211         camel_object_unref (encrypted_part);
212         
213         /* cache the decrypted content */
214         camel_object_ref (content);
215         mpe->decrypted = content;
216         
217         /* set the content-type params for this multipart/encrypted part */
218         mime_type = camel_content_type_new ("multipart", "encrypted");
219         camel_content_type_set_param (mime_type, "protocol", cipher->encrypt_protocol);
220         camel_data_wrapper_set_mime_type_field (CAMEL_DATA_WRAPPER (mpe), mime_type);
221         camel_content_type_unref (mime_type);
222         camel_multipart_set_boundary ((CamelMultipart *) mpe, NULL);
223         
224         return 0;
225 }
226
227
228 CamelMimePart *
229 camel_multipart_encrypted_decrypt (CamelMultipartEncrypted *mpe,
230                                    CamelCipherContext *cipher,
231                                    CamelException *ex)
232 {
233         CamelMimePart *version_part, *encrypted_part, *decrypted_part;
234         CamelContentType *mime_type;
235         CamelDataWrapper *wrapper;
236         CamelStream *filtered_stream;
237         CamelMimeFilter *crlf_filter;
238         CamelStream *ciphertext;
239         CamelStream *stream;
240         const char *protocol;
241         char *content_type;
242         
243         g_return_val_if_fail (CAMEL_IS_MULTIPART_ENCRYPTED (mpe), NULL);
244         g_return_val_if_fail (CAMEL_IS_CIPHER_CONTEXT (cipher), NULL);
245         g_return_val_if_fail (cipher->encrypt_protocol != NULL, NULL);
246         
247         if (mpe->decrypted) {
248                 /* we seem to have already decrypted the part */
249                 camel_object_ref (mpe->decrypted);
250                 return mpe->decrypted;
251         }
252         
253         protocol = mpe->protocol;
254         
255         if (protocol) {
256                 /* make sure the protocol matches the cipher encrypt protocol */
257                 if (strcasecmp (cipher->encrypt_protocol, protocol) != 0) {
258                         camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
259                                              _("Failed to decrypt MIME part: protocol error"));
260                         
261                         return NULL;
262                 }
263         } else {
264                 /* *shrug* - I guess just go on as if they match? */
265                 protocol = cipher->encrypt_protocol;
266         }
267         
268         /* make sure the protocol matches the version part's content-type */
269         version_part = camel_multipart_get_part (CAMEL_MULTIPART (mpe), CAMEL_MULTIPART_ENCRYPTED_VERSION);
270         wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (version_part));
271         content_type = camel_data_wrapper_get_mime_type (wrapper);
272         if (strcasecmp (content_type, protocol) != 0) {
273                 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
274                                      _("Failed to decrypt MIME part: protocol error"));
275                 
276                 g_free (content_type);
277                 
278                 return NULL;
279         }
280         g_free (content_type);
281         
282         /* get the encrypted part (second part) */
283         encrypted_part = camel_multipart_get_part (CAMEL_MULTIPART (mpe), CAMEL_MULTIPART_ENCRYPTED_CONTENT);
284         mime_type = camel_mime_part_get_content_type (encrypted_part);
285         if (!camel_content_type_is (mime_type, "application", "octet-stream")) {
286                 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
287                                      _("Failed to decrypt MIME part: invalid structure"));
288                 return NULL;
289         }
290         
291         /* get the ciphertext stream */
292         ciphertext = camel_stream_mem_new ();
293         wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (encrypted_part));
294         camel_data_wrapper_write_to_stream (wrapper, ciphertext);
295         camel_stream_reset (ciphertext);
296         
297         stream = camel_stream_mem_new ();
298         filtered_stream = (CamelStream *) camel_stream_filter_new_with_stream (stream);
299         crlf_filter = camel_mime_filter_crlf_new (CAMEL_MIME_FILTER_CRLF_DECODE,
300                                                   CAMEL_MIME_FILTER_CRLF_MODE_CRLF_ONLY);
301         camel_stream_filter_add (CAMEL_STREAM_FILTER (filtered_stream), crlf_filter);
302         camel_object_unref (crlf_filter);
303         
304         /* get the cleartext */
305         if (camel_cipher_decrypt (cipher, ciphertext, filtered_stream, ex) == -1) {
306                 camel_object_unref (filtered_stream);
307                 camel_object_unref (ciphertext);
308                 camel_object_unref (stream);
309                 
310                 return NULL;
311         }
312         
313         camel_stream_flush (filtered_stream);
314         camel_object_unref (filtered_stream);
315         camel_object_unref (ciphertext);
316         camel_stream_reset (stream);
317         
318         decrypted_part = camel_mime_part_new ();
319         camel_data_wrapper_construct_from_stream (CAMEL_DATA_WRAPPER (decrypted_part), stream);
320         
321         if (decrypted_part) {
322                 /* cache the decrypted part */
323                 camel_object_ref (decrypted_part);
324                 mpe->decrypted = decrypted_part;
325         } else {
326                 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
327                                      _("Failed to decrypt MIME part: parse error"));
328         }
329         
330         return decrypted_part;
331 }