Avoid wait on taking screenshot. Now it shouldn't be required.
[modest] / src / modest-tny-msg.c
1 /* Copyright (c) 2006, Nokia Corporation
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  *   notice, this list of conditions and the following disclaimer in the
12  *   documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Nokia Corporation nor the names of its
14  *   contributors may be used to endorse or promote products derived from
15  *   this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <string.h>
31 #include <gtkhtml/gtkhtml.h>
32 #include <tny-gtk-text-buffer-stream.h>
33 #include <tny-simple-list.h>
34 #include <tny-folder.h>
35 #include <modest-runtime.h>
36 #include <modest-defs.h>
37 #include "modest-formatter.h"
38 #include <tny-camel-mem-stream.h>
39 #include <tny-camel-mime-part.h>
40 #include <glib/gprintf.h>
41 #include <modest-tny-folder.h>
42 #include "modest-tny-mime-part.h"
43 #include <modest-error.h>
44
45
46 #ifdef HAVE_CONFIG_H
47 #include <config.h>
48 #endif /*HAVE_CONFIG_H */
49
50 #include <modest-tny-msg.h>
51 #include "modest-text-utils.h"
52
53 static TnyMimePart * add_body_part (TnyMsg *msg, const gchar *body,
54                                     const gchar *content_type);
55 static TnyMimePart * add_html_body_part (TnyMsg *msg, const gchar *body);
56 static gint add_attachments (TnyMimePart *part, GList *attachments_list, gboolean add_inline, GError **err);
57 static void add_images (TnyMsg *msg, GList *attachments_list, GError **err);
58 static char * get_content_type(const gchar *s);
59 static gboolean is_ascii(const gchar *s);
60
61
62 TnyMsg*
63 modest_tny_msg_new (const gchar* mailto, const gchar* from, const gchar *cc,
64                     const gchar *bcc, const gchar* subject,
65                     const gchar *references, const gchar *in_reply_to,
66                     const gchar *body,
67                     GList *attachments, gint *attached, GError **err)
68 {
69         TnyMsg *new_msg;
70         TnyHeader *header;
71         gchar *content_type;
72         gint tmp_attached = 0;
73
74         /* Create new msg */
75         new_msg = modest_formatter_create_message (NULL, TRUE, (attachments != NULL), FALSE);
76         header  = tny_msg_get_header (new_msg);
77
78         if ((from != NULL) && (strlen(from) > 0)) {
79                 tny_header_set_from (TNY_HEADER (header), from);
80                 tny_header_set_replyto (TNY_HEADER (header), from);
81         }
82         if ((mailto != NULL) && (strlen(mailto) > 0)) {
83                 gchar *removed_to = modest_text_utils_remove_duplicate_addresses (mailto);
84                 tny_header_set_to (TNY_HEADER (header), removed_to);
85                 g_free (removed_to);
86         }
87         if ((cc != NULL) && (strlen(cc) > 0))
88                 tny_header_set_cc (TNY_HEADER (header), cc);
89         if ((bcc != NULL) && (strlen(bcc) > 0))
90                 tny_header_set_bcc (TNY_HEADER (header), bcc);
91
92         if ((subject != NULL) && (strlen(subject) > 0))
93                 tny_header_set_subject (TNY_HEADER (header), subject);
94
95         content_type = get_content_type(body);
96
97         /* set modest as the X-Mailer
98          * we could this in the platform factory, but then the header
99          * would show up before all the others.
100          */
101         tny_mime_part_set_header_pair (TNY_MIME_PART (new_msg), "X-Mailer", "Modest "
102                                        VERSION);
103
104         if (references)
105                 tny_mime_part_set_header_pair (TNY_MIME_PART (new_msg), "References", references);
106
107         if (in_reply_to)
108                 tny_mime_part_set_header_pair (TNY_MIME_PART (new_msg), "In-Reply-To", in_reply_to);
109
110         /* Add the body of the new mail */
111         /* This is needed even if body is NULL or empty. */
112         add_body_part (new_msg, body, content_type);
113         g_free (content_type);
114
115         /* Add attachments */
116         if (attachments)
117                 tmp_attached = add_attachments (TNY_MIME_PART (new_msg), attachments, FALSE, err);
118         if (attached)
119                 *attached = tmp_attached;
120         if (header)
121                 g_object_unref(header);
122
123         return new_msg;
124 }
125
126 TnyMsg*
127 modest_tny_msg_new_html_plain (const gchar* mailto, const gchar* from, const gchar *cc,
128                                const gchar *bcc, const gchar* subject, 
129                                const gchar *references, const gchar *in_reply_to,
130                                const gchar *html_body, const gchar *plain_body,
131                                GList *attachments, GList *images, gint *attached, GError **err)
132 {
133         TnyMsg *new_msg;
134         TnyHeader *header;
135         gchar *content_type;
136         gint tmp_attached;
137         
138         /* Create new msg */
139         new_msg = modest_formatter_create_message (NULL, FALSE, (attachments != NULL), (images != NULL));
140         header  = tny_msg_get_header (new_msg);
141         
142         if ((from != NULL) && (strlen(from) > 0)) {
143                 tny_header_set_from (TNY_HEADER (header), from);
144                 tny_header_set_replyto (TNY_HEADER (header), from);
145         }
146         if ((mailto != NULL) && (strlen(mailto) > 0)) 
147                 tny_header_set_to (TNY_HEADER (header), mailto);
148         if ((cc != NULL) && (strlen(cc) > 0)) 
149                 tny_header_set_cc (TNY_HEADER (header), cc);
150         if ((bcc != NULL) && (strlen(bcc) > 0)) 
151                 tny_header_set_bcc (TNY_HEADER (header), bcc);
152         
153         if ((subject != NULL) && (strlen(subject) > 0)) 
154                 tny_header_set_subject (TNY_HEADER (header), subject);
155
156         content_type = get_content_type(plain_body);
157         
158         /* set modest as the X-Mailer
159          * we could this in the platform factory, but then the header
160          * would show up before all the others.
161          */
162         tny_mime_part_set_header_pair (TNY_MIME_PART (new_msg), "X-Mailer", "Modest "
163                                        VERSION);
164
165         if (references)
166                 tny_mime_part_set_header_pair (TNY_MIME_PART (new_msg), "References", references);
167
168         if (in_reply_to)
169                 tny_mime_part_set_header_pair (TNY_MIME_PART (new_msg), "In-Reply-To", in_reply_to);
170         
171         /* Add the body of the new mail */      
172         add_body_part (new_msg, plain_body, content_type);
173         add_html_body_part (new_msg, html_body);
174         g_free (content_type);
175                        
176         /* Add attachments */
177         tmp_attached = add_attachments (TNY_MIME_PART (new_msg), attachments, FALSE, err);
178         if (attached)
179                 *attached = tmp_attached;
180         add_images (new_msg, images, err);
181         if (header)
182                 g_object_unref(header);
183
184         return new_msg;
185 }
186
187
188 /* FIXME: this func copy from modest-mail-operation: refactor */
189 static TnyMimePart *
190 add_body_part (TnyMsg *msg, 
191                const gchar *body,
192                const gchar *content_type)
193 {
194         TnyMimePart *text_body_part = NULL;
195         TnyStream *text_body_stream;
196
197         /* Create the stream */
198         text_body_stream = TNY_STREAM (tny_camel_mem_stream_new_with_buffer
199                                         (body, (body ? strlen(body) : 0)));
200
201         text_body_part = modest_formatter_create_body_part (NULL, msg);
202
203         /* Construct MIME part */
204         tny_stream_reset (text_body_stream);
205         tny_mime_part_construct (text_body_part,
206                                  text_body_stream,
207                                  content_type, "7bit");
208         tny_stream_reset (text_body_stream);
209
210         g_object_unref (G_OBJECT(text_body_part));
211
212         /* Clean */
213         g_object_unref (text_body_stream);
214
215         return text_body_part;
216 }
217
218 static TnyMimePart *
219 add_html_body_part (TnyMsg *msg, 
220                     const gchar *body)
221 {
222         TnyMimePart *html_body_part = NULL;
223         TnyStream *html_body_stream;
224
225         /* Create the stream */
226         html_body_stream = TNY_STREAM (tny_camel_mem_stream_new_with_buffer
227                                         (body, strlen(body)));
228
229         /* Create body part if needed */
230         html_body_part = modest_formatter_create_body_part (NULL, msg);
231
232         /* Construct MIME part */
233         tny_stream_reset (html_body_stream);
234         tny_mime_part_construct (html_body_part,
235                                  html_body_stream,
236                                  "text/html; charset=utf-8", 
237                                  "7bit"); /* Sometimes it might be needed 
238                                              to make this one a 8bit! */
239         tny_stream_reset (html_body_stream);
240
241         g_object_unref (G_OBJECT(html_body_part));
242
243         /* Clean */
244         g_object_unref (html_body_stream);
245
246         return html_body_part;
247 }
248
249 static TnyMimePart *
250 copy_mime_part (TnyMimePart *part, GError **err)
251 {
252         TnyMimePart *result = NULL;
253         const gchar *attachment_content_type;
254         const gchar *attachment_filename;
255         const gchar *attachment_cid;
256         TnyList *parts;
257         TnyIterator *iterator;
258         TnyStream *attachment_stream;
259         const gchar *enc;
260         gint ret;
261         
262         if (TNY_IS_MSG (part)) {
263                 g_object_ref (part);
264                 return part;
265         }
266
267         result = tny_platform_factory_new_mime_part (
268                 modest_runtime_get_platform_factory());
269
270         attachment_content_type = tny_mime_part_get_content_type (part);
271
272         /* get mime part headers */
273         attachment_filename = tny_mime_part_get_filename (part);
274         attachment_cid = tny_mime_part_get_content_id (part);
275         
276         /* fill the stream */
277         attachment_stream = tny_mime_part_get_decoded_stream (part);
278         enc = tny_mime_part_get_transfer_encoding (part);
279         if (attachment_stream == NULL) {
280                 if (err != NULL && *err == NULL)
281                         g_set_error (err, MODEST_MAIL_OPERATION_ERROR, MODEST_MAIL_OPERATION_ERROR_FILE_IO, _("TODO: couldn't retrieve attachment"));
282                 g_object_unref (result);
283                 return NULL;
284         } else {
285                 ret = tny_stream_reset (attachment_stream);
286                 ret = tny_mime_part_construct (result,
287                                                attachment_stream,
288                                                attachment_content_type, 
289                                                enc);
290                 ret = tny_stream_reset (attachment_stream);
291         }
292         
293         /* set other mime part fields */
294         tny_mime_part_set_filename (result, attachment_filename);
295         tny_mime_part_set_content_id (result, attachment_cid);
296
297         /* copy subparts */
298         parts = tny_simple_list_new ();
299         tny_mime_part_get_parts (part, parts);
300         iterator = tny_list_create_iterator (parts);
301         while (!tny_iterator_is_done (iterator)) {
302                 TnyMimePart *subpart = TNY_MIME_PART (tny_iterator_get_current (iterator));
303                 if (subpart) {
304                         const gchar *subpart_cid;
305                         TnyMimePart *subpart_copy = copy_mime_part (subpart, err);
306                         if (subpart_copy != NULL) {
307                                 subpart_cid = tny_mime_part_get_content_id (subpart);
308                                 tny_mime_part_add_part (result, subpart_copy);
309                                 if (subpart_cid)
310                                         tny_mime_part_set_content_id (result, subpart_cid);
311                                 g_object_unref (subpart_copy);
312                         }
313                         g_object_unref (subpart);
314                 }
315
316                 tny_iterator_next (iterator);
317         }
318         g_object_unref (iterator);
319         g_object_unref (parts);
320         g_object_unref (attachment_stream);
321
322         return result;
323 }
324
325 static gint
326 add_attachments (TnyMimePart *part, GList *attachments_list, gboolean add_inline, GError **err)
327 {
328         GList *pos;
329         TnyMimePart *attachment_part, *old_attachment;
330         gint ret;
331         gint attached = 0;
332
333         for (pos = (GList *)attachments_list; pos; pos = pos->next) {
334
335                 old_attachment = pos->data;
336                 if (!tny_mime_part_is_purged (old_attachment)) {
337                         gchar *old_cid;
338                         old_cid = g_strdup (tny_mime_part_get_content_id (old_attachment));
339                         attachment_part = copy_mime_part (old_attachment, err);
340                         if (attachment_part != NULL) {
341                                 if (add_inline) {
342                                         tny_mime_part_set_header_pair (attachment_part, "Content-Disposition",
343                                                                        "inline");
344                                 } else {
345                                         const gchar *filename;
346
347                                         filename = tny_mime_part_get_filename (old_attachment);
348                                         if (filename) {
349                                                 /* If the mime part has a filename do not set it again
350                                                    because Camel won't replace the old one. Instead it
351                                                    will append the filename to the old one and that will
352                                                    mislead email clients */
353                                                 if (!tny_mime_part_get_filename (attachment_part))
354                                                         tny_mime_part_set_filename (attachment_part, filename);
355                                         } else {
356                                                 tny_mime_part_set_header_pair (attachment_part, "Content-Disposition",
357                                                                                "attachment");
358                                         }
359                                 }
360                                 if (!TNY_IS_MSG (old_attachment))  {
361                                         tny_mime_part_set_transfer_encoding (TNY_MIME_PART (attachment_part), "base64");
362                                 }
363                                 ret = tny_mime_part_add_part (TNY_MIME_PART (part), attachment_part);
364                                 attached++;
365                                 if (old_cid)
366                                         tny_mime_part_set_content_id (attachment_part, old_cid);
367                                 g_object_unref (attachment_part);
368                         }
369                         g_free (old_cid);
370                 }
371         }
372         return attached;
373 }
374
375 static void
376 add_images (TnyMsg *msg, GList *images_list, GError **err)
377 {
378         TnyMimePart *related_part = NULL;
379         const gchar *content_type;
380
381         content_type = tny_mime_part_get_content_type (TNY_MIME_PART (msg));
382
383         if ((content_type != NULL) && !strcasecmp (content_type, "multipart/related")) {
384                 related_part = g_object_ref (msg);
385         } else if ((content_type != NULL) && !strcasecmp (content_type, "multipart/mixed")) {
386                 TnyList *parts = TNY_LIST (tny_simple_list_new ());
387                 TnyIterator *iter = NULL;
388                 tny_mime_part_get_parts (TNY_MIME_PART (msg), parts);
389                 iter = tny_list_create_iterator (parts);
390
391                 while (!tny_iterator_is_done (iter)) {
392                         TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
393                         if (part && !g_ascii_strcasecmp (tny_mime_part_get_content_type (part), "multipart/related")) {
394                                 related_part = part;
395                                 break;
396                         }
397                         if (part)
398                                 g_object_unref (part);
399                         tny_iterator_next (iter);
400                 }
401                 g_object_unref (iter);
402                 g_object_unref (parts);
403         }
404
405         if (related_part != NULL) {
406                 /* TODO: attach images in their proper place */
407                 add_attachments (related_part, images_list, TRUE, err);
408                 g_object_unref (related_part);
409         }
410 }
411
412
413 gchar * 
414 modest_tny_msg_get_body (TnyMsg *msg, gboolean want_html, gboolean *is_html)
415 {
416         TnyStream *stream;
417         TnyMimePart *body;
418         GtkTextBuffer *buf;
419         GtkTextIter start, end;
420         gchar *to_quote;
421         gboolean result_was_html = TRUE;
422
423         g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
424         
425         body = modest_tny_msg_find_body_part(msg, want_html);
426         if (!body)
427                 return NULL;
428
429         buf = gtk_text_buffer_new (NULL);
430         stream = TNY_STREAM (tny_gtk_text_buffer_stream_new (buf));
431         tny_stream_reset (stream);
432         tny_mime_part_decode_to_stream (body, stream, NULL);
433         tny_stream_reset (stream);
434         
435         gtk_text_buffer_get_bounds (buf, &start, &end);
436         to_quote = gtk_text_buffer_get_text (buf, &start, &end, FALSE);
437         if (tny_mime_part_content_type_is (body, "text/plain")) {
438                 gchar *to_quote_converted = modest_text_utils_convert_to_html (to_quote);
439                 g_free (to_quote);
440                 to_quote = to_quote_converted;
441                 result_was_html = FALSE;
442         }
443
444         g_object_unref (buf);
445         g_object_unref (G_OBJECT(stream));
446         g_object_unref (G_OBJECT(body));
447
448         if (is_html != NULL)
449                 *is_html = result_was_html;
450
451         return to_quote;
452 }
453
454
455 static TnyMimePart*
456 modest_tny_msg_find_body_part_in_alternative (TnyMimePart *msg, gboolean want_html)
457 {
458         TnyList *parts;
459         TnyIterator *iter;
460         TnyMimePart *part = NULL;
461         TnyMimePart *first_part = NULL;
462         const gchar *desired_mime_type = want_html ? "text/html" : "text/plain";
463
464         parts = TNY_LIST (tny_simple_list_new());
465         tny_mime_part_get_parts (TNY_MIME_PART (msg), parts);
466
467         for (iter  = tny_list_create_iterator(parts); 
468              !tny_iterator_is_done (iter);
469              tny_iterator_next (iter)) {
470                 gchar *content_type;
471                 gboolean is_body;
472
473                 part = TNY_MIME_PART (tny_iterator_get_current (iter));
474
475                 if (first_part == NULL) {
476                         g_object_ref (part);
477                         first_part = part;
478                 }
479
480                 is_body = FALSE;
481                 content_type = g_ascii_strdown (tny_mime_part_get_content_type (part), -1);
482                 is_body = g_str_has_prefix (content_type, desired_mime_type);
483                 if (is_body)
484                         break;
485
486                 g_object_unref (part);
487                 part = NULL;
488
489         }
490         g_object_unref (iter);
491         g_object_unref (parts);
492
493         if (part == NULL) {
494                 return first_part;
495         } else {
496                 if (first_part) g_object_unref (first_part);
497                 return part;
498         }
499 }
500
501 TnyMimePart*
502 modest_tny_msg_find_body_part_from_mime_part (TnyMimePart *msg, gboolean want_html)
503 {
504         TnyMimePart *part = NULL;
505         TnyList *parts = NULL;
506         TnyIterator *iter = NULL;
507         gchar *header_content_type;
508         gchar *header_content_type_lower = NULL;
509         
510         if (!msg)
511                 return NULL;
512
513         /* If it's an application multipart, then we don't get into as we don't
514          * support them (for example application/sml or wap messages */
515         header_content_type = modest_tny_mime_part_get_header_value (msg, "Content-Type");
516         if (header_content_type) {
517                 header_content_type = g_strstrip (header_content_type);
518                 header_content_type_lower = g_ascii_strdown (header_content_type, -1);
519         }
520         if (header_content_type_lower && 
521             g_str_has_prefix (header_content_type_lower, "multipart/") &&
522             !g_str_has_prefix (header_content_type_lower, "multipart/signed") &&
523             strstr (header_content_type_lower, "application/")) {
524                 g_free (header_content_type_lower);
525                 g_free (header_content_type);
526                 return NULL;
527         }       
528         if (header_content_type_lower && 
529             g_str_has_prefix (header_content_type_lower, "multipart/alternative")) {
530                 g_free (header_content_type_lower);
531                 g_free (header_content_type);
532                 return modest_tny_msg_find_body_part_in_alternative (msg, want_html);
533         }       
534         g_free (header_content_type_lower);
535         g_free (header_content_type);
536
537         parts = TNY_LIST (tny_simple_list_new());
538         tny_mime_part_get_parts (TNY_MIME_PART (msg), parts);
539
540         iter  = tny_list_create_iterator(parts);
541
542         /* no parts? assume it's single-part message */
543         if (tny_iterator_is_done(iter)) {
544                 gchar *content_type;
545                 gboolean is_text_part;
546                 g_object_unref (G_OBJECT(iter));
547                 content_type = modest_tny_mime_part_get_content_type (msg);
548                 if (content_type == NULL)
549                         return NULL;
550                 is_text_part = 
551                         g_str_has_prefix (content_type, "text/");
552                 g_free (content_type);
553                 /* if this part cannot be a supported body return NULL */
554                 if (!is_text_part) {
555                         return NULL;
556                 } else {
557                         return TNY_MIME_PART (g_object_ref(G_OBJECT(msg)));
558                 }
559         } else {
560                 do {
561                         gchar *tmp, *content_type = NULL;
562                         gboolean has_content_disp_name = FALSE;
563
564                         part = TNY_MIME_PART(tny_iterator_get_current (iter));
565
566                         if (!part) {
567                                 g_warning ("%s: not a valid mime part", __FUNCTION__);
568                                 tny_iterator_next (iter);
569                                 continue;
570                         }
571
572                         /* it's a message --> ignore */
573                         if (part && TNY_IS_MSG (part)) {
574                                 g_object_unref (part);
575                                 part = NULL;
576                                 tny_iterator_next (iter);
577                                 continue;
578                         }                       
579
580                         /* we need to strdown the content type, because
581                          * tny_mime_part_has_content_type does not do it...
582                          */
583                         content_type = g_ascii_strdown (tny_mime_part_get_content_type (part), -1);
584                         
585                         /* mime-parts with a content-disposition header (either 'inline' or 'attachment')
586                          * and a 'name=' thingy cannot be body parts
587                          */
588                                 
589                         tmp = modest_tny_mime_part_get_header_value (part, "Content-Disposition");
590                         if (tmp) {
591                                 gchar *content_disp = g_ascii_strdown(tmp, -1);
592                                 g_free (tmp);
593                                 has_content_disp_name = g_strstr_len (content_disp, strlen(content_disp), "name=") != NULL;
594                                 g_free (content_disp);
595                         }
596                         
597                         if (g_str_has_prefix (content_type, "text/") && 
598                             !has_content_disp_name &&
599                             !modest_tny_mime_part_is_attachment_for_modest (part)) {
600                                 /* we found the body. Doesn't have to be the desired mime part, first
601                                    text/ part in a mixed is the body */
602                                 g_free (content_type);
603                                 break;
604
605                         } else if (g_str_has_prefix(content_type, "multipart/alternative")) {
606
607                                 /* multipart? recurse! */
608                                 g_object_unref (part);
609                                 g_free (content_type);
610                                 part = modest_tny_msg_find_body_part_in_alternative (part, want_html);
611                                 if (part)
612                                         break;
613
614                         } else  if (g_str_has_prefix(content_type, "multipart")) {
615
616                                 /* multipart? recurse! */
617                                 g_object_unref (part);
618                                 g_free (content_type);
619                                 part = modest_tny_msg_find_body_part_from_mime_part (part, want_html);
620                                 if (part)
621                                         break;
622                         } else
623                                 g_free (content_type);
624                         
625                         if (part) {
626                                 g_object_unref (G_OBJECT(part));
627                                 part = NULL;
628                         }
629                         
630                         tny_iterator_next (iter);
631                         
632                 } while (!tny_iterator_is_done(iter));
633         }
634         
635         g_object_unref (G_OBJECT(iter));
636         g_object_unref (G_OBJECT(parts));
637
638         return part; /* this maybe NULL, this is not an error; some message just don't have a body
639                       * part */
640 }
641
642
643 TnyMimePart*
644 modest_tny_msg_find_body_part (TnyMsg *msg, gboolean want_html)
645 {
646         g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
647         
648         return modest_tny_msg_find_body_part_from_mime_part (TNY_MIME_PART(msg),
649                                                              want_html);
650 }
651
652
653 #define MODEST_TNY_MSG_PARENT_UID "parent-uid"
654
655 static TnyMsg *
656 create_reply_forward_mail (TnyMsg *msg, TnyHeader *header, const gchar *from,
657                            const gchar *signature, gboolean is_reply,
658                            guint type /*ignored*/, GList *attachments)
659 {
660         TnyMsg *new_msg;
661         TnyHeader *new_header;
662         gchar *old_subject;
663         gchar *new_subject;
664         TnyMimePart *body = NULL;
665         TnyMimePart *html_body = NULL;
666         ModestFormatter *formatter;
667         gboolean no_text_part;
668         gchar *parent_uid;
669         gboolean forward_as_attach = FALSE;
670
671         if (header)
672                 g_object_ref (header);
673         else
674                 header = tny_msg_get_header (msg);
675
676         /* Get body from original msg. Always look for the text/plain
677            part of the message to create the reply/forwarded mail */
678         if (msg != NULL) {
679                 body   = modest_tny_msg_find_body_part (msg, FALSE);
680                 html_body = modest_tny_msg_find_body_part (msg, TRUE);
681         }
682
683         if (modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_PREFER_FORMATTED_TEXT,
684                                   NULL))
685                 formatter = modest_formatter_new ("text/html", signature);
686         else
687                 formatter = modest_formatter_new ("text/plain", signature);
688
689
690         /* if we don't have a text-part */
691         no_text_part = (!body) || (strcmp (tny_mime_part_get_content_type (body), "text/html")==0);
692
693         /* when we're reply, include the text part if we have it, or nothing otherwise. */
694         if (is_reply)
695                 new_msg = modest_formatter_quote  (formatter, body, header,
696                                                     attachments);
697         else {
698                 if (no_text_part || (html_body && (strcmp (tny_mime_part_get_content_type (html_body), "text/html")==0))) {
699                         forward_as_attach = TRUE;
700                         new_msg = modest_formatter_attach (formatter, msg, header);
701                 } else {
702                         forward_as_attach = FALSE;
703                         new_msg = modest_formatter_inline  (formatter, body, header,
704                                                             attachments);
705                 }
706         }
707
708         g_object_unref (G_OBJECT(formatter));
709         if (body)
710                 g_object_unref (G_OBJECT(body));
711         if (html_body)
712                 g_object_unref (G_OBJECT(html_body));
713
714         /* Fill the header */
715         new_header = tny_msg_get_header (new_msg);
716         tny_header_set_from (new_header, from);
717         tny_header_set_replyto (new_header, from);
718
719         /* Change the subject */
720         old_subject = tny_header_dup_subject (header);
721         new_subject =
722                 (gchar *) modest_text_utils_derived_subject (old_subject, is_reply);
723         g_free (old_subject);
724         tny_header_set_subject (new_header, (const gchar *) new_subject);
725         g_free (new_subject);
726
727         /* get the parent uid, and set it as a gobject property on the new msg */
728         parent_uid = modest_tny_folder_get_header_unique_id (header);
729         g_object_set_data_full (G_OBJECT(new_msg), MODEST_TNY_MSG_PARENT_UID,
730                                 parent_uid, g_free);
731
732         /* set modest as the X-Mailer
733          * we could this in the platform factory, but then the header
734          * would show up before all the others.
735          */
736         tny_mime_part_set_header_pair (TNY_MIME_PART (msg), "X-Mailer", "Modest "
737                                        VERSION);
738
739         /* Clean */
740         g_object_unref (G_OBJECT (new_header));
741         g_object_unref (G_OBJECT (header));
742         /* ugly to unref it here instead of in the calling func */
743
744         if (!is_reply & !forward_as_attach) {
745                 add_attachments (TNY_MIME_PART (new_msg), attachments, FALSE, NULL);
746         }
747
748         return new_msg;
749 }
750
751 const gchar*
752 modest_tny_msg_get_parent_uid (TnyMsg *msg)
753 {
754         g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
755         
756         return g_object_get_data (G_OBJECT(msg), MODEST_TNY_MSG_PARENT_UID);
757 }
758
759 static gchar *get_signed_protocol (TnyMimePart *part)
760 {
761         TnyList *header_pairs;
762         TnyIterator *iterator;
763         gchar *result = NULL;
764
765         header_pairs = TNY_LIST (tny_simple_list_new ());
766         tny_mime_part_get_header_pairs (part, header_pairs);
767         iterator = tny_list_create_iterator (header_pairs);
768
769         while (!result && !tny_iterator_is_done (iterator)) {
770                 TnyPair *pair;
771                 const gchar *name;
772
773                 pair = TNY_PAIR (tny_iterator_get_current (iterator));
774                 name = tny_pair_get_name (pair);
775                 if (name && !g_ascii_strcasecmp (name, "Content-Type")) {
776                         const gchar *s;
777                         s = tny_pair_get_value (pair);
778                         if (s) {
779                                 s = strstr (s, "protocol=");
780                                 if (s) {
781                                         const gchar *t;
782                                         s += 9;
783                                         if (*s == '\"') {
784                                                 s++;
785                                                 t = strstr (s, "\"");
786                                         } else {
787                                                 t = strstr (s, ";");
788                                         }
789                                         result = g_strndup (s, t - s);
790                                 }
791                         }
792                 }
793
794                 g_object_unref (pair);
795                 tny_iterator_next (iterator);
796         }
797
798         g_object_unref (iterator);
799         g_object_unref (header_pairs);
800
801         return result;
802 }
803
804 TnyMimePart *
805 modest_tny_msg_get_attachments_parent (TnyMsg *msg)
806 {
807         TnyMimePart *result;
808         const gchar *content_type;
809
810         result = NULL;
811
812         content_type = tny_mime_part_get_content_type (TNY_MIME_PART (msg));
813         if (content_type && !strcmp (content_type, "multipart/signed")) {
814                 TnyList *msg_children;
815                 TnyIterator *iterator;
816                 gchar *signed_protocol;
817
818                 msg_children = TNY_LIST (tny_simple_list_new ());
819                 tny_mime_part_get_parts (TNY_MIME_PART (msg), msg_children);
820
821                 iterator = tny_list_create_iterator (msg_children);
822                 signed_protocol = get_signed_protocol (TNY_MIME_PART (msg));
823                         
824                 while (!result && !tny_iterator_is_done (iterator)) {
825                         TnyMimePart *part;
826
827                         part = TNY_MIME_PART (tny_iterator_get_current (iterator));
828                         if (signed_protocol) {
829                                 const gchar *part_content_type;
830
831                                 part_content_type = tny_mime_part_get_content_type (part);
832                                 if (part_content_type && g_ascii_strcasecmp (part_content_type, signed_protocol)) {
833                                         result = g_object_ref (part);
834                                 }
835                         } else {
836                                 result = g_object_ref (part);
837                         }
838
839                         g_object_unref (part);
840                         tny_iterator_next (iterator);
841                 }
842
843                 g_object_unref (iterator);
844                 g_free (signed_protocol);
845                 g_object_unref (msg_children);
846         }
847         if (result == NULL) {
848                 result = g_object_ref (msg);
849         }
850
851         return result;
852 }
853
854
855
856 static void
857 add_if_attachment (gpointer data, gpointer user_data)
858 {
859         TnyMimePart *part;
860         GList **attachments_list;
861
862         part = TNY_MIME_PART (data);
863         attachments_list = ((GList **) user_data);
864
865         if ((tny_mime_part_is_attachment (part))||(TNY_IS_MSG (part))) {
866                 *attachments_list = g_list_prepend (*attachments_list, part);
867                 g_object_ref (part);
868         }
869 }
870
871 TnyMsg* 
872 modest_tny_msg_create_forward_msg (TnyMsg *msg, 
873                                    const gchar *from,
874                                    const gchar *signature,
875                                    ModestTnyMsgForwardType forward_type)
876 {
877         TnyMsg *new_msg;
878         TnyList *parts = NULL;
879         GList *attachments_list = NULL;
880         TnyMimePart *part_to_check = NULL;
881
882         g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
883
884         part_to_check = modest_tny_msg_get_attachments_parent (TNY_MSG (msg));
885         
886         /* Add attachments */
887         parts = TNY_LIST (tny_simple_list_new());
888         tny_mime_part_get_parts (TNY_MIME_PART (part_to_check), parts);
889         tny_list_foreach (parts, add_if_attachment, &attachments_list);
890
891         new_msg = create_reply_forward_mail (msg, NULL, from, signature, FALSE, forward_type,
892                                              attachments_list);
893
894         /* Clean */
895         if (attachments_list) {
896                 g_list_foreach (attachments_list, (GFunc) g_object_unref, NULL);
897                 g_list_free (attachments_list);
898         }
899         g_object_unref (G_OBJECT (parts));
900         g_object_unref (part_to_check);
901
902         return new_msg;
903 }
904
905
906
907 static gint
908 count_addresses (const gchar* addresses)
909 {
910         gint count = 1;
911
912         if (!addresses)
913                 return 0;
914         
915         while (*addresses) {
916                 if (*addresses == ',' || *addresses == ';')
917                         ++count;
918                 ++addresses;
919         }
920         
921         return count;
922 }
923
924 static void
925 remove_undisclosed_recipients (gchar **recipients)
926 {
927         GSList *addresses, *node;
928         gboolean is_first;
929         GString *result;
930
931         g_return_if_fail (recipients);
932         addresses = modest_text_utils_split_addresses_list (*recipients);
933
934         is_first = TRUE;
935         result = g_string_new ("");
936         for (node = addresses; node != NULL; node = g_slist_next (node)) {
937                 const gchar *address = (const gchar *) node->data;
938
939                 if (address && strstr (address, "undisclosed-recipients"))
940                         continue;
941
942                 if (is_first)
943                         is_first = FALSE;
944                 else
945                         result = g_string_append (result, ", ");
946
947                 result = g_string_append (result, address);
948         }
949         g_slist_foreach (addresses, (GFunc)g_free, NULL);
950         g_slist_free (addresses);
951
952         g_free (*recipients);
953         *recipients = g_string_free (result, FALSE);
954 }
955
956
957 /* get the new To:, based on the old header,
958  * result is newly allocated or NULL in case of error
959  * */
960 static gchar*
961 get_new_to (TnyMsg *msg, TnyHeader *header, const gchar* from,
962             ModestTnyMsgReplyMode reply_mode)
963 {
964         const gchar *reply_header = "Reply-To:";
965         const gchar *from_header = "From:";
966         gchar* old_reply_to;
967         gchar* old_from;
968         gchar* new_to;
969         gchar* tmp;
970         
971         /* according to RFC2369 (http://www.faqs.org/rfcs/rfc2369.html), we
972          * can identify Mailing-List posts by the List-Help header.
973          * for mailing lists, both the Reply-To: and From: should be included
974          * in the new To:; for now, we're ignoring List-Post
975          */
976         gchar* list_help = modest_tny_mime_part_get_header_value (TNY_MIME_PART(msg), 
977                                                                   "List-Help");
978         gboolean is_mailing_list = (list_help != NULL);
979         g_free (list_help);
980
981
982         /* reply to sender, use ReplyTo or From */
983         old_reply_to = modest_tny_mime_part_get_header_value (TNY_MIME_PART(msg), 
984                                                               "Reply-To"); 
985         old_from     = tny_header_dup_from (header);
986
987         if (!old_from && !old_reply_to) {
988                 g_debug ("%s: failed to get either Reply-To: or From: from header",
989                            __FUNCTION__);
990                 return NULL;
991         }
992
993         /* Prevent DoS attacks caused by malformed emails */
994         if (old_from) {
995                 gchar *tmp = old_from;
996                 old_from = modest_text_utils_get_secure_header ((const gchar *) tmp, from_header);
997                 g_free (tmp);
998         }
999         if (old_reply_to) {
1000                 gchar *tmp = old_reply_to;
1001                 old_reply_to = modest_text_utils_get_secure_header ((const gchar *) tmp, reply_header);
1002                 g_free (tmp);
1003         }
1004
1005         /* for mailing lists, use both Reply-To and From if we did a
1006          * 'Reply All:'
1007          * */
1008         if (is_mailing_list && reply_mode == MODEST_TNY_MSG_REPLY_MODE_ALL &&
1009             old_reply_to && old_from && strcmp (old_from, old_reply_to) != 0)
1010                 new_to = g_strjoin (",", old_reply_to, old_from, NULL);
1011         else
1012                 /* otherwise use either Reply-To: (preferred) or From: */
1013                 new_to = g_strdup (old_reply_to ? old_reply_to : old_from);
1014         g_free (old_from);
1015         g_free (old_reply_to);
1016
1017         /* in case of ReplyAll, we need to add the Recipients in the old To: */
1018         if (reply_mode == MODEST_TNY_MSG_REPLY_MODE_ALL) {
1019                 gchar *old_to = tny_header_dup_to (header);
1020                 if (!old_to) 
1021                         g_debug ("%s: no To: address found in source mail",
1022                                    __FUNCTION__);
1023                 else {
1024                         /* append the old To: */
1025                         gchar *tmp = g_strjoin (",", new_to, old_to, NULL);
1026                         g_free (new_to);
1027                         new_to = tmp;
1028                         g_free (old_to);
1029                 }
1030
1031                 /* remove duplicate entries */
1032                 gchar *tmp = modest_text_utils_remove_duplicate_addresses (new_to);
1033                 g_free (new_to);
1034                 new_to = tmp;
1035                 
1036                 /* now, strip me (the new From:) from the new_to, but only if
1037                  * there are >1 addresses there */
1038                 if (count_addresses (new_to) > 1) {
1039                         gchar *tmp = modest_text_utils_remove_address (new_to, from);
1040                         g_free (new_to);
1041                         new_to = tmp;
1042                 }
1043         }
1044
1045         tmp = modest_text_utils_simplify_recipients (new_to);
1046         remove_undisclosed_recipients  (&tmp);
1047         g_free (new_to);
1048         new_to = tmp;
1049
1050         return new_to;
1051 }
1052
1053
1054 /* get the new Cc:, based on the old header,
1055  * result is newly allocated or NULL in case of error */
1056 static gchar*
1057 get_new_cc (TnyHeader *header, const gchar* from, const gchar *new_to)
1058 {
1059         gchar *old_cc, *result, *dup;
1060
1061         old_cc = tny_header_dup_cc (header);
1062         if (!old_cc)
1063                 return NULL;
1064
1065         /* remove me (the new From:) from the Cc: list */
1066         dup =  modest_text_utils_remove_address (old_cc, from);
1067
1068         if (new_to) {
1069                 gchar **to_parts, **current;
1070
1071                 to_parts = g_strsplit (new_to, ",", 0);
1072                 for (current = to_parts; current && *current != '\0'; current++) {
1073                         gchar *dup2;
1074
1075                         dup2 = modest_text_utils_remove_address (dup, g_strstrip (*current));
1076                         g_free (dup);
1077                         dup = dup2;
1078                 }
1079                 g_strfreev (to_parts);
1080         }
1081
1082         result = modest_text_utils_remove_duplicate_addresses (dup);
1083         g_free (dup);
1084         dup = result;
1085         result = modest_text_utils_simplify_recipients (dup);
1086         remove_undisclosed_recipients  (&result);
1087         g_free (dup);
1088         g_free (old_cc);
1089         return result;
1090 }
1091
1092 void 
1093 modest_tny_msg_get_references (TnyMsg *msg, gchar **message_id, gchar **references, gchar **in_reply_to)
1094 {
1095         TnyList *headers;
1096         TnyIterator *iterator;
1097         gchar *l_message_id;
1098         gchar *l_references;
1099         gchar *l_in_reply_to;
1100
1101         g_return_if_fail (TNY_IS_MSG (msg));
1102
1103         l_message_id = NULL;
1104         l_references = NULL;
1105         l_in_reply_to = NULL;
1106
1107         headers = TNY_LIST (tny_simple_list_new ());
1108         tny_mime_part_get_header_pairs (TNY_MIME_PART (msg), headers);
1109
1110         iterator = tny_list_create_iterator (headers);
1111         while (!tny_iterator_is_done (iterator)) {
1112                 TnyPair *pair;
1113                 const gchar *name;
1114
1115                 pair = TNY_PAIR (tny_iterator_get_current (iterator));
1116                 name = tny_pair_get_name (pair);
1117                 if (!g_ascii_strcasecmp (name, "References")) {
1118                         if (l_references) g_free (l_references);
1119                         l_references = g_strdup (tny_pair_get_value (pair));
1120                 } else if (!g_ascii_strcasecmp (name, "In-Reply-To")) {
1121                         if (l_in_reply_to) g_free (l_in_reply_to);
1122                         l_in_reply_to = g_strdup (tny_pair_get_value (pair));
1123                 } else if (!g_ascii_strcasecmp (name, "Message-ID")) {
1124                         if (l_message_id) g_free (l_message_id);
1125                         l_message_id = g_strdup (tny_pair_get_value (pair));
1126                 }
1127
1128                 g_object_unref (pair);
1129                 tny_iterator_next (iterator);
1130         }
1131
1132         g_object_unref (iterator);
1133         g_object_unref (headers);
1134
1135         if (message_id) {
1136                 *message_id = l_message_id;
1137         } else {
1138                 g_free (l_message_id);
1139         }
1140
1141         if (in_reply_to) {
1142                 *in_reply_to = l_in_reply_to;
1143         } else {
1144                 g_free (l_in_reply_to);
1145         }
1146
1147         if (references) {
1148                 *references = l_references;
1149         } else {
1150                 g_free (l_references);
1151         }
1152 }
1153
1154 static void 
1155 set_references (TnyMsg *reply_msg, TnyMsg *original_msg)
1156 {
1157         gchar *orig_references, *orig_in_reply_to, *orig_message_id;
1158         gchar *references, *in_reply_to;
1159
1160         modest_tny_msg_get_references (original_msg, &orig_message_id, &orig_references, &orig_in_reply_to);
1161
1162         references = NULL;
1163         in_reply_to = NULL;
1164
1165         if (orig_message_id)
1166                 in_reply_to = g_strdup (orig_message_id);
1167
1168         if (orig_references) {
1169                 if (orig_message_id)
1170                         references = g_strconcat (orig_references, "\n        ", orig_message_id, NULL);
1171                 else
1172                         references = g_strdup (orig_references);
1173
1174         } else if (orig_in_reply_to) {
1175                 if (orig_message_id)
1176                         references = g_strconcat (orig_in_reply_to, "\n        ", orig_message_id, NULL);
1177                 else
1178                         references = g_strdup (orig_in_reply_to);
1179         } else if (orig_message_id) {
1180                 references = g_strdup (orig_message_id);
1181         }
1182
1183         g_free (orig_references);
1184         g_free (orig_in_reply_to);
1185         g_free (orig_message_id);
1186
1187         if (in_reply_to) {
1188                 tny_mime_part_set_header_pair (TNY_MIME_PART (reply_msg), "In-Reply-To", in_reply_to);
1189         }
1190         if (references) {
1191                 tny_mime_part_set_header_pair (TNY_MIME_PART (reply_msg), "References", references);
1192         }
1193 }
1194
1195 TnyMsg* 
1196 modest_tny_msg_create_reply_msg (TnyMsg *msg,
1197                                  TnyHeader *header,
1198                                  const gchar *from,
1199                                  const gchar *signature,
1200                                  ModestTnyMsgReplyType reply_type,
1201                                  ModestTnyMsgReplyMode reply_mode)
1202 {
1203         TnyMsg *new_msg = NULL;
1204         TnyHeader *new_header;
1205         gchar *new_to = NULL;
1206         TnyList *parts = NULL;
1207         GList *attachments_list = NULL;
1208         TnyMimePart *part_to_check = NULL;
1209
1210         g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
1211
1212         part_to_check = modest_tny_msg_get_attachments_parent (TNY_MSG (msg));
1213
1214         parts = TNY_LIST (tny_simple_list_new());
1215         tny_mime_part_get_parts (TNY_MIME_PART (part_to_check), parts);
1216         tny_list_foreach (parts, add_if_attachment, &attachments_list);
1217
1218         new_msg = create_reply_forward_mail (msg, header, from, signature, TRUE, reply_type,
1219                                              attachments_list);
1220
1221         set_references (new_msg, msg);
1222         if (attachments_list != NULL) {
1223                 g_list_foreach (attachments_list, (GFunc) g_object_unref, NULL);
1224                 g_list_free (attachments_list);
1225         }
1226         g_object_unref (parts);
1227
1228         /* Fill the header */
1229         if (header)
1230                 g_object_ref (header);
1231         else
1232                 header = tny_msg_get_header (msg);
1233
1234         
1235         new_header = tny_msg_get_header(new_msg);
1236         new_to = get_new_to (msg, header, from, reply_mode);
1237         if (!new_to)
1238                 g_debug ("%s: failed to get new To:", __FUNCTION__);
1239         else {
1240                 tny_header_set_to (new_header, new_to);
1241         }
1242
1243         if (reply_mode == MODEST_TNY_MSG_REPLY_MODE_ALL) {
1244                 gchar *new_cc = get_new_cc (header, from, new_to);
1245                 if (new_cc) { 
1246                         tny_header_set_cc (new_header, new_cc);
1247                         g_free (new_cc);
1248                 }
1249         }
1250
1251         if (new_to)
1252                 g_free (new_to);
1253
1254         /* Clean */
1255         g_object_unref (G_OBJECT (new_header));
1256         g_object_unref (G_OBJECT (header));
1257         g_object_unref (G_OBJECT (part_to_check));
1258
1259         return new_msg;
1260 }
1261
1262
1263 static gboolean
1264 is_ascii(const gchar *s)
1265 {
1266         if (!s)
1267                 return TRUE;
1268         while (s[0]) {
1269                 if (s[0] & 128 || s[0] < 32)
1270                         return FALSE;
1271                 s++;
1272         }
1273         return TRUE;
1274 }
1275
1276 static char *
1277 get_content_type(const gchar *s)
1278 {
1279         GString *type;
1280         
1281         type = g_string_new("text/plain");
1282         if (!is_ascii(s)) {
1283                 if (g_utf8_validate(s, -1, NULL)) {
1284                         g_string_append(type, "; charset=\"utf-8\"");
1285                 } else {
1286                         /* it should be impossible to reach this, but better safe than sorry */
1287                         g_debug("invalid utf8 in message");
1288                         g_string_append(type, "; charset=\"latin1\"");
1289                 }
1290         }
1291         return g_string_free(type, FALSE);
1292 }
1293
1294 guint64
1295 modest_tny_msg_estimate_size (const gchar *plain_body, const gchar *html_body,
1296                               guint64 parts_count,
1297                               guint64 parts_size)
1298 {
1299         guint64 result;
1300
1301         /* estimation of headers size */
1302         result = 1024;
1303
1304         /* We add a 20% of size due to the increase in 7bit encoding */
1305         if (plain_body) {
1306                 result += strlen (plain_body) * 120 / 100;
1307         }
1308         if (html_body) {
1309                 result += strlen (html_body) * 120 / 100;
1310         }
1311
1312         /* 256 bytes per additional part because of their headers */
1313         result += parts_count * 256;
1314
1315         /* 150% of increase per encoding */
1316         result += parts_size * 3 / 2;
1317
1318         return result;
1319 }
1320
1321 GSList *
1322 modest_tny_msg_header_get_all_recipients_list (TnyHeader *header)
1323 {
1324         GSList *recipients = NULL;
1325         gchar *from = NULL, *to = NULL, *cc = NULL, *bcc = NULL;
1326         gchar *after_remove, *joined;
1327
1328         if (header == NULL)
1329                 return NULL;
1330
1331         from = tny_header_dup_from (header);
1332         to = tny_header_dup_to (header);
1333         cc = tny_header_dup_cc (header);
1334         bcc = tny_header_dup_bcc (header);
1335
1336         joined = modest_text_utils_join_addresses (from, to, cc, bcc);
1337         after_remove = modest_text_utils_remove_duplicate_addresses (joined);
1338         g_free (joined);
1339
1340         recipients = modest_text_utils_split_addresses_list (after_remove);
1341         g_free (after_remove);
1342
1343         if (from)
1344                 g_free (from);
1345         if (to)
1346                 g_free (to);
1347         if (cc)
1348                 g_free (cc);
1349         if (bcc)
1350                 g_free (bcc);
1351
1352         return recipients;
1353 }
1354
1355 GSList *
1356 modest_tny_msg_get_all_recipients_list (TnyMsg *msg)
1357 {
1358         TnyHeader *header = NULL;
1359         GSList *recipients = NULL;
1360
1361         if (msg == NULL)
1362                 return NULL;
1363
1364         header = tny_msg_get_header (msg);
1365         if (header == NULL)
1366                 return NULL;
1367
1368         recipients = modest_tny_msg_header_get_all_recipients_list (header);
1369         g_object_unref (header);
1370
1371         return recipients;
1372 }
1373